av6-pdf-engine 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/.prettierignore +4 -0
  2. package/.prettierrc +6 -0
  3. package/README.md +155 -0
  4. package/dist/index.d.mts +296 -0
  5. package/dist/index.d.ts +296 -3
  6. package/dist/index.js +1979 -18
  7. package/dist/index.mjs +1940 -0
  8. package/package.json +12 -21
  9. package/dist/index.d.ts.map +0 -1
  10. package/dist/index.js.map +0 -1
  11. package/dist/renderer-engine/blocks/barcode.d.ts +0 -3
  12. package/dist/renderer-engine/blocks/barcode.d.ts.map +0 -1
  13. package/dist/renderer-engine/blocks/barcode.js +0 -47
  14. package/dist/renderer-engine/blocks/barcode.js.map +0 -1
  15. package/dist/renderer-engine/blocks/columns.d.ts +0 -3
  16. package/dist/renderer-engine/blocks/columns.d.ts.map +0 -1
  17. package/dist/renderer-engine/blocks/columns.js +0 -105
  18. package/dist/renderer-engine/blocks/columns.js.map +0 -1
  19. package/dist/renderer-engine/blocks/image.d.ts +0 -4
  20. package/dist/renderer-engine/blocks/image.d.ts.map +0 -1
  21. package/dist/renderer-engine/blocks/image.js +0 -57
  22. package/dist/renderer-engine/blocks/image.js.map +0 -1
  23. package/dist/renderer-engine/blocks/index.d.ts +0 -8
  24. package/dist/renderer-engine/blocks/index.d.ts.map +0 -1
  25. package/dist/renderer-engine/blocks/index.js +0 -24
  26. package/dist/renderer-engine/blocks/index.js.map +0 -1
  27. package/dist/renderer-engine/blocks/key-value-grid.d.ts +0 -3
  28. package/dist/renderer-engine/blocks/key-value-grid.d.ts.map +0 -1
  29. package/dist/renderer-engine/blocks/key-value-grid.js +0 -245
  30. package/dist/renderer-engine/blocks/key-value-grid.js.map +0 -1
  31. package/dist/renderer-engine/blocks/line.d.ts +0 -3
  32. package/dist/renderer-engine/blocks/line.d.ts.map +0 -1
  33. package/dist/renderer-engine/blocks/line.js +0 -35
  34. package/dist/renderer-engine/blocks/line.js.map +0 -1
  35. package/dist/renderer-engine/blocks/table.d.ts +0 -4
  36. package/dist/renderer-engine/blocks/table.d.ts.map +0 -1
  37. package/dist/renderer-engine/blocks/table.js +0 -201
  38. package/dist/renderer-engine/blocks/table.js.map +0 -1
  39. package/dist/renderer-engine/blocks/text.d.ts +0 -3
  40. package/dist/renderer-engine/blocks/text.d.ts.map +0 -1
  41. package/dist/renderer-engine/blocks/text.js +0 -52
  42. package/dist/renderer-engine/blocks/text.js.map +0 -1
  43. package/dist/renderer-engine/index.d.ts +0 -4
  44. package/dist/renderer-engine/index.d.ts.map +0 -1
  45. package/dist/renderer-engine/index.js +0 -215
  46. package/dist/renderer-engine/index.js.map +0 -1
  47. package/dist/renderer-engine/utils/block-renderer.d.ts +0 -17
  48. package/dist/renderer-engine/utils/block-renderer.d.ts.map +0 -1
  49. package/dist/renderer-engine/utils/block-renderer.js +0 -86
  50. package/dist/renderer-engine/utils/block-renderer.js.map +0 -1
  51. package/dist/renderer-engine/utils/context.d.ts +0 -3
  52. package/dist/renderer-engine/utils/context.d.ts.map +0 -1
  53. package/dist/renderer-engine/utils/context.js +0 -16
  54. package/dist/renderer-engine/utils/context.js.map +0 -1
  55. package/dist/renderer-engine/utils/ensure-space.d.ts +0 -9
  56. package/dist/renderer-engine/utils/ensure-space.d.ts.map +0 -1
  57. package/dist/renderer-engine/utils/ensure-space.js +0 -18
  58. package/dist/renderer-engine/utils/ensure-space.js.map +0 -1
  59. package/dist/renderer-engine/utils/env.d.ts +0 -3
  60. package/dist/renderer-engine/utils/env.d.ts.map +0 -1
  61. package/dist/renderer-engine/utils/env.js +0 -10
  62. package/dist/renderer-engine/utils/env.js.map +0 -1
  63. package/dist/renderer-engine/utils/finish-page.d.ts +0 -13
  64. package/dist/renderer-engine/utils/finish-page.d.ts.map +0 -1
  65. package/dist/renderer-engine/utils/finish-page.js +0 -27
  66. package/dist/renderer-engine/utils/finish-page.js.map +0 -1
  67. package/dist/renderer-engine/utils/footer.d.ts +0 -3
  68. package/dist/renderer-engine/utils/footer.d.ts.map +0 -1
  69. package/dist/renderer-engine/utils/footer.js +0 -89
  70. package/dist/renderer-engine/utils/footer.js.map +0 -1
  71. package/dist/renderer-engine/utils/header.d.ts +0 -3
  72. package/dist/renderer-engine/utils/header.d.ts.map +0 -1
  73. package/dist/renderer-engine/utils/header.js +0 -47
  74. package/dist/renderer-engine/utils/header.js.map +0 -1
  75. package/dist/renderer-engine/utils/image-loader.d.ts +0 -4
  76. package/dist/renderer-engine/utils/image-loader.d.ts.map +0 -1
  77. package/dist/renderer-engine/utils/image-loader.js +0 -52
  78. package/dist/renderer-engine/utils/image-loader.js.map +0 -1
  79. package/dist/renderer-engine/utils/index.d.ts +0 -19
  80. package/dist/renderer-engine/utils/index.d.ts.map +0 -1
  81. package/dist/renderer-engine/utils/index.js +0 -35
  82. package/dist/renderer-engine/utils/index.js.map +0 -1
  83. package/dist/renderer-engine/utils/layout.d.ts +0 -8
  84. package/dist/renderer-engine/utils/layout.d.ts.map +0 -1
  85. package/dist/renderer-engine/utils/layout.js +0 -22
  86. package/dist/renderer-engine/utils/layout.js.map +0 -1
  87. package/dist/renderer-engine/utils/measure-block-height.d.ts +0 -8
  88. package/dist/renderer-engine/utils/measure-block-height.d.ts.map +0 -1
  89. package/dist/renderer-engine/utils/measure-block-height.js +0 -220
  90. package/dist/renderer-engine/utils/measure-block-height.js.map +0 -1
  91. package/dist/renderer-engine/utils/page-background.d.ts +0 -6
  92. package/dist/renderer-engine/utils/page-background.d.ts.map +0 -1
  93. package/dist/renderer-engine/utils/page-background.js +0 -30
  94. package/dist/renderer-engine/utils/page-background.js.map +0 -1
  95. package/dist/renderer-engine/utils/page-flow.d.ts +0 -3
  96. package/dist/renderer-engine/utils/page-flow.d.ts.map +0 -1
  97. package/dist/renderer-engine/utils/page-flow.js +0 -9
  98. package/dist/renderer-engine/utils/page-flow.js.map +0 -1
  99. package/dist/renderer-engine/utils/page-limit.d.ts +0 -8
  100. package/dist/renderer-engine/utils/page-limit.d.ts.map +0 -1
  101. package/dist/renderer-engine/utils/page-limit.js +0 -15
  102. package/dist/renderer-engine/utils/page-limit.js.map +0 -1
  103. package/dist/renderer-engine/utils/qr-bar-code.d.ts +0 -17
  104. package/dist/renderer-engine/utils/qr-bar-code.d.ts.map +0 -1
  105. package/dist/renderer-engine/utils/qr-bar-code.js +0 -141
  106. package/dist/renderer-engine/utils/qr-bar-code.js.map +0 -1
  107. package/dist/renderer-engine/utils/signature.d.ts +0 -20
  108. package/dist/renderer-engine/utils/signature.d.ts.map +0 -1
  109. package/dist/renderer-engine/utils/signature.js +0 -214
  110. package/dist/renderer-engine/utils/signature.js.map +0 -1
  111. package/dist/renderer-engine/utils/start-page-layout.d.ts +0 -20
  112. package/dist/renderer-engine/utils/start-page-layout.d.ts.map +0 -1
  113. package/dist/renderer-engine/utils/start-page-layout.js +0 -34
  114. package/dist/renderer-engine/utils/start-page-layout.js.map +0 -1
  115. package/dist/renderer-engine/utils/styles.d.ts +0 -7
  116. package/dist/renderer-engine/utils/styles.d.ts.map +0 -1
  117. package/dist/renderer-engine/utils/styles.js +0 -124
  118. package/dist/renderer-engine/utils/styles.js.map +0 -1
  119. package/dist/renderer-engine/utils/watermark.d.ts +0 -11
  120. package/dist/renderer-engine/utils/watermark.d.ts.map +0 -1
  121. package/dist/renderer-engine/utils/watermark.js +0 -57
  122. package/dist/renderer-engine/utils/watermark.js.map +0 -1
  123. package/dist/types/barcode.d.ts +0 -23
  124. package/dist/types/barcode.d.ts.map +0 -1
  125. package/dist/types/barcode.js +0 -11
  126. package/dist/types/barcode.js.map +0 -1
  127. package/dist/types/columns.d.ts +0 -14
  128. package/dist/types/columns.d.ts.map +0 -1
  129. package/dist/types/columns.js +0 -3
  130. package/dist/types/columns.js.map +0 -1
  131. package/dist/types/common.d.ts +0 -102
  132. package/dist/types/common.d.ts.map +0 -1
  133. package/dist/types/common.js +0 -3
  134. package/dist/types/common.js.map +0 -1
  135. package/dist/types/context.d.ts +0 -17
  136. package/dist/types/context.d.ts.map +0 -1
  137. package/dist/types/context.js +0 -3
  138. package/dist/types/context.js.map +0 -1
  139. package/dist/types/image.d.ts +0 -13
  140. package/dist/types/image.d.ts.map +0 -1
  141. package/dist/types/image.js +0 -3
  142. package/dist/types/image.js.map +0 -1
  143. package/dist/types/index.d.ts +0 -13
  144. package/dist/types/index.d.ts.map +0 -1
  145. package/dist/types/index.js +0 -29
  146. package/dist/types/index.js.map +0 -1
  147. package/dist/types/key-value-grid.d.ts +0 -34
  148. package/dist/types/key-value-grid.d.ts.map +0 -1
  149. package/dist/types/key-value-grid.js +0 -3
  150. package/dist/types/key-value-grid.js.map +0 -1
  151. package/dist/types/line.d.ts +0 -11
  152. package/dist/types/line.d.ts.map +0 -1
  153. package/dist/types/line.js +0 -3
  154. package/dist/types/line.js.map +0 -1
  155. package/dist/types/page-break.d.ts +0 -5
  156. package/dist/types/page-break.d.ts.map +0 -1
  157. package/dist/types/page-break.js +0 -3
  158. package/dist/types/page-break.js.map +0 -1
  159. package/dist/types/qr.d.ts +0 -22
  160. package/dist/types/qr.d.ts.map +0 -1
  161. package/dist/types/qr.js +0 -10
  162. package/dist/types/qr.js.map +0 -1
  163. package/dist/types/signature.d.ts +0 -12
  164. package/dist/types/signature.d.ts.map +0 -1
  165. package/dist/types/signature.js +0 -3
  166. package/dist/types/signature.js.map +0 -1
  167. package/dist/types/table.d.ts +0 -32
  168. package/dist/types/table.d.ts.map +0 -1
  169. package/dist/types/table.js +0 -3
  170. package/dist/types/table.js.map +0 -1
  171. package/dist/types/text.d.ts +0 -19
  172. package/dist/types/text.d.ts.map +0 -1
  173. package/dist/types/text.js +0 -3
  174. package/dist/types/text.js.map +0 -1
  175. package/dist/types/utility.d.ts +0 -1
  176. package/dist/types/utility.d.ts.map +0 -1
  177. package/dist/types/utility.js +0 -2
  178. package/dist/types/utility.js.map +0 -1
package/dist/index.mjs ADDED
@@ -0,0 +1,1940 @@
1
+ // src/renderer-engine/index.ts
2
+ import fs from "fs";
3
+ import PDFDocument from "pdfkit";
4
+
5
+ // src/renderer-engine/blocks/barcode.ts
6
+ var processBarcodeBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
7
+ const imgSource = block.src;
8
+ const imgWidth = block.width ?? 120;
9
+ const imgHeight = block.height ?? 40;
10
+ const localLeft = block.marginLeft ?? 0;
11
+ const localRight = block.marginRight ?? 0;
12
+ const baseLeft = env.marginLeft + localLeft;
13
+ const availableWidth = Math.max(env.innerWidth - localLeft - localRight, 1);
14
+ const mt = block.marginTop ?? 0;
15
+ const mb = block.marginBottom ?? 0;
16
+ const baseY = y ?? ctx.currentY;
17
+ const startY = baseY + mt;
18
+ const heightNeeded = mt + imgHeight + mb;
19
+ if (y === null) {
20
+ ensureSpaceFor(heightNeeded, env);
21
+ }
22
+ let x = baseLeft;
23
+ if (block.align === "center") {
24
+ x = baseLeft + (availableWidth - imgWidth) / 2;
25
+ } else if (block.align === "right") {
26
+ x = baseLeft + availableWidth - imgWidth;
27
+ }
28
+ const rotation = typeof block.rotate === "number" ? block.rotate : block.orientation === "vertical" ? 90 : 0;
29
+ try {
30
+ drawRotatedImage(doc, imgSource, x, startY, imgWidth, imgHeight, rotation);
31
+ } catch (e) {
32
+ console.warn("Failed to load barcode image:", e);
33
+ }
34
+ const newY = startY + imgHeight + mb;
35
+ if (y === null) ctx.currentY = newY;
36
+ return newY;
37
+ };
38
+
39
+ // src/renderer-engine/blocks/columns.ts
40
+ var processColumnsBlock = (ctx, block, y, env, computeColumnPixelWidths2, renderBlock, ensureSpaceFor, measureBlockHeight) => {
41
+ const blockLeft = block.marginLeft ?? 0;
42
+ const blockRight = block.marginRight ?? 0;
43
+ const baseLeft = env.marginLeft + blockLeft;
44
+ const totalWidth = Math.max(env.innerWidth - blockLeft - blockRight, 1);
45
+ const mt = block.marginTop ?? 0;
46
+ const mb = block.marginBottom ?? 0;
47
+ const cols = block.columns ?? [];
48
+ const n = cols.length;
49
+ if (!n) {
50
+ const baseY0 = y ?? ctx.currentY;
51
+ const endY = baseY0 + mt + mb;
52
+ if (y === null) ctx.currentY = endY;
53
+ return endY;
54
+ }
55
+ let colWidths;
56
+ const mode = block.mode ?? "fixedGap";
57
+ let gap;
58
+ if (mode === "spaceBetween" && n > 1) {
59
+ if (block.widths && block.widths.length === n) {
60
+ colWidths = computeColumnPixelWidths2(block.widths, totalWidth);
61
+ } else {
62
+ colWidths = Array(n).fill(totalWidth / n);
63
+ }
64
+ const totalColsWidth = colWidths.reduce((a, b) => a + b, 0);
65
+ const remaining = Math.max(totalWidth - totalColsWidth, 0);
66
+ gap = remaining / (n - 1);
67
+ } else {
68
+ gap = block.gap ?? 20;
69
+ const totalGapsWidth = gap * (n - 1);
70
+ const widthForCols = Math.max(totalWidth - totalGapsWidth, 1);
71
+ if (block.widths && block.widths.length === n) {
72
+ colWidths = computeColumnPixelWidths2(block.widths, widthForCols);
73
+ } else {
74
+ colWidths = Array(n).fill(widthForCols / n);
75
+ }
76
+ }
77
+ const xBases = [];
78
+ {
79
+ let x = baseLeft;
80
+ for (let i = 0; i < n; i++) {
81
+ xBases.push(x);
82
+ x += colWidths[i] + (i < n - 1 ? gap : 0);
83
+ }
84
+ }
85
+ const measureColHeights = [];
86
+ for (let colIndex = 0; colIndex < n; colIndex++) {
87
+ const colEnvMeasure = {
88
+ marginLeft: 0,
89
+ innerWidth: colWidths[colIndex],
90
+ allowPageBreak: false
91
+ // measure as keep-together
92
+ };
93
+ let h = 0;
94
+ for (const b of cols[colIndex]) {
95
+ if (b.visible === false) continue;
96
+ h += measureBlockHeight(b, colEnvMeasure);
97
+ }
98
+ measureColHeights.push(h);
99
+ }
100
+ const maxColHeight = Math.max(...measureColHeights, 0);
101
+ const totalHeightNeeded = mt + maxColHeight + mb;
102
+ if (y === null) {
103
+ ensureSpaceFor(totalHeightNeeded, env);
104
+ }
105
+ const baseY = y ?? ctx.currentY;
106
+ const startY = baseY + mt;
107
+ const heights = [];
108
+ for (let colIndex = 0; colIndex < n; colIndex++) {
109
+ const colEnv = {
110
+ marginLeft: xBases[colIndex],
111
+ innerWidth: colWidths[colIndex],
112
+ allowPageBreak: false
113
+ // keep whole column block intact on one page
114
+ };
115
+ let colY = startY;
116
+ for (const b of cols[colIndex]) {
117
+ if (b.visible === false) continue;
118
+ colY = renderBlock(b, colY, colEnv);
119
+ }
120
+ heights.push(colY - startY);
121
+ }
122
+ const maxHeight = Math.max(...heights, 0);
123
+ const newY = startY + maxHeight + mb;
124
+ if (y === null) ctx.currentY = newY;
125
+ return newY;
126
+ };
127
+
128
+ // src/renderer-engine/blocks/image.ts
129
+ var drawRotatedImage = (doc, src, x, y, width, height, rotationDeg) => {
130
+ if (!rotationDeg) {
131
+ doc.image(src, x, y, { width, height });
132
+ return;
133
+ }
134
+ const cx = x + width / 2;
135
+ const cy = y + height / 2;
136
+ doc.save();
137
+ doc.rotate(rotationDeg, { origin: [cx, cy] });
138
+ doc.image(src, x, y, { width, height });
139
+ doc.restore();
140
+ };
141
+ var processImageBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
142
+ const imgSource = block.src;
143
+ const imgWidth = block.width ?? 80;
144
+ const imgHeight = block.height ?? 50;
145
+ const localLeft = block.marginLeft ?? 0;
146
+ const localRight = block.marginRight ?? 0;
147
+ const baseLeft = env.marginLeft + localLeft;
148
+ const availableWidth = Math.max(env.innerWidth - localLeft - localRight, 1);
149
+ const mt = block.marginTop ?? 0;
150
+ const mb = block.marginBottom ?? 0;
151
+ const baseY = y ?? ctx.currentY;
152
+ const startY = baseY + mt;
153
+ const heightNeeded = mt + imgHeight + mb;
154
+ if (y === null) {
155
+ ensureSpaceFor(heightNeeded, env);
156
+ }
157
+ let x = baseLeft;
158
+ if (block.align === "center") {
159
+ x = baseLeft + (availableWidth - imgWidth) / 2;
160
+ } else if (block.align === "right") {
161
+ x = baseLeft + availableWidth - imgWidth;
162
+ }
163
+ try {
164
+ doc.image(imgSource, x, startY, {
165
+ width: imgWidth,
166
+ height: imgHeight
167
+ });
168
+ } catch (e) {
169
+ console.warn("Failed to load image:", e);
170
+ }
171
+ const newY = startY + imgHeight + mb;
172
+ if (y === null) ctx.currentY = newY;
173
+ return newY;
174
+ };
175
+
176
+ // src/renderer-engine/blocks/key-value-grid.ts
177
+ var processKeyValueGridBlock = (doc, ctx, styles, block, y, env, computeColumnPixelWidths2, ensureSpaceFor) => {
178
+ const mt = block.marginTop ?? 0;
179
+ const mb = block.marginBottom ?? 0;
180
+ const rowGap = block.rowGap ?? 4;
181
+ const orientation = block.orientation ?? "horizontal";
182
+ const cols = block.columns ?? [];
183
+ const colCount = cols.length;
184
+ if (!colCount) {
185
+ const baseY2 = y ?? ctx.currentY;
186
+ const endY2 = baseY2 + mt + mb;
187
+ if (y === null) ctx.currentY = endY2;
188
+ return endY2;
189
+ }
190
+ const blockLeft = block.marginLeft ?? 0;
191
+ const blockRight = block.marginRight ?? 0;
192
+ const baseLeft = env.marginLeft + blockLeft;
193
+ const totalWidth = Math.max(env.innerWidth - blockLeft - blockRight, 1);
194
+ const columnGap = block.columnGap ?? 30;
195
+ const totalGapWidth = columnGap * (colCount - 1);
196
+ const widthForCols = Math.max(totalWidth - totalGapWidth, 1);
197
+ let colWidths;
198
+ if (block.columnWidths && block.columnWidths.length === colCount) {
199
+ const safeWidths = block.columnWidths.map(
200
+ (w) => w === void 0 ? "*" : w
201
+ );
202
+ colWidths = computeColumnPixelWidths2(safeWidths, widthForCols);
203
+ } else {
204
+ const equal = widthForCols / colCount;
205
+ colWidths = Array(colCount).fill(equal);
206
+ }
207
+ const xBases = [];
208
+ {
209
+ let x = baseLeft;
210
+ for (let i = 0; i < colCount; i++) {
211
+ xBases.push(x);
212
+ x += colWidths[i] + columnGap;
213
+ }
214
+ }
215
+ const maxRows = Math.max(...cols.map((c) => c.length));
216
+ const separatorText = block.separator;
217
+ const sepBoxWidth = separatorText ? 10 : 0;
218
+ const baseKeyWidthRatio = 0.35;
219
+ const resolveStyled = (base, styleNames) => resolveTextBlock(styles, {
220
+ ...base,
221
+ style: styleNames
222
+ });
223
+ const measureTextHeight = (tb, width) => {
224
+ const fontName = getFontNameForText(tb);
225
+ doc.font(fontName);
226
+ doc.fontSize(tb.fontSize ?? 10);
227
+ return doc.heightOfString(tb.text ?? "", { width });
228
+ };
229
+ const buildKeyBlock = (item) => ({
230
+ type: "text",
231
+ text: item.key ?? "",
232
+ align: block.keyAlign ?? "left",
233
+ ...block.keyTextStyle ?? {},
234
+ ...item.keyTextStyle ?? {}
235
+ });
236
+ const buildValueBlock = (item) => ({
237
+ type: "text",
238
+ text: item.value ?? "",
239
+ align: block.valueAlign ?? "left",
240
+ ...block.valueTextStyle ?? {},
241
+ ...item.valueTextStyle ?? {}
242
+ });
243
+ let totalHeight = mt;
244
+ if (orientation === "vertical") {
245
+ const keyValueGap = block.verticalKeyValueGap ?? 2;
246
+ let maxColHeight = 0;
247
+ for (let colIndex = 0; colIndex < colCount; colIndex++) {
248
+ const col = cols[colIndex];
249
+ const colWidth = colWidths[colIndex];
250
+ let colHeight = 0;
251
+ for (const item of col) {
252
+ const keyStyleNames = item.keyStyle ?? block.keyStyle ?? void 0;
253
+ const valueStyleNames = item.valueStyle ?? block.valueStyle ?? void 0;
254
+ const keyBase = buildKeyBlock(item);
255
+ const keyResolved = resolveStyled(keyBase, keyStyleNames);
256
+ const keyHeight = measureTextHeight(keyResolved, colWidth);
257
+ const valueBase = buildValueBlock(item);
258
+ const valueResolved = resolveStyled(valueBase, valueStyleNames);
259
+ const valueHeight = measureTextHeight(valueResolved, colWidth);
260
+ const rowHeight = keyHeight + keyValueGap + valueHeight;
261
+ colHeight += rowHeight + rowGap;
262
+ }
263
+ if (colHeight > maxColHeight) maxColHeight = colHeight;
264
+ }
265
+ totalHeight += maxColHeight;
266
+ } else {
267
+ for (let rowIndex = 0; rowIndex < maxRows; rowIndex++) {
268
+ let rowHeight = 0;
269
+ for (let colIndex = 0; colIndex < colCount; colIndex++) {
270
+ const col = cols[colIndex];
271
+ const item = col[rowIndex];
272
+ if (!item) continue;
273
+ const colWidth = colWidths[colIndex];
274
+ const keyWidthPx = block.keyWidth === "*" ? Math.max(colWidth * baseKeyWidthRatio, 20) : block.keyWidth ?? 80;
275
+ const keyStyleNames = item.keyStyle ?? block.keyStyle ?? void 0;
276
+ const valueStyleNames = item.valueStyle ?? block.valueStyle ?? void 0;
277
+ const keyBase = buildKeyBlock(item);
278
+ const keyResolved = resolveStyled(keyBase, keyStyleNames);
279
+ const keyHeight = measureTextHeight(keyResolved, keyWidthPx);
280
+ const valueBase = buildValueBlock(item);
281
+ const valueResolved = resolveStyled(valueBase, valueStyleNames);
282
+ const valueXInsideCol = keyWidthPx + (separatorText ? sepBoxWidth + 4 : 4);
283
+ const valueWidth = Math.max(colWidth - valueXInsideCol, 1);
284
+ const valueHeight = measureTextHeight(valueResolved, valueWidth);
285
+ const cellHeight = Math.max(keyHeight, valueHeight);
286
+ if (cellHeight > rowHeight) rowHeight = cellHeight;
287
+ }
288
+ if (rowHeight > 0) {
289
+ totalHeight += rowHeight + rowGap;
290
+ }
291
+ }
292
+ }
293
+ totalHeight += mb;
294
+ if (y === null) {
295
+ ensureSpaceFor(totalHeight, env);
296
+ }
297
+ const baseY = y ?? ctx.currentY;
298
+ let rowY = baseY + mt;
299
+ if (orientation === "vertical") {
300
+ const keyValueGap = block.verticalKeyValueGap ?? 2;
301
+ let maxColBottom = rowY;
302
+ for (let colIndex = 0; colIndex < colCount; colIndex++) {
303
+ const col = cols[colIndex];
304
+ const colWidth = colWidths[colIndex];
305
+ const colX = xBases[colIndex];
306
+ let colY = rowY;
307
+ for (const item of col) {
308
+ const keyStyleNames = item.keyStyle ?? block.keyStyle ?? void 0;
309
+ const valueStyleNames = item.valueStyle ?? block.valueStyle ?? void 0;
310
+ const keyBlock = buildKeyBlock(item);
311
+ const keyResolved = resolveStyled(keyBlock, keyStyleNames);
312
+ const keyHeight = measureTextHeight(keyResolved, colWidth);
313
+ drawStyledText(doc, keyResolved, colX, colY, colWidth);
314
+ colY += keyHeight + keyValueGap;
315
+ const valueBlock = buildValueBlock(item);
316
+ const valueResolved = resolveStyled(valueBlock, valueStyleNames);
317
+ const valueHeight = measureTextHeight(valueResolved, colWidth);
318
+ drawStyledText(doc, valueResolved, colX, colY, colWidth);
319
+ colY += valueHeight + rowGap;
320
+ }
321
+ if (colY > maxColBottom) maxColBottom = colY;
322
+ }
323
+ rowY = maxColBottom;
324
+ } else {
325
+ for (let rowIndex = 0; rowIndex < maxRows; rowIndex++) {
326
+ let rowHeight = 0;
327
+ for (let colIndex = 0; colIndex < colCount; colIndex++) {
328
+ const col = cols[colIndex];
329
+ const item = col[rowIndex];
330
+ if (!item) continue;
331
+ const colWidth = colWidths[colIndex];
332
+ const keyWidthPx = block.keyWidth === "*" ? Math.max(colWidth * baseKeyWidthRatio, 20) : block.keyWidth ?? 80;
333
+ const keyStyleNames = item.keyStyle ?? block.keyStyle ?? void 0;
334
+ const valueStyleNames = item.valueStyle ?? block.valueStyle ?? void 0;
335
+ const keyBase = buildKeyBlock(item);
336
+ const keyResolved = resolveStyled(keyBase, keyStyleNames);
337
+ const keyHeight = measureTextHeight(keyResolved, keyWidthPx);
338
+ const valueBase = buildValueBlock(item);
339
+ const valueResolved = resolveStyled(valueBase, valueStyleNames);
340
+ const valueXInsideCol = keyWidthPx + (separatorText ? sepBoxWidth + 4 : 4);
341
+ const valueWidth = Math.max(colWidth - valueXInsideCol, 1);
342
+ const valueHeight = measureTextHeight(valueResolved, valueWidth);
343
+ const cellHeight = Math.max(keyHeight, valueHeight);
344
+ if (cellHeight > rowHeight) rowHeight = cellHeight;
345
+ }
346
+ if (rowHeight === 0) continue;
347
+ for (let colIndex = 0; colIndex < colCount; colIndex++) {
348
+ const col = cols[colIndex];
349
+ const item = col[rowIndex];
350
+ if (!item) continue;
351
+ const colX = xBases[colIndex];
352
+ const colWidth = colWidths[colIndex];
353
+ const keyWidthPx = block.keyWidth === "*" ? Math.max(colWidth * baseKeyWidthRatio, 20) : block.keyWidth ?? 80;
354
+ const keyStyleNames = item.keyStyle ?? block.keyStyle ?? void 0;
355
+ const valueStyleNames = item.valueStyle ?? block.valueStyle ?? void 0;
356
+ const keyBlock = buildKeyBlock(item);
357
+ const keyResolved = resolveStyled(keyBlock, keyStyleNames);
358
+ drawStyledText(doc, keyResolved, colX, rowY, keyWidthPx);
359
+ if (separatorText) {
360
+ const sepBlock = {
361
+ type: "text",
362
+ text: separatorText,
363
+ align: "left"
364
+ };
365
+ const sepResolved = resolveStyled(sepBlock, keyStyleNames);
366
+ const sepX = colX + keyWidthPx + 2;
367
+ drawStyledText(doc, sepResolved, sepX, rowY, sepBoxWidth);
368
+ }
369
+ const valueX = colX + keyWidthPx + (separatorText ? sepBoxWidth + 4 : 4);
370
+ const valueWidth = Math.max(colWidth - (valueX - colX), 1);
371
+ const valueBlock = buildValueBlock(item);
372
+ const valueResolved = resolveStyled(valueBlock, valueStyleNames);
373
+ drawStyledText(doc, valueResolved, valueX, rowY, valueWidth);
374
+ }
375
+ rowY += rowHeight + rowGap;
376
+ }
377
+ }
378
+ const endY = rowY + mb;
379
+ if (y === null) ctx.currentY = endY;
380
+ return endY;
381
+ };
382
+
383
+ // src/renderer-engine/blocks/line.ts
384
+ var processLineBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
385
+ const localLeft = block.marginLeft ?? 0;
386
+ const localRight = block.marginRight ?? 0;
387
+ const xLeft = env.marginLeft + localLeft;
388
+ const width = Math.max(env.innerWidth - localLeft - localRight, 1);
389
+ const mt = block.marginTop ?? 0;
390
+ const mb = block.marginBottom ?? 0;
391
+ const baseY = y ?? ctx.currentY;
392
+ const startY = baseY + mt;
393
+ const lineWidth = block.lineWidth ?? 1;
394
+ const heightNeeded = mt + lineWidth + mb;
395
+ if (y === null) {
396
+ ensureSpaceFor(heightNeeded, env);
397
+ }
398
+ doc.save();
399
+ if (block.color) {
400
+ doc.strokeColor(block.color);
401
+ }
402
+ doc.lineWidth(lineWidth);
403
+ doc.moveTo(xLeft, startY).lineTo(xLeft + width, startY).stroke();
404
+ doc.restore();
405
+ const newY = startY + lineWidth + mb;
406
+ if (y === null) ctx.currentY = newY;
407
+ return newY;
408
+ };
409
+
410
+ // src/renderer-engine/blocks/table.ts
411
+ var measureTableHeight = (doc, table, env, styles, computeColumnPixelWidths2) => {
412
+ const localLeft = table.marginLeft ?? 0;
413
+ const localRight = table.marginRight ?? 0;
414
+ const width = Math.max(env.innerWidth - localLeft - localRight, 1);
415
+ const colWidths = computeColumnPixelWidths2(table.widths, width);
416
+ let totalHeight = 0;
417
+ totalHeight += table.marginTop ?? 0;
418
+ table.body.forEach((row) => {
419
+ let rowHeight = 0;
420
+ row.forEach((rawCell, colIndex) => {
421
+ const cell = resolveTableCell(styles, rawCell);
422
+ const colW = colWidths[colIndex] ?? 50;
423
+ const fontSize = cell.fontSize ?? 10;
424
+ const isBold = !!cell.bold;
425
+ const isItalic = !!cell.italic;
426
+ const fontName = cell.font ? cell.font : isBold && isItalic ? "Helvetica-BoldOblique" : isBold ? "Helvetica-Bold" : isItalic ? "Helvetica-Oblique" : "Helvetica";
427
+ doc.font(fontName);
428
+ doc.fontSize(fontSize);
429
+ const h = doc.heightOfString(cell.text, { width: colW });
430
+ rowHeight = Math.max(rowHeight, h + 4);
431
+ });
432
+ totalHeight += rowHeight;
433
+ });
434
+ totalHeight += table.lineGap ?? 6;
435
+ totalHeight += table.marginBottom ?? 0;
436
+ return totalHeight;
437
+ };
438
+ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidths2, bottomLimitForContent, finishPage2) => {
439
+ const localLeft = table.marginLeft ?? 0;
440
+ const localRight = table.marginRight ?? 0;
441
+ const baseLeft = env.marginLeft + localLeft;
442
+ const width = Math.max(env.innerWidth - localLeft - localRight, 1);
443
+ const mt = table.marginTop ?? 0;
444
+ const mb = table.marginBottom ?? 0;
445
+ const baseY = y ?? ctx.currentY;
446
+ const startY = baseY + mt;
447
+ const colWidths = computeColumnPixelWidths2(table.widths, width);
448
+ const layout = table.layout ?? {
449
+ border: "all",
450
+ hLineColor: "#ccc",
451
+ vLineColor: "#ccc"
452
+ };
453
+ const borderAll = layout.border !== "none";
454
+ const headerRows = table.headerRows ?? 0;
455
+ const measureRowHeight = (row) => {
456
+ let rowHeight = 0;
457
+ row.forEach((rawCell, colIndex) => {
458
+ const cell = resolveTableCell(styles, rawCell);
459
+ const colW = colWidths[colIndex] ?? 50;
460
+ const fontSize = cell.fontSize ?? 10;
461
+ const fontName = cell.font ? cell.font : cell.bold ? "Helvetica-Bold" : "Helvetica";
462
+ doc.font(fontName);
463
+ doc.fontSize(fontSize);
464
+ const textWidth = Math.max(colW - 4, 1);
465
+ const h = doc.heightOfString(cell.text, {
466
+ width: textWidth
467
+ }) + 4;
468
+ rowHeight = Math.max(rowHeight, h);
469
+ });
470
+ return rowHeight || 12;
471
+ };
472
+ const drawRow = (row, rowTop2, rowHeight, isHeaderRow, drawTopBorder) => {
473
+ row.forEach((rawCell, colIndex) => {
474
+ const cell = resolveTableCell(styles, rawCell);
475
+ const colW = colWidths[colIndex] ?? 50;
476
+ const x = baseLeft + colWidths.slice(0, colIndex).reduce((a, b) => a + b, 0);
477
+ const fontSize = cell.fontSize ?? 10;
478
+ doc.save();
479
+ if (cell.fillColor) {
480
+ doc.rect(x, rowTop2, colW, rowHeight).fillOpacity(1).fill(cell.fillColor);
481
+ doc.fillOpacity(1);
482
+ }
483
+ const isBold = !!cell.bold;
484
+ const isItalic = !!cell.italic;
485
+ const fontName = cell.font ? cell.font : isBold && isItalic ? "Helvetica-BoldOblique" : isBold ? "Helvetica-Bold" : isItalic ? "Helvetica-Oblique" : "Helvetica";
486
+ doc.font(fontName);
487
+ doc.fontSize(fontSize);
488
+ if (cell.color) {
489
+ doc.fillColor(cell.color);
490
+ } else {
491
+ doc.fillColor("black");
492
+ }
493
+ const align = cell.align ?? "left";
494
+ const textWidth = Math.max(colW - 4, 1);
495
+ const textHeight = doc.heightOfString(cell.text, {
496
+ width: textWidth
497
+ });
498
+ let textY = rowTop2 + 2;
499
+ if (isHeaderRow) {
500
+ const available = rowHeight - 4;
501
+ const offset = Math.max((available - textHeight) / 2, 0);
502
+ textY = rowTop2 + 2 + offset;
503
+ }
504
+ doc.text(cell.text, x + 2, textY, {
505
+ width: textWidth,
506
+ align,
507
+ underline: !!cell.underline,
508
+ strike: !!cell.strike,
509
+ link: cell.link
510
+ });
511
+ doc.restore();
512
+ });
513
+ if (borderAll) {
514
+ const bottomY = rowTop2 + rowHeight;
515
+ doc.save();
516
+ doc.lineWidth(0.5);
517
+ doc.strokeColor(layout.hLineColor ?? "#ccc");
518
+ if (drawTopBorder) {
519
+ doc.moveTo(baseLeft, rowTop2).lineTo(baseLeft + width, rowTop2).stroke();
520
+ }
521
+ doc.moveTo(baseLeft, bottomY).lineTo(baseLeft + width, bottomY).stroke();
522
+ doc.restore();
523
+ doc.save();
524
+ doc.lineWidth(0.5);
525
+ doc.strokeColor(layout.vLineColor ?? "#ccc");
526
+ let x = baseLeft;
527
+ colWidths.forEach((colW) => {
528
+ doc.moveTo(x, rowTop2).lineTo(x, bottomY).stroke();
529
+ x += colW;
530
+ });
531
+ doc.moveTo(baseLeft + width, rowTop2).lineTo(baseLeft + width, bottomY).stroke();
532
+ doc.restore();
533
+ }
534
+ return rowTop2 + rowHeight;
535
+ };
536
+ const drawHeaderRowsOnNewPage = () => {
537
+ if (!headerRows) return doc.page.margins.top + mt;
538
+ let rowTop2 = doc.page.margins.top + mt;
539
+ for (let i = 0; i < headerRows; i++) {
540
+ const headerRow = table.body[i];
541
+ const headerHeight = measureRowHeight(headerRow);
542
+ rowTop2 = drawRow(headerRow, rowTop2, headerHeight, true, true);
543
+ }
544
+ return rowTop2;
545
+ };
546
+ let rowTop = startY;
547
+ table.body.forEach((row, rowIndex) => {
548
+ const isHeaderRow = rowIndex < headerRows;
549
+ const rowHeight = measureRowHeight(row);
550
+ const bottomLimit = bottomLimitForContent();
551
+ if (rowTop + rowHeight > bottomLimit) {
552
+ finishPage2(true);
553
+ rowTop = headerRows ? drawHeaderRowsOnNewPage() : doc.page.margins.top + mt;
554
+ }
555
+ const drawTopBorder = true;
556
+ rowTop = drawRow(row, rowTop, rowHeight, isHeaderRow, drawTopBorder);
557
+ });
558
+ const newY = rowTop + (table.lineGap ?? 6) + mb;
559
+ if (y === null) ctx.currentY = newY;
560
+ return newY;
561
+ };
562
+
563
+ // src/renderer-engine/blocks/text.ts
564
+ var processTextBlock = (doc, ctx, styles, block, y, env, ensureSpaceFor) => {
565
+ const tb = resolveTextBlock(styles, block);
566
+ const localLeft = tb.marginLeft ?? 0;
567
+ const localRight = tb.marginRight ?? 0;
568
+ const xLeft = env.marginLeft + localLeft;
569
+ const width = Math.max(env.innerWidth - localLeft - localRight, 1);
570
+ const baseY = y ?? ctx.currentY;
571
+ const startY = baseY;
572
+ doc.fontSize(tb.fontSize ?? 10);
573
+ const isBold = !!tb.bold;
574
+ const isItalic = !!tb.italic;
575
+ let fontName = tb.font;
576
+ if (!fontName) {
577
+ if (isBold && isItalic) fontName = "Helvetica-BoldOblique";
578
+ else if (isBold) fontName = "Helvetica-Bold";
579
+ else if (isItalic) fontName = "Helvetica-Oblique";
580
+ else fontName = "Helvetica";
581
+ }
582
+ doc.font(fontName);
583
+ if (tb.color) doc.fillColor(tb.color);
584
+ else doc.fillColor("black");
585
+ const textHeight = doc.heightOfString(tb.text, { width });
586
+ if (y === null) {
587
+ const gapCheck = tb.lineGap ?? 4;
588
+ ensureSpaceFor(textHeight + gapCheck, env);
589
+ }
590
+ doc.text(tb.text, xLeft, startY, {
591
+ width,
592
+ align: tb.align ?? "left",
593
+ underline: !!tb.underline,
594
+ strike: !!tb.strike,
595
+ link: tb.link
596
+ });
597
+ const gap = tb.lineGap ?? 4;
598
+ const newY = startY + textHeight + gap;
599
+ if (y === null) ctx.currentY = newY;
600
+ return newY;
601
+ };
602
+
603
+ // src/renderer-engine/utils/block-renderer.ts
604
+ function createBlockRenderer(deps) {
605
+ const {
606
+ doc,
607
+ ctx,
608
+ styles,
609
+ computeColumnPixelWidths: computeColumnPixelWidths2,
610
+ finishPage: finishPage2,
611
+ processSignatureBlock
612
+ } = deps;
613
+ const bottomLimitForContent = createBottomLimitForContent(doc, ctx);
614
+ const ensureSpaceFor = createEnsureSpaceFor(
615
+ ctx,
616
+ bottomLimitForContent,
617
+ finishPage2
618
+ );
619
+ const measureBlockHeight = createMeasureBlockHeight({
620
+ doc,
621
+ styles,
622
+ computeColumnPixelWidths: computeColumnPixelWidths2
623
+ });
624
+ const renderBlock = (block, y, env) => {
625
+ if (block.visible === false) {
626
+ return y ?? ctx.currentY;
627
+ }
628
+ switch (block.type) {
629
+ case "text":
630
+ return processTextBlock(
631
+ doc,
632
+ ctx,
633
+ styles,
634
+ block,
635
+ y,
636
+ env,
637
+ ensureSpaceFor
638
+ );
639
+ case "image":
640
+ return processImageBlock(
641
+ doc,
642
+ ctx,
643
+ block,
644
+ y,
645
+ env,
646
+ ensureSpaceFor
647
+ );
648
+ case "qr": {
649
+ const qb = block;
650
+ const imageLike = {
651
+ type: "image",
652
+ src: qb.src,
653
+ width: qb.size,
654
+ height: qb.size,
655
+ align: qb.align,
656
+ marginTop: qb.marginTop,
657
+ marginBottom: qb.marginBottom,
658
+ marginLeft: qb.marginLeft,
659
+ marginRight: qb.marginRight
660
+ };
661
+ return processImageBlock(doc, ctx, imageLike, y, env, ensureSpaceFor);
662
+ }
663
+ case "barcode":
664
+ return processBarcodeBlock(doc, ctx, block, y, env, ensureSpaceFor);
665
+ case "line":
666
+ return processLineBlock(
667
+ doc,
668
+ ctx,
669
+ block,
670
+ y,
671
+ env,
672
+ ensureSpaceFor
673
+ );
674
+ case "table":
675
+ return processTableBlock(
676
+ doc,
677
+ ctx,
678
+ styles,
679
+ block,
680
+ y,
681
+ env,
682
+ computeColumnPixelWidths2,
683
+ bottomLimitForContent,
684
+ finishPage2
685
+ );
686
+ case "columns":
687
+ return processColumnsBlock(
688
+ ctx,
689
+ block,
690
+ y,
691
+ env,
692
+ computeColumnPixelWidths2,
693
+ renderBlock,
694
+ ensureSpaceFor,
695
+ measureBlockHeight
696
+ );
697
+ case "keyValueGrid":
698
+ return processKeyValueGridBlock(
699
+ doc,
700
+ ctx,
701
+ styles,
702
+ block,
703
+ y,
704
+ env,
705
+ computeColumnPixelWidths2,
706
+ ensureSpaceFor
707
+ );
708
+ case "signature":
709
+ if (!env.allowPageBreak) {
710
+ throw new Error(
711
+ "Signature block is only allowed in main content flow."
712
+ );
713
+ }
714
+ if (y !== null) {
715
+ throw new Error(
716
+ "Signature block must be part of main flow, not drawn at explicit Y."
717
+ );
718
+ }
719
+ processSignatureBlock(block);
720
+ return ctx.currentY;
721
+ case "pageBreak":
722
+ if (!env.allowPageBreak || ctx.inFooter) {
723
+ return y ?? ctx.currentY;
724
+ }
725
+ finishPage2(true);
726
+ return ctx.currentY;
727
+ default:
728
+ return ctx.currentY;
729
+ }
730
+ };
731
+ const renderBlockArray = (blocks, startY, env) => {
732
+ let localY = startY;
733
+ for (const block of blocks) {
734
+ if (block.visible === false) {
735
+ continue;
736
+ }
737
+ localY = renderBlock(block, localY, env);
738
+ }
739
+ return localY;
740
+ };
741
+ return {
742
+ renderBlock,
743
+ renderBlockArray
744
+ };
745
+ }
746
+
747
+ // src/renderer-engine/utils/context.ts
748
+ function createInitialContext(doc) {
749
+ return {
750
+ pageNumber: 1,
751
+ currentY: doc.page.margins.top,
752
+ signatureBlock: null,
753
+ signatureTopY: null,
754
+ signatureHeight: 0,
755
+ signaturePlaced: false,
756
+ afterSignature: false,
757
+ inFooter: false
758
+ };
759
+ }
760
+
761
+ // src/renderer-engine/utils/ensure-space.ts
762
+ function createEnsureSpaceFor(ctx, bottomLimitForContent, finishPage2) {
763
+ return (heightNeeded, env) => {
764
+ if (!env.allowPageBreak || ctx.inFooter) return;
765
+ const bottomLimit = bottomLimitForContent();
766
+ if (ctx.currentY + heightNeeded > bottomLimit) {
767
+ finishPage2(true);
768
+ }
769
+ };
770
+ }
771
+
772
+ // src/renderer-engine/utils/env.ts
773
+ var contentEnv = (doc) => ({
774
+ marginLeft: doc.page.margins.left,
775
+ innerWidth: doc.page.width - doc.page.margins.left - doc.page.margins.right,
776
+ allowPageBreak: true
777
+ });
778
+
779
+ // src/renderer-engine/utils/finish-page.ts
780
+ function finishPage({
781
+ addNewPage,
782
+ doc,
783
+ def,
784
+ ctx,
785
+ footerBandHeight,
786
+ renderBlockArray,
787
+ startNewPageLayout
788
+ }) {
789
+ const mode = def.watermark?.mode;
790
+ if (def.watermark && watermarkUsesLast(mode)) {
791
+ const isLast = !addNewPage;
792
+ drawWatermarkForPage(doc, def.watermark, ctx.pageNumber, isLast);
793
+ }
794
+ if (ctx.signatureBlock && ctx.signatureTopY !== null) {
795
+ const env = contentEnv(doc);
796
+ drawSignatureBlock(
797
+ doc,
798
+ ctx.signatureBlock,
799
+ ctx.signatureTopY,
800
+ env,
801
+ renderBlockArray
802
+ );
803
+ }
804
+ drawFooter(doc, def, ctx, footerBandHeight, renderBlockArray);
805
+ if (addNewPage) {
806
+ doc.addPage();
807
+ ctx.pageNumber += 1;
808
+ startNewPageLayout();
809
+ }
810
+ }
811
+
812
+ // src/renderer-engine/utils/footer.ts
813
+ function normalizeFooter(input, ctx, doc) {
814
+ if (!input) return null;
815
+ if (typeof input === "function") {
816
+ const result = input(ctx.pageNumber, {
817
+ width: doc.page.width,
818
+ height: doc.page.height
819
+ });
820
+ if (!result) return null;
821
+ return normalizeFooter(result, ctx, doc);
822
+ }
823
+ if (input.blocks && Array.isArray(input.blocks)) {
824
+ const f = input;
825
+ return {
826
+ visible: f.visible,
827
+ blocks: f.blocks,
828
+ marginTop: f.marginTop,
829
+ marginBottom: f.marginBottom,
830
+ marginLeft: f.marginLeft,
831
+ marginRight: f.marginRight,
832
+ backgroundColor: f.backgroundColor,
833
+ backgroundImage: f.backgroundImage
834
+ };
835
+ }
836
+ if (input.type) {
837
+ return { blocks: [input] };
838
+ }
839
+ if (Array.isArray(input)) {
840
+ return { blocks: input };
841
+ }
842
+ return null;
843
+ }
844
+ function drawFooter(doc, def, ctx, footerBandHeight, renderBlockArray) {
845
+ const footerConfig = def.footer;
846
+ if (!footerConfig) return;
847
+ const layout = normalizeFooter(footerConfig, ctx, doc);
848
+ if (!layout || !layout.blocks.length) return;
849
+ if (layout.visible === false) return;
850
+ const bandHeight = footerBandHeight;
851
+ if (!bandHeight) return;
852
+ const { marginTop = 4, backgroundColor, backgroundImage, blocks } = layout;
853
+ const footerMarginLeft = layout.marginLeft ?? 0;
854
+ const footerMarginRight = layout.marginRight ?? 0;
855
+ const contentWidth = doc.page.width - footerMarginLeft - footerMarginRight;
856
+ const footerEnv = {
857
+ marginLeft: footerMarginLeft,
858
+ innerWidth: contentWidth,
859
+ allowPageBreak: false
860
+ };
861
+ const pageHeight = doc.page.height;
862
+ const bandTop = pageHeight - bandHeight;
863
+ if (backgroundColor) {
864
+ doc.save();
865
+ doc.rect(0, bandTop, doc.page.width, bandHeight).fill(backgroundColor);
866
+ doc.restore();
867
+ }
868
+ if (backgroundImage) {
869
+ try {
870
+ doc.image(backgroundImage, 0, bandTop, {
871
+ width: doc.page.width,
872
+ height: bandHeight
873
+ });
874
+ } catch (e) {
875
+ console.warn("Failed to load footer background image:", e);
876
+ }
877
+ }
878
+ doc.save();
879
+ doc.rect(footerMarginLeft, bandTop, contentWidth, bandHeight).clip();
880
+ doc.translate(0, bandTop);
881
+ ctx.inFooter = true;
882
+ const localStartY = marginTop;
883
+ renderBlockArray(blocks, localStartY, footerEnv);
884
+ ctx.inFooter = false;
885
+ doc.restore();
886
+ }
887
+
888
+ // src/renderer-engine/utils/header.ts
889
+ function drawHeader(doc, def, headerBandHeight, header, renderBlockArray) {
890
+ if (!header || !header.blocks.length) return;
891
+ if (header.visible === false) return;
892
+ if (!headerBandHeight) return;
893
+ const bandTop = 0;
894
+ const bandHeight = def.margins.top;
895
+ const headerMarginLeft = header.marginLeft ?? 0;
896
+ const headerMarginRight = header.marginRight ?? 0;
897
+ const contentWidth = doc.page.width - headerMarginLeft - headerMarginRight;
898
+ const headerEnv = {
899
+ marginLeft: headerMarginLeft,
900
+ innerWidth: contentWidth,
901
+ allowPageBreak: false
902
+ };
903
+ if (header.backgroundColor) {
904
+ doc.save();
905
+ doc.rect(0, bandTop, doc.page.width, bandHeight).fill(header.backgroundColor);
906
+ doc.restore();
907
+ }
908
+ if (header.backgroundImage) {
909
+ try {
910
+ doc.image(header.backgroundImage, 0, bandTop, {
911
+ width: doc.page.width,
912
+ height: bandHeight
913
+ });
914
+ } catch (e) {
915
+ console.warn("Failed to load header background image:", e);
916
+ }
917
+ }
918
+ doc.save();
919
+ doc.rect(headerMarginLeft, bandTop, contentWidth, bandHeight).clip();
920
+ const startY = bandTop + (header.marginTop ?? 0);
921
+ renderBlockArray(header.blocks, startY, headerEnv);
922
+ doc.restore();
923
+ }
924
+
925
+ // src/renderer-engine/utils/image-loader.ts
926
+ import axios from "axios";
927
+ async function normalizeImageSrc(src) {
928
+ if (!src) return src;
929
+ if (Buffer.isBuffer(src)) return src;
930
+ if (src.startsWith("http://") || src.startsWith("https://")) {
931
+ const res = await axios.get(src, {
932
+ responseType: "arraybuffer"
933
+ });
934
+ return Buffer.from(res.data);
935
+ }
936
+ return src;
937
+ }
938
+ async function materializeImagesInBlocks(blocks) {
939
+ const out = [];
940
+ for (const block of blocks) {
941
+ if (block.type === "image") {
942
+ const img = { ...block };
943
+ img.src = await normalizeImageSrc(img.src);
944
+ out.push(img);
945
+ } else if (block.type === "columns") {
946
+ out.push({
947
+ ...block,
948
+ columns: await Promise.all(
949
+ block.columns.map((col) => materializeImagesInBlocks(col))
950
+ )
951
+ });
952
+ } else if (block.type === "signature" && block.blocks) {
953
+ out.push({
954
+ ...block,
955
+ blocks: await materializeImagesInBlocks(block.blocks)
956
+ });
957
+ } else {
958
+ out.push(block);
959
+ }
960
+ }
961
+ return out;
962
+ }
963
+
964
+ // src/renderer-engine/utils/layout.ts
965
+ function computeColumnPixelWidths(widths, totalWidth) {
966
+ let fixedTotal = 0;
967
+ let starCount = 0;
968
+ widths.forEach((w) => {
969
+ if (w === "*") starCount++;
970
+ else fixedTotal += w;
971
+ });
972
+ const remaining = Math.max(totalWidth - fixedTotal, 0);
973
+ const starWidth = starCount > 0 ? remaining / starCount : 0;
974
+ return widths.map((w) => w === "*" ? starWidth : w);
975
+ }
976
+
977
+ // src/renderer-engine/utils/styles.ts
978
+ function mergeStyleDefs(styles, names) {
979
+ const result = {};
980
+ if (!names || !styles) return result;
981
+ const arr = Array.isArray(names) ? names : [names];
982
+ for (const name of arr) {
983
+ const st = styles[name];
984
+ if (!st) continue;
985
+ if (st.fontSize !== void 0) result.fontSize = st.fontSize;
986
+ if (st.lineGap !== void 0) result.lineGap = st.lineGap;
987
+ if (st.align !== void 0) result.align = st.align;
988
+ if (st.bold !== void 0) result.bold = st.bold;
989
+ if (st.italic !== void 0) result.italic = st.italic;
990
+ if (st.underline !== void 0) result.underline = st.underline;
991
+ if (st.strike !== void 0) result.strike = st.strike;
992
+ if (st.link !== void 0) result.link = st.link;
993
+ if (st.color !== void 0) result.color = st.color;
994
+ if (st.fillColor !== void 0) result.fillColor = st.fillColor;
995
+ if (st.font !== void 0) result.font = st.font;
996
+ }
997
+ return result;
998
+ }
999
+ function resolveTextBlock(styles, block) {
1000
+ const styleDef = mergeStyleDefs(styles, block.style);
1001
+ return {
1002
+ ...styleDef,
1003
+ ...block,
1004
+ fontSize: block.fontSize ?? styleDef.fontSize,
1005
+ lineGap: block.lineGap ?? styleDef.lineGap,
1006
+ align: block.align ?? styleDef.align,
1007
+ bold: block.bold ?? styleDef.bold,
1008
+ italic: block.italic ?? styleDef.italic,
1009
+ underline: block.underline ?? styleDef.underline,
1010
+ strike: block.strike ?? styleDef.strike,
1011
+ link: block.link ?? styleDef.link,
1012
+ color: block.color ?? styleDef.color,
1013
+ // fillColor: block.fillColor ?? styleDef.fillColor,
1014
+ font: block.font ?? styleDef.font
1015
+ };
1016
+ }
1017
+ function resolveTableCell(styles, cell) {
1018
+ const styleDef = mergeStyleDefs(styles, cell.style);
1019
+ return {
1020
+ ...styleDef,
1021
+ ...cell,
1022
+ fontSize: cell.fontSize ?? styleDef.fontSize,
1023
+ align: cell.align ?? styleDef.align,
1024
+ bold: cell.bold ?? styleDef.bold,
1025
+ italic: cell.italic ?? styleDef.italic,
1026
+ color: cell.color ?? styleDef.color,
1027
+ fillColor: cell.fillColor ?? styleDef.fillColor,
1028
+ font: cell.font ?? styleDef.font
1029
+ };
1030
+ }
1031
+ var getFontNameForText = (tb) => {
1032
+ if (tb.font) return tb.font;
1033
+ const bold = tb.bold ?? false;
1034
+ const italic = tb.italic ?? false;
1035
+ if (bold && italic) return "Helvetica-BoldOblique";
1036
+ if (bold) return "Helvetica-Bold";
1037
+ if (italic) return "Helvetica-Oblique";
1038
+ return "Helvetica";
1039
+ };
1040
+ var drawStyledText = (doc, tb, x, y, width) => {
1041
+ doc.save();
1042
+ doc.fontSize(tb.fontSize ?? 10);
1043
+ doc.font(getFontNameForText(tb));
1044
+ if (tb.color) doc.fillColor(tb.color);
1045
+ else doc.fillColor("black");
1046
+ const textHeight = doc.heightOfString(tb.text, { width });
1047
+ doc.text(tb.text, x, y, {
1048
+ width,
1049
+ align: tb.align ?? "left"
1050
+ });
1051
+ const underline = tb.underline;
1052
+ const strike = tb.strike;
1053
+ if (underline) {
1054
+ const lineY = y + textHeight;
1055
+ doc.moveTo(x, lineY).lineTo(x + width, lineY).stroke();
1056
+ }
1057
+ if (strike) {
1058
+ const lineY = y + textHeight / 2;
1059
+ doc.moveTo(x, lineY).lineTo(x + width, lineY).stroke();
1060
+ }
1061
+ doc.restore();
1062
+ };
1063
+
1064
+ // src/renderer-engine/utils/measure-block-height.ts
1065
+ function createMeasureBlockHeight(deps) {
1066
+ const { doc, styles, computeColumnPixelWidths: computeColumnPixelWidths2 } = deps;
1067
+ const measureText = (b, env) => {
1068
+ const tb = resolveTextBlock(styles, b);
1069
+ const width = Math.max(env.innerWidth, 1);
1070
+ doc.font(getFontNameForText(tb));
1071
+ doc.fontSize(tb.fontSize ?? 10);
1072
+ const h = doc.heightOfString(tb.text ?? "", { width });
1073
+ const gap = tb.lineGap ?? 4;
1074
+ return h + gap;
1075
+ };
1076
+ const measureImageLike = (mt, mb, h) => (mt ?? 0) + h + (mb ?? 0);
1077
+ const measureImage = (b) => {
1078
+ const h = b.height ?? 50;
1079
+ return measureImageLike(b.marginTop, b.marginBottom, h);
1080
+ };
1081
+ const measureQr = (b) => {
1082
+ const size = b.size ?? 80;
1083
+ return measureImageLike(b.marginTop, b.marginBottom, size);
1084
+ };
1085
+ const measureBarcode = (b) => {
1086
+ const h = b.height ?? 40;
1087
+ return measureImageLike(b.marginTop, b.marginBottom, h);
1088
+ };
1089
+ const measureLine = (b) => {
1090
+ const lw = b.lineWidth ?? 1;
1091
+ const mt = b.marginTop ?? 0;
1092
+ const mb = b.marginBottom ?? 0;
1093
+ return mt + lw + mb;
1094
+ };
1095
+ const measureTable = (b, env) => {
1096
+ const mt = b.marginTop ?? 0;
1097
+ const mb = b.marginBottom ?? 0;
1098
+ const ml = b.marginLeft ?? 0;
1099
+ const mr = b.marginRight ?? 0;
1100
+ const innerWidth = Math.max(env.innerWidth - ml - mr, 1);
1101
+ const fakeEnv = {
1102
+ marginLeft: 0,
1103
+ innerWidth,
1104
+ allowPageBreak: false
1105
+ };
1106
+ const h = measureTableHeight(
1107
+ doc,
1108
+ b,
1109
+ fakeEnv,
1110
+ styles,
1111
+ computeColumnPixelWidths2
1112
+ );
1113
+ return mt + h + mb;
1114
+ };
1115
+ const measureColumns = (b, env) => {
1116
+ const mt = b.marginTop ?? 0;
1117
+ const mb = b.marginBottom ?? 0;
1118
+ const blockLeft = b.marginLeft ?? 0;
1119
+ const blockRight = b.marginRight ?? 0;
1120
+ const totalWidth = Math.max(env.innerWidth - blockLeft - blockRight, 1);
1121
+ const cols = b.columns ?? [];
1122
+ const n = cols.length;
1123
+ if (!n) return mt + mb;
1124
+ let colWidths;
1125
+ const mode = b.mode ?? "fixedGap";
1126
+ let gap;
1127
+ if (mode === "spaceBetween" && n > 1) {
1128
+ if (b.widths && b.widths.length === n) {
1129
+ colWidths = computeColumnPixelWidths2(b.widths, totalWidth);
1130
+ } else {
1131
+ colWidths = Array(n).fill(totalWidth / n);
1132
+ }
1133
+ const totalColsWidth = colWidths.reduce((a, x) => a + x, 0);
1134
+ const remaining = Math.max(totalWidth - totalColsWidth, 0);
1135
+ gap = remaining / (n - 1);
1136
+ void gap;
1137
+ } else {
1138
+ gap = b.gap ?? 20;
1139
+ const totalGapsWidth = gap * (n - 1);
1140
+ const widthForCols = Math.max(totalWidth - totalGapsWidth, 1);
1141
+ if (b.widths && b.widths.length === n) {
1142
+ colWidths = computeColumnPixelWidths2(b.widths, widthForCols);
1143
+ } else {
1144
+ colWidths = Array(n).fill(widthForCols / n);
1145
+ }
1146
+ }
1147
+ const heights = [];
1148
+ for (let i = 0; i < n; i++) {
1149
+ const colBlocks = cols[i] ?? [];
1150
+ const colEnv = {
1151
+ marginLeft: 0,
1152
+ innerWidth: colWidths[i],
1153
+ allowPageBreak: false
1154
+ };
1155
+ let colH = 0;
1156
+ for (const child of colBlocks) {
1157
+ if (!child || child.visible === false) continue;
1158
+ colH += measure(child, colEnv);
1159
+ }
1160
+ heights.push(colH);
1161
+ }
1162
+ const maxH = Math.max(...heights, 0);
1163
+ return mt + maxH + mb;
1164
+ };
1165
+ const measureKeyValueGrid = (b, env) => {
1166
+ const mt = b.marginTop ?? 0;
1167
+ const mb = b.marginBottom ?? 0;
1168
+ const rowGap = b.rowGap ?? 4;
1169
+ const orientation = b.orientation ?? "horizontal";
1170
+ const cols = b.columns ?? [];
1171
+ const colCount = cols.length;
1172
+ if (!colCount) return mt + mb;
1173
+ const blockLeft = b.marginLeft ?? 0;
1174
+ const blockRight = b.marginRight ?? 0;
1175
+ const totalWidth = Math.max(env.innerWidth - blockLeft - blockRight, 1);
1176
+ let colWidths;
1177
+ if (b.columnWidths && b.columnWidths.length === colCount) {
1178
+ const safe = b.columnWidths.map((w) => w === void 0 ? "*" : w);
1179
+ colWidths = computeColumnPixelWidths2(safe, totalWidth);
1180
+ } else {
1181
+ colWidths = Array(colCount).fill(totalWidth / colCount);
1182
+ }
1183
+ const separatorText = b.separator;
1184
+ const sepBoxWidth = separatorText ? 10 : 0;
1185
+ const baseKeyWidthRatio = 0.35;
1186
+ const measureKVText = (tb, width) => {
1187
+ const r = resolveTextBlock(styles, tb);
1188
+ const fontName = getFontNameForText(r);
1189
+ doc.font(fontName);
1190
+ doc.fontSize(r.fontSize ?? 10);
1191
+ return doc.heightOfString(r.text ?? "", { width });
1192
+ };
1193
+ let totalHeight = mt;
1194
+ if (orientation === "vertical") {
1195
+ const keyValueGap = b.verticalKeyValueGap ?? 2;
1196
+ let maxColHeight = 0;
1197
+ for (let colIndex = 0; colIndex < colCount; colIndex++) {
1198
+ const col = cols[colIndex] ?? [];
1199
+ const colWidth = colWidths[colIndex];
1200
+ let colHeight = 0;
1201
+ for (const item of col) {
1202
+ const keyH = measureKVText(
1203
+ { type: "text", text: item.key ?? "" },
1204
+ colWidth
1205
+ );
1206
+ const valH = measureKVText(
1207
+ { type: "text", text: item.value ?? "" },
1208
+ colWidth
1209
+ );
1210
+ colHeight += keyH + keyValueGap + valH + rowGap;
1211
+ }
1212
+ if (colHeight > maxColHeight) maxColHeight = colHeight;
1213
+ }
1214
+ totalHeight += maxColHeight;
1215
+ } else {
1216
+ const maxRows = Math.max(...cols.map((c) => c ? c.length : 0), 0);
1217
+ for (let rowIndex = 0; rowIndex < maxRows; rowIndex++) {
1218
+ let rowHeight = 0;
1219
+ for (let colIndex = 0; colIndex < colCount; colIndex++) {
1220
+ const item = cols[colIndex]?.[rowIndex];
1221
+ if (!item) continue;
1222
+ const colWidth = colWidths[colIndex];
1223
+ const keyWidthPx = b.keyWidth === "*" ? Math.max(colWidth * baseKeyWidthRatio, 20) : b.keyWidth ?? 80;
1224
+ const keyH = measureKVText(
1225
+ { type: "text", text: item.key ?? "" },
1226
+ keyWidthPx
1227
+ );
1228
+ const valueXInsideCol = keyWidthPx + (separatorText ? sepBoxWidth + 4 : 4);
1229
+ const valueWidth = Math.max(colWidth - valueXInsideCol, 1);
1230
+ const valH = measureKVText(
1231
+ { type: "text", text: item.value ?? "" },
1232
+ valueWidth
1233
+ );
1234
+ rowHeight = Math.max(rowHeight, Math.max(keyH, valH));
1235
+ }
1236
+ if (rowHeight > 0) totalHeight += rowHeight + rowGap;
1237
+ }
1238
+ }
1239
+ totalHeight += mb;
1240
+ return totalHeight;
1241
+ };
1242
+ const measure = (block, env) => {
1243
+ if (!block || block.visible === false) return 0;
1244
+ switch (block.type) {
1245
+ case "text":
1246
+ return measureText(block, env);
1247
+ case "image":
1248
+ return measureImage(block);
1249
+ case "qr":
1250
+ return measureQr(block);
1251
+ case "barcode":
1252
+ return measureBarcode(block);
1253
+ case "line":
1254
+ return measureLine(block);
1255
+ case "table":
1256
+ return measureTable(block, env);
1257
+ case "columns":
1258
+ return measureColumns(block, env);
1259
+ case "keyValueGrid":
1260
+ return measureKeyValueGrid(block, env);
1261
+ case "signature":
1262
+ return block.height ?? 0;
1263
+ case "pageBreak":
1264
+ return 0;
1265
+ default:
1266
+ return 0;
1267
+ }
1268
+ };
1269
+ return (block, env) => measure(block, env);
1270
+ }
1271
+
1272
+ // src/renderer-engine/utils/page-background.ts
1273
+ function drawPageBackground(doc, pageBackground) {
1274
+ if (!pageBackground) return;
1275
+ const { src, opacity = 1 } = pageBackground;
1276
+ const pageWidth = doc.page.width;
1277
+ const pageHeight = doc.page.height;
1278
+ doc.save();
1279
+ try {
1280
+ if (opacity < 1) {
1281
+ doc.opacity(opacity);
1282
+ }
1283
+ doc.image(src, 0, 0, {
1284
+ width: pageWidth,
1285
+ height: pageHeight
1286
+ });
1287
+ } catch (e) {
1288
+ console.warn("Failed to load page background image:", e);
1289
+ }
1290
+ doc.restore();
1291
+ doc.opacity(1);
1292
+ }
1293
+
1294
+ // src/renderer-engine/utils/page-limit.ts
1295
+ function createBottomLimitForContent(doc, ctx) {
1296
+ return () => {
1297
+ const pageBottomForContent = doc.page.height - doc.page.margins.bottom;
1298
+ return ctx.signatureTopY ?? pageBottomForContent;
1299
+ };
1300
+ }
1301
+
1302
+ // src/renderer-engine/utils/qr-bar-code.ts
1303
+ import bwipjs from "bwip-js";
1304
+ import QRCode from "qrcode";
1305
+ function generateQrBuffer(value, size, version, errorCorrectionLevel) {
1306
+ const options = {
1307
+ width: size,
1308
+ margin: 0
1309
+ };
1310
+ if (version && version >= 1 && version <= 40) {
1311
+ options.version = version;
1312
+ }
1313
+ options.errorCorrectionLevel = errorCorrectionLevel ?? "M";
1314
+ return new Promise((resolve, reject) => {
1315
+ QRCode.toBuffer(value, options, (err, buffer) => {
1316
+ if (err) {
1317
+ return reject(err);
1318
+ }
1319
+ resolve(buffer);
1320
+ });
1321
+ });
1322
+ }
1323
+ function mapBarcodeTypeToBcid(bcType) {
1324
+ switch (bcType) {
1325
+ case "EAN13":
1326
+ return "ean13";
1327
+ case "CODE39":
1328
+ return "code39";
1329
+ case "ITF":
1330
+ return "interleaved2of5";
1331
+ case "CODE93":
1332
+ return "code93";
1333
+ case "CODE128":
1334
+ default:
1335
+ return "code128";
1336
+ }
1337
+ }
1338
+ function generateBarcodeBuffer(value, options = {}) {
1339
+ const {
1340
+ bcType,
1341
+ scale = 3,
1342
+ barHeight = 10,
1343
+ includetext = false,
1344
+ textalign = "center"
1345
+ } = options;
1346
+ const bcid = mapBarcodeTypeToBcid(bcType);
1347
+ return new Promise((resolve, reject) => {
1348
+ let textValue = value;
1349
+ if (bcType === "EAN13") {
1350
+ const digitsOnly = textValue.replace(/\D/g, "");
1351
+ if (digitsOnly.length === 13) {
1352
+ textValue = digitsOnly.slice(0, 12);
1353
+ } else if (digitsOnly.length === 12) {
1354
+ textValue = textValue;
1355
+ } else {
1356
+ return reject(
1357
+ new Error(
1358
+ `EAN13 barcode value must have 12 or 13 digits, got "${value}"`
1359
+ )
1360
+ );
1361
+ }
1362
+ }
1363
+ const bwipOptions = {
1364
+ bcid,
1365
+ text: textValue,
1366
+ scale,
1367
+ height: barHeight,
1368
+ includetext
1369
+ };
1370
+ bwipOptions.textxalign = textalign;
1371
+ bwipjs.toBuffer(bwipOptions, (err, png) => {
1372
+ if (err) {
1373
+ return reject(err);
1374
+ }
1375
+ resolve(png);
1376
+ });
1377
+ });
1378
+ }
1379
+ async function materializeQrAndBarcodesInBlocks(blocks) {
1380
+ const out = [];
1381
+ for (const block of blocks) {
1382
+ if (block.type === "columns") {
1383
+ const colBlock = block;
1384
+ const newCols = [];
1385
+ for (const col of colBlock.columns) {
1386
+ newCols.push(await materializeQrAndBarcodesInBlocks(col));
1387
+ }
1388
+ out.push({
1389
+ ...colBlock,
1390
+ columns: newCols
1391
+ });
1392
+ } else if (block.type === "signature") {
1393
+ const sig = block;
1394
+ if (sig.blocks && sig.blocks.length) {
1395
+ const newInner = await materializeQrAndBarcodesInBlocks(sig.blocks);
1396
+ out.push({
1397
+ ...sig,
1398
+ blocks: newInner
1399
+ });
1400
+ } else {
1401
+ out.push(block);
1402
+ }
1403
+ } else if (block.type === "qr") {
1404
+ const qb = { ...block };
1405
+ if (!qb.src && qb.value) {
1406
+ const size = qb.size ?? 80;
1407
+ const buf = await generateQrBuffer(
1408
+ qb.value,
1409
+ size,
1410
+ qb.qrVersion,
1411
+ qb.errorCorrectionLevel
1412
+ );
1413
+ qb.src = buf;
1414
+ }
1415
+ out.push(qb);
1416
+ } else if (block.type === "barcode") {
1417
+ const bb = { ...block };
1418
+ if (!bb.src && bb.value) {
1419
+ const buf = await generateBarcodeBuffer(bb.value, {
1420
+ bcType: bb.bcType,
1421
+ scale: bb.scale,
1422
+ barHeight: bb.barHeight,
1423
+ includetext: bb.includetext,
1424
+ textalign: bb.textalign
1425
+ });
1426
+ bb.src = buf;
1427
+ }
1428
+ out.push(bb);
1429
+ } else {
1430
+ out.push(block);
1431
+ }
1432
+ }
1433
+ return out;
1434
+ }
1435
+
1436
+ // src/renderer-engine/utils/signature.ts
1437
+ function drawSignatureBlock(doc, block, y, env, renderBlockArray) {
1438
+ const localLeft = block.marginLeft ?? 0;
1439
+ const localRight = block.marginRight ?? 0;
1440
+ const bandLeft = env.marginLeft + localLeft;
1441
+ const bandWidth = Math.max(env.innerWidth - localLeft - localRight, 1);
1442
+ const bandHeight = block.height;
1443
+ doc.save();
1444
+ doc.rect(bandLeft, y, bandWidth, bandHeight).clip();
1445
+ if (block.blocks && block.blocks.length) {
1446
+ const sigEnv = {
1447
+ marginLeft: bandLeft,
1448
+ innerWidth: bandWidth,
1449
+ allowPageBreak: false
1450
+ };
1451
+ renderBlockArray(block.blocks, y, sigEnv);
1452
+ } else {
1453
+ const imgWidth = block.width ?? 120;
1454
+ const xCenter = bandLeft + (bandWidth - imgWidth) / 2;
1455
+ if (block.image) {
1456
+ try {
1457
+ doc.image(block.image, xCenter, y, { width: imgWidth });
1458
+ } catch (e) {
1459
+ console.warn("Failed to load signature image:", e);
1460
+ }
1461
+ }
1462
+ const label = block.label ?? "Authorized Signatory";
1463
+ const labelY = y + bandHeight - 14;
1464
+ doc.fontSize(9).text(label, bandLeft, labelY, {
1465
+ width: bandWidth,
1466
+ align: "center"
1467
+ });
1468
+ }
1469
+ doc.restore();
1470
+ }
1471
+ function createProcessSignatureBlock({
1472
+ doc,
1473
+ ctx,
1474
+ styles,
1475
+ finishPage: finishPage2,
1476
+ contentEnvFn
1477
+ }) {
1478
+ return (block) => {
1479
+ if (ctx.signatureBlock) {
1480
+ finishPage2(true);
1481
+ }
1482
+ const env = contentEnvFn(doc);
1483
+ const localLeft = block.marginLeft ?? 0;
1484
+ const localRight = block.marginRight ?? 0;
1485
+ const bandWidth = Math.max(env.innerWidth - localLeft - localRight, 1);
1486
+ let requiredHeight = block.height;
1487
+ if (block.blocks && block.blocks.length) {
1488
+ let required = 0;
1489
+ const measureImageLike = (mt, mb, h) => {
1490
+ return (mt ?? 0) + h + (mb ?? 0);
1491
+ };
1492
+ const measureColumns = (c) => {
1493
+ const blockLeft = c.marginLeft ?? 0;
1494
+ const blockRight = c.marginRight ?? 0;
1495
+ const totalWidth = Math.max(bandWidth - blockLeft - blockRight, 1);
1496
+ const cols = c.columns;
1497
+ const n = cols.length;
1498
+ if (!n) return (c.marginTop ?? 0) + (c.marginBottom ?? 0);
1499
+ let colWidths;
1500
+ let gap;
1501
+ const mode = c.mode ?? "fixedGap";
1502
+ if (mode === "spaceBetween" && n > 1) {
1503
+ if (c.widths && c.widths.length === n) {
1504
+ colWidths = computeColumnPixelWidths(c.widths, totalWidth);
1505
+ } else {
1506
+ const equal = totalWidth / n;
1507
+ colWidths = Array(n).fill(equal);
1508
+ }
1509
+ const totalColsWidth = colWidths.reduce((a, b) => a + b, 0);
1510
+ const remaining = Math.max(totalWidth - totalColsWidth, 0);
1511
+ gap = remaining / (n - 1);
1512
+ } else {
1513
+ gap = c.gap ?? 20;
1514
+ const totalGapsWidth = gap * (n - 1);
1515
+ const widthForCols = Math.max(totalWidth - totalGapsWidth, 1);
1516
+ if (c.widths && c.widths.length === n) {
1517
+ colWidths = computeColumnPixelWidths(c.widths, widthForCols);
1518
+ } else {
1519
+ const equal = widthForCols / n;
1520
+ colWidths = Array(n).fill(equal);
1521
+ }
1522
+ }
1523
+ const mt = c.marginTop ?? 0;
1524
+ const mb = c.marginBottom ?? 0;
1525
+ const colHeights = [];
1526
+ cols.forEach((colBlocks, idx) => {
1527
+ const colEnv = {
1528
+ marginLeft: 0,
1529
+ innerWidth: colWidths[idx],
1530
+ allowPageBreak: false
1531
+ };
1532
+ let colY = 0;
1533
+ for (const b of colBlocks) {
1534
+ colY += measureInnerBlock(b, colEnv);
1535
+ }
1536
+ colHeights.push(colY);
1537
+ });
1538
+ const maxColHeight = Math.max(...colHeights, 0);
1539
+ return mt + maxColHeight + mb;
1540
+ };
1541
+ const innerEnv = {
1542
+ marginLeft: 0,
1543
+ innerWidth: bandWidth,
1544
+ allowPageBreak: false
1545
+ };
1546
+ const measureInnerBlock = (b, envForColumns) => {
1547
+ switch (b.type) {
1548
+ case "text": {
1549
+ const tb = resolveTextBlock(styles, b);
1550
+ const localLeft2 = tb.marginLeft ?? 0;
1551
+ const localRight2 = tb.marginRight ?? 0;
1552
+ const width = Math.max(
1553
+ envForColumns.innerWidth - localLeft2 - localRight2,
1554
+ 1
1555
+ );
1556
+ doc.fontSize(tb.fontSize ?? 10);
1557
+ const isBold = !!tb.bold;
1558
+ const isItalic = !!tb.italic;
1559
+ let fontName = tb.font;
1560
+ if (!fontName) {
1561
+ if (isBold && isItalic) fontName = "Helvetica-BoldOblique";
1562
+ else if (isBold) fontName = "Helvetica-Bold";
1563
+ else if (isItalic) fontName = "Helvetica-Oblique";
1564
+ else fontName = "Helvetica";
1565
+ }
1566
+ doc.font(fontName);
1567
+ const h = doc.heightOfString(tb.text, { width });
1568
+ const gap = tb.lineGap ?? 4;
1569
+ return h + gap;
1570
+ }
1571
+ case "image": {
1572
+ const ib = b;
1573
+ const h = ib.height ?? 50;
1574
+ return measureImageLike(ib.marginTop, ib.marginBottom, h);
1575
+ }
1576
+ case "qr": {
1577
+ const qb = b;
1578
+ const size = qb.size ?? 80;
1579
+ return measureImageLike(qb.marginTop, qb.marginBottom, size);
1580
+ }
1581
+ case "barcode": {
1582
+ const bb = b;
1583
+ const h = bb.height ?? 40;
1584
+ return measureImageLike(bb.marginTop, bb.marginBottom, h);
1585
+ }
1586
+ case "line": {
1587
+ const lb = b;
1588
+ const lw = lb.lineWidth ?? 1;
1589
+ const mt = lb.marginTop ?? 0;
1590
+ const mb = lb.marginBottom ?? 0;
1591
+ return mt + lw + mb;
1592
+ }
1593
+ case "table": {
1594
+ const t = b;
1595
+ const fakeEnv = {
1596
+ marginLeft: t.marginLeft ?? 0,
1597
+ innerWidth: bandWidth - (t.marginLeft ?? 0) - (t.marginRight ?? 0),
1598
+ allowPageBreak: false
1599
+ };
1600
+ return measureTableHeight(
1601
+ doc,
1602
+ t,
1603
+ fakeEnv,
1604
+ styles,
1605
+ computeColumnPixelWidths
1606
+ );
1607
+ }
1608
+ case "columns":
1609
+ return measureColumns(b);
1610
+ case "signature":
1611
+ return b.height;
1612
+ default:
1613
+ return 0;
1614
+ }
1615
+ };
1616
+ for (const inner of block.blocks) {
1617
+ required += measureInnerBlock(inner, innerEnv);
1618
+ }
1619
+ requiredHeight = Math.max(requiredHeight, required);
1620
+ }
1621
+ const sigHeight = requiredHeight;
1622
+ const pageHeight = doc.page.height;
1623
+ const candidateTop = pageHeight - doc.page.margins.bottom - sigHeight;
1624
+ if (ctx.currentY > candidateTop) {
1625
+ finishPage2(true);
1626
+ }
1627
+ const newPageHeight = doc.page.height;
1628
+ const newCandidateTop = newPageHeight - doc.page.margins.bottom - sigHeight;
1629
+ ctx.signatureBlock = { ...block, height: sigHeight };
1630
+ ctx.signatureTopY = newCandidateTop;
1631
+ ctx.signatureHeight = sigHeight;
1632
+ ctx.signaturePlaced = true;
1633
+ ctx.afterSignature = true;
1634
+ };
1635
+ }
1636
+
1637
+ // src/renderer-engine/utils/start-page-layout.ts
1638
+ function createStartNewPageLayout({
1639
+ doc,
1640
+ def,
1641
+ ctx,
1642
+ headerBandHeight,
1643
+ renderBlockArray
1644
+ }) {
1645
+ return () => {
1646
+ drawPageBackground(doc, def.pageBackground);
1647
+ drawHeader(
1648
+ doc,
1649
+ def,
1650
+ headerBandHeight,
1651
+ def.header ?? void 0,
1652
+ renderBlockArray
1653
+ );
1654
+ const mode = def.watermark?.mode;
1655
+ if (def.watermark && !watermarkUsesLast(mode)) {
1656
+ drawWatermarkForPage(doc, def.watermark, ctx.pageNumber, false);
1657
+ }
1658
+ ctx.currentY = doc.page.margins.top;
1659
+ ctx.signatureBlock = null;
1660
+ ctx.signatureTopY = null;
1661
+ ctx.signatureHeight = 0;
1662
+ ctx.signaturePlaced = false;
1663
+ ctx.afterSignature = false;
1664
+ };
1665
+ }
1666
+
1667
+ // src/renderer-engine/utils/watermark.ts
1668
+ function watermarkUsesLast(mode) {
1669
+ return mode === "last" || mode === "firstLast" || mode === "exceptLast" || mode === "exceptFirstLast";
1670
+ }
1671
+ function drawWatermarkForPage(doc, watermark, pageNumber, isLast) {
1672
+ const {
1673
+ text,
1674
+ opacity = 0.06,
1675
+ fontSize = 60,
1676
+ color = "gray",
1677
+ angle = 45,
1678
+ mode = "all"
1679
+ } = watermark;
1680
+ const shouldDraw = (() => {
1681
+ switch (mode) {
1682
+ case "all":
1683
+ return true;
1684
+ case "first":
1685
+ return pageNumber === 1;
1686
+ case "last":
1687
+ return isLast;
1688
+ case "firstLast":
1689
+ return pageNumber === 1 || isLast;
1690
+ case "exceptFirstLast":
1691
+ return pageNumber !== 1 && !isLast;
1692
+ case "exceptFirst":
1693
+ return pageNumber !== 1;
1694
+ case "exceptLast":
1695
+ return !isLast;
1696
+ default:
1697
+ return true;
1698
+ }
1699
+ })();
1700
+ if (!shouldDraw) return;
1701
+ const centerX = doc.page.width / 2;
1702
+ const centerY = doc.page.height / 2;
1703
+ doc.save();
1704
+ doc.fillColor(color);
1705
+ doc.opacity(opacity);
1706
+ doc.fontSize(fontSize);
1707
+ const textWidth = doc.widthOfString(text);
1708
+ const textHeight = doc.currentLineHeight();
1709
+ const x = centerX - textWidth / 2;
1710
+ const y = centerY - textHeight / 2;
1711
+ doc.rotate(angle, { origin: [centerX, centerY] });
1712
+ doc.text(text, x, y);
1713
+ doc.restore();
1714
+ doc.opacity(1);
1715
+ }
1716
+
1717
+ // src/renderer-engine/index.ts
1718
+ async function renderCustomPdf(def, outputPath) {
1719
+ if (def.header) {
1720
+ def.header.blocks = await materializeQrAndBarcodesInBlocks(
1721
+ def.header.blocks
1722
+ );
1723
+ def.header.blocks = await materializeImagesInBlocks(def.header.blocks);
1724
+ }
1725
+ def.content = await materializeQrAndBarcodesInBlocks(def.content);
1726
+ def.content = await materializeImagesInBlocks(def.content);
1727
+ if (def.pageBackground?.src) {
1728
+ def.pageBackground.src = await normalizeImageSrc(def.pageBackground.src);
1729
+ }
1730
+ if (def.header?.backgroundImage) {
1731
+ def.header.backgroundImage = await normalizeImageSrc(
1732
+ def.header.backgroundImage
1733
+ );
1734
+ }
1735
+ if (def.footer && typeof def.footer !== "function") {
1736
+ const footer = def.footer;
1737
+ if (footer.backgroundImage) {
1738
+ footer.backgroundImage = await normalizeImageSrc(footer.backgroundImage);
1739
+ }
1740
+ }
1741
+ return new Promise((resolve, reject) => {
1742
+ const headerBandHeight = def.margins.top ?? 0;
1743
+ const footerBandHeight = def.margins.bottom ?? 0;
1744
+ const doc = new PDFDocument({
1745
+ size: def.pageSize || "A4",
1746
+ margins: {
1747
+ top: headerBandHeight,
1748
+ bottom: footerBandHeight,
1749
+ left: def.margins.left,
1750
+ right: def.margins.right
1751
+ }
1752
+ });
1753
+ if (def.fonts && def.fonts.length) {
1754
+ for (const f of def.fonts) {
1755
+ try {
1756
+ doc.registerFont(f.name, f.src);
1757
+ } catch (e) {
1758
+ console.warn("Failed to register font:", f.name, e);
1759
+ }
1760
+ }
1761
+ }
1762
+ const stream = fs.createWriteStream(outputPath);
1763
+ doc.pipe(stream);
1764
+ const ctx = createInitialContext(doc);
1765
+ const styles = def.styles ?? {};
1766
+ const finishPage2 = (addNewPage) => finishPage({
1767
+ addNewPage,
1768
+ doc,
1769
+ def,
1770
+ ctx,
1771
+ footerBandHeight,
1772
+ renderBlockArray,
1773
+ startNewPageLayout
1774
+ });
1775
+ const processSignatureBlock = createProcessSignatureBlock({
1776
+ doc,
1777
+ ctx,
1778
+ styles,
1779
+ finishPage: finishPage2,
1780
+ contentEnvFn: contentEnv
1781
+ });
1782
+ const { renderBlock, renderBlockArray } = createBlockRenderer({
1783
+ doc,
1784
+ ctx,
1785
+ styles,
1786
+ computeColumnPixelWidths,
1787
+ finishPage: finishPage2,
1788
+ processSignatureBlock
1789
+ });
1790
+ const startNewPageLayout = createStartNewPageLayout({
1791
+ doc,
1792
+ def,
1793
+ ctx,
1794
+ headerBandHeight,
1795
+ renderBlockArray
1796
+ });
1797
+ startNewPageLayout();
1798
+ for (const block of def.content) {
1799
+ if (block.type === "signature") {
1800
+ processSignatureBlock(block);
1801
+ continue;
1802
+ }
1803
+ if (ctx.afterSignature) {
1804
+ finishPage2(true);
1805
+ ctx.afterSignature = false;
1806
+ }
1807
+ renderBlock(block, null, contentEnv(doc));
1808
+ }
1809
+ finishPage2(false);
1810
+ doc.end();
1811
+ stream.on("finish", () => resolve());
1812
+ stream.on("error", (err) => reject(err));
1813
+ });
1814
+ }
1815
+ async function renderCustomPdfToBuffer(def) {
1816
+ if (def.header) {
1817
+ def.header.blocks = await materializeQrAndBarcodesInBlocks(
1818
+ def.header.blocks
1819
+ );
1820
+ def.header.blocks = await materializeImagesInBlocks(def.header.blocks);
1821
+ }
1822
+ def.content = await materializeQrAndBarcodesInBlocks(def.content);
1823
+ def.content = await materializeImagesInBlocks(def.content);
1824
+ if (def.pageBackground?.src) {
1825
+ def.pageBackground.src = await normalizeImageSrc(def.pageBackground.src);
1826
+ }
1827
+ if (def.header?.backgroundImage) {
1828
+ def.header.backgroundImage = await normalizeImageSrc(
1829
+ def.header.backgroundImage
1830
+ );
1831
+ }
1832
+ if (def.footer && typeof def.footer !== "function") {
1833
+ const footer = def.footer;
1834
+ if (footer.backgroundImage) {
1835
+ footer.backgroundImage = await normalizeImageSrc(footer.backgroundImage);
1836
+ }
1837
+ }
1838
+ return new Promise((resolve, reject) => {
1839
+ const headerBandHeight = def.margins.top ?? 0;
1840
+ const footerBandHeight = def.margins.bottom ?? 0;
1841
+ const doc = new PDFDocument({
1842
+ size: def.pageSize || "A4",
1843
+ margins: {
1844
+ top: headerBandHeight,
1845
+ bottom: footerBandHeight,
1846
+ left: def.margins.left,
1847
+ right: def.margins.right
1848
+ }
1849
+ });
1850
+ const chunks = [];
1851
+ doc.on("data", (chunk) => {
1852
+ chunks.push(chunk);
1853
+ });
1854
+ doc.on("end", () => {
1855
+ resolve(Buffer.concat(chunks));
1856
+ });
1857
+ doc.on("error", (err) => {
1858
+ reject(err);
1859
+ });
1860
+ if (def.fonts && def.fonts.length) {
1861
+ for (const f of def.fonts) {
1862
+ try {
1863
+ doc.registerFont(f.name, f.src);
1864
+ } catch (e) {
1865
+ console.warn("Failed to register font:", f.name, e);
1866
+ }
1867
+ }
1868
+ }
1869
+ const ctx = createInitialContext(doc);
1870
+ const styles = def.styles ?? {};
1871
+ const finishPage2 = (addNewPage) => finishPage({
1872
+ addNewPage,
1873
+ doc,
1874
+ def,
1875
+ ctx,
1876
+ footerBandHeight,
1877
+ renderBlockArray,
1878
+ startNewPageLayout
1879
+ });
1880
+ const processSignatureBlock = createProcessSignatureBlock({
1881
+ doc,
1882
+ ctx,
1883
+ styles,
1884
+ finishPage: finishPage2,
1885
+ contentEnvFn: contentEnv
1886
+ });
1887
+ const { renderBlock, renderBlockArray } = createBlockRenderer({
1888
+ doc,
1889
+ ctx,
1890
+ styles,
1891
+ computeColumnPixelWidths,
1892
+ finishPage: finishPage2,
1893
+ processSignatureBlock
1894
+ });
1895
+ const startNewPageLayout = createStartNewPageLayout({
1896
+ doc,
1897
+ def,
1898
+ ctx,
1899
+ headerBandHeight,
1900
+ renderBlockArray
1901
+ });
1902
+ startNewPageLayout();
1903
+ for (const block of def.content) {
1904
+ if (block.type === "signature") {
1905
+ processSignatureBlock(block);
1906
+ continue;
1907
+ }
1908
+ if (ctx.afterSignature) {
1909
+ finishPage2(true);
1910
+ ctx.afterSignature = false;
1911
+ }
1912
+ renderBlock(block, null, contentEnv(doc));
1913
+ }
1914
+ finishPage2(false);
1915
+ doc.end();
1916
+ });
1917
+ }
1918
+
1919
+ // src/types/barcode.ts
1920
+ var BARCODE_TYPES = {
1921
+ CODE128: "CODE128",
1922
+ EAN13: "EAN13",
1923
+ CODE39: "CODE39",
1924
+ ITF: "ITF",
1925
+ CODE93: "CODE93"
1926
+ };
1927
+
1928
+ // src/types/qr.ts
1929
+ var QR_ERROR_LEVEL = {
1930
+ LOW: "L",
1931
+ MEDIUM: "M",
1932
+ QUARTILE: "Q",
1933
+ HIGH: "H"
1934
+ };
1935
+ export {
1936
+ BARCODE_TYPES,
1937
+ QR_ERROR_LEVEL,
1938
+ renderCustomPdf,
1939
+ renderCustomPdfToBuffer
1940
+ };