PrintPdf.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. using FileInfoClass;
  2. using PdfiumViewer;
  3. using System;
  4. using System.Drawing;
  5. using System.Drawing.Printing;
  6. using System.IO;
  7. using System.Threading.Tasks;
  8. using System.Windows.Forms;
  9. namespace prBrowser
  10. {
  11. class PrintPdf
  12. {
  13. Control _uiContext;
  14. private int totalPageSize = 0;
  15. private int currentPageIndex = 0;
  16. float dpiX = 150;
  17. float dpiY = 150;
  18. //int printableWidth = 0;
  19. //int printableHeight = 0;
  20. private PrintDocument myPrintDocument;
  21. private PdfiumViewer.PdfDocument pdfDocument;
  22. private PrintSettings settings;
  23. string tempPdfPath = string.Empty;
  24. private PrintState _currentState = PrintState.Ready;
  25. private readonly object _stateLock = new object();
  26. string logPath = @"C:\ProgramData\prykNT\prBrowser\bin\service\log\test.log";
  27. FileClass fileclass = new FileClass();
  28. // 打印模式枚举
  29. private enum PrintMode
  30. {
  31. SinglePage,
  32. MultiPage
  33. }
  34. // 打印状态机
  35. private enum PrintState
  36. {
  37. Ready,
  38. PrintingPage,
  39. WaitingUserConfirmation,
  40. Completed,
  41. Cancelled
  42. }
  43. private PrintMode _printMode = PrintMode.SinglePage;
  44. // 设置打印模式
  45. public void SetPrintMode(bool singlePageMode)
  46. {
  47. _printMode = singlePageMode ? PrintMode.SinglePage : PrintMode.MultiPage;
  48. }
  49. // 安全的状态切换
  50. private void ChangeState(PrintState newState)
  51. {
  52. lock (_stateLock)
  53. {
  54. _currentState = newState;
  55. }
  56. }
  57. // 启动打印
  58. public void StartPrinting()
  59. {
  60. lock (_stateLock)
  61. {
  62. if (_currentState == PrintState.Ready)
  63. {
  64. currentPageIndex = 0;
  65. ChangeState(PrintState.PrintingPage);
  66. myPrintDocument.Print();
  67. }
  68. }
  69. }
  70. // 用户确认继续打印
  71. public void UserConfirmContinue()
  72. {
  73. lock (_stateLock)
  74. {
  75. if (_currentState == PrintState.WaitingUserConfirmation)
  76. {
  77. ChangeState(PrintState.PrintingPage);
  78. // 直接启动下一页打印
  79. if (currentPageIndex < totalPageSize)
  80. {
  81. myPrintDocument.Print();
  82. }
  83. }
  84. }
  85. }
  86. // 用户取消打印
  87. public void UserCancel()
  88. {
  89. ChangeState(PrintState.Cancelled);
  90. Dispose();
  91. }
  92. public void Print(string pdfPath, PrintSettings settings)
  93. {
  94. this.settings = settings;
  95. try
  96. {
  97. tempPdfPath = pdfPath;
  98. pdfDocument = PdfDocument.Load(tempPdfPath);
  99. //pdfDocument.Save("d:\\test.pdf");
  100. totalPageSize = pdfDocument.PageCount;
  101. //printableWidth = this.settings.printerSettings.DefaultPageSettings.PaperSize.Width * 3;
  102. //printableHeight = (int)(this.settings.printerSettings.DefaultPageSettings.PaperSize.Height * 2.9);
  103. // 根据页数和设置决定打印模式
  104. bool useSinglePageMode = totalPageSize > 1 && (settings.manualDuplex && "all".Equals(settings.duplexTips));
  105. fileclass.WriteLogFile(logPath, $"Print.useSinglePageMode:{useSinglePageMode}");
  106. SetPrintMode(useSinglePageMode);
  107. initPrinter();
  108. // 启动打印
  109. StartPrinting();
  110. }
  111. catch (Exception ex)
  112. {
  113. ShowErrorMessage(ex.Message);
  114. }
  115. }
  116. private void initPrinter()
  117. {
  118. myPrintDocument = new PrintDocument();
  119. myPrintDocument.PrintPage += new PrintPageEventHandler(myPrintDocument_PrintPage);
  120. myPrintDocument.EndPrint += new PrintEventHandler(myPrintDocument_EndPrint);
  121. StandardPrintController standardPrint = new StandardPrintController();
  122. myPrintDocument.DefaultPageSettings = this.settings.printerSettings.DefaultPageSettings;
  123. myPrintDocument.PrinterSettings = this.settings.printerSettings;
  124. myPrintDocument.PrintController = standardPrint;
  125. }
  126. private void myPrintDocument_PrintPage(object sender, PrintPageEventArgs e)
  127. {
  128. try
  129. {
  130. lock (_stateLock)
  131. {
  132. if (currentPageIndex >= totalPageSize || _currentState != PrintState.PrintingPage)
  133. {
  134. e.HasMorePages = false;
  135. return;
  136. }
  137. Rectangle printArea = e.MarginBounds;
  138. if (printArea.Width <= 0 || printArea.Height <= 0)
  139. printArea = new Rectangle(0, 0, e.PageBounds.Width, e.PageBounds.Height);
  140. else
  141. printArea = new Rectangle(0, 0, printArea.Width, printArea.Height);
  142. // 针对 2.pdf 这类发票:假设有效内容在左上角 500x300 points 区域
  143. // A4 = 595x842 points,我们只取 (0,0,500,300)
  144. float cropWidthPoints = 250; // 根据实际调整
  145. float cropHeightPoints = 150;
  146. // 转为像素
  147. int renderWidth = (int)((cropWidthPoints / 72f) * e.Graphics.DpiX);
  148. int renderHeight = (int)((cropHeightPoints / 72f) * e.Graphics.DpiY);
  149. using (Image image = pdfDocument.Render(
  150. currentPageIndex,
  151. renderWidth,
  152. renderHeight,
  153. e.Graphics.DpiX,
  154. e.Graphics.DpiY,
  155. PdfRenderFlags.Annotations | PdfRenderFlags.ForPrinting))
  156. {
  157. // 等比缩放并居中(或拉伸)
  158. float scaleX = (float)printArea.Width / image.Width;
  159. float scaleY = (float)printArea.Height / image.Height;
  160. float scale = Math.Min(scaleX, scaleY); // 保持比例
  161. int drawW = (int)(image.Width * scale);
  162. int drawH = (int)(image.Height * scale);
  163. int x = ((printArea.Width - drawW) / 2) - 10;
  164. int y = (printArea.Height - drawH) / 2;
  165. fileclass.WriteLogFile(logPath, $"[RENDER] Drawing page {currentPageIndex} to printer");
  166. e.Graphics.DrawImage(image, x, y, drawW, drawH);
  167. //string debugImagePath = $@"C:\temp\debug_page_{currentPageIndex}.png";
  168. //image.Save(debugImagePath, System.Drawing.Imaging.ImageFormat.Png);
  169. //fileclass.WriteLogFile(logPath, $"[DEBUG SAVE] Saved {debugImagePath}");
  170. }
  171. //printableWidth = this.settings.printerSettings.DefaultPageSettings.PaperSize.Width * 3;
  172. //printableHeight = (int)(this.settings.printerSettings.DefaultPageSettings.PaperSize.Height * 2.9);
  173. // 根据页数和设置决定打印模式
  174. bool useSinglePageMode = totalPageSize > 1 && (settings.manualDuplex && "all".Equals(settings.duplexTips));
  175. fileclass.WriteLogFile(logPath, $"myPrintDocument_PrintPage.useSinglePageMode:{useSinglePageMode}");
  176. fileclass.WriteLogFile(logPath, $"myPrintDocument_PrintPage.totalPageSize:{totalPageSize}");
  177. fileclass.WriteLogFile(logPath, $"myPrintDocument_PrintPage.currentPageIndex:{currentPageIndex}");
  178. //e.HasMorePages = false;
  179. e.HasMorePages = (currentPageIndex + 1) < totalPageSize;
  180. currentPageIndex++;
  181. }
  182. }
  183. catch (Exception ex)
  184. {
  185. ChangeState(PrintState.Cancelled);
  186. e.Cancel = true;
  187. ShowErrorMessage($"打印错误: {ex.Message}");
  188. }
  189. }
  190. private void myPrintDocument_EndPrint(object sender, PrintEventArgs e)
  191. {
  192. try
  193. {
  194. lock (_stateLock)
  195. {
  196. if (_currentState == PrintState.Cancelled)
  197. {
  198. Dispose();
  199. return;
  200. }
  201. //currentPageIndex++;
  202. // 检查是否完成
  203. if (currentPageIndex >= totalPageSize)
  204. {
  205. ChangeState(PrintState.Completed);
  206. Dispose();
  207. return;
  208. }
  209. // 根据打印模式处理下一页
  210. ChangeState(PrintState.WaitingUserConfirmation);
  211. if (NeedDuplexPrompt(currentPageIndex))
  212. {
  213. Task.Delay(500).ContinueWith(_ =>
  214. {
  215. _uiContext?.BeginInvoke(new Action(() =>
  216. {
  217. if (_currentState == PrintState.WaitingUserConfirmation)
  218. {
  219. // 提示
  220. ShowUserPrompt();
  221. }
  222. }));
  223. });
  224. }
  225. else {
  226. // 直接打印
  227. UserConfirmContinue();
  228. }
  229. }
  230. }
  231. catch (Exception ex)
  232. {
  233. ShowErrorMessage($"结束打印错误: {ex.Message}");
  234. Dispose();
  235. }
  236. }
  237. private bool NeedDuplexPrompt(int pageIndex)
  238. {
  239. if (!settings.manualDuplex) return false;
  240. bool isEvenPage = (pageIndex % 2 == 1);
  241. return ("all".Equals(settings.duplexTips)) ||
  242. ("even".Equals(settings.duplexTips) && isEvenPage);
  243. }
  244. private void ShowUserPrompt()
  245. {
  246. if (_uiContext?.InvokeRequired == true)
  247. {
  248. _uiContext.BeginInvoke(new Action(ShowUserPrompt));
  249. return;
  250. }
  251. //DialogResult result = showConfirmDialog();
  252. DialogResult result = MessageBox.Show($"请更背面,点击'是(Y)'继续打印第 {currentPageIndex + 1} 页", "打印确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
  253. if (result == DialogResult.Yes)
  254. {
  255. UserConfirmContinue();
  256. }
  257. else
  258. {
  259. UserCancel();
  260. }
  261. }
  262. private DialogResult showConfirmDialog() {
  263. var promptForm = new Form
  264. {
  265. Width = 400,
  266. Height = 150,
  267. FormBorderStyle = FormBorderStyle.FixedDialog,
  268. Text = _printMode == PrintMode.SinglePage ? "打印确认" : "双面打印确认",
  269. StartPosition = FormStartPosition.CenterParent
  270. };
  271. string promptText = $"请更换纸张,点击确认继续打印第 {currentPageIndex + 1} 页";
  272. var label = new Label
  273. {
  274. Text = promptText,
  275. Dock = DockStyle.Top,
  276. TextAlign = ContentAlignment.MiddleCenter,
  277. Height = 50
  278. };
  279. var yesBtn = new Button { Text = "确认", DialogResult = DialogResult.Yes, Dock = DockStyle.Left, Width = 180 };
  280. var noBtn = new Button { Text = "取消", DialogResult = DialogResult.No, Dock = DockStyle.Right, Width = 180 };
  281. yesBtn.Click += (s, e) => { promptForm.DialogResult = DialogResult.Yes; promptForm.Close(); };
  282. noBtn.Click += (s, e) => { promptForm.DialogResult = DialogResult.No; promptForm.Close(); };
  283. var panel = new Panel { Dock = DockStyle.Bottom, Height = 35 };
  284. panel.Controls.Add(yesBtn);
  285. panel.Controls.Add(noBtn);
  286. promptForm.Controls.Add(label);
  287. promptForm.Controls.Add(panel);
  288. var result = promptForm.ShowDialog(_uiContext);
  289. return result;
  290. }
  291. private void ShowErrorMessage(string message)
  292. {
  293. if (_uiContext.InvokeRequired)
  294. {
  295. _uiContext.Invoke(new Action<string>(ShowErrorMessage), message);
  296. return;
  297. }
  298. MessageBox.Show(message, "打印错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
  299. }
  300. private void DisposePdfDocument()
  301. {
  302. if (pdfDocument != null)
  303. {
  304. pdfDocument.Dispose();
  305. pdfDocument = null;
  306. }
  307. }
  308. protected void DisposePrint(bool disposing)
  309. {
  310. if (disposing && myPrintDocument != null)
  311. {
  312. try
  313. {
  314. myPrintDocument.PrintPage -= myPrintDocument_PrintPage;
  315. myPrintDocument.EndPrint -= myPrintDocument_EndPrint;
  316. myPrintDocument.Dispose();
  317. myPrintDocument = null;
  318. }
  319. catch (Exception ex)
  320. {
  321. // 记录日志但不抛出异常,避免在Dispose过程中出现问题
  322. System.Diagnostics.Debug.WriteLine($"DisposePrint error: {ex.Message}");
  323. }
  324. }
  325. }
  326. public void Dispose()
  327. {
  328. try
  329. {
  330. ChangeState(PrintState.Cancelled);
  331. DisposePdfDocument();
  332. DisposePrint(true);
  333. // 清理临时文件
  334. if (!string.IsNullOrEmpty(tempPdfPath) && File.Exists(tempPdfPath))
  335. {
  336. try
  337. {
  338. File.Delete(tempPdfPath);
  339. }
  340. catch (Exception ex)
  341. {
  342. System.Diagnostics.Debug.WriteLine($"Failed to delete temp PDF: {ex.Message}");
  343. }
  344. }
  345. }
  346. catch (Exception ex)
  347. {
  348. System.Diagnostics.Debug.WriteLine($"Dispose error: {ex.Message}");
  349. }
  350. }
  351. }
  352. }