After receiving quite a few requests on making the PDF image conversion work in a web application, I wanted to see how hard it would be to do. Not hard at all as it turns out, I had a nice working sample running with a bare 5 minutes of work.
The sample available for download below is a basic ASP.NET application, comprised of a single page with an IHttpHandler
for displaying the image. In order to make this sample as easy as possible, it uses pure server side controls and code, nothing client side.
Getting Started
In order to run this sample, you'll need the Cyotek.GhostScript
and Cyotek.GhostScript.PdfConversion.zip components described in a previous article.
You'll also need to download GhostScript. As with my other articles on the subject, please make sure you check their license terms - they seem very keen that people don't use the GPL version or distribute GhostScript without a commercial license.
Locating gsdll32.dll
In order for this to work, gsdll32.dll needs to be somewhere in your applications path. This could be in your system32 directory on 32bit Windows, or SysWOW64 on 64bit Windows.
While developing this sample, I also tried having the file in the bin directory of the website - this also worked fine. However, as the website was running on my local machine, it's probably running in Full Trust, and I have no idea if it will work in Medium Trust or lower.
I'm Running 64bit Windows!
Congratulations! I have nothing but issues with 32bit web servers. But I digress. The sample projects I have provided on this website all use the 32bit version of GhostScript. There is a 64bit version available, but I haven't downloaded it to test. Your options should be as follows:
- Build against the 64 bit GhostScript DLL. This may need some refactoring if their
public
API has changed. At the very least, you'll need to change the DLL filename in the native method calls.
- Using IIS7 or higher? Keep using the 32bit version, and set your worker pool to run in 32bit mode
- Using IIS6? Commiserations, I feel your pain. The only option here, if you stay 32bit, is to have the entire IIS run as 32bit.
I have tested on a Windows 7 Professional 64bit machine as follows:
- Firstly, using IIS Express which is running as a 32bit process
- Secondly, using IIS7 with a custom application pool running in 32bit mode
Both of these scenarios worked perfectly well.
Creating the Solution
Create a new ASP.NET Web Forms Site
Note: Even though this example uses pure WebForms, there's no reason that this sort of code won't work fine in ASP.NET MVC or any other .NET framework of your choice.
Open up Default.aspx and add some controls similar to the following:
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="Default.aspx.cs" Inherits="GhostScriptWebTest._Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>PDF Conversion Example</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<p>
<asp:LinkButton runat="server" ID="previousLinkButton"
Text="Previous" OnClick="previousLinkButton_Click" />
<asp:LinkButton runat="server" ID="nextLinkButton"
Text="Next" OnClick="nextLinkButton_Click" />
</p>
<p>
<asp:Image runat="server" ID="pdfImage"
ImageUrl="~/PdfImage.ashx?fileName=sample.pdf&page=1" />
</p>
</div>
</form>
</body>
</html>
The controls should be fairly self explanatory! The main thing of interest is the pdfImage
Image control - this will call a Generic Handler that I'll describe in the next section. Note that VS2010 and VS2012 have another option, an ASP.NET Handler - this implements the same IHttpHandler
interface but doesn't have a .ashx file and is registered differently. If you are using IIS7 or above, you're probably better off using that.
Note that by default the pdfImage
control is pointing to a sample file named sample.pdf - add any old PDF to the root of your website and name it sample. Ensure that the Build Action for the PDF is set to Content, otherwise it won't be deployed with your application.
Creating the Image Handler
Tutorials on creating image handlers with IHttpHandler
can be found scattered throughout the net, so I'll not go into how they work, but just describe the implementation I'm using in this example. Add a new generic handler to your project, then fill in the ProcessRequest
method as follows. Make sure you add the two GhostScript API components to your solution and add references to them to your web application first!
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Web;
using Cyotek.GhostScript.PdfConversion;
namespace GhostScriptWebTest
{
public class PdfImage : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string fileName;
int pageNumber;
Pdf2Image convertor;
Bitmap image;
fileName = context.Server.MapPath("~/" +
context.Request.QueryString["fileName"]);
pageNumber = Convert.ToInt32(context.Request.QueryString["page"]);
convertor = new Pdf2Image(fileName);
image = convertor.GetImage(pageNumber);
context.Response.ContentType = "image/png";
image.Save(context.Response.OutputStream, ImageFormat.Png);
}
public bool IsReusable
{ get { return true; } }
}
}
Again, this is extremely simple code. I extract the query string of the request to obtain the file name of the PDF document to convert, and the page to display. I then create an instance of the Pdf2Image
class, and grab an image of the specified page.
Next, you need to set the ContentType
of the Response
object so the web browser knows what to do with your content. Finally, I save the image directly to the response's OutputStream
. Make sure that the format you save the image as matches the content type you've specified.
With these steps complete, building and running the website should present you with a pair of hyper links, and the first page of your PDF file as a static image. [Well, it will if you add a pair of blank event handlers for those defined for the two hyperlink buttons anyway.]
Simple Navigation
Now that we can display our PDF, we'll add some basic navigation. Open up the code behind file for Default.aspx and fill in the event handlers for the two hyperlink buttons.
using System;
using System.Collections.Specialized;
using System.Web;
using Cyotek.GhostScript.PdfConversion;
namespace GhostScriptWebTest
{
public partial class _Default : System.Web.UI.Page
{
protected void previousLinkButton_Click(object sender, EventArgs e)
{
this.IncrementPage(-1);
}
protected void nextLinkButton_Click(object sender, EventArgs e)
{
this.IncrementPage(1);
}
private void IncrementPage(int increment)
{
NameValueCollection queryString;
int pageNumber;
string pdfFileName;
Pdf2Image converter;
queryString = HttpUtility.ParseQueryString
(pdfImage.ImageUrl.Substring(pdfImage.ImageUrl.IndexOf("?")));
pdfFileName = queryString["fileName"];
pageNumber = Convert.ToInt32(queryString["page"]) + increment;
converter = new Pdf2Image(this.Server.MapPath("~/" + pdfFileName));
if (pageNumber > 0 && pageNumber <= converter.PageCount)
pdfImage.ImageUrl = string.Format
("~/PdfImage.ashx?fileName={0}&page={1}", pdfFileName, pageNumber);
}
}
}
As with the image handler, this code simply extracts the file name of the PDF file and the current page number. It also creates a new instance of the Pdf2Image
class in order to obtain the number of pages in the document. If the new page number is in range, it updates the ImageUrl
of the pdfImage
causing the image handler to pull back the next page.
In Conclusion
This sample is pretty inefficient and at the very least should be caching the images. But, it's as simple an example as I can make. Hopefully, someone will find it useful. At the present time, I'm not working with the GhostScript API library so I suspect this will be the last article on the subject for the time being.