av6-pdf-engine 1.0.8 → 1.1.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.d.mts CHANGED
@@ -61,6 +61,12 @@ interface TableCell {
61
61
  color?: string;
62
62
  fillColor?: string;
63
63
  style?: StyleRef;
64
+ colSpan?: number;
65
+ rowSpan?: number;
66
+ paddingTop?: number;
67
+ paddingRight?: number;
68
+ paddingBottom?: number;
69
+ paddingLeft?: number;
64
70
  }
65
71
  interface TableBlock extends BaseBlock {
66
72
  type: "table";
@@ -78,6 +84,12 @@ interface TableBlock extends BaseBlock {
78
84
  marginLeft?: number;
79
85
  marginRight?: number;
80
86
  }
87
+ interface PlacedCell {
88
+ cell: TableCell;
89
+ startCol: number;
90
+ colSpan: number;
91
+ rowSpan: number;
92
+ }
81
93
 
82
94
  interface ColumnsBlock extends BaseBlock {
83
95
  type: "columns";
@@ -217,6 +229,7 @@ interface FontRegistration {
217
229
  interface CustomDocDefinition {
218
230
  pageSize?: string;
219
231
  margins: PageMargins;
232
+ pageOrientation?: "portrait" | "landscape";
220
233
  /** Optional font registrations (custom TTF/OTF) */
221
234
  fonts?: FontRegistration[];
222
235
  header?: HeaderDef;
@@ -312,9 +325,6 @@ declare enum PdfEngineErrorCode {
312
325
  PDF_ERROR_PDFKIT_ERROR = "PDF_ERROR_PDFKIT_ERROR"
313
326
  }
314
327
 
315
- declare function renderCustomPdf(def: CustomDocDefinition, outputPath: string): Promise<void>;
316
- declare function renderCustomPdfToBuffer(def: CustomDocDefinition): Promise<Buffer>;
317
-
318
328
  declare class PdfEngineError extends Error {
319
329
  readonly code: PdfEngineErrorCode;
320
330
  readonly statusCode: number;
@@ -339,4 +349,7 @@ declare function toPdfEngineError(cause: unknown, fallback: {
339
349
  retryable?: boolean;
340
350
  }): PdfEngineError;
341
351
 
342
- export { type Alignment, BARCODE_TYPES, type BarcodeBlock, type BarcodeType, type BaseBlock, type Block, type BuiltInFont, type ColumnsBlock, type CustomDocDefinition, type FontName, type FontRegistration, type FooterDef, type FooterInput, type HeaderDef, type ImageBlock, type InlineTextStyle, type KeyValueGridBlock, type KeyValueItem, type LineBlock, type Orientation, type PDFDoc, type PageBackgroundDef, type PageBreakBlock, type PageContext, type PageMargins, PdfEngineError, PdfEngineErrorCode, type QRErrorLevel, QR_ERROR_LEVEL, type QrCodeBlock, type RenderEnv, type SignatureBlock, type StyleDef, type StyleRef, type TableBlock, type TableCell, type TextBlock, type WatermarkDef, type WatermarkMode, type Width, isPdfEngineError, renderCustomPdf, renderCustomPdfToBuffer, toPdfEngineError };
352
+ declare function renderCustomPdf(def: CustomDocDefinition, outputPath: string): Promise<void>;
353
+ declare function renderCustomPdfToBuffer(def: CustomDocDefinition): Promise<Buffer>;
354
+
355
+ export { type Alignment, BARCODE_TYPES, type BarcodeBlock, type BarcodeType, type BaseBlock, type Block, type BuiltInFont, type ColumnsBlock, type CustomDocDefinition, type FontName, type FontRegistration, type FooterDef, type FooterInput, type HeaderDef, type ImageBlock, type InlineTextStyle, type KeyValueGridBlock, type KeyValueItem, type LineBlock, type Orientation, type PDFDoc, type PageBackgroundDef, type PageBreakBlock, type PageContext, type PageMargins, PdfEngineError, PdfEngineErrorCode, type PlacedCell, type QRErrorLevel, QR_ERROR_LEVEL, type QrCodeBlock, type RenderEnv, type SignatureBlock, type StyleDef, type StyleRef, type TableBlock, type TableCell, type TextBlock, type WatermarkDef, type WatermarkMode, type Width, isPdfEngineError, renderCustomPdf, renderCustomPdfToBuffer, toPdfEngineError };
package/dist/index.d.ts CHANGED
@@ -61,6 +61,12 @@ interface TableCell {
61
61
  color?: string;
62
62
  fillColor?: string;
63
63
  style?: StyleRef;
64
+ colSpan?: number;
65
+ rowSpan?: number;
66
+ paddingTop?: number;
67
+ paddingRight?: number;
68
+ paddingBottom?: number;
69
+ paddingLeft?: number;
64
70
  }
65
71
  interface TableBlock extends BaseBlock {
66
72
  type: "table";
@@ -78,6 +84,12 @@ interface TableBlock extends BaseBlock {
78
84
  marginLeft?: number;
79
85
  marginRight?: number;
80
86
  }
87
+ interface PlacedCell {
88
+ cell: TableCell;
89
+ startCol: number;
90
+ colSpan: number;
91
+ rowSpan: number;
92
+ }
81
93
 
82
94
  interface ColumnsBlock extends BaseBlock {
83
95
  type: "columns";
@@ -217,6 +229,7 @@ interface FontRegistration {
217
229
  interface CustomDocDefinition {
218
230
  pageSize?: string;
219
231
  margins: PageMargins;
232
+ pageOrientation?: "portrait" | "landscape";
220
233
  /** Optional font registrations (custom TTF/OTF) */
221
234
  fonts?: FontRegistration[];
222
235
  header?: HeaderDef;
@@ -312,9 +325,6 @@ declare enum PdfEngineErrorCode {
312
325
  PDF_ERROR_PDFKIT_ERROR = "PDF_ERROR_PDFKIT_ERROR"
313
326
  }
314
327
 
315
- declare function renderCustomPdf(def: CustomDocDefinition, outputPath: string): Promise<void>;
316
- declare function renderCustomPdfToBuffer(def: CustomDocDefinition): Promise<Buffer>;
317
-
318
328
  declare class PdfEngineError extends Error {
319
329
  readonly code: PdfEngineErrorCode;
320
330
  readonly statusCode: number;
@@ -339,4 +349,7 @@ declare function toPdfEngineError(cause: unknown, fallback: {
339
349
  retryable?: boolean;
340
350
  }): PdfEngineError;
341
351
 
342
- export { type Alignment, BARCODE_TYPES, type BarcodeBlock, type BarcodeType, type BaseBlock, type Block, type BuiltInFont, type ColumnsBlock, type CustomDocDefinition, type FontName, type FontRegistration, type FooterDef, type FooterInput, type HeaderDef, type ImageBlock, type InlineTextStyle, type KeyValueGridBlock, type KeyValueItem, type LineBlock, type Orientation, type PDFDoc, type PageBackgroundDef, type PageBreakBlock, type PageContext, type PageMargins, PdfEngineError, PdfEngineErrorCode, type QRErrorLevel, QR_ERROR_LEVEL, type QrCodeBlock, type RenderEnv, type SignatureBlock, type StyleDef, type StyleRef, type TableBlock, type TableCell, type TextBlock, type WatermarkDef, type WatermarkMode, type Width, isPdfEngineError, renderCustomPdf, renderCustomPdfToBuffer, toPdfEngineError };
352
+ declare function renderCustomPdf(def: CustomDocDefinition, outputPath: string): Promise<void>;
353
+ declare function renderCustomPdfToBuffer(def: CustomDocDefinition): Promise<Buffer>;
354
+
355
+ export { type Alignment, BARCODE_TYPES, type BarcodeBlock, type BarcodeType, type BaseBlock, type Block, type BuiltInFont, type ColumnsBlock, type CustomDocDefinition, type FontName, type FontRegistration, type FooterDef, type FooterInput, type HeaderDef, type ImageBlock, type InlineTextStyle, type KeyValueGridBlock, type KeyValueItem, type LineBlock, type Orientation, type PDFDoc, type PageBackgroundDef, type PageBreakBlock, type PageContext, type PageMargins, PdfEngineError, PdfEngineErrorCode, type PlacedCell, type QRErrorLevel, QR_ERROR_LEVEL, type QrCodeBlock, type RenderEnv, type SignatureBlock, type StyleDef, type StyleRef, type TableBlock, type TableCell, type TextBlock, type WatermarkDef, type WatermarkMode, type Width, isPdfEngineError, renderCustomPdf, renderCustomPdfToBuffer, toPdfEngineError };
package/dist/index.js CHANGED
@@ -41,6 +41,38 @@ __export(index_exports, {
41
41
  });
42
42
  module.exports = __toCommonJS(index_exports);
43
43
 
44
+ // src/error/index.ts
45
+ var PdfEngineError = class extends Error {
46
+ code;
47
+ statusCode;
48
+ details;
49
+ retryable;
50
+ cause;
51
+ constructor(args) {
52
+ super(args.message);
53
+ this.name = "PdfEngineError";
54
+ this.code = args.code;
55
+ this.statusCode = args.statusCode ?? 400;
56
+ this.details = args.details;
57
+ this.retryable = args.retryable ?? false;
58
+ this.cause = args.cause;
59
+ }
60
+ };
61
+ function isPdfEngineError(e) {
62
+ return e instanceof PdfEngineError;
63
+ }
64
+ function toPdfEngineError(cause, fallback) {
65
+ if (cause instanceof PdfEngineError) return cause;
66
+ return new PdfEngineError({
67
+ code: fallback.code,
68
+ message: fallback.message,
69
+ statusCode: fallback.statusCode,
70
+ details: fallback.details,
71
+ retryable: fallback.retryable,
72
+ cause
73
+ });
74
+ }
75
+
44
76
  // src/renderer-engine/index.ts
45
77
  var import_fs = __toESM(require("fs"));
46
78
  var import_pdfkit = __toESM(require("pdfkit"));
@@ -487,29 +519,117 @@ var processLineBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
487
519
  };
488
520
 
489
521
  // src/renderer-engine/blocks/table.ts
522
+ var normalizeSpan = (value) => {
523
+ if (!value || value < 1) return 1;
524
+ return Math.floor(value);
525
+ };
526
+ var sum = (arr) => arr.reduce((a, b) => a + b, 0);
527
+ var getCellFontName = (cell) => {
528
+ const isBold = !!cell.bold;
529
+ const isItalic = !!cell.italic;
530
+ if (cell.font) return cell.font;
531
+ if (isBold && isItalic) return "Helvetica-BoldOblique";
532
+ if (isBold) return "Helvetica-Bold";
533
+ if (isItalic) return "Helvetica-Oblique";
534
+ return "Helvetica";
535
+ };
536
+ var decrementActiveSpans = (activeRowSpans) => {
537
+ for (let i = 0; i < activeRowSpans.length; i++) {
538
+ if (activeRowSpans[i] > 0) {
539
+ activeRowSpans[i] -= 1;
540
+ }
541
+ }
542
+ };
543
+ var findNextFreeCol = (activeRowSpans, fromIndex) => {
544
+ let col = fromIndex;
545
+ while (col < activeRowSpans.length && activeRowSpans[col] > 0) {
546
+ col++;
547
+ }
548
+ return col;
549
+ };
550
+ var buildPlacedRow = (row, totalCols, activeRowSpans) => {
551
+ const placed = [];
552
+ let cursor = 0;
553
+ for (const rawCell of row) {
554
+ cursor = findNextFreeCol(activeRowSpans, cursor);
555
+ if (cursor >= totalCols) break;
556
+ const colSpan = Math.min(normalizeSpan(rawCell.colSpan), totalCols - cursor);
557
+ const rowSpan = normalizeSpan(rawCell.rowSpan);
558
+ placed.push({
559
+ cell: rawCell,
560
+ startCol: cursor,
561
+ colSpan,
562
+ rowSpan
563
+ });
564
+ if (rowSpan > 1) {
565
+ for (let c = cursor; c < cursor + colSpan; c++) {
566
+ activeRowSpans[c] = Math.max(activeRowSpans[c], rowSpan - 1);
567
+ }
568
+ }
569
+ cursor += colSpan;
570
+ }
571
+ return placed;
572
+ };
573
+ var getPlacedCellWidth = (colWidths, startCol, colSpan) => {
574
+ return sum(colWidths.slice(startCol, startCol + colSpan));
575
+ };
576
+ var getRowSpanGroupHeight = (rowHeights, rowIndex, rowSpan) => {
577
+ return sum(rowHeights.slice(rowIndex, rowIndex + rowSpan));
578
+ };
490
579
  var measureTableHeight = (doc, table, env, styles, computeColumnPixelWidths2) => {
491
580
  const localLeft = table.marginLeft ?? 0;
492
581
  const localRight = table.marginRight ?? 0;
493
582
  const width = Math.max(env.innerWidth - localLeft - localRight, 1);
583
+ const totalCols = table.widths.length;
494
584
  const colWidths = computeColumnPixelWidths2(table.widths, width);
495
- let totalHeight = 0;
496
- totalHeight += table.marginTop ?? 0;
497
- table.body.forEach((row) => {
498
- let rowHeight = 0;
499
- row.forEach((rawCell, colIndex) => {
585
+ const rowHeights = [];
586
+ const activeRowSpans = Array(totalCols).fill(0);
587
+ const placedRows = [];
588
+ for (let rowIndex = 0; rowIndex < table.body.length; rowIndex++) {
589
+ const row = table.body[rowIndex];
590
+ const placedRow = buildPlacedRow(row, totalCols, activeRowSpans);
591
+ placedRows.push(placedRow);
592
+ let rowHeight = 12;
593
+ placedRow.forEach(({ cell: rawCell, startCol, colSpan }) => {
500
594
  const cell = resolveTableCell(styles, rawCell);
501
- const colW = colWidths[colIndex] ?? 50;
502
- const fontSize = cell.fontSize ?? 10;
503
- const isBold = !!cell.bold;
504
- const isItalic = !!cell.italic;
505
- const fontName = cell.font ? cell.font : isBold && isItalic ? "Helvetica-BoldOblique" : isBold ? "Helvetica-Bold" : isItalic ? "Helvetica-Oblique" : "Helvetica";
506
- doc.font(fontName);
507
- doc.fontSize(fontSize);
508
- const h = doc.heightOfString(cell.text, { width: colW });
509
- rowHeight = Math.max(rowHeight, h + 4);
595
+ const paddingLeft = cell.paddingLeft ?? 2;
596
+ const paddingRight = cell.paddingRight ?? 2;
597
+ const paddingTop = cell.paddingTop ?? 2;
598
+ const paddingBottom = cell.paddingBottom ?? 2;
599
+ const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
600
+ const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
601
+ doc.font(getCellFontName(cell));
602
+ doc.fontSize(cell.fontSize ?? 10);
603
+ const h = doc.heightOfString(cell.text, { width: textWidth }) + paddingTop + paddingBottom;
604
+ rowHeight = Math.max(rowHeight, h);
510
605
  });
511
- totalHeight += rowHeight;
512
- });
606
+ rowHeights.push(rowHeight);
607
+ decrementActiveSpans(activeRowSpans);
608
+ }
609
+ for (let rowIndex = 0; rowIndex < placedRows.length; rowIndex++) {
610
+ const placedRow = placedRows[rowIndex];
611
+ placedRow.forEach(({ cell: rawCell, startCol, colSpan, rowSpan }) => {
612
+ if (rowSpan <= 1) return;
613
+ const cell = resolveTableCell(styles, rawCell);
614
+ const paddingLeft = cell.paddingLeft ?? 2;
615
+ const paddingRight = cell.paddingRight ?? 2;
616
+ const paddingTop = cell.paddingTop ?? 2;
617
+ const paddingBottom = cell.paddingBottom ?? 2;
618
+ const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
619
+ const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
620
+ doc.font(getCellFontName(cell));
621
+ doc.fontSize(cell.fontSize ?? 10);
622
+ const neededHeight = doc.heightOfString(cell.text, { width: textWidth }) + paddingTop + paddingBottom;
623
+ const currentGroupHeight = getRowSpanGroupHeight(rowHeights, rowIndex, rowSpan);
624
+ if (neededHeight > currentGroupHeight) {
625
+ const deficit = neededHeight - currentGroupHeight;
626
+ rowHeights[rowIndex + rowSpan - 1] = (rowHeights[rowIndex + rowSpan - 1] ?? 12) + deficit;
627
+ }
628
+ });
629
+ }
630
+ let totalHeight = 0;
631
+ totalHeight += table.marginTop ?? 0;
632
+ totalHeight += sum(rowHeights);
513
633
  totalHeight += table.lineGap ?? 6;
514
634
  totalHeight += table.marginBottom ?? 0;
515
635
  return totalHeight;
@@ -523,6 +643,7 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
523
643
  const mb = table.marginBottom ?? 0;
524
644
  const baseY = y ?? ctx.currentY;
525
645
  const startY = baseY + mt;
646
+ const totalCols = table.widths.length;
526
647
  const colWidths = computeColumnPixelWidths2(table.widths, width);
527
648
  const layout = table.layout ?? {
528
649
  border: "all",
@@ -531,56 +652,84 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
531
652
  };
532
653
  const borderAll = layout.border !== "none";
533
654
  const headerRows = table.headerRows ?? 0;
534
- const measureRowHeight = (row) => {
535
- let rowHeight = 0;
536
- row.forEach((rawCell, colIndex) => {
537
- const cell = resolveTableCell(styles, rawCell);
538
- const colW = colWidths[colIndex] ?? 50;
539
- const fontSize = cell.fontSize ?? 10;
540
- const fontName = cell.font ? cell.font : cell.bold ? "Helvetica-Bold" : "Helvetica";
541
- doc.font(fontName);
542
- doc.fontSize(fontSize);
543
- const textWidth = Math.max(colW - 4, 1);
544
- const h = doc.heightOfString(cell.text, {
545
- width: textWidth
546
- }) + 4;
547
- rowHeight = Math.max(rowHeight, h);
548
- });
549
- return rowHeight || 12;
655
+ const buildPlacementAndHeights = (rows) => {
656
+ const placedRows = [];
657
+ const rowHeights = [];
658
+ const activeRowSpans = Array(totalCols).fill(0);
659
+ for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
660
+ const row = rows[rowIndex];
661
+ const placedRow = buildPlacedRow(row, totalCols, activeRowSpans);
662
+ placedRows.push(placedRow);
663
+ let rowHeight = 12;
664
+ placedRow.forEach(({ cell: rawCell, startCol, colSpan }) => {
665
+ const cell = resolveTableCell(styles, rawCell);
666
+ const paddingLeft = cell.paddingLeft ?? 2;
667
+ const paddingRight = cell.paddingRight ?? 2;
668
+ const paddingTop = cell.paddingTop ?? 2;
669
+ const paddingBottom = cell.paddingBottom ?? 2;
670
+ const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
671
+ const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
672
+ doc.font(getCellFontName(cell));
673
+ doc.fontSize(cell.fontSize ?? 10);
674
+ const h = doc.heightOfString(cell.text, { width: textWidth }) + paddingTop + paddingBottom;
675
+ rowHeight = Math.max(rowHeight, h);
676
+ });
677
+ rowHeights.push(rowHeight);
678
+ decrementActiveSpans(activeRowSpans);
679
+ }
680
+ for (let rowIndex = 0; rowIndex < placedRows.length; rowIndex++) {
681
+ const placedRow = placedRows[rowIndex];
682
+ placedRow.forEach(({ cell: rawCell, startCol, colSpan, rowSpan }) => {
683
+ if (rowSpan <= 1) return;
684
+ const cell = resolveTableCell(styles, rawCell);
685
+ const paddingLeft = cell.paddingLeft ?? 2;
686
+ const paddingRight = cell.paddingRight ?? 2;
687
+ const paddingTop = cell.paddingTop ?? 2;
688
+ const paddingBottom = cell.paddingBottom ?? 2;
689
+ const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
690
+ const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
691
+ doc.font(getCellFontName(cell));
692
+ doc.fontSize(cell.fontSize ?? 10);
693
+ const neededHeight = doc.heightOfString(cell.text, { width: textWidth }) + paddingTop + paddingBottom;
694
+ const currentGroupHeight = getRowSpanGroupHeight(rowHeights, rowIndex, rowSpan);
695
+ if (neededHeight > currentGroupHeight) {
696
+ const deficit = neededHeight - currentGroupHeight;
697
+ rowHeights[rowIndex + rowSpan - 1] = (rowHeights[rowIndex + rowSpan - 1] ?? 12) + deficit;
698
+ }
699
+ });
700
+ }
701
+ return { placedRows, rowHeights };
550
702
  };
551
- const drawRow = (row, rowTop2, rowHeight, isHeaderRow, drawTopBorder) => {
552
- row.forEach((rawCell, colIndex) => {
703
+ const allLayout = buildPlacementAndHeights(table.body);
704
+ const drawPlacedRow = (placedRow, rowTop2, rowHeight, rowIndex, rowHeights, isHeaderRow, drawTopBorder) => {
705
+ placedRow.forEach(({ cell: rawCell, startCol, colSpan, rowSpan }) => {
553
706
  const cell = resolveTableCell(styles, rawCell);
554
- const colW = colWidths[colIndex] ?? 50;
555
- const x = baseLeft + colWidths.slice(0, colIndex).reduce((a, b) => a + b, 0);
707
+ const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
708
+ const x = baseLeft + sum(colWidths.slice(0, startCol));
556
709
  const fontSize = cell.fontSize ?? 10;
710
+ const drawHeight = getRowSpanGroupHeight(rowHeights, rowIndex, rowSpan);
711
+ const paddingLeft = cell.paddingLeft ?? 2;
712
+ const paddingRight = cell.paddingRight ?? 2;
713
+ const paddingTop = cell.paddingTop ?? 2;
714
+ const paddingBottom = cell.paddingBottom ?? 2;
557
715
  doc.save();
558
716
  if (cell.fillColor) {
559
- doc.rect(x, rowTop2, colW, rowHeight).fillOpacity(1).fill(cell.fillColor);
717
+ doc.rect(x, rowTop2, colW, drawHeight).fillOpacity(1).fill(cell.fillColor);
560
718
  doc.fillOpacity(1);
561
719
  }
562
- const isBold = !!cell.bold;
563
- const isItalic = !!cell.italic;
564
- const fontName = cell.font ? cell.font : isBold && isItalic ? "Helvetica-BoldOblique" : isBold ? "Helvetica-Bold" : isItalic ? "Helvetica-Oblique" : "Helvetica";
565
- doc.font(fontName);
720
+ doc.font(getCellFontName(cell));
566
721
  doc.fontSize(fontSize);
567
- if (cell.color) {
568
- doc.fillColor(cell.color);
569
- } else {
570
- doc.fillColor("black");
571
- }
722
+ doc.fillColor(cell.color ?? "black");
572
723
  const align = cell.align ?? "left";
573
- const textWidth = Math.max(colW - 4, 1);
574
- const textHeight = doc.heightOfString(cell.text, {
575
- width: textWidth
576
- });
577
- let textY = rowTop2 + 2;
724
+ const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
725
+ const textHeight = doc.heightOfString(cell.text, { width: textWidth });
726
+ let textY = rowTop2 + paddingTop;
578
727
  if (isHeaderRow) {
579
- const available = rowHeight - 4;
728
+ const available = Math.max(drawHeight - paddingTop - paddingBottom, 0);
580
729
  const offset = Math.max((available - textHeight) / 2, 0);
581
- textY = rowTop2 + 2 + offset;
730
+ textY = rowTop2 + paddingTop + offset;
582
731
  }
583
- doc.text(cell.text, x + 2, textY, {
732
+ doc.text(cell.text, x + paddingLeft, textY, {
584
733
  width: textWidth,
585
734
  align,
586
735
  underline: !!cell.underline,
@@ -588,52 +737,58 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
588
737
  link: cell.link
589
738
  });
590
739
  doc.restore();
591
- });
592
- if (borderAll) {
593
- const bottomY = rowTop2 + rowHeight;
594
- doc.save();
595
- doc.lineWidth(0.5);
596
- doc.strokeColor(layout.hLineColor ?? "#ccc");
597
- if (drawTopBorder) {
598
- doc.moveTo(baseLeft, rowTop2).lineTo(baseLeft + width, rowTop2).stroke();
599
- }
600
- doc.moveTo(baseLeft, bottomY).lineTo(baseLeft + width, bottomY).stroke();
601
- doc.restore();
602
- doc.save();
603
- doc.lineWidth(0.5);
604
- doc.strokeColor(layout.vLineColor ?? "#ccc");
605
- let x = baseLeft;
606
- colWidths.forEach((colW) => {
740
+ if (borderAll) {
741
+ const bottomY = rowTop2 + drawHeight;
742
+ doc.save();
743
+ doc.lineWidth(0.5);
744
+ doc.strokeColor(layout.hLineColor ?? "#ccc");
745
+ if (drawTopBorder) {
746
+ doc.moveTo(x, rowTop2).lineTo(x + colW, rowTop2).stroke();
747
+ }
748
+ doc.moveTo(x, bottomY).lineTo(x + colW, bottomY).stroke();
749
+ doc.restore();
750
+ doc.save();
751
+ doc.lineWidth(0.5);
752
+ doc.strokeColor(layout.vLineColor ?? "#ccc");
607
753
  doc.moveTo(x, rowTop2).lineTo(x, bottomY).stroke();
608
- x += colW;
609
- });
610
- doc.moveTo(baseLeft + width, rowTop2).lineTo(baseLeft + width, bottomY).stroke();
611
- doc.restore();
612
- }
754
+ doc.moveTo(x + colW, rowTop2).lineTo(x + colW, bottomY).stroke();
755
+ doc.restore();
756
+ }
757
+ });
613
758
  return rowTop2 + rowHeight;
614
759
  };
760
+ const headerRowsSource = table.body.slice(0, headerRows);
615
761
  const drawHeaderRowsOnNewPage = () => {
616
762
  if (!headerRows) return doc.page.margins.top + mt;
763
+ const headerLayout = buildPlacementAndHeights(headerRowsSource);
617
764
  let rowTop2 = doc.page.margins.top + mt;
618
- for (let i = 0; i < headerRows; i++) {
619
- const headerRow = table.body[i];
620
- const headerHeight = measureRowHeight(headerRow);
621
- rowTop2 = drawRow(headerRow, rowTop2, headerHeight, true, true);
765
+ for (let i = 0; i < headerLayout.placedRows.length; i++) {
766
+ rowTop2 = drawPlacedRow(
767
+ headerLayout.placedRows[i],
768
+ rowTop2,
769
+ headerLayout.rowHeights[i],
770
+ i,
771
+ headerLayout.rowHeights,
772
+ true,
773
+ true
774
+ );
622
775
  }
623
776
  return rowTop2;
624
777
  };
625
778
  let rowTop = startY;
626
- table.body.forEach((row, rowIndex) => {
779
+ for (let rowIndex = 0; rowIndex < table.body.length; rowIndex++) {
627
780
  const isHeaderRow = rowIndex < headerRows;
628
- const rowHeight = measureRowHeight(row);
781
+ const placedRow = allLayout.placedRows[rowIndex];
782
+ const rowHeight = allLayout.rowHeights[rowIndex];
783
+ const maxRowSpan = Math.max(...placedRow.map((p) => p.rowSpan), 1);
784
+ const reservedHeight = getRowSpanGroupHeight(allLayout.rowHeights, rowIndex, maxRowSpan);
629
785
  const bottomLimit = bottomLimitForContent();
630
- if (rowTop + rowHeight > bottomLimit) {
786
+ if (rowTop + reservedHeight > bottomLimit) {
631
787
  finishPage2(true);
632
788
  rowTop = headerRows ? drawHeaderRowsOnNewPage() : doc.page.margins.top + mt;
633
789
  }
634
- const drawTopBorder = true;
635
- rowTop = drawRow(row, rowTop, rowHeight, isHeaderRow, drawTopBorder);
636
- });
790
+ rowTop = drawPlacedRow(placedRow, rowTop, rowHeight, rowIndex, allLayout.rowHeights, isHeaderRow, true);
791
+ }
637
792
  const newY = rowTop + (table.lineGap ?? 6) + mb;
638
793
  if (y === null) ctx.currentY = newY;
639
794
  return newY;
@@ -828,38 +983,6 @@ var contentEnv = (doc) => ({
828
983
  allowPageBreak: true
829
984
  });
830
985
 
831
- // src/renderer-engine/utils/error.ts
832
- var PdfEngineError = class extends Error {
833
- code;
834
- statusCode;
835
- details;
836
- retryable;
837
- cause;
838
- constructor(args) {
839
- super(args.message);
840
- this.name = "PdfEngineError";
841
- this.code = args.code;
842
- this.statusCode = args.statusCode ?? 400;
843
- this.details = args.details;
844
- this.retryable = args.retryable ?? false;
845
- this.cause = args.cause;
846
- }
847
- };
848
- function isPdfEngineError(e) {
849
- return e instanceof PdfEngineError;
850
- }
851
- function toPdfEngineError(cause, fallback) {
852
- if (cause instanceof PdfEngineError) return cause;
853
- return new PdfEngineError({
854
- code: fallback.code,
855
- message: fallback.message,
856
- statusCode: fallback.statusCode,
857
- details: fallback.details,
858
- retryable: fallback.retryable,
859
- cause
860
- });
861
- }
862
-
863
986
  // src/renderer-engine/utils/finish-page.ts
864
987
  function finishPage({
865
988
  addNewPage,
@@ -1109,7 +1232,11 @@ function resolveTableCell(styles, cell) {
1109
1232
  italic: cell.italic ?? styleDef.italic,
1110
1233
  color: cell.color ?? styleDef.color,
1111
1234
  fillColor: cell.fillColor ?? styleDef.fillColor,
1112
- font: cell.font ?? styleDef.font
1235
+ font: cell.font ?? styleDef.font,
1236
+ paddingTop: cell.paddingTop,
1237
+ paddingRight: cell.paddingRight,
1238
+ paddingBottom: cell.paddingBottom,
1239
+ paddingLeft: cell.paddingLeft
1113
1240
  };
1114
1241
  }
1115
1242
  var getFontNameForText = (tb) => {
@@ -1780,6 +1907,7 @@ async function renderCustomPdf(def, outputPath) {
1780
1907
  const footerBandHeight = def.margins.bottom ?? 0;
1781
1908
  const doc = new import_pdfkit.default({
1782
1909
  size: def.pageSize || "A4",
1910
+ layout: def.pageOrientation === "landscape" ? "landscape" : "portrait",
1783
1911
  margins: {
1784
1912
  top: headerBandHeight,
1785
1913
  bottom: footerBandHeight,
@@ -1884,6 +2012,7 @@ async function renderCustomPdfToBuffer(def) {
1884
2012
  const footerBandHeight = def.margins.bottom ?? 0;
1885
2013
  const doc = new import_pdfkit.default({
1886
2014
  size: def.pageSize || "A4",
2015
+ layout: def.pageOrientation === "landscape" ? "landscape" : "portrait",
1887
2016
  margins: {
1888
2017
  top: headerBandHeight,
1889
2018
  bottom: footerBandHeight,
package/dist/index.mjs CHANGED
@@ -1,3 +1,35 @@
1
+ // src/error/index.ts
2
+ var PdfEngineError = class extends Error {
3
+ code;
4
+ statusCode;
5
+ details;
6
+ retryable;
7
+ cause;
8
+ constructor(args) {
9
+ super(args.message);
10
+ this.name = "PdfEngineError";
11
+ this.code = args.code;
12
+ this.statusCode = args.statusCode ?? 400;
13
+ this.details = args.details;
14
+ this.retryable = args.retryable ?? false;
15
+ this.cause = args.cause;
16
+ }
17
+ };
18
+ function isPdfEngineError(e) {
19
+ return e instanceof PdfEngineError;
20
+ }
21
+ function toPdfEngineError(cause, fallback) {
22
+ if (cause instanceof PdfEngineError) return cause;
23
+ return new PdfEngineError({
24
+ code: fallback.code,
25
+ message: fallback.message,
26
+ statusCode: fallback.statusCode,
27
+ details: fallback.details,
28
+ retryable: fallback.retryable,
29
+ cause
30
+ });
31
+ }
32
+
1
33
  // src/renderer-engine/index.ts
2
34
  import fs from "fs";
3
35
  import PDFDocument from "pdfkit";
@@ -444,29 +476,117 @@ var processLineBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
444
476
  };
445
477
 
446
478
  // src/renderer-engine/blocks/table.ts
479
+ var normalizeSpan = (value) => {
480
+ if (!value || value < 1) return 1;
481
+ return Math.floor(value);
482
+ };
483
+ var sum = (arr) => arr.reduce((a, b) => a + b, 0);
484
+ var getCellFontName = (cell) => {
485
+ const isBold = !!cell.bold;
486
+ const isItalic = !!cell.italic;
487
+ if (cell.font) return cell.font;
488
+ if (isBold && isItalic) return "Helvetica-BoldOblique";
489
+ if (isBold) return "Helvetica-Bold";
490
+ if (isItalic) return "Helvetica-Oblique";
491
+ return "Helvetica";
492
+ };
493
+ var decrementActiveSpans = (activeRowSpans) => {
494
+ for (let i = 0; i < activeRowSpans.length; i++) {
495
+ if (activeRowSpans[i] > 0) {
496
+ activeRowSpans[i] -= 1;
497
+ }
498
+ }
499
+ };
500
+ var findNextFreeCol = (activeRowSpans, fromIndex) => {
501
+ let col = fromIndex;
502
+ while (col < activeRowSpans.length && activeRowSpans[col] > 0) {
503
+ col++;
504
+ }
505
+ return col;
506
+ };
507
+ var buildPlacedRow = (row, totalCols, activeRowSpans) => {
508
+ const placed = [];
509
+ let cursor = 0;
510
+ for (const rawCell of row) {
511
+ cursor = findNextFreeCol(activeRowSpans, cursor);
512
+ if (cursor >= totalCols) break;
513
+ const colSpan = Math.min(normalizeSpan(rawCell.colSpan), totalCols - cursor);
514
+ const rowSpan = normalizeSpan(rawCell.rowSpan);
515
+ placed.push({
516
+ cell: rawCell,
517
+ startCol: cursor,
518
+ colSpan,
519
+ rowSpan
520
+ });
521
+ if (rowSpan > 1) {
522
+ for (let c = cursor; c < cursor + colSpan; c++) {
523
+ activeRowSpans[c] = Math.max(activeRowSpans[c], rowSpan - 1);
524
+ }
525
+ }
526
+ cursor += colSpan;
527
+ }
528
+ return placed;
529
+ };
530
+ var getPlacedCellWidth = (colWidths, startCol, colSpan) => {
531
+ return sum(colWidths.slice(startCol, startCol + colSpan));
532
+ };
533
+ var getRowSpanGroupHeight = (rowHeights, rowIndex, rowSpan) => {
534
+ return sum(rowHeights.slice(rowIndex, rowIndex + rowSpan));
535
+ };
447
536
  var measureTableHeight = (doc, table, env, styles, computeColumnPixelWidths2) => {
448
537
  const localLeft = table.marginLeft ?? 0;
449
538
  const localRight = table.marginRight ?? 0;
450
539
  const width = Math.max(env.innerWidth - localLeft - localRight, 1);
540
+ const totalCols = table.widths.length;
451
541
  const colWidths = computeColumnPixelWidths2(table.widths, width);
452
- let totalHeight = 0;
453
- totalHeight += table.marginTop ?? 0;
454
- table.body.forEach((row) => {
455
- let rowHeight = 0;
456
- row.forEach((rawCell, colIndex) => {
542
+ const rowHeights = [];
543
+ const activeRowSpans = Array(totalCols).fill(0);
544
+ const placedRows = [];
545
+ for (let rowIndex = 0; rowIndex < table.body.length; rowIndex++) {
546
+ const row = table.body[rowIndex];
547
+ const placedRow = buildPlacedRow(row, totalCols, activeRowSpans);
548
+ placedRows.push(placedRow);
549
+ let rowHeight = 12;
550
+ placedRow.forEach(({ cell: rawCell, startCol, colSpan }) => {
457
551
  const cell = resolveTableCell(styles, rawCell);
458
- const colW = colWidths[colIndex] ?? 50;
459
- const fontSize = cell.fontSize ?? 10;
460
- const isBold = !!cell.bold;
461
- const isItalic = !!cell.italic;
462
- const fontName = cell.font ? cell.font : isBold && isItalic ? "Helvetica-BoldOblique" : isBold ? "Helvetica-Bold" : isItalic ? "Helvetica-Oblique" : "Helvetica";
463
- doc.font(fontName);
464
- doc.fontSize(fontSize);
465
- const h = doc.heightOfString(cell.text, { width: colW });
466
- rowHeight = Math.max(rowHeight, h + 4);
552
+ const paddingLeft = cell.paddingLeft ?? 2;
553
+ const paddingRight = cell.paddingRight ?? 2;
554
+ const paddingTop = cell.paddingTop ?? 2;
555
+ const paddingBottom = cell.paddingBottom ?? 2;
556
+ const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
557
+ const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
558
+ doc.font(getCellFontName(cell));
559
+ doc.fontSize(cell.fontSize ?? 10);
560
+ const h = doc.heightOfString(cell.text, { width: textWidth }) + paddingTop + paddingBottom;
561
+ rowHeight = Math.max(rowHeight, h);
467
562
  });
468
- totalHeight += rowHeight;
469
- });
563
+ rowHeights.push(rowHeight);
564
+ decrementActiveSpans(activeRowSpans);
565
+ }
566
+ for (let rowIndex = 0; rowIndex < placedRows.length; rowIndex++) {
567
+ const placedRow = placedRows[rowIndex];
568
+ placedRow.forEach(({ cell: rawCell, startCol, colSpan, rowSpan }) => {
569
+ if (rowSpan <= 1) return;
570
+ const cell = resolveTableCell(styles, rawCell);
571
+ const paddingLeft = cell.paddingLeft ?? 2;
572
+ const paddingRight = cell.paddingRight ?? 2;
573
+ const paddingTop = cell.paddingTop ?? 2;
574
+ const paddingBottom = cell.paddingBottom ?? 2;
575
+ const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
576
+ const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
577
+ doc.font(getCellFontName(cell));
578
+ doc.fontSize(cell.fontSize ?? 10);
579
+ const neededHeight = doc.heightOfString(cell.text, { width: textWidth }) + paddingTop + paddingBottom;
580
+ const currentGroupHeight = getRowSpanGroupHeight(rowHeights, rowIndex, rowSpan);
581
+ if (neededHeight > currentGroupHeight) {
582
+ const deficit = neededHeight - currentGroupHeight;
583
+ rowHeights[rowIndex + rowSpan - 1] = (rowHeights[rowIndex + rowSpan - 1] ?? 12) + deficit;
584
+ }
585
+ });
586
+ }
587
+ let totalHeight = 0;
588
+ totalHeight += table.marginTop ?? 0;
589
+ totalHeight += sum(rowHeights);
470
590
  totalHeight += table.lineGap ?? 6;
471
591
  totalHeight += table.marginBottom ?? 0;
472
592
  return totalHeight;
@@ -480,6 +600,7 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
480
600
  const mb = table.marginBottom ?? 0;
481
601
  const baseY = y ?? ctx.currentY;
482
602
  const startY = baseY + mt;
603
+ const totalCols = table.widths.length;
483
604
  const colWidths = computeColumnPixelWidths2(table.widths, width);
484
605
  const layout = table.layout ?? {
485
606
  border: "all",
@@ -488,56 +609,84 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
488
609
  };
489
610
  const borderAll = layout.border !== "none";
490
611
  const headerRows = table.headerRows ?? 0;
491
- const measureRowHeight = (row) => {
492
- let rowHeight = 0;
493
- row.forEach((rawCell, colIndex) => {
494
- const cell = resolveTableCell(styles, rawCell);
495
- const colW = colWidths[colIndex] ?? 50;
496
- const fontSize = cell.fontSize ?? 10;
497
- const fontName = cell.font ? cell.font : cell.bold ? "Helvetica-Bold" : "Helvetica";
498
- doc.font(fontName);
499
- doc.fontSize(fontSize);
500
- const textWidth = Math.max(colW - 4, 1);
501
- const h = doc.heightOfString(cell.text, {
502
- width: textWidth
503
- }) + 4;
504
- rowHeight = Math.max(rowHeight, h);
505
- });
506
- return rowHeight || 12;
612
+ const buildPlacementAndHeights = (rows) => {
613
+ const placedRows = [];
614
+ const rowHeights = [];
615
+ const activeRowSpans = Array(totalCols).fill(0);
616
+ for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
617
+ const row = rows[rowIndex];
618
+ const placedRow = buildPlacedRow(row, totalCols, activeRowSpans);
619
+ placedRows.push(placedRow);
620
+ let rowHeight = 12;
621
+ placedRow.forEach(({ cell: rawCell, startCol, colSpan }) => {
622
+ const cell = resolveTableCell(styles, rawCell);
623
+ const paddingLeft = cell.paddingLeft ?? 2;
624
+ const paddingRight = cell.paddingRight ?? 2;
625
+ const paddingTop = cell.paddingTop ?? 2;
626
+ const paddingBottom = cell.paddingBottom ?? 2;
627
+ const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
628
+ const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
629
+ doc.font(getCellFontName(cell));
630
+ doc.fontSize(cell.fontSize ?? 10);
631
+ const h = doc.heightOfString(cell.text, { width: textWidth }) + paddingTop + paddingBottom;
632
+ rowHeight = Math.max(rowHeight, h);
633
+ });
634
+ rowHeights.push(rowHeight);
635
+ decrementActiveSpans(activeRowSpans);
636
+ }
637
+ for (let rowIndex = 0; rowIndex < placedRows.length; rowIndex++) {
638
+ const placedRow = placedRows[rowIndex];
639
+ placedRow.forEach(({ cell: rawCell, startCol, colSpan, rowSpan }) => {
640
+ if (rowSpan <= 1) return;
641
+ const cell = resolveTableCell(styles, rawCell);
642
+ const paddingLeft = cell.paddingLeft ?? 2;
643
+ const paddingRight = cell.paddingRight ?? 2;
644
+ const paddingTop = cell.paddingTop ?? 2;
645
+ const paddingBottom = cell.paddingBottom ?? 2;
646
+ const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
647
+ const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
648
+ doc.font(getCellFontName(cell));
649
+ doc.fontSize(cell.fontSize ?? 10);
650
+ const neededHeight = doc.heightOfString(cell.text, { width: textWidth }) + paddingTop + paddingBottom;
651
+ const currentGroupHeight = getRowSpanGroupHeight(rowHeights, rowIndex, rowSpan);
652
+ if (neededHeight > currentGroupHeight) {
653
+ const deficit = neededHeight - currentGroupHeight;
654
+ rowHeights[rowIndex + rowSpan - 1] = (rowHeights[rowIndex + rowSpan - 1] ?? 12) + deficit;
655
+ }
656
+ });
657
+ }
658
+ return { placedRows, rowHeights };
507
659
  };
508
- const drawRow = (row, rowTop2, rowHeight, isHeaderRow, drawTopBorder) => {
509
- row.forEach((rawCell, colIndex) => {
660
+ const allLayout = buildPlacementAndHeights(table.body);
661
+ const drawPlacedRow = (placedRow, rowTop2, rowHeight, rowIndex, rowHeights, isHeaderRow, drawTopBorder) => {
662
+ placedRow.forEach(({ cell: rawCell, startCol, colSpan, rowSpan }) => {
510
663
  const cell = resolveTableCell(styles, rawCell);
511
- const colW = colWidths[colIndex] ?? 50;
512
- const x = baseLeft + colWidths.slice(0, colIndex).reduce((a, b) => a + b, 0);
664
+ const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
665
+ const x = baseLeft + sum(colWidths.slice(0, startCol));
513
666
  const fontSize = cell.fontSize ?? 10;
667
+ const drawHeight = getRowSpanGroupHeight(rowHeights, rowIndex, rowSpan);
668
+ const paddingLeft = cell.paddingLeft ?? 2;
669
+ const paddingRight = cell.paddingRight ?? 2;
670
+ const paddingTop = cell.paddingTop ?? 2;
671
+ const paddingBottom = cell.paddingBottom ?? 2;
514
672
  doc.save();
515
673
  if (cell.fillColor) {
516
- doc.rect(x, rowTop2, colW, rowHeight).fillOpacity(1).fill(cell.fillColor);
674
+ doc.rect(x, rowTop2, colW, drawHeight).fillOpacity(1).fill(cell.fillColor);
517
675
  doc.fillOpacity(1);
518
676
  }
519
- const isBold = !!cell.bold;
520
- const isItalic = !!cell.italic;
521
- const fontName = cell.font ? cell.font : isBold && isItalic ? "Helvetica-BoldOblique" : isBold ? "Helvetica-Bold" : isItalic ? "Helvetica-Oblique" : "Helvetica";
522
- doc.font(fontName);
677
+ doc.font(getCellFontName(cell));
523
678
  doc.fontSize(fontSize);
524
- if (cell.color) {
525
- doc.fillColor(cell.color);
526
- } else {
527
- doc.fillColor("black");
528
- }
679
+ doc.fillColor(cell.color ?? "black");
529
680
  const align = cell.align ?? "left";
530
- const textWidth = Math.max(colW - 4, 1);
531
- const textHeight = doc.heightOfString(cell.text, {
532
- width: textWidth
533
- });
534
- let textY = rowTop2 + 2;
681
+ const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
682
+ const textHeight = doc.heightOfString(cell.text, { width: textWidth });
683
+ let textY = rowTop2 + paddingTop;
535
684
  if (isHeaderRow) {
536
- const available = rowHeight - 4;
685
+ const available = Math.max(drawHeight - paddingTop - paddingBottom, 0);
537
686
  const offset = Math.max((available - textHeight) / 2, 0);
538
- textY = rowTop2 + 2 + offset;
687
+ textY = rowTop2 + paddingTop + offset;
539
688
  }
540
- doc.text(cell.text, x + 2, textY, {
689
+ doc.text(cell.text, x + paddingLeft, textY, {
541
690
  width: textWidth,
542
691
  align,
543
692
  underline: !!cell.underline,
@@ -545,52 +694,58 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
545
694
  link: cell.link
546
695
  });
547
696
  doc.restore();
548
- });
549
- if (borderAll) {
550
- const bottomY = rowTop2 + rowHeight;
551
- doc.save();
552
- doc.lineWidth(0.5);
553
- doc.strokeColor(layout.hLineColor ?? "#ccc");
554
- if (drawTopBorder) {
555
- doc.moveTo(baseLeft, rowTop2).lineTo(baseLeft + width, rowTop2).stroke();
556
- }
557
- doc.moveTo(baseLeft, bottomY).lineTo(baseLeft + width, bottomY).stroke();
558
- doc.restore();
559
- doc.save();
560
- doc.lineWidth(0.5);
561
- doc.strokeColor(layout.vLineColor ?? "#ccc");
562
- let x = baseLeft;
563
- colWidths.forEach((colW) => {
697
+ if (borderAll) {
698
+ const bottomY = rowTop2 + drawHeight;
699
+ doc.save();
700
+ doc.lineWidth(0.5);
701
+ doc.strokeColor(layout.hLineColor ?? "#ccc");
702
+ if (drawTopBorder) {
703
+ doc.moveTo(x, rowTop2).lineTo(x + colW, rowTop2).stroke();
704
+ }
705
+ doc.moveTo(x, bottomY).lineTo(x + colW, bottomY).stroke();
706
+ doc.restore();
707
+ doc.save();
708
+ doc.lineWidth(0.5);
709
+ doc.strokeColor(layout.vLineColor ?? "#ccc");
564
710
  doc.moveTo(x, rowTop2).lineTo(x, bottomY).stroke();
565
- x += colW;
566
- });
567
- doc.moveTo(baseLeft + width, rowTop2).lineTo(baseLeft + width, bottomY).stroke();
568
- doc.restore();
569
- }
711
+ doc.moveTo(x + colW, rowTop2).lineTo(x + colW, bottomY).stroke();
712
+ doc.restore();
713
+ }
714
+ });
570
715
  return rowTop2 + rowHeight;
571
716
  };
717
+ const headerRowsSource = table.body.slice(0, headerRows);
572
718
  const drawHeaderRowsOnNewPage = () => {
573
719
  if (!headerRows) return doc.page.margins.top + mt;
720
+ const headerLayout = buildPlacementAndHeights(headerRowsSource);
574
721
  let rowTop2 = doc.page.margins.top + mt;
575
- for (let i = 0; i < headerRows; i++) {
576
- const headerRow = table.body[i];
577
- const headerHeight = measureRowHeight(headerRow);
578
- rowTop2 = drawRow(headerRow, rowTop2, headerHeight, true, true);
722
+ for (let i = 0; i < headerLayout.placedRows.length; i++) {
723
+ rowTop2 = drawPlacedRow(
724
+ headerLayout.placedRows[i],
725
+ rowTop2,
726
+ headerLayout.rowHeights[i],
727
+ i,
728
+ headerLayout.rowHeights,
729
+ true,
730
+ true
731
+ );
579
732
  }
580
733
  return rowTop2;
581
734
  };
582
735
  let rowTop = startY;
583
- table.body.forEach((row, rowIndex) => {
736
+ for (let rowIndex = 0; rowIndex < table.body.length; rowIndex++) {
584
737
  const isHeaderRow = rowIndex < headerRows;
585
- const rowHeight = measureRowHeight(row);
738
+ const placedRow = allLayout.placedRows[rowIndex];
739
+ const rowHeight = allLayout.rowHeights[rowIndex];
740
+ const maxRowSpan = Math.max(...placedRow.map((p) => p.rowSpan), 1);
741
+ const reservedHeight = getRowSpanGroupHeight(allLayout.rowHeights, rowIndex, maxRowSpan);
586
742
  const bottomLimit = bottomLimitForContent();
587
- if (rowTop + rowHeight > bottomLimit) {
743
+ if (rowTop + reservedHeight > bottomLimit) {
588
744
  finishPage2(true);
589
745
  rowTop = headerRows ? drawHeaderRowsOnNewPage() : doc.page.margins.top + mt;
590
746
  }
591
- const drawTopBorder = true;
592
- rowTop = drawRow(row, rowTop, rowHeight, isHeaderRow, drawTopBorder);
593
- });
747
+ rowTop = drawPlacedRow(placedRow, rowTop, rowHeight, rowIndex, allLayout.rowHeights, isHeaderRow, true);
748
+ }
594
749
  const newY = rowTop + (table.lineGap ?? 6) + mb;
595
750
  if (y === null) ctx.currentY = newY;
596
751
  return newY;
@@ -785,38 +940,6 @@ var contentEnv = (doc) => ({
785
940
  allowPageBreak: true
786
941
  });
787
942
 
788
- // src/renderer-engine/utils/error.ts
789
- var PdfEngineError = class extends Error {
790
- code;
791
- statusCode;
792
- details;
793
- retryable;
794
- cause;
795
- constructor(args) {
796
- super(args.message);
797
- this.name = "PdfEngineError";
798
- this.code = args.code;
799
- this.statusCode = args.statusCode ?? 400;
800
- this.details = args.details;
801
- this.retryable = args.retryable ?? false;
802
- this.cause = args.cause;
803
- }
804
- };
805
- function isPdfEngineError(e) {
806
- return e instanceof PdfEngineError;
807
- }
808
- function toPdfEngineError(cause, fallback) {
809
- if (cause instanceof PdfEngineError) return cause;
810
- return new PdfEngineError({
811
- code: fallback.code,
812
- message: fallback.message,
813
- statusCode: fallback.statusCode,
814
- details: fallback.details,
815
- retryable: fallback.retryable,
816
- cause
817
- });
818
- }
819
-
820
943
  // src/renderer-engine/utils/finish-page.ts
821
944
  function finishPage({
822
945
  addNewPage,
@@ -1066,7 +1189,11 @@ function resolveTableCell(styles, cell) {
1066
1189
  italic: cell.italic ?? styleDef.italic,
1067
1190
  color: cell.color ?? styleDef.color,
1068
1191
  fillColor: cell.fillColor ?? styleDef.fillColor,
1069
- font: cell.font ?? styleDef.font
1192
+ font: cell.font ?? styleDef.font,
1193
+ paddingTop: cell.paddingTop,
1194
+ paddingRight: cell.paddingRight,
1195
+ paddingBottom: cell.paddingBottom,
1196
+ paddingLeft: cell.paddingLeft
1070
1197
  };
1071
1198
  }
1072
1199
  var getFontNameForText = (tb) => {
@@ -1737,6 +1864,7 @@ async function renderCustomPdf(def, outputPath) {
1737
1864
  const footerBandHeight = def.margins.bottom ?? 0;
1738
1865
  const doc = new PDFDocument({
1739
1866
  size: def.pageSize || "A4",
1867
+ layout: def.pageOrientation === "landscape" ? "landscape" : "portrait",
1740
1868
  margins: {
1741
1869
  top: headerBandHeight,
1742
1870
  bottom: footerBandHeight,
@@ -1841,6 +1969,7 @@ async function renderCustomPdfToBuffer(def) {
1841
1969
  const footerBandHeight = def.margins.bottom ?? 0;
1842
1970
  const doc = new PDFDocument({
1843
1971
  size: def.pageSize || "A4",
1972
+ layout: def.pageOrientation === "landscape" ? "landscape" : "portrait",
1844
1973
  margins: {
1845
1974
  top: headerBandHeight,
1846
1975
  bottom: footerBandHeight,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "av6-pdf-engine",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "module": "dist/index.mjs",