Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How To Convert PDF to Image Using Ghostscript API

0.00/5 (No votes)
28 Mar 2010 108  
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:

/// <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):

/// <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:

/// <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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here