Click here to Skip to main content
15,891,136 members
Articles / Programming Languages / C#

How To Convert PDF to Image Using Ghostscript API

Rate me:
Please Sign up or sign in to vote.
4.89/5 (76 votes)
28 Mar 2010CPOL4 min read 2.1M   45.3K   229   405
How to use Ghostscript library to create an image (or images) from a PDF file
ConvertPdf.png

Where to Download the Required Library

You will need at least GhostScript 8.64 (other versions have a problem with Vista). After you have installed the program, just copy the gs32dll.dll from the installation directory (the bin subdirectory) to the directory where you have the EXE of my program.

Requirements

The program REQUIRES the DLL of Ghostscript, it can be retrieved from the Ghostscript website.

Introduction

Often, I found the need to perform a conversion from a PDF to an image format.

Be it TIF, JPG or whatever format (I strongly suggest to convert PDF to PNG and NOT to JPEG since PNG is MUCH smaller and much better (since it has no information losses). Try for yourself and you will see!)

I found many programs and controls that allow me to do it but they are all expensive or incomplete for my needs. Since I know that Ghostscript performs this kind of work pretty well, I looked for a way to automate a simple conversion.

Background

To perform a conversion, I needed to pass several commands to the Ghostscript interpreter. I must convert a Unicode string to a null terminated ANSI string for Ghostscript. The result is stored in a byte array.

The parameters that we will provide the library are the same and in the same order that we should provide from the command line. So in case any modification attempt should fail on this project, be sure that they are working from the command line!

For a comprehensive list of all the parameters and their meanings, I suggest you read: How to use Ghostscript.

How to Interface with the Ghostscript Library

The functions that are needed to call the library must be invoked using P/Invoke:

C#
/// <summary>Create a new instance of Ghostscript.</summary>
/// <param name="pinstance"></param>
/// <param name="caller_handle"></param>
/// <returns>The instance passed to other GS function</returns>
[DllImport("gsdll32.dll", EntryPoint="gsapi_new_instance")]
private static extern int gsapi_new_instance (out IntPtr pinstance,
    IntPtr caller_handle);
 
/// <summary>This will make the conversion</summary>
/// <param name="instance"></param><param name="argc"></param><param name="argv"></param>
/// <returns>0 if is ok</returns>
[DllImport("gsdll32.dll", EntryPoint="gsapi_init_with_args")]
private static extern int gsapi_init_with_args (IntPtr instance, int argc, IntPtr argv);
/// <summary>Exit the interpreter</summary>
/// <param name="instance"></param><returns></returns>
[DllImport("gsdll32.dll", EntryPoint="gsapi_exit")]
private static extern int gsapi_exit (IntPtr instance);
 
/// <summary>Destroy an instance of Ghostscript.</summary>
/// <param name="instance"></param>
[DllImport("gsdll32.dll", EntryPoint="gsapi_delete_instance")]
private static extern void gsapi_delete_instance (IntPtr instance);

Now that we have this function, we MUST call them in this order:

  1. gsapi_new_instance
  2. gsapi_init_with_args
  3. gsapi_exit
  4. gsapi_delete_instance

Pay attention to the last two, it is a common mistake to invert them!

Now how to call it. (In the real code, there are also parameter checks, but I skip them here for simplicity):

C#
/// <summary>Convert a single file!</summary>
/// <param name="inputFile">The file PDf to convert</param>
/// <param name="outputFile">The image file that will be created</param>
/// <returns>True if the conversion succeeds!</returns>
public bool Convert(string inputFile,string outputFile)
{
    //These are the variables that I'm going to use
    int intReturn,intCounter,intElementCount;
    IntPtr intGSInstanceHandle;
    object[] aAnsiArgs;
    IntPtr[] aPtrArgs;
    GCHandle[] aGCHandle;
    IntPtr callerHandle, intptrArgs;
    GCHandle gchandleArgs;
    //Generate the list of the parameters
    string[] sArgs = GetGeneratedArgs(inputFile,outputFile);
    // Convert the Unicode strings to null terminated ANSI byte arrays
    // then get pointers to the byte arrays.
    intElementCount = sArgs.Length;
    aAnsiArgs = new object[intElementCount];
    aPtrArgs = new IntPtr[intElementCount];
    aGCHandle = new GCHandle[intElementCount];
    //Convert the parameters
    for(intCounter = 0; intCounter< intElementCount; intCounter++)
    {
        aAnsiArgs[intCounter] = StringToAnsiZ(sArgs[intCounter]);
        aGCHandle[intCounter] =
                 GCHandle.Alloc(aAnsiArgs[intCounter], GCHandleType.Pinned);
        aPtrArgs[intCounter] = aGCHandle[intCounter].AddrOfPinnedObject();
    }
    gchandleArgs = GCHandle.Alloc(aPtrArgs, GCHandleType.Pinned);
    intptrArgs = gchandleArgs.AddrOfPinnedObject();
    //Create a new instance of the library!
    try
    {
        intReturn = gsapi_new_instance(out intGSInstanceHandle, _objHandle);
        //Be sure that we create an instance!
        if (intReturn < 0)
        {
            MessageBox.Show("I can't create a new instance of Ghostscript
                                   please verify no other instance are running!");
            //Here you should also clean the memory
            ClearParameters(ref aGCHandle,ref gchandleArgs);
            return false;        }
    }
    catch (DllNotFoundException ex)
    {//in this case the DLL we are using is not the DLL we expect
        MessageBox.Show("The gs32dll.dll in the program directory
                 doesn't expose the methods i need!
                 \nplease download the version 8.63 from the original website!");
        return false;
    }
    callerHandle = IntPtr.Zero;//remove unwanted handler
    intReturn = -1;//if nothing changes, it is an error!
    //Ok now is the time to call the interesting module
    try {intReturn =
         gsapi_init_with_args(intGSInstanceHandle, intElementCount, intptrArgs);}
    catch (Exception ex)  { MessageBox.Show(ex.Message);}
    finally//No matter what happens, I MUST close the instance!
    {   //free all the memory
        ClearParameters(ref aGCHandle,ref gchandleArgs);
        gchandleArgs.Free();
        gsapi_exit(intGSInstanceHandle);//Close the instance
        gsapi_delete_instance(intGSInstanceHandle);//delete it
    }
    //Conversion was successful if return code was 0 or e_Quit
    return (intReturn == 0) | (intReturn == e_Quit);//e_Quit = -101
}

How to Call the Library We Just Created

Using this library is pretty simple.

  1. Create an instance of the class
  2. Provide the parameters that are passed as properties (this is optional now)
  3. Call the function: "Convert" with input and output name (optionally even the parameters)

Here there is an example:

C#
/// <summary>Convert a single file</summary>
/// <remarks>this function PRETEND that the filename is right!</remarks>
private void ConvertSingleImage(string filename)
{
    //Setup the converter
    converter.FirstPageToConvert = (int)numericFirstPage.Value;
    converter.LastPageToConvert = (int)numericLastPage.Value;
    converter.FitPage = checkFitTopage.Checked;
    converter.JPEGQuality = (int)numQuality.Value;
    converter.OutputFormat = comboFormat.Text;
    System.IO.FileInfo input = new FileInfo(filename);
    string output = string.Format("{0}\\{1}{2}",
                          input.Directory,input.Name,txtExtension.Text);
    //If the output file exists already, be sure to add a
    //random name at the end until it is unique!
    while (File.Exists(output))
        output = output.Replace(txtExtension.Text,
         string.Format("{1}{0}", txtExtension.Text,DateTime.Now.Ticks));
    txtArguments.Text = converter.ParametersUsed;
    if (converter.Convert(input.FullName, output) == true)
        lblInfo.Text = string.Format("{0}:File converted!",
                          DateTime.Now.ToShortTimeString());
    else
        lblInfo.Text = string.Format("{0}:File NOT converted!
                          Check Args!", DateTime.Now.ToShortTimeString());
}

Remark About the Library

Ghostscript isn't threadsafe, if you call it more than once, you MUST provide a lock system to be sure you are not calling two instances at the same time.

How to Use This as a Library

Since I have seen a lot of problems regarding how to use this library, I split my project in 2 main projects, one is a DLL (PDFToImage.dll) and the other one is the simple GUI of the DLL.

To use it in an ASP page, YOU MUST set the property "ThrowException" to true and now the library will only throw an exception on error and not show any Messagebox (that you could not want on an ASP page for example).

In case your needs are different from mine, I added a way to pass the library directly the parameters you want. In this case, you will only have to provide input, output name and a string with the parameters as usual in the same form and order then you would provide the command line.

If you want to use this library in an ASP.NET page, you MUST copy both PDFToImage.dll and gs32dll.dll in the BIN directory of your solution!

How to Debug Problems

The program displays a nice arguments list that will help you to find why a file doesn't convert! Open a command prompt, enter the directory where you installed Ghostscript and execute the file gs32win.exe with the parameters as expressed in the textbox (you should only add " to enclose the path of the file that are the last 2 arguments).

You will see how the real Ghostscript would react to it, so you will understand why an error occurred!

History

  • 1.0.3 (2008 January): Initial version
  • 1.1.0 (2009 March 25): Made it possible to use as DLL and to pass other parameters
  • 1.1.1 (2009 March 26): Fixed an International issue, thanks to tchu_2000
  • 1.1.2b (2009 March 27): Fixed multiple page output, and cleaned up the International convention
  • 1.1.3 (2009 April 4): Fixed duplicated parameter, added new parameters option (PageSize, Multithreads support, AntiAlaising and so on)
  • 1.2 (2009 November 17): Fixed bug in parameter (thanks Barabara), added more font Options (Thanks Davalv), added Mutex to avoid Concurrency issue (Thanks Davalv), added recognition of 64bit problems

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: Could not load file or assembly 'PdfToImage' or one of its dependencies. This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded. Pin
Lord TaGoH22-Jun-10 2:45
Lord TaGoH22-Jun-10 2:45 
GeneralRe: Could not load file or assembly 'PdfToImage' or one of its dependencies. This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded. Pin
rasmus.christiansen@gmail.com22-Jun-10 3:01
rasmus.christiansen@gmail.com22-Jun-10 3:01 
GeneralRe: Could not load file or assembly 'PdfToImage' or one of its dependencies. This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded. Pin
Lord TaGoH22-Jun-10 9:40
Lord TaGoH22-Jun-10 9:40 
GeneralQuality of PNG16m is low Pin
MatejCerk21-Apr-10 0:09
MatejCerk21-Apr-10 0:09 
GeneralRe: Quality of PNG16m is low Pin
Lord TaGoH23-Apr-10 5:31
Lord TaGoH23-Apr-10 5:31 
GeneralRe: Quality of PNG16m is low Pin
Mohammad Sanati30-Apr-10 14:39
Mohammad Sanati30-Apr-10 14:39 
GeneralRe: Quality of PNG16m is low Pin
jgricci5-Oct-10 8:21
jgricci5-Oct-10 8:21 
GeneralConvert to VB Pin
chuck sndyer20-Apr-10 6:58
chuck sndyer20-Apr-10 6:58 
Converted the code to VB and using it in an aspx application.

In the aspx.vb page I simplified the call:
Private converter As New PDFConvert()
Private Sub ConvertPDF(ByVal filename As String)

   Dim Converted As Boolean = False
   'Setup the converter
   converter.OutputToMultipleFile = False
   converter.FirstPageToConvert = 1
   converter.LastPageToConvert = 1
   converter.FitPage = False
   converter.JPEGQuality = 10
   converter.OutputFormat = "png16m"

   Dim input As System.IO.FileInfo = New FileInfo(filename)
   Dim OutputFileName As String = "~\images\" & Path.ChangeExtension(filename, "png")

   Dim output As String = String.Format("{0}\{1}{2}", input.Directory, input.Name, "png")
   If File.Exists(OutputFileName) Then
      File.Delete(OutputFileName)
   End If

   Converted = converter.Convert(input.FullName, OutputFileName)

End Sub


But when I get the ExecuteGhostScriptCommand function I'm getting this error message:
"An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B)"
when the code reaches the "intReturn" line.
Try
    <big>intReturn = gsapi_new_instance(intGSInstanceHandle, _objHandle)</big>
     'Be sure that we create an istance!
     If intReturn < 0 Then
       ClearParameters(aGCHandle, gchandleArgs)
       Throw New ApplicationException("I can't create a new instance of Ghostscript 
          please verify no other instance are running!")
      End If
  Catch formatException As BadImageFormatException
       '99.9% of time i'm just loading a 32bit dll in a 64bit enviroment!
       ClearParameters(aGCHandle, gchandleArgs)
       'Check if i'm in a 64bit enviroment or a 32bit
       If IntPtr.Size = 8 Then
          ' 8 * 8 = 64
          Throw New ApplicationException(String.Format("The gsdll32.dll you provide is 
            not compatible with the current architecture that is 64bit," & "Please
            download any version above version 8.64 from the original website in the 
            64bit or x64 or AMD64 version!"))
       ElseIf IntPtr.Size = 4 Then
          ' 4 * 8 = 32
          Throw New ApplicationException(String.Format("The gsdll32.dll you provide is 
          not compatible with the current architecture that is 32bit," & "Please 
          download any version above version 8.64 from the original website in the 
          32bit or x86 or i386 version!"))
       End If
  Catch ex As DllNotFoundException
      'in this case the dll we r using isn't the dll we expect
       ClearParameters(aGCHandle, gchandleArgs)
       Throw New ApplicationException("The gsdll32.dll wasn't found in default dlls 
          search path" & "or is not in correct version (doesn't expose the 
          required methods). Please download " & "at least the version 8.64 from 
          the original website")
  End Try


Any suggestions as to what I should change to make it work correctly??

chuck snyder
GeneralRe: Convert to VB Pin
MatejCerk20-Apr-10 23:51
MatejCerk20-Apr-10 23:51 
GeneralRe: Convert to VB Pin
Lord TaGoH23-Apr-10 5:35
Lord TaGoH23-Apr-10 5:35 
QuestionGhostScript redistribution Pin
Marek.T30-Mar-10 0:28
Marek.T30-Mar-10 0:28 
AnswerRe: GhostScript redistribution Pin
Lord TaGoH30-Mar-10 9:08
Lord TaGoH30-Mar-10 9:08 
AnswerRe: GhostScript redistribution Pin
nardev4-Apr-10 1:38
nardev4-Apr-10 1:38 
AnswerRe: GhostScript redistribution Pin
Dan Randolph20-Apr-12 11:25
Dan Randolph20-Apr-12 11:25 
GeneralTransparency Pin
Scotty8624-Mar-10 2:14
Scotty8624-Mar-10 2:14 
GeneralRe: Transparency Pin
Lord TaGoH24-Mar-10 6:35
Lord TaGoH24-Mar-10 6:35 
GeneralRe: Transparency Pin
Scotty8625-Mar-10 0:16
Scotty8625-Mar-10 0:16 
GeneralRe: Transparency Pin
Lord TaGoH25-Mar-10 5:58
Lord TaGoH25-Mar-10 5:58 
GeneralRe: Transparency Pin
Scotty8625-Mar-10 6:20
Scotty8625-Mar-10 6:20 
GeneralRe: Transparency Pin
Lord TaGoH25-Mar-10 7:50
Lord TaGoH25-Mar-10 7:50 
GeneralRe: Transparency Pin
Scotty8625-Mar-10 23:18
Scotty8625-Mar-10 23:18 
GeneralRe: Transparency Pin
Lord TaGoH27-Mar-10 1:41
Lord TaGoH27-Mar-10 1:41 
GeneralRe: Transparency [modified] Pin
Scotty8628-Mar-10 23:40
Scotty8628-Mar-10 23:40 
GeneralRe: Transparency Pin
Haspel29-Mar-10 0:51
Haspel29-Mar-10 0:51 
GeneralRe: Transparency [modified] Pin
Scotty8629-Mar-10 1:00
Scotty8629-Mar-10 1:00 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.