Print to PDF from ASP.NET
This content is based on version 10.8 and later of the
virtual printer. At the time of writing, it has not been released.
Please contact us if you want to have a beta of that version to work with
ASP.NET and IIS.
It is very common that we hear from VB.NET or C# programmers that they
want to create PDF documents from ASP.NET applications.
This guide will show you how this can be done.
Working with ASP.NET running under IIS, it can often be a challenge to
handle the security. This is also an important issue when you want to
print a PDF document and stream it to the user. Hopefully, the following
will help you overcome these challenges and put you on track with a
good PDF solution for your ASP.NET application.
At the end of this guide, you will find a link to the source code used
to build this example.
What Does the Example Do?
The example is an ASP.NET handler that creates and streams a PDF document
to the web site visitor. The PDF code can be placed in a normal ASP.NET
form as well as this handler.
Click here for a live Demo of this example
Print from ASP.NET using the PrintDocument class
This example will focus on printing from C# using the PrintDocument class
and the PrintPageEventHandler. This is the typical way that you
incorporate printing in your Microsoft.NET application.
Even though the example is in C#, the principles should also apply to
VB.NET or any other .NET compatible language.
The Challenges
When we want to print to PDF from ASP.NET we run into a couple of
challenges. The first one is the security surrounding IIS and the
second one is concurrency.
Printing and Security
In a normal IIS installation, the user context is locked down to
serving files and running scripts. This example was built to run with
default security settings. This means that you do not have to extend
the existing permissions for the IIS user to make this work.
Doing this could pose a security risk for your server and we really
do not want that.
Normally, the PDF printer uses the user context of the printing user
to perform the PDF creation. This makes a lot of sense when you use
it from the desktop or a Terminal Server. Even if you use it from a
normal service, it can be a benefit to run the conversion in the context
of the service user. However, when we look at the IIS the user context
is too locked down. Your visitors from the web site may be anonymous
and therefore we cannot rely on the normal security context.
Luckily, the PDF printer can be configured to run in the security context
of the Print Spooler service. This service normally uses the System
account to log in. The System account has access to most local resources
such as local hard drives. In order to configure the printer to run in
the Spooler context, a number of registry settings should be made.
Instead of having to do this manually, the printer setup program has
a command line switch that tells it to install a printer and prepare it
for use with IIS.
Printing and Concurrency
When a user visits your web site and want's to receive a PDF document,
you start a process that produces the requested document.
Another user could come to your web site and request another PDF
while the first one is still being produced.
Normally the printer is controlled by creating a runonce.ini configuration
file. This file sets parameters for creation of the next PDF print job.
With concurrent users, this way of printing cannot be used because one
runonce.ini may overwrite another that has not been processed yet by the
printer. Therefore, we have to use a feature in the PDF printer that
allows us to tell the printer exactly which runonce configuration to use
for a specific print job.
Running the Example
Here is how you install the printer and setup the ASP.NET application.
Installing the PDF Printer
The first step to making this example run is to install a PDF printer
and prepare it for use with IIS. Remember that you need to use version
10.8 or later for this to work. Even if you have the printer installed
already, I recommend that you install this new instance of the printer
and name it for use with IIS. To complete these tasks you simply run the
following command line in a folder where you have the setup program for
the printer:
Setup_PDFWriter_bioPDF_14_1_0_2951.exe /SPOOLERCONTEXT /SILENT /PRINTERNAME="IIS PDF Printer"
This will set the correct registry entries create a folder structure
for files related to the PDF creation.
By default, these files and folders are created here:
C:\ProgramData\PDF Writer\IIS PDF Printer
Create the ASP.NET Application
You need to download the example files and register the folder as an
IIS application with support for ASP.NET.
Remember to make the APP_DATA folder writable for the APS.NET application
user.
When the application is created and the printer is installed you can
browse to Default.aspx and try the example.
The Source Code
You can download the source code for this example in the ZIP file below or
get inspired by the source code listing.
print-from-aspnet-handler.zip
<%@ WebHandler Language="C#" Class="Print" %>
using System;
using System.Web;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Linq;
using bioPDF.PdfWriter;
public class Print : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string printerName = "IIS PDF Printer";
string downloadName = "aspnet test.pdf";
string jobId = Guid.NewGuid().ToString();
string jobName = string.Format("ASP.NET-{0}", jobId);
string tempPath = Path.Combine(HttpContext.Current.Server.MapPath("~/App_Data"),
"Temp");
string jobTempPath = Path.Combine(tempPath, jobId);
string pdfName = string.Format("ASP.NET-{0}.pdf", jobId);
string pdfPath = Path.Combine(jobTempPath, pdfName);
string statusFile = Path.Combine(jobTempPath, "status.ini");
// Create folders
Directory.CreateDirectory(jobTempPath);
// Set parameters for print job
PdfSettings pdfSettings = new PdfSettings();
pdfSettings.PrinterName = printerName;
// These settings will only have effect if they are not overwritten by values in
// the global.ini
pdfSettings.SetValue("Output", pdfPath);
pdfSettings.SetValue("StatusFile", statusFile);
pdfSettings.SetValue("ShowSettings", "never");
pdfSettings.SetValue("ShowSaveAS", "never");
pdfSettings.SetValue("ShowProgress", "no");
pdfSettings.SetValue("ShowProgressFinished", "no");
pdfSettings.SetValue("ShowPDF", "no");
pdfSettings.SetValue("ConfirmOverwrite", "no");
pdfSettings.SetValue("RememberLastFolderName", "no");
pdfSettings.SetValue("RememberLastFileName", "no");
pdfSettings.SetValue("WatermarkLayer", "bottom");
pdfSettings.SetValue("WatermarkText", "Test from ASP.NET");
// Create a runonce.ini that is specific to this job
// http://www.biopdf.com/guide/configuration_files.php
string defaultRunoncePath =
pdfSettings.GetSettingsFilePath(PdfSettingsFileType.RunOnce);
string specificRunoncePath = Path.Combine(
Path.GetDirectoryName(defaultRunoncePath),
string.Format("runonce_{0}.ini", Uri.EscapeDataString(jobName)));
pdfSettings.WriteSettings(specificRunoncePath);
// Create print job
PrintDocument pd = new PrintDocument();
pd.DocumentName = jobName;
pd.PrintPage += new PrintPageEventHandler(this.pd_PrintPage);
pd.PrinterSettings.PrinterName = printerName;
pd.DefaultPageSettings.Landscape = false;
pd.DefaultPageSettings.PaperSize = new PaperSize("Letter", 850, 1100);
pd.Print();
// Wait for PDF creation to finish
if (PdfUtil.WaitForFile(statusFile, 20000))
{
// Read error information from status file
string errorValue = PdfUtil.ReadIniString(statusFile, "Status", "Errors",
"");
if (errorValue == "0")
{
// Stream PDF to browser
context.Response.ClearContent();
context.Response.ContentType = "Application/pdf";
context.Response.AddHeader("Content-Disposition",
"inline; filename=" + downloadName);
context.Response.WriteFile(pdfPath);
context.Response.Flush();
// Remove files after the PDF is streamed to the client browser
File.Delete(pdfPath);
File.Delete(statusFile);
Directory.Delete(jobTempPath);
context.Response.End();
return;
}
else
{
string errorMessage = PdfUtil.ReadIniString(statusFile, "Status",
"MessageText", "");
WriteErrorMessage(string.Format("An error was reported: {0}; {1}",
errorValue, errorMessage));
}
}
WriteErrorMessage("No PDF was created.");
}
private void WriteErrorMessage(string message)
{
HttpContext.Current.Response.ContentType = "text/plain";
HttpContext.Current.Response.Write(message);
}
private void pd_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
// Output something in the print
SolidBrush myBrush = new SolidBrush(Color.Blue);
Font font = new Font("Arial", 12);
e.Graphics.DrawString("Print test: " + DateTime.Now.ToString(),
font, myBrush, e.MarginBounds.Left, e.MarginBounds.Top);
myBrush.Dispose();
}
public bool IsReusable
{
get
{
return false;
}
}
}
Trouble Shooting
If you have not set the write access for the IIS user or users in general
on the APP_DATA folder, you will see an error similar to this:
Access to the path 'C:\inetpub\Print From Handler\App_Data\Temp\b2c27634-1ee5-4afc-a4da-0f93c7b2452f' is denied.
|