av6-pdf-engine 1.0.9 → 1.2.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 +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +299 -86
- package/dist/index.mjs +299 -86
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -61,12 +61,18 @@ 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";
|
|
67
73
|
headerRows?: number;
|
|
68
74
|
widths: Width[];
|
|
69
|
-
body: TableCell[][]
|
|
75
|
+
body: TableCell[][] | TableBodyIterable[] | Array<TableCell[] | TableBodyIterable>;
|
|
70
76
|
layout?: {
|
|
71
77
|
border?: "all" | "none";
|
|
72
78
|
hLineColor?: string;
|
|
@@ -77,6 +83,18 @@ interface TableBlock extends BaseBlock {
|
|
|
77
83
|
marginBottom?: number;
|
|
78
84
|
marginLeft?: number;
|
|
79
85
|
marginRight?: number;
|
|
86
|
+
[key: string]: unknown;
|
|
87
|
+
}
|
|
88
|
+
interface TableBodyIterable {
|
|
89
|
+
isIterable: boolean;
|
|
90
|
+
iterableKey: string;
|
|
91
|
+
content: Array<TableCell>;
|
|
92
|
+
}
|
|
93
|
+
interface PlacedCell {
|
|
94
|
+
cell: TableCell;
|
|
95
|
+
startCol: number;
|
|
96
|
+
colSpan: number;
|
|
97
|
+
rowSpan: number;
|
|
80
98
|
}
|
|
81
99
|
|
|
82
100
|
interface ColumnsBlock extends BaseBlock {
|
|
@@ -217,6 +235,7 @@ interface FontRegistration {
|
|
|
217
235
|
interface CustomDocDefinition {
|
|
218
236
|
pageSize?: string;
|
|
219
237
|
margins: PageMargins;
|
|
238
|
+
pageOrientation?: "portrait" | "landscape";
|
|
220
239
|
/** Optional font registrations (custom TTF/OTF) */
|
|
221
240
|
fonts?: FontRegistration[];
|
|
222
241
|
header?: HeaderDef;
|
|
@@ -339,4 +358,4 @@ declare function toPdfEngineError(cause: unknown, fallback: {
|
|
|
339
358
|
declare function renderCustomPdf(def: CustomDocDefinition, outputPath: string): Promise<void>;
|
|
340
359
|
declare function renderCustomPdfToBuffer(def: CustomDocDefinition): Promise<Buffer>;
|
|
341
360
|
|
|
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 };
|
|
361
|
+
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 TableBodyIterable, type TableCell, type TextBlock, type WatermarkDef, type WatermarkMode, type Width, isPdfEngineError, renderCustomPdf, renderCustomPdfToBuffer, toPdfEngineError };
|
package/dist/index.d.ts
CHANGED
|
@@ -61,12 +61,18 @@ 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";
|
|
67
73
|
headerRows?: number;
|
|
68
74
|
widths: Width[];
|
|
69
|
-
body: TableCell[][]
|
|
75
|
+
body: TableCell[][] | TableBodyIterable[] | Array<TableCell[] | TableBodyIterable>;
|
|
70
76
|
layout?: {
|
|
71
77
|
border?: "all" | "none";
|
|
72
78
|
hLineColor?: string;
|
|
@@ -77,6 +83,18 @@ interface TableBlock extends BaseBlock {
|
|
|
77
83
|
marginBottom?: number;
|
|
78
84
|
marginLeft?: number;
|
|
79
85
|
marginRight?: number;
|
|
86
|
+
[key: string]: unknown;
|
|
87
|
+
}
|
|
88
|
+
interface TableBodyIterable {
|
|
89
|
+
isIterable: boolean;
|
|
90
|
+
iterableKey: string;
|
|
91
|
+
content: Array<TableCell>;
|
|
92
|
+
}
|
|
93
|
+
interface PlacedCell {
|
|
94
|
+
cell: TableCell;
|
|
95
|
+
startCol: number;
|
|
96
|
+
colSpan: number;
|
|
97
|
+
rowSpan: number;
|
|
80
98
|
}
|
|
81
99
|
|
|
82
100
|
interface ColumnsBlock extends BaseBlock {
|
|
@@ -217,6 +235,7 @@ interface FontRegistration {
|
|
|
217
235
|
interface CustomDocDefinition {
|
|
218
236
|
pageSize?: string;
|
|
219
237
|
margins: PageMargins;
|
|
238
|
+
pageOrientation?: "portrait" | "landscape";
|
|
220
239
|
/** Optional font registrations (custom TTF/OTF) */
|
|
221
240
|
fonts?: FontRegistration[];
|
|
222
241
|
header?: HeaderDef;
|
|
@@ -339,4 +358,4 @@ declare function toPdfEngineError(cause: unknown, fallback: {
|
|
|
339
358
|
declare function renderCustomPdf(def: CustomDocDefinition, outputPath: string): Promise<void>;
|
|
340
359
|
declare function renderCustomPdfToBuffer(def: CustomDocDefinition): Promise<Buffer>;
|
|
341
360
|
|
|
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 };
|
|
361
|
+
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 TableBodyIterable, type TableCell, type TextBlock, type WatermarkDef, type WatermarkMode, type Width, isPdfEngineError, renderCustomPdf, renderCustomPdfToBuffer, toPdfEngineError };
|
package/dist/index.js
CHANGED
|
@@ -519,29 +519,200 @@ var processLineBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
|
|
|
519
519
|
};
|
|
520
520
|
|
|
521
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
|
+
};
|
|
579
|
+
var isTableBodyIterableEntry = (entry) => {
|
|
580
|
+
if (!entry || typeof entry !== "object") return false;
|
|
581
|
+
const row = entry;
|
|
582
|
+
return typeof row.isIterable === "boolean" && typeof row.iterableKey === "string" && Array.isArray(row.content);
|
|
583
|
+
};
|
|
584
|
+
var isTableCellInput = (rawBlock) => !("type" in rawBlock);
|
|
585
|
+
var toTableCell = (rawBlock) => {
|
|
586
|
+
if ("type" in rawBlock && rawBlock.type !== "text") {
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
const tableCellInput = isTableCellInput(rawBlock) ? rawBlock : void 0;
|
|
590
|
+
return {
|
|
591
|
+
text: rawBlock.text,
|
|
592
|
+
italic: rawBlock.italic,
|
|
593
|
+
underline: rawBlock.underline,
|
|
594
|
+
link: rawBlock.link,
|
|
595
|
+
strike: rawBlock.strike,
|
|
596
|
+
bold: rawBlock.bold,
|
|
597
|
+
fontSize: rawBlock.fontSize,
|
|
598
|
+
font: rawBlock.font,
|
|
599
|
+
align: rawBlock.align,
|
|
600
|
+
color: rawBlock.color,
|
|
601
|
+
fillColor: tableCellInput?.fillColor,
|
|
602
|
+
style: rawBlock.style,
|
|
603
|
+
colSpan: tableCellInput?.colSpan,
|
|
604
|
+
rowSpan: tableCellInput?.rowSpan,
|
|
605
|
+
paddingTop: tableCellInput?.paddingTop,
|
|
606
|
+
paddingRight: tableCellInput?.paddingRight,
|
|
607
|
+
paddingBottom: tableCellInput?.paddingBottom,
|
|
608
|
+
paddingLeft: tableCellInput?.paddingLeft
|
|
609
|
+
};
|
|
610
|
+
};
|
|
611
|
+
var resolvePathValue = (obj, dottedPath) => {
|
|
612
|
+
if (!obj || typeof obj !== "object") return void 0;
|
|
613
|
+
return dottedPath.split(".").reduce((acc, key) => {
|
|
614
|
+
if (!acc || typeof acc !== "object") return void 0;
|
|
615
|
+
return acc[key];
|
|
616
|
+
}, obj);
|
|
617
|
+
};
|
|
618
|
+
var interpolateCellText = (text, item, rowIndex) => {
|
|
619
|
+
return text.replace(/\{\{\s*([^}]+)\s*\}\}/g, (_match, tokenRaw) => {
|
|
620
|
+
const token = tokenRaw.trim();
|
|
621
|
+
if (!token) return "";
|
|
622
|
+
if (token === "index") return String(rowIndex + 1);
|
|
623
|
+
const normalizedPath = token.startsWith("item.") ? token.slice(5) : token;
|
|
624
|
+
const value = resolvePathValue(item, normalizedPath);
|
|
625
|
+
if (value === void 0 || value === null) return "";
|
|
626
|
+
return typeof value === "string" ? value : String(value);
|
|
627
|
+
});
|
|
628
|
+
};
|
|
629
|
+
var applyIterableData = (cell, item, rowIndex) => ({
|
|
630
|
+
...cell,
|
|
631
|
+
text: interpolateCellText(cell.text, item, rowIndex)
|
|
632
|
+
});
|
|
633
|
+
var expandIterableRow = (table, bodyDef) => {
|
|
634
|
+
const templateRow = bodyDef.content.map(toTableCell).filter((cell) => cell !== null);
|
|
635
|
+
if (!templateRow.length) {
|
|
636
|
+
return [];
|
|
637
|
+
}
|
|
638
|
+
if (!bodyDef.isIterable) {
|
|
639
|
+
return [templateRow];
|
|
640
|
+
}
|
|
641
|
+
const iterableSource = table[bodyDef.iterableKey];
|
|
642
|
+
if (!Array.isArray(iterableSource) || !iterableSource.length) {
|
|
643
|
+
return [templateRow];
|
|
644
|
+
}
|
|
645
|
+
return iterableSource.map((item, rowIndex) => templateRow.map((cell) => applyIterableData(cell, item, rowIndex)));
|
|
646
|
+
};
|
|
647
|
+
var normalizeTableRows = (table) => {
|
|
648
|
+
if (!Array.isArray(table.body)) return [];
|
|
649
|
+
const rows = [];
|
|
650
|
+
for (const entry of table.body) {
|
|
651
|
+
if (Array.isArray(entry)) {
|
|
652
|
+
rows.push(entry);
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
if (isTableBodyIterableEntry(entry)) {
|
|
656
|
+
rows.push(...expandIterableRow(table, entry));
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
return rows;
|
|
660
|
+
};
|
|
522
661
|
var measureTableHeight = (doc, table, env, styles, computeColumnPixelWidths2) => {
|
|
523
662
|
const localLeft = table.marginLeft ?? 0;
|
|
524
663
|
const localRight = table.marginRight ?? 0;
|
|
525
664
|
const width = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
665
|
+
const rows = normalizeTableRows(table);
|
|
666
|
+
const totalCols = table.widths.length;
|
|
526
667
|
const colWidths = computeColumnPixelWidths2(table.widths, width);
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
row
|
|
668
|
+
const rowHeights = [];
|
|
669
|
+
const activeRowSpans = Array(totalCols).fill(0);
|
|
670
|
+
const placedRows = [];
|
|
671
|
+
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
672
|
+
const row = rows[rowIndex];
|
|
673
|
+
const placedRow = buildPlacedRow(row, totalCols, activeRowSpans);
|
|
674
|
+
placedRows.push(placedRow);
|
|
675
|
+
let rowHeight = 12;
|
|
676
|
+
placedRow.forEach(({ cell: rawCell, startCol, colSpan }) => {
|
|
532
677
|
const cell = resolveTableCell(styles, rawCell);
|
|
533
|
-
const
|
|
534
|
-
const
|
|
535
|
-
const
|
|
536
|
-
const
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
doc.
|
|
540
|
-
|
|
541
|
-
|
|
678
|
+
const paddingLeft = cell.paddingLeft ?? 2;
|
|
679
|
+
const paddingRight = cell.paddingRight ?? 2;
|
|
680
|
+
const paddingTop = cell.paddingTop ?? 2;
|
|
681
|
+
const paddingBottom = cell.paddingBottom ?? 2;
|
|
682
|
+
const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
|
|
683
|
+
const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
|
|
684
|
+
doc.font(getCellFontName(cell));
|
|
685
|
+
doc.fontSize(cell.fontSize ?? 10);
|
|
686
|
+
const h = doc.heightOfString(cell.text, { width: textWidth }) + paddingTop + paddingBottom;
|
|
687
|
+
rowHeight = Math.max(rowHeight, h);
|
|
542
688
|
});
|
|
543
|
-
|
|
544
|
-
|
|
689
|
+
rowHeights.push(rowHeight);
|
|
690
|
+
decrementActiveSpans(activeRowSpans);
|
|
691
|
+
}
|
|
692
|
+
for (let rowIndex = 0; rowIndex < placedRows.length; rowIndex++) {
|
|
693
|
+
const placedRow = placedRows[rowIndex];
|
|
694
|
+
placedRow.forEach(({ cell: rawCell, startCol, colSpan, rowSpan }) => {
|
|
695
|
+
if (rowSpan <= 1) return;
|
|
696
|
+
const cell = resolveTableCell(styles, rawCell);
|
|
697
|
+
const paddingLeft = cell.paddingLeft ?? 2;
|
|
698
|
+
const paddingRight = cell.paddingRight ?? 2;
|
|
699
|
+
const paddingTop = cell.paddingTop ?? 2;
|
|
700
|
+
const paddingBottom = cell.paddingBottom ?? 2;
|
|
701
|
+
const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
|
|
702
|
+
const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
|
|
703
|
+
doc.font(getCellFontName(cell));
|
|
704
|
+
doc.fontSize(cell.fontSize ?? 10);
|
|
705
|
+
const neededHeight = doc.heightOfString(cell.text, { width: textWidth }) + paddingTop + paddingBottom;
|
|
706
|
+
const currentGroupHeight = getRowSpanGroupHeight(rowHeights, rowIndex, rowSpan);
|
|
707
|
+
if (neededHeight > currentGroupHeight) {
|
|
708
|
+
const deficit = neededHeight - currentGroupHeight;
|
|
709
|
+
rowHeights[rowIndex + rowSpan - 1] = (rowHeights[rowIndex + rowSpan - 1] ?? 12) + deficit;
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
let totalHeight = 0;
|
|
714
|
+
totalHeight += table.marginTop ?? 0;
|
|
715
|
+
totalHeight += sum(rowHeights);
|
|
545
716
|
totalHeight += table.lineGap ?? 6;
|
|
546
717
|
totalHeight += table.marginBottom ?? 0;
|
|
547
718
|
return totalHeight;
|
|
@@ -555,6 +726,7 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
|
|
|
555
726
|
const mb = table.marginBottom ?? 0;
|
|
556
727
|
const baseY = y ?? ctx.currentY;
|
|
557
728
|
const startY = baseY + mt;
|
|
729
|
+
const totalCols = table.widths.length;
|
|
558
730
|
const colWidths = computeColumnPixelWidths2(table.widths, width);
|
|
559
731
|
const layout = table.layout ?? {
|
|
560
732
|
border: "all",
|
|
@@ -563,56 +735,85 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
|
|
|
563
735
|
};
|
|
564
736
|
const borderAll = layout.border !== "none";
|
|
565
737
|
const headerRows = table.headerRows ?? 0;
|
|
566
|
-
const
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
738
|
+
const rows = normalizeTableRows(table);
|
|
739
|
+
const buildPlacementAndHeights = (rows2) => {
|
|
740
|
+
const placedRows = [];
|
|
741
|
+
const rowHeights = [];
|
|
742
|
+
const activeRowSpans = Array(totalCols).fill(0);
|
|
743
|
+
for (let rowIndex = 0; rowIndex < rows2.length; rowIndex++) {
|
|
744
|
+
const row = rows2[rowIndex];
|
|
745
|
+
const placedRow = buildPlacedRow(row, totalCols, activeRowSpans);
|
|
746
|
+
placedRows.push(placedRow);
|
|
747
|
+
let rowHeight = 12;
|
|
748
|
+
placedRow.forEach(({ cell: rawCell, startCol, colSpan }) => {
|
|
749
|
+
const cell = resolveTableCell(styles, rawCell);
|
|
750
|
+
const paddingLeft = cell.paddingLeft ?? 2;
|
|
751
|
+
const paddingRight = cell.paddingRight ?? 2;
|
|
752
|
+
const paddingTop = cell.paddingTop ?? 2;
|
|
753
|
+
const paddingBottom = cell.paddingBottom ?? 2;
|
|
754
|
+
const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
|
|
755
|
+
const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
|
|
756
|
+
doc.font(getCellFontName(cell));
|
|
757
|
+
doc.fontSize(cell.fontSize ?? 10);
|
|
758
|
+
const h = doc.heightOfString(cell.text, { width: textWidth }) + paddingTop + paddingBottom;
|
|
759
|
+
rowHeight = Math.max(rowHeight, h);
|
|
760
|
+
});
|
|
761
|
+
rowHeights.push(rowHeight);
|
|
762
|
+
decrementActiveSpans(activeRowSpans);
|
|
763
|
+
}
|
|
764
|
+
for (let rowIndex = 0; rowIndex < placedRows.length; rowIndex++) {
|
|
765
|
+
const placedRow = placedRows[rowIndex];
|
|
766
|
+
placedRow.forEach(({ cell: rawCell, startCol, colSpan, rowSpan }) => {
|
|
767
|
+
if (rowSpan <= 1) return;
|
|
768
|
+
const cell = resolveTableCell(styles, rawCell);
|
|
769
|
+
const paddingLeft = cell.paddingLeft ?? 2;
|
|
770
|
+
const paddingRight = cell.paddingRight ?? 2;
|
|
771
|
+
const paddingTop = cell.paddingTop ?? 2;
|
|
772
|
+
const paddingBottom = cell.paddingBottom ?? 2;
|
|
773
|
+
const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
|
|
774
|
+
const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
|
|
775
|
+
doc.font(getCellFontName(cell));
|
|
776
|
+
doc.fontSize(cell.fontSize ?? 10);
|
|
777
|
+
const neededHeight = doc.heightOfString(cell.text, { width: textWidth }) + paddingTop + paddingBottom;
|
|
778
|
+
const currentGroupHeight = getRowSpanGroupHeight(rowHeights, rowIndex, rowSpan);
|
|
779
|
+
if (neededHeight > currentGroupHeight) {
|
|
780
|
+
const deficit = neededHeight - currentGroupHeight;
|
|
781
|
+
rowHeights[rowIndex + rowSpan - 1] = (rowHeights[rowIndex + rowSpan - 1] ?? 12) + deficit;
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
return { placedRows, rowHeights };
|
|
582
786
|
};
|
|
583
|
-
const
|
|
584
|
-
|
|
787
|
+
const allLayout = buildPlacementAndHeights(rows);
|
|
788
|
+
const drawPlacedRow = (placedRow, rowTop2, rowHeight, rowIndex, rowHeights, isHeaderRow, drawTopBorder) => {
|
|
789
|
+
placedRow.forEach(({ cell: rawCell, startCol, colSpan, rowSpan }) => {
|
|
585
790
|
const cell = resolveTableCell(styles, rawCell);
|
|
586
|
-
const colW = colWidths
|
|
587
|
-
const x = baseLeft + colWidths.slice(0,
|
|
791
|
+
const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
|
|
792
|
+
const x = baseLeft + sum(colWidths.slice(0, startCol));
|
|
588
793
|
const fontSize = cell.fontSize ?? 10;
|
|
794
|
+
const drawHeight = getRowSpanGroupHeight(rowHeights, rowIndex, rowSpan);
|
|
795
|
+
const paddingLeft = cell.paddingLeft ?? 2;
|
|
796
|
+
const paddingRight = cell.paddingRight ?? 2;
|
|
797
|
+
const paddingTop = cell.paddingTop ?? 2;
|
|
798
|
+
const paddingBottom = cell.paddingBottom ?? 2;
|
|
589
799
|
doc.save();
|
|
590
800
|
if (cell.fillColor) {
|
|
591
|
-
doc.rect(x, rowTop2, colW,
|
|
801
|
+
doc.rect(x, rowTop2, colW, drawHeight).fillOpacity(1).fill(cell.fillColor);
|
|
592
802
|
doc.fillOpacity(1);
|
|
593
803
|
}
|
|
594
|
-
|
|
595
|
-
const isItalic = !!cell.italic;
|
|
596
|
-
const fontName = cell.font ? cell.font : isBold && isItalic ? "Helvetica-BoldOblique" : isBold ? "Helvetica-Bold" : isItalic ? "Helvetica-Oblique" : "Helvetica";
|
|
597
|
-
doc.font(fontName);
|
|
804
|
+
doc.font(getCellFontName(cell));
|
|
598
805
|
doc.fontSize(fontSize);
|
|
599
|
-
|
|
600
|
-
doc.fillColor(cell.color);
|
|
601
|
-
} else {
|
|
602
|
-
doc.fillColor("black");
|
|
603
|
-
}
|
|
806
|
+
doc.fillColor(cell.color ?? "black");
|
|
604
807
|
const align = cell.align ?? "left";
|
|
605
|
-
const textWidth = Math.max(colW -
|
|
606
|
-
const textHeight = doc.heightOfString(cell.text, {
|
|
607
|
-
|
|
608
|
-
});
|
|
609
|
-
let textY = rowTop2 + 2;
|
|
808
|
+
const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
|
|
809
|
+
const textHeight = doc.heightOfString(cell.text, { width: textWidth });
|
|
810
|
+
let textY = rowTop2 + paddingTop;
|
|
610
811
|
if (isHeaderRow) {
|
|
611
|
-
const available =
|
|
812
|
+
const available = Math.max(drawHeight - paddingTop - paddingBottom, 0);
|
|
612
813
|
const offset = Math.max((available - textHeight) / 2, 0);
|
|
613
|
-
textY = rowTop2 +
|
|
814
|
+
textY = rowTop2 + paddingTop + offset;
|
|
614
815
|
}
|
|
615
|
-
doc.text(cell.text, x +
|
|
816
|
+
doc.text(cell.text, x + paddingLeft, textY, {
|
|
616
817
|
width: textWidth,
|
|
617
818
|
align,
|
|
618
819
|
underline: !!cell.underline,
|
|
@@ -620,52 +821,58 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
|
|
|
620
821
|
link: cell.link
|
|
621
822
|
});
|
|
622
823
|
doc.restore();
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
doc.strokeColor(layout.vLineColor ?? "#ccc");
|
|
637
|
-
let x = baseLeft;
|
|
638
|
-
colWidths.forEach((colW) => {
|
|
824
|
+
if (borderAll) {
|
|
825
|
+
const bottomY = rowTop2 + drawHeight;
|
|
826
|
+
doc.save();
|
|
827
|
+
doc.lineWidth(0.5);
|
|
828
|
+
doc.strokeColor(layout.hLineColor ?? "#ccc");
|
|
829
|
+
if (drawTopBorder) {
|
|
830
|
+
doc.moveTo(x, rowTop2).lineTo(x + colW, rowTop2).stroke();
|
|
831
|
+
}
|
|
832
|
+
doc.moveTo(x, bottomY).lineTo(x + colW, bottomY).stroke();
|
|
833
|
+
doc.restore();
|
|
834
|
+
doc.save();
|
|
835
|
+
doc.lineWidth(0.5);
|
|
836
|
+
doc.strokeColor(layout.vLineColor ?? "#ccc");
|
|
639
837
|
doc.moveTo(x, rowTop2).lineTo(x, bottomY).stroke();
|
|
640
|
-
x
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
}
|
|
838
|
+
doc.moveTo(x + colW, rowTop2).lineTo(x + colW, bottomY).stroke();
|
|
839
|
+
doc.restore();
|
|
840
|
+
}
|
|
841
|
+
});
|
|
645
842
|
return rowTop2 + rowHeight;
|
|
646
843
|
};
|
|
844
|
+
const headerRowsSource = rows.slice(0, headerRows);
|
|
647
845
|
const drawHeaderRowsOnNewPage = () => {
|
|
648
846
|
if (!headerRows) return doc.page.margins.top + mt;
|
|
847
|
+
const headerLayout = buildPlacementAndHeights(headerRowsSource);
|
|
649
848
|
let rowTop2 = doc.page.margins.top + mt;
|
|
650
|
-
for (let i = 0; i <
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
849
|
+
for (let i = 0; i < headerLayout.placedRows.length; i++) {
|
|
850
|
+
rowTop2 = drawPlacedRow(
|
|
851
|
+
headerLayout.placedRows[i],
|
|
852
|
+
rowTop2,
|
|
853
|
+
headerLayout.rowHeights[i],
|
|
854
|
+
i,
|
|
855
|
+
headerLayout.rowHeights,
|
|
856
|
+
true,
|
|
857
|
+
true
|
|
858
|
+
);
|
|
654
859
|
}
|
|
655
860
|
return rowTop2;
|
|
656
861
|
};
|
|
657
862
|
let rowTop = startY;
|
|
658
|
-
|
|
863
|
+
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
659
864
|
const isHeaderRow = rowIndex < headerRows;
|
|
660
|
-
const
|
|
865
|
+
const placedRow = allLayout.placedRows[rowIndex];
|
|
866
|
+
const rowHeight = allLayout.rowHeights[rowIndex];
|
|
867
|
+
const maxRowSpan = Math.max(...placedRow.map((p) => p.rowSpan), 1);
|
|
868
|
+
const reservedHeight = getRowSpanGroupHeight(allLayout.rowHeights, rowIndex, maxRowSpan);
|
|
661
869
|
const bottomLimit = bottomLimitForContent();
|
|
662
|
-
if (rowTop +
|
|
870
|
+
if (rowTop + reservedHeight > bottomLimit) {
|
|
663
871
|
finishPage2(true);
|
|
664
872
|
rowTop = headerRows ? drawHeaderRowsOnNewPage() : doc.page.margins.top + mt;
|
|
665
873
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
});
|
|
874
|
+
rowTop = drawPlacedRow(placedRow, rowTop, rowHeight, rowIndex, allLayout.rowHeights, isHeaderRow, true);
|
|
875
|
+
}
|
|
669
876
|
const newY = rowTop + (table.lineGap ?? 6) + mb;
|
|
670
877
|
if (y === null) ctx.currentY = newY;
|
|
671
878
|
return newY;
|
|
@@ -1109,7 +1316,11 @@ function resolveTableCell(styles, cell) {
|
|
|
1109
1316
|
italic: cell.italic ?? styleDef.italic,
|
|
1110
1317
|
color: cell.color ?? styleDef.color,
|
|
1111
1318
|
fillColor: cell.fillColor ?? styleDef.fillColor,
|
|
1112
|
-
font: cell.font ?? styleDef.font
|
|
1319
|
+
font: cell.font ?? styleDef.font,
|
|
1320
|
+
paddingTop: cell.paddingTop,
|
|
1321
|
+
paddingRight: cell.paddingRight,
|
|
1322
|
+
paddingBottom: cell.paddingBottom,
|
|
1323
|
+
paddingLeft: cell.paddingLeft
|
|
1113
1324
|
};
|
|
1114
1325
|
}
|
|
1115
1326
|
var getFontNameForText = (tb) => {
|
|
@@ -1780,6 +1991,7 @@ async function renderCustomPdf(def, outputPath) {
|
|
|
1780
1991
|
const footerBandHeight = def.margins.bottom ?? 0;
|
|
1781
1992
|
const doc = new import_pdfkit.default({
|
|
1782
1993
|
size: def.pageSize || "A4",
|
|
1994
|
+
layout: def.pageOrientation === "landscape" ? "landscape" : "portrait",
|
|
1783
1995
|
margins: {
|
|
1784
1996
|
top: headerBandHeight,
|
|
1785
1997
|
bottom: footerBandHeight,
|
|
@@ -1884,6 +2096,7 @@ async function renderCustomPdfToBuffer(def) {
|
|
|
1884
2096
|
const footerBandHeight = def.margins.bottom ?? 0;
|
|
1885
2097
|
const doc = new import_pdfkit.default({
|
|
1886
2098
|
size: def.pageSize || "A4",
|
|
2099
|
+
layout: def.pageOrientation === "landscape" ? "landscape" : "portrait",
|
|
1887
2100
|
margins: {
|
|
1888
2101
|
top: headerBandHeight,
|
|
1889
2102
|
bottom: footerBandHeight,
|
package/dist/index.mjs
CHANGED
|
@@ -476,29 +476,200 @@ var processLineBlock = (doc, ctx, block, y, env, ensureSpaceFor) => {
|
|
|
476
476
|
};
|
|
477
477
|
|
|
478
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
|
+
};
|
|
536
|
+
var isTableBodyIterableEntry = (entry) => {
|
|
537
|
+
if (!entry || typeof entry !== "object") return false;
|
|
538
|
+
const row = entry;
|
|
539
|
+
return typeof row.isIterable === "boolean" && typeof row.iterableKey === "string" && Array.isArray(row.content);
|
|
540
|
+
};
|
|
541
|
+
var isTableCellInput = (rawBlock) => !("type" in rawBlock);
|
|
542
|
+
var toTableCell = (rawBlock) => {
|
|
543
|
+
if ("type" in rawBlock && rawBlock.type !== "text") {
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
const tableCellInput = isTableCellInput(rawBlock) ? rawBlock : void 0;
|
|
547
|
+
return {
|
|
548
|
+
text: rawBlock.text,
|
|
549
|
+
italic: rawBlock.italic,
|
|
550
|
+
underline: rawBlock.underline,
|
|
551
|
+
link: rawBlock.link,
|
|
552
|
+
strike: rawBlock.strike,
|
|
553
|
+
bold: rawBlock.bold,
|
|
554
|
+
fontSize: rawBlock.fontSize,
|
|
555
|
+
font: rawBlock.font,
|
|
556
|
+
align: rawBlock.align,
|
|
557
|
+
color: rawBlock.color,
|
|
558
|
+
fillColor: tableCellInput?.fillColor,
|
|
559
|
+
style: rawBlock.style,
|
|
560
|
+
colSpan: tableCellInput?.colSpan,
|
|
561
|
+
rowSpan: tableCellInput?.rowSpan,
|
|
562
|
+
paddingTop: tableCellInput?.paddingTop,
|
|
563
|
+
paddingRight: tableCellInput?.paddingRight,
|
|
564
|
+
paddingBottom: tableCellInput?.paddingBottom,
|
|
565
|
+
paddingLeft: tableCellInput?.paddingLeft
|
|
566
|
+
};
|
|
567
|
+
};
|
|
568
|
+
var resolvePathValue = (obj, dottedPath) => {
|
|
569
|
+
if (!obj || typeof obj !== "object") return void 0;
|
|
570
|
+
return dottedPath.split(".").reduce((acc, key) => {
|
|
571
|
+
if (!acc || typeof acc !== "object") return void 0;
|
|
572
|
+
return acc[key];
|
|
573
|
+
}, obj);
|
|
574
|
+
};
|
|
575
|
+
var interpolateCellText = (text, item, rowIndex) => {
|
|
576
|
+
return text.replace(/\{\{\s*([^}]+)\s*\}\}/g, (_match, tokenRaw) => {
|
|
577
|
+
const token = tokenRaw.trim();
|
|
578
|
+
if (!token) return "";
|
|
579
|
+
if (token === "index") return String(rowIndex + 1);
|
|
580
|
+
const normalizedPath = token.startsWith("item.") ? token.slice(5) : token;
|
|
581
|
+
const value = resolvePathValue(item, normalizedPath);
|
|
582
|
+
if (value === void 0 || value === null) return "";
|
|
583
|
+
return typeof value === "string" ? value : String(value);
|
|
584
|
+
});
|
|
585
|
+
};
|
|
586
|
+
var applyIterableData = (cell, item, rowIndex) => ({
|
|
587
|
+
...cell,
|
|
588
|
+
text: interpolateCellText(cell.text, item, rowIndex)
|
|
589
|
+
});
|
|
590
|
+
var expandIterableRow = (table, bodyDef) => {
|
|
591
|
+
const templateRow = bodyDef.content.map(toTableCell).filter((cell) => cell !== null);
|
|
592
|
+
if (!templateRow.length) {
|
|
593
|
+
return [];
|
|
594
|
+
}
|
|
595
|
+
if (!bodyDef.isIterable) {
|
|
596
|
+
return [templateRow];
|
|
597
|
+
}
|
|
598
|
+
const iterableSource = table[bodyDef.iterableKey];
|
|
599
|
+
if (!Array.isArray(iterableSource) || !iterableSource.length) {
|
|
600
|
+
return [templateRow];
|
|
601
|
+
}
|
|
602
|
+
return iterableSource.map((item, rowIndex) => templateRow.map((cell) => applyIterableData(cell, item, rowIndex)));
|
|
603
|
+
};
|
|
604
|
+
var normalizeTableRows = (table) => {
|
|
605
|
+
if (!Array.isArray(table.body)) return [];
|
|
606
|
+
const rows = [];
|
|
607
|
+
for (const entry of table.body) {
|
|
608
|
+
if (Array.isArray(entry)) {
|
|
609
|
+
rows.push(entry);
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
if (isTableBodyIterableEntry(entry)) {
|
|
613
|
+
rows.push(...expandIterableRow(table, entry));
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
return rows;
|
|
617
|
+
};
|
|
479
618
|
var measureTableHeight = (doc, table, env, styles, computeColumnPixelWidths2) => {
|
|
480
619
|
const localLeft = table.marginLeft ?? 0;
|
|
481
620
|
const localRight = table.marginRight ?? 0;
|
|
482
621
|
const width = Math.max(env.innerWidth - localLeft - localRight, 1);
|
|
622
|
+
const rows = normalizeTableRows(table);
|
|
623
|
+
const totalCols = table.widths.length;
|
|
483
624
|
const colWidths = computeColumnPixelWidths2(table.widths, width);
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
row
|
|
625
|
+
const rowHeights = [];
|
|
626
|
+
const activeRowSpans = Array(totalCols).fill(0);
|
|
627
|
+
const placedRows = [];
|
|
628
|
+
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
629
|
+
const row = rows[rowIndex];
|
|
630
|
+
const placedRow = buildPlacedRow(row, totalCols, activeRowSpans);
|
|
631
|
+
placedRows.push(placedRow);
|
|
632
|
+
let rowHeight = 12;
|
|
633
|
+
placedRow.forEach(({ cell: rawCell, startCol, colSpan }) => {
|
|
489
634
|
const cell = resolveTableCell(styles, rawCell);
|
|
490
|
-
const
|
|
491
|
-
const
|
|
492
|
-
const
|
|
493
|
-
const
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
doc.
|
|
497
|
-
|
|
498
|
-
|
|
635
|
+
const paddingLeft = cell.paddingLeft ?? 2;
|
|
636
|
+
const paddingRight = cell.paddingRight ?? 2;
|
|
637
|
+
const paddingTop = cell.paddingTop ?? 2;
|
|
638
|
+
const paddingBottom = cell.paddingBottom ?? 2;
|
|
639
|
+
const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
|
|
640
|
+
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;
|
|
644
|
+
rowHeight = Math.max(rowHeight, h);
|
|
499
645
|
});
|
|
500
|
-
|
|
501
|
-
|
|
646
|
+
rowHeights.push(rowHeight);
|
|
647
|
+
decrementActiveSpans(activeRowSpans);
|
|
648
|
+
}
|
|
649
|
+
for (let rowIndex = 0; rowIndex < placedRows.length; rowIndex++) {
|
|
650
|
+
const placedRow = placedRows[rowIndex];
|
|
651
|
+
placedRow.forEach(({ cell: rawCell, startCol, colSpan, rowSpan }) => {
|
|
652
|
+
if (rowSpan <= 1) return;
|
|
653
|
+
const cell = resolveTableCell(styles, rawCell);
|
|
654
|
+
const paddingLeft = cell.paddingLeft ?? 2;
|
|
655
|
+
const paddingRight = cell.paddingRight ?? 2;
|
|
656
|
+
const paddingTop = cell.paddingTop ?? 2;
|
|
657
|
+
const paddingBottom = cell.paddingBottom ?? 2;
|
|
658
|
+
const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
|
|
659
|
+
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;
|
|
663
|
+
const currentGroupHeight = getRowSpanGroupHeight(rowHeights, rowIndex, rowSpan);
|
|
664
|
+
if (neededHeight > currentGroupHeight) {
|
|
665
|
+
const deficit = neededHeight - currentGroupHeight;
|
|
666
|
+
rowHeights[rowIndex + rowSpan - 1] = (rowHeights[rowIndex + rowSpan - 1] ?? 12) + deficit;
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
let totalHeight = 0;
|
|
671
|
+
totalHeight += table.marginTop ?? 0;
|
|
672
|
+
totalHeight += sum(rowHeights);
|
|
502
673
|
totalHeight += table.lineGap ?? 6;
|
|
503
674
|
totalHeight += table.marginBottom ?? 0;
|
|
504
675
|
return totalHeight;
|
|
@@ -512,6 +683,7 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
|
|
|
512
683
|
const mb = table.marginBottom ?? 0;
|
|
513
684
|
const baseY = y ?? ctx.currentY;
|
|
514
685
|
const startY = baseY + mt;
|
|
686
|
+
const totalCols = table.widths.length;
|
|
515
687
|
const colWidths = computeColumnPixelWidths2(table.widths, width);
|
|
516
688
|
const layout = table.layout ?? {
|
|
517
689
|
border: "all",
|
|
@@ -520,56 +692,85 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
|
|
|
520
692
|
};
|
|
521
693
|
const borderAll = layout.border !== "none";
|
|
522
694
|
const headerRows = table.headerRows ?? 0;
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
const
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
695
|
+
const rows = normalizeTableRows(table);
|
|
696
|
+
const buildPlacementAndHeights = (rows2) => {
|
|
697
|
+
const placedRows = [];
|
|
698
|
+
const rowHeights = [];
|
|
699
|
+
const activeRowSpans = Array(totalCols).fill(0);
|
|
700
|
+
for (let rowIndex = 0; rowIndex < rows2.length; rowIndex++) {
|
|
701
|
+
const row = rows2[rowIndex];
|
|
702
|
+
const placedRow = buildPlacedRow(row, totalCols, activeRowSpans);
|
|
703
|
+
placedRows.push(placedRow);
|
|
704
|
+
let rowHeight = 12;
|
|
705
|
+
placedRow.forEach(({ cell: rawCell, startCol, colSpan }) => {
|
|
706
|
+
const cell = resolveTableCell(styles, rawCell);
|
|
707
|
+
const paddingLeft = cell.paddingLeft ?? 2;
|
|
708
|
+
const paddingRight = cell.paddingRight ?? 2;
|
|
709
|
+
const paddingTop = cell.paddingTop ?? 2;
|
|
710
|
+
const paddingBottom = cell.paddingBottom ?? 2;
|
|
711
|
+
const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
|
|
712
|
+
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;
|
|
716
|
+
rowHeight = Math.max(rowHeight, h);
|
|
717
|
+
});
|
|
718
|
+
rowHeights.push(rowHeight);
|
|
719
|
+
decrementActiveSpans(activeRowSpans);
|
|
720
|
+
}
|
|
721
|
+
for (let rowIndex = 0; rowIndex < placedRows.length; rowIndex++) {
|
|
722
|
+
const placedRow = placedRows[rowIndex];
|
|
723
|
+
placedRow.forEach(({ cell: rawCell, startCol, colSpan, rowSpan }) => {
|
|
724
|
+
if (rowSpan <= 1) return;
|
|
725
|
+
const cell = resolveTableCell(styles, rawCell);
|
|
726
|
+
const paddingLeft = cell.paddingLeft ?? 2;
|
|
727
|
+
const paddingRight = cell.paddingRight ?? 2;
|
|
728
|
+
const paddingTop = cell.paddingTop ?? 2;
|
|
729
|
+
const paddingBottom = cell.paddingBottom ?? 2;
|
|
730
|
+
const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
|
|
731
|
+
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;
|
|
735
|
+
const currentGroupHeight = getRowSpanGroupHeight(rowHeights, rowIndex, rowSpan);
|
|
736
|
+
if (neededHeight > currentGroupHeight) {
|
|
737
|
+
const deficit = neededHeight - currentGroupHeight;
|
|
738
|
+
rowHeights[rowIndex + rowSpan - 1] = (rowHeights[rowIndex + rowSpan - 1] ?? 12) + deficit;
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
return { placedRows, rowHeights };
|
|
539
743
|
};
|
|
540
|
-
const
|
|
541
|
-
|
|
744
|
+
const allLayout = buildPlacementAndHeights(rows);
|
|
745
|
+
const drawPlacedRow = (placedRow, rowTop2, rowHeight, rowIndex, rowHeights, isHeaderRow, drawTopBorder) => {
|
|
746
|
+
placedRow.forEach(({ cell: rawCell, startCol, colSpan, rowSpan }) => {
|
|
542
747
|
const cell = resolveTableCell(styles, rawCell);
|
|
543
|
-
const colW = colWidths
|
|
544
|
-
const x = baseLeft + colWidths.slice(0,
|
|
748
|
+
const colW = getPlacedCellWidth(colWidths, startCol, colSpan);
|
|
749
|
+
const x = baseLeft + sum(colWidths.slice(0, startCol));
|
|
545
750
|
const fontSize = cell.fontSize ?? 10;
|
|
751
|
+
const drawHeight = getRowSpanGroupHeight(rowHeights, rowIndex, rowSpan);
|
|
752
|
+
const paddingLeft = cell.paddingLeft ?? 2;
|
|
753
|
+
const paddingRight = cell.paddingRight ?? 2;
|
|
754
|
+
const paddingTop = cell.paddingTop ?? 2;
|
|
755
|
+
const paddingBottom = cell.paddingBottom ?? 2;
|
|
546
756
|
doc.save();
|
|
547
757
|
if (cell.fillColor) {
|
|
548
|
-
doc.rect(x, rowTop2, colW,
|
|
758
|
+
doc.rect(x, rowTop2, colW, drawHeight).fillOpacity(1).fill(cell.fillColor);
|
|
549
759
|
doc.fillOpacity(1);
|
|
550
760
|
}
|
|
551
|
-
|
|
552
|
-
const isItalic = !!cell.italic;
|
|
553
|
-
const fontName = cell.font ? cell.font : isBold && isItalic ? "Helvetica-BoldOblique" : isBold ? "Helvetica-Bold" : isItalic ? "Helvetica-Oblique" : "Helvetica";
|
|
554
|
-
doc.font(fontName);
|
|
761
|
+
doc.font(getCellFontName(cell));
|
|
555
762
|
doc.fontSize(fontSize);
|
|
556
|
-
|
|
557
|
-
doc.fillColor(cell.color);
|
|
558
|
-
} else {
|
|
559
|
-
doc.fillColor("black");
|
|
560
|
-
}
|
|
763
|
+
doc.fillColor(cell.color ?? "black");
|
|
561
764
|
const align = cell.align ?? "left";
|
|
562
|
-
const textWidth = Math.max(colW -
|
|
563
|
-
const textHeight = doc.heightOfString(cell.text, {
|
|
564
|
-
|
|
565
|
-
});
|
|
566
|
-
let textY = rowTop2 + 2;
|
|
765
|
+
const textWidth = Math.max(colW - paddingLeft - paddingRight, 1);
|
|
766
|
+
const textHeight = doc.heightOfString(cell.text, { width: textWidth });
|
|
767
|
+
let textY = rowTop2 + paddingTop;
|
|
567
768
|
if (isHeaderRow) {
|
|
568
|
-
const available =
|
|
769
|
+
const available = Math.max(drawHeight - paddingTop - paddingBottom, 0);
|
|
569
770
|
const offset = Math.max((available - textHeight) / 2, 0);
|
|
570
|
-
textY = rowTop2 +
|
|
771
|
+
textY = rowTop2 + paddingTop + offset;
|
|
571
772
|
}
|
|
572
|
-
doc.text(cell.text, x +
|
|
773
|
+
doc.text(cell.text, x + paddingLeft, textY, {
|
|
573
774
|
width: textWidth,
|
|
574
775
|
align,
|
|
575
776
|
underline: !!cell.underline,
|
|
@@ -577,52 +778,58 @@ var processTableBlock = (doc, ctx, styles, table, y, env, computeColumnPixelWidt
|
|
|
577
778
|
link: cell.link
|
|
578
779
|
});
|
|
579
780
|
doc.restore();
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
doc.strokeColor(layout.vLineColor ?? "#ccc");
|
|
594
|
-
let x = baseLeft;
|
|
595
|
-
colWidths.forEach((colW) => {
|
|
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");
|
|
596
794
|
doc.moveTo(x, rowTop2).lineTo(x, bottomY).stroke();
|
|
597
|
-
x
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
}
|
|
795
|
+
doc.moveTo(x + colW, rowTop2).lineTo(x + colW, bottomY).stroke();
|
|
796
|
+
doc.restore();
|
|
797
|
+
}
|
|
798
|
+
});
|
|
602
799
|
return rowTop2 + rowHeight;
|
|
603
800
|
};
|
|
801
|
+
const headerRowsSource = rows.slice(0, headerRows);
|
|
604
802
|
const drawHeaderRowsOnNewPage = () => {
|
|
605
803
|
if (!headerRows) return doc.page.margins.top + mt;
|
|
804
|
+
const headerLayout = buildPlacementAndHeights(headerRowsSource);
|
|
606
805
|
let rowTop2 = doc.page.margins.top + mt;
|
|
607
|
-
for (let i = 0; i <
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
806
|
+
for (let i = 0; i < headerLayout.placedRows.length; i++) {
|
|
807
|
+
rowTop2 = drawPlacedRow(
|
|
808
|
+
headerLayout.placedRows[i],
|
|
809
|
+
rowTop2,
|
|
810
|
+
headerLayout.rowHeights[i],
|
|
811
|
+
i,
|
|
812
|
+
headerLayout.rowHeights,
|
|
813
|
+
true,
|
|
814
|
+
true
|
|
815
|
+
);
|
|
611
816
|
}
|
|
612
817
|
return rowTop2;
|
|
613
818
|
};
|
|
614
819
|
let rowTop = startY;
|
|
615
|
-
|
|
820
|
+
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
|
|
616
821
|
const isHeaderRow = rowIndex < headerRows;
|
|
617
|
-
const
|
|
822
|
+
const placedRow = allLayout.placedRows[rowIndex];
|
|
823
|
+
const rowHeight = allLayout.rowHeights[rowIndex];
|
|
824
|
+
const maxRowSpan = Math.max(...placedRow.map((p) => p.rowSpan), 1);
|
|
825
|
+
const reservedHeight = getRowSpanGroupHeight(allLayout.rowHeights, rowIndex, maxRowSpan);
|
|
618
826
|
const bottomLimit = bottomLimitForContent();
|
|
619
|
-
if (rowTop +
|
|
827
|
+
if (rowTop + reservedHeight > bottomLimit) {
|
|
620
828
|
finishPage2(true);
|
|
621
829
|
rowTop = headerRows ? drawHeaderRowsOnNewPage() : doc.page.margins.top + mt;
|
|
622
830
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
});
|
|
831
|
+
rowTop = drawPlacedRow(placedRow, rowTop, rowHeight, rowIndex, allLayout.rowHeights, isHeaderRow, true);
|
|
832
|
+
}
|
|
626
833
|
const newY = rowTop + (table.lineGap ?? 6) + mb;
|
|
627
834
|
if (y === null) ctx.currentY = newY;
|
|
628
835
|
return newY;
|
|
@@ -1066,7 +1273,11 @@ function resolveTableCell(styles, cell) {
|
|
|
1066
1273
|
italic: cell.italic ?? styleDef.italic,
|
|
1067
1274
|
color: cell.color ?? styleDef.color,
|
|
1068
1275
|
fillColor: cell.fillColor ?? styleDef.fillColor,
|
|
1069
|
-
font: cell.font ?? styleDef.font
|
|
1276
|
+
font: cell.font ?? styleDef.font,
|
|
1277
|
+
paddingTop: cell.paddingTop,
|
|
1278
|
+
paddingRight: cell.paddingRight,
|
|
1279
|
+
paddingBottom: cell.paddingBottom,
|
|
1280
|
+
paddingLeft: cell.paddingLeft
|
|
1070
1281
|
};
|
|
1071
1282
|
}
|
|
1072
1283
|
var getFontNameForText = (tb) => {
|
|
@@ -1737,6 +1948,7 @@ async function renderCustomPdf(def, outputPath) {
|
|
|
1737
1948
|
const footerBandHeight = def.margins.bottom ?? 0;
|
|
1738
1949
|
const doc = new PDFDocument({
|
|
1739
1950
|
size: def.pageSize || "A4",
|
|
1951
|
+
layout: def.pageOrientation === "landscape" ? "landscape" : "portrait",
|
|
1740
1952
|
margins: {
|
|
1741
1953
|
top: headerBandHeight,
|
|
1742
1954
|
bottom: footerBandHeight,
|
|
@@ -1841,6 +2053,7 @@ async function renderCustomPdfToBuffer(def) {
|
|
|
1841
2053
|
const footerBandHeight = def.margins.bottom ?? 0;
|
|
1842
2054
|
const doc = new PDFDocument({
|
|
1843
2055
|
size: def.pageSize || "A4",
|
|
2056
|
+
layout: def.pageOrientation === "landscape" ? "landscape" : "portrait",
|
|
1844
2057
|
margins: {
|
|
1845
2058
|
top: headerBandHeight,
|
|
1846
2059
|
bottom: footerBandHeight,
|