GhostScript Helper

Share your advanced PureBasic knowledge/code with the community.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

GhostScript Helper

Post 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
I may look like a mule, but I'm not a complete ass.
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: GhostScript Helper

Post 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.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: GhostScript Helper

Post 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.
I may look like a mule, but I'm not a complete ass.
IdeasVacuum
Always Here
Always Here
Posts: 6426
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: GhostScript Helper

Post 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
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: GhostScript Helper

Post by srod »

Fair enough. :)
I may look like a mule, but I'm not a complete ass.
User avatar
captain_skank
Enthusiast
Enthusiast
Posts: 639
Joined: Fri Oct 06, 2006 3:57 pm
Location: England

Re: GhostScript Helper

Post by captain_skank »

This is a great piece of code srod - works exactly as advertised.

cheers
Post Reply