/SPThumbnailExtender/SPThumbnailExtender/PDF/GhostScriptSharp.cs

# · C# · 482 lines · 346 code · 51 blank · 85 comment · 15 complexity · a2834ee7956e416a236cf0262be7c068 MD5 · raw file

  1. using System;
  2. using System.Text;
  3. using System.Runtime.InteropServices;
  4. namespace GhostscriptSharp
  5. {
  6. /// <summary>
  7. /// Wraps the Ghostscript API with a C# interface
  8. /// http://www.ghostscript.com/download/gsdnld.html
  9. /// </summary>
  10. public class GhostscriptWrapper
  11. {
  12. #region Hooks into Ghostscript DLL
  13. [DllImport("gsdll64.dll", EntryPoint = "gsapi_new_instance")]
  14. private static extern int CreateAPIInstance(out IntPtr pinstance, IntPtr caller_handle);
  15. [DllImport("gsdll64.dll", EntryPoint = "gsapi_init_with_args")]
  16. private static extern int InitAPI(IntPtr instance, int argc, string[] argv);
  17. [DllImport("gsdll64.dll", EntryPoint = "gsapi_exit")]
  18. private static extern int ExitAPI(IntPtr instance);
  19. [DllImport("gsdll64.dll", EntryPoint = "gsapi_delete_instance")]
  20. private static extern void DeleteAPIInstance(IntPtr instance);
  21. #endregion
  22. #region Globals
  23. private static readonly string[] ARGS = new string[] {
  24. // Keep gs from writing information to standard output
  25. "-q",
  26. "-dQUIET",
  27. "-dPARANOIDSAFER", // Run this command in safe mode
  28. "-dBATCH", // Keep gs from going into interactive mode
  29. "-dNOPAUSE", // Do not prompt and pause for each page
  30. "-dNOPROMPT", // Disable prompts for user interaction
  31. "-dMaxBitmap=500000000", // Set high for better performance
  32. "-dNumRenderingThreads=4", // Multi-core, come-on!
  33. // Configure the output anti-aliasing, resolution, etc
  34. "-dAlignToPixels=0",
  35. "-dGridFitTT=0",
  36. "-dTextAlphaBits=4",
  37. "-dGraphicsAlphaBits=4"
  38. };
  39. #endregion
  40. /// <summary>
  41. /// Generates a thumbnail jpg for the pdf at the input path and saves it
  42. /// at the output path
  43. /// </summary>
  44. public static void GeneratePageThumb(string inputPath, string outputPath, int page, int width, int height)
  45. {
  46. GeneratePageThumbs(inputPath, outputPath, page, page, width, height);
  47. }
  48. /// <summary>
  49. /// Generates a collection of thumbnail jpgs for the pdf at the input path
  50. /// starting with firstPage and ending with lastPage.
  51. /// Put "%d" somewhere in the output path to have each of the pages numbered
  52. /// </summary>
  53. public static void GeneratePageThumbs(string inputPath, string outputPath, int firstPage, int lastPage, int width, int height)
  54. {
  55. CallAPI(GetArgs(inputPath, outputPath, firstPage, lastPage, width, height));
  56. }
  57. /// <summary>
  58. /// Rasterises a PDF into selected format
  59. /// </summary>
  60. /// <param name="inputPath">PDF file to convert</param>
  61. /// <param name="outputPath">Destination file</param>
  62. /// <param name="settings">Conversion settings</param>
  63. public static void GenerateOutput(string inputPath, string outputPath, GhostscriptSettings settings)
  64. {
  65. CallAPI(GetArgs(inputPath, outputPath, settings));
  66. }
  67. public static void GetPagesCount(string inputPath, string outputPath)
  68. {
  69. }
  70. /// <summary>
  71. /// Calls the Ghostscript API with a collection of arguments to be passed to it
  72. /// </summary>
  73. private static void CallAPI(string[] args)
  74. {
  75. // Get a pointer to an instance of the Ghostscript API and run the API with the current arguments
  76. IntPtr gsInstancePtr;
  77. lock (resourceLock)
  78. {
  79. CreateAPIInstance(out gsInstancePtr, IntPtr.Zero);
  80. try
  81. {
  82. int result = InitAPI(gsInstancePtr, args.Length, args);
  83. if (result < 0)
  84. {
  85. throw new ExternalException("Ghostscript conversion error", result);
  86. }
  87. }
  88. finally
  89. {
  90. Cleanup(gsInstancePtr);
  91. }
  92. }
  93. }
  94. /// <summary>
  95. /// GS can only support a single instance, so we need to bottleneck any multi-threaded systems.
  96. /// </summary>
  97. private static object resourceLock = new object();
  98. /// <summary>
  99. /// Frees up the memory used for the API arguments and clears the Ghostscript API instance
  100. /// </summary>
  101. private static void Cleanup(IntPtr gsInstancePtr)
  102. {
  103. ExitAPI(gsInstancePtr);
  104. DeleteAPIInstance(gsInstancePtr);
  105. }
  106. /// <summary>
  107. /// Returns an array of arguments to be sent to the Ghostscript API
  108. /// </summary>
  109. /// <param name="inputPath">Path to the source file</param>
  110. /// <param name="outputPath">Path to the output file</param>
  111. /// <param name="firstPage">The page of the file to start on</param>
  112. /// <param name="lastPage">The page of the file to end on</param>
  113. private static string[] GetArgs(string inputPath,
  114. string outputPath,
  115. int firstPage,
  116. int lastPage,
  117. int width,
  118. int height)
  119. {
  120. // To maintain backwards compatibility, this method uses previous hardcoded values.
  121. GhostscriptSettings s = new GhostscriptSettings();
  122. s.Device = Settings.GhostscriptDevices.jpeg;
  123. s.Page.Start = firstPage;
  124. s.Page.End = lastPage;
  125. s.Resolution = new System.Drawing.Size(width, height);
  126. Settings.GhostscriptPageSize pageSize = new Settings.GhostscriptPageSize();
  127. pageSize.Native = GhostscriptSharp.Settings.GhostscriptPageSizes.a7;
  128. s.Size = pageSize;
  129. return GetArgs(inputPath, outputPath, s);
  130. }
  131. /// <summary>
  132. /// Returns an array of arguments to be sent to the Ghostscript API
  133. /// </summary>
  134. /// <param name="inputPath">Path to the source file</param>
  135. /// <param name="outputPath">Path to the output file</param>
  136. /// <param name="settings">API parameters</param>
  137. /// <returns>API arguments</returns>
  138. private static string[] GetArgs(string inputPath,
  139. string outputPath,
  140. GhostscriptSettings settings)
  141. {
  142. System.Collections.ArrayList args = new System.Collections.ArrayList(ARGS);
  143. if (settings.Device == Settings.GhostscriptDevices.UNDEFINED)
  144. {
  145. throw new ArgumentException("An output device must be defined for Ghostscript", "GhostscriptSettings.Device");
  146. }
  147. if (settings.Page.AllPages == false && (settings.Page.Start <= 0 && settings.Page.End < settings.Page.Start))
  148. {
  149. throw new ArgumentException("Pages to be printed must be defined.", "GhostscriptSettings.Pages");
  150. }
  151. if (settings.Resolution.IsEmpty)
  152. {
  153. throw new ArgumentException("An output resolution must be defined", "GhostscriptSettings.Resolution");
  154. }
  155. if (settings.Size.Native == Settings.GhostscriptPageSizes.UNDEFINED && settings.Size.Manual.IsEmpty)
  156. {
  157. throw new ArgumentException("Page size must be defined", "GhostscriptSettings.Size");
  158. }
  159. // Output device
  160. args.Add(String.Format("-sDEVICE={0}", settings.Device));
  161. // Pages to output
  162. if (settings.Page.AllPages)
  163. {
  164. args.Add("-dFirstPage=1");
  165. }
  166. else
  167. {
  168. args.Add(String.Format("-dFirstPage={0}", settings.Page.Start));
  169. if (settings.Page.End >= settings.Page.Start)
  170. {
  171. args.Add(String.Format("-dLastPage={0}", settings.Page.End));
  172. }
  173. }
  174. // Page size
  175. if (settings.Size.Native == Settings.GhostscriptPageSizes.UNDEFINED)
  176. {
  177. args.Add(String.Format("-dDEVICEWIDTHPOINTS={0}", settings.Size.Manual.Width));
  178. args.Add(String.Format("-dDEVICEHEIGHTPOINTS={0}", settings.Size.Manual.Height));
  179. }
  180. else
  181. {
  182. args.Add(String.Format("-sPAPERSIZE={0}", settings.Size.Native.ToString()));
  183. }
  184. // Page resolution
  185. args.Add(String.Format("-dDEVICEXRESOLUTION={0}", settings.Resolution.Width));
  186. args.Add(String.Format("-dDEVICEYRESOLUTION={0}", settings.Resolution.Height));
  187. // Files
  188. args.Add(String.Format("-sOutputFile={0}", outputPath));
  189. args.Add(inputPath);
  190. return (string[])args.ToArray(typeof(string));
  191. }
  192. }
  193. /// <summary>
  194. /// Ghostscript settings
  195. /// </summary>
  196. public class GhostscriptSettings
  197. {
  198. private Settings.GhostscriptDevices _device;
  199. private Settings.GhostscriptPages _pages = new Settings.GhostscriptPages();
  200. private System.Drawing.Size _resolution;
  201. private Settings.GhostscriptPageSize _size = new Settings.GhostscriptPageSize();
  202. public Settings.GhostscriptDevices Device
  203. {
  204. get { return this._device; }
  205. set { this._device = value; }
  206. }
  207. public Settings.GhostscriptPages Page
  208. {
  209. get { return this._pages; }
  210. set { this._pages = value; }
  211. }
  212. public System.Drawing.Size Resolution
  213. {
  214. get { return this._resolution; }
  215. set { this._resolution = value; }
  216. }
  217. public Settings.GhostscriptPageSize Size
  218. {
  219. get { return this._size; }
  220. set { this._size = value; }
  221. }
  222. }
  223. }
  224. namespace GhostscriptSharp.Settings
  225. {
  226. /// <summary>
  227. /// Which pages to output
  228. /// </summary>
  229. public class GhostscriptPages
  230. {
  231. private bool _allPages = true;
  232. private int _start;
  233. private int _end;
  234. /// <summary>
  235. /// Output all pages avaialble in document
  236. /// </summary>
  237. public bool AllPages
  238. {
  239. set
  240. {
  241. this._start = -1;
  242. this._end = -1;
  243. this._allPages = true;
  244. }
  245. get
  246. {
  247. return this._allPages;
  248. }
  249. }
  250. /// <summary>
  251. /// Start output at this page (1 for page 1)
  252. /// </summary>
  253. public int Start
  254. {
  255. set
  256. {
  257. this._allPages = false;
  258. this._start = value;
  259. }
  260. get
  261. {
  262. return this._start;
  263. }
  264. }
  265. /// <summary>
  266. /// Page to stop output at
  267. /// </summary>
  268. public int End
  269. {
  270. set
  271. {
  272. this._allPages = false;
  273. this._end = value;
  274. }
  275. get
  276. {
  277. return this._end;
  278. }
  279. }
  280. }
  281. /// <summary>
  282. /// Output devices for GhostScript
  283. /// </summary>
  284. public enum GhostscriptDevices
  285. {
  286. UNDEFINED,
  287. png16m,
  288. pnggray,
  289. png256,
  290. png16,
  291. pngmono,
  292. pngalpha,
  293. jpeg,
  294. jpeggray,
  295. tiffgray,
  296. tiff12nc,
  297. tiff24nc,
  298. tiff32nc,
  299. tiffsep,
  300. tiffcrle,
  301. tiffg3,
  302. tiffg32d,
  303. tiffg4,
  304. tifflzw,
  305. tiffpack,
  306. faxg3,
  307. faxg32d,
  308. faxg4,
  309. bmpmono,
  310. bmpgray,
  311. bmpsep1,
  312. bmpsep8,
  313. bmp16,
  314. bmp256,
  315. bmp16m,
  316. bmp32b,
  317. pcxmono,
  318. pcxgray,
  319. pcx16,
  320. pcx256,
  321. pcx24b,
  322. pcxcmyk,
  323. psdcmyk,
  324. psdrgb,
  325. pdfwrite,
  326. pswrite,
  327. epswrite,
  328. pxlmono,
  329. pxlcolor
  330. }
  331. /// <summary>
  332. /// Output document physical dimensions
  333. /// </summary>
  334. public class GhostscriptPageSize
  335. {
  336. private GhostscriptPageSizes _fixed;
  337. private System.Drawing.Size _manual;
  338. /// <summary>
  339. /// Custom document size
  340. /// </summary>
  341. public System.Drawing.Size Manual
  342. {
  343. set
  344. {
  345. this._fixed = GhostscriptPageSizes.UNDEFINED;
  346. this._manual = value;
  347. }
  348. get
  349. {
  350. return this._manual;
  351. }
  352. }
  353. /// <summary>
  354. /// Standard paper size
  355. /// </summary>
  356. public GhostscriptPageSizes Native
  357. {
  358. set
  359. {
  360. this._fixed = value;
  361. this._manual = new System.Drawing.Size(0, 0);
  362. }
  363. get
  364. {
  365. return this._fixed;
  366. }
  367. }
  368. }
  369. /// <summary>
  370. /// Native page sizes
  371. /// </summary>
  372. /// <remarks>
  373. /// Missing 11x17 as enums can't start with a number, and I can't be bothered
  374. /// to add in logic to handle it - if you need it, do it yourself.
  375. /// </remarks>
  376. public enum GhostscriptPageSizes
  377. {
  378. UNDEFINED,
  379. ledger,
  380. legal,
  381. letter,
  382. lettersmall,
  383. archE,
  384. archD,
  385. archC,
  386. archB,
  387. archA,
  388. a0,
  389. a1,
  390. a2,
  391. a3,
  392. a4,
  393. a4small,
  394. a5,
  395. a6,
  396. a7,
  397. a8,
  398. a9,
  399. a10,
  400. isob0,
  401. isob1,
  402. isob2,
  403. isob3,
  404. isob4,
  405. isob5,
  406. isob6,
  407. c0,
  408. c1,
  409. c2,
  410. c3,
  411. c4,
  412. c5,
  413. c6,
  414. jisb0,
  415. jisb1,
  416. jisb2,
  417. jisb3,
  418. jisb4,
  419. jisb5,
  420. jisb6,
  421. b0,
  422. b1,
  423. b2,
  424. b3,
  425. b4,
  426. b5,
  427. flsa,
  428. flse,
  429. halfletter
  430. }
  431. }