Click here to Skip to main content
15,867,594 members
Articles / Multimedia / GDI+
Article

Crossing the bridge between Ghostscript and GDI+

Rate me:
Please Sign up or sign in to vote.
4.91/5 (32 votes)
1 Dec 20027 min read 351.3K   3.2K   73   110
A C++ wrapper for the Ghostscript DLL that enables to render PS directly to GDI+ Bitmap

Sample Image - ghostwrapper.png

Introduction

This articles present a C++ interface for the Ghostscript DLL. Ghostscript[^] (GS) is a (very) famous interpretor for the Postscript language[^], it is used to render .ps files to a variety of image formats and printers. For the majority of case, using the gswin32.exe with command line parameters is good enough. However, someone might want to integrate GS into it's application with using gswin32.exe.

The solution could be using the C API interface to the DLL functions given by GS, however it is not suited for the (lazy) MSVC user: there are no available projects or workspace to use this API and you have to use make (an old UNIX souvenir) to rebuild the source (a nightmare).

To avoid all those problems, a C++ wrapper for the dll has been coded with the following features:

  • Dynamic loading of gsdll32.dll, by searching the path or the registry,
  • retrieving of all the C API methods in the dll
  • Intelligent class for the initialization of the Ghostscript engine,
  • make Ghostscript render to GDI+.
  • UNICODE compliant

Note that all the classes are documented with Doxygen.

Licensing

Before getting into work, you may take a look at this quote from the Ghostscript licensing policy:

GNU Ghostscript may not be incorporated into commercial products which forbid copying or for which customers cannot obtain source code for no more than the cost of reproduction, although it may be distributed ("aggregated") with commercial products; AFPL Ghostscript may not be incorporated into commercial products at all, and may be distributed commercially only under extremely limited circumstances. However, Ghostscript is also available for commercial licensing, which in addition to the right to incorporate Ghostscript into commercial products includes support, a limited warranty, high-quality fonts, and other benefits. For more information about commercial licensing of Ghostscript, please contact our commercial distribution partner, the only entity legally authorized to distribute Ghostscript per se on any terms other than the GNU or AFPL licenses

So if you plan to use this wrapper in a commercial applications, take a look at the note on Commercial use.

Wrapper architecture

There are 2 main classes used to build the interface to gsdll32.dll:

  • CAPI is the interface to the gsdll32.dll. It handles dll loading/unloading, method retreiving and interface,
  • CAPISettings is used to set all the format output options, flags, display callbacks and other possible parameters of the ghostscript engine. Once this object is ready, it is used to start an engine.

Here's a brief description of the other classes:

Class nameDescription
CModuleLoad, unload ghostscript library
CAPIC API interface
CAPISettingsEngine initializer
CCallbackBase class for display callbacks
CGDIpCallbackGDI+ and ghostscript interface

All the classes are in the gsdll namespace. From now on, we will consider the following:

using namespace gsdll;
// ghostscript setting
CAPISettings settings;

Make sure Ghostscript is installed on your machine before trying to use this package (tested with AFPL Ghostscript 8.0).

Initializing Ghostscript with CAPISettings

The class CAPISettings is used to set-up the Ghostscript engine.

Output device

You can choose the device in the EDevice enumeration:

// setting JPEG output
settings.SetDevice( CAPISettings::DeviceJPEG);

Here are the main output devices among all the available:

  • DevicePNG16m, 24-bit PNG,
  • DeviceBMP16m, 24-bit BMP,
  • DeviceJPEG, JPEG,
  • DevicePDF, Adobe PDF writer
  • DeviceDisplay, custom ouput device (more details about this device below)

As mentionned aboce, the user can provide GS with a custom output device type. This type of device is used to make GS render to GDI+ Bitmap object and will be discussed later.

Output file

GS also allows you to control where it sends its output. With a display device this isn't necessary as the device handles presenting the output on screen internally. Some specialized printer drivers operate this way as well, but most devices are general and need to be directed to a particular file or printer.

To send the output to a file, use the SetOutputFile method. For instance, to direct all output into the file ABC.xyz, use

settings.SetOutputFile(_T("ABC.xyz")); 

When printing on MS Windows systems, output normally goes directly to the printer, PRN. When using GS as a file rasterizer (converting PostScript or PDF to a raster image format) you will of course want to specify an appropriately named file for the output.

GS also accepts the special filename '-' which indicates the output should be writtent to stardard output (the command shell).

Be aware that filenames beginning with the character have a special meaning in PostScript. If you need to specify a file name that actually begins with , you must prepend the os% filedevice explicitly. For example to output to a file named abc, you need to specify

settings.SetOutputFile(_T("%%os%%%%abc")); 

Please see GS and the PostScript Language and the PostScript Language Reference Manual for more details on and filedevices. Note that on MS Windows systems, the character also has a special meaning for the command processor (shell), so you will have to double it.

Specifying a single output file works fine for printing and rasterizing figures, but sometimes you want images of each page of a multi-page document. You can tell GS to put each page of output in a series of similarly named files. To do this place a template '%d' in the filename which GS will replace with the page number.

You can also control the number of digits used in the file name:

settings.SetOutputFile(_T("ABC-%%d.png")); 
produces
'ABC-1.png', ... , 'ABC-10.png', ...
settings.SetOutputFile(_T("ABC-%%03d.pgm")); 
produces
'ABC-001.pgm', ... , 'ABC-010.pgm', ...
settings.SetOutputFile(_T("ABC_p%%04d.tiff")); 
produces
'ABC_p0001.tiff', ... , 'ABC_p0510.tiff', ... , 'ABC_p5238.tiff'

Generally 03d is the best option for normal documents. As noted above, on MS Windows systems, you will have to double the character.

Miscellanous options

There are several option to control the raster quality:

  • SetResolution, sets the output resolution in dpi,
  • SetTextAlphaBits, controls the text sub-sampling,
  • SetGraphicAlphaBits, controls the text sub-sampling.

If rendering to JPEG, you can set the output quality from 0 (bad quality, good compression) to 100 (good quality, bad compression):

settings.SetDevice(DeviceJPEG);
settings.SetJPEGQuality(50);

Custom options

There are plenty of available flags to control the output of GS. Some are implemented in the CAPISettings class, they will suit the basic needs. However, for the advanced user, it is possible to set custom arguments using AddCustomArgument:

settings.AddCustomArgument(_T("-sPAPERSIZE=a4"));

Starting the engine:

Once you have set all the parameters, just build a CAPI instance using the CAPISettings object in the constructor:

// build and start GS api
CAPI gsapi( settings);
if (gsapi.IsInvalid())
{
	// the api was not properly initialized
}

Using the Ghostcript engine with CAPI

Once your CAPI instance has been built successfully, you have to feed it with Postscript. You can do that in numerous ways:

  • Sending an entire file:
    gsapi.RunFile(_T("thefiletorender.ps"));
  • Sending a string
    gsapi.RunString(_T("1 2 add == flush\n"));
    Note that you can specify the number of characters to be read.
  • Sending numerous strings:
    // prepare for receiving strings
    gsapi.RunStringBegin();
    // sending string, the method RunStringContinue can be called several times
    gs.RunStringContinue(_T("more ps code"));
    // stop and render
    gsapi.RunStringEnd();	

When rasterizing, GS produces numerous messages (notification or error messages). These messages are redirected to 2 string streams (static members of CAPI):

// get the message output buffer of GS
TRACE(gsapi.GetOutBuffer());
// get the message error buffer of GS
TRACE(gsapi.GetErrBuffer());

Implementing your own ouput device with CCallback

As mentioned above, it is possible to implement your own output device. This is made by furnishing GS with a series of function callbacks. In order to help the user, these callbacks have been encapsulated into a virtual base class: CCallback .

In order to write your own device, you must derive a class from CCallback and implement all the following virtual functions:

int Open(...)New device has been opened.
int PreClose(...)Device is about to be closed.
int Close(...)Device has been closed.
int PreSize(...)Device is about to be resized.
int Size(...)Device has been resized. New pointer to raster returned argument.
int Sync(..)flushpage
int Page(...)Showpage If you want to pause on showpage, then don't return immediately.
DWORD GetFormat() constreturns the display format

Here a snipet to attach your custom device to GS:

// a custom display callback inherited from CCallback
CMyCustomCallback callback;
// attaching to GS
settings.SetDisplayCallback(&callback);

I will not go more into details about the use of those functions. For the interrested user, the creation of a custom output device is illustrated with CGDIpCallback which implements the outputing to GDI+ Bitmap.

Crossing the bridge between GS and GDI+: CGDIpCallback

This class implements the necessary callback in order to have a output device to GDI+.

When the callback Size is called, the address of the raster buffer is stored and a Bitmap is created with the appropriate dimensions.

After that, when Page is called, the raster is blitted into the Bitmap. Each time Page is called, the bitmap is stored in a bitmap list for later use. These bitmaps can be accessed using GetBitmapContainer:

CGDIpCallback callback;
// processing and rendering
...
// iterating the produced bitmaps:
CGDIpCallback::BitmapContainer& bc=callback.GetBitmapContainer();
CGDIpCallback::BitmapContainer::iterator it;
for (it=bc.begin(); it!=bc.end();++it)
{
	Bitmap* pBitmap=*it;
	// working on bitmap
	...
}

Limitations

The GS dll accepts only 1 instance running in the same thread. Hence, once an engine is started, no other can be build successfully until it is destroyed.

Demo project

The demo project is Postscript engine. Enter some postscript code and raster, if successful, the result is displayed in the dialog window.

References

[1] Ghostscript web site
[2] PostScript® Language Reference, Third Edition

Update history

  • 2-11-2002,
    • Added UNICODE support
    • Better DLL search in CModule. Thanks to Russel Lang from Ghostgum.com
    • Added licensing policy
  • 2-11-2002, initial release.

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


Written By
Engineer
United States United States
Jonathan de Halleux is Civil Engineer in Applied Mathematics. He finished his PhD in 2004 in the rainy country of Belgium. After 2 years in the Common Language Runtime (i.e. .net), he is now working at Microsoft Research on Pex (http://research.microsoft.com/pex).

Comments and Discussions

 
GeneralRe: How To Extract a Specific PDF page using GhostScript Pin
apey5-Apr-04 3:42
apey5-Apr-04 3:42 
GeneralRe: How To Extract a Specific PDF page using GhostScript Pin
Anonymous19-May-05 18:53
Anonymous19-May-05 18:53 
QuestionSplit? Pin
NotProfessional18-Nov-03 6:51
NotProfessional18-Nov-03 6:51 
AnswerRe: Split? Pin
NotProfessional18-Nov-03 10:38
NotProfessional18-Nov-03 10:38 
GeneralFormat Conversion Pin
Darren Schroeder11-Nov-03 8:53
Darren Schroeder11-Nov-03 8:53 
GeneralRe: Format Conversion Pin
Anonymous11-Nov-03 20:55
Anonymous11-Nov-03 20:55 
GeneralRe: Format Conversion Pin
Darren Schroeder12-Nov-03 2:04
Darren Schroeder12-Nov-03 2:04 
GeneralRe: Format Conversion Pin
Jonathan de Halleux13-Nov-03 1:18
Jonathan de Halleux13-Nov-03 1:18 
GeneralRe: Format Conversion Pin
Darren Schroeder13-Nov-03 2:04
Darren Schroeder13-Nov-03 2:04 
GeneralRe: Format Conversion Pin
Jonathan de Halleux13-Nov-03 2:34
Jonathan de Halleux13-Nov-03 2:34 
GeneralTry this PS code. It's just cool. Pin
yevhen20-Aug-03 20:56
yevhen20-Aug-03 20:56 
GeneralProtect PDF-File Pin
Harald Geißelhart18-Aug-03 3:18
Harald Geißelhart18-Aug-03 3:18 
GeneralRe: Protect PDF-File Pin
Jonathan de Halleux24-Aug-03 0:03
Jonathan de Halleux24-Aug-03 0:03 
GeneralGreat class lib - found little bug Pin
Andreas Walkenhorst30-Apr-03 9:18
Andreas Walkenhorst30-Apr-03 9:18 
GeneralFix Pin
Jonathan de Halleux1-May-03 22:17
Jonathan de Halleux1-May-03 22:17 
Questionanybody know how GhsotScript use window icm profile? Pin
menbal20-Apr-03 20:46
menbal20-Apr-03 20:46 
AnswerRe: anybody know how GhsotScript use window icm profile? Pin
Jonathan de Halleux1-May-03 4:42
Jonathan de Halleux1-May-03 4:42 
GeneralPDF = 0kb Pin
auk_ie7-Apr-03 10:47
auk_ie7-Apr-03 10:47 
GeneralRe: PDF = 0kb Pin
auk_ie7-Apr-03 13:32
auk_ie7-Apr-03 13:32 
GeneralRe: PDF = 0kb Pin
auk_ie7-Apr-03 14:25
auk_ie7-Apr-03 14:25 
GeneralProblem compiling Pin
jsanjosembet.es7-Apr-03 0:15
jsanjosembet.es7-Apr-03 0:15 
GeneralSDK Pin
Jonathan de Halleux7-Apr-03 0:25
Jonathan de Halleux7-Apr-03 0:25 
GeneralRe: SDK Pin
jsanjosembet.es7-Apr-03 0:50
jsanjosembet.es7-Apr-03 0:50 
GeneralRe: SDK Pin
Jonathan de Halleux7-Apr-03 1:24
Jonathan de Halleux7-Apr-03 1:24 
GeneralRe: SDK Pin
jsanjosembet.es7-Apr-03 1:34
jsanjosembet.es7-Apr-03 1:34 

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.