using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks;using System.Runtime.InteropServices; using System.IO; using System.Security; using System.ComponentModel; namespace gsview { /* Warning. This list is in a particular order. The devices before * psdrgb do not support multiple pages. Those including psdrgb do * support multiple pages. This is used in the conversion process. * Also note that mupdf devices go at the beginning of the list */ public enum gsDevice_t { svg, pnm, pclbitmap, pwg, bmp16, /* Add mupdf devices before this one */ bmp16m, bmp256, bmp32b, bmpgray, bmpmono, eps2write, jpeg, jpegcmyk, jpeggray, pamcmyk32, pamcmyk4, pbm, pgm, png16, png16m, png256, pngalpha, pnggray, pngmono, psdcmyk, psdrgb, /* Add single page gs devices before this device */ pdfwrite, ps2write, pxlcolor, pxlmono, tiff12nc, tiff24nc, tiff32nc, tiff64nc, tiffcrle, tiffg3, tiffg32d, tiffg4, tiffgray, tifflzw, tiffpack, tiffsep, txtwrite, xpswrite }; public enum GS_Task_t { PS_DISTILL, CREATE_XPS, SAVE_RESULT } public enum GS_Result_t { gsOK, gsFAILED, gsCANCELLED } /* Parameters */ public struct gsParams_t { public String init_string; public String init_file; public int resolution; public gsDevice_t device; public String devicename; public String outputfile; public String inputfile; public GS_Task_t task; public GS_Result_t result; public int num_pages; public String options; public bool need_multi_page; public System.Collections.IList pages; public int firstpage; public int lastpage; public int currpage; /* valid only when pages != null */ }; public class gsEventArgs : EventArgs { private bool m_completed; private int m_progress; private gsParams_t m_param; public bool Completed { get { return m_completed; } } public gsParams_t Params { get { return m_param; } } public int Progress { get { return m_progress; } } public gsEventArgs(bool completed, int progress, gsParams_t param) { m_completed = completed; m_progress = progress; m_param = param; } } /* from gs */ public struct gsapi_revision_t { public IntPtr product; public IntPtr copyright; public int revision; public int revisiondate; } public enum gsEncoding { GS_ARG_ENCODING_LOCAL = 0, GS_ARG_ENCODING_UTF8 = 1, GS_ARG_ENCODING_UTF16LE = 2 }; public enum gsStatus { GS_READY, GS_BUSY, GS_ERROR }; static class gsConstants { public const int E_QUIT = -101; public const int GS_READ_BUFFER = 32768; } [SuppressUnmanagedCodeSecurity] class ghostsharp { /* Callback proto for stdio */ public delegate int gsStdIOHandler(IntPtr caller_handle, IntPtr buffer, int len); /* Ghostscript API */ [DllImport("gsdll64.dll", EntryPoint = "gsapi_revision", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern int gsapi_revision64(ref gsapi_revision_t vers, int size); [DllImport("gsdll64.dll", EntryPoint="gsapi_new_instance", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern int gsapi_new_instance64(out IntPtr pinstance, IntPtr caller_handle); [DllImport("gsdll64.dll", EntryPoint = "gsapi_delete_instance", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern void gsapi_delete_instance64(IntPtr instance); [DllImport("gsdll64.dll", EntryPoint = "gsapi_init_with_args", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern int gsapi_init_with_args64(IntPtr instance, int argc, IntPtr argv); [DllImport("gsdll64.dll", EntryPoint = "gsapi_exit", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern int gsapi_exit64(IntPtr instance); [DllImport("gsdll64.dll", EntryPoint = "gsapi_set_arg_encoding", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern int gsapi_set_arg_encoding64(IntPtr instance, int encoding); [DllImport("gsdll64.dll", EntryPoint = "gsapi_set_stdio", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern int gsapi_set_stdio64(IntPtr instance, gsStdIOHandler stdin, gsStdIOHandler stdout, gsStdIOHandler stderr); [DllImport("gsdll64.dll", EntryPoint = "gsapi_run_string_begin", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern void gsapi_run_string_begin64(IntPtr instance, int usererr, ref int exitcode); [DllImport("gsdll64.dll", EntryPoint = "gsapi_run_string_continue", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern void gsapi_run_string_continue64(IntPtr instance, IntPtr command, int count, int usererr, ref int exitcode); [DllImport("gsdll64.dll", EntryPoint = "gsapi_run_string_end", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern void gsapi_run_string_end64(IntPtr instance, int usererr, ref int exitcode); /* 32 Bit DLL */ [DllImport("gsdll32.dll", EntryPoint = "gsapi_revision", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern int gsapi_revision32(ref gsapi_revision_t vers, int size); [DllImport("gsdll32.dll", EntryPoint = "gsapi_new_instance", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern int gsapi_new_instance32(out IntPtr pinstance, IntPtr caller_handle); [DllImport("gsdll32.dll", EntryPoint = "gsapi_delete_instance", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern void gsapi_delete_instance32(IntPtr instance); [DllImport("gsdll32.dll", EntryPoint = "gsapi_init_with_args", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern int gsapi_init_with_args32(IntPtr instance, int argc, IntPtr argv); [DllImport("gsdll32.dll", EntryPoint = "gsapi_exit", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern int gsapi_exit32(IntPtr instance); [DllImport("gsdll32.dll", EntryPoint = "gsapi_set_arg_encoding", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern int gsapi_set_arg_encoding32(IntPtr instance, int encoding); [DllImport("gsdll32.dll", EntryPoint = "gsapi_set_stdio", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern int gsapi_set_stdio32(IntPtr instance, gsStdIOHandler stdin, gsStdIOHandler stdout, gsStdIOHandler stderr); [DllImport("gsdll32.dll", EntryPoint = "gsapi_run_string_begin", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern void gsapi_run_string_begin32(IntPtr instance, int usererr, ref int exitcode); [DllImport("gsdll32.dll", EntryPoint = "gsapi_run_string_continue", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern void gsapi_run_string_continue32(IntPtr instance, IntPtr command, int count, int usererr, ref int exitcode); [DllImport("gsdll32.dll", EntryPoint = "gsapi_run_string_end", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private static extern void gsapi_run_string_end32(IntPtr instance, int usererr, ref int exitcode); /* In case the DLL is not found we need to wrap the methods up with * a try/catch. Also select 32 or 64 bit DLL at this time. This * C# code is compiled as ANYCPU type */ private int tc_gsapi_revision(ref gsapi_revision_t vers, int size) { int code = 0; try { if (is64bit) code = gsapi_revision64(ref vers, size); else code = gsapi_revision32(ref vers, size); } catch (DllNotFoundException) { /* DLL not found */ String output = "DllNotFoundException: Ghostscript DLL not found"; gsDLLProblemMain(this, output); return -1; } catch (BadImageFormatException) { /* Using 32 bit with 64 or vice versa */ String output = "BadImageFormatException: Incorrect Ghostscript DLL"; gsDLLProblemMain(this, output); return -1; } return code; } private int tc_gsapi_new_instance(out IntPtr pinstance, IntPtr caller_handle) { int code = 0; pinstance = IntPtr.Zero; try { if (is64bit) code = gsapi_new_instance64(out pinstance, caller_handle); else code = gsapi_new_instance32(out pinstance, caller_handle); } catch (DllNotFoundException) { /* DLL not found */ String output = "DllNotFoundException: Ghostscript DLL not found"; gsDLLProblemMain(this, output); return -1; } catch (BadImageFormatException) { /* Using 32 bit with 64 or vice versa */ String output = "BadImageFormatException: Incorrect Ghostscript DLL"; gsDLLProblemMain(this, output); return -1; } return code; } private int tc_gsapi_delete_instance(IntPtr instance) { try { if (is64bit) gsapi_delete_instance64(instance); else gsapi_delete_instance32(instance); } catch (DllNotFoundException) { /* DLL not found */ String output = "DllNotFoundException: Ghostscript DLL not found"; gsDLLProblemMain(this, output); return -1; } catch (BadImageFormatException) { /* Using 32 bit with 64 or vice versa */ String output = "BadImageFormatException: Incorrect Ghostscript DLL"; gsDLLProblemMain(this, output); return -1; } return 0; } private int tc_gsapi_init_with_args(IntPtr instance, int argc, IntPtr argv) { int code; try { if (is64bit) code = gsapi_init_with_args64(instance, argc, argv); else code = gsapi_init_with_args32(instance, argc, argv); } catch (DllNotFoundException) { /* DLL not found */ String output = "DllNotFoundException: Ghostscript DLL not found"; gsDLLProblemMain(this, output); return -1; } catch (BadImageFormatException) { /* Using 32 bit with 64 or vice versa */ String output = "BadImageFormatException: Incorrect Ghostscript DLL"; gsDLLProblemMain(this, output); return -1; } return code; } private int tc_gsapi_exit(IntPtr instance) { int code; try { if (is64bit) code = gsapi_exit64(instance); else code = gsapi_exit32(instance); } catch (DllNotFoundException) { /* DLL not found */ String output = "DllNotFoundException: Ghostscript DLL not found"; gsDLLProblemMain(this, output); return -1; } catch (BadImageFormatException) { /* Using 32 bit with 64 or vice versa */ String output = "BadImageFormatException: Incorrect Ghostscript DLL"; gsDLLProblemMain(this, output); return -1; } return code; } private int tc_gsapi_set_arg_encoding(IntPtr instance, int encoding) { int code; try { if (is64bit) code = gsapi_set_arg_encoding64(instance, encoding); else code = gsapi_set_arg_encoding32(instance, encoding); } catch (DllNotFoundException) { /* DLL not found */ String output = "DllNotFoundException: Ghostscript DLL not found"; gsDLLProblemMain(this, output); return -1; } catch (BadImageFormatException) { /* Using 32 bit with 64 or vice versa */ String output = "BadImageFormatException: Incorrect Ghostscript DLL"; gsDLLProblemMain(this, output); return -1; } return code; } private int tc_gsapi_set_stdio(IntPtr instance, gsStdIOHandler stdin, gsStdIOHandler stdout, gsStdIOHandler stderr) { int code; try { if (is64bit) code = gsapi_set_stdio64(instance, stdin, stdout, stderr); else code = gsapi_set_stdio32(instance, stdin, stdout, stderr); } catch (DllNotFoundException) { /* DLL not found */ String output = "DllNotFoundException: Ghostscript DLL not found"; gsDLLProblemMain(this, output); return -1; } catch (BadImageFormatException) { /* Using 32 bit with 64 or vice versa */ String output = "BadImageFormatException: Incorrect Ghostscript DLL"; gsDLLProblemMain(this, output); return -1; } return code; } private int tc_gsapi_run_string_begin(IntPtr instance, int usererr, ref int exitcode) { try { if (is64bit) gsapi_run_string_begin64(instance, usererr, ref exitcode); else gsapi_run_string_begin32(instance, usererr, ref exitcode); } catch (DllNotFoundException) { /* DLL not found */ String output = "DllNotFoundException: Ghostscript DLL not found"; gsDLLProblemMain(this, output); return -1; } catch (BadImageFormatException) { /* Using 32 bit with 64 or vice versa */ String output = "BadImageFormatException: Incorrect Ghostscript DLL"; gsDLLProblemMain(this, output); return -1; } return 0; } private int tc_gsapi_run_string_continue(IntPtr instance, IntPtr command, int count, int usererr, ref int exitcode) { try { if (is64bit) gsapi_run_string_continue64(instance, command, count, usererr, ref exitcode); else gsapi_run_string_continue32(instance, command, count, usererr, ref exitcode); } catch (DllNotFoundException) { /* DLL not found */ String output = "DllNotFoundException: Ghostscript DLL not found"; gsDLLProblemMain(this, output); return -1; } catch (BadImageFormatException) { /* Using 32 bit with 64 or vice versa */ String output = "BadImageFormatException: Incorrect Ghostscript DLL"; gsDLLProblemMain(this, output); return -1; } return 0; } private int tc_gsapi_run_string_end(IntPtr instance, int usererr, ref int exitcode) { try { if (is64bit) gsapi_run_string_end64(instance, usererr, ref exitcode); else gsapi_run_string_end32(instance, usererr, ref exitcode); } catch (DllNotFoundException) { /* DLL not found */ String output = "DllNotFoundException: Ghostscript DLL not found"; gsDLLProblemMain(this, output); return -1; } catch (BadImageFormatException) { /* Using 32 bit with 64 or vice versa */ String output = "BadImageFormatException: Incorrect Ghostscript DLL"; gsDLLProblemMain(this, output); return -1; } return 0; } private int StdInCallback(IntPtr handle, IntPtr pointer, int count) { String output = Marshal.PtrToStringAnsi(pointer); return count; } private int StdOutCallback(IntPtr handle, IntPtr pointer, int count) { String output = Marshal.PtrToStringAnsi(pointer); gsIOUpdateMain(this, output, count); if (m_params.task != GS_Task_t.PS_DISTILL) { /* See if we have a page number */ if (count >= 7 && output.Substring(0, 4) == "Page") { String page = output.Substring(5, count - 6); int numVal; try { double perc = 0.0; numVal = System.Convert.ToInt32(page); if (m_params.firstpage == -1 && m_params.lastpage == -1 && m_params.pages == null) { /* Doing full document */ perc = 100.0 * (double)numVal / (double)m_params.num_pages; } else { if (m_params.pages != null) { perc = 100.0 * ((double)numVal - m_params.currpage) / (double)m_params.num_pages; m_params.currpage = m_params.currpage + 1; } else { /* continugous set of pages */ perc = 100.0 * ((double)numVal - m_params.firstpage + 1) / (double)m_params.num_pages; } } m_worker.ReportProgress((int)perc); } catch (FormatException e) { Console.WriteLine("XPSPrint Error: Input string is not a sequence of digits."); } catch (OverflowException e) { Console.WriteLine("XPSPrint Error: The number cannot fit in an Int32."); } } } return count; } private int StdErrCallback(IntPtr handle, IntPtr pointer, int count) { String output = Marshal.PtrToStringAnsi(pointer); gsIOUpdateMain(this, output, count); return count; } IntPtr gsInstance; BackgroundWorker m_worker; bool is64bit; gsParams_t m_params; /* Callbacks to Main */ internal delegate void gsDLLProblem(object gsObject, String mess); internal event gsDLLProblem gsDLLProblemMain; internal delegate void gsIOCallBackMain(object gsObject, String mess, int len); internal event gsIOCallBackMain gsIOUpdateMain; internal delegate void gsCallBackMain(object gsObject, gsEventArgs info); internal event gsCallBackMain gsUpdateMain; public ghostsharp() { /* Determine now if we are 64 or 32 bit */ is64bit = EnvironmentCheck.IS64Bit; m_worker = null; gsInstance = IntPtr.Zero; } private List GetOptions(String options) { List optionlist = new List(); if (options != "") { string[] words = options.Split(' '); for (int k = 0; k < words.Length; k++) { if (words[k].Length > 0) { optionlist.Add(words[k]); } } } return optionlist; } /* A standard command line approach to using gs API */ private void gsWork1(object sender, DoWorkEventArgs e) { gsParams_t gsparams = (gsParams_t) e.Argument; String out_file = gsparams.outputfile; String in_file = gsparams.inputfile; int num_params = 8; /* base number */ int rend_count = 1; String options; int count; List optionlist; optionlist = GetOptions(gsparams.options); num_params = num_params + optionlist.Count; if (gsparams.pages != null) { rend_count = gsparams.pages.Count; num_params = num_params + 2; } if (gsparams.init_file != null) num_params = num_params + 1; if (gsparams.init_string != null) num_params = num_params + 2; var argParam = new GCHandle[num_params]; var argPtrs = new IntPtr[num_params]; String[] strParams = new String[num_params]; List CharacterArray = new List(num_params); GCHandle argPtrsStable; /* New instance */ int code = tc_gsapi_new_instance(out gsInstance, IntPtr.Zero); if (code < 0) { gsparams.result = GS_Result_t.gsFAILED; e.Result = gsparams; return; } var RaiseStdInCallback = new gsStdIOHandler(StdInCallback); var RaiseStdOutCallback = new gsStdIOHandler(StdOutCallback); var RaiseStdErrCallback = new gsStdIOHandler(StdErrCallback); var stdInPtr = Marshal.GetFunctionPointerForDelegate(RaiseStdInCallback); var stdOutPtr = Marshal.GetFunctionPointerForDelegate(RaiseStdOutCallback); var stdErrPtr = Marshal.GetFunctionPointerForDelegate(RaiseStdErrCallback); // Setup stdio callback functions code = tc_gsapi_set_stdio(gsInstance, RaiseStdInCallback, RaiseStdOutCallback, RaiseStdErrCallback); code = tc_gsapi_set_arg_encoding(gsInstance, (int)gsEncoding.GS_ARG_ENCODING_UTF8); if (code == 0) { for (int jj = 0; jj < rend_count; jj++) { strParams[0] = "gs"; /* This does not matter */ strParams[1] = "-dNOPAUSE"; strParams[2] = "-dBATCH"; if (gsparams.devicename != null) { strParams[3] = "-sDEVICE=" + gsparams.devicename; } else { strParams[3] = "-sDEVICE=" + Enum.GetName(typeof(gsDevice_t), gsparams.device); } strParams[4] = "-r" + gsparams.resolution; /* Create temp file if file not specified */ if (out_file == null) { out_file = Path.GetTempFileName(); gsparams.outputfile = out_file; } count = 5; /* Add in the options */ for (int kk = 0; kk < optionlist.Count; kk++) { strParams[count] = optionlist[kk]; count++; } /* We have discontinuous page selection */ if (gsparams.pages != null) { String firstpage, lastpage; options = gsparams.options; SelectPage curr_page = (SelectPage)(gsparams.pages[jj]); firstpage = "-dFirstPage=" + curr_page.Page; lastpage = "-dLastPage=" + curr_page.Page; strParams[count] = firstpage; count++; strParams[count] = lastpage; count++; /* Look for file extension. */ string extension = System.IO.Path.GetExtension(out_file); int len = extension.Length; String new_out_file = out_file.Substring(0, out_file.Length - len); strParams[count] = "-o" + new_out_file + "_page" + curr_page.Page + extension; } else { if (gsparams.need_multi_page) { /* Look for file extension. */ string extension = System.IO.Path.GetExtension(out_file); int len = extension.Length; String new_out_file = out_file.Substring(0, out_file.Length - len); strParams[count] = "-o" + new_out_file + "_page%d" + extension; } else strParams[count] = "-o" + out_file; } if (gsparams.init_string != null) { count++; strParams[count] = "-c"; count++; strParams[count] = gsparams.init_string; } count++; strParams[count] = "-f"; if (gsparams.init_file != null) { count++; strParams[count] = gsparams.init_file; } count++; strParams[count] = in_file; /* Now convert our Strings to char* and get pinned handles to these. * This keeps the c# GC from moving stuff around on us */ for (int k = 0; k < num_params; k++) { CharacterArray.Add(System.Text.Encoding.UTF8.GetBytes(strParams[k].ToCharArray())); argParam[k] = GCHandle.Alloc(CharacterArray[k], GCHandleType.Pinned); argPtrs[k] = argParam[k].AddrOfPinnedObject(); } /* Also stick the array of pointers into memory that will not be GCd */ argPtrsStable = GCHandle.Alloc(argPtrs, GCHandleType.Pinned); code = tc_gsapi_init_with_args(gsInstance, num_params, argPtrsStable.AddrOfPinnedObject()); /* All the pinned items need to be freed so the GC can do its job */ for (int k = 0; k < num_params; k++) { argParam[k].Free(); } argPtrsStable.Free(); /* Free the character array list in case we have multiple runs */ CharacterArray.Clear(); if (code < 0) break; } } int code1 = tc_gsapi_exit(gsInstance); if ((code == 0) || (code == gsConstants.E_QUIT)) code = code1; RaiseStdInCallback = null; RaiseStdOutCallback = null; RaiseStdErrCallback = null; tc_gsapi_delete_instance(gsInstance); if ((code == 0) || (code == gsConstants.E_QUIT)) { gsparams.result = GS_Result_t.gsOK; e.Result = gsparams; return; } gsparams.result = GS_Result_t.gsFAILED; e.Result = gsparams; return; } /* Feeding gs piecemeal so that we can have some progress callback */ private void gsWork2(object sender, DoWorkEventArgs e) { gsParams_t Params = (gsParams_t)e.Argument; String out_file = Params.outputfile; String in_file = Params.inputfile; int num_params = 6; if (Params.options.Length > 0) num_params = num_params + 1; int exitcode = 0; var argParam = new GCHandle[num_params]; var argPtrs = new IntPtr[num_params]; var Feed = new GCHandle(); var FeedPtr = new IntPtr(); String[] strParams = new String[num_params]; List CharacterArray = new List(num_params); GCHandle argPtrsStable; Byte[] Buffer = new Byte[gsConstants.GS_READ_BUFFER]; BackgroundWorker worker = sender as BackgroundWorker; /* Open the file */ var fs = new FileStream(in_file, FileMode.Open); var len = (int) fs.Length; /* New instance */ int code = tc_gsapi_new_instance(out gsInstance, IntPtr.Zero); if (code < 0) { Params.result = GS_Result_t.gsFAILED; e.Result = Params; return; } var RaiseStdInCallback = new gsStdIOHandler(StdInCallback); var RaiseStdOutCallback = new gsStdIOHandler(StdOutCallback); var RaiseStdErrCallback = new gsStdIOHandler(StdErrCallback); var stdInPtr = Marshal.GetFunctionPointerForDelegate(RaiseStdInCallback); var stdOutPtr = Marshal.GetFunctionPointerForDelegate(RaiseStdOutCallback); var stdErrPtr = Marshal.GetFunctionPointerForDelegate(RaiseStdErrCallback); // Setup stdio callback functions code = tc_gsapi_set_stdio(gsInstance, RaiseStdInCallback, RaiseStdOutCallback, RaiseStdErrCallback); code = tc_gsapi_set_arg_encoding(gsInstance, (int)gsEncoding.GS_ARG_ENCODING_UTF8); if (code == 0) { strParams[0] = "gs"; /* This does not matter */ strParams[1] = "-dNOPAUSE"; strParams[2] = "-dBATCH"; if (Params.devicename != null) { strParams[3] = "-sDEVICE=" + Params.devicename; } else { strParams[3] = "-sDEVICE=" + Enum.GetName(typeof(gsDevice_t), Params.device); } strParams[4] = "-r" + Params.resolution; /* Create temp file if file not specified */ if (out_file == null) { out_file = Path.GetTempFileName(); Params.outputfile = out_file; } if (Params.options.Length > 0) { strParams[5] = Params.options; strParams[6] = "-o" + out_file; } else strParams[5] = "-o" + out_file; /* Now convert our Strings to char* and get pinned handles to these. * This keeps the c# GC from moving stuff around on us */ for (int k = 0; k < num_params; k++) { CharacterArray.Add(System.Text.Encoding.UTF8.GetBytes(strParams[k].ToCharArray())); argParam[k] = GCHandle.Alloc(CharacterArray[k], GCHandleType.Pinned); argPtrs[k] = argParam[k].AddrOfPinnedObject(); } /* Also stick the array of pointers into memory that will not be GCd */ argPtrsStable = GCHandle.Alloc(argPtrs, GCHandleType.Pinned); code = tc_gsapi_init_with_args(gsInstance, num_params, argPtrsStable.AddrOfPinnedObject()); /* First pin the data buffer */ Feed = GCHandle.Alloc(Buffer, GCHandleType.Pinned); FeedPtr = Feed.AddrOfPinnedObject(); /* Now start feeding the input piece meal and do a call back * with our progress */ if (code == 0) { int count; double perc; int total = 0; tc_gsapi_run_string_begin(gsInstance, 0, ref exitcode); while ((count = fs.Read(Buffer, 0, gsConstants.GS_READ_BUFFER)) > 0) { tc_gsapi_run_string_continue(gsInstance, FeedPtr, count, 0, ref exitcode); if (exitcode < 0) { code = exitcode; break; } total = total + count; perc = 100.0 * (double) total / (double) len; worker.ReportProgress((int)perc); if (worker.CancellationPending == true) { e.Cancel = true; break; } } tc_gsapi_run_string_end(gsInstance, 0, ref exitcode); if (code == 0) code = exitcode; } /* All the pinned items need to be freed so the GC can do its job */ for (int k = 0; k < num_params; k++) { argParam[k].Free(); } argPtrsStable.Free(); Feed.Free(); } int code1 = tc_gsapi_exit(gsInstance); if ((code == 0) || (code == gsConstants.E_QUIT)) code = code1; tc_gsapi_delete_instance(gsInstance); if ((code == 0) || (code == gsConstants.E_QUIT)) { Params.result = GS_Result_t.gsOK; e.Result = Params; return; } Params.result = GS_Result_t.gsFAILED; e.Result = Params; return; } /* Callback */ private void gsCompleted(object sender, RunWorkerCompletedEventArgs e) { gsParams_t Value; gsEventArgs info; gsParams_t Params = (gsParams_t) e.Result; if (Params.task == GS_Task_t.PS_DISTILL) m_worker.DoWork -= new DoWorkEventHandler(gsWork2); else m_worker.DoWork -= new DoWorkEventHandler(gsWork1); if (e.Cancelled) { Value = new gsParams_t(); Value.result = GS_Result_t.gsCANCELLED; info = new gsEventArgs(true, 100, Value); } else { Value = (gsParams_t)e.Result; info = new gsEventArgs(true, 100, Value); } gsUpdateMain(this, info); } private void gsProgressChanged(object sender, ProgressChangedEventArgs e) { /* Callback with progress */ gsParams_t Value = new gsParams_t(); gsEventArgs info = new gsEventArgs(false, e.ProgressPercentage, Value); gsUpdateMain(this, info); } public gsStatus DistillPS(String fileName, int resolution) { gsParams_t gsparams = new gsParams_t(); ; gsparams.init_file = null; gsparams.init_string = null; gsparams.device = gsDevice_t.pdfwrite; gsparams.devicename = null; gsparams.outputfile = null; gsparams.resolution = resolution; gsparams.inputfile = fileName; gsparams.num_pages = -1; gsparams.task = GS_Task_t.PS_DISTILL; gsparams.options = ""; gsparams.need_multi_page = false; gsparams.pages = null; gsparams.firstpage = -1; gsparams.lastpage = -1; gsparams.currpage = -1; return RunGhostscript(gsparams); } public gsStatus CreateXPS(String fileName, int resolution, int num_pages) { gsParams_t gsparams = new gsParams_t(); gsparams.init_file = null; gsparams.init_string = null; gsparams.device = gsDevice_t.xpswrite; gsparams.outputfile = null; gsparams.resolution = resolution; gsparams.inputfile = fileName; gsparams.task = GS_Task_t.CREATE_XPS; gsparams.num_pages = num_pages; gsparams.options = ""; gsparams.need_multi_page = false; gsparams.pages = null; gsparams.firstpage = -1; gsparams.lastpage = -1; gsparams.currpage = -1; return RunGhostscript(gsparams); } public gsStatus Convert(String fileName, String options, String device, String outputFile, int num_pages, int resolution, bool multi_page_needed, System.Collections.IList pages, int firstpage, int lastpage, String init_file, String init_string) { gsParams_t gsparams = new gsParams_t(); gsparams.init_file = init_file; gsparams.init_string = init_string; gsparams.devicename = device; gsparams.outputfile = outputFile; gsparams.inputfile = fileName; gsparams.task = GS_Task_t.SAVE_RESULT; gsparams.num_pages = num_pages; gsparams.options = options; gsparams.resolution = resolution; gsparams.need_multi_page = multi_page_needed; gsparams.pages = pages; gsparams.firstpage = firstpage; gsparams.lastpage = lastpage; gsparams.currpage = 1; return RunGhostscript(gsparams); } public gsStatus GetStatus() { if (m_worker != null && m_worker.IsBusy) return gsStatus.GS_BUSY; else return gsStatus.GS_READY; } public String GetVersion() { gsapi_revision_t vers; vers.copyright = IntPtr.Zero; vers.product = IntPtr.Zero; vers.revision = 0; vers.revisiondate = 0; int size = System.Runtime.InteropServices.Marshal.SizeOf(vers); if (tc_gsapi_revision(ref vers, size) == 0) { String product = Marshal.PtrToStringAnsi(vers.product); String output; int major = vers.revision / 100; int minor = vers.revision - major * 100; String versnum = major + "." + minor; output = product + " " + versnum; return output; } else return null; } private gsStatus RunGhostscript(gsParams_t Params) { try { if (m_worker != null && m_worker.IsBusy) { m_worker.CancelAsync(); return gsStatus.GS_BUSY; } if (m_worker == null) { m_worker = new BackgroundWorker(); m_worker.WorkerReportsProgress = true; m_worker.WorkerSupportsCancellation = true; m_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(gsCompleted); m_worker.ProgressChanged += new ProgressChangedEventHandler(gsProgressChanged); } if (Params.task == GS_Task_t.PS_DISTILL) m_worker.DoWork += new DoWorkEventHandler(gsWork2); else m_worker.DoWork += new DoWorkEventHandler(gsWork1); m_params = Params; m_worker.RunWorkerAsync(Params); return gsStatus.GS_READY; } catch (OutOfMemoryException e) { Console.WriteLine("Memory allocation failed during gs rendering\n"); return gsStatus.GS_ERROR; } } public void Cancel() { m_worker.CancelAsync(); } } /* We need to determine if we are a 64 bit or 32 bit process. In .NET 4+ * there is a simple check, Environment.Is64BitProcess. I don't want to * rely upon having that new of version. Hence, the following */ public static class EnvironmentCheck { [StructLayout(LayoutKind.Sequential)] internal struct SYSTEM_INFO { public ushort processorArchitecture; ushort reserved; public uint pageSize; public IntPtr minimumApplicationAddress; public IntPtr maximumApplicationAddress; public IntPtr activeProcessorMask; public uint numberOfProcessors; public uint processorType; public uint allocationGranularity; public ushort processorLevel; public ushort processorRevision; } [DllImport("kernel32.dll")] internal static extern void GetNativeSystemInfo(ref SYSTEM_INFO si); [DllImport("kernel32.dll")] internal static extern bool IsWow64Process(IntPtr handle, out bool result); [DllImport("kernel32.dll")] internal static extern IntPtr GetCurrentProcess(); public static bool IS64Bit { get { SYSTEM_INFO si = new SYSTEM_INFO(); GetNativeSystemInfo(ref si); if (si.processorArchitecture == 0) // zero meaning x86 (check the docs) return false; // 64 bit OS, is it a 64 bit process? bool result; if (!IsWow64Process(GetCurrentProcess(), out result)) throw new InvalidOperationException(); return !result; } } } }