PrintHelper.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. using ComPDFKit.Import;
  2. using ComPDFKit.PDFPage;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Drawing;
  6. using System.Drawing.Imaging;
  7. using System.Drawing.Printing;
  8. using System.Linq;
  9. using System.Printing;
  10. using System.Reflection;
  11. using System.Runtime.InteropServices;
  12. using System.Text;
  13. using System.Threading.Tasks;
  14. using System.Windows.Forms;
  15. namespace ComPDFKit.Controls.Printer
  16. {
  17. public enum DuplexingStage
  18. {
  19. None,
  20. FontSide,
  21. BackSide
  22. }
  23. public static class PrintHelper
  24. {
  25. public static readonly PrintServer printServer = new PrintServer();
  26. private static PrintSettingsInfo printSettingsInfo;
  27. private static PrintQueue printQueue;
  28. private static int MaxCopies = 1;
  29. private static bool FinishedFrontSide = false;
  30. private static bool isManualDuplex = false;
  31. private static List<OutputColor> outputColors;
  32. private static List<Duplexing> duplexings;
  33. private static Bitmap blankPageBitmapForPrint;
  34. private static int PrintIndex;
  35. private static void SetPrinterLimits()
  36. {
  37. MaxCopies = printQueue.GetPrintCapabilities().MaxCopyCount.HasValue ? printQueue.GetPrintCapabilities().MaxCopyCount.Value : 1;
  38. outputColors = new List<OutputColor>(printQueue.GetPrintCapabilities().OutputColorCapability);
  39. duplexings = new List<Duplexing>(printQueue.GetPrintCapabilities().DuplexingCapability);
  40. }
  41. public static void PrintDocument(PrintSettingsInfo printSettings)
  42. {
  43. printSettingsInfo = printSettings;
  44. printQueue = printServer.GetPrintQueue(printSettings.PrinterName);
  45. SetPrinterLimits();
  46. if (printSettings.DuplexPrintMod == DuplexPrintMod.None ||
  47. (printSettings.PrintMode is BookletModeInfo bookletModeInfo && bookletModeInfo.Subset != BookletSubset.BothSides))
  48. {
  49. HandlePrintQueue(DuplexingStage.None);
  50. }
  51. else if (printSettings.DuplexPrintMod > 0 ||
  52. (printSettings.PrintMode is BookletModeInfo bookletModeInfo1 && bookletModeInfo1.Subset == BookletSubset.BothSides))
  53. {
  54. if (duplexings.Contains(Duplexing.TwoSidedShortEdge) || duplexings.Contains(Duplexing.TwoSidedLongEdge))
  55. {
  56. HandlePrintQueue(DuplexingStage.None);
  57. }
  58. else
  59. {
  60. if (OnlyOnePage())
  61. {
  62. HandlePrintQueue(DuplexingStage.None);
  63. }
  64. else
  65. {
  66. HandlePrintQueue(DuplexingStage.FontSide);
  67. if (FinishedFrontSide)
  68. {
  69. DialogResult dialogResult = MessageBox.Show("Do you want to print the back side?", "Print", MessageBoxButtons.YesNo);
  70. if (dialogResult == DialogResult.OK)
  71. {
  72. HandlePrintQueue(DuplexingStage.BackSide);
  73. }
  74. }
  75. }
  76. }
  77. }
  78. bool OnlyOnePage()
  79. {
  80. return ((printSettings.PrintMode is SizeModeInfo || printSettings.PrintMode is PosterModeInfo) &&
  81. printSettings.PageRangeList.Count == 1) ||
  82. (printSettings.PrintMode is MultipleModeInfo multipleModeInfo &&
  83. (int)Math.Ceiling(printSettings.PageRangeList.Count / (double)(multipleModeInfo.Sheet.TotalPageNumber)) == 1);
  84. }
  85. }
  86. private static void HandlePrintQueue(DuplexingStage stage)
  87. {
  88. if (printQueue != null)
  89. {
  90. using (var printDocument = new PrintDocument())
  91. {
  92. var printTicket = new PrintTicket();
  93. printDocument.PrinterSettings.PrinterName = printQueue.Name;
  94. printDocument.DefaultPageSettings.Color = !printSettingsInfo.IsGrayscale && outputColors.Contains(OutputColor.Color);
  95. if (printSettingsInfo.DuplexPrintMod > 0 || printSettingsInfo.PrintMode is BookletModeInfo)
  96. {
  97. if (stage == DuplexingStage.None)
  98. {
  99. printDocument.DefaultPageSettings.Landscape = (printSettingsInfo.PrintOrientation == PrintOrientation.Landscape);
  100. }
  101. else
  102. {
  103. printDocument.PrinterSettings.Duplex = Duplex.Simplex;
  104. isManualDuplex = true;
  105. PrintIndex = stage == DuplexingStage.FontSide ? 0 : 1;
  106. }
  107. }
  108. else
  109. {
  110. printDocument.PrinterSettings.Duplex = Duplex.Simplex;
  111. }
  112. printQueue.DefaultPrintTicket = printTicket;
  113. printDocument.DefaultPageSettings.PaperSize = printSettingsInfo.PaperSize;
  114. List<PaperSource> paperSources = Enumerable.Range(0, printDocument.PrinterSettings.PaperSources.Count).
  115. Select(i => printDocument.PrinterSettings.PaperSources[i]).ToList();
  116. printDocument.DefaultPageSettings.Margins = printSettingsInfo.Margins;
  117. printDocument.DefaultPageSettings.PaperSource = paperSources.FirstOrDefault();
  118. if (printSettingsInfo.Copies >= printQueue.GetPrintCapabilities().MaxCopyCount)
  119. {
  120. printDocument.PrinterSettings.Copies = (short)MaxCopies;
  121. }
  122. else
  123. {
  124. printDocument.PrinterSettings.Copies = (short)printSettingsInfo.Copies;
  125. }
  126. printDocument.PrintPage += PrintDocument_PrintPage;
  127. printDocument.PrinterSettings.PrintFileName = printSettingsInfo.Document.FileName;
  128. try
  129. {
  130. printDocument.Print();
  131. }
  132. catch (Exception ex)
  133. {
  134. MessageBox.Show(ex.Message);
  135. }
  136. }
  137. }
  138. }
  139. public static Bitmap ToGray(Bitmap bmp, int mode)
  140. {
  141. if (bmp == null)
  142. {
  143. return null;
  144. }
  145. int w = bmp.Width;
  146. int h = bmp.Height;
  147. try
  148. {
  149. byte newColor = 0;
  150. BitmapData srcData = bmp.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
  151. unsafe
  152. {
  153. byte* p = (byte*)srcData.Scan0.ToPointer();
  154. for (int y = 0; y < h; y++)
  155. {
  156. for (int x = 0; x < w; x++)
  157. {
  158. if (mode == 0)
  159. {
  160. newColor = (byte)((float)p[0] * 0.114f + (float)p[1] * 0.587f + (float)p[2] * 0.299f);
  161. }
  162. else
  163. {
  164. newColor = (byte)((float)(p[0] + p[1] + p[2]) / 3.0f);
  165. }
  166. p[0] = newColor;
  167. p[1] = newColor;
  168. p[2] = newColor;
  169. p += 3;
  170. }
  171. p += srcData.Stride - w * 3;
  172. }
  173. bmp.UnlockBits(srcData);
  174. return bmp;
  175. }
  176. }
  177. catch
  178. {
  179. return null;
  180. }
  181. }
  182. public static Bitmap BuildBmp(int width, int height, byte[] imgBytes)
  183. {
  184. // Check if the byte array length matches the expected size
  185. int expectedLength = width * height * 4;
  186. if (imgBytes.Length != expectedLength)
  187. {
  188. throw new ArgumentException("The length of imgBytes does not match the expected size.");
  189. }
  190. // Create a new bitmap with the specified width, height, and pixel format
  191. Bitmap bitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
  192. // Lock the bitmap's bits for writing
  193. BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, bitmap.PixelFormat);
  194. try
  195. {
  196. // Copy the byte array to the bitmap's scan0 pointer
  197. Marshal.Copy(imgBytes, 0, bmpData.Scan0, imgBytes.Length);
  198. }
  199. finally
  200. {
  201. // Ensure that the bitmap is always unlocked, even if an exception occurs
  202. bitmap.UnlockBits(bmpData);
  203. }
  204. return bitmap;
  205. }
  206. public static int CaculatePrintedPageCount(PrintSettingsInfo printSettingsInfo)
  207. {
  208. int tempCount = printSettingsInfo.PageRangeList.Count;
  209. if (printSettingsInfo.PrintMode is MultipleModeInfo multipleModeInfo)
  210. {
  211. tempCount = (int)Math.Ceiling((double)tempCount /
  212. multipleModeInfo.Sheet.TotalPageNumber);
  213. }
  214. else if (printSettingsInfo.PrintMode is BookletModeInfo bookletInfo)
  215. {
  216. if (tempCount != 1)
  217. {
  218. tempCount = (bookletInfo.EndPageIndex - bookletInfo.BeginPageIndex + 1) / 2;
  219. if (bookletInfo.Subset != BookletSubset.BothSides)
  220. {
  221. tempCount /= 2;
  222. }
  223. }
  224. }
  225. return tempCount;
  226. }
  227. public static Bitmap CombineBitmap(Bitmap background, Bitmap foreground, System.Drawing.Point point)
  228. {
  229. if (background == null)
  230. {
  231. return null;
  232. }
  233. int bgWidth = background.Width;
  234. int bgHeight = background.Height;
  235. Bitmap newMap = new Bitmap(bgWidth, bgHeight);
  236. Graphics graphics = Graphics.FromImage(newMap);
  237. graphics.DrawImage(background, new System.Drawing.Point(0, 0));
  238. graphics.DrawImage(foreground, point);
  239. graphics.Dispose();
  240. return newMap;
  241. }
  242. private static void PrintDocumentModSize(PrintPageEventArgs e)
  243. {
  244. int PrintedPageCount = CaculatePrintedPageCount(printSettingsInfo);
  245. int widthDpiRatio = (int)e.Graphics.DpiX / 100;
  246. int heightDpiRatio = (int)e.Graphics.DpiY / 100;
  247. if (printSettingsInfo.PrintMode is SizeModeInfo sizeMode)
  248. {
  249. CPDFPage page = printSettingsInfo.Document.PageAtIndex(printSettingsInfo.PageRangeList[PrintIndex]);
  250. Rectangle realBound = e.PageBounds;
  251. Point point = new Point(0, 0);
  252. if (page != null)
  253. {
  254. CSize cSize = page.PageSize;
  255. System.Drawing.Size pageSize = new System.Drawing.Size((int)cSize.width * widthDpiRatio, (int)cSize.height * heightDpiRatio);
  256. byte[] bmpData = new byte[(int)(pageSize.Width * pageSize.Height * 4)];
  257. page.RenderPageBitmap(0, 0, pageSize.Width, pageSize.Height, 0xFFFFFFFF, bmpData, printSettingsInfo.IsPrintAnnot ? 1 : 0, printSettingsInfo.IsPrintForm);
  258. Bitmap bitmap = BuildBmp((int)pageSize.Width, (int)pageSize.Height, bmpData);
  259. if (printSettingsInfo.IsGrayscale)
  260. {
  261. bitmap = ToGray(bitmap, 0);
  262. }
  263. if (sizeMode.SizeType == SizeType.Adaptive)
  264. {
  265. int resizeHeight;
  266. int resizeWidth;
  267. if (bitmap.Height / bitmap.Width >= printSettingsInfo.PaperSize.Height / printSettingsInfo.PaperSize.Width)
  268. {
  269. resizeHeight = printSettingsInfo.PaperSize.Height * heightDpiRatio;
  270. resizeWidth = bitmap.Width * printSettingsInfo.PaperSize.Height / bitmap.Height * widthDpiRatio;
  271. }
  272. else
  273. {
  274. resizeWidth = printSettingsInfo.PaperSize.Width * widthDpiRatio;
  275. resizeHeight = bitmap.Height * printSettingsInfo.PaperSize.Width / bitmap.Width * heightDpiRatio;
  276. }
  277. using (Bitmap resizedBitmap = new Bitmap(bitmap, resizeWidth, resizeHeight))
  278. {
  279. if (isManualDuplex && PrintIndex % 2 == 1 && printSettingsInfo.DuplexPrintMod == DuplexPrintMod.FlipShortEdge)
  280. {
  281. resizedBitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
  282. }
  283. float aspectRatioResizedBitmap = (float)resizedBitmap.Width / resizedBitmap.Height;
  284. float aspectRatioRealBound = (float)realBound.Width / realBound.Height;
  285. if (aspectRatioResizedBitmap != aspectRatioRealBound)
  286. {
  287. if (realBound.Width / aspectRatioResizedBitmap <= realBound.Height)
  288. {
  289. realBound.Height = (int)(realBound.Width / aspectRatioResizedBitmap);
  290. }
  291. else
  292. {
  293. realBound.Width = (int)(realBound.Height * aspectRatioResizedBitmap);
  294. }
  295. }
  296. e.Graphics.DrawImage(resizedBitmap, realBound, new Rectangle(0, 0, resizedBitmap.Width, resizedBitmap.Height), GraphicsUnit.Pixel);
  297. }
  298. }
  299. else if (sizeMode.SizeType == SizeType.Actural)
  300. {
  301. using (Bitmap resizedBitmap = ResizeBitmap(bitmap, 100))
  302. {
  303. if (isManualDuplex && PrintIndex % 2 == 1 && printSettingsInfo.DuplexPrintMod == DuplexPrintMod.FlipShortEdge)
  304. {
  305. resizedBitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
  306. }
  307. float aspectRatioResizedBitmap = (float)resizedBitmap.Width / resizedBitmap.Height;
  308. float aspectRatioRealBound = (float)realBound.Width / realBound.Height;
  309. if (aspectRatioResizedBitmap != aspectRatioRealBound)
  310. {
  311. if (realBound.Width / aspectRatioResizedBitmap <= realBound.Height)
  312. {
  313. realBound.Height = (int)(realBound.Width / aspectRatioResizedBitmap);
  314. }
  315. else
  316. {
  317. realBound.Width = (int)(realBound.Height * aspectRatioResizedBitmap);
  318. }
  319. }
  320. e.Graphics.DrawImage(resizedBitmap, realBound, new Rectangle(0, 0, resizedBitmap.Width, resizedBitmap.Height), GraphicsUnit.Pixel);
  321. }
  322. }
  323. else if (sizeMode.SizeType == SizeType.Customized)
  324. {
  325. using (Bitmap resizedBitmap = ResizeBitmap(bitmap, (printSettingsInfo.PrintMode as SizeModeInfo).Scale))
  326. {
  327. if (isManualDuplex && PrintIndex % 2 == 1 && printSettingsInfo.DuplexPrintMod == DuplexPrintMod.FlipShortEdge)
  328. {
  329. resizedBitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
  330. }
  331. float aspectRatioResizedBitmap = (float)resizedBitmap.Width / resizedBitmap.Height;
  332. float aspectRatioRealBound = (float)realBound.Width / realBound.Height;
  333. realBound.Width = (int)(realBound.Width * (printSettingsInfo.PrintMode as SizeModeInfo).Scale / 100.0);
  334. realBound.Height = (int)(realBound.Height * (printSettingsInfo.PrintMode as SizeModeInfo).Scale / 100.0);
  335. if (aspectRatioResizedBitmap != aspectRatioRealBound)
  336. {
  337. if (realBound.Width / aspectRatioResizedBitmap <= realBound.Height)
  338. {
  339. realBound.Height = (int)(realBound.Width / aspectRatioResizedBitmap);
  340. }
  341. else
  342. {
  343. realBound.Width = (int)(realBound.Height * aspectRatioResizedBitmap);
  344. }
  345. }
  346. realBound.X = (e.PageBounds.Width - realBound.Width) / 2;
  347. realBound.Y = (e.PageBounds.Height - realBound.Height) / 2;
  348. e.Graphics.DrawImage(resizedBitmap, realBound, new Rectangle(0, 0, resizedBitmap.Width, resizedBitmap.Height), GraphicsUnit.Pixel);
  349. }
  350. }
  351. bitmap.Dispose();
  352. GC.Collect();
  353. if (isManualDuplex && PrintedPageCount != 1)
  354. {
  355. if (PrintIndex < PrintedPageCount - 2)
  356. {
  357. PrintIndex += 2;
  358. e.HasMorePages = true;
  359. }
  360. else
  361. {
  362. e.HasMorePages = false;
  363. if (PrintIndex % 2 == 0)
  364. {
  365. //
  366. }
  367. }
  368. }
  369. else
  370. {
  371. if (PrintIndex < PrintedPageCount - 1)
  372. {
  373. PrintIndex++;
  374. e.HasMorePages = true;
  375. }
  376. else
  377. {
  378. e.HasMorePages = false;
  379. }
  380. }
  381. }
  382. }
  383. }
  384. private static void PrintDocument_PrintPage(object sender, PrintPageEventArgs e)
  385. {
  386. switch (printSettingsInfo.PrintMode)
  387. {
  388. case SizeModeInfo _:
  389. PrintDocumentModSize(e);
  390. break;
  391. case PosterModeInfo _:
  392. break;
  393. case MultipleModeInfo _:
  394. break;
  395. case BookletModeInfo _:
  396. break;
  397. }
  398. }
  399. internal static Bitmap ResizeBitmap(Bitmap bitmap, float scale)
  400. {
  401. int newWidth = (int)(bitmap.Width * scale / 72);
  402. int newHeight = (int)(bitmap.Height * scale / 72);
  403. Bitmap newBitmap = new Bitmap(newWidth, newHeight);
  404. using (Graphics g = Graphics.FromImage(newBitmap))
  405. {
  406. g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
  407. g.DrawImage(bitmap, 0, 0, newWidth, newHeight);
  408. }
  409. return newBitmap;
  410. }
  411. }
  412. }