av6-pdf-engine 2.0.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 +245 -23
- package/dist/index.d.ts +245 -23
- package/dist/index.js +1325 -813
- package/dist/index.mjs +1291 -812
- package/package.json +6 -1
- package/.prettierignore +0 -4
- package/.prettierrc +0 -6
- package/tmp/border-showcase.pdf +0 -0
- package/tmp/quote-details.pdf +0 -0
package/dist/index.js
CHANGED
|
@@ -34,11 +34,44 @@ __export(index_exports, {
|
|
|
34
34
|
PdfEngineError: () => PdfEngineError,
|
|
35
35
|
PdfEngineErrorCode: () => PdfEngineErrorCode,
|
|
36
36
|
QR_ERROR_LEVEL: () => QR_ERROR_LEVEL,
|
|
37
|
+
computeColumnPixelWidths: () => computeColumnPixelWidths,
|
|
38
|
+
contentEnv: () => contentEnv,
|
|
39
|
+
createBlockRenderer: () => createBlockRenderer,
|
|
40
|
+
createBottomLimitForContent: () => createBottomLimitForContent,
|
|
41
|
+
createEnsureSpaceFor: () => createEnsureSpaceFor,
|
|
42
|
+
createInitialContext: () => createInitialContext,
|
|
43
|
+
createMeasureBlockHeight: () => createMeasureBlockHeight,
|
|
44
|
+
createProcessSignatureBlock: () => createProcessSignatureBlock,
|
|
45
|
+
createStartNewPageLayout: () => createStartNewPageLayout,
|
|
46
|
+
drawFooter: () => drawFooter,
|
|
47
|
+
drawHeader: () => drawHeader,
|
|
48
|
+
drawPageBackground: () => drawPageBackground,
|
|
49
|
+
drawSignatureBlock: () => drawSignatureBlock,
|
|
50
|
+
drawStyledText: () => drawStyledText,
|
|
51
|
+
drawWatermarkForPage: () => drawWatermarkForPage,
|
|
52
|
+
finishPage: () => finishPage,
|
|
53
|
+
generateBarcodeBuffer: () => generateBarcodeBuffer,
|
|
54
|
+
generateQrBuffer: () => generateQrBuffer,
|
|
55
|
+
getBottomLimitForContent: () => getBottomLimitForContent,
|
|
56
|
+
getFontNameForText: () => getFontNameForText,
|
|
57
|
+
hasPadding: () => hasPadding,
|
|
37
58
|
images: () => images,
|
|
38
59
|
isPdfEngineError: () => isPdfEngineError,
|
|
60
|
+
mapBarcodeTypeToBcid: () => mapBarcodeTypeToBcid,
|
|
61
|
+
materializeImagesInBlocks: () => materializeImagesInBlocks,
|
|
62
|
+
materializeQrAndBarcodesInBlocks: () => materializeQrAndBarcodesInBlocks,
|
|
63
|
+
mergeStyleDefs: () => mergeStyleDefs,
|
|
64
|
+
normalizeImageSrc: () => normalizeImageSrc,
|
|
39
65
|
renderCustomPdf: () => renderCustomPdf,
|
|
40
66
|
renderCustomPdfToBuffer: () => renderCustomPdfToBuffer,
|
|
41
|
-
|
|
67
|
+
resolveBlockPadding: () => resolveBlockPadding,
|
|
68
|
+
resolveFooterPadding: () => resolveFooterPadding,
|
|
69
|
+
resolveHeaderPadding: () => resolveHeaderPadding,
|
|
70
|
+
resolveSpacing: () => resolveSpacing,
|
|
71
|
+
resolveTableCell: () => resolveTableCell,
|
|
72
|
+
resolveTextBlock: () => resolveTextBlock,
|
|
73
|
+
toPdfEngineError: () => toPdfEngineError,
|
|
74
|
+
watermarkUsesLast: () => watermarkUsesLast
|
|
42
75
|
});
|
|
43
76
|
module.exports = __toCommonJS(index_exports);
|
|
44
77
|
|
|
@@ -267,12 +300,11 @@ var processImageBlock = (doc, ctx, block, y, env, ensureSpaceFor, defaultImage)
|
|
|
267
300
|
const availableWidth = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
268
301
|
const mt = block.marginTop ?? 0;
|
|
269
302
|
const mb = block.marginBottom ?? 0;
|
|
270
|
-
const baseY = y ?? ctx.currentY;
|
|
271
|
-
const startY = baseY + mt;
|
|
272
303
|
const heightNeeded = mt + imgHeight + mb;
|
|
273
304
|
if (y === null) {
|
|
274
305
|
ensureSpaceFor(heightNeeded, env);
|
|
275
306
|
}
|
|
307
|
+
const startY = (y ?? ctx.currentY) + mt;
|
|
276
308
|
let x = baseLeft;
|
|
277
309
|
if (block.align === "center") {
|
|
278
310
|
x = baseLeft + (availableWidth - imgWidth) / 2;
|
|
@@ -348,7 +380,7 @@ var processKeyValueGridBlock = (doc, ctx, styles, block, y, env, computeColumnPi
|
|
|
348
380
|
...base,
|
|
349
381
|
style: styleNames
|
|
350
382
|
});
|
|
351
|
-
const
|
|
383
|
+
const measureTextHeight2 = (tb, width) => {
|
|
352
384
|
const fontName = getFontNameForText(tb);
|
|
353
385
|
doc.font(fontName);
|
|
354
386
|
doc.fontSize(tb.fontSize ?? 10);
|
|
@@ -381,10 +413,10 @@ var processKeyValueGridBlock = (doc, ctx, styles, block, y, env, computeColumnPi
|
|
|
381
413
|
const valueStyleNames = item.valueStyle ?? block.valueStyle ?? void 0;
|
|
382
414
|
const keyBase = buildKeyBlock(item);
|
|
383
415
|
const keyResolved = resolveStyled(keyBase, keyStyleNames);
|
|
384
|
-
const keyHeight =
|
|
416
|
+
const keyHeight = measureTextHeight2(keyResolved, colWidth);
|
|
385
417
|
const valueBase = buildValueBlock(item);
|
|
386
418
|
const valueResolved = resolveStyled(valueBase, valueStyleNames);
|
|
387
|
-
const valueHeight =
|
|
419
|
+
const valueHeight = measureTextHeight2(valueResolved, colWidth);
|
|
388
420
|
const rowHeight = keyHeight + keyValueGap + valueHeight;
|
|
389
421
|
colHeight += rowHeight + rowGap;
|
|
390
422
|
}
|
|
@@ -404,12 +436,12 @@ var processKeyValueGridBlock = (doc, ctx, styles, block, y, env, computeColumnPi
|
|
|
404
436
|
const valueStyleNames = item.valueStyle ?? block.valueStyle ?? void 0;
|
|
405
437
|
const keyBase = buildKeyBlock(item);
|
|
406
438
|
const keyResolved = resolveStyled(keyBase, keyStyleNames);
|
|
407
|
-
const keyHeight =
|
|
439
|
+
const keyHeight = measureTextHeight2(keyResolved, keyWidthPx);
|
|
408
440
|
const valueBase = buildValueBlock(item);
|
|
409
441
|
const valueResolved = resolveStyled(valueBase, valueStyleNames);
|
|
410
442
|
const valueXInsideCol = keyWidthPx + (separatorText ? sepBoxWidth + 4 : 4);
|
|
411
443
|
const valueWidth = Math.max(colWidth - valueXInsideCol, 1);
|
|
412
|
-
const valueHeight =
|
|
444
|
+
const valueHeight = measureTextHeight2(valueResolved, valueWidth);
|
|
413
445
|
const cellHeight = Math.max(keyHeight, valueHeight);
|
|
414
446
|
if (cellHeight > rowHeight) rowHeight = cellHeight;
|
|
415
447
|
}
|
|
@@ -437,12 +469,12 @@ var processKeyValueGridBlock = (doc, ctx, styles, block, y, env, computeColumnPi
|
|
|
437
469
|
const valueStyleNames = item.valueStyle ?? block.valueStyle ?? void 0;
|
|
438
470
|
const keyBlock = buildKeyBlock(item);
|
|
439
471
|
const keyResolved = resolveStyled(keyBlock, keyStyleNames);
|
|
440
|
-
const keyHeight =
|
|
472
|
+
const keyHeight = measureTextHeight2(keyResolved, colWidth);
|
|
441
473
|
drawStyledText(doc, keyResolved, colX, colY, colWidth);
|
|
442
474
|
colY += keyHeight + keyValueGap;
|
|
443
475
|
const valueBlock = buildValueBlock(item);
|
|
444
476
|
const valueResolved = resolveStyled(valueBlock, valueStyleNames);
|
|
445
|
-
const valueHeight =
|
|
477
|
+
const valueHeight = measureTextHeight2(valueResolved, colWidth);
|
|
446
478
|
drawStyledText(doc, valueResolved, colX, colY, colWidth);
|
|
447
479
|
colY += valueHeight + rowGap;
|
|
448
480
|
}
|
|
@@ -462,12 +494,12 @@ var processKeyValueGridBlock = (doc, ctx, styles, block, y, env, computeColumnPi
|
|
|
462
494
|
const valueStyleNames = item.valueStyle ?? block.valueStyle ?? void 0;
|
|
463
495
|
const keyBase = buildKeyBlock(item);
|
|
464
496
|
const keyResolved = resolveStyled(keyBase, keyStyleNames);
|
|
465
|
-
const keyHeight =
|
|
497
|
+
const keyHeight = measureTextHeight2(keyResolved, keyWidthPx);
|
|
466
498
|
const valueBase = buildValueBlock(item);
|
|
467
499
|
const valueResolved = resolveStyled(valueBase, valueStyleNames);
|
|
468
500
|
const valueXInsideCol = keyWidthPx + (separatorText ? sepBoxWidth + 4 : 4);
|
|
469
501
|
const valueWidth = Math.max(colWidth - valueXInsideCol, 1);
|
|
470
|
-
const valueHeight =
|
|
502
|
+
const valueHeight = measureTextHeight2(valueResolved, valueWidth);
|
|
471
503
|
const cellHeight = Math.max(keyHeight, valueHeight);
|
|
472
504
|
if (cellHeight > rowHeight) rowHeight = cellHeight;
|
|
473
505
|
}
|
|
@@ -508,6 +540,149 @@ var processKeyValueGridBlock = (doc, ctx, styles, block, y, env, computeColumnPi
|
|
|
508
540
|
return endY;
|
|
509
541
|
};
|
|
510
542
|
|
|
543
|
+
// src/renderer-engine/blocks/list.ts
|
|
544
|
+
var DEFAULT_BULLET = "\u2022";
|
|
545
|
+
var DEFAULT_MARKER_GAP = 6;
|
|
546
|
+
var DEFAULT_ITEM_GAP = 4;
|
|
547
|
+
var MARKER_PADDING = 2;
|
|
548
|
+
var normalizeListItem = (item) => typeof item === "string" ? { text: item } : item;
|
|
549
|
+
var buildTextBlock = (list, item) => ({
|
|
550
|
+
type: "text",
|
|
551
|
+
text: item.text ?? "",
|
|
552
|
+
fontSize: item.fontSize ?? list.fontSize,
|
|
553
|
+
lineGap: item.lineGap ?? list.lineGap,
|
|
554
|
+
color: item.color ?? list.color,
|
|
555
|
+
bold: item.bold,
|
|
556
|
+
italic: item.italic,
|
|
557
|
+
underline: item.underline,
|
|
558
|
+
strike: item.strike,
|
|
559
|
+
link: item.link,
|
|
560
|
+
font: item.font,
|
|
561
|
+
style: item.style ?? list.style
|
|
562
|
+
});
|
|
563
|
+
var getMarker = (list, index) => {
|
|
564
|
+
if ((list.listStyle ?? "bullet") === "number") {
|
|
565
|
+
const start = list.start ?? 1;
|
|
566
|
+
return `${start + index}.`;
|
|
567
|
+
}
|
|
568
|
+
return list.bullet ?? DEFAULT_BULLET;
|
|
569
|
+
};
|
|
570
|
+
var applyTextOptions = (doc, tb) => {
|
|
571
|
+
doc.fontSize(tb.fontSize ?? 10);
|
|
572
|
+
doc.font(getFontNameForText(tb));
|
|
573
|
+
if (tb.color) doc.fillColor(tb.color);
|
|
574
|
+
else doc.fillColor("black");
|
|
575
|
+
};
|
|
576
|
+
var measureTextHeight = (doc, tb, width) => {
|
|
577
|
+
applyTextOptions(doc, tb);
|
|
578
|
+
const lineGap = tb.lineGap ?? 4;
|
|
579
|
+
return doc.heightOfString(tb.text ?? "", { width, lineGap });
|
|
580
|
+
};
|
|
581
|
+
var measureMarkerWidth = (doc, marker, tb) => {
|
|
582
|
+
applyTextOptions(doc, tb);
|
|
583
|
+
return doc.widthOfString(marker) + MARKER_PADDING;
|
|
584
|
+
};
|
|
585
|
+
var computeMarkerWidth = (doc, list, items, tbSample) => {
|
|
586
|
+
if (list.markerWidth !== void 0) return list.markerWidth;
|
|
587
|
+
let maxW = 0;
|
|
588
|
+
for (let i = 0; i < items.length; i++) {
|
|
589
|
+
const marker = getMarker(list, i);
|
|
590
|
+
const w = measureMarkerWidth(doc, marker, tbSample);
|
|
591
|
+
if (w > maxW) maxW = w;
|
|
592
|
+
}
|
|
593
|
+
return maxW;
|
|
594
|
+
};
|
|
595
|
+
var measureItemHeight = (doc, list, item, index, markerWidth, textWidth, styles) => {
|
|
596
|
+
const tb = resolveTextBlock(styles, buildTextBlock(list, item));
|
|
597
|
+
const marker = getMarker(list, index);
|
|
598
|
+
const markerTb = { ...tb, text: marker, underline: false, strike: false, link: void 0 };
|
|
599
|
+
const markerH = measureTextHeight(doc, markerTb, markerWidth);
|
|
600
|
+
const textH = measureTextHeight(doc, tb, textWidth);
|
|
601
|
+
const lineGap = tb.lineGap ?? 4;
|
|
602
|
+
return Math.max(markerH, textH) + lineGap;
|
|
603
|
+
};
|
|
604
|
+
var measureListHeight = (doc, styles, block, env) => {
|
|
605
|
+
const items = (block.items ?? []).map(normalizeListItem);
|
|
606
|
+
const mt = block.marginTop ?? 0;
|
|
607
|
+
const mb = block.marginBottom ?? 0;
|
|
608
|
+
const itemGap = block.itemGap ?? DEFAULT_ITEM_GAP;
|
|
609
|
+
const markerGap = block.markerGap ?? DEFAULT_MARKER_GAP;
|
|
610
|
+
if (!items.length) return mt + mb;
|
|
611
|
+
const localLeft = block.marginLeft ?? 0;
|
|
612
|
+
const localRight = block.marginRight ?? 0;
|
|
613
|
+
const innerWidth = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
614
|
+
const firstResolved = resolveTextBlock(styles, buildTextBlock(block, items[0]));
|
|
615
|
+
const markerWidth = computeMarkerWidth(doc, block, items, firstResolved);
|
|
616
|
+
const textWidth = Math.max(innerWidth - markerWidth - markerGap, 1);
|
|
617
|
+
let total = mt;
|
|
618
|
+
for (let i = 0; i < items.length; i++) {
|
|
619
|
+
total += measureItemHeight(doc, block, items[i], i, markerWidth, textWidth, styles);
|
|
620
|
+
if (i < items.length - 1) total += itemGap;
|
|
621
|
+
}
|
|
622
|
+
return total + mb;
|
|
623
|
+
};
|
|
624
|
+
var drawListText = (doc, tb, x, y, width) => {
|
|
625
|
+
applyTextOptions(doc, tb);
|
|
626
|
+
const lineGap = tb.lineGap ?? 4;
|
|
627
|
+
doc.text(tb.text ?? "", x, y, {
|
|
628
|
+
width,
|
|
629
|
+
align: tb.align ?? "left",
|
|
630
|
+
underline: !!tb.underline,
|
|
631
|
+
strike: !!tb.strike,
|
|
632
|
+
link: tb.link,
|
|
633
|
+
lineGap
|
|
634
|
+
});
|
|
635
|
+
};
|
|
636
|
+
var processListBlock = (doc, ctx, styles, block, y, env, ensureSpaceFor) => {
|
|
637
|
+
const items = (block.items ?? []).map(normalizeListItem);
|
|
638
|
+
const mt = block.marginTop ?? 0;
|
|
639
|
+
const mb = block.marginBottom ?? 0;
|
|
640
|
+
const itemGap = block.itemGap ?? DEFAULT_ITEM_GAP;
|
|
641
|
+
const markerGap = block.markerGap ?? DEFAULT_MARKER_GAP;
|
|
642
|
+
const localLeft = block.marginLeft ?? 0;
|
|
643
|
+
const localRight = block.marginRight ?? 0;
|
|
644
|
+
const xLeft = env.marginLeft + localLeft;
|
|
645
|
+
const innerWidth = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
646
|
+
if (!items.length) {
|
|
647
|
+
const endY2 = (y ?? ctx.currentY) + mt + mb;
|
|
648
|
+
if (y === null) ctx.currentY = endY2;
|
|
649
|
+
return endY2;
|
|
650
|
+
}
|
|
651
|
+
const firstResolved = resolveTextBlock(styles, buildTextBlock(block, items[0]));
|
|
652
|
+
const markerWidth = computeMarkerWidth(doc, block, items, firstResolved);
|
|
653
|
+
const textX = xLeft + markerWidth + markerGap;
|
|
654
|
+
const textWidth = Math.max(innerWidth - markerWidth - markerGap, 1);
|
|
655
|
+
if (y !== null) {
|
|
656
|
+
let drawY = y + mt;
|
|
657
|
+
for (let i = 0; i < items.length; i++) {
|
|
658
|
+
if (i > 0) drawY += itemGap;
|
|
659
|
+
const tb = resolveTextBlock(styles, buildTextBlock(block, items[i]));
|
|
660
|
+
const marker = getMarker(block, i);
|
|
661
|
+
const markerTb = { ...tb, text: marker, underline: false, strike: false, link: void 0 };
|
|
662
|
+
const itemHeight = measureItemHeight(doc, block, items[i], i, markerWidth, textWidth, styles);
|
|
663
|
+
drawListText(doc, markerTb, xLeft, drawY, markerWidth);
|
|
664
|
+
drawListText(doc, tb, textX, drawY, textWidth);
|
|
665
|
+
drawY += itemHeight;
|
|
666
|
+
}
|
|
667
|
+
return drawY + mb;
|
|
668
|
+
}
|
|
669
|
+
for (let i = 0; i < items.length; i++) {
|
|
670
|
+
const leading = i === 0 ? mt : itemGap;
|
|
671
|
+
const itemHeight = measureItemHeight(doc, block, items[i], i, markerWidth, textWidth, styles);
|
|
672
|
+
ensureSpaceFor(leading + itemHeight, env);
|
|
673
|
+
const drawY = ctx.currentY + leading;
|
|
674
|
+
const tb = resolveTextBlock(styles, buildTextBlock(block, items[i]));
|
|
675
|
+
const marker = getMarker(block, i);
|
|
676
|
+
const markerTb = { ...tb, text: marker, underline: false, strike: false, link: void 0 };
|
|
677
|
+
drawListText(doc, markerTb, xLeft, drawY, markerWidth);
|
|
678
|
+
drawListText(doc, tb, textX, drawY, textWidth);
|
|
679
|
+
ctx.currentY = drawY + itemHeight;
|
|
680
|
+
}
|
|
681
|
+
const endY = ctx.currentY + mb;
|
|
682
|
+
ctx.currentY = endY;
|
|
683
|
+
return endY;
|
|
684
|
+
};
|
|
685
|
+
|
|
511
686
|
// src/renderer-engine/blocks/line.ts
|
|
512
687
|
var processLineBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
|
|
513
688
|
const localLeft = block.marginLeft ?? 0;
|
|
@@ -516,13 +691,12 @@ var processLineBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
|
|
|
516
691
|
const width = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
517
692
|
const mt = block.marginTop ?? 0;
|
|
518
693
|
const mb = block.marginBottom ?? 0;
|
|
519
|
-
const baseY = y ?? ctx.currentY;
|
|
520
|
-
const startY = baseY + mt;
|
|
521
694
|
const lineWidth = block.lineWidth ?? 1;
|
|
522
695
|
const heightNeeded = mt + lineWidth + mb;
|
|
523
696
|
if (y === null) {
|
|
524
697
|
ensureSpaceFor(heightNeeded, env);
|
|
525
698
|
}
|
|
699
|
+
const startY = (y ?? ctx.currentY) + mt;
|
|
526
700
|
doc.save();
|
|
527
701
|
if (block.color) {
|
|
528
702
|
doc.strokeColor(block.color);
|
|
@@ -535,7 +709,99 @@ var processLineBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
|
|
|
535
709
|
return newY;
|
|
536
710
|
};
|
|
537
711
|
|
|
712
|
+
// src/renderer-engine/blocks/shape.ts
|
|
713
|
+
var processShapeBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
|
|
714
|
+
const mt = block.marginTop ?? 0;
|
|
715
|
+
const mb = block.marginBottom ?? 0;
|
|
716
|
+
const ml = block.marginLeft ?? 0;
|
|
717
|
+
const mr = block.marginRight ?? 0;
|
|
718
|
+
const lineWidth = block.lineWidth ?? 1;
|
|
719
|
+
const width = block.width ?? Math.max(env.innerWidth - ml - mr, 1);
|
|
720
|
+
const height = block.height ?? lineWidth;
|
|
721
|
+
const heightNeeded = mt + height + mb;
|
|
722
|
+
if (y === null) {
|
|
723
|
+
ensureSpaceFor(heightNeeded, env);
|
|
724
|
+
}
|
|
725
|
+
const startY = (y ?? ctx.currentY) + mt;
|
|
726
|
+
const availableWidth = Math.max(env.innerWidth - ml - mr, 1);
|
|
727
|
+
let x = env.marginLeft + ml;
|
|
728
|
+
if (block.align === "center") {
|
|
729
|
+
x = env.marginLeft + ml + (availableWidth - width) / 2;
|
|
730
|
+
} else if (block.align === "right") {
|
|
731
|
+
x = env.marginLeft + ml + availableWidth - width;
|
|
732
|
+
}
|
|
733
|
+
const drawY = startY;
|
|
734
|
+
doc.save();
|
|
735
|
+
if (typeof block.opacity === "number") {
|
|
736
|
+
doc.opacity(block.opacity);
|
|
737
|
+
}
|
|
738
|
+
doc.lineWidth(lineWidth);
|
|
739
|
+
if (block.strokeColor) {
|
|
740
|
+
doc.strokeColor(block.strokeColor);
|
|
741
|
+
}
|
|
742
|
+
if (block.fillColor) {
|
|
743
|
+
doc.fillColor(block.fillColor);
|
|
744
|
+
}
|
|
745
|
+
switch (block.shape) {
|
|
746
|
+
case "rect":
|
|
747
|
+
doc.rect(x, drawY, width, height);
|
|
748
|
+
break;
|
|
749
|
+
case "roundedRect":
|
|
750
|
+
doc.roundedRect(x, drawY, width, height, block.radius ?? 6);
|
|
751
|
+
break;
|
|
752
|
+
case "circle": {
|
|
753
|
+
const radius = block.radius ?? Math.min(width, height) / 2;
|
|
754
|
+
doc.circle(x + radius, drawY + radius, radius);
|
|
755
|
+
break;
|
|
756
|
+
}
|
|
757
|
+
case "ellipse":
|
|
758
|
+
doc.ellipse(x + width / 2, drawY + height / 2, width / 2, height / 2);
|
|
759
|
+
break;
|
|
760
|
+
case "line":
|
|
761
|
+
doc.moveTo(x, drawY).lineTo(x + width, drawY);
|
|
762
|
+
break;
|
|
763
|
+
case "polygon":
|
|
764
|
+
if (block.points?.length) {
|
|
765
|
+
const [first, ...rest] = block.points;
|
|
766
|
+
doc.moveTo(x + first.x, drawY + first.y);
|
|
767
|
+
rest.forEach((point) => {
|
|
768
|
+
doc.lineTo(x + point.x, drawY + point.y);
|
|
769
|
+
});
|
|
770
|
+
doc.closePath();
|
|
771
|
+
}
|
|
772
|
+
break;
|
|
773
|
+
case "path":
|
|
774
|
+
if (block.path) {
|
|
775
|
+
const safePath = block.path.trim().replace(/[\n\r\t]+/g, " ").replace(/\s+/g, " ");
|
|
776
|
+
if (safePath.includes("undefined") || safePath.includes("NaN")) {
|
|
777
|
+
throw new Error(`Invalid shape path: ${safePath}`);
|
|
778
|
+
}
|
|
779
|
+
doc.translate(x, drawY);
|
|
780
|
+
doc.path(safePath);
|
|
781
|
+
}
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
if (block.fillColor && block.strokeColor) {
|
|
785
|
+
doc.fillAndStroke(block.fillColor, block.strokeColor);
|
|
786
|
+
} else if (block.fillColor) {
|
|
787
|
+
doc.fill(block.fillColor);
|
|
788
|
+
} else {
|
|
789
|
+
doc.stroke();
|
|
790
|
+
}
|
|
791
|
+
doc.restore();
|
|
792
|
+
const newY = startY + height + mb;
|
|
793
|
+
if (y === null) {
|
|
794
|
+
ctx.currentY = newY;
|
|
795
|
+
}
|
|
796
|
+
return newY;
|
|
797
|
+
};
|
|
798
|
+
|
|
538
799
|
// src/renderer-engine/blocks/table.ts
|
|
800
|
+
var shouldSkipHeaderOnlyTable = (table, rows) => {
|
|
801
|
+
const headerRows = table.headerRows ?? 0;
|
|
802
|
+
if (headerRows <= 0) return false;
|
|
803
|
+
return rows.length <= headerRows;
|
|
804
|
+
};
|
|
539
805
|
var normalizeSpan = (value) => {
|
|
540
806
|
if (!value || value < 1) return 1;
|
|
541
807
|
return Math.floor(value);
|
|
@@ -690,6 +956,9 @@ var measureTableHeight = (doc, table, env, styles, computeColumnPixelWidths2, me
|
|
|
690
956
|
const localRight = table.marginRight ?? 0;
|
|
691
957
|
const width = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
692
958
|
const rows = normalizeTableRows(table);
|
|
959
|
+
if (shouldSkipHeaderOnlyTable(table, rows)) {
|
|
960
|
+
return 0;
|
|
961
|
+
}
|
|
693
962
|
const totalCols = table.widths.length;
|
|
694
963
|
const colWidths = computeColumnPixelWidths2(table.widths, width);
|
|
695
964
|
const rowHeights = [];
|
|
@@ -766,6 +1035,9 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
|
|
|
766
1035
|
};
|
|
767
1036
|
const headerRows = table.headerRows ?? 0;
|
|
768
1037
|
const rows = normalizeTableRows(table);
|
|
1038
|
+
if (shouldSkipHeaderOnlyTable(table, rows)) {
|
|
1039
|
+
return baseY;
|
|
1040
|
+
}
|
|
769
1041
|
const buildPlacementAndHeights = (rows2) => {
|
|
770
1042
|
const placedRows = [];
|
|
771
1043
|
const rowHeights = [];
|
|
@@ -1045,12 +1317,12 @@ var drawCellBorder = (doc, border, x, y, width, height) => {
|
|
|
1045
1317
|
// src/renderer-engine/blocks/text.ts
|
|
1046
1318
|
var processTextBlock = (doc, ctx, styles, block, y, env, ensureSpaceFor) => {
|
|
1047
1319
|
const tb = resolveTextBlock(styles, block);
|
|
1320
|
+
const mt = tb.marginTop ?? 0;
|
|
1321
|
+
const mb = tb.marginBottom ?? 0;
|
|
1048
1322
|
const localLeft = tb.marginLeft ?? 0;
|
|
1049
1323
|
const localRight = tb.marginRight ?? 0;
|
|
1050
1324
|
const xLeft = env.marginLeft + localLeft;
|
|
1051
1325
|
const width = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
1052
|
-
const baseY = y ?? ctx.currentY;
|
|
1053
|
-
const startY = baseY;
|
|
1054
1326
|
doc.fontSize(tb.fontSize ?? 10);
|
|
1055
1327
|
const isBold = !!tb.bold;
|
|
1056
1328
|
const isItalic = !!tb.italic;
|
|
@@ -1064,11 +1336,12 @@ var processTextBlock = (doc, ctx, styles, block, y, env, ensureSpaceFor) => {
|
|
|
1064
1336
|
doc.font(fontName);
|
|
1065
1337
|
if (tb.color) doc.fillColor(tb.color);
|
|
1066
1338
|
else doc.fillColor("black");
|
|
1067
|
-
const
|
|
1339
|
+
const lineGap = tb.lineGap ?? 4;
|
|
1340
|
+
const textHeight = doc.heightOfString(tb.text, { width, lineGap });
|
|
1068
1341
|
if (y === null) {
|
|
1069
|
-
|
|
1070
|
-
ensureSpaceFor(textHeight + gapCheck, env);
|
|
1342
|
+
ensureSpaceFor(mt + textHeight + lineGap + mb, env);
|
|
1071
1343
|
}
|
|
1344
|
+
const startY = (y ?? ctx.currentY) + mt;
|
|
1072
1345
|
doc.text(tb.text, xLeft, startY, {
|
|
1073
1346
|
width,
|
|
1074
1347
|
align: tb.align ?? "left",
|
|
@@ -1076,414 +1349,82 @@ var processTextBlock = (doc, ctx, styles, block, y, env, ensureSpaceFor) => {
|
|
|
1076
1349
|
strike: !!tb.strike,
|
|
1077
1350
|
link: tb.link
|
|
1078
1351
|
});
|
|
1079
|
-
const
|
|
1080
|
-
const newY = startY + textHeight + gap;
|
|
1352
|
+
const newY = doc.y + lineGap + mb;
|
|
1081
1353
|
if (y === null) ctx.currentY = newY;
|
|
1082
1354
|
return newY;
|
|
1083
1355
|
};
|
|
1084
1356
|
|
|
1085
|
-
// src/renderer-engine/utils/block-renderer.ts
|
|
1086
|
-
function createBlockRenderer(deps) {
|
|
1087
|
-
const { doc, ctx, styles, computeColumnPixelWidths: computeColumnPixelWidths2, finishPage: finishPage2, processSignatureBlock, defaultImage } = deps;
|
|
1088
|
-
const bottomLimitForContent = createBottomLimitForContent(doc, ctx);
|
|
1089
|
-
const ensureSpaceFor = createEnsureSpaceFor(ctx, bottomLimitForContent, finishPage2);
|
|
1090
|
-
const measureBlockHeight = createMeasureBlockHeight({
|
|
1091
|
-
doc,
|
|
1092
|
-
styles,
|
|
1093
|
-
computeColumnPixelWidths: computeColumnPixelWidths2
|
|
1094
|
-
});
|
|
1095
|
-
const renderBlock = (block, y, env) => {
|
|
1096
|
-
if (block.visible === false) {
|
|
1097
|
-
return y ?? ctx.currentY;
|
|
1098
|
-
}
|
|
1099
|
-
if ((block.backgroundColor || block.backgroundImage) && block.type !== "pageBreak" && block.type !== "signature") {
|
|
1100
|
-
const startY = y ?? ctx.currentY;
|
|
1101
|
-
const blockHeight = measureBlockHeight(block, env);
|
|
1102
|
-
const opacity = block.backgroundOpacity ?? 1;
|
|
1103
|
-
doc.save();
|
|
1104
|
-
if (block.backgroundColor) {
|
|
1105
|
-
doc.fillOpacity(opacity).rect(env.marginLeft, startY, env.innerWidth, blockHeight).fill(block.backgroundColor);
|
|
1106
|
-
doc.fillOpacity(1);
|
|
1107
|
-
}
|
|
1108
|
-
if (block.backgroundImage) {
|
|
1109
|
-
try {
|
|
1110
|
-
doc.image(block.backgroundImage, env.marginLeft, startY, {
|
|
1111
|
-
width: env.innerWidth,
|
|
1112
|
-
height: blockHeight
|
|
1113
|
-
});
|
|
1114
|
-
} catch (_) {
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
doc.restore();
|
|
1118
|
-
}
|
|
1119
|
-
switch (block.type) {
|
|
1120
|
-
case "text":
|
|
1121
|
-
return processTextBlock(doc, ctx, styles, block, y, env, ensureSpaceFor);
|
|
1122
|
-
case "image":
|
|
1123
|
-
return processImageBlock(doc, ctx, block, y, env, ensureSpaceFor, defaultImage);
|
|
1124
|
-
case "qr": {
|
|
1125
|
-
const qb = block;
|
|
1126
|
-
const imageLike = {
|
|
1127
|
-
type: "image",
|
|
1128
|
-
src: qb.src,
|
|
1129
|
-
width: qb.size,
|
|
1130
|
-
height: qb.size,
|
|
1131
|
-
align: qb.align,
|
|
1132
|
-
marginTop: qb.marginTop,
|
|
1133
|
-
marginBottom: qb.marginBottom,
|
|
1134
|
-
marginLeft: qb.marginLeft,
|
|
1135
|
-
marginRight: qb.marginRight
|
|
1136
|
-
};
|
|
1137
|
-
return processImageBlock(doc, ctx, imageLike, y, env, ensureSpaceFor, defaultImage);
|
|
1138
|
-
}
|
|
1139
|
-
case "barcode":
|
|
1140
|
-
return processBarcodeBlock(doc, ctx, block, y, env, ensureSpaceFor);
|
|
1141
|
-
case "line":
|
|
1142
|
-
return processLineBlock(doc, ctx, block, y, env, ensureSpaceFor);
|
|
1143
|
-
case "table":
|
|
1144
|
-
return processTableBlock(
|
|
1145
|
-
doc,
|
|
1146
|
-
ctx,
|
|
1147
|
-
styles,
|
|
1148
|
-
block,
|
|
1149
|
-
y,
|
|
1150
|
-
env,
|
|
1151
|
-
computeColumnPixelWidths2,
|
|
1152
|
-
bottomLimitForContent,
|
|
1153
|
-
finishPage2,
|
|
1154
|
-
(b, blockY, blockEnv) => renderBlock(b, blockY, blockEnv),
|
|
1155
|
-
measureBlockHeight
|
|
1156
|
-
);
|
|
1157
|
-
case "columns":
|
|
1158
|
-
return processColumnsBlock(
|
|
1159
|
-
ctx,
|
|
1160
|
-
block,
|
|
1161
|
-
y,
|
|
1162
|
-
env,
|
|
1163
|
-
computeColumnPixelWidths2,
|
|
1164
|
-
renderBlock,
|
|
1165
|
-
ensureSpaceFor,
|
|
1166
|
-
measureBlockHeight
|
|
1167
|
-
);
|
|
1168
|
-
case "keyValueGrid":
|
|
1169
|
-
return processKeyValueGridBlock(
|
|
1170
|
-
doc,
|
|
1171
|
-
ctx,
|
|
1172
|
-
styles,
|
|
1173
|
-
block,
|
|
1174
|
-
y,
|
|
1175
|
-
env,
|
|
1176
|
-
computeColumnPixelWidths2,
|
|
1177
|
-
ensureSpaceFor
|
|
1178
|
-
);
|
|
1179
|
-
case "signature":
|
|
1180
|
-
if (!env.allowPageBreak) {
|
|
1181
|
-
throw new PdfEngineError({
|
|
1182
|
-
code: "PDF_ERROR_SIGNATURE_NOT_IN_MAIN_FLOW" /* PDF_ERROR_SIGNATURE_NOT_IN_MAIN_FLOW */,
|
|
1183
|
-
message: "Signature block is only allowed in main content flow.",
|
|
1184
|
-
statusCode: 400,
|
|
1185
|
-
details: { blockType: "signature" }
|
|
1186
|
-
});
|
|
1187
|
-
}
|
|
1188
|
-
if (y !== null) {
|
|
1189
|
-
throw new PdfEngineError({
|
|
1190
|
-
code: "PDF_ERROR_SIGNATURE_EXPLICIT_Y_FORBIDDEN" /* PDF_ERROR_SIGNATURE_EXPLICIT_Y_FORBIDDEN */,
|
|
1191
|
-
message: "Signature block must be part of main flow, not drawn at explicit Y.",
|
|
1192
|
-
statusCode: 400,
|
|
1193
|
-
details: { blockType: "signature", y }
|
|
1194
|
-
});
|
|
1195
|
-
}
|
|
1196
|
-
processSignatureBlock(block);
|
|
1197
|
-
return ctx.currentY;
|
|
1198
|
-
case "pageBreak":
|
|
1199
|
-
if (!env.allowPageBreak || ctx.inFooter) {
|
|
1200
|
-
return y ?? ctx.currentY;
|
|
1201
|
-
}
|
|
1202
|
-
finishPage2(true);
|
|
1203
|
-
return ctx.currentY;
|
|
1204
|
-
default:
|
|
1205
|
-
return ctx.currentY;
|
|
1206
|
-
}
|
|
1207
|
-
};
|
|
1208
|
-
const renderBlockArray = (blocks, startY, env) => {
|
|
1209
|
-
let localY = startY;
|
|
1210
|
-
for (const block of blocks) {
|
|
1211
|
-
if (block.visible === false) {
|
|
1212
|
-
continue;
|
|
1213
|
-
}
|
|
1214
|
-
localY = renderBlock(block, localY, env);
|
|
1215
|
-
}
|
|
1216
|
-
return localY;
|
|
1217
|
-
};
|
|
1218
|
-
return {
|
|
1219
|
-
renderBlock,
|
|
1220
|
-
renderBlockArray
|
|
1221
|
-
};
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
// src/renderer-engine/utils/context.ts
|
|
1225
|
-
function createInitialContext(doc) {
|
|
1226
|
-
return {
|
|
1227
|
-
pageNumber: 1,
|
|
1228
|
-
currentY: doc.page.margins.top,
|
|
1229
|
-
signatureBlock: null,
|
|
1230
|
-
signatureTopY: null,
|
|
1231
|
-
signatureHeight: 0,
|
|
1232
|
-
signaturePlaced: false,
|
|
1233
|
-
afterSignature: false,
|
|
1234
|
-
inFooter: false
|
|
1235
|
-
};
|
|
1236
|
-
}
|
|
1237
|
-
|
|
1238
1357
|
// src/renderer-engine/utils/ensure-space.ts
|
|
1239
|
-
function createEnsureSpaceFor(ctx, bottomLimitForContent, finishPage2) {
|
|
1358
|
+
function createEnsureSpaceFor(ctx, doc, bottomLimitForContent, finishPage2) {
|
|
1240
1359
|
return (heightNeeded, env) => {
|
|
1241
1360
|
if (!env.allowPageBreak || ctx.inFooter) return;
|
|
1242
1361
|
const bottomLimit = bottomLimitForContent();
|
|
1362
|
+
const fullContentHeight = bottomLimit - doc.page.margins.top;
|
|
1363
|
+
if (heightNeeded > fullContentHeight) {
|
|
1364
|
+
console.warn(
|
|
1365
|
+
`[pdf-engine] Block height (${Math.round(heightNeeded)}pt) exceeds page content area (${Math.round(fullContentHeight)}pt). Content will overflow.`
|
|
1366
|
+
);
|
|
1367
|
+
}
|
|
1243
1368
|
if (ctx.currentY + heightNeeded > bottomLimit) {
|
|
1244
1369
|
finishPage2(true);
|
|
1245
1370
|
}
|
|
1246
1371
|
};
|
|
1247
1372
|
}
|
|
1248
1373
|
|
|
1249
|
-
// src/renderer-engine/utils/
|
|
1250
|
-
var
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1374
|
+
// src/renderer-engine/utils/spacing.ts
|
|
1375
|
+
var emptySpacing = () => ({
|
|
1376
|
+
top: 0,
|
|
1377
|
+
right: 0,
|
|
1378
|
+
bottom: 0,
|
|
1379
|
+
left: 0
|
|
1254
1380
|
});
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
if (def.watermark && watermarkUsesLast(mode)) {
|
|
1268
|
-
const isLast = !addNewPage;
|
|
1269
|
-
drawWatermarkForPage(doc, def.watermark, ctx.pageNumber, isLast);
|
|
1270
|
-
}
|
|
1271
|
-
if (ctx.signatureBlock && ctx.signatureTopY !== null) {
|
|
1272
|
-
const env = contentEnv(doc);
|
|
1273
|
-
drawSignatureBlock(doc, ctx.signatureBlock, ctx.signatureTopY, env, renderBlockArray);
|
|
1381
|
+
var resolveSpacing = (value, overrides) => {
|
|
1382
|
+
const result = emptySpacing();
|
|
1383
|
+
if (typeof value === "number") {
|
|
1384
|
+
result.top = value;
|
|
1385
|
+
result.right = value;
|
|
1386
|
+
result.bottom = value;
|
|
1387
|
+
result.left = value;
|
|
1388
|
+
} else if (value) {
|
|
1389
|
+
result.top = value.top ?? 0;
|
|
1390
|
+
result.right = value.right ?? 0;
|
|
1391
|
+
result.bottom = value.bottom ?? 0;
|
|
1392
|
+
result.left = value.left ?? 0;
|
|
1274
1393
|
}
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
}
|
|
1310
|
-
if (Array.isArray(input)) {
|
|
1311
|
-
return { blocks: input };
|
|
1312
|
-
}
|
|
1313
|
-
return null;
|
|
1314
|
-
}
|
|
1315
|
-
function drawFooter(doc, def, ctx, footerBandHeight, renderBlockArray) {
|
|
1316
|
-
const footerConfig = def.footer;
|
|
1317
|
-
if (!footerConfig) return;
|
|
1318
|
-
const layout = normalizeFooter(footerConfig, ctx, doc);
|
|
1319
|
-
if (!layout || !layout.blocks.length) return;
|
|
1320
|
-
if (layout.visible === false) return;
|
|
1321
|
-
const bandHeight = footerBandHeight;
|
|
1322
|
-
if (!bandHeight) return;
|
|
1323
|
-
const { marginTop = 4, backgroundColor, backgroundImage, blocks } = layout;
|
|
1324
|
-
const footerMarginLeft = layout.marginLeft ?? 0;
|
|
1325
|
-
const footerMarginRight = layout.marginRight ?? 0;
|
|
1326
|
-
const contentWidth = doc.page.width - footerMarginLeft - footerMarginRight;
|
|
1327
|
-
const footerEnv = {
|
|
1328
|
-
marginLeft: footerMarginLeft,
|
|
1329
|
-
innerWidth: contentWidth,
|
|
1330
|
-
allowPageBreak: false
|
|
1331
|
-
};
|
|
1332
|
-
const pageHeight = doc.page.height;
|
|
1333
|
-
const bandTop = pageHeight - bandHeight;
|
|
1334
|
-
if (backgroundColor) {
|
|
1335
|
-
doc.save();
|
|
1336
|
-
doc.rect(0, bandTop, doc.page.width, bandHeight).fill(backgroundColor);
|
|
1337
|
-
doc.restore();
|
|
1338
|
-
}
|
|
1339
|
-
if (backgroundImage) {
|
|
1340
|
-
try {
|
|
1341
|
-
doc.image(backgroundImage, 0, bandTop, {
|
|
1342
|
-
width: doc.page.width,
|
|
1343
|
-
height: bandHeight
|
|
1344
|
-
});
|
|
1345
|
-
} catch (e) {
|
|
1346
|
-
console.warn("Failed to load footer background image:", e);
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
doc.save();
|
|
1350
|
-
doc.rect(footerMarginLeft, bandTop, contentWidth, bandHeight).clip();
|
|
1351
|
-
doc.translate(0, bandTop);
|
|
1352
|
-
ctx.inFooter = true;
|
|
1353
|
-
const localStartY = marginTop;
|
|
1354
|
-
renderBlockArray(blocks, localStartY, footerEnv);
|
|
1355
|
-
ctx.inFooter = false;
|
|
1356
|
-
doc.restore();
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
// src/renderer-engine/utils/header.ts
|
|
1360
|
-
function drawHeader(doc, def, headerBandHeight, header, renderBlockArray) {
|
|
1361
|
-
if (!header || !header.blocks.length) return;
|
|
1362
|
-
if (header.visible === false) return;
|
|
1363
|
-
if (!headerBandHeight) return;
|
|
1364
|
-
const bandTop = 0;
|
|
1365
|
-
const bandHeight = def.margins.top;
|
|
1366
|
-
const headerMarginLeft = header.marginLeft ?? 0;
|
|
1367
|
-
const headerMarginRight = header.marginRight ?? 0;
|
|
1368
|
-
const contentWidth = doc.page.width - headerMarginLeft - headerMarginRight;
|
|
1369
|
-
const headerEnv = {
|
|
1370
|
-
marginLeft: headerMarginLeft,
|
|
1371
|
-
innerWidth: contentWidth,
|
|
1372
|
-
allowPageBreak: false
|
|
1373
|
-
};
|
|
1374
|
-
if (header.backgroundColor) {
|
|
1375
|
-
doc.save();
|
|
1376
|
-
doc.rect(0, bandTop, doc.page.width, bandHeight).fill(header.backgroundColor);
|
|
1377
|
-
doc.restore();
|
|
1378
|
-
}
|
|
1379
|
-
if (header.backgroundImage) {
|
|
1380
|
-
try {
|
|
1381
|
-
doc.image(header.backgroundImage, 0, bandTop, {
|
|
1382
|
-
width: doc.page.width,
|
|
1383
|
-
height: bandHeight
|
|
1384
|
-
});
|
|
1385
|
-
} catch (e) {
|
|
1386
|
-
console.warn("Failed to load header background image:", e);
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
doc.save();
|
|
1390
|
-
doc.rect(headerMarginLeft, bandTop, contentWidth, bandHeight).clip();
|
|
1391
|
-
const startY = bandTop + (header.marginTop ?? 0);
|
|
1392
|
-
renderBlockArray(header.blocks, startY, headerEnv);
|
|
1393
|
-
doc.restore();
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
// src/renderer-engine/utils/image-loader.ts
|
|
1397
|
-
var import_axios = __toESM(require("axios"));
|
|
1398
|
-
async function normalizeImageSrc(src, fallback) {
|
|
1399
|
-
if (!src) return fallback ?? src;
|
|
1400
|
-
if (Buffer.isBuffer(src)) return src;
|
|
1401
|
-
if (src.startsWith("data:")) {
|
|
1402
|
-
const commaIdx = src.indexOf(",");
|
|
1403
|
-
if (commaIdx !== -1) {
|
|
1404
|
-
return Buffer.from(src.slice(commaIdx + 1), "base64");
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
if (src.startsWith("http://") || src.startsWith("https://")) {
|
|
1408
|
-
try {
|
|
1409
|
-
const res = await import_axios.default.get(src, { responseType: "arraybuffer" });
|
|
1410
|
-
return Buffer.from(res.data);
|
|
1411
|
-
} catch (e) {
|
|
1412
|
-
if (fallback !== void 0) {
|
|
1413
|
-
console.warn(`Failed to fetch remote image "${src}", using default image.`);
|
|
1414
|
-
return fallback;
|
|
1415
|
-
}
|
|
1416
|
-
throw toPdfEngineError(e, {
|
|
1417
|
-
code: "PDF_ERROR_IMAGE_FETCH_FAILED" /* PDF_ERROR_IMAGE_FETCH_FAILED */,
|
|
1418
|
-
message: "Failed to fetch remote image for PDF.",
|
|
1419
|
-
statusCode: 422,
|
|
1420
|
-
details: { url: src },
|
|
1421
|
-
retryable: false
|
|
1422
|
-
});
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
|
-
return src;
|
|
1426
|
-
}
|
|
1427
|
-
async function materializeImagesInBlocks(blocks, fallback) {
|
|
1428
|
-
const out = [];
|
|
1429
|
-
for (const block of blocks) {
|
|
1430
|
-
const blockAny = block;
|
|
1431
|
-
if (blockAny.backgroundImage) {
|
|
1432
|
-
blockAny.backgroundImage = await normalizeImageSrc(blockAny.backgroundImage, fallback);
|
|
1433
|
-
}
|
|
1434
|
-
if (block.type === "image") {
|
|
1435
|
-
const img = { ...block };
|
|
1436
|
-
img.src = await normalizeImageSrc(img.src, fallback);
|
|
1437
|
-
out.push(img);
|
|
1438
|
-
} else if (block.type === "columns") {
|
|
1439
|
-
out.push({
|
|
1440
|
-
...block,
|
|
1441
|
-
columns: await Promise.all(block.columns.map((col) => materializeImagesInBlocks(col, fallback)))
|
|
1442
|
-
});
|
|
1443
|
-
} else if (block.type === "signature" && block.blocks) {
|
|
1444
|
-
out.push({
|
|
1445
|
-
...block,
|
|
1446
|
-
blocks: await materializeImagesInBlocks(block.blocks, fallback)
|
|
1447
|
-
});
|
|
1448
|
-
} else if (block.type === "table") {
|
|
1449
|
-
const tb = block;
|
|
1450
|
-
const newBody = await Promise.all(
|
|
1451
|
-
tb.body.map(
|
|
1452
|
-
async (entry) => {
|
|
1453
|
-
const materializeCell = async (cell) => {
|
|
1454
|
-
if (!cell.blocks?.length) return cell;
|
|
1455
|
-
return { ...cell, blocks: await materializeImagesInBlocks(cell.blocks, fallback) };
|
|
1456
|
-
};
|
|
1457
|
-
if (Array.isArray(entry)) {
|
|
1458
|
-
return await Promise.all(entry.map(materializeCell));
|
|
1459
|
-
}
|
|
1460
|
-
if (entry && typeof entry === "object" && "content" in entry && Array.isArray(entry.content)) {
|
|
1461
|
-
return { ...entry, content: await Promise.all(entry.content.map(materializeCell)) };
|
|
1462
|
-
}
|
|
1463
|
-
return entry;
|
|
1464
|
-
}
|
|
1465
|
-
)
|
|
1466
|
-
);
|
|
1467
|
-
out.push({ ...tb, body: newBody });
|
|
1468
|
-
} else {
|
|
1469
|
-
out.push(block);
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
return out;
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
// src/renderer-engine/utils/layout.ts
|
|
1476
|
-
function computeColumnPixelWidths(widths, totalWidth) {
|
|
1477
|
-
let fixedTotal = 0;
|
|
1478
|
-
let starCount = 0;
|
|
1479
|
-
widths.forEach((w) => {
|
|
1480
|
-
if (w === "*") starCount++;
|
|
1481
|
-
else fixedTotal += w;
|
|
1482
|
-
});
|
|
1483
|
-
const remaining = Math.max(totalWidth - fixedTotal, 0);
|
|
1484
|
-
const starWidth = starCount > 0 ? remaining / starCount : 0;
|
|
1485
|
-
return widths.map((w) => w === "*" ? starWidth : w);
|
|
1486
|
-
}
|
|
1394
|
+
return {
|
|
1395
|
+
top: overrides?.top ?? result.top,
|
|
1396
|
+
right: overrides?.right ?? result.right,
|
|
1397
|
+
bottom: overrides?.bottom ?? result.bottom,
|
|
1398
|
+
left: overrides?.left ?? result.left
|
|
1399
|
+
};
|
|
1400
|
+
};
|
|
1401
|
+
var resolveBlockPadding = (block) => {
|
|
1402
|
+
return resolveSpacing(block.padding, {
|
|
1403
|
+
top: block.paddingTop,
|
|
1404
|
+
right: block.paddingRight,
|
|
1405
|
+
bottom: block.paddingBottom,
|
|
1406
|
+
left: block.paddingLeft
|
|
1407
|
+
});
|
|
1408
|
+
};
|
|
1409
|
+
var resolveHeaderPadding = (header) => {
|
|
1410
|
+
return resolveSpacing(header.padding, {
|
|
1411
|
+
top: header.paddingTop,
|
|
1412
|
+
right: header.paddingRight,
|
|
1413
|
+
bottom: header.paddingBottom,
|
|
1414
|
+
left: header.paddingLeft
|
|
1415
|
+
});
|
|
1416
|
+
};
|
|
1417
|
+
var resolveFooterPadding = (footer) => {
|
|
1418
|
+
return resolveSpacing(footer.padding, {
|
|
1419
|
+
top: footer.paddingTop,
|
|
1420
|
+
right: footer.paddingRight,
|
|
1421
|
+
bottom: footer.paddingBottom,
|
|
1422
|
+
left: footer.paddingLeft
|
|
1423
|
+
});
|
|
1424
|
+
};
|
|
1425
|
+
var hasPadding = (padding) => {
|
|
1426
|
+
return padding.top > 0 || padding.right > 0 || padding.bottom > 0 || padding.left > 0;
|
|
1427
|
+
};
|
|
1487
1428
|
|
|
1488
1429
|
// src/renderer-engine/utils/styles.ts
|
|
1489
1430
|
function mergeStyleDefs(styles, names) {
|
|
@@ -1579,191 +1520,772 @@ var drawStyledText = (doc, tb, x, y, width) => {
|
|
|
1579
1520
|
// src/renderer-engine/utils/measure-block-height.ts
|
|
1580
1521
|
function createMeasureBlockHeight(deps) {
|
|
1581
1522
|
const { doc, styles, computeColumnPixelWidths: computeColumnPixelWidths2 } = deps;
|
|
1582
|
-
const measureText = (
|
|
1583
|
-
const tb = resolveTextBlock(styles,
|
|
1584
|
-
const
|
|
1523
|
+
const measureText = (block, env) => {
|
|
1524
|
+
const tb = resolveTextBlock(styles, block);
|
|
1525
|
+
const mt = tb.marginTop ?? 0;
|
|
1526
|
+
const mb = tb.marginBottom ?? 0;
|
|
1527
|
+
const localLeft = tb.marginLeft ?? 0;
|
|
1528
|
+
const localRight = tb.marginRight ?? 0;
|
|
1529
|
+
const width = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
1585
1530
|
doc.font(getFontNameForText(tb));
|
|
1586
1531
|
doc.fontSize(tb.fontSize ?? 10);
|
|
1587
|
-
const
|
|
1588
|
-
const
|
|
1589
|
-
return
|
|
1532
|
+
const lineGap = tb.lineGap ?? 4;
|
|
1533
|
+
const height = doc.heightOfString(tb.text ?? "", { width, lineGap });
|
|
1534
|
+
return mt + height + lineGap + mb;
|
|
1590
1535
|
};
|
|
1591
|
-
const
|
|
1592
|
-
|
|
1593
|
-
const h = b.height ?? 50;
|
|
1594
|
-
return measureImageLike(b.marginTop, b.marginBottom, h);
|
|
1536
|
+
const measureImage = (block) => {
|
|
1537
|
+
return (block.marginTop ?? 0) + (block.height ?? 50) + (block.marginBottom ?? 0);
|
|
1595
1538
|
};
|
|
1596
|
-
const measureQr = (
|
|
1597
|
-
|
|
1598
|
-
return measureImageLike(b.marginTop, b.marginBottom, size);
|
|
1539
|
+
const measureQr = (block) => {
|
|
1540
|
+
return (block.marginTop ?? 0) + (block.size ?? 80) + (block.marginBottom ?? 0);
|
|
1599
1541
|
};
|
|
1600
|
-
const measureBarcode = (
|
|
1601
|
-
|
|
1602
|
-
return measureImageLike(b.marginTop, b.marginBottom, h);
|
|
1542
|
+
const measureBarcode = (block) => {
|
|
1543
|
+
return (block.marginTop ?? 0) + (block.height ?? 40) + (block.marginBottom ?? 0);
|
|
1603
1544
|
};
|
|
1604
|
-
const
|
|
1605
|
-
|
|
1606
|
-
const mt = b.marginTop ?? 0;
|
|
1607
|
-
const mb = b.marginBottom ?? 0;
|
|
1608
|
-
return mt + lw + mb;
|
|
1545
|
+
const measureShape = (block) => {
|
|
1546
|
+
return (block.marginTop ?? 0) + (block.height ?? block.lineWidth ?? 1) + (block.marginBottom ?? 0);
|
|
1609
1547
|
};
|
|
1610
|
-
const
|
|
1611
|
-
|
|
1612
|
-
const mb = b.marginBottom ?? 0;
|
|
1613
|
-
const ml = b.marginLeft ?? 0;
|
|
1614
|
-
const mr = b.marginRight ?? 0;
|
|
1615
|
-
const innerWidth = Math.max(env.innerWidth - ml - mr, 1);
|
|
1616
|
-
const fakeEnv = {
|
|
1617
|
-
marginLeft: 0,
|
|
1618
|
-
innerWidth,
|
|
1619
|
-
allowPageBreak: false
|
|
1620
|
-
};
|
|
1621
|
-
const h = measureTableHeight(doc, b, fakeEnv, styles, computeColumnPixelWidths2, measure);
|
|
1622
|
-
return mt + h + mb;
|
|
1548
|
+
const measureLine = (block) => {
|
|
1549
|
+
return (block.marginTop ?? 0) + (block.lineWidth ?? 1) + (block.marginBottom ?? 0);
|
|
1623
1550
|
};
|
|
1624
|
-
const measureColumns = (
|
|
1625
|
-
const mt =
|
|
1626
|
-
const mb =
|
|
1627
|
-
const blockLeft =
|
|
1628
|
-
const blockRight =
|
|
1551
|
+
const measureColumns = (block, env) => {
|
|
1552
|
+
const mt = block.marginTop ?? 0;
|
|
1553
|
+
const mb = block.marginBottom ?? 0;
|
|
1554
|
+
const blockLeft = block.marginLeft ?? 0;
|
|
1555
|
+
const blockRight = block.marginRight ?? 0;
|
|
1629
1556
|
const totalWidth = Math.max(env.innerWidth - blockLeft - blockRight, 1);
|
|
1630
|
-
const cols =
|
|
1557
|
+
const cols = block.columns ?? [];
|
|
1631
1558
|
const n = cols.length;
|
|
1632
1559
|
if (!n) return mt + mb;
|
|
1633
1560
|
let colWidths;
|
|
1634
|
-
const mode =
|
|
1635
|
-
let gap;
|
|
1561
|
+
const mode = block.mode ?? "fixedGap";
|
|
1636
1562
|
if (mode === "spaceBetween" && n > 1) {
|
|
1637
|
-
if (
|
|
1638
|
-
colWidths = computeColumnPixelWidths2(
|
|
1563
|
+
if (block.widths && block.widths.length === n) {
|
|
1564
|
+
colWidths = computeColumnPixelWidths2(block.widths, totalWidth);
|
|
1639
1565
|
} else {
|
|
1640
1566
|
colWidths = Array(n).fill(totalWidth / n);
|
|
1641
1567
|
}
|
|
1642
|
-
const totalColsWidth = colWidths.reduce((a, x) => a + x, 0);
|
|
1643
|
-
const remaining = Math.max(totalWidth - totalColsWidth, 0);
|
|
1644
|
-
gap = remaining / (n - 1);
|
|
1645
|
-
void gap;
|
|
1646
1568
|
} else {
|
|
1647
|
-
gap =
|
|
1569
|
+
const gap = block.gap ?? 20;
|
|
1648
1570
|
const totalGapsWidth = gap * (n - 1);
|
|
1649
1571
|
const widthForCols = Math.max(totalWidth - totalGapsWidth, 1);
|
|
1650
|
-
if (
|
|
1651
|
-
colWidths = computeColumnPixelWidths2(
|
|
1572
|
+
if (block.widths && block.widths.length === n) {
|
|
1573
|
+
colWidths = computeColumnPixelWidths2(block.widths, widthForCols);
|
|
1652
1574
|
} else {
|
|
1653
1575
|
colWidths = Array(n).fill(widthForCols / n);
|
|
1654
1576
|
}
|
|
1655
1577
|
}
|
|
1656
|
-
const heights = [];
|
|
1657
|
-
for (let i = 0; i < n; i++) {
|
|
1658
|
-
const colBlocks = cols[i] ?? [];
|
|
1659
|
-
const colEnv = {
|
|
1660
|
-
marginLeft: 0,
|
|
1661
|
-
innerWidth: colWidths[i],
|
|
1662
|
-
allowPageBreak: false
|
|
1663
|
-
};
|
|
1664
|
-
let
|
|
1665
|
-
for (const child of colBlocks) {
|
|
1666
|
-
if (!child || child.visible === false) continue;
|
|
1667
|
-
|
|
1668
|
-
}
|
|
1669
|
-
heights.push(
|
|
1578
|
+
const heights = [];
|
|
1579
|
+
for (let i = 0; i < n; i++) {
|
|
1580
|
+
const colBlocks = cols[i] ?? [];
|
|
1581
|
+
const colEnv = {
|
|
1582
|
+
marginLeft: 0,
|
|
1583
|
+
innerWidth: colWidths[i],
|
|
1584
|
+
allowPageBreak: false
|
|
1585
|
+
};
|
|
1586
|
+
let colHeight = 0;
|
|
1587
|
+
for (const child of colBlocks) {
|
|
1588
|
+
if (!child || child.visible === false) continue;
|
|
1589
|
+
colHeight += measure(child, colEnv);
|
|
1590
|
+
}
|
|
1591
|
+
heights.push(colHeight);
|
|
1592
|
+
}
|
|
1593
|
+
return mt + Math.max(...heights, 0) + mb;
|
|
1594
|
+
};
|
|
1595
|
+
const measureTable = (block, env) => {
|
|
1596
|
+
const mt = block.marginTop ?? 0;
|
|
1597
|
+
const mb = block.marginBottom ?? 0;
|
|
1598
|
+
const ml = block.marginLeft ?? 0;
|
|
1599
|
+
const mr = block.marginRight ?? 0;
|
|
1600
|
+
const innerWidth = Math.max(env.innerWidth - ml - mr, 1);
|
|
1601
|
+
const fakeEnv = {
|
|
1602
|
+
marginLeft: 0,
|
|
1603
|
+
innerWidth,
|
|
1604
|
+
allowPageBreak: false
|
|
1605
|
+
};
|
|
1606
|
+
const height = measureTableHeight(doc, block, fakeEnv, styles, computeColumnPixelWidths2, measure);
|
|
1607
|
+
return mt + height + mb;
|
|
1608
|
+
};
|
|
1609
|
+
const measureKeyValueGrid = (block, env) => {
|
|
1610
|
+
const mt = block.marginTop ?? 0;
|
|
1611
|
+
const mb = block.marginBottom ?? 0;
|
|
1612
|
+
const ml = block.marginLeft ?? 0;
|
|
1613
|
+
const mr = block.marginRight ?? 0;
|
|
1614
|
+
const totalWidth = Math.max(env.innerWidth - ml - mr, 1);
|
|
1615
|
+
const cols = block.columns ?? [];
|
|
1616
|
+
const colCount = cols.length;
|
|
1617
|
+
if (!colCount) return mt + mb;
|
|
1618
|
+
const rowGap = block.rowGap ?? 4;
|
|
1619
|
+
const keyValueGap = block.verticalKeyValueGap ?? 2;
|
|
1620
|
+
const separatorText = block.separator ?? "";
|
|
1621
|
+
const separatorWidth = separatorText ? doc.widthOfString(separatorText) : 0;
|
|
1622
|
+
let colWidths;
|
|
1623
|
+
if (block.columnWidths && block.columnWidths.length === colCount) {
|
|
1624
|
+
colWidths = computeColumnPixelWidths2(
|
|
1625
|
+
block.columnWidths.map((w) => w ?? "*"),
|
|
1626
|
+
totalWidth
|
|
1627
|
+
);
|
|
1628
|
+
} else {
|
|
1629
|
+
colWidths = Array(colCount).fill(totalWidth / colCount);
|
|
1630
|
+
}
|
|
1631
|
+
const measureTextHeight2 = (text, width) => {
|
|
1632
|
+
doc.fontSize(10);
|
|
1633
|
+
return doc.heightOfString(text ?? "", { width }) + 4;
|
|
1634
|
+
};
|
|
1635
|
+
let totalHeight = mt;
|
|
1636
|
+
if (block.orientation === "vertical") {
|
|
1637
|
+
let maxColHeight = 0;
|
|
1638
|
+
for (let colIndex = 0; colIndex < colCount; colIndex++) {
|
|
1639
|
+
const col = cols[colIndex] ?? [];
|
|
1640
|
+
const colWidth = colWidths[colIndex];
|
|
1641
|
+
let colHeight = 0;
|
|
1642
|
+
for (const item of col) {
|
|
1643
|
+
const keyHeight = measureTextHeight2(item.key ?? "", colWidth);
|
|
1644
|
+
const valueHeight = measureTextHeight2(item.value ?? "", colWidth);
|
|
1645
|
+
colHeight += keyHeight + keyValueGap + valueHeight + rowGap;
|
|
1646
|
+
}
|
|
1647
|
+
maxColHeight = Math.max(maxColHeight, colHeight);
|
|
1648
|
+
}
|
|
1649
|
+
totalHeight += maxColHeight;
|
|
1650
|
+
} else {
|
|
1651
|
+
const maxRows = Math.max(...cols.map((c) => c ? c.length : 0), 0);
|
|
1652
|
+
for (let rowIndex = 0; rowIndex < maxRows; rowIndex++) {
|
|
1653
|
+
let rowHeight = 0;
|
|
1654
|
+
for (let colIndex = 0; colIndex < colCount; colIndex++) {
|
|
1655
|
+
const item = cols[colIndex]?.[rowIndex];
|
|
1656
|
+
if (!item) continue;
|
|
1657
|
+
const colWidth = colWidths[colIndex];
|
|
1658
|
+
const keyWidth = block.keyWidth === "*" ? Math.max(colWidth * 0.35, 20) : block.keyWidth ?? 80;
|
|
1659
|
+
const keyHeight = measureTextHeight2(item.key ?? "", keyWidth);
|
|
1660
|
+
const valueXInsideCol = keyWidth + (separatorText ? separatorWidth + 4 : 4);
|
|
1661
|
+
const valueWidth = Math.max(colWidth - valueXInsideCol, 1);
|
|
1662
|
+
const valueHeight = measureTextHeight2(item.value ?? "", valueWidth);
|
|
1663
|
+
rowHeight = Math.max(rowHeight, keyHeight, valueHeight);
|
|
1664
|
+
}
|
|
1665
|
+
if (rowHeight > 0) {
|
|
1666
|
+
totalHeight += rowHeight + rowGap;
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
return totalHeight + mb;
|
|
1671
|
+
};
|
|
1672
|
+
const stripBoxModelProps = (block) => {
|
|
1673
|
+
return {
|
|
1674
|
+
...block,
|
|
1675
|
+
marginTop: 0,
|
|
1676
|
+
marginBottom: 0,
|
|
1677
|
+
marginLeft: 0,
|
|
1678
|
+
marginRight: 0,
|
|
1679
|
+
backgroundColor: void 0,
|
|
1680
|
+
backgroundImage: void 0,
|
|
1681
|
+
backgroundOpacity: void 0,
|
|
1682
|
+
backgroundBlocks: void 0,
|
|
1683
|
+
padding: void 0,
|
|
1684
|
+
paddingTop: void 0,
|
|
1685
|
+
paddingRight: void 0,
|
|
1686
|
+
paddingBottom: void 0,
|
|
1687
|
+
paddingLeft: void 0
|
|
1688
|
+
};
|
|
1689
|
+
};
|
|
1690
|
+
const measureRaw = (block, env) => {
|
|
1691
|
+
switch (block.type) {
|
|
1692
|
+
case "text":
|
|
1693
|
+
return measureText(block, env);
|
|
1694
|
+
case "image":
|
|
1695
|
+
return measureImage(block);
|
|
1696
|
+
case "qr":
|
|
1697
|
+
return measureQr(block);
|
|
1698
|
+
case "barcode":
|
|
1699
|
+
return measureBarcode(block);
|
|
1700
|
+
case "line":
|
|
1701
|
+
return measureLine(block);
|
|
1702
|
+
case "shape":
|
|
1703
|
+
return measureShape(block);
|
|
1704
|
+
case "columns":
|
|
1705
|
+
return measureColumns(block, env);
|
|
1706
|
+
case "table":
|
|
1707
|
+
return measureTable(block, env);
|
|
1708
|
+
case "keyValueGrid":
|
|
1709
|
+
return measureKeyValueGrid(block, env);
|
|
1710
|
+
case "list":
|
|
1711
|
+
return measureListHeight(doc, styles, block, env);
|
|
1712
|
+
case "signature":
|
|
1713
|
+
return block.height ?? 0;
|
|
1714
|
+
case "pageBreak":
|
|
1715
|
+
return 0;
|
|
1716
|
+
default:
|
|
1717
|
+
return 0;
|
|
1718
|
+
}
|
|
1719
|
+
};
|
|
1720
|
+
const measure = (block, env) => {
|
|
1721
|
+
if (!block || block.visible === false) return 0;
|
|
1722
|
+
if (env.isBackgroundLayer) {
|
|
1723
|
+
return measureRaw(block, env);
|
|
1724
|
+
}
|
|
1725
|
+
const padding = resolveBlockPadding(block);
|
|
1726
|
+
const hasBoxBackground = !!block.backgroundColor || !!block.backgroundImage || !!block.backgroundBlocks?.length;
|
|
1727
|
+
const shouldUseBoxModel = hasPadding(padding) || hasBoxBackground;
|
|
1728
|
+
if (!shouldUseBoxModel) {
|
|
1729
|
+
return measureRaw(block, env);
|
|
1730
|
+
}
|
|
1731
|
+
const marginTop = block.marginTop ?? 0;
|
|
1732
|
+
const marginBottom = block.marginBottom ?? 0;
|
|
1733
|
+
const marginLeft = block.marginLeft ?? 0;
|
|
1734
|
+
const marginRight = block.marginRight ?? 0;
|
|
1735
|
+
const outerWidth = Math.max(env.innerWidth - marginLeft - marginRight, 1);
|
|
1736
|
+
const innerWidth = Math.max(outerWidth - padding.left - padding.right, 1);
|
|
1737
|
+
const innerEnv = {
|
|
1738
|
+
...env,
|
|
1739
|
+
marginLeft: 0,
|
|
1740
|
+
innerWidth
|
|
1741
|
+
};
|
|
1742
|
+
const innerBlock = stripBoxModelProps(block);
|
|
1743
|
+
const innerHeight = measureRaw(innerBlock, innerEnv);
|
|
1744
|
+
return marginTop + padding.top + innerHeight + padding.bottom + marginBottom;
|
|
1745
|
+
};
|
|
1746
|
+
return (block, env) => measure(block, env);
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
// src/renderer-engine/utils/page-limit.ts
|
|
1750
|
+
function createBottomLimitForContent(doc, ctx) {
|
|
1751
|
+
return () => {
|
|
1752
|
+
const pageBottomForContent = doc.page.height - doc.page.margins.bottom;
|
|
1753
|
+
return ctx.signatureTopY ?? pageBottomForContent;
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
// src/renderer-engine/utils/block-renderer.ts
|
|
1758
|
+
function createBlockRenderer(deps) {
|
|
1759
|
+
const { doc, ctx, styles, computeColumnPixelWidths: computeColumnPixelWidths2, finishPage: finishPage2, processSignatureBlock, defaultImage } = deps;
|
|
1760
|
+
const bottomLimitForContent = createBottomLimitForContent(doc, ctx);
|
|
1761
|
+
const ensureSpaceFor = createEnsureSpaceFor(ctx, doc, bottomLimitForContent, finishPage2);
|
|
1762
|
+
const measureBlockHeight = createMeasureBlockHeight({
|
|
1763
|
+
doc,
|
|
1764
|
+
styles,
|
|
1765
|
+
computeColumnPixelWidths: computeColumnPixelWidths2
|
|
1766
|
+
});
|
|
1767
|
+
const stripBoxModelProps = (block) => {
|
|
1768
|
+
return {
|
|
1769
|
+
...block,
|
|
1770
|
+
marginTop: 0,
|
|
1771
|
+
marginBottom: 0,
|
|
1772
|
+
marginLeft: 0,
|
|
1773
|
+
marginRight: 0,
|
|
1774
|
+
backgroundColor: void 0,
|
|
1775
|
+
backgroundImage: void 0,
|
|
1776
|
+
backgroundOpacity: void 0,
|
|
1777
|
+
backgroundBlocks: void 0,
|
|
1778
|
+
padding: void 0,
|
|
1779
|
+
paddingTop: void 0,
|
|
1780
|
+
paddingRight: void 0,
|
|
1781
|
+
paddingBottom: void 0,
|
|
1782
|
+
paddingLeft: void 0
|
|
1783
|
+
};
|
|
1784
|
+
};
|
|
1785
|
+
const renderBlockCore = (block, y, env) => {
|
|
1786
|
+
if (block.visible === false) {
|
|
1787
|
+
return y ?? ctx.currentY;
|
|
1788
|
+
}
|
|
1789
|
+
switch (block.type) {
|
|
1790
|
+
case "text":
|
|
1791
|
+
return processTextBlock(doc, ctx, styles, block, y, env, ensureSpaceFor);
|
|
1792
|
+
case "image":
|
|
1793
|
+
return processImageBlock(doc, ctx, block, y, env, ensureSpaceFor, defaultImage);
|
|
1794
|
+
case "qr": {
|
|
1795
|
+
const qb = block;
|
|
1796
|
+
const imageLike = {
|
|
1797
|
+
type: "image",
|
|
1798
|
+
src: qb.src,
|
|
1799
|
+
width: qb.size,
|
|
1800
|
+
height: qb.size,
|
|
1801
|
+
align: qb.align,
|
|
1802
|
+
marginTop: qb.marginTop,
|
|
1803
|
+
marginBottom: qb.marginBottom,
|
|
1804
|
+
marginLeft: qb.marginLeft,
|
|
1805
|
+
marginRight: qb.marginRight
|
|
1806
|
+
};
|
|
1807
|
+
return processImageBlock(doc, ctx, imageLike, y, env, ensureSpaceFor, defaultImage);
|
|
1808
|
+
}
|
|
1809
|
+
case "barcode":
|
|
1810
|
+
return processBarcodeBlock(doc, ctx, block, y, env, ensureSpaceFor);
|
|
1811
|
+
case "line":
|
|
1812
|
+
return processLineBlock(doc, ctx, block, y, env, ensureSpaceFor);
|
|
1813
|
+
case "shape":
|
|
1814
|
+
return processShapeBlock(doc, ctx, block, y, env, ensureSpaceFor);
|
|
1815
|
+
case "columns":
|
|
1816
|
+
return processColumnsBlock(
|
|
1817
|
+
ctx,
|
|
1818
|
+
block,
|
|
1819
|
+
y,
|
|
1820
|
+
env,
|
|
1821
|
+
computeColumnPixelWidths2,
|
|
1822
|
+
renderBlock,
|
|
1823
|
+
ensureSpaceFor,
|
|
1824
|
+
measureBlockHeight
|
|
1825
|
+
);
|
|
1826
|
+
case "table":
|
|
1827
|
+
return processTableBlock(
|
|
1828
|
+
doc,
|
|
1829
|
+
ctx,
|
|
1830
|
+
styles,
|
|
1831
|
+
block,
|
|
1832
|
+
y,
|
|
1833
|
+
env,
|
|
1834
|
+
computeColumnPixelWidths2,
|
|
1835
|
+
bottomLimitForContent,
|
|
1836
|
+
finishPage2,
|
|
1837
|
+
renderBlock,
|
|
1838
|
+
measureBlockHeight
|
|
1839
|
+
);
|
|
1840
|
+
case "keyValueGrid":
|
|
1841
|
+
return processKeyValueGridBlock(
|
|
1842
|
+
doc,
|
|
1843
|
+
ctx,
|
|
1844
|
+
styles,
|
|
1845
|
+
block,
|
|
1846
|
+
y,
|
|
1847
|
+
env,
|
|
1848
|
+
computeColumnPixelWidths2,
|
|
1849
|
+
ensureSpaceFor
|
|
1850
|
+
);
|
|
1851
|
+
case "list":
|
|
1852
|
+
return processListBlock(
|
|
1853
|
+
doc,
|
|
1854
|
+
ctx,
|
|
1855
|
+
styles,
|
|
1856
|
+
block,
|
|
1857
|
+
y,
|
|
1858
|
+
env,
|
|
1859
|
+
ensureSpaceFor
|
|
1860
|
+
);
|
|
1861
|
+
case "pageBreak":
|
|
1862
|
+
finishPage2(true);
|
|
1863
|
+
return ctx.currentY;
|
|
1864
|
+
case "signature":
|
|
1865
|
+
processSignatureBlock(block);
|
|
1866
|
+
return y ?? ctx.currentY;
|
|
1867
|
+
default:
|
|
1868
|
+
throw new PdfEngineError({
|
|
1869
|
+
code: "PDF_ERROR_BLOCK_UNSUPPORTED" /* PDF_ERROR_BLOCK_UNSUPPORTED */,
|
|
1870
|
+
message: `Unsupported block type: ${block.type}`,
|
|
1871
|
+
statusCode: 422,
|
|
1872
|
+
details: { block }
|
|
1873
|
+
});
|
|
1874
|
+
}
|
|
1875
|
+
};
|
|
1876
|
+
const renderBlock = (block, y, env) => {
|
|
1877
|
+
if (block.visible === false) {
|
|
1878
|
+
return y ?? ctx.currentY;
|
|
1879
|
+
}
|
|
1880
|
+
if (env.isBackgroundLayer) {
|
|
1881
|
+
return renderBlockCore(block, y, env);
|
|
1882
|
+
}
|
|
1883
|
+
const padding = resolveBlockPadding(block);
|
|
1884
|
+
const hasBoxBackground = !!block.backgroundColor || !!block.backgroundImage || !!block.backgroundBlocks?.length;
|
|
1885
|
+
const shouldUseBoxModel = hasPadding(padding) || hasBoxBackground;
|
|
1886
|
+
if (!shouldUseBoxModel) {
|
|
1887
|
+
return renderBlockCore(block, y, env);
|
|
1888
|
+
}
|
|
1889
|
+
const marginTop = block.marginTop ?? 0;
|
|
1890
|
+
const marginBottom = block.marginBottom ?? 0;
|
|
1891
|
+
const marginLeft = block.marginLeft ?? 0;
|
|
1892
|
+
const marginRight = block.marginRight ?? 0;
|
|
1893
|
+
const outerX = env.marginLeft + marginLeft;
|
|
1894
|
+
const outerWidth = Math.max(env.innerWidth - marginLeft - marginRight, 1);
|
|
1895
|
+
const innerX = outerX + padding.left;
|
|
1896
|
+
const innerWidth = Math.max(outerWidth - padding.left - padding.right, 1);
|
|
1897
|
+
const innerEnv = {
|
|
1898
|
+
...env,
|
|
1899
|
+
marginLeft: innerX,
|
|
1900
|
+
innerWidth
|
|
1901
|
+
};
|
|
1902
|
+
const innerBlock = stripBoxModelProps(block);
|
|
1903
|
+
const innerHeight = measureBlockHeight(innerBlock, innerEnv);
|
|
1904
|
+
const boxHeight = padding.top + innerHeight + padding.bottom;
|
|
1905
|
+
const totalHeight = marginTop + boxHeight + marginBottom;
|
|
1906
|
+
if (y === null && env.allowPageBreak !== false && block.type !== "table") {
|
|
1907
|
+
ensureSpaceFor(totalHeight, env);
|
|
1908
|
+
}
|
|
1909
|
+
const finalOuterY = (y ?? ctx.currentY) + marginTop;
|
|
1910
|
+
if (block.backgroundColor) {
|
|
1911
|
+
doc.save();
|
|
1912
|
+
doc.fillOpacity(block.backgroundOpacity ?? 1).rect(outerX, finalOuterY, outerWidth, boxHeight).fill(block.backgroundColor);
|
|
1913
|
+
doc.restore();
|
|
1914
|
+
}
|
|
1915
|
+
if (block.backgroundImage) {
|
|
1916
|
+
try {
|
|
1917
|
+
doc.save();
|
|
1918
|
+
doc.opacity(block.backgroundOpacity ?? 1);
|
|
1919
|
+
doc.image(block.backgroundImage, outerX, finalOuterY, {
|
|
1920
|
+
width: outerWidth,
|
|
1921
|
+
height: boxHeight
|
|
1922
|
+
});
|
|
1923
|
+
doc.restore();
|
|
1924
|
+
} catch (_) {
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
if (block.backgroundBlocks?.length) {
|
|
1928
|
+
doc.save();
|
|
1929
|
+
doc.opacity(block.backgroundOpacity ?? 1);
|
|
1930
|
+
renderBlockArray(block.backgroundBlocks, finalOuterY, {
|
|
1931
|
+
...env,
|
|
1932
|
+
marginLeft: outerX,
|
|
1933
|
+
innerWidth: outerWidth,
|
|
1934
|
+
allowPageBreak: false,
|
|
1935
|
+
isBackgroundLayer: true
|
|
1936
|
+
});
|
|
1937
|
+
doc.restore();
|
|
1938
|
+
}
|
|
1939
|
+
const innerEndY = renderBlockCore(innerBlock, finalOuterY + padding.top, innerEnv);
|
|
1940
|
+
const newY = innerEndY + padding.bottom + marginBottom;
|
|
1941
|
+
if (y === null) {
|
|
1942
|
+
ctx.currentY = newY;
|
|
1943
|
+
}
|
|
1944
|
+
return newY;
|
|
1945
|
+
};
|
|
1946
|
+
const renderBlockArray = (blocks, startY, env) => {
|
|
1947
|
+
let localY = startY;
|
|
1948
|
+
for (const block of blocks) {
|
|
1949
|
+
localY = renderBlock(block, localY, env);
|
|
1950
|
+
}
|
|
1951
|
+
return localY;
|
|
1952
|
+
};
|
|
1953
|
+
return { renderBlock, renderBlockArray, measureBlockHeight };
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
// src/renderer-engine/utils/context.ts
|
|
1957
|
+
function createInitialContext(doc) {
|
|
1958
|
+
return {
|
|
1959
|
+
pageNumber: 1,
|
|
1960
|
+
currentY: doc.page.margins.top,
|
|
1961
|
+
signatureBlock: null,
|
|
1962
|
+
signatureTopY: null,
|
|
1963
|
+
signatureHeight: 0,
|
|
1964
|
+
signaturePlaced: false,
|
|
1965
|
+
afterSignature: false,
|
|
1966
|
+
inFooter: false,
|
|
1967
|
+
inManualPageAdd: false
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
// src/renderer-engine/utils/env.ts
|
|
1972
|
+
var contentEnv = (doc) => ({
|
|
1973
|
+
marginLeft: doc.page.margins.left,
|
|
1974
|
+
innerWidth: doc.page.width - doc.page.margins.left - doc.page.margins.right,
|
|
1975
|
+
allowPageBreak: true
|
|
1976
|
+
});
|
|
1977
|
+
|
|
1978
|
+
// src/renderer-engine/utils/finish-page.ts
|
|
1979
|
+
function finishPage({
|
|
1980
|
+
addNewPage,
|
|
1981
|
+
doc,
|
|
1982
|
+
def,
|
|
1983
|
+
ctx,
|
|
1984
|
+
footerBandHeight,
|
|
1985
|
+
renderBlockArray,
|
|
1986
|
+
startNewPageLayout
|
|
1987
|
+
}) {
|
|
1988
|
+
const mode = def.watermark?.mode;
|
|
1989
|
+
if (def.watermark && watermarkUsesLast(mode)) {
|
|
1990
|
+
const isLast = !addNewPage;
|
|
1991
|
+
drawWatermarkForPage(doc, def.watermark, ctx.pageNumber, isLast);
|
|
1992
|
+
}
|
|
1993
|
+
if (ctx.signatureBlock && ctx.signatureTopY !== null) {
|
|
1994
|
+
const env = contentEnv(doc);
|
|
1995
|
+
drawSignatureBlock(doc, ctx.signatureBlock, ctx.signatureTopY, env, renderBlockArray);
|
|
1996
|
+
}
|
|
1997
|
+
drawFooter(doc, def, ctx, footerBandHeight, renderBlockArray);
|
|
1998
|
+
if (addNewPage) {
|
|
1999
|
+
ctx.inManualPageAdd = true;
|
|
2000
|
+
doc.addPage();
|
|
2001
|
+
ctx.inManualPageAdd = false;
|
|
2002
|
+
ctx.pageNumber += 1;
|
|
2003
|
+
startNewPageLayout();
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
// src/renderer-engine/utils/footer.ts
|
|
2008
|
+
function normalizeFooter(input, ctx, doc) {
|
|
2009
|
+
if (!input) return null;
|
|
2010
|
+
if (typeof input === "function") {
|
|
2011
|
+
const result = input(ctx.pageNumber, {
|
|
2012
|
+
width: doc.page.width,
|
|
2013
|
+
height: doc.page.height
|
|
2014
|
+
});
|
|
2015
|
+
if (!result) return null;
|
|
2016
|
+
return normalizeFooter(result, ctx, doc);
|
|
2017
|
+
}
|
|
2018
|
+
if (input.blocks && Array.isArray(input.blocks)) {
|
|
2019
|
+
const footer = input;
|
|
2020
|
+
return {
|
|
2021
|
+
visible: footer.visible,
|
|
2022
|
+
blocks: footer.blocks,
|
|
2023
|
+
backgroundBlocks: footer.backgroundBlocks,
|
|
2024
|
+
marginTop: footer.marginTop,
|
|
2025
|
+
marginBottom: footer.marginBottom,
|
|
2026
|
+
marginLeft: footer.marginLeft,
|
|
2027
|
+
marginRight: footer.marginRight,
|
|
2028
|
+
padding: footer.padding,
|
|
2029
|
+
paddingTop: footer.paddingTop,
|
|
2030
|
+
paddingRight: footer.paddingRight,
|
|
2031
|
+
paddingBottom: footer.paddingBottom,
|
|
2032
|
+
paddingLeft: footer.paddingLeft,
|
|
2033
|
+
backgroundColor: footer.backgroundColor,
|
|
2034
|
+
backgroundImage: footer.backgroundImage,
|
|
2035
|
+
backgroundOpacity: footer.backgroundOpacity
|
|
2036
|
+
};
|
|
2037
|
+
}
|
|
2038
|
+
if (input.type) {
|
|
2039
|
+
return { blocks: [input] };
|
|
2040
|
+
}
|
|
2041
|
+
if (Array.isArray(input)) {
|
|
2042
|
+
return { blocks: input };
|
|
2043
|
+
}
|
|
2044
|
+
return null;
|
|
2045
|
+
}
|
|
2046
|
+
function drawFooter(doc, def, ctx, footerBandHeight, renderBlockArray) {
|
|
2047
|
+
const footerConfig = def.footer;
|
|
2048
|
+
if (!footerConfig) return;
|
|
2049
|
+
const layout = normalizeFooter(footerConfig, ctx, doc);
|
|
2050
|
+
if (!layout) return;
|
|
2051
|
+
if (layout.visible === false) return;
|
|
2052
|
+
const blocks = layout.blocks ?? [];
|
|
2053
|
+
const backgroundBlocks = layout.backgroundBlocks ?? [];
|
|
2054
|
+
if (!blocks.length && !backgroundBlocks.length && !layout.backgroundColor && !layout.backgroundImage) {
|
|
2055
|
+
return;
|
|
2056
|
+
}
|
|
2057
|
+
const bandHeight = footerBandHeight;
|
|
2058
|
+
if (!bandHeight) return;
|
|
2059
|
+
const footerMarginLeft = layout.marginLeft ?? 0;
|
|
2060
|
+
const footerMarginRight = layout.marginRight ?? 0;
|
|
2061
|
+
const outerX = footerMarginLeft;
|
|
2062
|
+
const outerWidth = doc.page.width - footerMarginLeft - footerMarginRight;
|
|
2063
|
+
const padding = resolveFooterPadding(layout);
|
|
2064
|
+
const innerX = outerX + padding.left;
|
|
2065
|
+
const innerWidth = Math.max(outerWidth - padding.left - padding.right, 1);
|
|
2066
|
+
const outerEnv = {
|
|
2067
|
+
marginLeft: outerX,
|
|
2068
|
+
innerWidth: outerWidth,
|
|
2069
|
+
allowPageBreak: false
|
|
2070
|
+
};
|
|
2071
|
+
const innerEnv = {
|
|
2072
|
+
marginLeft: innerX,
|
|
2073
|
+
innerWidth,
|
|
2074
|
+
allowPageBreak: false
|
|
2075
|
+
};
|
|
2076
|
+
const pageHeight = doc.page.height;
|
|
2077
|
+
const bandTop = pageHeight - bandHeight;
|
|
2078
|
+
const backgroundOpacity = layout.backgroundOpacity ?? 1;
|
|
2079
|
+
if (layout.backgroundColor) {
|
|
2080
|
+
doc.save();
|
|
2081
|
+
doc.fillOpacity(backgroundOpacity).rect(0, bandTop, doc.page.width, bandHeight).fill(layout.backgroundColor);
|
|
2082
|
+
doc.restore();
|
|
2083
|
+
}
|
|
2084
|
+
if (layout.backgroundImage) {
|
|
2085
|
+
try {
|
|
2086
|
+
doc.save();
|
|
2087
|
+
doc.opacity(backgroundOpacity);
|
|
2088
|
+
doc.image(layout.backgroundImage, 0, bandTop, {
|
|
2089
|
+
width: doc.page.width,
|
|
2090
|
+
height: bandHeight
|
|
2091
|
+
});
|
|
2092
|
+
doc.restore();
|
|
2093
|
+
} catch (e) {
|
|
2094
|
+
console.warn("Failed to load footer background image:", e);
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
doc.save();
|
|
2098
|
+
doc.rect(outerX, bandTop, outerWidth, bandHeight).clip();
|
|
2099
|
+
doc.translate(0, bandTop);
|
|
2100
|
+
ctx.inFooter = true;
|
|
2101
|
+
const localStartY = layout.marginTop ?? 4;
|
|
2102
|
+
if (backgroundBlocks.length) {
|
|
2103
|
+
doc.save();
|
|
2104
|
+
doc.opacity(backgroundOpacity);
|
|
2105
|
+
renderBlockArray(backgroundBlocks, localStartY, {
|
|
2106
|
+
...outerEnv,
|
|
2107
|
+
allowPageBreak: false,
|
|
2108
|
+
isBackgroundLayer: true
|
|
2109
|
+
});
|
|
2110
|
+
doc.restore();
|
|
2111
|
+
}
|
|
2112
|
+
if (blocks.length) {
|
|
2113
|
+
renderBlockArray(blocks, localStartY + padding.top, innerEnv);
|
|
2114
|
+
}
|
|
2115
|
+
ctx.inFooter = false;
|
|
2116
|
+
doc.restore();
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
// src/renderer-engine/utils/header.ts
|
|
2120
|
+
function drawHeader(doc, def, headerBandHeight, header, renderBlockArray) {
|
|
2121
|
+
if (!header) return;
|
|
2122
|
+
if (header.visible === false) return;
|
|
2123
|
+
if (!headerBandHeight) return;
|
|
2124
|
+
const blocks = header.blocks ?? [];
|
|
2125
|
+
const backgroundBlocks = header.backgroundBlocks ?? [];
|
|
2126
|
+
if (!blocks.length && !backgroundBlocks.length && !header.backgroundColor && !header.backgroundImage) {
|
|
2127
|
+
return;
|
|
2128
|
+
}
|
|
2129
|
+
const bandTop = 0;
|
|
2130
|
+
const bandHeight = def.margins.top;
|
|
2131
|
+
const headerMarginLeft = header.marginLeft ?? 0;
|
|
2132
|
+
const headerMarginRight = header.marginRight ?? 0;
|
|
2133
|
+
const outerX = headerMarginLeft;
|
|
2134
|
+
const outerWidth = doc.page.width - headerMarginLeft - headerMarginRight;
|
|
2135
|
+
const padding = resolveHeaderPadding(header);
|
|
2136
|
+
const innerX = outerX + padding.left;
|
|
2137
|
+
const innerWidth = Math.max(outerWidth - padding.left - padding.right, 1);
|
|
2138
|
+
const outerEnv = {
|
|
2139
|
+
marginLeft: outerX,
|
|
2140
|
+
innerWidth: outerWidth,
|
|
2141
|
+
allowPageBreak: false
|
|
2142
|
+
};
|
|
2143
|
+
const innerEnv = {
|
|
2144
|
+
marginLeft: innerX,
|
|
2145
|
+
innerWidth,
|
|
2146
|
+
allowPageBreak: false
|
|
2147
|
+
};
|
|
2148
|
+
const backgroundOpacity = header.backgroundOpacity ?? 1;
|
|
2149
|
+
if (header.backgroundColor) {
|
|
2150
|
+
doc.save();
|
|
2151
|
+
doc.fillOpacity(backgroundOpacity).rect(0, bandTop, doc.page.width, bandHeight).fill(header.backgroundColor);
|
|
2152
|
+
doc.restore();
|
|
2153
|
+
}
|
|
2154
|
+
if (header.backgroundImage) {
|
|
2155
|
+
try {
|
|
2156
|
+
doc.save();
|
|
2157
|
+
doc.opacity(backgroundOpacity);
|
|
2158
|
+
doc.image(header.backgroundImage, 0, bandTop, {
|
|
2159
|
+
width: doc.page.width,
|
|
2160
|
+
height: bandHeight
|
|
2161
|
+
});
|
|
2162
|
+
doc.restore();
|
|
2163
|
+
} catch (e) {
|
|
2164
|
+
console.warn("Failed to load header background image:", e);
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
doc.save();
|
|
2168
|
+
doc.rect(outerX, bandTop, outerWidth, bandHeight).clip();
|
|
2169
|
+
const startY = bandTop + (header.marginTop ?? 0);
|
|
2170
|
+
if (backgroundBlocks.length) {
|
|
2171
|
+
doc.save();
|
|
2172
|
+
doc.opacity(backgroundOpacity);
|
|
2173
|
+
renderBlockArray(backgroundBlocks, startY, {
|
|
2174
|
+
...outerEnv,
|
|
2175
|
+
allowPageBreak: false,
|
|
2176
|
+
isBackgroundLayer: true
|
|
2177
|
+
});
|
|
2178
|
+
doc.restore();
|
|
2179
|
+
}
|
|
2180
|
+
if (blocks.length) {
|
|
2181
|
+
renderBlockArray(blocks, startY + padding.top, innerEnv);
|
|
2182
|
+
}
|
|
2183
|
+
doc.restore();
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
// src/renderer-engine/utils/image-loader.ts
|
|
2187
|
+
var import_axios = __toESM(require("axios"));
|
|
2188
|
+
async function normalizeImageSrc(src, fallback) {
|
|
2189
|
+
if (!src) return fallback ?? src;
|
|
2190
|
+
if (Buffer.isBuffer(src)) return src;
|
|
2191
|
+
if (src.startsWith("data:")) {
|
|
2192
|
+
const commaIdx = src.indexOf(",");
|
|
2193
|
+
if (commaIdx !== -1) {
|
|
2194
|
+
return Buffer.from(src.slice(commaIdx + 1), "base64");
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
if (src.startsWith("http://") || src.startsWith("https://")) {
|
|
2198
|
+
try {
|
|
2199
|
+
const res = await import_axios.default.get(src, {
|
|
2200
|
+
responseType: "arraybuffer"
|
|
2201
|
+
});
|
|
2202
|
+
return Buffer.from(res.data);
|
|
2203
|
+
} catch (e) {
|
|
2204
|
+
if (fallback !== void 0) {
|
|
2205
|
+
console.warn(`Failed to fetch remote image "${src}", using default image.`);
|
|
2206
|
+
return fallback;
|
|
2207
|
+
}
|
|
2208
|
+
throw toPdfEngineError(e, {
|
|
2209
|
+
code: "PDF_ERROR_IMAGE_FETCH_FAILED" /* PDF_ERROR_IMAGE_FETCH_FAILED */,
|
|
2210
|
+
message: "Failed to fetch remote image for PDF.",
|
|
2211
|
+
statusCode: 422,
|
|
2212
|
+
details: { url: src },
|
|
2213
|
+
retryable: false
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
return src;
|
|
2218
|
+
}
|
|
2219
|
+
async function materializeImagesInBlocks(blocks, fallback) {
|
|
2220
|
+
const out = [];
|
|
2221
|
+
for (const block of blocks) {
|
|
2222
|
+
const blockAny = { ...block };
|
|
2223
|
+
if (blockAny.backgroundImage) {
|
|
2224
|
+
blockAny.backgroundImage = await normalizeImageSrc(blockAny.backgroundImage, fallback);
|
|
1670
2225
|
}
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
};
|
|
1674
|
-
const measureKeyValueGrid = (b, env) => {
|
|
1675
|
-
const mt = b.marginTop ?? 0;
|
|
1676
|
-
const mb = b.marginBottom ?? 0;
|
|
1677
|
-
const rowGap = b.rowGap ?? 4;
|
|
1678
|
-
const orientation = b.orientation ?? "horizontal";
|
|
1679
|
-
const cols = b.columns ?? [];
|
|
1680
|
-
const colCount = cols.length;
|
|
1681
|
-
if (!colCount) return mt + mb;
|
|
1682
|
-
const blockLeft = b.marginLeft ?? 0;
|
|
1683
|
-
const blockRight = b.marginRight ?? 0;
|
|
1684
|
-
const totalWidth = Math.max(env.innerWidth - blockLeft - blockRight, 1);
|
|
1685
|
-
let colWidths;
|
|
1686
|
-
if (b.columnWidths && b.columnWidths.length === colCount) {
|
|
1687
|
-
const safe = b.columnWidths.map((w) => w === void 0 ? "*" : w);
|
|
1688
|
-
colWidths = computeColumnPixelWidths2(safe, totalWidth);
|
|
1689
|
-
} else {
|
|
1690
|
-
colWidths = Array(colCount).fill(totalWidth / colCount);
|
|
2226
|
+
if (blockAny.backgroundBlocks?.length) {
|
|
2227
|
+
blockAny.backgroundBlocks = await materializeImagesInBlocks(blockAny.backgroundBlocks, fallback);
|
|
1691
2228
|
}
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
if (
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
2229
|
+
if (block.type === "image") {
|
|
2230
|
+
const img = { ...blockAny };
|
|
2231
|
+
img.src = await normalizeImageSrc(img.src, fallback);
|
|
2232
|
+
out.push(img);
|
|
2233
|
+
} else if (block.type === "columns") {
|
|
2234
|
+
out.push({
|
|
2235
|
+
...blockAny,
|
|
2236
|
+
columns: await Promise.all(
|
|
2237
|
+
block.columns.map((col) => materializeImagesInBlocks(col, fallback))
|
|
2238
|
+
)
|
|
2239
|
+
});
|
|
2240
|
+
} else if (block.type === "signature" && block.blocks) {
|
|
2241
|
+
out.push({
|
|
2242
|
+
...blockAny,
|
|
2243
|
+
blocks: await materializeImagesInBlocks(block.blocks, fallback)
|
|
2244
|
+
});
|
|
2245
|
+
} else if (block.type === "table") {
|
|
2246
|
+
const tb = blockAny;
|
|
2247
|
+
const newBody = await Promise.all(
|
|
2248
|
+
tb.body.map(
|
|
2249
|
+
async (entry) => {
|
|
2250
|
+
const materializeCell = async (cell) => {
|
|
2251
|
+
if (!cell.blocks?.length) return cell;
|
|
2252
|
+
return {
|
|
2253
|
+
...cell,
|
|
2254
|
+
blocks: await materializeImagesInBlocks(cell.blocks, fallback)
|
|
2255
|
+
};
|
|
2256
|
+
};
|
|
2257
|
+
if (Array.isArray(entry)) {
|
|
2258
|
+
return await Promise.all(entry.map(materializeCell));
|
|
2259
|
+
}
|
|
2260
|
+
if (entry && typeof entry === "object" && "content" in entry && Array.isArray(entry.content)) {
|
|
2261
|
+
return {
|
|
2262
|
+
...entry,
|
|
2263
|
+
content: await Promise.all(entry.content.map(materializeCell))
|
|
2264
|
+
};
|
|
2265
|
+
}
|
|
2266
|
+
return entry;
|
|
2267
|
+
}
|
|
2268
|
+
)
|
|
2269
|
+
);
|
|
2270
|
+
out.push({ ...tb, body: newBody });
|
|
1718
2271
|
} else {
|
|
1719
|
-
|
|
1720
|
-
for (let rowIndex = 0; rowIndex < maxRows; rowIndex++) {
|
|
1721
|
-
let rowHeight = 0;
|
|
1722
|
-
for (let colIndex = 0; colIndex < colCount; colIndex++) {
|
|
1723
|
-
const item = cols[colIndex]?.[rowIndex];
|
|
1724
|
-
if (!item) continue;
|
|
1725
|
-
const colWidth = colWidths[colIndex];
|
|
1726
|
-
const keyWidthPx = b.keyWidth === "*" ? Math.max(colWidth * baseKeyWidthRatio, 20) : b.keyWidth ?? 80;
|
|
1727
|
-
const keyH = measureKVText({ type: "text", text: item.key ?? "" }, keyWidthPx);
|
|
1728
|
-
const valueXInsideCol = keyWidthPx + (separatorText ? sepBoxWidth + 4 : 4);
|
|
1729
|
-
const valueWidth = Math.max(colWidth - valueXInsideCol, 1);
|
|
1730
|
-
const valH = measureKVText({ type: "text", text: item.value ?? "" }, valueWidth);
|
|
1731
|
-
rowHeight = Math.max(rowHeight, Math.max(keyH, valH));
|
|
1732
|
-
}
|
|
1733
|
-
if (rowHeight > 0) totalHeight += rowHeight + rowGap;
|
|
1734
|
-
}
|
|
1735
|
-
}
|
|
1736
|
-
totalHeight += mb;
|
|
1737
|
-
return totalHeight;
|
|
1738
|
-
};
|
|
1739
|
-
const measure = (block, env) => {
|
|
1740
|
-
if (!block || block.visible === false) return 0;
|
|
1741
|
-
switch (block.type) {
|
|
1742
|
-
case "text":
|
|
1743
|
-
return measureText(block, env);
|
|
1744
|
-
case "image":
|
|
1745
|
-
return measureImage(block);
|
|
1746
|
-
case "qr":
|
|
1747
|
-
return measureQr(block);
|
|
1748
|
-
case "barcode":
|
|
1749
|
-
return measureBarcode(block);
|
|
1750
|
-
case "line":
|
|
1751
|
-
return measureLine(block);
|
|
1752
|
-
case "table":
|
|
1753
|
-
return measureTable(block, env);
|
|
1754
|
-
case "columns":
|
|
1755
|
-
return measureColumns(block, env);
|
|
1756
|
-
case "keyValueGrid":
|
|
1757
|
-
return measureKeyValueGrid(block, env);
|
|
1758
|
-
case "signature":
|
|
1759
|
-
return block.height ?? 0;
|
|
1760
|
-
case "pageBreak":
|
|
1761
|
-
return 0;
|
|
1762
|
-
default:
|
|
1763
|
-
return 0;
|
|
2272
|
+
out.push(blockAny);
|
|
1764
2273
|
}
|
|
1765
|
-
}
|
|
1766
|
-
return
|
|
2274
|
+
}
|
|
2275
|
+
return out;
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
// src/renderer-engine/utils/layout.ts
|
|
2279
|
+
function computeColumnPixelWidths(widths, totalWidth) {
|
|
2280
|
+
let fixedTotal = 0;
|
|
2281
|
+
let starCount = 0;
|
|
2282
|
+
widths.forEach((w) => {
|
|
2283
|
+
if (w === "*") starCount++;
|
|
2284
|
+
else fixedTotal += w;
|
|
2285
|
+
});
|
|
2286
|
+
const remaining = Math.max(totalWidth - fixedTotal, 0);
|
|
2287
|
+
const starWidth = starCount > 0 ? remaining / starCount : 0;
|
|
2288
|
+
return widths.map((w) => w === "*" ? starWidth : w);
|
|
1767
2289
|
}
|
|
1768
2290
|
|
|
1769
2291
|
// src/renderer-engine/utils/page-background.ts
|
|
@@ -1788,12 +2310,10 @@ function drawPageBackground(doc, pageBackground) {
|
|
|
1788
2310
|
doc.opacity(1);
|
|
1789
2311
|
}
|
|
1790
2312
|
|
|
1791
|
-
// src/renderer-engine/utils/page-
|
|
1792
|
-
function
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
return ctx.signatureTopY ?? pageBottomForContent;
|
|
1796
|
-
};
|
|
2313
|
+
// src/renderer-engine/utils/page-flow.ts
|
|
2314
|
+
function getBottomLimitForContent(doc, ctx) {
|
|
2315
|
+
const pageBottomForContent = doc.page.height - doc.page.margins.bottom;
|
|
2316
|
+
return ctx.signatureTopY ?? pageBottomForContent;
|
|
1797
2317
|
}
|
|
1798
2318
|
|
|
1799
2319
|
// src/renderer-engine/utils/qr-bar-code.ts
|
|
@@ -1816,7 +2336,10 @@ function generateQrBuffer(value, size, version, errorCorrectionLevel) {
|
|
|
1816
2336
|
code: "PDF_ERROR_QR_GENERATION_FAILED" /* PDF_ERROR_QR_GENERATION_FAILED */,
|
|
1817
2337
|
message: "Failed to generate QR code.",
|
|
1818
2338
|
statusCode: 500,
|
|
1819
|
-
details: {
|
|
2339
|
+
details: {
|
|
2340
|
+
valuePreview: String(value).slice(0, 60),
|
|
2341
|
+
size
|
|
2342
|
+
},
|
|
1820
2343
|
retryable: false
|
|
1821
2344
|
})
|
|
1822
2345
|
);
|
|
@@ -1843,109 +2366,101 @@ function mapBarcodeTypeToBcid(bcType) {
|
|
|
1843
2366
|
function generateBarcodeBuffer(value, options = {}) {
|
|
1844
2367
|
const { bcType, scale = 3, barHeight = 10, includetext = false, textalign = "center" } = options;
|
|
1845
2368
|
const bcid = mapBarcodeTypeToBcid(bcType);
|
|
2369
|
+
if (bcType === "EAN13" && !/^\d{12,13}$/.test(value)) {
|
|
2370
|
+
throw toPdfEngineError(new Error("Invalid EAN13 value"), {
|
|
2371
|
+
code: "PDF_ERROR_BARCODE_EAN13_INVALID_LENGTH" /* PDF_ERROR_BARCODE_EAN13_INVALID_LENGTH */,
|
|
2372
|
+
message: "EAN13 barcode value must be 12 or 13 digits.",
|
|
2373
|
+
statusCode: 422,
|
|
2374
|
+
details: { value },
|
|
2375
|
+
retryable: false
|
|
2376
|
+
});
|
|
2377
|
+
}
|
|
1846
2378
|
return new Promise((resolve, reject) => {
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
};
|
|
1873
|
-
bwipOptions.textxalign = textalign;
|
|
1874
|
-
import_bwip_js.default.toBuffer(bwipOptions, (err, png) => {
|
|
1875
|
-
if (err) {
|
|
1876
|
-
return reject(
|
|
1877
|
-
toPdfEngineError(err, {
|
|
1878
|
-
code: "PDF_ERROR_BARCODE_GENERATION_FAILED" /* PDF_ERROR_BARCODE_GENERATION_FAILED */,
|
|
1879
|
-
message: "Failed to generate barcode.",
|
|
1880
|
-
statusCode: 500,
|
|
1881
|
-
details: { bcType, valuePreview: String(value).slice(0, 60) },
|
|
1882
|
-
retryable: false
|
|
1883
|
-
})
|
|
1884
|
-
);
|
|
2379
|
+
import_bwip_js.default.toBuffer(
|
|
2380
|
+
{
|
|
2381
|
+
bcid,
|
|
2382
|
+
text: value,
|
|
2383
|
+
scale,
|
|
2384
|
+
height: barHeight,
|
|
2385
|
+
includetext,
|
|
2386
|
+
textxalign: textalign
|
|
2387
|
+
},
|
|
2388
|
+
(err, png) => {
|
|
2389
|
+
if (err) {
|
|
2390
|
+
return reject(
|
|
2391
|
+
toPdfEngineError(err, {
|
|
2392
|
+
code: "PDF_ERROR_BARCODE_GENERATION_FAILED" /* PDF_ERROR_BARCODE_GENERATION_FAILED */,
|
|
2393
|
+
message: "Failed to generate barcode.",
|
|
2394
|
+
statusCode: 500,
|
|
2395
|
+
details: {
|
|
2396
|
+
valuePreview: String(value).slice(0, 60),
|
|
2397
|
+
bcType
|
|
2398
|
+
},
|
|
2399
|
+
retryable: false
|
|
2400
|
+
})
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
resolve(png);
|
|
1885
2404
|
}
|
|
1886
|
-
|
|
1887
|
-
});
|
|
2405
|
+
);
|
|
1888
2406
|
});
|
|
1889
2407
|
}
|
|
1890
2408
|
async function materializeQrAndBarcodesInBlocks(blocks) {
|
|
1891
2409
|
const out = [];
|
|
1892
2410
|
for (const block of blocks) {
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
out.push({
|
|
1900
|
-
...colBlock,
|
|
1901
|
-
columns: newCols
|
|
1902
|
-
});
|
|
1903
|
-
} else if (block.type === "signature") {
|
|
1904
|
-
const sig = block;
|
|
1905
|
-
if (sig.blocks && sig.blocks.length) {
|
|
1906
|
-
const newInner = await materializeQrAndBarcodesInBlocks(sig.blocks);
|
|
1907
|
-
out.push({
|
|
1908
|
-
...sig,
|
|
1909
|
-
blocks: newInner
|
|
1910
|
-
});
|
|
1911
|
-
} else {
|
|
1912
|
-
out.push(block);
|
|
1913
|
-
}
|
|
1914
|
-
} else if (block.type === "qr") {
|
|
1915
|
-
const qb = { ...block };
|
|
2411
|
+
const blockAny = { ...block };
|
|
2412
|
+
if (blockAny.backgroundBlocks?.length) {
|
|
2413
|
+
blockAny.backgroundBlocks = await materializeQrAndBarcodesInBlocks(blockAny.backgroundBlocks);
|
|
2414
|
+
}
|
|
2415
|
+
if (block.type === "qr") {
|
|
2416
|
+
const qb = blockAny;
|
|
1916
2417
|
if (!qb.src && qb.value) {
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
qb.src = buf;
|
|
2418
|
+
qb.size = qb.size ?? 80;
|
|
2419
|
+
qb.src = await generateQrBuffer(qb.value, qb.size, qb.qrVersion, qb.errorCorrectionLevel);
|
|
1920
2420
|
}
|
|
1921
2421
|
out.push(qb);
|
|
1922
2422
|
} else if (block.type === "barcode") {
|
|
1923
|
-
const bb =
|
|
2423
|
+
const bb = blockAny;
|
|
1924
2424
|
if (!bb.src && bb.value) {
|
|
1925
|
-
|
|
2425
|
+
bb.src = await generateBarcodeBuffer(bb.value, {
|
|
1926
2426
|
bcType: bb.bcType,
|
|
1927
2427
|
scale: bb.scale,
|
|
1928
2428
|
barHeight: bb.barHeight,
|
|
1929
2429
|
includetext: bb.includetext,
|
|
1930
2430
|
textalign: bb.textalign
|
|
1931
2431
|
});
|
|
1932
|
-
bb.src = buf;
|
|
1933
2432
|
}
|
|
1934
2433
|
out.push(bb);
|
|
2434
|
+
} else if (block.type === "columns") {
|
|
2435
|
+
out.push({
|
|
2436
|
+
...blockAny,
|
|
2437
|
+
columns: await Promise.all(block.columns.map((col) => materializeQrAndBarcodesInBlocks(col)))
|
|
2438
|
+
});
|
|
2439
|
+
} else if (block.type === "signature" && block.blocks) {
|
|
2440
|
+
out.push({
|
|
2441
|
+
...blockAny,
|
|
2442
|
+
blocks: await materializeQrAndBarcodesInBlocks(block.blocks)
|
|
2443
|
+
});
|
|
1935
2444
|
} else if (block.type === "table") {
|
|
1936
|
-
const tb =
|
|
2445
|
+
const tb = blockAny;
|
|
1937
2446
|
const newBody = await Promise.all(
|
|
1938
2447
|
tb.body.map(
|
|
1939
2448
|
async (entry) => {
|
|
1940
2449
|
const materializeCell = async (cell) => {
|
|
1941
2450
|
if (!cell.blocks?.length) return cell;
|
|
1942
|
-
return {
|
|
2451
|
+
return {
|
|
2452
|
+
...cell,
|
|
2453
|
+
blocks: await materializeQrAndBarcodesInBlocks(cell.blocks)
|
|
2454
|
+
};
|
|
1943
2455
|
};
|
|
1944
2456
|
if (Array.isArray(entry)) {
|
|
1945
2457
|
return await Promise.all(entry.map(materializeCell));
|
|
1946
2458
|
}
|
|
1947
2459
|
if (entry && typeof entry === "object" && "content" in entry && Array.isArray(entry.content)) {
|
|
1948
|
-
return {
|
|
2460
|
+
return {
|
|
2461
|
+
...entry,
|
|
2462
|
+
content: await Promise.all(entry.content.map(materializeCell))
|
|
2463
|
+
};
|
|
1949
2464
|
}
|
|
1950
2465
|
return entry;
|
|
1951
2466
|
}
|
|
@@ -1953,7 +2468,7 @@ async function materializeQrAndBarcodesInBlocks(blocks) {
|
|
|
1953
2468
|
);
|
|
1954
2469
|
out.push({ ...tb, body: newBody });
|
|
1955
2470
|
} else {
|
|
1956
|
-
out.push(
|
|
2471
|
+
out.push(blockAny);
|
|
1957
2472
|
}
|
|
1958
2473
|
}
|
|
1959
2474
|
return out;
|
|
@@ -2207,102 +2722,143 @@ function drawWatermarkForPage(doc, watermark, pageNumber, isLast) {
|
|
|
2207
2722
|
}
|
|
2208
2723
|
|
|
2209
2724
|
// src/renderer-engine/index.ts
|
|
2210
|
-
var BUILT_IN_DEFAULT_IMAGE = Buffer.from(
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
)
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
if (
|
|
2725
|
+
var BUILT_IN_DEFAULT_IMAGE = Buffer.from(images.default.slice(images.default.indexOf(",") + 1), "base64");
|
|
2726
|
+
async function materializeFooterAssets(footer, fallbackImage) {
|
|
2727
|
+
if (!footer) return footer;
|
|
2728
|
+
if (typeof footer === "function") {
|
|
2729
|
+
return footer;
|
|
2730
|
+
}
|
|
2731
|
+
if (Array.isArray(footer)) {
|
|
2732
|
+
let blocks = await materializeQrAndBarcodesInBlocks(footer);
|
|
2733
|
+
blocks = await materializeImagesInBlocks(blocks, fallbackImage);
|
|
2734
|
+
return blocks;
|
|
2735
|
+
}
|
|
2736
|
+
if (footer.type) {
|
|
2737
|
+
let blocks = await materializeQrAndBarcodesInBlocks([footer]);
|
|
2738
|
+
blocks = await materializeImagesInBlocks(blocks, fallbackImage);
|
|
2739
|
+
return blocks[0];
|
|
2740
|
+
}
|
|
2741
|
+
const footerDef = footer;
|
|
2742
|
+
if (footerDef.backgroundImage) {
|
|
2743
|
+
footerDef.backgroundImage = await normalizeImageSrc(footerDef.backgroundImage, fallbackImage);
|
|
2744
|
+
}
|
|
2745
|
+
if (footerDef.blocks?.length) {
|
|
2746
|
+
footerDef.blocks = await materializeQrAndBarcodesInBlocks(footerDef.blocks);
|
|
2747
|
+
footerDef.blocks = await materializeImagesInBlocks(footerDef.blocks, fallbackImage);
|
|
2748
|
+
}
|
|
2749
|
+
if (footerDef.backgroundBlocks?.length) {
|
|
2750
|
+
footerDef.backgroundBlocks = await materializeQrAndBarcodesInBlocks(footerDef.backgroundBlocks);
|
|
2751
|
+
footerDef.backgroundBlocks = await materializeImagesInBlocks(footerDef.backgroundBlocks, fallbackImage);
|
|
2752
|
+
}
|
|
2753
|
+
return footerDef;
|
|
2754
|
+
}
|
|
2755
|
+
async function materializeDocAssets(def, fallbackImage) {
|
|
2756
|
+
if (def.header?.blocks?.length) {
|
|
2217
2757
|
def.header.blocks = await materializeQrAndBarcodesInBlocks(def.header.blocks);
|
|
2218
2758
|
def.header.blocks = await materializeImagesInBlocks(def.header.blocks, fallbackImage);
|
|
2219
2759
|
}
|
|
2760
|
+
if (def.header?.backgroundImage) {
|
|
2761
|
+
def.header.backgroundImage = await normalizeImageSrc(def.header.backgroundImage, fallbackImage);
|
|
2762
|
+
}
|
|
2763
|
+
if (def.header?.backgroundBlocks?.length) {
|
|
2764
|
+
def.header.backgroundBlocks = await materializeQrAndBarcodesInBlocks(def.header.backgroundBlocks);
|
|
2765
|
+
def.header.backgroundBlocks = await materializeImagesInBlocks(def.header.backgroundBlocks, fallbackImage);
|
|
2766
|
+
}
|
|
2220
2767
|
def.content = await materializeQrAndBarcodesInBlocks(def.content);
|
|
2221
2768
|
def.content = await materializeImagesInBlocks(def.content, fallbackImage);
|
|
2222
2769
|
if (def.pageBackground?.src) {
|
|
2223
2770
|
def.pageBackground.src = await normalizeImageSrc(def.pageBackground.src, fallbackImage);
|
|
2224
2771
|
}
|
|
2225
|
-
|
|
2226
|
-
|
|
2772
|
+
def.footer = await materializeFooterAssets(def.footer, fallbackImage);
|
|
2773
|
+
}
|
|
2774
|
+
function runRender(doc, def, fallbackImage) {
|
|
2775
|
+
const headerBandHeight = def.margins.top ?? 0;
|
|
2776
|
+
const footerBandHeight = def.margins.bottom ?? 0;
|
|
2777
|
+
if (def.fonts && def.fonts.length) {
|
|
2778
|
+
for (const font of def.fonts) {
|
|
2779
|
+
try {
|
|
2780
|
+
doc.registerFont(font.name, font.src);
|
|
2781
|
+
} catch (e) {
|
|
2782
|
+
console.warn("Failed to register font:", font.name, e);
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2227
2785
|
}
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2786
|
+
const ctx = createInitialContext(doc);
|
|
2787
|
+
const styles = def.styles ?? {};
|
|
2788
|
+
const finishPage2 = (addNewPage) => finishPage({
|
|
2789
|
+
addNewPage,
|
|
2790
|
+
doc,
|
|
2791
|
+
def,
|
|
2792
|
+
ctx,
|
|
2793
|
+
footerBandHeight,
|
|
2794
|
+
renderBlockArray,
|
|
2795
|
+
startNewPageLayout
|
|
2796
|
+
});
|
|
2797
|
+
const processSignatureBlock = createProcessSignatureBlock({
|
|
2798
|
+
doc,
|
|
2799
|
+
ctx,
|
|
2800
|
+
styles,
|
|
2801
|
+
finishPage: finishPage2,
|
|
2802
|
+
contentEnvFn: contentEnv
|
|
2803
|
+
});
|
|
2804
|
+
const { renderBlock, renderBlockArray } = createBlockRenderer({
|
|
2805
|
+
doc,
|
|
2806
|
+
ctx,
|
|
2807
|
+
styles,
|
|
2808
|
+
computeColumnPixelWidths,
|
|
2809
|
+
finishPage: finishPage2,
|
|
2810
|
+
processSignatureBlock,
|
|
2811
|
+
defaultImage: fallbackImage
|
|
2812
|
+
});
|
|
2813
|
+
const startNewPageLayout = createStartNewPageLayout({
|
|
2814
|
+
doc,
|
|
2815
|
+
def,
|
|
2816
|
+
ctx,
|
|
2817
|
+
headerBandHeight,
|
|
2818
|
+
renderBlockArray
|
|
2819
|
+
});
|
|
2820
|
+
doc.on("pageAdded", () => {
|
|
2821
|
+
if (ctx.inManualPageAdd) return;
|
|
2822
|
+
ctx.pageNumber += 1;
|
|
2823
|
+
drawPageBackground(doc, def.pageBackground);
|
|
2824
|
+
drawHeader(doc, def, headerBandHeight, def.header ?? void 0, renderBlockArray);
|
|
2825
|
+
const mode = def.watermark?.mode;
|
|
2826
|
+
if (def.watermark && !watermarkUsesLast(mode)) {
|
|
2827
|
+
drawWatermarkForPage(doc, def.watermark, ctx.pageNumber, false);
|
|
2232
2828
|
}
|
|
2829
|
+
ctx.currentY = doc.page.margins.top;
|
|
2830
|
+
});
|
|
2831
|
+
startNewPageLayout();
|
|
2832
|
+
for (const block of def.content) {
|
|
2833
|
+
if (block.type === "signature") {
|
|
2834
|
+
processSignatureBlock(block);
|
|
2835
|
+
continue;
|
|
2836
|
+
}
|
|
2837
|
+
if (ctx.afterSignature) {
|
|
2838
|
+
finishPage2(true);
|
|
2839
|
+
ctx.afterSignature = false;
|
|
2840
|
+
}
|
|
2841
|
+
renderBlock(block, null, contentEnv(doc));
|
|
2233
2842
|
}
|
|
2843
|
+
finishPage2(false);
|
|
2844
|
+
doc.end();
|
|
2845
|
+
}
|
|
2846
|
+
async function renderCustomPdf(def, outputPath) {
|
|
2847
|
+
const fallbackImage = def.defaultImage ?? BUILT_IN_DEFAULT_IMAGE;
|
|
2848
|
+
await materializeDocAssets(def, fallbackImage);
|
|
2234
2849
|
return new Promise((resolve, reject) => {
|
|
2235
|
-
const headerBandHeight = def.margins.top ?? 0;
|
|
2236
|
-
const footerBandHeight = def.margins.bottom ?? 0;
|
|
2237
2850
|
const doc = new import_pdfkit.default({
|
|
2238
2851
|
size: def.pageSize || "A4",
|
|
2239
2852
|
layout: def.pageOrientation === "landscape" ? "landscape" : "portrait",
|
|
2240
2853
|
margins: {
|
|
2241
|
-
top:
|
|
2242
|
-
bottom:
|
|
2854
|
+
top: def.margins.top ?? 0,
|
|
2855
|
+
bottom: def.margins.bottom ?? 0,
|
|
2243
2856
|
left: def.margins.left,
|
|
2244
2857
|
right: def.margins.right
|
|
2245
2858
|
}
|
|
2246
2859
|
});
|
|
2247
|
-
if (def.fonts && def.fonts.length) {
|
|
2248
|
-
for (const f of def.fonts) {
|
|
2249
|
-
try {
|
|
2250
|
-
doc.registerFont(f.name, f.src);
|
|
2251
|
-
} catch (e) {
|
|
2252
|
-
console.warn("Failed to register font:", f.name, e);
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
2860
|
const stream = import_fs.default.createWriteStream(outputPath);
|
|
2257
2861
|
doc.pipe(stream);
|
|
2258
|
-
const ctx = createInitialContext(doc);
|
|
2259
|
-
const styles = def.styles ?? {};
|
|
2260
|
-
const finishPage2 = (addNewPage) => finishPage({
|
|
2261
|
-
addNewPage,
|
|
2262
|
-
doc,
|
|
2263
|
-
def,
|
|
2264
|
-
ctx,
|
|
2265
|
-
footerBandHeight,
|
|
2266
|
-
renderBlockArray,
|
|
2267
|
-
startNewPageLayout
|
|
2268
|
-
});
|
|
2269
|
-
const processSignatureBlock = createProcessSignatureBlock({
|
|
2270
|
-
doc,
|
|
2271
|
-
ctx,
|
|
2272
|
-
styles,
|
|
2273
|
-
finishPage: finishPage2,
|
|
2274
|
-
contentEnvFn: contentEnv
|
|
2275
|
-
});
|
|
2276
|
-
const { renderBlock, renderBlockArray } = createBlockRenderer({
|
|
2277
|
-
doc,
|
|
2278
|
-
ctx,
|
|
2279
|
-
styles,
|
|
2280
|
-
computeColumnPixelWidths,
|
|
2281
|
-
finishPage: finishPage2,
|
|
2282
|
-
processSignatureBlock,
|
|
2283
|
-
defaultImage: fallbackImage
|
|
2284
|
-
});
|
|
2285
|
-
const startNewPageLayout = createStartNewPageLayout({
|
|
2286
|
-
doc,
|
|
2287
|
-
def,
|
|
2288
|
-
ctx,
|
|
2289
|
-
headerBandHeight,
|
|
2290
|
-
renderBlockArray
|
|
2291
|
-
});
|
|
2292
|
-
startNewPageLayout();
|
|
2293
|
-
for (const block of def.content) {
|
|
2294
|
-
if (block.type === "signature") {
|
|
2295
|
-
processSignatureBlock(block);
|
|
2296
|
-
continue;
|
|
2297
|
-
}
|
|
2298
|
-
if (ctx.afterSignature) {
|
|
2299
|
-
finishPage2(true);
|
|
2300
|
-
ctx.afterSignature = false;
|
|
2301
|
-
}
|
|
2302
|
-
renderBlock(block, null, contentEnv(doc));
|
|
2303
|
-
}
|
|
2304
|
-
finishPage2(false);
|
|
2305
|
-
doc.end();
|
|
2306
2862
|
stream.on("finish", () => resolve());
|
|
2307
2863
|
stream.on(
|
|
2308
2864
|
"error",
|
|
@@ -2316,115 +2872,38 @@ async function renderCustomPdf(def, outputPath) {
|
|
|
2316
2872
|
})
|
|
2317
2873
|
)
|
|
2318
2874
|
);
|
|
2875
|
+
runRender(doc, def, fallbackImage);
|
|
2319
2876
|
});
|
|
2320
2877
|
}
|
|
2321
2878
|
async function renderCustomPdfToBuffer(def) {
|
|
2322
2879
|
const fallbackImage = def.defaultImage ?? BUILT_IN_DEFAULT_IMAGE;
|
|
2323
|
-
|
|
2324
|
-
def.header.blocks = await materializeQrAndBarcodesInBlocks(def.header.blocks);
|
|
2325
|
-
def.header.blocks = await materializeImagesInBlocks(def.header.blocks, fallbackImage);
|
|
2326
|
-
}
|
|
2327
|
-
def.content = await materializeQrAndBarcodesInBlocks(def.content);
|
|
2328
|
-
def.content = await materializeImagesInBlocks(def.content, fallbackImage);
|
|
2329
|
-
if (def.pageBackground?.src) {
|
|
2330
|
-
def.pageBackground.src = await normalizeImageSrc(def.pageBackground.src, fallbackImage);
|
|
2331
|
-
}
|
|
2332
|
-
if (def.header?.backgroundImage) {
|
|
2333
|
-
def.header.backgroundImage = await normalizeImageSrc(def.header.backgroundImage, fallbackImage);
|
|
2334
|
-
}
|
|
2335
|
-
if (def.footer && typeof def.footer !== "function") {
|
|
2336
|
-
const footer = def.footer;
|
|
2337
|
-
if (footer.backgroundImage) {
|
|
2338
|
-
footer.backgroundImage = await normalizeImageSrc(footer.backgroundImage, fallbackImage);
|
|
2339
|
-
}
|
|
2340
|
-
}
|
|
2880
|
+
await materializeDocAssets(def, fallbackImage);
|
|
2341
2881
|
return new Promise((resolve, reject) => {
|
|
2342
|
-
const headerBandHeight = def.margins.top ?? 0;
|
|
2343
|
-
const footerBandHeight = def.margins.bottom ?? 0;
|
|
2344
2882
|
const doc = new import_pdfkit.default({
|
|
2345
2883
|
size: def.pageSize || "A4",
|
|
2346
2884
|
layout: def.pageOrientation === "landscape" ? "landscape" : "portrait",
|
|
2347
2885
|
margins: {
|
|
2348
|
-
top:
|
|
2349
|
-
bottom:
|
|
2886
|
+
top: def.margins.top ?? 0,
|
|
2887
|
+
bottom: def.margins.bottom ?? 0,
|
|
2350
2888
|
left: def.margins.left,
|
|
2351
2889
|
right: def.margins.right
|
|
2352
2890
|
}
|
|
2353
2891
|
});
|
|
2354
2892
|
const chunks = [];
|
|
2355
|
-
doc.on("data", (chunk) =>
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
});
|
|
2361
|
-
doc.on("error", (err) => {
|
|
2362
|
-
reject(
|
|
2893
|
+
doc.on("data", (chunk) => chunks.push(chunk));
|
|
2894
|
+
doc.on("end", () => resolve(Buffer.concat(chunks)));
|
|
2895
|
+
doc.on(
|
|
2896
|
+
"error",
|
|
2897
|
+
(err) => reject(
|
|
2363
2898
|
toPdfEngineError(err, {
|
|
2364
2899
|
code: "PDF_ERROR_PDFKIT_ERROR" /* PDF_ERROR_PDFKIT_ERROR */,
|
|
2365
2900
|
message: "PDFKit emitted an error while rendering.",
|
|
2366
2901
|
statusCode: 500,
|
|
2367
2902
|
retryable: true
|
|
2368
2903
|
})
|
|
2369
|
-
)
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
for (const f of def.fonts) {
|
|
2373
|
-
try {
|
|
2374
|
-
doc.registerFont(f.name, f.src);
|
|
2375
|
-
} catch (e) {
|
|
2376
|
-
console.warn("Failed to register font:", f.name, e);
|
|
2377
|
-
}
|
|
2378
|
-
}
|
|
2379
|
-
}
|
|
2380
|
-
const ctx = createInitialContext(doc);
|
|
2381
|
-
const styles = def.styles ?? {};
|
|
2382
|
-
const finishPage2 = (addNewPage) => finishPage({
|
|
2383
|
-
addNewPage,
|
|
2384
|
-
doc,
|
|
2385
|
-
def,
|
|
2386
|
-
ctx,
|
|
2387
|
-
footerBandHeight,
|
|
2388
|
-
renderBlockArray,
|
|
2389
|
-
startNewPageLayout
|
|
2390
|
-
});
|
|
2391
|
-
const processSignatureBlock = createProcessSignatureBlock({
|
|
2392
|
-
doc,
|
|
2393
|
-
ctx,
|
|
2394
|
-
styles,
|
|
2395
|
-
finishPage: finishPage2,
|
|
2396
|
-
contentEnvFn: contentEnv
|
|
2397
|
-
});
|
|
2398
|
-
const { renderBlock, renderBlockArray } = createBlockRenderer({
|
|
2399
|
-
doc,
|
|
2400
|
-
ctx,
|
|
2401
|
-
styles,
|
|
2402
|
-
computeColumnPixelWidths,
|
|
2403
|
-
finishPage: finishPage2,
|
|
2404
|
-
processSignatureBlock,
|
|
2405
|
-
defaultImage: fallbackImage
|
|
2406
|
-
});
|
|
2407
|
-
const startNewPageLayout = createStartNewPageLayout({
|
|
2408
|
-
doc,
|
|
2409
|
-
def,
|
|
2410
|
-
ctx,
|
|
2411
|
-
headerBandHeight,
|
|
2412
|
-
renderBlockArray
|
|
2413
|
-
});
|
|
2414
|
-
startNewPageLayout();
|
|
2415
|
-
for (const block of def.content) {
|
|
2416
|
-
if (block.type === "signature") {
|
|
2417
|
-
processSignatureBlock(block);
|
|
2418
|
-
continue;
|
|
2419
|
-
}
|
|
2420
|
-
if (ctx.afterSignature) {
|
|
2421
|
-
finishPage2(true);
|
|
2422
|
-
ctx.afterSignature = false;
|
|
2423
|
-
}
|
|
2424
|
-
renderBlock(block, null, contentEnv(doc));
|
|
2425
|
-
}
|
|
2426
|
-
finishPage2(false);
|
|
2427
|
-
doc.end();
|
|
2904
|
+
)
|
|
2905
|
+
);
|
|
2906
|
+
runRender(doc, def, fallbackImage);
|
|
2428
2907
|
});
|
|
2429
2908
|
}
|
|
2430
2909
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -2433,9 +2912,42 @@ async function renderCustomPdfToBuffer(def) {
|
|
|
2433
2912
|
PdfEngineError,
|
|
2434
2913
|
PdfEngineErrorCode,
|
|
2435
2914
|
QR_ERROR_LEVEL,
|
|
2915
|
+
computeColumnPixelWidths,
|
|
2916
|
+
contentEnv,
|
|
2917
|
+
createBlockRenderer,
|
|
2918
|
+
createBottomLimitForContent,
|
|
2919
|
+
createEnsureSpaceFor,
|
|
2920
|
+
createInitialContext,
|
|
2921
|
+
createMeasureBlockHeight,
|
|
2922
|
+
createProcessSignatureBlock,
|
|
2923
|
+
createStartNewPageLayout,
|
|
2924
|
+
drawFooter,
|
|
2925
|
+
drawHeader,
|
|
2926
|
+
drawPageBackground,
|
|
2927
|
+
drawSignatureBlock,
|
|
2928
|
+
drawStyledText,
|
|
2929
|
+
drawWatermarkForPage,
|
|
2930
|
+
finishPage,
|
|
2931
|
+
generateBarcodeBuffer,
|
|
2932
|
+
generateQrBuffer,
|
|
2933
|
+
getBottomLimitForContent,
|
|
2934
|
+
getFontNameForText,
|
|
2935
|
+
hasPadding,
|
|
2436
2936
|
images,
|
|
2437
2937
|
isPdfEngineError,
|
|
2938
|
+
mapBarcodeTypeToBcid,
|
|
2939
|
+
materializeImagesInBlocks,
|
|
2940
|
+
materializeQrAndBarcodesInBlocks,
|
|
2941
|
+
mergeStyleDefs,
|
|
2942
|
+
normalizeImageSrc,
|
|
2438
2943
|
renderCustomPdf,
|
|
2439
2944
|
renderCustomPdfToBuffer,
|
|
2440
|
-
|
|
2945
|
+
resolveBlockPadding,
|
|
2946
|
+
resolveFooterPadding,
|
|
2947
|
+
resolveHeaderPadding,
|
|
2948
|
+
resolveSpacing,
|
|
2949
|
+
resolveTableCell,
|
|
2950
|
+
resolveTextBlock,
|
|
2951
|
+
toPdfEngineError,
|
|
2952
|
+
watermarkUsesLast
|
|
2441
2953
|
});
|