LodopPrinter.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <?php
  2. namespace app\common\library\ps;
  3. use think\Config;
  4. /**
  5. * Lodop 模式:测试环境只预览,生产可改为正式打印。
  6. */
  7. class LodopPrinter
  8. {
  9. /**
  10. * @return string preview|print
  11. */
  12. public static function mode()
  13. {
  14. $mode = Config::get('site.ps_lodop_mode') ?: (Config::get('ps.lodop.mode') ?: 'preview');
  15. return $mode === 'print' ? 'print' : 'preview';
  16. }
  17. /**
  18. * 下发给前端的 Lodop 调用包。多张流程单用 NEWPAGE 分页,copies 为整套重复次数。
  19. *
  20. * @param string $html 兼容单页
  21. * @param string $title
  22. * @param array $pages [['html'=>..., 'title'=>...], ...]
  23. * @param int $copies
  24. * @param array $page ['w'=>mm,'h'=>mm,'paper_size'=>'A4']
  25. * @return array
  26. */
  27. public static function buildTask($html, $title = '流程单', array $pages = [], $copies = 1, array $page = [])
  28. {
  29. $mode = self::mode();
  30. $copies = max(1, (int)$copies);
  31. if (!$pages) {
  32. $pages = [[
  33. 'html' => $html,
  34. 'title' => $title,
  35. ]];
  36. }
  37. $first = $pages[0]['html'] ?? $html;
  38. $printTexts = [];
  39. foreach ($pages as $row) {
  40. if (!empty($row['print_items']) && is_array($row['print_items'])) {
  41. $printTexts[] = $row['print_items'];
  42. } elseif (!empty($row['items']) && is_array($row['items'])) {
  43. $printTexts[] = $row['items'];
  44. } else {
  45. $printTexts[] = [];
  46. }
  47. }
  48. return [
  49. 'mode' => $mode,
  50. 'js_url' => Config::get('site.ps_lodop_js') ?: (Config::get('ps.lodop.c_lodop_js') ?: 'http://localhost:8000/CLodopfuncs.js'),
  51. 'title' => $title,
  52. 'html' => $first,
  53. 'pages' => $pages,
  54. 'print_texts' => $printTexts,
  55. 'copies' => $copies,
  56. 'print_api' => $mode === 'print' ? 'PRINT' : 'PREVIEW',
  57. 'page' => $page,
  58. 'commands' => self::commands($pages, $title, $copies, $mode, $page),
  59. ];
  60. }
  61. /**
  62. * 前端按顺序调用 CLODOP 的指令列表
  63. * @param array $pages
  64. * @param string $title
  65. * @param int $copies
  66. * @param string $mode
  67. * @param array $page
  68. * @return array
  69. */
  70. protected static function commands(array $pages, $title, $copies, $mode, array $page = [])
  71. {
  72. $size = self::paperMm($page);
  73. $w = $size['w'];
  74. $h = $size['h'];
  75. $wMm = $w . 'mm';
  76. $hMm = $h . 'mm';
  77. $commands = [
  78. ['PRINT_INIT', $title],
  79. // 第四参数不要写 A4/A5:打印机没有该纸时会落到默认标签纸(常见 90×60、110×210)。
  80. ['SET_PRINT_PAGESIZE', 1, (int)round($w * 10), (int)round($h * 10), ''],
  81. ['SET_PRINT_MODE', 'POS_BASEON_PAPER', 1],
  82. ['SET_PRINT_COPIES', $copies],
  83. ['SET_PRINT_STYLE', 'FontName', '微软雅黑'],
  84. ['SET_PRINT_STYLE', 'FontSize', 10],
  85. ['SET_PRINT_STYLE', 'Bold', 0],
  86. ['SET_PRINT_STYLE', 'Italic', 0],
  87. ];
  88. $last = count($pages) - 1;
  89. foreach ($pages as $index => $item) {
  90. $texts = [];
  91. if (!empty($item['print_items']) && is_array($item['print_items'])) {
  92. $texts = $item['print_items'];
  93. } elseif (!empty($item['items']) && is_array($item['items'])) {
  94. $texts = $item['items'];
  95. }
  96. if ($texts) {
  97. foreach ($texts as $row) {
  98. if (($row['type'] ?? '') === 'qrcode') {
  99. $img = trim((string)($row['image'] ?? ''));
  100. $qrTop = (string)($row['top'] ?? '0mm');
  101. $qrLeft = (string)($row['left'] ?? '0mm');
  102. $qrWidth = (string)($row['width'] ?? '24mm');
  103. $qrHeight = (string)($row['height'] ?? '24mm');
  104. if ($img !== '') {
  105. $commands[] = [
  106. 'ADD_PRINT_IMAGE',
  107. $qrTop,
  108. $qrLeft,
  109. $qrWidth,
  110. $qrHeight,
  111. $img,
  112. ];
  113. $commands[] = ['SET_PRINT_STYLEA', 0, 'Stretch', 1];
  114. } else {
  115. $commands[] = [
  116. 'ADD_PRINT_BARCODE',
  117. $qrTop,
  118. $qrLeft,
  119. $qrWidth,
  120. $qrHeight,
  121. 'QRCode',
  122. (string)($row['text'] ?? ''),
  123. ];
  124. $commands[] = ['SET_PRINT_STYLEA', 0, 'QRCodeErrorLevel', 'L'];
  125. $commands[] = ['SET_PRINT_STYLEA', 0, 'DataCharset', 'UTF-8'];
  126. }
  127. continue;
  128. }
  129. $commands[] = [
  130. 'ADD_PRINT_TEXT',
  131. $row['top'],
  132. $row['left'],
  133. $row['width'],
  134. $row['height'],
  135. (string)($row['text'] ?? ''),
  136. ];
  137. $commands[] = ['SET_PRINT_STYLEA', 0, 'FontName', $row['font'] ?? '微软雅黑'];
  138. $commands[] = ['SET_PRINT_STYLEA', 0, 'FontSize', (int)($row['size'] ?? 10)];
  139. $commands[] = ['SET_PRINT_STYLEA', 0, 'Bold', !empty($row['bold']) ? 1 : 0];
  140. $commands[] = ['SET_PRINT_STYLEA', 0, 'Italic', 0];
  141. $commands[] = ['SET_PRINT_STYLEA', 0, 'Alignment', (int)($row['align'] ?? 1)];
  142. }
  143. } else {
  144. $html = self::stripCoverImage((string)($item['html'] ?? ''));
  145. $commands[] = ['ADD_PRINT_HTM', 0, 0, $wMm, $hMm, self::wrapHtml($html, $w, $h)];
  146. }
  147. if ($index < $last) {
  148. $commands[] = ['NEWPAGE'];
  149. }
  150. }
  151. $commands[] = [$mode === 'print' ? 'PRINT' : 'PREVIEW'];
  152. return $commands;
  153. }
  154. /**
  155. * 纸张毫米。始终带实数宽高,避免落到打印机默认标签纸。
  156. * @param array $page
  157. * @return array{w:float,h:float,paper_size:string}
  158. */
  159. protected static function paperMm(array $page)
  160. {
  161. $resolved = PaperSize::resolve(
  162. (string)($page['paper_size'] ?? ''),
  163. (float)($page['w'] ?? 0),
  164. (float)($page['h'] ?? 0)
  165. );
  166. return [
  167. 'w' => (float)$resolved['w'],
  168. 'h' => (float)$resolved['h'],
  169. 'paper_size' => (string)$resolved['name'],
  170. ];
  171. }
  172. /**
  173. * @param string $html
  174. * @return string
  175. */
  176. protected static function stripCoverImage($html)
  177. {
  178. $html = preg_replace('/<img[^>]*alt="流程单底图"[^>]*>/i', '', (string)$html);
  179. return is_string($html) ? $html : '';
  180. }
  181. /**
  182. * @param string $html
  183. * @param float $w
  184. * @param float $h
  185. * @return string
  186. */
  187. protected static function wrapHtml($html, $w, $h)
  188. {
  189. return '<!DOCTYPE html><html><head><meta charset="utf-8">'
  190. . '<style>html,body{margin:0;padding:0;width:' . $w . 'mm;height:' . $h . 'mm;overflow:hidden;background:transparent;font-family:\'Microsoft YaHei\',\'微软雅黑\',sans-serif;font-size:13px;font-weight:normal;}</style>'
  191. . '</head><body>' . $html . '</body></html>';
  192. }
  193. }