Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Using 64-bits Ghostscript dll with Unity

Discussion in 'Scripting' started by CipherZero, Jun 6, 2014.

  1. CipherZero

    CipherZero

    Joined:
    Jul 10, 2013
    Posts:
    38
    Hello everyone
    I'm trying to integrate the ghostscript dll into my project. I've followed the instruction, copying the dll file from ghostscript install path and put it under "Assets/Plugins" in my project folder(please be noted that I'm using a 64-bits version of ghostscript, that makes the dll as "gsdll64.dll"). However when I play my scene, it always give a "DllNotFoundException". The following is the sample code I found on the Internet(and I'm currently using it) of including the dll file.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System;
    4. using System.Collections.Generic;
    5. using System.Text;
    6. using System.Runtime.InteropServices;
    7. using System.Collections;
    8. /**
    9. Convert PDF to Image Format(JPEG) using Ghostscript API
    10.  
    11. convert a pdf to jpeg using ghostscript command line:
    12. gswin32c -q -dQUIET -dPARANOIDSAFER  -dBATCH -dNOPAUSE  -dNOPROMPT -dMaxBitmap=500000000 -dFirstPage=1 -dAlignToPixels=0 -dGridFitTT=0 -sDEVICE=jpeg -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -r100x100 -sOutputFile=output.jpg test.pdf
    13. see also:http://www.mattephraim.com/blog/2009/01/06/a-simple-c-wrapper-for-ghostscript/
    14. and: http://www.codeproject.com/KB/cs/GhostScriptUseWithCSharp.aspx
    15. Note:copy gsdll32.dll to system32 directory before using this ghostscript wrapper.
    16.  *
    17.  */
    18. namespace ConvertPDF
    19. {
    20.     class PDFConvert
    21.     {
    22.         #region GhostScript Import
    23.         /// <summary>Create a new instance of Ghostscript. This instance is passed to most other gsapi functions. The caller_handle will be provided to callback functions.
    24.         ///  At this stage, Ghostscript supports only one instance. </summary>
    25.         /// <param name="pinstance"></param>
    26.         /// <param name="caller_handle"></param>
    27.         /// <returns></returns>
    28.         [DllImport("gsdll64.dll", EntryPoint="gsapi_new_instance")]
    29.         private static extern int gsapi_new_instance (out IntPtr pinstance, IntPtr caller_handle);
    30.         /// <summary>This is the important function that will perform the conversion</summary>
    31.         /// <param name="instance"></param>
    32.         /// <param name="argc"></param>
    33.         /// <param name="argv"></param>
    34.         /// <returns></returns>
    35.         [DllImport("gsdll64.dll", EntryPoint="gsapi_init_with_args")]
    36.         private static extern int gsapi_init_with_args (IntPtr instance, int argc, IntPtr argv);
    37.         /// <summary>
    38.         /// Exit the interpreter. This must be called on shutdown if gsapi_init_with_args() has been called, and just before gsapi_delete_instance().
    39.         /// </summary>
    40.         /// <param name="instance"></param>
    41.         /// <returns></returns>
    42.         [DllImport("gsdll64.dll", EntryPoint="gsapi_exit")]
    43.         private static extern int gsapi_exit (IntPtr instance);
    44.         /// <summary>
    45.         /// Destroy an instance of Ghostscript. Before you call this, Ghostscript must have finished. If Ghostscript has been initialised, you must call gsapi_exit before gsapi_delete_instance.
    46.         /// </summary>
    47.         /// <param name="instance"></param>
    48.         [DllImport("gsdll64.dll", EntryPoint="gsapi_delete_instance")]
    49.         private static extern void gsapi_delete_instance (IntPtr instance);
    50.         #endregion
    51.         #region Variables
    52.         private string _sDeviceFormat;
    53.         private int _iWidth;
    54.         private int _iHeight;
    55.         private int _iResolutionX;
    56.         private int _iResolutionY;
    57.         private int _iJPEGQuality;
    58.         private Boolean _bFitPage;
    59.         private IntPtr _objHandle;
    60.         #endregion
    61.         #region Proprieties
    62.         public string OutputFormat
    63.         {
    64.             get { return _sDeviceFormat; }
    65.             set { _sDeviceFormat = value; }
    66.         }
    67.         public int Width
    68.         {
    69.             get { return _iWidth; }
    70.             set { _iWidth = value; }
    71.         }
    72.         public int Height
    73.         {
    74.             get { return _iHeight; }
    75.             set { _iHeight = value; }
    76.         }
    77.         public int ResolutionX
    78.         {
    79.             get { return _iResolutionX; }
    80.             set { _iResolutionX = value; }
    81.         }
    82.         public int ResolutionY
    83.         {
    84.             get { return _iResolutionY; }
    85.             set { _iResolutionY = value; }
    86.         }
    87.         public Boolean FitPage
    88.         {
    89.             get { return _bFitPage; }
    90.             set { _bFitPage = value; }
    91.         }
    92.         /// <summary>Quality of compression of JPG</summary>
    93.         public int JPEGQuality
    94.         {
    95.             get { return _iJPEGQuality; }
    96.             set { _iJPEGQuality = value; }
    97.         }
    98.         #endregion
    99.         #region Init
    100.         public PDFConvert(IntPtr objHandle)
    101.         {
    102.             _objHandle = objHandle;
    103.         }
    104.         public PDFConvert()
    105.         {
    106.             _objHandle = IntPtr.Zero;
    107.         }
    108.         #endregion
    109.         private byte[] StringToAnsiZ(string str)
    110.         {
    111.             //' Convert a Unicode string to a null terminated Ansi string for Ghostscript.
    112.             //' The result is stored in a byte array. Later you will need to convert
    113.             //' this byte array to a pointer with GCHandle.Alloc(XXXX, GCHandleType.Pinned)
    114.             //' and GSHandle.AddrOfPinnedObject()
    115.             int intElementCount;
    116.             int intCounter;
    117.             byte[] aAnsi;
    118.             byte bChar;
    119.             intElementCount = str.Length;
    120.             aAnsi = new byte[intElementCount+1];
    121.             for(intCounter = 0; intCounter < intElementCount;intCounter++)
    122.             {
    123.                 bChar = (byte)str[intCounter];
    124.                 aAnsi[intCounter] = bChar;
    125.             }
    126.             aAnsi[intElementCount] = 0;
    127.             return aAnsi;
    128.         }
    129.         /// <summary>Convert the file!</summary>
    130.         public void Convert(string inputFile,string outputFile,
    131.             int firstPage, int lastPage, string deviceFormat, int width, int height)
    132.         {
    133.             //Avoid to work when the file doesn't exist
    134.             if (!System.IO.File.Exists(inputFile))
    135.             {
    136.                 Debug.Log("File doesn't exists");
    137.                 return;
    138.             }
    139.             int intReturn;
    140.             IntPtr intGSInstanceHandle;
    141.             object[] aAnsiArgs;
    142.             IntPtr[] aPtrArgs;
    143.             GCHandle[] aGCHandle;
    144.             int intCounter;
    145.             int intElementCount;
    146.             IntPtr callerHandle;
    147.             GCHandle gchandleArgs;
    148.             IntPtr intptrArgs;
    149.             string[] sArgs = GetGeneratedArgs(inputFile,outputFile,
    150.                 firstPage, lastPage, deviceFormat, width, height);
    151.             // Convert the Unicode strings to null terminated ANSI byte arrays
    152.             // then get pointers to the byte arrays.
    153.             intElementCount = sArgs.Length;
    154.             aAnsiArgs = new object[intElementCount];
    155.             aPtrArgs = new IntPtr[intElementCount];
    156.             aGCHandle = new GCHandle[intElementCount];
    157.             // Create a handle for each of the arguments after
    158.             // they've been converted to an ANSI null terminated
    159.             // string. Then store the pointers for each of the handles
    160.             for(intCounter = 0; intCounter< intElementCount; intCounter++)
    161.             {
    162.                 aAnsiArgs[intCounter] = StringToAnsiZ(sArgs[intCounter]);
    163.                 aGCHandle[intCounter] = GCHandle.Alloc(aAnsiArgs[intCounter], GCHandleType.Pinned);
    164.                 aPtrArgs[intCounter] = aGCHandle[intCounter].AddrOfPinnedObject();
    165.             }
    166.             // Get a new handle for the array of argument pointers
    167.             gchandleArgs = GCHandle.Alloc(aPtrArgs, GCHandleType.Pinned);
    168.             intptrArgs = gchandleArgs.AddrOfPinnedObject();
    169.             intReturn = gsapi_new_instance(out intGSInstanceHandle, _objHandle);
    170.             callerHandle = IntPtr.Zero;
    171.             try
    172.             {
    173.                 intReturn = gsapi_init_with_args(intGSInstanceHandle, intElementCount, intptrArgs);
    174.             }
    175.             catch (Exception ex)
    176.             {
    177.                 //System.Windows.Forms.MessageBox.Show(ex.Message);
    178.                 Debug.Log("file doesn't exists.");
    179.                
    180.             }
    181.             finally
    182.             {
    183.                 for (intCounter = 0; intCounter < intReturn; intCounter++)
    184.                 {
    185.                     aGCHandle[intCounter].Free();
    186.                 }
    187.                 gchandleArgs.Free();
    188.                 gsapi_exit(intGSInstanceHandle);
    189.                 gsapi_delete_instance(intGSInstanceHandle);
    190.             }
    191.         }
    192.         private string[] GetGeneratedArgs(string inputFile, string outputFile,
    193.             int firstPage, int lastPage, string deviceFormat, int width, int height)
    194.         {
    195.             this._sDeviceFormat = deviceFormat;
    196.             this._iResolutionX = width;
    197.             this._iResolutionY = height;
    198.             // Count how many extra args are need - HRangel - 11/29/2006, 3:13:43 PM
    199.             ArrayList lstExtraArgs = new ArrayList();
    200.             if ( _sDeviceFormat=="jpg" && _iJPEGQuality > 0 && _iJPEGQuality < 101)
    201.                 lstExtraArgs.Add("-dJPEGQ=" + _iJPEGQuality);
    202.             if (_iWidth > 0 && _iHeight > 0)
    203.                 lstExtraArgs.Add("-g" + _iWidth + "x" + _iHeight);
    204.             if (_bFitPage)
    205.                 lstExtraArgs.Add("-dPDFFitPage");
    206.             if (_iResolutionX > 0)
    207.             {
    208.                 if (_iResolutionY > 0)
    209.                     lstExtraArgs.Add("-r" + _iResolutionX + "x" + _iResolutionY);
    210.                 else
    211.                     lstExtraArgs.Add("-r" + _iResolutionX);
    212.             }
    213.             // Load Fixed Args - HRangel - 11/29/2006, 3:34:02 PM
    214.             int iFixedCount = 17;
    215.             int iExtraArgsCount = lstExtraArgs.Count;
    216.             string[] args = new string[iFixedCount + lstExtraArgs.Count];
    217.             /*
    218.             // Keep gs from writing information to standard output
    219.         "-q",                    
    220.         "-dQUIET",
    221.        
    222.         "-dPARANOIDSAFER", // Run this command in safe mode
    223.         "-dBATCH", // Keep gs from going into interactive mode
    224.         "-dNOPAUSE", // Do not prompt and pause for each page
    225.         "-dNOPROMPT", // Disable prompts for user interaction          
    226.         "-dMaxBitmap=500000000", // Set high for better performance
    227.        
    228.         // Set the starting and ending pages
    229.         String.Format("-dFirstPage={0}", firstPage),
    230.         String.Format("-dLastPage={0}", lastPage),  
    231.        
    232.         // Configure the output anti-aliasing, resolution, etc
    233.         "-dAlignToPixels=0",
    234.         "-dGridFitTT=0",
    235.         "-sDEVICE=jpeg",
    236.         "-dTextAlphaBits=4",
    237.         "-dGraphicsAlphaBits=4",
    238.             */
    239.             args[0]="pdf2img";//this parameter have little real use
    240.             args[1]="-dNOPAUSE";//I don't want interruptions
    241.             args[2]="-dBATCH";//stop after
    242.             //args[3]="-dSAFER";
    243.             args[3] = "-dPARANOIDSAFER";
    244.             args[4]="-sDEVICE="+_sDeviceFormat;//what kind of export format i should provide
    245.             args[5] = "-q";
    246.             args[6] = "-dQUIET";
    247.             args[7] = "-dNOPROMPT";
    248.             args[8] = "-dMaxBitmap=500000000";
    249.             args[9] = String.Format("-dFirstPage={0}", firstPage);
    250.             args[10] = String.Format("-dLastPage={0}", lastPage);
    251.             args[11] = "-dAlignToPixels=0";
    252.             args[12] = "-dGridFitTT=0";
    253.             args[13] = "-dTextAlphaBits=4";
    254.             args[14] = "-dGraphicsAlphaBits=4";
    255.             //For a complete list watch here:
    256.             //http://pages.cs.wisc.edu/~ghost/doc/cvs/Devices.htm
    257.             //Fill the remaining parameters
    258.             for (int i=0; i < iExtraArgsCount; i++)
    259.             {
    260.                 args[15+i] = (string) lstExtraArgs[i];
    261.             }
    262.             //Fill outputfile and inputfile
    263.             args[15 + iExtraArgsCount] = string.Format("-sOutputFile={0}",outputFile);
    264.             args[16 + iExtraArgsCount] = string.Format("{0}",inputFile);
    265.             return args;
    266.         }
    267.         public void pdf2jpgTest()
    268.         {            
    269.             //this.Convert(@"C://tmp//pdfimg//test1.pdf",@"C://tmp//pdfimg//out.jpg",1,1,"jpeg",100,100);
    270.             //this.Convert(@"C://tmp//pdfimg//test.pdf", @"C://tmp//pdfimg//out2.jpg", 291, 291, "jpeg", 800, 800);
    271.            
    272.             this.Convert(@"C:/tempPDF/Sources/sample.pdf",
    273.                         @"C:/tempPDF/Textures/texting.jpg",
    274.                         1,
    275.                         10,
    276.                         "jpeg",
    277.                         800,
    278.                         600);
    279.         }
    280.     }
    281. }
    282.  
    and the following is my code of calling the class
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4. using ConvertPDF;
    5.  
    6. public class testScript : MonoBehaviour {
    7.  
    8.     // Use this for initialization
    9.     void Start ()
    10.     {
    11.         PDFConvert converter = new PDFConvert();
    12.        
    13.         converter.Convert(@"C:/tempPDF/Sources/sample.pdf",
    14.                          @"C:/tempPDF/Textures/texting.jpg",
    15.                          1,
    16.                          10,
    17.                          "jpeg",
    18.                          800,
    19.                          600);
    20.        
    21.        
    22.     }
    23. }
    24.  
    I'm don't know what am I missing and have been stuck here for a few days, so any idea or help would be greatly appreciated.
     
  2. sloopidoopi

    sloopidoopi

    Joined:
    Jan 2, 2010
    Posts:
    244
    Mixing 32bit (Unity) with 64bit dll's is not possible right out of the box. Your dll has to be a 32 bit version and it has to be compiled against maximal Net version 3.5
     
    CipherZero likes this.
  3. CipherZero

    CipherZero

    Joined:
    Jul 10, 2013
    Posts:
    38
    arrhhh I see.
    Actually I had though about switching to 32-bits ghostscript, but I kept thinking it'll be pretty stupid if a 64-bits dll doesn't support a 32-bits application coz I generally think 64-bits is kinda "backward compatible".

    The ghostscript I'm using is downloaded from http://www.ghostscript.com/ , which I believe to be the official website. So I'm not sure whether the dll is compiled against maximal Net version 3.5. I hope it is so.


    UPDATE:
    just a quick update, after i've downloaded and installed a 32-bits ghostscript, modified the script(changing from gsdll64 to gsdll32),and run it, it still gives the DllNotFoundException
     
    Last edited: Jun 9, 2014
  4. sloopidoopi

    sloopidoopi

    Joined:
    Jan 2, 2010
    Posts:
    244
    Well, I guess that there are other problems with ghostscript then the 32/64 bit problem.I don't see a net version of ghostscript on the official site. There is a net wrapper library here : http://ghostscriptnet.codeplex.com but it uses some features like unsafe code that has to be handled in a special way with unity. Look the forum for 'unsafe code' . Perhaps they use other features that the mono version of unity can't handle.
     
  5. CipherZero

    CipherZero

    Joined:
    Jul 10, 2013
    Posts:
    38
    Hey thx for the answer, but I finally found the answer. This is extremely stupid.
    The correct way to to call the library is
    Code (CSharp):
    1. [DllImport("gsdll32", EntryPoint="gsapi_new_instance")]
    2. [DllImport("gsdll32", EntryPoint="gsapi_init_with_args")]
    3. [DllImport("gsdll32", EntryPoint="gsapi_exit")]
    4. [DllImport("gsdll32", EntryPoint="gsapi_delete_instance")]
    That is, the dll name there should be without the ".dll". A lot of example from the Internet has the ".dll" included, which in this case, won't work.
     
  6. sloopidoopi

    sloopidoopi

    Joined:
    Jan 2, 2010
    Posts:
    244
    Cool,
    did you write your own NET wrapper or do you use an existing one?
     
  7. jvhgamer

    jvhgamer

    Joined:
    Jun 27, 2017
    Posts:
    36
    @CipherZero @sloopidoopi Failed to load 'Assets/Plugins/gsdll32.dll', expected x64 architecture, but was x86 architecture. You must recompile your plugin for x64 architecture.

    Did you ever encounter that error?

    Using the gsdll64 fixes the error for me as the x32 is based on x86 architecture and the x64 on x64 arch... weird this didn't work for you.
     
    Last edited: Jul 6, 2017
  8. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,377
    Probably because it's 3 years later, and we're on a much more recent version of Unity.