Page 1 of 1

GhostScript Helper

Posted: Sat Nov 05, 2011 2:27 pm
by srod
Hi,

thought some others might find use for this wee little helper lib for Windows. You can use this little helper to first locate the latest GhostScript installation on your user's machines and from there, either use the installed version of GhostScript (if found) or any copy of the GhostScript dll placed into any folder (without having to run the GhostScript installer) to convert a ps (post-script) file to pdf.

The helper contains 2 functions : GhostScriptHelper_GetGhostScriptInstallation() and GhostScriptHelper_ps2pdf().

The second function has essentially been lifted from Progi1984's GhostScript library which I just tweaked to make sure it would run with Unicode enabled etc. Thanks to Progi1984 for sharing his great work.

This library can of course be used to create pdf documents on the fly. Simply arrange for your app to print to a file (via a postscript printer) and then use this little helper to do the rest. All of this can easily be automated (using API). Not a substitute for using PurePDF really, but this kind of thing does have it's place.

Whatever you use this for then please make sure you stick to the terms of the GhostScript license.

Code: Select all

;/////////////////////////////////////////////////////////////////////////////////
;GhostScriptHelper.
;
;©nsxSoftware 2011.
;==============
;   Stephen Rodriguez (srod)
;   Created with Purebasic 4.6 for Windows.
;
;   Platforms:  Windows.
;   Fully Unicode compliant.
;
;Thanks to progi1984 for his GhostScript library from which I stole the ps2pdf routine and converted to Unicode etc.
;/////////////////////////////////////////////////////////////////////////////////


;/////////////////////////////////////////////////////////////////////////////////
;The following function attempts to locate the GhostScript library on the user's computer. This assumes that the GhostScript installer was used.
;If successful, returns the full path (including the library name) of the latest version of the library found on the machine,
;otherwise an empty string is returned.
;Failure does not necessarily mean that the library is not present - just that we cannot locate it.
Procedure.s GhostScriptHelper_GetGhostScriptInstallation()
  Protected result$, hKey1, numSubkeys, maxSubKeyLen, nameBuffer, i, t1, t1$, key$, latestKey, latestKey$
  Protected bufferSize, type
  ;Attempt to open the relevant registry parent key.
  If RegOpenKeyEx_(#HKEY_LOCAL_MACHINE, "Software\GPL Ghostscript", 0, #KEY_READ, @hKey1) = #ERROR_SUCCESS And hKey1
    If RegQueryInfoKey_(hKey1, 0, 0, 0, @numSubkeys, @maxSubKeyLen, 0, 0, 0, 0, 0, 0) = #ERROR_SUCCESS
      If numSubkeys And maxSubKeyLen
        nameBuffer = AllocateMemory((maxSubKeyLen+1)<<(SizeOf(CHARACTER)-1))
        If nameBuffer 
          For i = 0 To numSubkeys - 1
            t1 = maxSubKeyLen+1
            RegEnumKeyEx_(hKey1, i, nameBuffer, @t1, 0, 0, 0, 0)
            key$ = PeekS(nameBuffer)
            If key$
              t1$ = RemoveString(key$, ".")
              t1 = Val(key$)
              If t1 > latestKey
                latestKey = t1
                latestKey$ = key$
              EndIf
            EndIf
          Next
          FreeMemory(nameBuffer)        
          If latestKey$
            latestKey$ = "Software\GPL Ghostscript\" + latestKey$
          EndIf
        EndIf
      EndIf
    EndIf
    RegCloseKey_(hKey1)
    ;latestKey$ contains the subkey name of the relevant registry entry. We just have to read it's value.
    If latestKey$
      If RegOpenKeyEx_(#HKEY_LOCAL_MACHINE, latestKey$, 0, #KEY_READ, @hKey1) = #ERROR_SUCCESS And hKey1
        RegQueryValueEx_(hKey1, "GS_DLL", 0, @type, 0, @bufferSize)
        If type = #REG_SZ And bufferSize
          nameBuffer = AllocateMemory(buffersize)
          If nameBuffer
            If RegQueryValueEx_(hKey1, "GS_DLL", 0, 0, nameBuffer, @bufferSize) = #ERROR_SUCCESS
              result$ = PeekS(nameBuffer)
            EndIf
            FreeMemory(nameBuffer)
          EndIf
        EndIf
        RegCloseKey_(hKey1)
      EndIf
    EndIf
  EndIf
  ProcedureReturn result$
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////


;/////////////////////////////////////////////////////////////////////////////////
;The following uses GhostScript to convert a ps file to pdf.
;ghostScript$ should contain the complete path to the GhostScript dll. Either use a local copy of the dll stored in the application
;folder or, if prefering to use the GhostScript installation itself, try locating the GhostScript installation folder using the 
;above GhostScriptHelper_GetGhostScriptInstallation() function.
;Returns zero if an error.
Procedure.i GhostScriptHelper_ps2pdf(ghostScript$, inputFile$, outputFile$="")
  Protected result, libID, t1$, gsargc = 10, code, instance, utf8Buffer, *ptr
  Protected Dim  gsargv.i(gsargc)
  If ghostScript$ And inputFile$ And FileSize(inputFile$) > 0
    If outputFile$ = ""
      t1$ = GetExtensionPart(inputFile$)
      If t1$
        outputFile$ = ReplaceString(inputFile$, t1$, "pdf")
      Else
        outputFile$ = inputFile$ + ".pdf"
      EndIf
    EndIf
    ;Create a UTF-8 array of command strings.
      utf8Buffer = AllocateMemory(2048)
    If utf8Buffer
      *ptr = utf8Buffer
      gsargv(0) = ?GhostScript_ps2pdf_ps2pdf
      gsargv(1) = ?GhostScript_ps2pdf_dNOPAUSE
      gsargv(2) = ?GhostScript_ps2pdf_dBATCH
      gsargv(3) = ?GhostScript_ps2pdf_dSAFER
      gsargv(4) = ?GhostScript_ps2pdf_sDEVICEpdfwrite
      gsargv(6) = ?GhostScript_ps2pdf_C
      gsargv(7) = ?GhostScript_ps2pdf_setpdfwrite
      gsargv(8) = ?GhostScript_ps2pdf_F
      PokeS(*ptr, "-sOutputFile="+outputFile$, -1, #PB_UTF8)
      gsargv(5) = *ptr : *ptr + StringByteLength("-sOutputFile="+outputFile$, #PB_UTF8) + 1
      PokeS(*ptr, inputFile$, -1, #PB_UTF8)
      gsargv(9) = *ptr
      libID = OpenLibrary(#PB_Any, ghostScript$)
      If libID
        code = CallFunction(libID, "gsapi_new_instance", @instance, #Null)
        If code >= 0
          code = CallFunction(libID, "gsapi_init_with_args", instance, gsargc, @gsargv())
          If code >=0
            result = #True
          EndIf
          CallFunction(libID, "gsapi_exit", instance)
          CallFunction(libID, "gsapi_delete_instance", instance)
        EndIf  
        CloseLibrary(libID)  
      EndIf
      FreeMemory(utf8Buffer)
    EndIf
  EndIf
  ProcedureReturn result
EndProcedure
;/////////////////////////////////////////////////////////////////////////////////


;/////////////////////////////////////////////////////////////////////////////////
;DATA SECTION (UTF8 STRINGS).
  DataSection
    GhostScript_ps2pdf_ps2pdf: ;'ps2pdf'
      Data.a 112, 115, 50, 112, 100, 102, 0
    GhostScript_ps2pdf_dNOPAUSE: ;'-dNOPAUSE'
      Data.a 45, 100, 78, 79, 80, 65, 85, 83, 69, 0
    GhostScript_ps2pdf_dBATCH: ;'-dBATCH'
      Data.a 45, 100, 66, 65, 84, 67, 72, 0
    GhostScript_ps2pdf_dSAFER: ;'-dSAFER'
      Data.a 45, 100, 83, 65, 70, 69, 82, 0
    GhostScript_ps2pdf_sDEVICEpdfwrite: ;'-sDEVICE=pdfwrite'
      Data.a 45, 115, 68, 69, 86, 73, 67, 69, 61, 112, 100, 102, 119, 114, 105, 116, 101, 0
    GhostScript_ps2pdf_C: ;'-c'
      Data.a 45, 99, 0
    GhostScript_ps2pdf_setpdfwrite: ;'.setpdfwrite'
      Data.a 46, 115, 101, 116, 112, 100, 102, 119, 114, 105, 116, 101, 0
    GhostScript_ps2pdf_F: ;'-f'
      Data.a 45, 102, 0
  EndDataSection
;/////////////////////////////////////////////////////////////////////////////////



Demo (place a postscript file 'test.ps' into your app's folder) :

Code: Select all

;/////////////////////////////////////////////////////////////////////////////////
;GhostScriptHelper demo program
;/////////////////////////////////////////////////////////////////////////////////

XIncludeFile "GhostScriptHelper.pbi"

ghostScript$ = GhostScriptHelper_GetGhostScriptInstallation()

Debug ghostScript$

If GhostScriptHelper_ps2pdf(ghostScript$, "test.ps")
  MessageRequester("GhostScript Helper", "Conversion of 'test.ps' to PDF successful.")
Else
  MessageRequester("GhostScript Helper", "Conversion of 'test.ps' to PDF unsuccessful.")
EndIf

Re: GhostScript Helper

Posted: Tue Nov 08, 2011 1:25 am
by IdeasVacuum
...Thanks for sharing this srod, works 'straight out of the box'. I think some of the virtual PDF printers use this method. Unfortunately, my results lose at least the top of a document (approx first 48 pixels in Y) and can lose a similar vertical strip too.

Re: GhostScript Helper

Posted: Tue Nov 08, 2011 10:27 am
by srod
Perhaps that is the physical offsets employed by the particular printer/driver you are using which are then getting in the way of GhostScript etc. I am certainly seeing no such problems here.

You will need to check the settings of the driver you are using. My advice would be to actually use a virtual PDF printer (such as CutePDF) as the postscript printer as these will invariably be 'tweaked' as appropriate.

I'll run a few more tests however to see if I can reproduce this.

Re: GhostScript Helper

Posted: Tue Nov 08, 2011 2:04 pm
by IdeasVacuum
Hi srod

Not using a printer driver with it, just created a PS file with XnView then converted it to Pdf using your code. At this point in time, I can't see me needing to handle PS files, so don't go wasting your time for me, but I think it is the page size allocated for the Pdf - if the image is bigger than the page, it simply gets cropped.

Sample PS file produced by XnView, which round-trips OK out of/ in to XnView: http://www.professorcad.co.uk/PB_Stuff/Test.ps

Re: GhostScript Helper

Posted: Tue Nov 08, 2011 2:49 pm
by srod
Fair enough. :)

Re: GhostScript Helper

Posted: Tue Nov 08, 2011 5:17 pm
by captain_skank
This is a great piece of code srod - works exactly as advertised.

cheers