av6-pdf-engine 1.2.0 → 3.0.0
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.
- package/README.md +686 -88
- package/dist/index.d.mts +277 -23
- package/dist/index.d.ts +277 -23
- package/dist/index.js +1562 -801
- package/dist/index.mjs +1527 -800
- package/package.json +6 -1
- package/.prettierignore +0 -4
- package/.prettierrc +0 -6
package/dist/index.mjs
CHANGED
|
@@ -30,6 +30,11 @@ function toPdfEngineError(cause, fallback) {
|
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// src/images/index.ts
|
|
34
|
+
var images = {
|
|
35
|
+
default: "data:image/png;base64,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"
|
|
36
|
+
};
|
|
37
|
+
|
|
33
38
|
// src/renderer-engine/index.ts
|
|
34
39
|
import fs from "fs";
|
|
35
40
|
import PDFDocument from "pdfkit";
|
|
@@ -208,7 +213,7 @@ var drawRotatedImage = (doc, src, x, y, width, height, rotationDeg) => {
|
|
|
208
213
|
doc.image(src, x, y, { width, height });
|
|
209
214
|
doc.restore();
|
|
210
215
|
};
|
|
211
|
-
var processImageBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
|
|
216
|
+
var processImageBlock = (doc, ctx, block, y, env, ensureSpaceFor, defaultImage) => {
|
|
212
217
|
const imgSource = block.src;
|
|
213
218
|
const imgWidth = block.width ?? 80;
|
|
214
219
|
const imgHeight = block.height ?? 50;
|
|
@@ -218,12 +223,11 @@ var processImageBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
|
|
|
218
223
|
const availableWidth = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
219
224
|
const mt = block.marginTop ?? 0;
|
|
220
225
|
const mb = block.marginBottom ?? 0;
|
|
221
|
-
const baseY = y ?? ctx.currentY;
|
|
222
|
-
const startY = baseY + mt;
|
|
223
226
|
const heightNeeded = mt + imgHeight + mb;
|
|
224
227
|
if (y === null) {
|
|
225
228
|
ensureSpaceFor(heightNeeded, env);
|
|
226
229
|
}
|
|
230
|
+
const startY = (y ?? ctx.currentY) + mt;
|
|
227
231
|
let x = baseLeft;
|
|
228
232
|
if (block.align === "center") {
|
|
229
233
|
x = baseLeft + (availableWidth - imgWidth) / 2;
|
|
@@ -236,7 +240,18 @@ var processImageBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
|
|
|
236
240
|
height: imgHeight
|
|
237
241
|
});
|
|
238
242
|
} catch (e) {
|
|
239
|
-
|
|
243
|
+
if (defaultImage !== void 0) {
|
|
244
|
+
try {
|
|
245
|
+
doc.image(defaultImage, x, startY, {
|
|
246
|
+
width: imgWidth,
|
|
247
|
+
height: imgHeight
|
|
248
|
+
});
|
|
249
|
+
} catch {
|
|
250
|
+
console.warn("Failed to render default image fallback:", e);
|
|
251
|
+
}
|
|
252
|
+
} else {
|
|
253
|
+
console.warn("Failed to load image:", e);
|
|
254
|
+
}
|
|
240
255
|
}
|
|
241
256
|
const newY = startY + imgHeight + mb;
|
|
242
257
|
if (y === null) ctx.currentY = newY;
|
|
@@ -288,7 +303,7 @@ var processKeyValueGridBlock = (doc, ctx, styles, block, y, env, computeColumnPi
|
|
|
288
303
|
...base,
|
|
289
304
|
style: styleNames
|
|
290
305
|
});
|
|
291
|
-
const
|
|
306
|
+
const measureTextHeight2 = (tb, width) => {
|
|
292
307
|
const fontName = getFontNameForText(tb);
|
|
293
308
|
doc.font(fontName);
|
|
294
309
|
doc.fontSize(tb.fontSize ?? 10);
|
|
@@ -321,10 +336,10 @@ var processKeyValueGridBlock = (doc, ctx, styles, block, y, env, computeColumnPi
|
|
|
321
336
|
const valueStyleNames = item.valueStyle ?? block.valueStyle ?? void 0;
|
|
322
337
|
const keyBase = buildKeyBlock(item);
|
|
323
338
|
const keyResolved = resolveStyled(keyBase, keyStyleNames);
|
|
324
|
-
const keyHeight =
|
|
339
|
+
const keyHeight = measureTextHeight2(keyResolved, colWidth);
|
|
325
340
|
const valueBase = buildValueBlock(item);
|
|
326
341
|
const valueResolved = resolveStyled(valueBase, valueStyleNames);
|
|
327
|
-
const valueHeight =
|
|
342
|
+
const valueHeight = measureTextHeight2(valueResolved, colWidth);
|
|
328
343
|
const rowHeight = keyHeight + keyValueGap + valueHeight;
|
|
329
344
|
colHeight += rowHeight + rowGap;
|
|
330
345
|
}
|
|
@@ -344,12 +359,12 @@ var processKeyValueGridBlock = (doc, ctx, styles, block, y, env, computeColumnPi
|
|
|
344
359
|
const valueStyleNames = item.valueStyle ?? block.valueStyle ?? void 0;
|
|
345
360
|
const keyBase = buildKeyBlock(item);
|
|
346
361
|
const keyResolved = resolveStyled(keyBase, keyStyleNames);
|
|
347
|
-
const keyHeight =
|
|
362
|
+
const keyHeight = measureTextHeight2(keyResolved, keyWidthPx);
|
|
348
363
|
const valueBase = buildValueBlock(item);
|
|
349
364
|
const valueResolved = resolveStyled(valueBase, valueStyleNames);
|
|
350
365
|
const valueXInsideCol = keyWidthPx + (separatorText ? sepBoxWidth + 4 : 4);
|
|
351
366
|
const valueWidth = Math.max(colWidth - valueXInsideCol, 1);
|
|
352
|
-
const valueHeight =
|
|
367
|
+
const valueHeight = measureTextHeight2(valueResolved, valueWidth);
|
|
353
368
|
const cellHeight = Math.max(keyHeight, valueHeight);
|
|
354
369
|
if (cellHeight > rowHeight) rowHeight = cellHeight;
|
|
355
370
|
}
|
|
@@ -377,12 +392,12 @@ var processKeyValueGridBlock = (doc, ctx, styles, block, y, env, computeColumnPi
|
|
|
377
392
|
const valueStyleNames = item.valueStyle ?? block.valueStyle ?? void 0;
|
|
378
393
|
const keyBlock = buildKeyBlock(item);
|
|
379
394
|
const keyResolved = resolveStyled(keyBlock, keyStyleNames);
|
|
380
|
-
const keyHeight =
|
|
395
|
+
const keyHeight = measureTextHeight2(keyResolved, colWidth);
|
|
381
396
|
drawStyledText(doc, keyResolved, colX, colY, colWidth);
|
|
382
397
|
colY += keyHeight + keyValueGap;
|
|
383
398
|
const valueBlock = buildValueBlock(item);
|
|
384
399
|
const valueResolved = resolveStyled(valueBlock, valueStyleNames);
|
|
385
|
-
const valueHeight =
|
|
400
|
+
const valueHeight = measureTextHeight2(valueResolved, colWidth);
|
|
386
401
|
drawStyledText(doc, valueResolved, colX, colY, colWidth);
|
|
387
402
|
colY += valueHeight + rowGap;
|
|
388
403
|
}
|
|
@@ -402,12 +417,12 @@ var processKeyValueGridBlock = (doc, ctx, styles, block, y, env, computeColumnPi
|
|
|
402
417
|
const valueStyleNames = item.valueStyle ?? block.valueStyle ?? void 0;
|
|
403
418
|
const keyBase = buildKeyBlock(item);
|
|
404
419
|
const keyResolved = resolveStyled(keyBase, keyStyleNames);
|
|
405
|
-
const keyHeight =
|
|
420
|
+
const keyHeight = measureTextHeight2(keyResolved, keyWidthPx);
|
|
406
421
|
const valueBase = buildValueBlock(item);
|
|
407
422
|
const valueResolved = resolveStyled(valueBase, valueStyleNames);
|
|
408
423
|
const valueXInsideCol = keyWidthPx + (separatorText ? sepBoxWidth + 4 : 4);
|
|
409
424
|
const valueWidth = Math.max(colWidth - valueXInsideCol, 1);
|
|
410
|
-
const valueHeight =
|
|
425
|
+
const valueHeight = measureTextHeight2(valueResolved, valueWidth);
|
|
411
426
|
const cellHeight = Math.max(keyHeight, valueHeight);
|
|
412
427
|
if (cellHeight > rowHeight) rowHeight = cellHeight;
|
|
413
428
|
}
|
|
@@ -448,6 +463,149 @@ var processKeyValueGridBlock = (doc, ctx, styles, block, y, env, computeColumnPi
|
|
|
448
463
|
return endY;
|
|
449
464
|
};
|
|
450
465
|
|
|
466
|
+
// src/renderer-engine/blocks/list.ts
|
|
467
|
+
var DEFAULT_BULLET = "\u2022";
|
|
468
|
+
var DEFAULT_MARKER_GAP = 6;
|
|
469
|
+
var DEFAULT_ITEM_GAP = 4;
|
|
470
|
+
var MARKER_PADDING = 2;
|
|
471
|
+
var normalizeListItem = (item) => typeof item === "string" ? { text: item } : item;
|
|
472
|
+
var buildTextBlock = (list, item) => ({
|
|
473
|
+
type: "text",
|
|
474
|
+
text: item.text ?? "",
|
|
475
|
+
fontSize: item.fontSize ?? list.fontSize,
|
|
476
|
+
lineGap: item.lineGap ?? list.lineGap,
|
|
477
|
+
color: item.color ?? list.color,
|
|
478
|
+
bold: item.bold,
|
|
479
|
+
italic: item.italic,
|
|
480
|
+
underline: item.underline,
|
|
481
|
+
strike: item.strike,
|
|
482
|
+
link: item.link,
|
|
483
|
+
font: item.font,
|
|
484
|
+
style: item.style ?? list.style
|
|
485
|
+
});
|
|
486
|
+
var getMarker = (list, index) => {
|
|
487
|
+
if ((list.listStyle ?? "bullet") === "number") {
|
|
488
|
+
const start = list.start ?? 1;
|
|
489
|
+
return `${start + index}.`;
|
|
490
|
+
}
|
|
491
|
+
return list.bullet ?? DEFAULT_BULLET;
|
|
492
|
+
};
|
|
493
|
+
var applyTextOptions = (doc, tb) => {
|
|
494
|
+
doc.fontSize(tb.fontSize ?? 10);
|
|
495
|
+
doc.font(getFontNameForText(tb));
|
|
496
|
+
if (tb.color) doc.fillColor(tb.color);
|
|
497
|
+
else doc.fillColor("black");
|
|
498
|
+
};
|
|
499
|
+
var measureTextHeight = (doc, tb, width) => {
|
|
500
|
+
applyTextOptions(doc, tb);
|
|
501
|
+
const lineGap = tb.lineGap ?? 4;
|
|
502
|
+
return doc.heightOfString(tb.text ?? "", { width, lineGap });
|
|
503
|
+
};
|
|
504
|
+
var measureMarkerWidth = (doc, marker, tb) => {
|
|
505
|
+
applyTextOptions(doc, tb);
|
|
506
|
+
return doc.widthOfString(marker) + MARKER_PADDING;
|
|
507
|
+
};
|
|
508
|
+
var computeMarkerWidth = (doc, list, items, tbSample) => {
|
|
509
|
+
if (list.markerWidth !== void 0) return list.markerWidth;
|
|
510
|
+
let maxW = 0;
|
|
511
|
+
for (let i = 0; i < items.length; i++) {
|
|
512
|
+
const marker = getMarker(list, i);
|
|
513
|
+
const w = measureMarkerWidth(doc, marker, tbSample);
|
|
514
|
+
if (w > maxW) maxW = w;
|
|
515
|
+
}
|
|
516
|
+
return maxW;
|
|
517
|
+
};
|
|
518
|
+
var measureItemHeight = (doc, list, item, index, markerWidth, textWidth, styles) => {
|
|
519
|
+
const tb = resolveTextBlock(styles, buildTextBlock(list, item));
|
|
520
|
+
const marker = getMarker(list, index);
|
|
521
|
+
const markerTb = { ...tb, text: marker, underline: false, strike: false, link: void 0 };
|
|
522
|
+
const markerH = measureTextHeight(doc, markerTb, markerWidth);
|
|
523
|
+
const textH = measureTextHeight(doc, tb, textWidth);
|
|
524
|
+
const lineGap = tb.lineGap ?? 4;
|
|
525
|
+
return Math.max(markerH, textH) + lineGap;
|
|
526
|
+
};
|
|
527
|
+
var measureListHeight = (doc, styles, block, env) => {
|
|
528
|
+
const items = (block.items ?? []).map(normalizeListItem);
|
|
529
|
+
const mt = block.marginTop ?? 0;
|
|
530
|
+
const mb = block.marginBottom ?? 0;
|
|
531
|
+
const itemGap = block.itemGap ?? DEFAULT_ITEM_GAP;
|
|
532
|
+
const markerGap = block.markerGap ?? DEFAULT_MARKER_GAP;
|
|
533
|
+
if (!items.length) return mt + mb;
|
|
534
|
+
const localLeft = block.marginLeft ?? 0;
|
|
535
|
+
const localRight = block.marginRight ?? 0;
|
|
536
|
+
const innerWidth = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
537
|
+
const firstResolved = resolveTextBlock(styles, buildTextBlock(block, items[0]));
|
|
538
|
+
const markerWidth = computeMarkerWidth(doc, block, items, firstResolved);
|
|
539
|
+
const textWidth = Math.max(innerWidth - markerWidth - markerGap, 1);
|
|
540
|
+
let total = mt;
|
|
541
|
+
for (let i = 0; i < items.length; i++) {
|
|
542
|
+
total += measureItemHeight(doc, block, items[i], i, markerWidth, textWidth, styles);
|
|
543
|
+
if (i < items.length - 1) total += itemGap;
|
|
544
|
+
}
|
|
545
|
+
return total + mb;
|
|
546
|
+
};
|
|
547
|
+
var drawListText = (doc, tb, x, y, width) => {
|
|
548
|
+
applyTextOptions(doc, tb);
|
|
549
|
+
const lineGap = tb.lineGap ?? 4;
|
|
550
|
+
doc.text(tb.text ?? "", x, y, {
|
|
551
|
+
width,
|
|
552
|
+
align: tb.align ?? "left",
|
|
553
|
+
underline: !!tb.underline,
|
|
554
|
+
strike: !!tb.strike,
|
|
555
|
+
link: tb.link,
|
|
556
|
+
lineGap
|
|
557
|
+
});
|
|
558
|
+
};
|
|
559
|
+
var processListBlock = (doc, ctx, styles, block, y, env, ensureSpaceFor) => {
|
|
560
|
+
const items = (block.items ?? []).map(normalizeListItem);
|
|
561
|
+
const mt = block.marginTop ?? 0;
|
|
562
|
+
const mb = block.marginBottom ?? 0;
|
|
563
|
+
const itemGap = block.itemGap ?? DEFAULT_ITEM_GAP;
|
|
564
|
+
const markerGap = block.markerGap ?? DEFAULT_MARKER_GAP;
|
|
565
|
+
const localLeft = block.marginLeft ?? 0;
|
|
566
|
+
const localRight = block.marginRight ?? 0;
|
|
567
|
+
const xLeft = env.marginLeft + localLeft;
|
|
568
|
+
const innerWidth = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
569
|
+
if (!items.length) {
|
|
570
|
+
const endY2 = (y ?? ctx.currentY) + mt + mb;
|
|
571
|
+
if (y === null) ctx.currentY = endY2;
|
|
572
|
+
return endY2;
|
|
573
|
+
}
|
|
574
|
+
const firstResolved = resolveTextBlock(styles, buildTextBlock(block, items[0]));
|
|
575
|
+
const markerWidth = computeMarkerWidth(doc, block, items, firstResolved);
|
|
576
|
+
const textX = xLeft + markerWidth + markerGap;
|
|
577
|
+
const textWidth = Math.max(innerWidth - markerWidth - markerGap, 1);
|
|
578
|
+
if (y !== null) {
|
|
579
|
+
let drawY = y + mt;
|
|
580
|
+
for (let i = 0; i < items.length; i++) {
|
|
581
|
+
if (i > 0) drawY += itemGap;
|
|
582
|
+
const tb = resolveTextBlock(styles, buildTextBlock(block, items[i]));
|
|
583
|
+
const marker = getMarker(block, i);
|
|
584
|
+
const markerTb = { ...tb, text: marker, underline: false, strike: false, link: void 0 };
|
|
585
|
+
const itemHeight = measureItemHeight(doc, block, items[i], i, markerWidth, textWidth, styles);
|
|
586
|
+
drawListText(doc, markerTb, xLeft, drawY, markerWidth);
|
|
587
|
+
drawListText(doc, tb, textX, drawY, textWidth);
|
|
588
|
+
drawY += itemHeight;
|
|
589
|
+
}
|
|
590
|
+
return drawY + mb;
|
|
591
|
+
}
|
|
592
|
+
for (let i = 0; i < items.length; i++) {
|
|
593
|
+
const leading = i === 0 ? mt : itemGap;
|
|
594
|
+
const itemHeight = measureItemHeight(doc, block, items[i], i, markerWidth, textWidth, styles);
|
|
595
|
+
ensureSpaceFor(leading + itemHeight, env);
|
|
596
|
+
const drawY = ctx.currentY + leading;
|
|
597
|
+
const tb = resolveTextBlock(styles, buildTextBlock(block, items[i]));
|
|
598
|
+
const marker = getMarker(block, i);
|
|
599
|
+
const markerTb = { ...tb, text: marker, underline: false, strike: false, link: void 0 };
|
|
600
|
+
drawListText(doc, markerTb, xLeft, drawY, markerWidth);
|
|
601
|
+
drawListText(doc, tb, textX, drawY, textWidth);
|
|
602
|
+
ctx.currentY = drawY + itemHeight;
|
|
603
|
+
}
|
|
604
|
+
const endY = ctx.currentY + mb;
|
|
605
|
+
ctx.currentY = endY;
|
|
606
|
+
return endY;
|
|
607
|
+
};
|
|
608
|
+
|
|
451
609
|
// src/renderer-engine/blocks/line.ts
|
|
452
610
|
var processLineBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
|
|
453
611
|
const localLeft = block.marginLeft ?? 0;
|
|
@@ -456,13 +614,12 @@ var processLineBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
|
|
|
456
614
|
const width = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
457
615
|
const mt = block.marginTop ?? 0;
|
|
458
616
|
const mb = block.marginBottom ?? 0;
|
|
459
|
-
const baseY = y ?? ctx.currentY;
|
|
460
|
-
const startY = baseY + mt;
|
|
461
617
|
const lineWidth = block.lineWidth ?? 1;
|
|
462
618
|
const heightNeeded = mt + lineWidth + mb;
|
|
463
619
|
if (y === null) {
|
|
464
620
|
ensureSpaceFor(heightNeeded, env);
|
|
465
621
|
}
|
|
622
|
+
const startY = (y ?? ctx.currentY) + mt;
|
|
466
623
|
doc.save();
|
|
467
624
|
if (block.color) {
|
|
468
625
|
doc.strokeColor(block.color);
|
|
@@ -475,7 +632,99 @@ var processLineBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
|
|
|
475
632
|
return newY;
|
|
476
633
|
};
|
|
477
634
|
|
|
635
|
+
// src/renderer-engine/blocks/shape.ts
|
|
636
|
+
var processShapeBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
|
|
637
|
+
const mt = block.marginTop ?? 0;
|
|
638
|
+
const mb = block.marginBottom ?? 0;
|
|
639
|
+
const ml = block.marginLeft ?? 0;
|
|
640
|
+
const mr = block.marginRight ?? 0;
|
|
641
|
+
const lineWidth = block.lineWidth ?? 1;
|
|
642
|
+
const width = block.width ?? Math.max(env.innerWidth - ml - mr, 1);
|
|
643
|
+
const height = block.height ?? lineWidth;
|
|
644
|
+
const heightNeeded = mt + height + mb;
|
|
645
|
+
if (y === null) {
|
|
646
|
+
ensureSpaceFor(heightNeeded, env);
|
|
647
|
+
}
|
|
648
|
+
const startY = (y ?? ctx.currentY) + mt;
|
|
649
|
+
const availableWidth = Math.max(env.innerWidth - ml - mr, 1);
|
|
650
|
+
let x = env.marginLeft + ml;
|
|
651
|
+
if (block.align === "center") {
|
|
652
|
+
x = env.marginLeft + ml + (availableWidth - width) / 2;
|
|
653
|
+
} else if (block.align === "right") {
|
|
654
|
+
x = env.marginLeft + ml + availableWidth - width;
|
|
655
|
+
}
|
|
656
|
+
const drawY = startY;
|
|
657
|
+
doc.save();
|
|
658
|
+
if (typeof block.opacity === "number") {
|
|
659
|
+
doc.opacity(block.opacity);
|
|
660
|
+
}
|
|
661
|
+
doc.lineWidth(lineWidth);
|
|
662
|
+
if (block.strokeColor) {
|
|
663
|
+
doc.strokeColor(block.strokeColor);
|
|
664
|
+
}
|
|
665
|
+
if (block.fillColor) {
|
|
666
|
+
doc.fillColor(block.fillColor);
|
|
667
|
+
}
|
|
668
|
+
switch (block.shape) {
|
|
669
|
+
case "rect":
|
|
670
|
+
doc.rect(x, drawY, width, height);
|
|
671
|
+
break;
|
|
672
|
+
case "roundedRect":
|
|
673
|
+
doc.roundedRect(x, drawY, width, height, block.radius ?? 6);
|
|
674
|
+
break;
|
|
675
|
+
case "circle": {
|
|
676
|
+
const radius = block.radius ?? Math.min(width, height) / 2;
|
|
677
|
+
doc.circle(x + radius, drawY + radius, radius);
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
case "ellipse":
|
|
681
|
+
doc.ellipse(x + width / 2, drawY + height / 2, width / 2, height / 2);
|
|
682
|
+
break;
|
|
683
|
+
case "line":
|
|
684
|
+
doc.moveTo(x, drawY).lineTo(x + width, drawY);
|
|
685
|
+
break;
|
|
686
|
+
case "polygon":
|
|
687
|
+
if (block.points?.length) {
|
|
688
|
+
const [first, ...rest] = block.points;
|
|
689
|
+
doc.moveTo(x + first.x, drawY + first.y);
|
|
690
|
+
rest.forEach((point) => {
|
|
691
|
+
doc.lineTo(x + point.x, drawY + point.y);
|
|
692
|
+
});
|
|
693
|
+
doc.closePath();
|
|
694
|
+
}
|
|
695
|
+
break;
|
|
696
|
+
case "path":
|
|
697
|
+
if (block.path) {
|
|
698
|
+
const safePath = block.path.trim().replace(/[\n\r\t]+/g, " ").replace(/\s+/g, " ");
|
|
699
|
+
if (safePath.includes("undefined") || safePath.includes("NaN")) {
|
|
700
|
+
throw new Error(`Invalid shape path: ${safePath}`);
|
|
701
|
+
}
|
|
702
|
+
doc.translate(x, drawY);
|
|
703
|
+
doc.path(safePath);
|
|
704
|
+
}
|
|
705
|
+
break;
|
|
706
|
+
}
|
|
707
|
+
if (block.fillColor && block.strokeColor) {
|
|
708
|
+
doc.fillAndStroke(block.fillColor, block.strokeColor);
|
|
709
|
+
} else if (block.fillColor) {
|
|
710
|
+
doc.fill(block.fillColor);
|
|
711
|
+
} else {
|
|
712
|
+
doc.stroke();
|
|
713
|
+
}
|
|
714
|
+
doc.restore();
|
|
715
|
+
const newY = startY + height + mb;
|
|
716
|
+
if (y === null) {
|
|
717
|
+
ctx.currentY = newY;
|
|
718
|
+
}
|
|
719
|
+
return newY;
|
|
720
|
+
};
|
|
721
|
+
|
|
478
722
|
// src/renderer-engine/blocks/table.ts
|
|
723
|
+
var shouldSkipHeaderOnlyTable = (table, rows) => {
|
|
724
|
+
const headerRows = table.headerRows ?? 0;
|
|
725
|
+
if (headerRows <= 0) return false;
|
|
726
|
+
return rows.length <= headerRows;
|
|
727
|
+
};
|
|
479
728
|
var normalizeSpan = (value) => {
|
|
480
729
|
if (!value || value < 1) return 1;
|
|
481
730
|
return Math.floor(value);
|
|
@@ -546,6 +795,7 @@ var toTableCell = (rawBlock) => {
|
|
|
546
795
|
const tableCellInput = isTableCellInput(rawBlock) ? rawBlock : void 0;
|
|
547
796
|
return {
|
|
548
797
|
text: rawBlock.text,
|
|
798
|
+
blocks: tableCellInput?.blocks,
|
|
549
799
|
italic: rawBlock.italic,
|
|
550
800
|
underline: rawBlock.underline,
|
|
551
801
|
link: rawBlock.link,
|
|
@@ -562,7 +812,8 @@ var toTableCell = (rawBlock) => {
|
|
|
562
812
|
paddingTop: tableCellInput?.paddingTop,
|
|
563
813
|
paddingRight: tableCellInput?.paddingRight,
|
|
564
814
|
paddingBottom: tableCellInput?.paddingBottom,
|
|
565
|
-
paddingLeft: tableCellInput?.paddingLeft
|
|
815
|
+
paddingLeft: tableCellInput?.paddingLeft,
|
|
816
|
+
border: tableCellInput?.border
|
|
566
817
|
};
|
|
567
818
|
};
|
|
568
819
|
var resolvePathValue = (obj, dottedPath) => {
|
|
@@ -585,7 +836,7 @@ var interpolateCellText = (text, item, rowIndex) => {
|
|
|
585
836
|
};
|
|
586
837
|
var applyIterableData = (cell, item, rowIndex) => ({
|
|
587
838
|
...cell,
|
|
588
|
-
text: interpolateCellText(cell.text, item, rowIndex)
|
|
839
|
+
text: cell.text !== void 0 ? interpolateCellText(cell.text, item, rowIndex) : void 0
|
|
589
840
|
});
|
|
590
841
|
var expandIterableRow = (table, bodyDef) => {
|
|
591
842
|
const templateRow = bodyDef.content.map(toTableCell).filter((cell) => cell !== null);
|
|
@@ -615,11 +866,22 @@ var normalizeTableRows = (table) => {
|
|
|
615
866
|
}
|
|
616
867
|
return rows;
|
|
617
868
|
};
|
|
618
|
-
var
|
|
869
|
+
var measureCellHeight = (doc, cell, textWidth, paddingTop, paddingBottom, measureBlockHeightFn) => {
|
|
870
|
+
if (cell.blocks?.length && measureBlockHeightFn) {
|
|
871
|
+
const cellEnv = { marginLeft: 0, innerWidth: textWidth, allowPageBreak: false };
|
|
872
|
+
const contentH = cell.blocks.filter((b) => b.visible !== false).reduce((acc, b) => acc + measureBlockHeightFn(b, cellEnv), 0);
|
|
873
|
+
return contentH + paddingTop + paddingBottom;
|
|
874
|
+
}
|
|
875
|
+
return doc.heightOfString(cell.text ?? "", { width: textWidth }) + paddingTop + paddingBottom;
|
|
876
|
+
};
|
|
877
|
+
var measureTableHeight = (doc, table, env, styles, computeColumnPixelWidths2, measureBlockHeightFn) => {
|
|
619
878
|
const localLeft = table.marginLeft ?? 0;
|
|
620
879
|
const localRight = table.marginRight ?? 0;
|
|
621
880
|
const width = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
622
881
|
const rows = normalizeTableRows(table);
|
|
882
|
+
if (shouldSkipHeaderOnlyTable(table, rows)) {
|
|
883
|
+
return 0;
|
|
884
|
+
}
|
|
623
885
|
const totalCols = table.widths.length;
|
|
624
886
|
const colWidths = computeColumnPixelWidths2(table.widths, width);
|
|
625
887
|
const rowHeights = [];
|
|
@@ -638,9 +900,11 @@ var measureTableHeight = (doc, table, env, styles, computeColumnPixelWidths2) =>
|
|
|
638
900
|
const paddingBottom = cell.paddingBottom ?? 2;
|
|
639
901
|
const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
|
|
640
902
|
const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
903
|
+
if (!cell.blocks?.length) {
|
|
904
|
+
doc.font(getCellFontName(cell));
|
|
905
|
+
doc.fontSize(cell.fontSize ?? 10);
|
|
906
|
+
}
|
|
907
|
+
const h = measureCellHeight(doc, cell, textWidth, paddingTop, paddingBottom, measureBlockHeightFn);
|
|
644
908
|
rowHeight = Math.max(rowHeight, h);
|
|
645
909
|
});
|
|
646
910
|
rowHeights.push(rowHeight);
|
|
@@ -657,9 +921,11 @@ var measureTableHeight = (doc, table, env, styles, computeColumnPixelWidths2) =>
|
|
|
657
921
|
const paddingBottom = cell.paddingBottom ?? 2;
|
|
658
922
|
const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
|
|
659
923
|
const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
924
|
+
if (!cell.blocks?.length) {
|
|
925
|
+
doc.font(getCellFontName(cell));
|
|
926
|
+
doc.fontSize(cell.fontSize ?? 10);
|
|
927
|
+
}
|
|
928
|
+
const neededHeight = measureCellHeight(doc, cell, textWidth, paddingTop, paddingBottom, measureBlockHeightFn);
|
|
663
929
|
const currentGroupHeight = getRowSpanGroupHeight(rowHeights, rowIndex, rowSpan);
|
|
664
930
|
if (neededHeight > currentGroupHeight) {
|
|
665
931
|
const deficit = neededHeight - currentGroupHeight;
|
|
@@ -674,7 +940,7 @@ var measureTableHeight = (doc, table, env, styles, computeColumnPixelWidths2) =>
|
|
|
674
940
|
totalHeight += table.marginBottom ?? 0;
|
|
675
941
|
return totalHeight;
|
|
676
942
|
};
|
|
677
|
-
var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidths2, bottomLimitForContent, finishPage2) => {
|
|
943
|
+
var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidths2, bottomLimitForContent, finishPage2, renderBlockFn, measureBlockHeightFn) => {
|
|
678
944
|
const localLeft = table.marginLeft ?? 0;
|
|
679
945
|
const localRight = table.marginRight ?? 0;
|
|
680
946
|
const baseLeft = env.marginLeft + localLeft;
|
|
@@ -690,9 +956,11 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
|
|
|
690
956
|
hLineColor: "#ccc",
|
|
691
957
|
vLineColor: "#ccc"
|
|
692
958
|
};
|
|
693
|
-
const borderAll = layout.border !== "none";
|
|
694
959
|
const headerRows = table.headerRows ?? 0;
|
|
695
960
|
const rows = normalizeTableRows(table);
|
|
961
|
+
if (shouldSkipHeaderOnlyTable(table, rows)) {
|
|
962
|
+
return baseY;
|
|
963
|
+
}
|
|
696
964
|
const buildPlacementAndHeights = (rows2) => {
|
|
697
965
|
const placedRows = [];
|
|
698
966
|
const rowHeights = [];
|
|
@@ -710,9 +978,11 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
|
|
|
710
978
|
const paddingBottom = cell.paddingBottom ?? 2;
|
|
711
979
|
const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
|
|
712
980
|
const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
981
|
+
if (!cell.blocks?.length) {
|
|
982
|
+
doc.font(getCellFontName(cell));
|
|
983
|
+
doc.fontSize(cell.fontSize ?? 10);
|
|
984
|
+
}
|
|
985
|
+
const h = measureCellHeight(doc, cell, textWidth, paddingTop, paddingBottom, measureBlockHeightFn);
|
|
716
986
|
rowHeight = Math.max(rowHeight, h);
|
|
717
987
|
});
|
|
718
988
|
rowHeights.push(rowHeight);
|
|
@@ -729,9 +999,11 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
|
|
|
729
999
|
const paddingBottom = cell.paddingBottom ?? 2;
|
|
730
1000
|
const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
|
|
731
1001
|
const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
1002
|
+
if (!cell.blocks?.length) {
|
|
1003
|
+
doc.font(getCellFontName(cell));
|
|
1004
|
+
doc.fontSize(cell.fontSize ?? 10);
|
|
1005
|
+
}
|
|
1006
|
+
const neededHeight = measureCellHeight(doc, cell, textWidth, paddingTop, paddingBottom, measureBlockHeightFn);
|
|
735
1007
|
const currentGroupHeight = getRowSpanGroupHeight(rowHeights, rowIndex, rowSpan);
|
|
736
1008
|
if (neededHeight > currentGroupHeight) {
|
|
737
1009
|
const deficit = neededHeight - currentGroupHeight;
|
|
@@ -754,47 +1026,60 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
|
|
|
754
1026
|
const paddingTop = cell.paddingTop ?? 2;
|
|
755
1027
|
const paddingBottom = cell.paddingBottom ?? 2;
|
|
756
1028
|
doc.save();
|
|
757
|
-
|
|
758
|
-
|
|
1029
|
+
const columnStyle = table.layout?.columnStyles?.[startCol];
|
|
1030
|
+
const rowStyle = table.layout?.rowStyles?.[rowIndex];
|
|
1031
|
+
const fillColor = cell.fillColor ?? rowStyle?.fillColor ?? columnStyle?.fillColor;
|
|
1032
|
+
if (fillColor) {
|
|
1033
|
+
doc.rect(x, rowTop2, colW, drawHeight).fillOpacity(1).fill(fillColor);
|
|
759
1034
|
doc.fillOpacity(1);
|
|
760
1035
|
}
|
|
761
|
-
doc.font(getCellFontName(cell));
|
|
762
|
-
doc.fontSize(fontSize);
|
|
763
|
-
doc.fillColor(cell.color ?? "black");
|
|
764
|
-
const align = cell.align ?? "left";
|
|
765
1036
|
const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
|
|
766
|
-
const
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
1037
|
+
const textY = rowTop2 + paddingTop;
|
|
1038
|
+
if (cell.blocks?.length && renderBlockFn) {
|
|
1039
|
+
const cellEnv = {
|
|
1040
|
+
marginLeft: x + paddingLeft,
|
|
1041
|
+
innerWidth: textWidth,
|
|
1042
|
+
allowPageBreak: false
|
|
1043
|
+
};
|
|
1044
|
+
const savedY = ctx.currentY;
|
|
1045
|
+
let contentY = textY;
|
|
1046
|
+
for (const block of cell.blocks) {
|
|
1047
|
+
if (!block || block.visible === false) continue;
|
|
1048
|
+
if (block.type === "signature" || block.type === "pageBreak") continue;
|
|
1049
|
+
contentY = renderBlockFn(block, contentY, cellEnv);
|
|
1050
|
+
}
|
|
1051
|
+
ctx.currentY = savedY;
|
|
1052
|
+
} else {
|
|
1053
|
+
doc.font(getCellFontName(cell));
|
|
1054
|
+
doc.fontSize(fontSize);
|
|
1055
|
+
doc.fillColor(cell.color ?? "black");
|
|
1056
|
+
const align = cell.align ?? "left";
|
|
1057
|
+
const textHeight = doc.heightOfString(cell.text ?? "", { width: textWidth });
|
|
1058
|
+
let paintY = textY;
|
|
1059
|
+
if (isHeaderRow) {
|
|
1060
|
+
const available = Math.max(drawHeight - paddingTop - paddingBottom, 0);
|
|
1061
|
+
const offset = Math.max((available - textHeight) / 2, 0);
|
|
1062
|
+
paintY = rowTop2 + paddingTop + offset;
|
|
1063
|
+
}
|
|
1064
|
+
doc.text(cell.text ?? "", x + paddingLeft, paintY, {
|
|
1065
|
+
width: textWidth,
|
|
1066
|
+
align,
|
|
1067
|
+
underline: !!cell.underline,
|
|
1068
|
+
strike: !!cell.strike,
|
|
1069
|
+
link: cell.link
|
|
1070
|
+
});
|
|
772
1071
|
}
|
|
773
|
-
doc.text(cell.text, x + paddingLeft, textY, {
|
|
774
|
-
width: textWidth,
|
|
775
|
-
align,
|
|
776
|
-
underline: !!cell.underline,
|
|
777
|
-
strike: !!cell.strike,
|
|
778
|
-
link: cell.link
|
|
779
|
-
});
|
|
780
1072
|
doc.restore();
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
doc.moveTo(x, bottomY).lineTo(x + colW, bottomY).stroke();
|
|
790
|
-
doc.restore();
|
|
791
|
-
doc.save();
|
|
792
|
-
doc.lineWidth(0.5);
|
|
793
|
-
doc.strokeColor(layout.vLineColor ?? "#ccc");
|
|
794
|
-
doc.moveTo(x, rowTop2).lineTo(x, bottomY).stroke();
|
|
795
|
-
doc.moveTo(x + colW, rowTop2).lineTo(x + colW, bottomY).stroke();
|
|
796
|
-
doc.restore();
|
|
1073
|
+
const effectiveBorder = getEffectiveCellBorder({
|
|
1074
|
+
table,
|
|
1075
|
+
cell,
|
|
1076
|
+
rowIndex,
|
|
1077
|
+
startCol
|
|
1078
|
+
});
|
|
1079
|
+
if (!drawTopBorder) {
|
|
1080
|
+
effectiveBorder.top.visible = false;
|
|
797
1081
|
}
|
|
1082
|
+
drawCellBorder(doc, effectiveBorder, x, rowTop2, colW, drawHeight);
|
|
798
1083
|
});
|
|
799
1084
|
return rowTop2 + rowHeight;
|
|
800
1085
|
};
|
|
@@ -834,16 +1119,133 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
|
|
|
834
1119
|
if (y === null) ctx.currentY = newY;
|
|
835
1120
|
return newY;
|
|
836
1121
|
};
|
|
1122
|
+
var SIDES = ["top", "right", "bottom", "left"];
|
|
1123
|
+
var DEFAULT_BORDER_COLOR = "#ccc";
|
|
1124
|
+
var DEFAULT_BORDER_WIDTH = 0.5;
|
|
1125
|
+
var isBorderStyleObject = (value) => {
|
|
1126
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
1127
|
+
};
|
|
1128
|
+
var normalizeOneSideBorder = (value, fallback) => {
|
|
1129
|
+
if (value === void 0) return fallback;
|
|
1130
|
+
if (typeof value === "boolean") {
|
|
1131
|
+
return {
|
|
1132
|
+
...fallback,
|
|
1133
|
+
visible: value
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
return {
|
|
1137
|
+
visible: value.visible ?? fallback.visible,
|
|
1138
|
+
color: value.color ?? fallback.color,
|
|
1139
|
+
width: value.width ?? fallback.width,
|
|
1140
|
+
dash: value.dash ?? fallback.dash
|
|
1141
|
+
};
|
|
1142
|
+
};
|
|
1143
|
+
var normalizeCellBorder = (input, fallback) => {
|
|
1144
|
+
if (input === void 0) return fallback;
|
|
1145
|
+
if (typeof input === "boolean") {
|
|
1146
|
+
return SIDES.reduce((acc, side) => {
|
|
1147
|
+
acc[side] = {
|
|
1148
|
+
...fallback[side],
|
|
1149
|
+
visible: input
|
|
1150
|
+
};
|
|
1151
|
+
return acc;
|
|
1152
|
+
}, {});
|
|
1153
|
+
}
|
|
1154
|
+
if (isBorderStyleObject(input) && ("visible" in input || "color" in input || "width" in input || "dash" in input)) {
|
|
1155
|
+
return SIDES.reduce((acc, side) => {
|
|
1156
|
+
acc[side] = normalizeOneSideBorder(input, fallback[side]);
|
|
1157
|
+
return acc;
|
|
1158
|
+
}, {});
|
|
1159
|
+
}
|
|
1160
|
+
const sideInput = input;
|
|
1161
|
+
return SIDES.reduce((acc, side) => {
|
|
1162
|
+
acc[side] = normalizeOneSideBorder(sideInput[side], fallback[side]);
|
|
1163
|
+
return acc;
|
|
1164
|
+
}, {});
|
|
1165
|
+
};
|
|
1166
|
+
var createDefaultTableBorder = (layout) => {
|
|
1167
|
+
const visible = layout?.border !== "none";
|
|
1168
|
+
const hColor = layout?.hLineColor ?? layout?.borderColor ?? DEFAULT_BORDER_COLOR;
|
|
1169
|
+
const vColor = layout?.vLineColor ?? layout?.borderColor ?? DEFAULT_BORDER_COLOR;
|
|
1170
|
+
const width = layout?.borderWidth ?? DEFAULT_BORDER_WIDTH;
|
|
1171
|
+
return {
|
|
1172
|
+
top: {
|
|
1173
|
+
visible,
|
|
1174
|
+
color: hColor,
|
|
1175
|
+
width,
|
|
1176
|
+
dash: layout?.borderDash
|
|
1177
|
+
},
|
|
1178
|
+
bottom: {
|
|
1179
|
+
visible,
|
|
1180
|
+
color: hColor,
|
|
1181
|
+
width,
|
|
1182
|
+
dash: layout?.borderDash
|
|
1183
|
+
},
|
|
1184
|
+
left: {
|
|
1185
|
+
visible,
|
|
1186
|
+
color: vColor,
|
|
1187
|
+
width,
|
|
1188
|
+
dash: layout?.borderDash
|
|
1189
|
+
},
|
|
1190
|
+
right: {
|
|
1191
|
+
visible,
|
|
1192
|
+
color: vColor,
|
|
1193
|
+
width,
|
|
1194
|
+
dash: layout?.borderDash
|
|
1195
|
+
}
|
|
1196
|
+
};
|
|
1197
|
+
};
|
|
1198
|
+
var getEffectiveCellBorder = (args) => {
|
|
1199
|
+
const { table, cell, rowIndex, startCol } = args;
|
|
1200
|
+
const tableBorder = createDefaultTableBorder(table.layout);
|
|
1201
|
+
const columnBorder = normalizeCellBorder(table.layout?.columnStyles?.[startCol]?.border, tableBorder);
|
|
1202
|
+
const rowBorder = normalizeCellBorder(table.layout?.rowStyles?.[rowIndex]?.border, columnBorder);
|
|
1203
|
+
return normalizeCellBorder(cell.border, rowBorder);
|
|
1204
|
+
};
|
|
1205
|
+
var drawBorderSide = (doc, side, border, x, y, width, height) => {
|
|
1206
|
+
if (!border.visible || border.width <= 0) return;
|
|
1207
|
+
doc.save();
|
|
1208
|
+
doc.lineWidth(border.width);
|
|
1209
|
+
doc.strokeColor(border.color);
|
|
1210
|
+
if (border.dash?.length) {
|
|
1211
|
+
doc.dash(border.dash[0], {
|
|
1212
|
+
space: border.dash[1] ?? border.dash[0]
|
|
1213
|
+
});
|
|
1214
|
+
} else {
|
|
1215
|
+
doc.undash();
|
|
1216
|
+
}
|
|
1217
|
+
switch (side) {
|
|
1218
|
+
case "top":
|
|
1219
|
+
doc.moveTo(x, y).lineTo(x + width, y).stroke();
|
|
1220
|
+
break;
|
|
1221
|
+
case "right":
|
|
1222
|
+
doc.moveTo(x + width, y).lineTo(x + width, y + height).stroke();
|
|
1223
|
+
break;
|
|
1224
|
+
case "bottom":
|
|
1225
|
+
doc.moveTo(x, y + height).lineTo(x + width, y + height).stroke();
|
|
1226
|
+
break;
|
|
1227
|
+
case "left":
|
|
1228
|
+
doc.moveTo(x, y).lineTo(x, y + height).stroke();
|
|
1229
|
+
break;
|
|
1230
|
+
}
|
|
1231
|
+
doc.restore();
|
|
1232
|
+
};
|
|
1233
|
+
var drawCellBorder = (doc, border, x, y, width, height) => {
|
|
1234
|
+
drawBorderSide(doc, "top", border.top, x, y, width, height);
|
|
1235
|
+
drawBorderSide(doc, "right", border.right, x, y, width, height);
|
|
1236
|
+
drawBorderSide(doc, "bottom", border.bottom, x, y, width, height);
|
|
1237
|
+
drawBorderSide(doc, "left", border.left, x, y, width, height);
|
|
1238
|
+
};
|
|
837
1239
|
|
|
838
1240
|
// src/renderer-engine/blocks/text.ts
|
|
839
1241
|
var processTextBlock = (doc, ctx, styles, block, y, env, ensureSpaceFor) => {
|
|
840
1242
|
const tb = resolveTextBlock(styles, block);
|
|
1243
|
+
const mt = tb.marginTop ?? 0;
|
|
1244
|
+
const mb = tb.marginBottom ?? 0;
|
|
841
1245
|
const localLeft = tb.marginLeft ?? 0;
|
|
842
1246
|
const localRight = tb.marginRight ?? 0;
|
|
843
1247
|
const xLeft = env.marginLeft + localLeft;
|
|
844
1248
|
const width = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
845
|
-
const baseY = y ?? ctx.currentY;
|
|
846
|
-
const startY = baseY;
|
|
847
1249
|
doc.fontSize(tb.fontSize ?? 10);
|
|
848
1250
|
const isBold = !!tb.bold;
|
|
849
1251
|
const isItalic = !!tb.italic;
|
|
@@ -857,11 +1259,12 @@ var processTextBlock = (doc, ctx, styles, block, y, env, ensureSpaceFor) => {
|
|
|
857
1259
|
doc.font(fontName);
|
|
858
1260
|
if (tb.color) doc.fillColor(tb.color);
|
|
859
1261
|
else doc.fillColor("black");
|
|
860
|
-
const
|
|
1262
|
+
const lineGap = tb.lineGap ?? 4;
|
|
1263
|
+
const textHeight = doc.heightOfString(tb.text, { width, lineGap });
|
|
861
1264
|
if (y === null) {
|
|
862
|
-
|
|
863
|
-
ensureSpaceFor(textHeight + gapCheck, env);
|
|
1265
|
+
ensureSpaceFor(mt + textHeight + lineGap + mb, env);
|
|
864
1266
|
}
|
|
1267
|
+
const startY = (y ?? ctx.currentY) + mt;
|
|
865
1268
|
doc.text(tb.text, xLeft, startY, {
|
|
866
1269
|
width,
|
|
867
1270
|
align: tb.align ?? "left",
|
|
@@ -869,358 +1272,82 @@ var processTextBlock = (doc, ctx, styles, block, y, env, ensureSpaceFor) => {
|
|
|
869
1272
|
strike: !!tb.strike,
|
|
870
1273
|
link: tb.link
|
|
871
1274
|
});
|
|
872
|
-
const
|
|
873
|
-
const newY = startY + textHeight + gap;
|
|
1275
|
+
const newY = doc.y + lineGap + mb;
|
|
874
1276
|
if (y === null) ctx.currentY = newY;
|
|
875
1277
|
return newY;
|
|
876
1278
|
};
|
|
877
1279
|
|
|
878
|
-
// src/renderer-engine/utils/block-renderer.ts
|
|
879
|
-
function createBlockRenderer(deps) {
|
|
880
|
-
const { doc, ctx, styles, computeColumnPixelWidths: computeColumnPixelWidths2, finishPage: finishPage2, processSignatureBlock } = deps;
|
|
881
|
-
const bottomLimitForContent = createBottomLimitForContent(doc, ctx);
|
|
882
|
-
const ensureSpaceFor = createEnsureSpaceFor(ctx, bottomLimitForContent, finishPage2);
|
|
883
|
-
const measureBlockHeight = createMeasureBlockHeight({
|
|
884
|
-
doc,
|
|
885
|
-
styles,
|
|
886
|
-
computeColumnPixelWidths: computeColumnPixelWidths2
|
|
887
|
-
});
|
|
888
|
-
const renderBlock = (block, y, env) => {
|
|
889
|
-
if (block.visible === false) {
|
|
890
|
-
return y ?? ctx.currentY;
|
|
891
|
-
}
|
|
892
|
-
switch (block.type) {
|
|
893
|
-
case "text":
|
|
894
|
-
return processTextBlock(doc, ctx, styles, block, y, env, ensureSpaceFor);
|
|
895
|
-
case "image":
|
|
896
|
-
return processImageBlock(doc, ctx, block, y, env, ensureSpaceFor);
|
|
897
|
-
case "qr": {
|
|
898
|
-
const qb = block;
|
|
899
|
-
const imageLike = {
|
|
900
|
-
type: "image",
|
|
901
|
-
src: qb.src,
|
|
902
|
-
width: qb.size,
|
|
903
|
-
height: qb.size,
|
|
904
|
-
align: qb.align,
|
|
905
|
-
marginTop: qb.marginTop,
|
|
906
|
-
marginBottom: qb.marginBottom,
|
|
907
|
-
marginLeft: qb.marginLeft,
|
|
908
|
-
marginRight: qb.marginRight
|
|
909
|
-
};
|
|
910
|
-
return processImageBlock(doc, ctx, imageLike, y, env, ensureSpaceFor);
|
|
911
|
-
}
|
|
912
|
-
case "barcode":
|
|
913
|
-
return processBarcodeBlock(doc, ctx, block, y, env, ensureSpaceFor);
|
|
914
|
-
case "line":
|
|
915
|
-
return processLineBlock(doc, ctx, block, y, env, ensureSpaceFor);
|
|
916
|
-
case "table":
|
|
917
|
-
return processTableBlock(
|
|
918
|
-
doc,
|
|
919
|
-
ctx,
|
|
920
|
-
styles,
|
|
921
|
-
block,
|
|
922
|
-
y,
|
|
923
|
-
env,
|
|
924
|
-
computeColumnPixelWidths2,
|
|
925
|
-
bottomLimitForContent,
|
|
926
|
-
finishPage2
|
|
927
|
-
);
|
|
928
|
-
case "columns":
|
|
929
|
-
return processColumnsBlock(
|
|
930
|
-
ctx,
|
|
931
|
-
block,
|
|
932
|
-
y,
|
|
933
|
-
env,
|
|
934
|
-
computeColumnPixelWidths2,
|
|
935
|
-
renderBlock,
|
|
936
|
-
ensureSpaceFor,
|
|
937
|
-
measureBlockHeight
|
|
938
|
-
);
|
|
939
|
-
case "keyValueGrid":
|
|
940
|
-
return processKeyValueGridBlock(
|
|
941
|
-
doc,
|
|
942
|
-
ctx,
|
|
943
|
-
styles,
|
|
944
|
-
block,
|
|
945
|
-
y,
|
|
946
|
-
env,
|
|
947
|
-
computeColumnPixelWidths2,
|
|
948
|
-
ensureSpaceFor
|
|
949
|
-
);
|
|
950
|
-
case "signature":
|
|
951
|
-
if (!env.allowPageBreak) {
|
|
952
|
-
throw new PdfEngineError({
|
|
953
|
-
code: "PDF_ERROR_SIGNATURE_NOT_IN_MAIN_FLOW" /* PDF_ERROR_SIGNATURE_NOT_IN_MAIN_FLOW */,
|
|
954
|
-
message: "Signature block is only allowed in main content flow.",
|
|
955
|
-
statusCode: 400,
|
|
956
|
-
details: { blockType: "signature" }
|
|
957
|
-
});
|
|
958
|
-
}
|
|
959
|
-
if (y !== null) {
|
|
960
|
-
throw new PdfEngineError({
|
|
961
|
-
code: "PDF_ERROR_SIGNATURE_EXPLICIT_Y_FORBIDDEN" /* PDF_ERROR_SIGNATURE_EXPLICIT_Y_FORBIDDEN */,
|
|
962
|
-
message: "Signature block must be part of main flow, not drawn at explicit Y.",
|
|
963
|
-
statusCode: 400,
|
|
964
|
-
details: { blockType: "signature", y }
|
|
965
|
-
});
|
|
966
|
-
}
|
|
967
|
-
processSignatureBlock(block);
|
|
968
|
-
return ctx.currentY;
|
|
969
|
-
case "pageBreak":
|
|
970
|
-
if (!env.allowPageBreak || ctx.inFooter) {
|
|
971
|
-
return y ?? ctx.currentY;
|
|
972
|
-
}
|
|
973
|
-
finishPage2(true);
|
|
974
|
-
return ctx.currentY;
|
|
975
|
-
default:
|
|
976
|
-
return ctx.currentY;
|
|
977
|
-
}
|
|
978
|
-
};
|
|
979
|
-
const renderBlockArray = (blocks, startY, env) => {
|
|
980
|
-
let localY = startY;
|
|
981
|
-
for (const block of blocks) {
|
|
982
|
-
if (block.visible === false) {
|
|
983
|
-
continue;
|
|
984
|
-
}
|
|
985
|
-
localY = renderBlock(block, localY, env);
|
|
986
|
-
}
|
|
987
|
-
return localY;
|
|
988
|
-
};
|
|
989
|
-
return {
|
|
990
|
-
renderBlock,
|
|
991
|
-
renderBlockArray
|
|
992
|
-
};
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
// src/renderer-engine/utils/context.ts
|
|
996
|
-
function createInitialContext(doc) {
|
|
997
|
-
return {
|
|
998
|
-
pageNumber: 1,
|
|
999
|
-
currentY: doc.page.margins.top,
|
|
1000
|
-
signatureBlock: null,
|
|
1001
|
-
signatureTopY: null,
|
|
1002
|
-
signatureHeight: 0,
|
|
1003
|
-
signaturePlaced: false,
|
|
1004
|
-
afterSignature: false,
|
|
1005
|
-
inFooter: false
|
|
1006
|
-
};
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
1280
|
// src/renderer-engine/utils/ensure-space.ts
|
|
1010
|
-
function createEnsureSpaceFor(ctx, bottomLimitForContent, finishPage2) {
|
|
1281
|
+
function createEnsureSpaceFor(ctx, doc, bottomLimitForContent, finishPage2) {
|
|
1011
1282
|
return (heightNeeded, env) => {
|
|
1012
1283
|
if (!env.allowPageBreak || ctx.inFooter) return;
|
|
1013
1284
|
const bottomLimit = bottomLimitForContent();
|
|
1285
|
+
const fullContentHeight = bottomLimit - doc.page.margins.top;
|
|
1286
|
+
if (heightNeeded > fullContentHeight) {
|
|
1287
|
+
console.warn(
|
|
1288
|
+
`[pdf-engine] Block height (${Math.round(heightNeeded)}pt) exceeds page content area (${Math.round(fullContentHeight)}pt). Content will overflow.`
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1014
1291
|
if (ctx.currentY + heightNeeded > bottomLimit) {
|
|
1015
1292
|
finishPage2(true);
|
|
1016
1293
|
}
|
|
1017
1294
|
};
|
|
1018
1295
|
}
|
|
1019
1296
|
|
|
1020
|
-
// src/renderer-engine/utils/
|
|
1021
|
-
var
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1297
|
+
// src/renderer-engine/utils/spacing.ts
|
|
1298
|
+
var emptySpacing = () => ({
|
|
1299
|
+
top: 0,
|
|
1300
|
+
right: 0,
|
|
1301
|
+
bottom: 0,
|
|
1302
|
+
left: 0
|
|
1025
1303
|
});
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
if (def.watermark && watermarkUsesLast(mode)) {
|
|
1039
|
-
const isLast = !addNewPage;
|
|
1040
|
-
drawWatermarkForPage(doc, def.watermark, ctx.pageNumber, isLast);
|
|
1304
|
+
var resolveSpacing = (value, overrides) => {
|
|
1305
|
+
const result = emptySpacing();
|
|
1306
|
+
if (typeof value === "number") {
|
|
1307
|
+
result.top = value;
|
|
1308
|
+
result.right = value;
|
|
1309
|
+
result.bottom = value;
|
|
1310
|
+
result.left = value;
|
|
1311
|
+
} else if (value) {
|
|
1312
|
+
result.top = value.top ?? 0;
|
|
1313
|
+
result.right = value.right ?? 0;
|
|
1314
|
+
result.bottom = value.bottom ?? 0;
|
|
1315
|
+
result.left = value.left ?? 0;
|
|
1041
1316
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
if (addNewPage) {
|
|
1048
|
-
doc.addPage();
|
|
1049
|
-
ctx.pageNumber += 1;
|
|
1050
|
-
startNewPageLayout();
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
|
|
1054
|
-
// src/renderer-engine/utils/footer.ts
|
|
1055
|
-
function normalizeFooter(input, ctx, doc) {
|
|
1056
|
-
if (!input) return null;
|
|
1057
|
-
if (typeof input === "function") {
|
|
1058
|
-
const result = input(ctx.pageNumber, {
|
|
1059
|
-
width: doc.page.width,
|
|
1060
|
-
height: doc.page.height
|
|
1061
|
-
});
|
|
1062
|
-
if (!result) return null;
|
|
1063
|
-
return normalizeFooter(result, ctx, doc);
|
|
1064
|
-
}
|
|
1065
|
-
if (input.blocks && Array.isArray(input.blocks)) {
|
|
1066
|
-
const f = input;
|
|
1067
|
-
return {
|
|
1068
|
-
visible: f.visible,
|
|
1069
|
-
blocks: f.blocks,
|
|
1070
|
-
marginTop: f.marginTop,
|
|
1071
|
-
marginBottom: f.marginBottom,
|
|
1072
|
-
marginLeft: f.marginLeft,
|
|
1073
|
-
marginRight: f.marginRight,
|
|
1074
|
-
backgroundColor: f.backgroundColor,
|
|
1075
|
-
backgroundImage: f.backgroundImage
|
|
1076
|
-
};
|
|
1077
|
-
}
|
|
1078
|
-
if (input.type) {
|
|
1079
|
-
return { blocks: [input] };
|
|
1080
|
-
}
|
|
1081
|
-
if (Array.isArray(input)) {
|
|
1082
|
-
return { blocks: input };
|
|
1083
|
-
}
|
|
1084
|
-
return null;
|
|
1085
|
-
}
|
|
1086
|
-
function drawFooter(doc, def, ctx, footerBandHeight, renderBlockArray) {
|
|
1087
|
-
const footerConfig = def.footer;
|
|
1088
|
-
if (!footerConfig) return;
|
|
1089
|
-
const layout = normalizeFooter(footerConfig, ctx, doc);
|
|
1090
|
-
if (!layout || !layout.blocks.length) return;
|
|
1091
|
-
if (layout.visible === false) return;
|
|
1092
|
-
const bandHeight = footerBandHeight;
|
|
1093
|
-
if (!bandHeight) return;
|
|
1094
|
-
const { marginTop = 4, backgroundColor, backgroundImage, blocks } = layout;
|
|
1095
|
-
const footerMarginLeft = layout.marginLeft ?? 0;
|
|
1096
|
-
const footerMarginRight = layout.marginRight ?? 0;
|
|
1097
|
-
const contentWidth = doc.page.width - footerMarginLeft - footerMarginRight;
|
|
1098
|
-
const footerEnv = {
|
|
1099
|
-
marginLeft: footerMarginLeft,
|
|
1100
|
-
innerWidth: contentWidth,
|
|
1101
|
-
allowPageBreak: false
|
|
1102
|
-
};
|
|
1103
|
-
const pageHeight = doc.page.height;
|
|
1104
|
-
const bandTop = pageHeight - bandHeight;
|
|
1105
|
-
if (backgroundColor) {
|
|
1106
|
-
doc.save();
|
|
1107
|
-
doc.rect(0, bandTop, doc.page.width, bandHeight).fill(backgroundColor);
|
|
1108
|
-
doc.restore();
|
|
1109
|
-
}
|
|
1110
|
-
if (backgroundImage) {
|
|
1111
|
-
try {
|
|
1112
|
-
doc.image(backgroundImage, 0, bandTop, {
|
|
1113
|
-
width: doc.page.width,
|
|
1114
|
-
height: bandHeight
|
|
1115
|
-
});
|
|
1116
|
-
} catch (e) {
|
|
1117
|
-
console.warn("Failed to load footer background image:", e);
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
doc.save();
|
|
1121
|
-
doc.rect(footerMarginLeft, bandTop, contentWidth, bandHeight).clip();
|
|
1122
|
-
doc.translate(0, bandTop);
|
|
1123
|
-
ctx.inFooter = true;
|
|
1124
|
-
const localStartY = marginTop;
|
|
1125
|
-
renderBlockArray(blocks, localStartY, footerEnv);
|
|
1126
|
-
ctx.inFooter = false;
|
|
1127
|
-
doc.restore();
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
// src/renderer-engine/utils/header.ts
|
|
1131
|
-
function drawHeader(doc, def, headerBandHeight, header, renderBlockArray) {
|
|
1132
|
-
if (!header || !header.blocks.length) return;
|
|
1133
|
-
if (header.visible === false) return;
|
|
1134
|
-
if (!headerBandHeight) return;
|
|
1135
|
-
const bandTop = 0;
|
|
1136
|
-
const bandHeight = def.margins.top;
|
|
1137
|
-
const headerMarginLeft = header.marginLeft ?? 0;
|
|
1138
|
-
const headerMarginRight = header.marginRight ?? 0;
|
|
1139
|
-
const contentWidth = doc.page.width - headerMarginLeft - headerMarginRight;
|
|
1140
|
-
const headerEnv = {
|
|
1141
|
-
marginLeft: headerMarginLeft,
|
|
1142
|
-
innerWidth: contentWidth,
|
|
1143
|
-
allowPageBreak: false
|
|
1317
|
+
return {
|
|
1318
|
+
top: overrides?.top ?? result.top,
|
|
1319
|
+
right: overrides?.right ?? result.right,
|
|
1320
|
+
bottom: overrides?.bottom ?? result.bottom,
|
|
1321
|
+
left: overrides?.left ?? result.left
|
|
1144
1322
|
};
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
doc.image(header.backgroundImage, 0, bandTop, {
|
|
1153
|
-
width: doc.page.width,
|
|
1154
|
-
height: bandHeight
|
|
1155
|
-
});
|
|
1156
|
-
} catch (e) {
|
|
1157
|
-
console.warn("Failed to load header background image:", e);
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
doc.save();
|
|
1161
|
-
doc.rect(headerMarginLeft, bandTop, contentWidth, bandHeight).clip();
|
|
1162
|
-
const startY = bandTop + (header.marginTop ?? 0);
|
|
1163
|
-
renderBlockArray(header.blocks, startY, headerEnv);
|
|
1164
|
-
doc.restore();
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
// src/renderer-engine/utils/image-loader.ts
|
|
1168
|
-
import axios from "axios";
|
|
1169
|
-
async function normalizeImageSrc(src) {
|
|
1170
|
-
if (!src) return src;
|
|
1171
|
-
if (Buffer.isBuffer(src)) return src;
|
|
1172
|
-
if (src.startsWith("http://") || src.startsWith("https://")) {
|
|
1173
|
-
try {
|
|
1174
|
-
const res = await axios.get(src, { responseType: "arraybuffer" });
|
|
1175
|
-
return Buffer.from(res.data);
|
|
1176
|
-
} catch (e) {
|
|
1177
|
-
throw toPdfEngineError(e, {
|
|
1178
|
-
code: "PDF_ERROR_IMAGE_FETCH_FAILED" /* PDF_ERROR_IMAGE_FETCH_FAILED */,
|
|
1179
|
-
message: "Failed to fetch remote image for PDF.",
|
|
1180
|
-
statusCode: 422,
|
|
1181
|
-
details: { url: src },
|
|
1182
|
-
retryable: false
|
|
1183
|
-
});
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
return src;
|
|
1187
|
-
}
|
|
1188
|
-
async function materializeImagesInBlocks(blocks) {
|
|
1189
|
-
const out = [];
|
|
1190
|
-
for (const block of blocks) {
|
|
1191
|
-
if (block.type === "image") {
|
|
1192
|
-
const img = { ...block };
|
|
1193
|
-
img.src = await normalizeImageSrc(img.src);
|
|
1194
|
-
out.push(img);
|
|
1195
|
-
} else if (block.type === "columns") {
|
|
1196
|
-
out.push({
|
|
1197
|
-
...block,
|
|
1198
|
-
columns: await Promise.all(block.columns.map((col) => materializeImagesInBlocks(col)))
|
|
1199
|
-
});
|
|
1200
|
-
} else if (block.type === "signature" && block.blocks) {
|
|
1201
|
-
out.push({
|
|
1202
|
-
...block,
|
|
1203
|
-
blocks: await materializeImagesInBlocks(block.blocks)
|
|
1204
|
-
});
|
|
1205
|
-
} else {
|
|
1206
|
-
out.push(block);
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
return out;
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
// src/renderer-engine/utils/layout.ts
|
|
1213
|
-
function computeColumnPixelWidths(widths, totalWidth) {
|
|
1214
|
-
let fixedTotal = 0;
|
|
1215
|
-
let starCount = 0;
|
|
1216
|
-
widths.forEach((w) => {
|
|
1217
|
-
if (w === "*") starCount++;
|
|
1218
|
-
else fixedTotal += w;
|
|
1323
|
+
};
|
|
1324
|
+
var resolveBlockPadding = (block) => {
|
|
1325
|
+
return resolveSpacing(block.padding, {
|
|
1326
|
+
top: block.paddingTop,
|
|
1327
|
+
right: block.paddingRight,
|
|
1328
|
+
bottom: block.paddingBottom,
|
|
1329
|
+
left: block.paddingLeft
|
|
1219
1330
|
});
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
return
|
|
1223
|
-
|
|
1331
|
+
};
|
|
1332
|
+
var resolveHeaderPadding = (header) => {
|
|
1333
|
+
return resolveSpacing(header.padding, {
|
|
1334
|
+
top: header.paddingTop,
|
|
1335
|
+
right: header.paddingRight,
|
|
1336
|
+
bottom: header.paddingBottom,
|
|
1337
|
+
left: header.paddingLeft
|
|
1338
|
+
});
|
|
1339
|
+
};
|
|
1340
|
+
var resolveFooterPadding = (footer) => {
|
|
1341
|
+
return resolveSpacing(footer.padding, {
|
|
1342
|
+
top: footer.paddingTop,
|
|
1343
|
+
right: footer.paddingRight,
|
|
1344
|
+
bottom: footer.paddingBottom,
|
|
1345
|
+
left: footer.paddingLeft
|
|
1346
|
+
});
|
|
1347
|
+
};
|
|
1348
|
+
var hasPadding = (padding) => {
|
|
1349
|
+
return padding.top > 0 || padding.right > 0 || padding.bottom > 0 || padding.left > 0;
|
|
1350
|
+
};
|
|
1224
1351
|
|
|
1225
1352
|
// src/renderer-engine/utils/styles.ts
|
|
1226
1353
|
function mergeStyleDefs(styles, names) {
|
|
@@ -1316,191 +1443,772 @@ var drawStyledText = (doc, tb, x, y, width) => {
|
|
|
1316
1443
|
// src/renderer-engine/utils/measure-block-height.ts
|
|
1317
1444
|
function createMeasureBlockHeight(deps) {
|
|
1318
1445
|
const { doc, styles, computeColumnPixelWidths: computeColumnPixelWidths2 } = deps;
|
|
1319
|
-
const measureText = (
|
|
1320
|
-
const tb = resolveTextBlock(styles,
|
|
1321
|
-
const
|
|
1446
|
+
const measureText = (block, env) => {
|
|
1447
|
+
const tb = resolveTextBlock(styles, block);
|
|
1448
|
+
const mt = tb.marginTop ?? 0;
|
|
1449
|
+
const mb = tb.marginBottom ?? 0;
|
|
1450
|
+
const localLeft = tb.marginLeft ?? 0;
|
|
1451
|
+
const localRight = tb.marginRight ?? 0;
|
|
1452
|
+
const width = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
1322
1453
|
doc.font(getFontNameForText(tb));
|
|
1323
1454
|
doc.fontSize(tb.fontSize ?? 10);
|
|
1324
|
-
const
|
|
1325
|
-
const
|
|
1326
|
-
return
|
|
1455
|
+
const lineGap = tb.lineGap ?? 4;
|
|
1456
|
+
const height = doc.heightOfString(tb.text ?? "", { width, lineGap });
|
|
1457
|
+
return mt + height + lineGap + mb;
|
|
1327
1458
|
};
|
|
1328
|
-
const
|
|
1329
|
-
|
|
1330
|
-
const h = b.height ?? 50;
|
|
1331
|
-
return measureImageLike(b.marginTop, b.marginBottom, h);
|
|
1459
|
+
const measureImage = (block) => {
|
|
1460
|
+
return (block.marginTop ?? 0) + (block.height ?? 50) + (block.marginBottom ?? 0);
|
|
1332
1461
|
};
|
|
1333
|
-
const measureQr = (
|
|
1334
|
-
|
|
1335
|
-
return measureImageLike(b.marginTop, b.marginBottom, size);
|
|
1462
|
+
const measureQr = (block) => {
|
|
1463
|
+
return (block.marginTop ?? 0) + (block.size ?? 80) + (block.marginBottom ?? 0);
|
|
1336
1464
|
};
|
|
1337
|
-
const measureBarcode = (
|
|
1338
|
-
|
|
1339
|
-
return measureImageLike(b.marginTop, b.marginBottom, h);
|
|
1465
|
+
const measureBarcode = (block) => {
|
|
1466
|
+
return (block.marginTop ?? 0) + (block.height ?? 40) + (block.marginBottom ?? 0);
|
|
1340
1467
|
};
|
|
1341
|
-
const
|
|
1342
|
-
|
|
1343
|
-
const mt = b.marginTop ?? 0;
|
|
1344
|
-
const mb = b.marginBottom ?? 0;
|
|
1345
|
-
return mt + lw + mb;
|
|
1468
|
+
const measureShape = (block) => {
|
|
1469
|
+
return (block.marginTop ?? 0) + (block.height ?? block.lineWidth ?? 1) + (block.marginBottom ?? 0);
|
|
1346
1470
|
};
|
|
1347
|
-
const
|
|
1348
|
-
|
|
1349
|
-
const mb = b.marginBottom ?? 0;
|
|
1350
|
-
const ml = b.marginLeft ?? 0;
|
|
1351
|
-
const mr = b.marginRight ?? 0;
|
|
1352
|
-
const innerWidth = Math.max(env.innerWidth - ml - mr, 1);
|
|
1353
|
-
const fakeEnv = {
|
|
1354
|
-
marginLeft: 0,
|
|
1355
|
-
innerWidth,
|
|
1356
|
-
allowPageBreak: false
|
|
1357
|
-
};
|
|
1358
|
-
const h = measureTableHeight(doc, b, fakeEnv, styles, computeColumnPixelWidths2);
|
|
1359
|
-
return mt + h + mb;
|
|
1471
|
+
const measureLine = (block) => {
|
|
1472
|
+
return (block.marginTop ?? 0) + (block.lineWidth ?? 1) + (block.marginBottom ?? 0);
|
|
1360
1473
|
};
|
|
1361
|
-
const measureColumns = (
|
|
1362
|
-
const mt =
|
|
1363
|
-
const mb =
|
|
1364
|
-
const blockLeft =
|
|
1365
|
-
const blockRight =
|
|
1474
|
+
const measureColumns = (block, env) => {
|
|
1475
|
+
const mt = block.marginTop ?? 0;
|
|
1476
|
+
const mb = block.marginBottom ?? 0;
|
|
1477
|
+
const blockLeft = block.marginLeft ?? 0;
|
|
1478
|
+
const blockRight = block.marginRight ?? 0;
|
|
1366
1479
|
const totalWidth = Math.max(env.innerWidth - blockLeft - blockRight, 1);
|
|
1367
|
-
const cols =
|
|
1480
|
+
const cols = block.columns ?? [];
|
|
1368
1481
|
const n = cols.length;
|
|
1369
1482
|
if (!n) return mt + mb;
|
|
1370
1483
|
let colWidths;
|
|
1371
|
-
const mode =
|
|
1372
|
-
let gap;
|
|
1484
|
+
const mode = block.mode ?? "fixedGap";
|
|
1373
1485
|
if (mode === "spaceBetween" && n > 1) {
|
|
1374
|
-
if (
|
|
1375
|
-
colWidths = computeColumnPixelWidths2(
|
|
1486
|
+
if (block.widths && block.widths.length === n) {
|
|
1487
|
+
colWidths = computeColumnPixelWidths2(block.widths, totalWidth);
|
|
1376
1488
|
} else {
|
|
1377
1489
|
colWidths = Array(n).fill(totalWidth / n);
|
|
1378
1490
|
}
|
|
1379
|
-
const totalColsWidth = colWidths.reduce((a, x) => a + x, 0);
|
|
1380
|
-
const remaining = Math.max(totalWidth - totalColsWidth, 0);
|
|
1381
|
-
gap = remaining / (n - 1);
|
|
1382
|
-
void gap;
|
|
1383
1491
|
} else {
|
|
1384
|
-
gap =
|
|
1492
|
+
const gap = block.gap ?? 20;
|
|
1385
1493
|
const totalGapsWidth = gap * (n - 1);
|
|
1386
1494
|
const widthForCols = Math.max(totalWidth - totalGapsWidth, 1);
|
|
1387
|
-
if (
|
|
1388
|
-
colWidths = computeColumnPixelWidths2(
|
|
1495
|
+
if (block.widths && block.widths.length === n) {
|
|
1496
|
+
colWidths = computeColumnPixelWidths2(block.widths, widthForCols);
|
|
1389
1497
|
} else {
|
|
1390
1498
|
colWidths = Array(n).fill(widthForCols / n);
|
|
1391
1499
|
}
|
|
1392
1500
|
}
|
|
1393
|
-
const heights = [];
|
|
1394
|
-
for (let i = 0; i < n; i++) {
|
|
1395
|
-
const colBlocks = cols[i] ?? [];
|
|
1396
|
-
const colEnv = {
|
|
1397
|
-
marginLeft: 0,
|
|
1398
|
-
innerWidth: colWidths[i],
|
|
1399
|
-
allowPageBreak: false
|
|
1400
|
-
};
|
|
1401
|
-
let
|
|
1402
|
-
for (const child of colBlocks) {
|
|
1403
|
-
if (!child || child.visible === false) continue;
|
|
1404
|
-
|
|
1501
|
+
const heights = [];
|
|
1502
|
+
for (let i = 0; i < n; i++) {
|
|
1503
|
+
const colBlocks = cols[i] ?? [];
|
|
1504
|
+
const colEnv = {
|
|
1505
|
+
marginLeft: 0,
|
|
1506
|
+
innerWidth: colWidths[i],
|
|
1507
|
+
allowPageBreak: false
|
|
1508
|
+
};
|
|
1509
|
+
let colHeight = 0;
|
|
1510
|
+
for (const child of colBlocks) {
|
|
1511
|
+
if (!child || child.visible === false) continue;
|
|
1512
|
+
colHeight += measure(child, colEnv);
|
|
1513
|
+
}
|
|
1514
|
+
heights.push(colHeight);
|
|
1515
|
+
}
|
|
1516
|
+
return mt + Math.max(...heights, 0) + mb;
|
|
1517
|
+
};
|
|
1518
|
+
const measureTable = (block, env) => {
|
|
1519
|
+
const mt = block.marginTop ?? 0;
|
|
1520
|
+
const mb = block.marginBottom ?? 0;
|
|
1521
|
+
const ml = block.marginLeft ?? 0;
|
|
1522
|
+
const mr = block.marginRight ?? 0;
|
|
1523
|
+
const innerWidth = Math.max(env.innerWidth - ml - mr, 1);
|
|
1524
|
+
const fakeEnv = {
|
|
1525
|
+
marginLeft: 0,
|
|
1526
|
+
innerWidth,
|
|
1527
|
+
allowPageBreak: false
|
|
1528
|
+
};
|
|
1529
|
+
const height = measureTableHeight(doc, block, fakeEnv, styles, computeColumnPixelWidths2, measure);
|
|
1530
|
+
return mt + height + mb;
|
|
1531
|
+
};
|
|
1532
|
+
const measureKeyValueGrid = (block, env) => {
|
|
1533
|
+
const mt = block.marginTop ?? 0;
|
|
1534
|
+
const mb = block.marginBottom ?? 0;
|
|
1535
|
+
const ml = block.marginLeft ?? 0;
|
|
1536
|
+
const mr = block.marginRight ?? 0;
|
|
1537
|
+
const totalWidth = Math.max(env.innerWidth - ml - mr, 1);
|
|
1538
|
+
const cols = block.columns ?? [];
|
|
1539
|
+
const colCount = cols.length;
|
|
1540
|
+
if (!colCount) return mt + mb;
|
|
1541
|
+
const rowGap = block.rowGap ?? 4;
|
|
1542
|
+
const keyValueGap = block.verticalKeyValueGap ?? 2;
|
|
1543
|
+
const separatorText = block.separator ?? "";
|
|
1544
|
+
const separatorWidth = separatorText ? doc.widthOfString(separatorText) : 0;
|
|
1545
|
+
let colWidths;
|
|
1546
|
+
if (block.columnWidths && block.columnWidths.length === colCount) {
|
|
1547
|
+
colWidths = computeColumnPixelWidths2(
|
|
1548
|
+
block.columnWidths.map((w) => w ?? "*"),
|
|
1549
|
+
totalWidth
|
|
1550
|
+
);
|
|
1551
|
+
} else {
|
|
1552
|
+
colWidths = Array(colCount).fill(totalWidth / colCount);
|
|
1553
|
+
}
|
|
1554
|
+
const measureTextHeight2 = (text, width) => {
|
|
1555
|
+
doc.fontSize(10);
|
|
1556
|
+
return doc.heightOfString(text ?? "", { width }) + 4;
|
|
1557
|
+
};
|
|
1558
|
+
let totalHeight = mt;
|
|
1559
|
+
if (block.orientation === "vertical") {
|
|
1560
|
+
let maxColHeight = 0;
|
|
1561
|
+
for (let colIndex = 0; colIndex < colCount; colIndex++) {
|
|
1562
|
+
const col = cols[colIndex] ?? [];
|
|
1563
|
+
const colWidth = colWidths[colIndex];
|
|
1564
|
+
let colHeight = 0;
|
|
1565
|
+
for (const item of col) {
|
|
1566
|
+
const keyHeight = measureTextHeight2(item.key ?? "", colWidth);
|
|
1567
|
+
const valueHeight = measureTextHeight2(item.value ?? "", colWidth);
|
|
1568
|
+
colHeight += keyHeight + keyValueGap + valueHeight + rowGap;
|
|
1569
|
+
}
|
|
1570
|
+
maxColHeight = Math.max(maxColHeight, colHeight);
|
|
1571
|
+
}
|
|
1572
|
+
totalHeight += maxColHeight;
|
|
1573
|
+
} else {
|
|
1574
|
+
const maxRows = Math.max(...cols.map((c) => c ? c.length : 0), 0);
|
|
1575
|
+
for (let rowIndex = 0; rowIndex < maxRows; rowIndex++) {
|
|
1576
|
+
let rowHeight = 0;
|
|
1577
|
+
for (let colIndex = 0; colIndex < colCount; colIndex++) {
|
|
1578
|
+
const item = cols[colIndex]?.[rowIndex];
|
|
1579
|
+
if (!item) continue;
|
|
1580
|
+
const colWidth = colWidths[colIndex];
|
|
1581
|
+
const keyWidth = block.keyWidth === "*" ? Math.max(colWidth * 0.35, 20) : block.keyWidth ?? 80;
|
|
1582
|
+
const keyHeight = measureTextHeight2(item.key ?? "", keyWidth);
|
|
1583
|
+
const valueXInsideCol = keyWidth + (separatorText ? separatorWidth + 4 : 4);
|
|
1584
|
+
const valueWidth = Math.max(colWidth - valueXInsideCol, 1);
|
|
1585
|
+
const valueHeight = measureTextHeight2(item.value ?? "", valueWidth);
|
|
1586
|
+
rowHeight = Math.max(rowHeight, keyHeight, valueHeight);
|
|
1587
|
+
}
|
|
1588
|
+
if (rowHeight > 0) {
|
|
1589
|
+
totalHeight += rowHeight + rowGap;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
return totalHeight + mb;
|
|
1594
|
+
};
|
|
1595
|
+
const stripBoxModelProps = (block) => {
|
|
1596
|
+
return {
|
|
1597
|
+
...block,
|
|
1598
|
+
marginTop: 0,
|
|
1599
|
+
marginBottom: 0,
|
|
1600
|
+
marginLeft: 0,
|
|
1601
|
+
marginRight: 0,
|
|
1602
|
+
backgroundColor: void 0,
|
|
1603
|
+
backgroundImage: void 0,
|
|
1604
|
+
backgroundOpacity: void 0,
|
|
1605
|
+
backgroundBlocks: void 0,
|
|
1606
|
+
padding: void 0,
|
|
1607
|
+
paddingTop: void 0,
|
|
1608
|
+
paddingRight: void 0,
|
|
1609
|
+
paddingBottom: void 0,
|
|
1610
|
+
paddingLeft: void 0
|
|
1611
|
+
};
|
|
1612
|
+
};
|
|
1613
|
+
const measureRaw = (block, env) => {
|
|
1614
|
+
switch (block.type) {
|
|
1615
|
+
case "text":
|
|
1616
|
+
return measureText(block, env);
|
|
1617
|
+
case "image":
|
|
1618
|
+
return measureImage(block);
|
|
1619
|
+
case "qr":
|
|
1620
|
+
return measureQr(block);
|
|
1621
|
+
case "barcode":
|
|
1622
|
+
return measureBarcode(block);
|
|
1623
|
+
case "line":
|
|
1624
|
+
return measureLine(block);
|
|
1625
|
+
case "shape":
|
|
1626
|
+
return measureShape(block);
|
|
1627
|
+
case "columns":
|
|
1628
|
+
return measureColumns(block, env);
|
|
1629
|
+
case "table":
|
|
1630
|
+
return measureTable(block, env);
|
|
1631
|
+
case "keyValueGrid":
|
|
1632
|
+
return measureKeyValueGrid(block, env);
|
|
1633
|
+
case "list":
|
|
1634
|
+
return measureListHeight(doc, styles, block, env);
|
|
1635
|
+
case "signature":
|
|
1636
|
+
return block.height ?? 0;
|
|
1637
|
+
case "pageBreak":
|
|
1638
|
+
return 0;
|
|
1639
|
+
default:
|
|
1640
|
+
return 0;
|
|
1641
|
+
}
|
|
1642
|
+
};
|
|
1643
|
+
const measure = (block, env) => {
|
|
1644
|
+
if (!block || block.visible === false) return 0;
|
|
1645
|
+
if (env.isBackgroundLayer) {
|
|
1646
|
+
return measureRaw(block, env);
|
|
1647
|
+
}
|
|
1648
|
+
const padding = resolveBlockPadding(block);
|
|
1649
|
+
const hasBoxBackground = !!block.backgroundColor || !!block.backgroundImage || !!block.backgroundBlocks?.length;
|
|
1650
|
+
const shouldUseBoxModel = hasPadding(padding) || hasBoxBackground;
|
|
1651
|
+
if (!shouldUseBoxModel) {
|
|
1652
|
+
return measureRaw(block, env);
|
|
1653
|
+
}
|
|
1654
|
+
const marginTop = block.marginTop ?? 0;
|
|
1655
|
+
const marginBottom = block.marginBottom ?? 0;
|
|
1656
|
+
const marginLeft = block.marginLeft ?? 0;
|
|
1657
|
+
const marginRight = block.marginRight ?? 0;
|
|
1658
|
+
const outerWidth = Math.max(env.innerWidth - marginLeft - marginRight, 1);
|
|
1659
|
+
const innerWidth = Math.max(outerWidth - padding.left - padding.right, 1);
|
|
1660
|
+
const innerEnv = {
|
|
1661
|
+
...env,
|
|
1662
|
+
marginLeft: 0,
|
|
1663
|
+
innerWidth
|
|
1664
|
+
};
|
|
1665
|
+
const innerBlock = stripBoxModelProps(block);
|
|
1666
|
+
const innerHeight = measureRaw(innerBlock, innerEnv);
|
|
1667
|
+
return marginTop + padding.top + innerHeight + padding.bottom + marginBottom;
|
|
1668
|
+
};
|
|
1669
|
+
return (block, env) => measure(block, env);
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
// src/renderer-engine/utils/page-limit.ts
|
|
1673
|
+
function createBottomLimitForContent(doc, ctx) {
|
|
1674
|
+
return () => {
|
|
1675
|
+
const pageBottomForContent = doc.page.height - doc.page.margins.bottom;
|
|
1676
|
+
return ctx.signatureTopY ?? pageBottomForContent;
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
// src/renderer-engine/utils/block-renderer.ts
|
|
1681
|
+
function createBlockRenderer(deps) {
|
|
1682
|
+
const { doc, ctx, styles, computeColumnPixelWidths: computeColumnPixelWidths2, finishPage: finishPage2, processSignatureBlock, defaultImage } = deps;
|
|
1683
|
+
const bottomLimitForContent = createBottomLimitForContent(doc, ctx);
|
|
1684
|
+
const ensureSpaceFor = createEnsureSpaceFor(ctx, doc, bottomLimitForContent, finishPage2);
|
|
1685
|
+
const measureBlockHeight = createMeasureBlockHeight({
|
|
1686
|
+
doc,
|
|
1687
|
+
styles,
|
|
1688
|
+
computeColumnPixelWidths: computeColumnPixelWidths2
|
|
1689
|
+
});
|
|
1690
|
+
const stripBoxModelProps = (block) => {
|
|
1691
|
+
return {
|
|
1692
|
+
...block,
|
|
1693
|
+
marginTop: 0,
|
|
1694
|
+
marginBottom: 0,
|
|
1695
|
+
marginLeft: 0,
|
|
1696
|
+
marginRight: 0,
|
|
1697
|
+
backgroundColor: void 0,
|
|
1698
|
+
backgroundImage: void 0,
|
|
1699
|
+
backgroundOpacity: void 0,
|
|
1700
|
+
backgroundBlocks: void 0,
|
|
1701
|
+
padding: void 0,
|
|
1702
|
+
paddingTop: void 0,
|
|
1703
|
+
paddingRight: void 0,
|
|
1704
|
+
paddingBottom: void 0,
|
|
1705
|
+
paddingLeft: void 0
|
|
1706
|
+
};
|
|
1707
|
+
};
|
|
1708
|
+
const renderBlockCore = (block, y, env) => {
|
|
1709
|
+
if (block.visible === false) {
|
|
1710
|
+
return y ?? ctx.currentY;
|
|
1711
|
+
}
|
|
1712
|
+
switch (block.type) {
|
|
1713
|
+
case "text":
|
|
1714
|
+
return processTextBlock(doc, ctx, styles, block, y, env, ensureSpaceFor);
|
|
1715
|
+
case "image":
|
|
1716
|
+
return processImageBlock(doc, ctx, block, y, env, ensureSpaceFor, defaultImage);
|
|
1717
|
+
case "qr": {
|
|
1718
|
+
const qb = block;
|
|
1719
|
+
const imageLike = {
|
|
1720
|
+
type: "image",
|
|
1721
|
+
src: qb.src,
|
|
1722
|
+
width: qb.size,
|
|
1723
|
+
height: qb.size,
|
|
1724
|
+
align: qb.align,
|
|
1725
|
+
marginTop: qb.marginTop,
|
|
1726
|
+
marginBottom: qb.marginBottom,
|
|
1727
|
+
marginLeft: qb.marginLeft,
|
|
1728
|
+
marginRight: qb.marginRight
|
|
1729
|
+
};
|
|
1730
|
+
return processImageBlock(doc, ctx, imageLike, y, env, ensureSpaceFor, defaultImage);
|
|
1731
|
+
}
|
|
1732
|
+
case "barcode":
|
|
1733
|
+
return processBarcodeBlock(doc, ctx, block, y, env, ensureSpaceFor);
|
|
1734
|
+
case "line":
|
|
1735
|
+
return processLineBlock(doc, ctx, block, y, env, ensureSpaceFor);
|
|
1736
|
+
case "shape":
|
|
1737
|
+
return processShapeBlock(doc, ctx, block, y, env, ensureSpaceFor);
|
|
1738
|
+
case "columns":
|
|
1739
|
+
return processColumnsBlock(
|
|
1740
|
+
ctx,
|
|
1741
|
+
block,
|
|
1742
|
+
y,
|
|
1743
|
+
env,
|
|
1744
|
+
computeColumnPixelWidths2,
|
|
1745
|
+
renderBlock,
|
|
1746
|
+
ensureSpaceFor,
|
|
1747
|
+
measureBlockHeight
|
|
1748
|
+
);
|
|
1749
|
+
case "table":
|
|
1750
|
+
return processTableBlock(
|
|
1751
|
+
doc,
|
|
1752
|
+
ctx,
|
|
1753
|
+
styles,
|
|
1754
|
+
block,
|
|
1755
|
+
y,
|
|
1756
|
+
env,
|
|
1757
|
+
computeColumnPixelWidths2,
|
|
1758
|
+
bottomLimitForContent,
|
|
1759
|
+
finishPage2,
|
|
1760
|
+
renderBlock,
|
|
1761
|
+
measureBlockHeight
|
|
1762
|
+
);
|
|
1763
|
+
case "keyValueGrid":
|
|
1764
|
+
return processKeyValueGridBlock(
|
|
1765
|
+
doc,
|
|
1766
|
+
ctx,
|
|
1767
|
+
styles,
|
|
1768
|
+
block,
|
|
1769
|
+
y,
|
|
1770
|
+
env,
|
|
1771
|
+
computeColumnPixelWidths2,
|
|
1772
|
+
ensureSpaceFor
|
|
1773
|
+
);
|
|
1774
|
+
case "list":
|
|
1775
|
+
return processListBlock(
|
|
1776
|
+
doc,
|
|
1777
|
+
ctx,
|
|
1778
|
+
styles,
|
|
1779
|
+
block,
|
|
1780
|
+
y,
|
|
1781
|
+
env,
|
|
1782
|
+
ensureSpaceFor
|
|
1783
|
+
);
|
|
1784
|
+
case "pageBreak":
|
|
1785
|
+
finishPage2(true);
|
|
1786
|
+
return ctx.currentY;
|
|
1787
|
+
case "signature":
|
|
1788
|
+
processSignatureBlock(block);
|
|
1789
|
+
return y ?? ctx.currentY;
|
|
1790
|
+
default:
|
|
1791
|
+
throw new PdfEngineError({
|
|
1792
|
+
code: "PDF_ERROR_BLOCK_UNSUPPORTED" /* PDF_ERROR_BLOCK_UNSUPPORTED */,
|
|
1793
|
+
message: `Unsupported block type: ${block.type}`,
|
|
1794
|
+
statusCode: 422,
|
|
1795
|
+
details: { block }
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
};
|
|
1799
|
+
const renderBlock = (block, y, env) => {
|
|
1800
|
+
if (block.visible === false) {
|
|
1801
|
+
return y ?? ctx.currentY;
|
|
1802
|
+
}
|
|
1803
|
+
if (env.isBackgroundLayer) {
|
|
1804
|
+
return renderBlockCore(block, y, env);
|
|
1805
|
+
}
|
|
1806
|
+
const padding = resolveBlockPadding(block);
|
|
1807
|
+
const hasBoxBackground = !!block.backgroundColor || !!block.backgroundImage || !!block.backgroundBlocks?.length;
|
|
1808
|
+
const shouldUseBoxModel = hasPadding(padding) || hasBoxBackground;
|
|
1809
|
+
if (!shouldUseBoxModel) {
|
|
1810
|
+
return renderBlockCore(block, y, env);
|
|
1811
|
+
}
|
|
1812
|
+
const marginTop = block.marginTop ?? 0;
|
|
1813
|
+
const marginBottom = block.marginBottom ?? 0;
|
|
1814
|
+
const marginLeft = block.marginLeft ?? 0;
|
|
1815
|
+
const marginRight = block.marginRight ?? 0;
|
|
1816
|
+
const outerX = env.marginLeft + marginLeft;
|
|
1817
|
+
const outerWidth = Math.max(env.innerWidth - marginLeft - marginRight, 1);
|
|
1818
|
+
const innerX = outerX + padding.left;
|
|
1819
|
+
const innerWidth = Math.max(outerWidth - padding.left - padding.right, 1);
|
|
1820
|
+
const innerEnv = {
|
|
1821
|
+
...env,
|
|
1822
|
+
marginLeft: innerX,
|
|
1823
|
+
innerWidth
|
|
1824
|
+
};
|
|
1825
|
+
const innerBlock = stripBoxModelProps(block);
|
|
1826
|
+
const innerHeight = measureBlockHeight(innerBlock, innerEnv);
|
|
1827
|
+
const boxHeight = padding.top + innerHeight + padding.bottom;
|
|
1828
|
+
const totalHeight = marginTop + boxHeight + marginBottom;
|
|
1829
|
+
if (y === null && env.allowPageBreak !== false && block.type !== "table") {
|
|
1830
|
+
ensureSpaceFor(totalHeight, env);
|
|
1831
|
+
}
|
|
1832
|
+
const finalOuterY = (y ?? ctx.currentY) + marginTop;
|
|
1833
|
+
if (block.backgroundColor) {
|
|
1834
|
+
doc.save();
|
|
1835
|
+
doc.fillOpacity(block.backgroundOpacity ?? 1).rect(outerX, finalOuterY, outerWidth, boxHeight).fill(block.backgroundColor);
|
|
1836
|
+
doc.restore();
|
|
1837
|
+
}
|
|
1838
|
+
if (block.backgroundImage) {
|
|
1839
|
+
try {
|
|
1840
|
+
doc.save();
|
|
1841
|
+
doc.opacity(block.backgroundOpacity ?? 1);
|
|
1842
|
+
doc.image(block.backgroundImage, outerX, finalOuterY, {
|
|
1843
|
+
width: outerWidth,
|
|
1844
|
+
height: boxHeight
|
|
1845
|
+
});
|
|
1846
|
+
doc.restore();
|
|
1847
|
+
} catch (_) {
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
if (block.backgroundBlocks?.length) {
|
|
1851
|
+
doc.save();
|
|
1852
|
+
doc.opacity(block.backgroundOpacity ?? 1);
|
|
1853
|
+
renderBlockArray(block.backgroundBlocks, finalOuterY, {
|
|
1854
|
+
...env,
|
|
1855
|
+
marginLeft: outerX,
|
|
1856
|
+
innerWidth: outerWidth,
|
|
1857
|
+
allowPageBreak: false,
|
|
1858
|
+
isBackgroundLayer: true
|
|
1859
|
+
});
|
|
1860
|
+
doc.restore();
|
|
1861
|
+
}
|
|
1862
|
+
const innerEndY = renderBlockCore(innerBlock, finalOuterY + padding.top, innerEnv);
|
|
1863
|
+
const newY = innerEndY + padding.bottom + marginBottom;
|
|
1864
|
+
if (y === null) {
|
|
1865
|
+
ctx.currentY = newY;
|
|
1866
|
+
}
|
|
1867
|
+
return newY;
|
|
1868
|
+
};
|
|
1869
|
+
const renderBlockArray = (blocks, startY, env) => {
|
|
1870
|
+
let localY = startY;
|
|
1871
|
+
for (const block of blocks) {
|
|
1872
|
+
localY = renderBlock(block, localY, env);
|
|
1873
|
+
}
|
|
1874
|
+
return localY;
|
|
1875
|
+
};
|
|
1876
|
+
return { renderBlock, renderBlockArray, measureBlockHeight };
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
// src/renderer-engine/utils/context.ts
|
|
1880
|
+
function createInitialContext(doc) {
|
|
1881
|
+
return {
|
|
1882
|
+
pageNumber: 1,
|
|
1883
|
+
currentY: doc.page.margins.top,
|
|
1884
|
+
signatureBlock: null,
|
|
1885
|
+
signatureTopY: null,
|
|
1886
|
+
signatureHeight: 0,
|
|
1887
|
+
signaturePlaced: false,
|
|
1888
|
+
afterSignature: false,
|
|
1889
|
+
inFooter: false,
|
|
1890
|
+
inManualPageAdd: false
|
|
1891
|
+
};
|
|
1892
|
+
}
|
|
1893
|
+
|
|
1894
|
+
// src/renderer-engine/utils/env.ts
|
|
1895
|
+
var contentEnv = (doc) => ({
|
|
1896
|
+
marginLeft: doc.page.margins.left,
|
|
1897
|
+
innerWidth: doc.page.width - doc.page.margins.left - doc.page.margins.right,
|
|
1898
|
+
allowPageBreak: true
|
|
1899
|
+
});
|
|
1900
|
+
|
|
1901
|
+
// src/renderer-engine/utils/finish-page.ts
|
|
1902
|
+
function finishPage({
|
|
1903
|
+
addNewPage,
|
|
1904
|
+
doc,
|
|
1905
|
+
def,
|
|
1906
|
+
ctx,
|
|
1907
|
+
footerBandHeight,
|
|
1908
|
+
renderBlockArray,
|
|
1909
|
+
startNewPageLayout
|
|
1910
|
+
}) {
|
|
1911
|
+
const mode = def.watermark?.mode;
|
|
1912
|
+
if (def.watermark && watermarkUsesLast(mode)) {
|
|
1913
|
+
const isLast = !addNewPage;
|
|
1914
|
+
drawWatermarkForPage(doc, def.watermark, ctx.pageNumber, isLast);
|
|
1915
|
+
}
|
|
1916
|
+
if (ctx.signatureBlock && ctx.signatureTopY !== null) {
|
|
1917
|
+
const env = contentEnv(doc);
|
|
1918
|
+
drawSignatureBlock(doc, ctx.signatureBlock, ctx.signatureTopY, env, renderBlockArray);
|
|
1919
|
+
}
|
|
1920
|
+
drawFooter(doc, def, ctx, footerBandHeight, renderBlockArray);
|
|
1921
|
+
if (addNewPage) {
|
|
1922
|
+
ctx.inManualPageAdd = true;
|
|
1923
|
+
doc.addPage();
|
|
1924
|
+
ctx.inManualPageAdd = false;
|
|
1925
|
+
ctx.pageNumber += 1;
|
|
1926
|
+
startNewPageLayout();
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
// src/renderer-engine/utils/footer.ts
|
|
1931
|
+
function normalizeFooter(input, ctx, doc) {
|
|
1932
|
+
if (!input) return null;
|
|
1933
|
+
if (typeof input === "function") {
|
|
1934
|
+
const result = input(ctx.pageNumber, {
|
|
1935
|
+
width: doc.page.width,
|
|
1936
|
+
height: doc.page.height
|
|
1937
|
+
});
|
|
1938
|
+
if (!result) return null;
|
|
1939
|
+
return normalizeFooter(result, ctx, doc);
|
|
1940
|
+
}
|
|
1941
|
+
if (input.blocks && Array.isArray(input.blocks)) {
|
|
1942
|
+
const footer = input;
|
|
1943
|
+
return {
|
|
1944
|
+
visible: footer.visible,
|
|
1945
|
+
blocks: footer.blocks,
|
|
1946
|
+
backgroundBlocks: footer.backgroundBlocks,
|
|
1947
|
+
marginTop: footer.marginTop,
|
|
1948
|
+
marginBottom: footer.marginBottom,
|
|
1949
|
+
marginLeft: footer.marginLeft,
|
|
1950
|
+
marginRight: footer.marginRight,
|
|
1951
|
+
padding: footer.padding,
|
|
1952
|
+
paddingTop: footer.paddingTop,
|
|
1953
|
+
paddingRight: footer.paddingRight,
|
|
1954
|
+
paddingBottom: footer.paddingBottom,
|
|
1955
|
+
paddingLeft: footer.paddingLeft,
|
|
1956
|
+
backgroundColor: footer.backgroundColor,
|
|
1957
|
+
backgroundImage: footer.backgroundImage,
|
|
1958
|
+
backgroundOpacity: footer.backgroundOpacity
|
|
1959
|
+
};
|
|
1960
|
+
}
|
|
1961
|
+
if (input.type) {
|
|
1962
|
+
return { blocks: [input] };
|
|
1963
|
+
}
|
|
1964
|
+
if (Array.isArray(input)) {
|
|
1965
|
+
return { blocks: input };
|
|
1966
|
+
}
|
|
1967
|
+
return null;
|
|
1968
|
+
}
|
|
1969
|
+
function drawFooter(doc, def, ctx, footerBandHeight, renderBlockArray) {
|
|
1970
|
+
const footerConfig = def.footer;
|
|
1971
|
+
if (!footerConfig) return;
|
|
1972
|
+
const layout = normalizeFooter(footerConfig, ctx, doc);
|
|
1973
|
+
if (!layout) return;
|
|
1974
|
+
if (layout.visible === false) return;
|
|
1975
|
+
const blocks = layout.blocks ?? [];
|
|
1976
|
+
const backgroundBlocks = layout.backgroundBlocks ?? [];
|
|
1977
|
+
if (!blocks.length && !backgroundBlocks.length && !layout.backgroundColor && !layout.backgroundImage) {
|
|
1978
|
+
return;
|
|
1979
|
+
}
|
|
1980
|
+
const bandHeight = footerBandHeight;
|
|
1981
|
+
if (!bandHeight) return;
|
|
1982
|
+
const footerMarginLeft = layout.marginLeft ?? 0;
|
|
1983
|
+
const footerMarginRight = layout.marginRight ?? 0;
|
|
1984
|
+
const outerX = footerMarginLeft;
|
|
1985
|
+
const outerWidth = doc.page.width - footerMarginLeft - footerMarginRight;
|
|
1986
|
+
const padding = resolveFooterPadding(layout);
|
|
1987
|
+
const innerX = outerX + padding.left;
|
|
1988
|
+
const innerWidth = Math.max(outerWidth - padding.left - padding.right, 1);
|
|
1989
|
+
const outerEnv = {
|
|
1990
|
+
marginLeft: outerX,
|
|
1991
|
+
innerWidth: outerWidth,
|
|
1992
|
+
allowPageBreak: false
|
|
1993
|
+
};
|
|
1994
|
+
const innerEnv = {
|
|
1995
|
+
marginLeft: innerX,
|
|
1996
|
+
innerWidth,
|
|
1997
|
+
allowPageBreak: false
|
|
1998
|
+
};
|
|
1999
|
+
const pageHeight = doc.page.height;
|
|
2000
|
+
const bandTop = pageHeight - bandHeight;
|
|
2001
|
+
const backgroundOpacity = layout.backgroundOpacity ?? 1;
|
|
2002
|
+
if (layout.backgroundColor) {
|
|
2003
|
+
doc.save();
|
|
2004
|
+
doc.fillOpacity(backgroundOpacity).rect(0, bandTop, doc.page.width, bandHeight).fill(layout.backgroundColor);
|
|
2005
|
+
doc.restore();
|
|
2006
|
+
}
|
|
2007
|
+
if (layout.backgroundImage) {
|
|
2008
|
+
try {
|
|
2009
|
+
doc.save();
|
|
2010
|
+
doc.opacity(backgroundOpacity);
|
|
2011
|
+
doc.image(layout.backgroundImage, 0, bandTop, {
|
|
2012
|
+
width: doc.page.width,
|
|
2013
|
+
height: bandHeight
|
|
2014
|
+
});
|
|
2015
|
+
doc.restore();
|
|
2016
|
+
} catch (e) {
|
|
2017
|
+
console.warn("Failed to load footer background image:", e);
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
doc.save();
|
|
2021
|
+
doc.rect(outerX, bandTop, outerWidth, bandHeight).clip();
|
|
2022
|
+
doc.translate(0, bandTop);
|
|
2023
|
+
ctx.inFooter = true;
|
|
2024
|
+
const localStartY = layout.marginTop ?? 4;
|
|
2025
|
+
if (backgroundBlocks.length) {
|
|
2026
|
+
doc.save();
|
|
2027
|
+
doc.opacity(backgroundOpacity);
|
|
2028
|
+
renderBlockArray(backgroundBlocks, localStartY, {
|
|
2029
|
+
...outerEnv,
|
|
2030
|
+
allowPageBreak: false,
|
|
2031
|
+
isBackgroundLayer: true
|
|
2032
|
+
});
|
|
2033
|
+
doc.restore();
|
|
2034
|
+
}
|
|
2035
|
+
if (blocks.length) {
|
|
2036
|
+
renderBlockArray(blocks, localStartY + padding.top, innerEnv);
|
|
2037
|
+
}
|
|
2038
|
+
ctx.inFooter = false;
|
|
2039
|
+
doc.restore();
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
// src/renderer-engine/utils/header.ts
|
|
2043
|
+
function drawHeader(doc, def, headerBandHeight, header, renderBlockArray) {
|
|
2044
|
+
if (!header) return;
|
|
2045
|
+
if (header.visible === false) return;
|
|
2046
|
+
if (!headerBandHeight) return;
|
|
2047
|
+
const blocks = header.blocks ?? [];
|
|
2048
|
+
const backgroundBlocks = header.backgroundBlocks ?? [];
|
|
2049
|
+
if (!blocks.length && !backgroundBlocks.length && !header.backgroundColor && !header.backgroundImage) {
|
|
2050
|
+
return;
|
|
2051
|
+
}
|
|
2052
|
+
const bandTop = 0;
|
|
2053
|
+
const bandHeight = def.margins.top;
|
|
2054
|
+
const headerMarginLeft = header.marginLeft ?? 0;
|
|
2055
|
+
const headerMarginRight = header.marginRight ?? 0;
|
|
2056
|
+
const outerX = headerMarginLeft;
|
|
2057
|
+
const outerWidth = doc.page.width - headerMarginLeft - headerMarginRight;
|
|
2058
|
+
const padding = resolveHeaderPadding(header);
|
|
2059
|
+
const innerX = outerX + padding.left;
|
|
2060
|
+
const innerWidth = Math.max(outerWidth - padding.left - padding.right, 1);
|
|
2061
|
+
const outerEnv = {
|
|
2062
|
+
marginLeft: outerX,
|
|
2063
|
+
innerWidth: outerWidth,
|
|
2064
|
+
allowPageBreak: false
|
|
2065
|
+
};
|
|
2066
|
+
const innerEnv = {
|
|
2067
|
+
marginLeft: innerX,
|
|
2068
|
+
innerWidth,
|
|
2069
|
+
allowPageBreak: false
|
|
2070
|
+
};
|
|
2071
|
+
const backgroundOpacity = header.backgroundOpacity ?? 1;
|
|
2072
|
+
if (header.backgroundColor) {
|
|
2073
|
+
doc.save();
|
|
2074
|
+
doc.fillOpacity(backgroundOpacity).rect(0, bandTop, doc.page.width, bandHeight).fill(header.backgroundColor);
|
|
2075
|
+
doc.restore();
|
|
2076
|
+
}
|
|
2077
|
+
if (header.backgroundImage) {
|
|
2078
|
+
try {
|
|
2079
|
+
doc.save();
|
|
2080
|
+
doc.opacity(backgroundOpacity);
|
|
2081
|
+
doc.image(header.backgroundImage, 0, bandTop, {
|
|
2082
|
+
width: doc.page.width,
|
|
2083
|
+
height: bandHeight
|
|
2084
|
+
});
|
|
2085
|
+
doc.restore();
|
|
2086
|
+
} catch (e) {
|
|
2087
|
+
console.warn("Failed to load header background image:", e);
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
doc.save();
|
|
2091
|
+
doc.rect(outerX, bandTop, outerWidth, bandHeight).clip();
|
|
2092
|
+
const startY = bandTop + (header.marginTop ?? 0);
|
|
2093
|
+
if (backgroundBlocks.length) {
|
|
2094
|
+
doc.save();
|
|
2095
|
+
doc.opacity(backgroundOpacity);
|
|
2096
|
+
renderBlockArray(backgroundBlocks, startY, {
|
|
2097
|
+
...outerEnv,
|
|
2098
|
+
allowPageBreak: false,
|
|
2099
|
+
isBackgroundLayer: true
|
|
2100
|
+
});
|
|
2101
|
+
doc.restore();
|
|
2102
|
+
}
|
|
2103
|
+
if (blocks.length) {
|
|
2104
|
+
renderBlockArray(blocks, startY + padding.top, innerEnv);
|
|
2105
|
+
}
|
|
2106
|
+
doc.restore();
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
// src/renderer-engine/utils/image-loader.ts
|
|
2110
|
+
import axios from "axios";
|
|
2111
|
+
async function normalizeImageSrc(src, fallback) {
|
|
2112
|
+
if (!src) return fallback ?? src;
|
|
2113
|
+
if (Buffer.isBuffer(src)) return src;
|
|
2114
|
+
if (src.startsWith("data:")) {
|
|
2115
|
+
const commaIdx = src.indexOf(",");
|
|
2116
|
+
if (commaIdx !== -1) {
|
|
2117
|
+
return Buffer.from(src.slice(commaIdx + 1), "base64");
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
if (src.startsWith("http://") || src.startsWith("https://")) {
|
|
2121
|
+
try {
|
|
2122
|
+
const res = await axios.get(src, {
|
|
2123
|
+
responseType: "arraybuffer"
|
|
2124
|
+
});
|
|
2125
|
+
return Buffer.from(res.data);
|
|
2126
|
+
} catch (e) {
|
|
2127
|
+
if (fallback !== void 0) {
|
|
2128
|
+
console.warn(`Failed to fetch remote image "${src}", using default image.`);
|
|
2129
|
+
return fallback;
|
|
1405
2130
|
}
|
|
1406
|
-
|
|
2131
|
+
throw toPdfEngineError(e, {
|
|
2132
|
+
code: "PDF_ERROR_IMAGE_FETCH_FAILED" /* PDF_ERROR_IMAGE_FETCH_FAILED */,
|
|
2133
|
+
message: "Failed to fetch remote image for PDF.",
|
|
2134
|
+
statusCode: 422,
|
|
2135
|
+
details: { url: src },
|
|
2136
|
+
retryable: false
|
|
2137
|
+
});
|
|
1407
2138
|
}
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
const
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
const colCount = cols.length;
|
|
1418
|
-
if (!colCount) return mt + mb;
|
|
1419
|
-
const blockLeft = b.marginLeft ?? 0;
|
|
1420
|
-
const blockRight = b.marginRight ?? 0;
|
|
1421
|
-
const totalWidth = Math.max(env.innerWidth - blockLeft - blockRight, 1);
|
|
1422
|
-
let colWidths;
|
|
1423
|
-
if (b.columnWidths && b.columnWidths.length === colCount) {
|
|
1424
|
-
const safe = b.columnWidths.map((w) => w === void 0 ? "*" : w);
|
|
1425
|
-
colWidths = computeColumnPixelWidths2(safe, totalWidth);
|
|
1426
|
-
} else {
|
|
1427
|
-
colWidths = Array(colCount).fill(totalWidth / colCount);
|
|
2139
|
+
}
|
|
2140
|
+
return src;
|
|
2141
|
+
}
|
|
2142
|
+
async function materializeImagesInBlocks(blocks, fallback) {
|
|
2143
|
+
const out = [];
|
|
2144
|
+
for (const block of blocks) {
|
|
2145
|
+
const blockAny = { ...block };
|
|
2146
|
+
if (blockAny.backgroundImage) {
|
|
2147
|
+
blockAny.backgroundImage = await normalizeImageSrc(blockAny.backgroundImage, fallback);
|
|
1428
2148
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
const baseKeyWidthRatio = 0.35;
|
|
1432
|
-
const measureKVText = (tb, width) => {
|
|
1433
|
-
const r = resolveTextBlock(styles, tb);
|
|
1434
|
-
const fontName = getFontNameForText(r);
|
|
1435
|
-
doc.font(fontName);
|
|
1436
|
-
doc.fontSize(r.fontSize ?? 10);
|
|
1437
|
-
return doc.heightOfString(r.text ?? "", { width });
|
|
1438
|
-
};
|
|
1439
|
-
let totalHeight = mt;
|
|
1440
|
-
if (orientation === "vertical") {
|
|
1441
|
-
const keyValueGap = b.verticalKeyValueGap ?? 2;
|
|
1442
|
-
let maxColHeight = 0;
|
|
1443
|
-
for (let colIndex = 0; colIndex < colCount; colIndex++) {
|
|
1444
|
-
const col = cols[colIndex] ?? [];
|
|
1445
|
-
const colWidth = colWidths[colIndex];
|
|
1446
|
-
let colHeight = 0;
|
|
1447
|
-
for (const item of col) {
|
|
1448
|
-
const keyH = measureKVText({ type: "text", text: item.key ?? "" }, colWidth);
|
|
1449
|
-
const valH = measureKVText({ type: "text", text: item.value ?? "" }, colWidth);
|
|
1450
|
-
colHeight += keyH + keyValueGap + valH + rowGap;
|
|
1451
|
-
}
|
|
1452
|
-
if (colHeight > maxColHeight) maxColHeight = colHeight;
|
|
1453
|
-
}
|
|
1454
|
-
totalHeight += maxColHeight;
|
|
1455
|
-
} else {
|
|
1456
|
-
const maxRows = Math.max(...cols.map((c) => c ? c.length : 0), 0);
|
|
1457
|
-
for (let rowIndex = 0; rowIndex < maxRows; rowIndex++) {
|
|
1458
|
-
let rowHeight = 0;
|
|
1459
|
-
for (let colIndex = 0; colIndex < colCount; colIndex++) {
|
|
1460
|
-
const item = cols[colIndex]?.[rowIndex];
|
|
1461
|
-
if (!item) continue;
|
|
1462
|
-
const colWidth = colWidths[colIndex];
|
|
1463
|
-
const keyWidthPx = b.keyWidth === "*" ? Math.max(colWidth * baseKeyWidthRatio, 20) : b.keyWidth ?? 80;
|
|
1464
|
-
const keyH = measureKVText({ type: "text", text: item.key ?? "" }, keyWidthPx);
|
|
1465
|
-
const valueXInsideCol = keyWidthPx + (separatorText ? sepBoxWidth + 4 : 4);
|
|
1466
|
-
const valueWidth = Math.max(colWidth - valueXInsideCol, 1);
|
|
1467
|
-
const valH = measureKVText({ type: "text", text: item.value ?? "" }, valueWidth);
|
|
1468
|
-
rowHeight = Math.max(rowHeight, Math.max(keyH, valH));
|
|
1469
|
-
}
|
|
1470
|
-
if (rowHeight > 0) totalHeight += rowHeight + rowGap;
|
|
1471
|
-
}
|
|
2149
|
+
if (blockAny.backgroundBlocks?.length) {
|
|
2150
|
+
blockAny.backgroundBlocks = await materializeImagesInBlocks(blockAny.backgroundBlocks, fallback);
|
|
1472
2151
|
}
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
if (
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
2152
|
+
if (block.type === "image") {
|
|
2153
|
+
const img = { ...blockAny };
|
|
2154
|
+
img.src = await normalizeImageSrc(img.src, fallback);
|
|
2155
|
+
out.push(img);
|
|
2156
|
+
} else if (block.type === "columns") {
|
|
2157
|
+
out.push({
|
|
2158
|
+
...blockAny,
|
|
2159
|
+
columns: await Promise.all(
|
|
2160
|
+
block.columns.map((col) => materializeImagesInBlocks(col, fallback))
|
|
2161
|
+
)
|
|
2162
|
+
});
|
|
2163
|
+
} else if (block.type === "signature" && block.blocks) {
|
|
2164
|
+
out.push({
|
|
2165
|
+
...blockAny,
|
|
2166
|
+
blocks: await materializeImagesInBlocks(block.blocks, fallback)
|
|
2167
|
+
});
|
|
2168
|
+
} else if (block.type === "table") {
|
|
2169
|
+
const tb = blockAny;
|
|
2170
|
+
const newBody = await Promise.all(
|
|
2171
|
+
tb.body.map(
|
|
2172
|
+
async (entry) => {
|
|
2173
|
+
const materializeCell = async (cell) => {
|
|
2174
|
+
if (!cell.blocks?.length) return cell;
|
|
2175
|
+
return {
|
|
2176
|
+
...cell,
|
|
2177
|
+
blocks: await materializeImagesInBlocks(cell.blocks, fallback)
|
|
2178
|
+
};
|
|
2179
|
+
};
|
|
2180
|
+
if (Array.isArray(entry)) {
|
|
2181
|
+
return await Promise.all(entry.map(materializeCell));
|
|
2182
|
+
}
|
|
2183
|
+
if (entry && typeof entry === "object" && "content" in entry && Array.isArray(entry.content)) {
|
|
2184
|
+
return {
|
|
2185
|
+
...entry,
|
|
2186
|
+
content: await Promise.all(entry.content.map(materializeCell))
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
return entry;
|
|
2190
|
+
}
|
|
2191
|
+
)
|
|
2192
|
+
);
|
|
2193
|
+
out.push({ ...tb, body: newBody });
|
|
2194
|
+
} else {
|
|
2195
|
+
out.push(blockAny);
|
|
1501
2196
|
}
|
|
1502
|
-
}
|
|
1503
|
-
return
|
|
2197
|
+
}
|
|
2198
|
+
return out;
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
// src/renderer-engine/utils/layout.ts
|
|
2202
|
+
function computeColumnPixelWidths(widths, totalWidth) {
|
|
2203
|
+
let fixedTotal = 0;
|
|
2204
|
+
let starCount = 0;
|
|
2205
|
+
widths.forEach((w) => {
|
|
2206
|
+
if (w === "*") starCount++;
|
|
2207
|
+
else fixedTotal += w;
|
|
2208
|
+
});
|
|
2209
|
+
const remaining = Math.max(totalWidth - fixedTotal, 0);
|
|
2210
|
+
const starWidth = starCount > 0 ? remaining / starCount : 0;
|
|
2211
|
+
return widths.map((w) => w === "*" ? starWidth : w);
|
|
1504
2212
|
}
|
|
1505
2213
|
|
|
1506
2214
|
// src/renderer-engine/utils/page-background.ts
|
|
@@ -1525,12 +2233,10 @@ function drawPageBackground(doc, pageBackground) {
|
|
|
1525
2233
|
doc.opacity(1);
|
|
1526
2234
|
}
|
|
1527
2235
|
|
|
1528
|
-
// src/renderer-engine/utils/page-
|
|
1529
|
-
function
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
return ctx.signatureTopY ?? pageBottomForContent;
|
|
1533
|
-
};
|
|
2236
|
+
// src/renderer-engine/utils/page-flow.ts
|
|
2237
|
+
function getBottomLimitForContent(doc, ctx) {
|
|
2238
|
+
const pageBottomForContent = doc.page.height - doc.page.margins.bottom;
|
|
2239
|
+
return ctx.signatureTopY ?? pageBottomForContent;
|
|
1534
2240
|
}
|
|
1535
2241
|
|
|
1536
2242
|
// src/renderer-engine/utils/qr-bar-code.ts
|
|
@@ -1553,7 +2259,10 @@ function generateQrBuffer(value, size, version, errorCorrectionLevel) {
|
|
|
1553
2259
|
code: "PDF_ERROR_QR_GENERATION_FAILED" /* PDF_ERROR_QR_GENERATION_FAILED */,
|
|
1554
2260
|
message: "Failed to generate QR code.",
|
|
1555
2261
|
statusCode: 500,
|
|
1556
|
-
details: {
|
|
2262
|
+
details: {
|
|
2263
|
+
valuePreview: String(value).slice(0, 60),
|
|
2264
|
+
size
|
|
2265
|
+
},
|
|
1557
2266
|
retryable: false
|
|
1558
2267
|
})
|
|
1559
2268
|
);
|
|
@@ -1580,97 +2289,109 @@ function mapBarcodeTypeToBcid(bcType) {
|
|
|
1580
2289
|
function generateBarcodeBuffer(value, options = {}) {
|
|
1581
2290
|
const { bcType, scale = 3, barHeight = 10, includetext = false, textalign = "center" } = options;
|
|
1582
2291
|
const bcid = mapBarcodeTypeToBcid(bcType);
|
|
2292
|
+
if (bcType === "EAN13" && !/^\d{12,13}$/.test(value)) {
|
|
2293
|
+
throw toPdfEngineError(new Error("Invalid EAN13 value"), {
|
|
2294
|
+
code: "PDF_ERROR_BARCODE_EAN13_INVALID_LENGTH" /* PDF_ERROR_BARCODE_EAN13_INVALID_LENGTH */,
|
|
2295
|
+
message: "EAN13 barcode value must be 12 or 13 digits.",
|
|
2296
|
+
statusCode: 422,
|
|
2297
|
+
details: { value },
|
|
2298
|
+
retryable: false
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
1583
2301
|
return new Promise((resolve, reject) => {
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
};
|
|
1610
|
-
bwipOptions.textxalign = textalign;
|
|
1611
|
-
bwipjs.toBuffer(bwipOptions, (err, png) => {
|
|
1612
|
-
if (err) {
|
|
1613
|
-
return reject(
|
|
1614
|
-
toPdfEngineError(err, {
|
|
1615
|
-
code: "PDF_ERROR_BARCODE_GENERATION_FAILED" /* PDF_ERROR_BARCODE_GENERATION_FAILED */,
|
|
1616
|
-
message: "Failed to generate barcode.",
|
|
1617
|
-
statusCode: 500,
|
|
1618
|
-
details: { bcType, valuePreview: String(value).slice(0, 60) },
|
|
1619
|
-
retryable: false
|
|
1620
|
-
})
|
|
1621
|
-
);
|
|
2302
|
+
bwipjs.toBuffer(
|
|
2303
|
+
{
|
|
2304
|
+
bcid,
|
|
2305
|
+
text: value,
|
|
2306
|
+
scale,
|
|
2307
|
+
height: barHeight,
|
|
2308
|
+
includetext,
|
|
2309
|
+
textxalign: textalign
|
|
2310
|
+
},
|
|
2311
|
+
(err, png) => {
|
|
2312
|
+
if (err) {
|
|
2313
|
+
return reject(
|
|
2314
|
+
toPdfEngineError(err, {
|
|
2315
|
+
code: "PDF_ERROR_BARCODE_GENERATION_FAILED" /* PDF_ERROR_BARCODE_GENERATION_FAILED */,
|
|
2316
|
+
message: "Failed to generate barcode.",
|
|
2317
|
+
statusCode: 500,
|
|
2318
|
+
details: {
|
|
2319
|
+
valuePreview: String(value).slice(0, 60),
|
|
2320
|
+
bcType
|
|
2321
|
+
},
|
|
2322
|
+
retryable: false
|
|
2323
|
+
})
|
|
2324
|
+
);
|
|
2325
|
+
}
|
|
2326
|
+
resolve(png);
|
|
1622
2327
|
}
|
|
1623
|
-
|
|
1624
|
-
});
|
|
2328
|
+
);
|
|
1625
2329
|
});
|
|
1626
2330
|
}
|
|
1627
2331
|
async function materializeQrAndBarcodesInBlocks(blocks) {
|
|
1628
2332
|
const out = [];
|
|
1629
2333
|
for (const block of blocks) {
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
out.push({
|
|
1637
|
-
...colBlock,
|
|
1638
|
-
columns: newCols
|
|
1639
|
-
});
|
|
1640
|
-
} else if (block.type === "signature") {
|
|
1641
|
-
const sig = block;
|
|
1642
|
-
if (sig.blocks && sig.blocks.length) {
|
|
1643
|
-
const newInner = await materializeQrAndBarcodesInBlocks(sig.blocks);
|
|
1644
|
-
out.push({
|
|
1645
|
-
...sig,
|
|
1646
|
-
blocks: newInner
|
|
1647
|
-
});
|
|
1648
|
-
} else {
|
|
1649
|
-
out.push(block);
|
|
1650
|
-
}
|
|
1651
|
-
} else if (block.type === "qr") {
|
|
1652
|
-
const qb = { ...block };
|
|
2334
|
+
const blockAny = { ...block };
|
|
2335
|
+
if (blockAny.backgroundBlocks?.length) {
|
|
2336
|
+
blockAny.backgroundBlocks = await materializeQrAndBarcodesInBlocks(blockAny.backgroundBlocks);
|
|
2337
|
+
}
|
|
2338
|
+
if (block.type === "qr") {
|
|
2339
|
+
const qb = blockAny;
|
|
1653
2340
|
if (!qb.src && qb.value) {
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
qb.src = buf;
|
|
2341
|
+
qb.size = qb.size ?? 80;
|
|
2342
|
+
qb.src = await generateQrBuffer(qb.value, qb.size, qb.qrVersion, qb.errorCorrectionLevel);
|
|
1657
2343
|
}
|
|
1658
2344
|
out.push(qb);
|
|
1659
2345
|
} else if (block.type === "barcode") {
|
|
1660
|
-
const bb =
|
|
2346
|
+
const bb = blockAny;
|
|
1661
2347
|
if (!bb.src && bb.value) {
|
|
1662
|
-
|
|
2348
|
+
bb.src = await generateBarcodeBuffer(bb.value, {
|
|
1663
2349
|
bcType: bb.bcType,
|
|
1664
2350
|
scale: bb.scale,
|
|
1665
2351
|
barHeight: bb.barHeight,
|
|
1666
2352
|
includetext: bb.includetext,
|
|
1667
2353
|
textalign: bb.textalign
|
|
1668
2354
|
});
|
|
1669
|
-
bb.src = buf;
|
|
1670
2355
|
}
|
|
1671
2356
|
out.push(bb);
|
|
2357
|
+
} else if (block.type === "columns") {
|
|
2358
|
+
out.push({
|
|
2359
|
+
...blockAny,
|
|
2360
|
+
columns: await Promise.all(block.columns.map((col) => materializeQrAndBarcodesInBlocks(col)))
|
|
2361
|
+
});
|
|
2362
|
+
} else if (block.type === "signature" && block.blocks) {
|
|
2363
|
+
out.push({
|
|
2364
|
+
...blockAny,
|
|
2365
|
+
blocks: await materializeQrAndBarcodesInBlocks(block.blocks)
|
|
2366
|
+
});
|
|
2367
|
+
} else if (block.type === "table") {
|
|
2368
|
+
const tb = blockAny;
|
|
2369
|
+
const newBody = await Promise.all(
|
|
2370
|
+
tb.body.map(
|
|
2371
|
+
async (entry) => {
|
|
2372
|
+
const materializeCell = async (cell) => {
|
|
2373
|
+
if (!cell.blocks?.length) return cell;
|
|
2374
|
+
return {
|
|
2375
|
+
...cell,
|
|
2376
|
+
blocks: await materializeQrAndBarcodesInBlocks(cell.blocks)
|
|
2377
|
+
};
|
|
2378
|
+
};
|
|
2379
|
+
if (Array.isArray(entry)) {
|
|
2380
|
+
return await Promise.all(entry.map(materializeCell));
|
|
2381
|
+
}
|
|
2382
|
+
if (entry && typeof entry === "object" && "content" in entry && Array.isArray(entry.content)) {
|
|
2383
|
+
return {
|
|
2384
|
+
...entry,
|
|
2385
|
+
content: await Promise.all(entry.content.map(materializeCell))
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
return entry;
|
|
2389
|
+
}
|
|
2390
|
+
)
|
|
2391
|
+
);
|
|
2392
|
+
out.push({ ...tb, body: newBody });
|
|
1672
2393
|
} else {
|
|
1673
|
-
out.push(
|
|
2394
|
+
out.push(blockAny);
|
|
1674
2395
|
}
|
|
1675
2396
|
}
|
|
1676
2397
|
return out;
|
|
@@ -1924,96 +2645,143 @@ function drawWatermarkForPage(doc, watermark, pageNumber, isLast) {
|
|
|
1924
2645
|
}
|
|
1925
2646
|
|
|
1926
2647
|
// src/renderer-engine/index.ts
|
|
1927
|
-
|
|
1928
|
-
|
|
2648
|
+
var BUILT_IN_DEFAULT_IMAGE = Buffer.from(images.default.slice(images.default.indexOf(",") + 1), "base64");
|
|
2649
|
+
async function materializeFooterAssets(footer, fallbackImage) {
|
|
2650
|
+
if (!footer) return footer;
|
|
2651
|
+
if (typeof footer === "function") {
|
|
2652
|
+
return footer;
|
|
2653
|
+
}
|
|
2654
|
+
if (Array.isArray(footer)) {
|
|
2655
|
+
let blocks = await materializeQrAndBarcodesInBlocks(footer);
|
|
2656
|
+
blocks = await materializeImagesInBlocks(blocks, fallbackImage);
|
|
2657
|
+
return blocks;
|
|
2658
|
+
}
|
|
2659
|
+
if (footer.type) {
|
|
2660
|
+
let blocks = await materializeQrAndBarcodesInBlocks([footer]);
|
|
2661
|
+
blocks = await materializeImagesInBlocks(blocks, fallbackImage);
|
|
2662
|
+
return blocks[0];
|
|
2663
|
+
}
|
|
2664
|
+
const footerDef = footer;
|
|
2665
|
+
if (footerDef.backgroundImage) {
|
|
2666
|
+
footerDef.backgroundImage = await normalizeImageSrc(footerDef.backgroundImage, fallbackImage);
|
|
2667
|
+
}
|
|
2668
|
+
if (footerDef.blocks?.length) {
|
|
2669
|
+
footerDef.blocks = await materializeQrAndBarcodesInBlocks(footerDef.blocks);
|
|
2670
|
+
footerDef.blocks = await materializeImagesInBlocks(footerDef.blocks, fallbackImage);
|
|
2671
|
+
}
|
|
2672
|
+
if (footerDef.backgroundBlocks?.length) {
|
|
2673
|
+
footerDef.backgroundBlocks = await materializeQrAndBarcodesInBlocks(footerDef.backgroundBlocks);
|
|
2674
|
+
footerDef.backgroundBlocks = await materializeImagesInBlocks(footerDef.backgroundBlocks, fallbackImage);
|
|
2675
|
+
}
|
|
2676
|
+
return footerDef;
|
|
2677
|
+
}
|
|
2678
|
+
async function materializeDocAssets(def, fallbackImage) {
|
|
2679
|
+
if (def.header?.blocks?.length) {
|
|
1929
2680
|
def.header.blocks = await materializeQrAndBarcodesInBlocks(def.header.blocks);
|
|
1930
|
-
def.header.blocks = await materializeImagesInBlocks(def.header.blocks);
|
|
2681
|
+
def.header.blocks = await materializeImagesInBlocks(def.header.blocks, fallbackImage);
|
|
2682
|
+
}
|
|
2683
|
+
if (def.header?.backgroundImage) {
|
|
2684
|
+
def.header.backgroundImage = await normalizeImageSrc(def.header.backgroundImage, fallbackImage);
|
|
2685
|
+
}
|
|
2686
|
+
if (def.header?.backgroundBlocks?.length) {
|
|
2687
|
+
def.header.backgroundBlocks = await materializeQrAndBarcodesInBlocks(def.header.backgroundBlocks);
|
|
2688
|
+
def.header.backgroundBlocks = await materializeImagesInBlocks(def.header.backgroundBlocks, fallbackImage);
|
|
1931
2689
|
}
|
|
1932
2690
|
def.content = await materializeQrAndBarcodesInBlocks(def.content);
|
|
1933
|
-
def.content = await materializeImagesInBlocks(def.content);
|
|
2691
|
+
def.content = await materializeImagesInBlocks(def.content, fallbackImage);
|
|
1934
2692
|
if (def.pageBackground?.src) {
|
|
1935
|
-
def.pageBackground.src = await normalizeImageSrc(def.pageBackground.src);
|
|
2693
|
+
def.pageBackground.src = await normalizeImageSrc(def.pageBackground.src, fallbackImage);
|
|
1936
2694
|
}
|
|
1937
|
-
|
|
1938
|
-
|
|
2695
|
+
def.footer = await materializeFooterAssets(def.footer, fallbackImage);
|
|
2696
|
+
}
|
|
2697
|
+
function runRender(doc, def, fallbackImage) {
|
|
2698
|
+
const headerBandHeight = def.margins.top ?? 0;
|
|
2699
|
+
const footerBandHeight = def.margins.bottom ?? 0;
|
|
2700
|
+
if (def.fonts && def.fonts.length) {
|
|
2701
|
+
for (const font of def.fonts) {
|
|
2702
|
+
try {
|
|
2703
|
+
doc.registerFont(font.name, font.src);
|
|
2704
|
+
} catch (e) {
|
|
2705
|
+
console.warn("Failed to register font:", font.name, e);
|
|
2706
|
+
}
|
|
2707
|
+
}
|
|
1939
2708
|
}
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
2709
|
+
const ctx = createInitialContext(doc);
|
|
2710
|
+
const styles = def.styles ?? {};
|
|
2711
|
+
const finishPage2 = (addNewPage) => finishPage({
|
|
2712
|
+
addNewPage,
|
|
2713
|
+
doc,
|
|
2714
|
+
def,
|
|
2715
|
+
ctx,
|
|
2716
|
+
footerBandHeight,
|
|
2717
|
+
renderBlockArray,
|
|
2718
|
+
startNewPageLayout
|
|
2719
|
+
});
|
|
2720
|
+
const processSignatureBlock = createProcessSignatureBlock({
|
|
2721
|
+
doc,
|
|
2722
|
+
ctx,
|
|
2723
|
+
styles,
|
|
2724
|
+
finishPage: finishPage2,
|
|
2725
|
+
contentEnvFn: contentEnv
|
|
2726
|
+
});
|
|
2727
|
+
const { renderBlock, renderBlockArray } = createBlockRenderer({
|
|
2728
|
+
doc,
|
|
2729
|
+
ctx,
|
|
2730
|
+
styles,
|
|
2731
|
+
computeColumnPixelWidths,
|
|
2732
|
+
finishPage: finishPage2,
|
|
2733
|
+
processSignatureBlock,
|
|
2734
|
+
defaultImage: fallbackImage
|
|
2735
|
+
});
|
|
2736
|
+
const startNewPageLayout = createStartNewPageLayout({
|
|
2737
|
+
doc,
|
|
2738
|
+
def,
|
|
2739
|
+
ctx,
|
|
2740
|
+
headerBandHeight,
|
|
2741
|
+
renderBlockArray
|
|
2742
|
+
});
|
|
2743
|
+
doc.on("pageAdded", () => {
|
|
2744
|
+
if (ctx.inManualPageAdd) return;
|
|
2745
|
+
ctx.pageNumber += 1;
|
|
2746
|
+
drawPageBackground(doc, def.pageBackground);
|
|
2747
|
+
drawHeader(doc, def, headerBandHeight, def.header ?? void 0, renderBlockArray);
|
|
2748
|
+
const mode = def.watermark?.mode;
|
|
2749
|
+
if (def.watermark && !watermarkUsesLast(mode)) {
|
|
2750
|
+
drawWatermarkForPage(doc, def.watermark, ctx.pageNumber, false);
|
|
2751
|
+
}
|
|
2752
|
+
ctx.currentY = doc.page.margins.top;
|
|
2753
|
+
});
|
|
2754
|
+
startNewPageLayout();
|
|
2755
|
+
for (const block of def.content) {
|
|
2756
|
+
if (block.type === "signature") {
|
|
2757
|
+
processSignatureBlock(block);
|
|
2758
|
+
continue;
|
|
2759
|
+
}
|
|
2760
|
+
if (ctx.afterSignature) {
|
|
2761
|
+
finishPage2(true);
|
|
2762
|
+
ctx.afterSignature = false;
|
|
1944
2763
|
}
|
|
2764
|
+
renderBlock(block, null, contentEnv(doc));
|
|
1945
2765
|
}
|
|
2766
|
+
finishPage2(false);
|
|
2767
|
+
doc.end();
|
|
2768
|
+
}
|
|
2769
|
+
async function renderCustomPdf(def, outputPath) {
|
|
2770
|
+
const fallbackImage = def.defaultImage ?? BUILT_IN_DEFAULT_IMAGE;
|
|
2771
|
+
await materializeDocAssets(def, fallbackImage);
|
|
1946
2772
|
return new Promise((resolve, reject) => {
|
|
1947
|
-
const headerBandHeight = def.margins.top ?? 0;
|
|
1948
|
-
const footerBandHeight = def.margins.bottom ?? 0;
|
|
1949
2773
|
const doc = new PDFDocument({
|
|
1950
2774
|
size: def.pageSize || "A4",
|
|
1951
2775
|
layout: def.pageOrientation === "landscape" ? "landscape" : "portrait",
|
|
1952
2776
|
margins: {
|
|
1953
|
-
top:
|
|
1954
|
-
bottom:
|
|
2777
|
+
top: def.margins.top ?? 0,
|
|
2778
|
+
bottom: def.margins.bottom ?? 0,
|
|
1955
2779
|
left: def.margins.left,
|
|
1956
2780
|
right: def.margins.right
|
|
1957
2781
|
}
|
|
1958
2782
|
});
|
|
1959
|
-
if (def.fonts && def.fonts.length) {
|
|
1960
|
-
for (const f of def.fonts) {
|
|
1961
|
-
try {
|
|
1962
|
-
doc.registerFont(f.name, f.src);
|
|
1963
|
-
} catch (e) {
|
|
1964
|
-
console.warn("Failed to register font:", f.name, e);
|
|
1965
|
-
}
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
2783
|
const stream = fs.createWriteStream(outputPath);
|
|
1969
2784
|
doc.pipe(stream);
|
|
1970
|
-
const ctx = createInitialContext(doc);
|
|
1971
|
-
const styles = def.styles ?? {};
|
|
1972
|
-
const finishPage2 = (addNewPage) => finishPage({
|
|
1973
|
-
addNewPage,
|
|
1974
|
-
doc,
|
|
1975
|
-
def,
|
|
1976
|
-
ctx,
|
|
1977
|
-
footerBandHeight,
|
|
1978
|
-
renderBlockArray,
|
|
1979
|
-
startNewPageLayout
|
|
1980
|
-
});
|
|
1981
|
-
const processSignatureBlock = createProcessSignatureBlock({
|
|
1982
|
-
doc,
|
|
1983
|
-
ctx,
|
|
1984
|
-
styles,
|
|
1985
|
-
finishPage: finishPage2,
|
|
1986
|
-
contentEnvFn: contentEnv
|
|
1987
|
-
});
|
|
1988
|
-
const { renderBlock, renderBlockArray } = createBlockRenderer({
|
|
1989
|
-
doc,
|
|
1990
|
-
ctx,
|
|
1991
|
-
styles,
|
|
1992
|
-
computeColumnPixelWidths,
|
|
1993
|
-
finishPage: finishPage2,
|
|
1994
|
-
processSignatureBlock
|
|
1995
|
-
});
|
|
1996
|
-
const startNewPageLayout = createStartNewPageLayout({
|
|
1997
|
-
doc,
|
|
1998
|
-
def,
|
|
1999
|
-
ctx,
|
|
2000
|
-
headerBandHeight,
|
|
2001
|
-
renderBlockArray
|
|
2002
|
-
});
|
|
2003
|
-
startNewPageLayout();
|
|
2004
|
-
for (const block of def.content) {
|
|
2005
|
-
if (block.type === "signature") {
|
|
2006
|
-
processSignatureBlock(block);
|
|
2007
|
-
continue;
|
|
2008
|
-
}
|
|
2009
|
-
if (ctx.afterSignature) {
|
|
2010
|
-
finishPage2(true);
|
|
2011
|
-
ctx.afterSignature = false;
|
|
2012
|
-
}
|
|
2013
|
-
renderBlock(block, null, contentEnv(doc));
|
|
2014
|
-
}
|
|
2015
|
-
finishPage2(false);
|
|
2016
|
-
doc.end();
|
|
2017
2785
|
stream.on("finish", () => resolve());
|
|
2018
2786
|
stream.on(
|
|
2019
2787
|
"error",
|
|
@@ -2027,113 +2795,38 @@ async function renderCustomPdf(def, outputPath) {
|
|
|
2027
2795
|
})
|
|
2028
2796
|
)
|
|
2029
2797
|
);
|
|
2798
|
+
runRender(doc, def, fallbackImage);
|
|
2030
2799
|
});
|
|
2031
2800
|
}
|
|
2032
2801
|
async function renderCustomPdfToBuffer(def) {
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
def.header.blocks = await materializeImagesInBlocks(def.header.blocks);
|
|
2036
|
-
}
|
|
2037
|
-
def.content = await materializeQrAndBarcodesInBlocks(def.content);
|
|
2038
|
-
def.content = await materializeImagesInBlocks(def.content);
|
|
2039
|
-
if (def.pageBackground?.src) {
|
|
2040
|
-
def.pageBackground.src = await normalizeImageSrc(def.pageBackground.src);
|
|
2041
|
-
}
|
|
2042
|
-
if (def.header?.backgroundImage) {
|
|
2043
|
-
def.header.backgroundImage = await normalizeImageSrc(def.header.backgroundImage);
|
|
2044
|
-
}
|
|
2045
|
-
if (def.footer && typeof def.footer !== "function") {
|
|
2046
|
-
const footer = def.footer;
|
|
2047
|
-
if (footer.backgroundImage) {
|
|
2048
|
-
footer.backgroundImage = await normalizeImageSrc(footer.backgroundImage);
|
|
2049
|
-
}
|
|
2050
|
-
}
|
|
2802
|
+
const fallbackImage = def.defaultImage ?? BUILT_IN_DEFAULT_IMAGE;
|
|
2803
|
+
await materializeDocAssets(def, fallbackImage);
|
|
2051
2804
|
return new Promise((resolve, reject) => {
|
|
2052
|
-
const headerBandHeight = def.margins.top ?? 0;
|
|
2053
|
-
const footerBandHeight = def.margins.bottom ?? 0;
|
|
2054
2805
|
const doc = new PDFDocument({
|
|
2055
2806
|
size: def.pageSize || "A4",
|
|
2056
2807
|
layout: def.pageOrientation === "landscape" ? "landscape" : "portrait",
|
|
2057
2808
|
margins: {
|
|
2058
|
-
top:
|
|
2059
|
-
bottom:
|
|
2809
|
+
top: def.margins.top ?? 0,
|
|
2810
|
+
bottom: def.margins.bottom ?? 0,
|
|
2060
2811
|
left: def.margins.left,
|
|
2061
2812
|
right: def.margins.right
|
|
2062
2813
|
}
|
|
2063
2814
|
});
|
|
2064
2815
|
const chunks = [];
|
|
2065
|
-
doc.on("data", (chunk) =>
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
});
|
|
2071
|
-
doc.on("error", (err) => {
|
|
2072
|
-
reject(
|
|
2816
|
+
doc.on("data", (chunk) => chunks.push(chunk));
|
|
2817
|
+
doc.on("end", () => resolve(Buffer.concat(chunks)));
|
|
2818
|
+
doc.on(
|
|
2819
|
+
"error",
|
|
2820
|
+
(err) => reject(
|
|
2073
2821
|
toPdfEngineError(err, {
|
|
2074
2822
|
code: "PDF_ERROR_PDFKIT_ERROR" /* PDF_ERROR_PDFKIT_ERROR */,
|
|
2075
2823
|
message: "PDFKit emitted an error while rendering.",
|
|
2076
2824
|
statusCode: 500,
|
|
2077
2825
|
retryable: true
|
|
2078
2826
|
})
|
|
2079
|
-
)
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
for (const f of def.fonts) {
|
|
2083
|
-
try {
|
|
2084
|
-
doc.registerFont(f.name, f.src);
|
|
2085
|
-
} catch (e) {
|
|
2086
|
-
console.warn("Failed to register font:", f.name, e);
|
|
2087
|
-
}
|
|
2088
|
-
}
|
|
2089
|
-
}
|
|
2090
|
-
const ctx = createInitialContext(doc);
|
|
2091
|
-
const styles = def.styles ?? {};
|
|
2092
|
-
const finishPage2 = (addNewPage) => finishPage({
|
|
2093
|
-
addNewPage,
|
|
2094
|
-
doc,
|
|
2095
|
-
def,
|
|
2096
|
-
ctx,
|
|
2097
|
-
footerBandHeight,
|
|
2098
|
-
renderBlockArray,
|
|
2099
|
-
startNewPageLayout
|
|
2100
|
-
});
|
|
2101
|
-
const processSignatureBlock = createProcessSignatureBlock({
|
|
2102
|
-
doc,
|
|
2103
|
-
ctx,
|
|
2104
|
-
styles,
|
|
2105
|
-
finishPage: finishPage2,
|
|
2106
|
-
contentEnvFn: contentEnv
|
|
2107
|
-
});
|
|
2108
|
-
const { renderBlock, renderBlockArray } = createBlockRenderer({
|
|
2109
|
-
doc,
|
|
2110
|
-
ctx,
|
|
2111
|
-
styles,
|
|
2112
|
-
computeColumnPixelWidths,
|
|
2113
|
-
finishPage: finishPage2,
|
|
2114
|
-
processSignatureBlock
|
|
2115
|
-
});
|
|
2116
|
-
const startNewPageLayout = createStartNewPageLayout({
|
|
2117
|
-
doc,
|
|
2118
|
-
def,
|
|
2119
|
-
ctx,
|
|
2120
|
-
headerBandHeight,
|
|
2121
|
-
renderBlockArray
|
|
2122
|
-
});
|
|
2123
|
-
startNewPageLayout();
|
|
2124
|
-
for (const block of def.content) {
|
|
2125
|
-
if (block.type === "signature") {
|
|
2126
|
-
processSignatureBlock(block);
|
|
2127
|
-
continue;
|
|
2128
|
-
}
|
|
2129
|
-
if (ctx.afterSignature) {
|
|
2130
|
-
finishPage2(true);
|
|
2131
|
-
ctx.afterSignature = false;
|
|
2132
|
-
}
|
|
2133
|
-
renderBlock(block, null, contentEnv(doc));
|
|
2134
|
-
}
|
|
2135
|
-
finishPage2(false);
|
|
2136
|
-
doc.end();
|
|
2827
|
+
)
|
|
2828
|
+
);
|
|
2829
|
+
runRender(doc, def, fallbackImage);
|
|
2137
2830
|
});
|
|
2138
2831
|
}
|
|
2139
2832
|
export {
|
|
@@ -2141,8 +2834,42 @@ export {
|
|
|
2141
2834
|
PdfEngineError,
|
|
2142
2835
|
PdfEngineErrorCode,
|
|
2143
2836
|
QR_ERROR_LEVEL,
|
|
2837
|
+
computeColumnPixelWidths,
|
|
2838
|
+
contentEnv,
|
|
2839
|
+
createBlockRenderer,
|
|
2840
|
+
createBottomLimitForContent,
|
|
2841
|
+
createEnsureSpaceFor,
|
|
2842
|
+
createInitialContext,
|
|
2843
|
+
createMeasureBlockHeight,
|
|
2844
|
+
createProcessSignatureBlock,
|
|
2845
|
+
createStartNewPageLayout,
|
|
2846
|
+
drawFooter,
|
|
2847
|
+
drawHeader,
|
|
2848
|
+
drawPageBackground,
|
|
2849
|
+
drawSignatureBlock,
|
|
2850
|
+
drawStyledText,
|
|
2851
|
+
drawWatermarkForPage,
|
|
2852
|
+
finishPage,
|
|
2853
|
+
generateBarcodeBuffer,
|
|
2854
|
+
generateQrBuffer,
|
|
2855
|
+
getBottomLimitForContent,
|
|
2856
|
+
getFontNameForText,
|
|
2857
|
+
hasPadding,
|
|
2858
|
+
images,
|
|
2144
2859
|
isPdfEngineError,
|
|
2860
|
+
mapBarcodeTypeToBcid,
|
|
2861
|
+
materializeImagesInBlocks,
|
|
2862
|
+
materializeQrAndBarcodesInBlocks,
|
|
2863
|
+
mergeStyleDefs,
|
|
2864
|
+
normalizeImageSrc,
|
|
2145
2865
|
renderCustomPdf,
|
|
2146
2866
|
renderCustomPdfToBuffer,
|
|
2147
|
-
|
|
2867
|
+
resolveBlockPadding,
|
|
2868
|
+
resolveFooterPadding,
|
|
2869
|
+
resolveHeaderPadding,
|
|
2870
|
+
resolveSpacing,
|
|
2871
|
+
resolveTableCell,
|
|
2872
|
+
resolveTextBlock,
|
|
2873
|
+
toPdfEngineError,
|
|
2874
|
+
watermarkUsesLast
|
|
2148
2875
|
};
|