Updated to work with GPL and AFPL versions for the reg lookup- 3/21/2005

I decided that Web Connection could use support for GhostScript today as part of its series of supported PDF drivers etc. GhostScript is open source so it's (sort of) free - as with all open source free, doesn't mean really free - commercial use still requires payment. GhostView provides a ton of functionality in dealing with PostScript and PDF manipulation. However, the one I have been interested in is PostScript to PDF conversion which allows you to basically print from any printer to a PostScript File and convert that file into a PDF document.

 

I have to say I’m always underwhelmed by the documentation of these open source kind of tools. The documentation is always messy and the examples always assume you want to compile the whole source tree, which in most cases is not at all what you want - I suspect most people just want to call into the existing functionality from their own applications. Digging around for a few integration samples turned up all sorts of huge projects that put wrappers for all functionality ontop of GhostScript.

 

My needs are a bit simpler: I simply want to call the GhostScript DLL – dynamically via a simple DLL interface without having to compile all of GhostScript into my wrapper DLL. I basically want to create a small wrapper DLL function that can be called via Interop from any language – specifically in my case from Visual FoxPro.

 

To do this I created this simple DLL function that I’ve now added to wwIPStuff.dll which already ships with Web Connection:

 

#include <windows.h>

 

BOOL WINAPI GhostPsToPdf(char *PsFile, char *PdfFile,

                         char *Resolution, char *PaperSize, char *GsDll )

{

      HMODULE hModule = NULL;

      char *DllPath;

 

      if (!GsDll)

      {

            HKEY hReg;

            char Path[MAX_PATH] ;

            DWORD Size = MAX_PATH;

                 

            char RegPath[MAX_PATH] = "SOFTWARE\\AFPL Ghostscript";

 

            if (RegOpenKey(HKEY_LOCAL_MACHINE,RegPath,&hReg) != ERROR_SUCCESS)

            {

                  strcpy(RegPath,"SOFTWARE\\GPL Ghostscript");

                  if (RegOpenKey(HKEY_LOCAL_MACHINE,RegPath,&hReg) != ERROR_SUCCESS)

                        return FALSE;

            }

           

            // *** Grab the first sub-key

            char Version[40]; // just to be safe

            DWORD VerSize=9;

            if (RegEnumKeyEx(hReg,0,Version,&VerSize,NULL,NULL,NULL,NULL) != ERROR_SUCCESS)

            {

                  RegCloseKey(hReg);

                  return FALSE;

            }

            RegCloseKey(hReg);

           

 

            // *** Add to the main path and open the subkey

            strcat(RegPath,"\\");

            strcat(RegPath,Version);

            if (RegOpenKey(HKEY_LOCAL_MACHINE,RegPath,&hReg) != ERROR_SUCCESS)

                  return FALSE;

     

            DWORD RegType = REG_SZ;

            if (RegQueryValueEx(hReg,"GS_DLL",0,&RegType,(LPBYTE) Path,&Size) != ERROR_SUCCESS)

            {

                  RegCloseKey(hReg);           

                  return FALSE;

            }

           

            DllPath = Path;

 

            RegCloseKey(hReg);

      }

      else

            DllPath = GsDll;

 

   hModule = LoadLibrary(DllPath);

   if (!hModule)

         return FALSE;

 

      if (!Resolution)

      {

            char cResolution[8] = "150x150";

            Resolution = cResolution;

      }

      char ArgPaperSize[50] = "-sPAPERSIZE=letter";

      if (PaperSize) 

      {

            strcpy(ArgPaperSize,"-sPAPERSIZE=");

            strcat(ArgPaperSize,PaperSize);

      }

 

     

     

   // *** Declare all the dynamic typedef function pointers

   typedef int (WINAPI *gsapi_new_instance)( char **, int );

   gsapi_new_instance lp_gsapi_new_instance ;

   lp_gsapi_new_instance = (gsapi_new_instance) GetProcAddress(hModule,"gsapi_new_instance");

 

   typedef int (WINAPI *gsapi_delete_instance) (char *);

   gsapi_delete_instance lp_gsapi_delete_instance;

   lp_gsapi_delete_instance = (gsapi_delete_instance) GetProcAddress(hModule,"gsapi_delete_instance");

 

   typedef int (WINAPI *gsapi_init_with_args) (char *,int,char *);

   gsapi_init_with_args lp_gsapi_init_with_args;

   lp_gsapi_init_with_args = (gsapi_init_with_args) GetProcAddress(hModule,"gsapi_init_with_args");

 

   typedef int (WINAPI *gsapi_exit) (char *);

   gsapi_exit lp_gsapi_exit;

   lp_gsapi_exit = (gsapi_exit) GetProcAddress(hModule,"gsapi_exit");

 

   char ArgResolution[80] = "-r";

   strcat(ArgResolution,Resolution);

 

   char ArgOutputFile[MAX_PATH + 16] = "-sOutputFile=";

   strcat(ArgOutputFile,PdfFile);

 

      // *** Set up the parameters to the engine 

    const char * gsargv[11];

    int gsargc;

    gsargv[0] = "PsToPdf";    /* actual value doesn't matter */

    gsargv[1] = "-dNOPAUSE";  // no prompts

    gsargv[2] = "-dBATCH";    // exit after processing

    gsargv[3] = "-dSAFER";    // Safe mode

      gsargv[4] = ArgResolution;

      gsargv[5] = ArgPaperSize;

      gsargv[6] = "-sDEVICE=pdfwrite"; 

      gsargv[7] = ArgOutputFile;

    gsargv[8] = "-c";

    gsargv[9] = ".setpdfwrite";

    gsargv[10] = "-f";

      gsargv[11] = PsFile;

    gsargc=12;

 

      // *** Simulate pointer to struct with plain char *

      char *Inst = NULL;

      int code = 0;

 

      code = (lp_gsapi_new_instance)(&Inst,NULL);

      if (code == 0)

      {

            // Do the conversion

            code = (lp_gsapi_init_with_args) (Inst,gsargc,(char *) gsargv );

            if (code == 0)

                  code = (lp_gsapi_exit) (Inst);

           

            // *** Release the handle

            (lp_gsapi_delete_instance) (Inst);

      }

 

      if (hModule)

            FreeLibrary(hModule);

 

      if (code == 0)

            return TRUE;

 

      return FALSE;

}

 

This code is obviously Windows specific since it uses the registry to figure out where the DLL lives. Note also that there’s a parameter to allow you to pass in the DLL name directly just in case the registry is not accessible (it requires Admin rights) or in case the registry entries aren’t there to start with.

 

Please excuse my sloppy C++ - whenever I do C++ I’m reminded that I’m soooo glad I don’t have to write C++ code and deal with pointers to pointers on a regular basis. Ughhh

 

Note in order for this to work you need to have GhostScript installed and you probably will want to install a Postscript printer (like the Apple Color LW 12/660 PS ) to print reports or documents to PostScript first.

FWIW, I also found some VFP code that does this all in native Fox code and ironically that’s what ultimately set me on the right path to creating the self-contained DLL code shown here. It's Print2PDF.prg, but heck I can’t find the damn Web link now and there’s no link reference in the source… Here’s an FTP site where you can download the code though - it includes the actual PDF conversion class as well as Ed Rauh's Heap class that deals with managing the parameter structure pointer in the above code.

 

I ended up not using the pure Fox code because it has some dependencies I didn’t want to pull into Web Connection and because this functionality is useful for other environments as well.

 

Hope this helps somebody out.