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 +17 -4
- package/dist/index.d.ts +17 -4
- package/dist/index.js +247 -118
- package/dist/index.mjs +247 -118
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
row
|
|
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
|
|
502
|
-
const
|
|
503
|
-
const
|
|
504
|
-
const
|
|
505
|
-
const
|
|
506
|
-
|
|
507
|
-
doc.
|
|
508
|
-
|
|
509
|
-
|
|
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
|
-
|
|
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
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
const
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
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
|
|
552
|
-
|
|
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
|
|
555
|
-
const x = baseLeft + colWidths.slice(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,
|
|
717
|
+
doc.rect(x, rowTop2, colW, drawHeight).fillOpacity(1).fill(cell.fillColor);
|
|
560
718
|
doc.fillOpacity(1);
|
|
561
719
|
}
|
|
562
|
-
|
|
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
|
-
|
|
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 -
|
|
574
|
-
const textHeight = doc.heightOfString(cell.text, {
|
|
575
|
-
|
|
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 =
|
|
728
|
+
const available = Math.max(drawHeight - paddingTop - paddingBottom, 0);
|
|
580
729
|
const offset = Math.max((available - textHeight) / 2, 0);
|
|
581
|
-
textY = rowTop2 +
|
|
730
|
+
textY = rowTop2 + paddingTop + offset;
|
|
582
731
|
}
|
|
583
|
-
doc.text(cell.text, x +
|
|
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
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
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
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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 <
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
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.
|
|
779
|
+
for (let rowIndex = 0; rowIndex < table.body.length; rowIndex++) {
|
|
627
780
|
const isHeaderRow = rowIndex < headerRows;
|
|
628
|
-
const
|
|
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 +
|
|
786
|
+
if (rowTop + reservedHeight > bottomLimit) {
|
|
631
787
|
finishPage2(true);
|
|
632
788
|
rowTop = headerRows ? drawHeaderRowsOnNewPage() : doc.page.margins.top + mt;
|
|
633
789
|
}
|
|
634
|
-
|
|
635
|
-
|
|
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
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
row
|
|
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
|
|
459
|
-
const
|
|
460
|
-
const
|
|
461
|
-
const
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
doc.
|
|
465
|
-
|
|
466
|
-
|
|
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
|
-
|
|
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
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
const
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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
|
|
509
|
-
|
|
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
|
|
512
|
-
const x = baseLeft + colWidths.slice(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,
|
|
674
|
+
doc.rect(x, rowTop2, colW, drawHeight).fillOpacity(1).fill(cell.fillColor);
|
|
517
675
|
doc.fillOpacity(1);
|
|
518
676
|
}
|
|
519
|
-
|
|
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
|
-
|
|
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 -
|
|
531
|
-
const textHeight = doc.heightOfString(cell.text, {
|
|
532
|
-
|
|
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 =
|
|
685
|
+
const available = Math.max(drawHeight - paddingTop - paddingBottom, 0);
|
|
537
686
|
const offset = Math.max((available - textHeight) / 2, 0);
|
|
538
|
-
textY = rowTop2 +
|
|
687
|
+
textY = rowTop2 + paddingTop + offset;
|
|
539
688
|
}
|
|
540
|
-
doc.text(cell.text, x +
|
|
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
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
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
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
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 <
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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.
|
|
736
|
+
for (let rowIndex = 0; rowIndex < table.body.length; rowIndex++) {
|
|
584
737
|
const isHeaderRow = rowIndex < headerRows;
|
|
585
|
-
const
|
|
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 +
|
|
743
|
+
if (rowTop + reservedHeight > bottomLimit) {
|
|
588
744
|
finishPage2(true);
|
|
589
745
|
rowTop = headerRows ? drawHeaderRowsOnNewPage() : doc.page.margins.top + mt;
|
|
590
746
|
}
|
|
591
|
-
|
|
592
|
-
|
|
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,
|