From f7cb3e7c459c8eb47702f88ad7307ce3709b2a43 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 15 Mar 2011 14:47:31 -0400 Subject: [PATCH 1/4] Added API class for direct access to GS API functions Added GhostscriptManager to control loading & freeing the Ghostscript library Added Examples/ directory with ported examples from original API code --- Examples/GhostscriptSharpExamples/Example1.cs | 60 +++ Examples/GhostscriptSharpExamples/Example2.cs | 107 ++++++ Examples/GhostscriptSharpExamples/Example3.cs | 72 ++++ .../GhostscriptSharpExamples/Files/input.ps | Bin 0 -> 21503 bytes .../GhostscriptSharpExamples.csproj | 64 ++++ .../Properties/AssemblyInfo.cs | 36 ++ GhostScriptSharp/EventArgs.cs | 18 + GhostScriptSharp/GhostscriptException.cs | 47 +++ GhostScriptSharp/GhostscriptManager.cs | 344 ++++++++++++++++++ GhostScriptSharp/GhostscriptSharp.cs | 309 ++-------------- GhostScriptSharp/GhostscriptSharp.csproj | 8 + .../Settings/GhostscriptDevices.cs | 81 +++++ .../Settings/GhostscriptPageSize.cs | 109 ++++++ GhostScriptSharp/Settings/GhostscriptPages.cs | 66 ++++ .../Settings/GhostscriptSettings.cs | 155 ++++++++ README.md | 41 ++- .../GhostscriptSharpTests.cs | 76 +++- .../GhostscriptSharpTests.csproj | 1 + .../GhostscriptSharpTests.csproj.user | 2 +- 19 files changed, 1314 insertions(+), 282 deletions(-) create mode 100644 Examples/GhostscriptSharpExamples/Example1.cs create mode 100644 Examples/GhostscriptSharpExamples/Example2.cs create mode 100644 Examples/GhostscriptSharpExamples/Example3.cs create mode 100644 Examples/GhostscriptSharpExamples/Files/input.ps create mode 100644 Examples/GhostscriptSharpExamples/GhostscriptSharpExamples.csproj create mode 100644 Examples/GhostscriptSharpExamples/Properties/AssemblyInfo.cs create mode 100644 GhostScriptSharp/EventArgs.cs create mode 100644 GhostScriptSharp/GhostscriptException.cs create mode 100644 GhostScriptSharp/GhostscriptManager.cs create mode 100644 GhostScriptSharp/Settings/GhostscriptDevices.cs create mode 100644 GhostScriptSharp/Settings/GhostscriptPageSize.cs create mode 100644 GhostScriptSharp/Settings/GhostscriptPages.cs create mode 100644 GhostScriptSharp/Settings/GhostscriptSettings.cs diff --git a/Examples/GhostscriptSharpExamples/Example1.cs b/Examples/GhostscriptSharpExamples/Example1.cs new file mode 100644 index 0000000..4d1e2f5 --- /dev/null +++ b/Examples/GhostscriptSharpExamples/Example1.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Text; +using GhostscriptSharp; +using System.Runtime.InteropServices; + +namespace Examples +{ + /// + /// Example of using GS DLL as a ps2pdf converter. + /// + /// Port of Ghostscript Example code at http://pages.cs.wisc.edu/~ghost/doc/cvs/API.htm + class Example1 + { + public const String KernelDllName = "kernel32.dll"; + public const String GhostscriptDllDirectory = @"C:\Program Files\gs\gs8.71\bin"; + + [DllImport(KernelDllName, SetLastError = true)] + static extern int SetDllDirectory(string lpPathName); + + static void Main(string[] args) + { + IntPtr minst; + + int code, code1; + string[] gsargv = new string[10]; + gsargv[0] = "ps2pdf"; /* actual value doesn't matter */ + gsargv[1] = "-dNOPAUSE"; + gsargv[2] = "-dBATCH"; + gsargv[3] = "-dSAFER"; + gsargv[4] = "-sDEVICE=pdfwrite"; + gsargv[5] = "-sOutputFile=out.pdf"; + gsargv[6] = "-c"; + gsargv[7] = ".setpdfwrite"; + gsargv[8] = "-f"; + gsargv[9] = @"Files\input.ps"; + + SetDllDirectory(GhostscriptDllDirectory); + + code = API.CreateAPIInstance(out minst, IntPtr.Zero); + if (code < 0) + { + System.Environment.Exit(1); + } + code = API.InitAPI(minst, gsargv.Length, gsargv); + code1 = API.ExitAPI(minst); + if ((code == 0) || (code == (int)API.GhostscriptErrorCode.e_Quit)) + { + code = code1; + } + + API.DeleteAPIInstance(minst); + if ((code == 0) || (code == (int)API.GhostscriptErrorCode.e_Quit)) + { + System.Environment.Exit(0); + } + System.Environment.Exit(1); + } + } +} diff --git a/Examples/GhostscriptSharpExamples/Example2.cs b/Examples/GhostscriptSharpExamples/Example2.cs new file mode 100644 index 0000000..552425b --- /dev/null +++ b/Examples/GhostscriptSharpExamples/Example2.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.Text; +using GhostscriptSharp; +using System.Runtime.InteropServices; + +namespace Examples +{ + /// + /// Similar to command line gs + /// + /// Port of Ghostscript Example code at http://pages.cs.wisc.edu/~ghost/doc/cvs/API.htm + class Example2 + { + public const String KernelDllName = "kernel32.dll"; + public const String GhostscriptDllDirectory = @"C:\Program Files\gs\gs8.71\bin"; + public static String start_string = "systemdict /start get exec" + System.Environment.NewLine; + + [DllImport(KernelDllName, SetLastError = true)] + static extern int SetDllDirectory(string lpPathName); + + [DllImport(KernelDllName, EntryPoint = "RtlMoveMemory", SetLastError = true)] + static extern int CopyMemory(IntPtr dest, IntPtr source, Int64 count); + + static void Main(string[] args) + { + #region StdIn Handler + StringBuilder sbInput = new StringBuilder(); + // This is very slow, especially because Ghostscript asks for input 1 character at a time + API.StdinCallback stdin = (caller_handle, str, n) => + { + if (n == 0) + { + str = IntPtr.Zero; + return 0; + } + if (sbInput.Length == 0) + { + sbInput.AppendLine(Console.ReadLine()); + } + if (sbInput.Length > 0) + { + int len = (sbInput.Length < n) ? sbInput.Length : n; + byte[] b = ASCIIEncoding.ASCII.GetBytes(sbInput.ToString(0, len)); + GCHandle cHandle = GCHandle.Alloc(b, GCHandleType.Pinned); + IntPtr cPtr = cHandle.AddrOfPinnedObject(); + Int64 copyLen = (long)len; + CopyMemory(str, cPtr, copyLen); + cPtr = IntPtr.Zero; + cHandle.Free(); + sbInput.Remove(0, len); + return len; + } + return 0; + }; + #endregion + #region StdOut Handler + API.StdoutCallback stdout = (caller_handle, buf, len) => + { + Console.Write(buf.Substring(0, len)); + return len; + }; + #endregion + #region StdErr Handler + API.StdoutCallback stderr = (caller_handle, buf, len) => + { + Console.Error.Write(buf.Substring(0, len)); + return len; + }; + #endregion + + string[] gsargv = new string[args.Length + 1]; + gsargv[0] = "GhostscriptSharp"; /* actual value doesn't matter */ + Array.Copy(args, 0, gsargv, 1, args.Length); + + IntPtr minst; + int code, code1; + int exit_code; + + SetDllDirectory(GhostscriptDllDirectory); + + code = API.CreateAPIInstance(out minst, IntPtr.Zero); + if (code < 0) + { + System.Environment.Exit(1); + } + API.Set_Stdio(minst, stdin, stdout, stderr); + code = API.InitAPI(minst, gsargv.Length, gsargv); + if (code == 0) + { + API.RunString(minst, start_string, 0, out exit_code); + } + code1 = API.ExitAPI(minst); + if ((code == 0) || (code == (int)API.GhostscriptErrorCode.e_Quit)) + { + code = code1; + } + + API.DeleteAPIInstance(minst); + if ((code == 0) || (code == (int)API.GhostscriptErrorCode.e_Quit)) + { + System.Environment.Exit(0); + } + System.Environment.Exit(1); + } + } +} diff --git a/Examples/GhostscriptSharpExamples/Example3.cs b/Examples/GhostscriptSharpExamples/Example3.cs new file mode 100644 index 0000000..699003d --- /dev/null +++ b/Examples/GhostscriptSharpExamples/Example3.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Text; +using GhostscriptSharp; +using System.Runtime.InteropServices; + +namespace Examples +{ + /// + /// Shows how to feed GhostscriptSharp piecemeal + /// + /// When feeding Ghostscript piecemeal buffers, one can use the normal operators to configure things and invoke library routines. The following example + /// would cause Ghostscript to open and process the file named "example.pdf" as if it had been passed as an argument to gsapi_init_with_args(). + /// code = gsapi_run_string(minst, "(example.pdf) .runlibfile", 0, &exit_code); + /// Port of Ghostscript Example code at http://pages.cs.wisc.edu/~ghost/doc/cvs/API.htm + class Example3 + { + public const String KernelDllName = "kernel32.dll"; + public const String GhostscriptDllDirectory = @"C:\Program Files\gs\gs8.71\bin"; + public static String command = "1 2 add == flush" + System.Environment.NewLine; + + [DllImport(KernelDllName, SetLastError = true)] + static extern int SetDllDirectory(string lpPathName); + + [DllImport(KernelDllName, EntryPoint = "RtlMoveMemory", SetLastError = true)] + static extern int CopyMemory(IntPtr dest, IntPtr source, Int64 count); + + static void Main(string[] args) + { + string[] gsargv = new string[args.Length + 1]; + gsargv[0] = "GhostscriptSharp"; /* actual value doesn't matter */ + Array.Copy(args, 0, gsargv, 1, args.Length); + + IntPtr minst; + int code, code1; + int exit_code; + + SetDllDirectory(GhostscriptDllDirectory); + + code = API.CreateAPIInstance(out minst, IntPtr.Zero); + if (code < 0) + { + System.Environment.Exit(1); + } + code = API.InitAPI(minst, gsargv.Length, gsargv); + if (code == 0) + { + API.RunStringBegin(minst, 0, out exit_code); + API.RunStringContinue(minst, command, Convert.ToUInt16(command.Length), 0, out exit_code); + API.RunStringContinue(minst, "qu", 2u, 0, out exit_code); + API.RunStringContinue(minst, "it", 2u, 0, out exit_code); + API.RunStringEnd(minst, 0, out exit_code); + } + code1 = API.ExitAPI(minst); + if ((code == 0) || (code == (int)API.GhostscriptErrorCode.e_Quit)) + { + code = code1; + } + + API.DeleteAPIInstance(minst); + int returnValue = 1; + if ((code == 0) || (code == (int)API.GhostscriptErrorCode.e_Quit)) + { + returnValue = 0; + } + Console.WriteLine("Example3 completed with exit value {0}", returnValue); + Console.WriteLine("Press any key to exit."); + Console.ReadKey(); + System.Environment.Exit(returnValue); + } + } +} diff --git a/Examples/GhostscriptSharpExamples/Files/input.ps b/Examples/GhostscriptSharpExamples/Files/input.ps new file mode 100644 index 0000000000000000000000000000000000000000..5ea6b912104705502e5d5b82119c156195963572 GIT binary patch literal 21503 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

)yhucp$58fBxY2bBl-d|Qtv`GAOvxpFbtZR{ zG7q|zq%(NlaPzhl^7rbLK2@fmeK@ax!d0{bBpDe_Z*U+hRfwV_m+^S5{!x52+@-jl z!+Bh3siSNV-G+hWMC{dX4&eZIjH1yV-ARN?EV-aV#q|qUH{keS=MI;b*Q#)>W%L2d zUWU6VY$O}SE3Dy!l1d{V#Or3{(Ib421Kc(cp?AK73m@6`4X(17B#j}2*d?uCrk1ZZ zu~5S-nVfO?M7wFiPj2!Rn>bRt*+B4xdlsrw+eR3-yj8ulKM(^-9%T?$LOx>daJQor z56?)>aZheZk;LOS1u5nuME2k0}Fn?lZ7x&BQkTQA*@=iyr)|74e&7+FoVYdfmlM69;mR;_m9 z%KL3opWt#$N=O1B4GzfW70OuzCzV38RcqE8&+2U%?NKzfJDUa0Mbvi|fIZ8D*FSpL z=4C^?y#4XAUFY*4L2$E=`*rqA0ogV4v+NVRDAnpTp9#u7&L@OL2M3R-mJGH%*{H5< z)g?C}c97?idCri@3SPtEGr{@ZraV;6FS>GRh!>AQOxDEa;sVVKl%0R&{WlI11ubW5 zNUHD#`)6r(SuFT}uW}t02O;3-P5oF5%5ni9@+R^@^-goA-sC2xVb_gXaWSoLjhi`gr_H$2Rp~2L zpXVfnW7I)+B~nvuOfuS;h~<2vdud%^7O!gKT(P(O4gnDuSC$J^Mdw^X*L|>nK)@)A zfmj$fWFr$Cw;SRL36isS2f#`Y9yZ`;tcFXtiGeH^VVYFozu~^QpXjb;(y6c)Aq9rq z!+m2PQ6E3IgKMPtz7(H5{xcln1QriB`1nkSJuJSCK`so^qB&FZP`a_6FLj#3b-KE4 z;6?tdfj>{obaM$^G0^3HPfa|8-_>rBAquqc#~e6Z(UMht626_;+b`_*Gj_yEF68rZ zA)`dS5)~F=;npv~gPleuK5yE+0xvSuq*mR!fj2iB(KFs%*zYH`nu{tH>Ip(&ES zUD0}noRjr=60;#1jU|QbEmm_1Yj)mvIBWA-U7-XYP@D{7E-z$Ad5@`Lk0e0mwD4pp z4yD34*ny&q7htSpHH~j2#~0KJWw;7d%IP0vWZDcyc;_354oShtKA+7e@YsikxSOPI zxfG3pLrwL7p@0|4<)?OCn=gJ*?D46exj(?dO14jj9i3U;Y>)@uB7AphIzDF9w}8e5n(w%F`6 z<@XA(XWkkpuHku&CW7sqofgi8Y`9MXKD|WQ!~az6>h?@|+o)-`nrt8+qQIvYXs1Ux zPDA+eS;6wd30t4R^bq%7E%7Qub++py>R_zyk~sp5P(oXJKl1ep+|l(F4u^jyACtgl zt^2|PB13#x>B;{+t$%fpD|2B7+(4dEwVt)x95T<|srrKfSkS}Q@>vQ3;q)Lsextru zupKNo$0rt%G3EJT_lTo{)$cV@ik4p{be~jw@YO{i_;xkjyZl7sBi~;Xa9k1?@qATf zd;0Ou6B742@bcpsb|3gLp!LsLd=BOUiQ{aUV15j&av<@sYUdGAL(RVQV^9xs*{frh u;&Qinsu4pLu}D + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {6B6799EB-0AF5-4AED-8164-A7DA275EFC9D} + Exe + Properties + Examples + Examples + v2.0 + 512 + Examples.Example1 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + {56605627-E6FA-4F47-9440-FB877CEA5C84} + GhostscriptSharp + + + + + PreserveNewest + + + + + \ No newline at end of file diff --git a/Examples/GhostscriptSharpExamples/Properties/AssemblyInfo.cs b/Examples/GhostscriptSharpExamples/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..5eee138 --- /dev/null +++ b/Examples/GhostscriptSharpExamples/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Examples")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Examples")] +[assembly: AssemblyCopyright("Copyright © 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("289eb845-b037-437a-98d9-1fe7eb6e6791")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/GhostScriptSharp/EventArgs.cs b/GhostScriptSharp/EventArgs.cs new file mode 100644 index 0000000..7e7c16c --- /dev/null +++ b/GhostScriptSharp/EventArgs.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace GhostscriptSharp +{ + public class StdOutputEventArgs : EventArgs + { + protected readonly String _output; + public String Output { get { return _output; } } + + public StdOutputEventArgs(String output) + : base() + { + _output = output; + } + } +} diff --git a/GhostScriptSharp/GhostscriptException.cs b/GhostScriptSharp/GhostscriptException.cs new file mode 100644 index 0000000..9aac5f6 --- /dev/null +++ b/GhostScriptSharp/GhostscriptException.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; + +using System.Text; +using System.Runtime.InteropServices; + +namespace GhostscriptSharp +{ + public class GhostscriptException : ExternalException + { + protected readonly API.GhostscriptErrorCode gsErrorCode; + public API.GhostscriptErrorCode GsErrorCode + { + get { return gsErrorCode; } + } + + public GhostscriptException() + : base() + { + } + + public GhostscriptException(string message) + : base(message) + { + gsErrorCode = API.GhostscriptErrorCode.UNKNOWN; + } + + public GhostscriptException(string message, Exception inner) + : base(message, inner) + { + } + + public GhostscriptException(string message, int errorCode) + : base(message, errorCode) + { + //ex.ErrorCode + if (Enum.IsDefined(typeof(API.GhostscriptErrorCode), errorCode)) + { + gsErrorCode = (API.GhostscriptErrorCode)errorCode; + } + else + { + gsErrorCode = API.GhostscriptErrorCode.UNKNOWN; + } + } + } +} diff --git a/GhostScriptSharp/GhostscriptManager.cs b/GhostScriptSharp/GhostscriptManager.cs new file mode 100644 index 0000000..257f7e0 --- /dev/null +++ b/GhostScriptSharp/GhostscriptManager.cs @@ -0,0 +1,344 @@ +using System; +using System.Collections.Generic; + +using System.Text; +using System.Runtime.InteropServices; +using System.IO; + +namespace GhostscriptSharp +{ + public class GhostscriptManager : IDisposable + { + #region Constants + + public const String KernelDllName = "kernel32.dll"; + + #endregion + + #region Hooks into the kernel32 DLL (for loading unmanaged code) + [DllImport(KernelDllName, SetLastError = true)] + static extern IntPtr LoadLibrary(string lpFileName); + + [DllImport(KernelDllName, SetLastError = true)] + static extern int FreeLibrary(IntPtr hModule); + + [DllImport(KernelDllName, SetLastError = true)] + static extern int SetDllDirectory(string lpPathName); + #endregion + + #region Globals + + protected static object resourceLock = new object(); + + protected static String[] defaultArgs; + protected static String[] DefaultArgs + { + get + { + if (defaultArgs == null) + { + defaultArgs = new String[] { + "-dPARANOIDSAFER", // Run in safe mode + "-dBATCH", // Exit after completing commands + "-dNOPAUSE", // Do not pause for each page + "-dNOPROMPT" // Don't prompt for user input + }; + } + return defaultArgs; + } + } + + protected static String ghostscriptLibraryPath; + public static String GhostscriptLibraryPath + { + get { return ghostscriptLibraryPath == null ? String.Empty : ghostscriptLibraryPath; } + set { ghostscriptLibraryPath = value; } + } + + protected static GhostscriptManager _instance; + public static GhostscriptManager GetInstance() + { + lock (resourceLock) + { + if (_instance == null) + { + _instance = new GhostscriptManager(); + } + return _instance; + } + } + + #endregion + + protected IntPtr libraryHandle; + + protected bool revisionInfoLoaded; + protected String productName; + protected String copyright; + protected Int32 revision; + protected Int32 revisionDate; + + protected GhostscriptSettings settings; + public GhostscriptSettings Settings + { + get { return settings; } + } + + protected GhostscriptManager() + { + revisionInfoLoaded = false; + libraryHandle = IntPtr.Zero; + + LoadGhostscriptLibrary(); + + this.settings = new GhostscriptSettings(); + } + + protected void LoadGhostscriptLibrary() + { + SetDllDirectory(GhostscriptLibraryPath); + libraryHandle = LoadLibrary(API.GhostscriptDllName); + } + + protected void UnloadGhostscriptLibrary() + { + if (libraryHandle != IntPtr.Zero) + { + FreeLibrary(libraryHandle); + libraryHandle = IntPtr.Zero; + } + } + + ///

+ /// Run the Ghostscript interpreter providing the output file and input file(s) + /// + /// The path to create the output file. Put '%d' the path to create multiple numbered files, one for each page + /// One or more input files + public void DoConvert(String outputPath, params String[] inputPaths) + { + IntPtr gsInstancePtr; + lock (resourceLock) + { + API.CreateAPIInstance(out gsInstancePtr, IntPtr.Zero); + try + { + if (StdOut != null || StdErr != null) + { + API.StdoutCallback stdout; + #region Set StdOut + if (StdOut != null) + { + stdout = (caller_handle, buf, len) => + { + StdOut(this, new StdOutputEventArgs(buf.Substring(0, len))); + return len; + }; + } + else + { + stdout = EmptyStdoutCallback; + } + #endregion + API.StdoutCallback stderr; + #region Set StdErr + if (StdErr != null) + { + stderr = (caller_handle, buf, len) => + { + StdOut(this, new StdOutputEventArgs(buf.Substring(0, len))); + return len; + }; + } + else + { + stderr = EmptyStdoutCallback; + } + #endregion + API.Set_Stdio(gsInstancePtr, EmptyStdinCallback, stdout, stderr); + } + String[] args = null; + { + List lArgs = new List(); + lArgs.Add("GhostscriptSharp"); // First arg is ignored, corresponds to argv[0] + lArgs.AddRange(GhostscriptManager.DefaultArgs); + lArgs.AddRange(this.Settings.GetGhostscriptArgs()); + lArgs.Add(String.Format("-sOutputFile={0}", outputPath)); + lArgs.AddRange(inputPaths); + args = lArgs.ToArray(); + } + int result = API.InitAPI(gsInstancePtr, args.Length, args); + if (result < 0) + { + throw new GhostscriptException("Ghostscript conversion error", result); + } + } + finally + { + API.ExitAPI(gsInstancePtr); + API.DeleteAPIInstance(gsInstancePtr); + } + } + } + + #region Revision Info Properties + + /// + /// Name of the product obtained from the Ghostscript DLL e.g. "GPL Ghostscript" + /// + public String ProductName + { + get + { + if (!revisionInfoLoaded) + { + LoadRevisionInfo(); + } + return productName; + } + } + + /// + /// Copyright Information obtained from the Ghostscript DLL + /// + public String Copyright + { + get + { + if (!revisionInfoLoaded) + { + LoadRevisionInfo(); + } + return copyright; + } + } + + /// + /// Revision Number of the Ghostscript DLL e.g. 871 for v8.71 + /// + public Int32 Revision + { + get + { + if (!revisionInfoLoaded) + { + LoadRevisionInfo(); + } + return revision; + } + } + + /// + /// Revision Date of the Ghostscript DLL in the format yyyyMMdd + /// + public Int32 RevisionDate + { + get + { + if (!revisionInfoLoaded) + { + LoadRevisionInfo(); + } + return revisionDate; + } + } + + /// + /// Get Ghostscript Library revision info + /// + /// + /// + /// + /// + protected void LoadRevisionInfo() + { + API.GS_Revision rev; + + API.GetRevision(out rev, API.GS_Revision_Size_Bytes); + this.productName = rev.strProduct; + this.copyright = rev.strCopyright; + this.revision = rev.intRevision; + this.revisionDate = rev.intRevisionDate; + this.revisionInfoLoaded = true; + } + + #endregion + + #region stdin, stdout, stderr handlers + + //public delegate void StdinReader(StringBuilder input); + public delegate void StdOutputHandler(object sender, StdOutputEventArgs args); + + //public event StdinReader StdIn; + public event StdOutputHandler StdOut; + public event StdOutputHandler StdErr; + + /// + /// "Default" implementation of StdinCallback - gives Ghostscript EOF whenever it requests input + /// + /// + /// + /// + /// 0 (EOF) whenever GS requests input + protected API.StdinCallback EmptyStdinCallback = new API.StdinCallback(delegate(IntPtr caller_handle, IntPtr buf, Int32 len) + { + return 0; // return EOF always + }); + //protected API.StdinCallback EmptyStdinCallback = (caller_handle, buf, len) => + //{ + // return 0; // return EOF always + //}; + + /// + /// "Default" implementation of StdoutCallback - does nothing with output, returns all characters handled + /// + /// + /// + /// + /// len (the number of characters handled) whenever GS outputs anything + protected API.StdoutCallback EmptyStdoutCallback = (caller_handle, buf, len) => + { + return len; // return all bytes handled + }; + + #endregion + + #region IDisposable Members + + public void Dispose() + { + UnloadGhostscriptLibrary(); + } + + #endregion + + #region Convenience Methods + + /// + /// Convert a postscript file to a pdf + /// + /// The path to create the output file. Put '%d' the path to create multiple numbered files, one for each page + /// One or more input files + public static void PsToPdf(String outputPath, params String[] inputPaths) + { + GhostscriptManager gsm = GhostscriptManager.GetInstance(); + bool libraryLoaded = (gsm.libraryHandle != IntPtr.Zero); + if (!libraryLoaded) + { + gsm.LoadGhostscriptLibrary(); + } + GhostscriptSettings oldSettings = gsm.Settings; + gsm.settings = new GhostscriptSettings(); + gsm.Settings.Device = GhostscriptSharp.Settings.GhostscriptDevices.pdfwrite; + gsm.Settings.Page.AllPages = true; + gsm.Settings.Quiet = true; + gsm.DoConvert(outputPath, inputPaths); + if (!libraryLoaded) + { + gsm.UnloadGhostscriptLibrary(); + } + gsm.settings = oldSettings; + } + + #endregion + } +} diff --git a/GhostScriptSharp/GhostscriptSharp.cs b/GhostScriptSharp/GhostscriptSharp.cs index 581080b..7a8f9c0 100644 --- a/GhostScriptSharp/GhostscriptSharp.cs +++ b/GhostScriptSharp/GhostscriptSharp.cs @@ -1,28 +1,36 @@ +/* ================================= MIT LICENSE ================================= + * + * Copyright (c) 2009 Matthew Ephraim + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ================================= MIT LICENSE ================================= */ + +/* =============================================================================== + * + * This code taken from Matthew Ephraim's GhostscriptSharp February 2011 + * + * https://github.com/mephraim/ghostscriptsharp/blob/master/GhostScriptSharp/GhostscriptSharp.cs + * + * =============================================================================== */ + using System; +using System.Collections.Generic; + using System.Text; using System.Runtime.InteropServices; namespace GhostscriptSharp { - /// - /// Wraps the Ghostscript API with a C# interface - /// - public class GhostscriptWrapper - { - #region Hooks into Ghostscript DLL - [DllImport("gsdll32.dll", EntryPoint = "gsapi_new_instance")] - private static extern int CreateAPIInstance(out IntPtr pinstance, IntPtr caller_handle); - - [DllImport("gsdll32.dll", EntryPoint = "gsapi_init_with_args")] - private static extern int InitAPI(IntPtr instance, int argc, string[] argv); - - [DllImport("gsdll32.dll", EntryPoint = "gsapi_exit")] - private static extern int ExitAPI(IntPtr instance); - - [DllImport("gsdll32.dll", EntryPoint = "gsapi_delete_instance")] - private static extern void DeleteAPIInstance(IntPtr instance); - #endregion - + /// + /// Wraps the Ghostscript API with a C# interface + /// + public class GhostscriptWrapper + { #region Globals private static readonly string[] ARGS = new string[] { @@ -84,10 +92,10 @@ private static void CallAPI(string[] args) IntPtr gsInstancePtr; lock (resourceLock) { - CreateAPIInstance(out gsInstancePtr, IntPtr.Zero); + API.CreateAPIInstance(out gsInstancePtr, IntPtr.Zero); try { - int result = InitAPI(gsInstancePtr, args.Length, args); + int result = API.InitAPI(gsInstancePtr, args.Length, args); if (result < 0) { @@ -111,8 +119,8 @@ private static void CallAPI(string[] args) /// private static void Cleanup(IntPtr gsInstancePtr) { - ExitAPI(gsInstancePtr); - DeleteAPIInstance(gsInstancePtr); + API.ExitAPI(gsInstancePtr); + API.DeleteAPIInstance(gsInstancePtr); } /// @@ -136,7 +144,7 @@ private static string[] GetArgs(string inputPath, s.Page.Start = firstPage; s.Page.End = lastPage; s.Resolution = new System.Drawing.Size(width, height); - + Settings.GhostscriptPageSize pageSize = new Settings.GhostscriptPageSize(); pageSize.Native = GhostscriptSharp.Settings.GhostscriptPageSizes.a7; s.Size = pageSize; @@ -217,257 +225,4 @@ private static string[] GetArgs(string inputPath, } } - - /// - /// Ghostscript settings - /// - public class GhostscriptSettings - { - private Settings.GhostscriptDevices _device; - private Settings.GhostscriptPages _pages = new Settings.GhostscriptPages(); - private System.Drawing.Size _resolution; - private Settings.GhostscriptPageSize _size = new Settings.GhostscriptPageSize(); - - public Settings.GhostscriptDevices Device - { - get { return this._device; } - set { this._device = value; } - } - - public Settings.GhostscriptPages Page - { - get { return this._pages; } - set { this._pages = value; } - } - - public System.Drawing.Size Resolution - { - get { return this._resolution; } - set { this._resolution = value; } - } - - public Settings.GhostscriptPageSize Size - { - get { return this._size; } - set { this._size = value; } - } - } } - -namespace GhostscriptSharp.Settings -{ - /// - /// Which pages to output - /// - public class GhostscriptPages - { - private bool _allPages = true; - private int _start; - private int _end; - - /// - /// Output all pages avaialble in document - /// - public bool AllPages - { - set - { - this._start = -1; - this._end = -1; - this._allPages = true; - } - get - { - return this._allPages; - } - } - - /// - /// Start output at this page (1 for page 1) - /// - public int Start - { - set - { - this._allPages = false; - this._start = value; - } - get - { - return this._start; - } - } - - /// - /// Page to stop output at - /// - public int End - { - set - { - this._allPages = false; - this._end = value; - } - get - { - return this._end; - } - } - } - - /// - /// Output devices for GhostScript - /// - public enum GhostscriptDevices - { - UNDEFINED, - png16m, - pnggray, - png256, - png16, - pngmono, - pngalpha, - jpeg, - jpeggray, - tiffgray, - tiff12nc, - tiff24nc, - tiff32nc, - tiffsep, - tiffcrle, - tiffg3, - tiffg32d, - tiffg4, - tifflzw, - tiffpack, - faxg3, - faxg32d, - faxg4, - bmpmono, - bmpgray, - bmpsep1, - bmpsep8, - bmp16, - bmp256, - bmp16m, - bmp32b, - pcxmono, - pcxgray, - pcx16, - pcx256, - pcx24b, - pcxcmyk, - psdcmyk, - psdrgb, - pdfwrite, - pswrite, - epswrite, - pxlmono, - pxlcolor - } - - /// - /// Output document physical dimensions - /// - public class GhostscriptPageSize - { - private GhostscriptPageSizes _fixed; - private System.Drawing.Size _manual; - - /// - /// Custom document size - /// - public System.Drawing.Size Manual - { - set - { - this._fixed = GhostscriptPageSizes.UNDEFINED; - this._manual = value; - } - get - { - return this._manual; - } - } - - /// - /// Standard paper size - /// - public GhostscriptPageSizes Native - { - set - { - this._fixed = value; - this._manual = new System.Drawing.Size(0, 0); - } - get - { - return this._fixed; - } - } - - } - - /// - /// Native page sizes - /// - /// - /// Missing 11x17 as enums can't start with a number, and I can't be bothered - /// to add in logic to handle it - if you need it, do it yourself. - /// - public enum GhostscriptPageSizes - { - UNDEFINED, - ledger, - legal, - letter, - lettersmall, - archE, - archD, - archC, - archB, - archA, - a0, - a1, - a2, - a3, - a4, - a4small, - a5, - a6, - a7, - a8, - a9, - a10, - isob0, - isob1, - isob2, - isob3, - isob4, - isob5, - isob6, - c0, - c1, - c2, - c3, - c4, - c5, - c6, - jisb0, - jisb1, - jisb2, - jisb3, - jisb4, - jisb5, - jisb6, - b0, - b1, - b2, - b3, - b4, - b5, - flsa, - flse, - halfletter - } -} \ No newline at end of file diff --git a/GhostScriptSharp/GhostscriptSharp.csproj b/GhostScriptSharp/GhostscriptSharp.csproj index b58c8a0..a6b9e33 100644 --- a/GhostScriptSharp/GhostscriptSharp.csproj +++ b/GhostScriptSharp/GhostscriptSharp.csproj @@ -46,8 +46,16 @@ + + + + + + + +