av6-pdf-engine 1.2.0 → 3.0.0

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