pdfmake 0.3.0-beta.11 → 0.3.0-beta.13
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/CHANGELOG.md +18 -2
- package/build/pdfmake.js +26384 -25999
- package/build/pdfmake.js.map +1 -1
- package/build/pdfmake.min.js +2 -2
- package/build/pdfmake.min.js.map +1 -1
- package/build/vfs_fonts.js +2 -1
- package/js/DocMeasure.js +9 -0
- package/js/DocumentContext.js +25 -9
- package/js/ElementWriter.js +12 -3
- package/js/LayoutBuilder.js +239 -118
- package/js/PageElementWriter.js +15 -0
- package/js/Printer.js +13 -0
- package/js/Renderer.js +22 -14
- package/js/SVGMeasure.js +2 -2
- package/js/TextInlines.js +1 -1
- package/js/URLResolver.js +3 -0
- package/js/helpers/tools.js +6 -0
- package/package.json +14 -14
- package/src/DocMeasure.js +8 -0
- package/src/DocumentContext.js +26 -12
- package/src/ElementWriter.js +11 -2
- package/src/LayoutBuilder.js +250 -119
- package/src/PageElementWriter.js +19 -0
- package/src/Printer.js +15 -0
- package/src/Renderer.js +13 -15
- package/src/SVGMeasure.js +2 -2
- package/src/TextInlines.js +1 -1
- package/src/URLResolver.js +4 -0
- package/src/helpers/tools.js +5 -0
- package/standard-fonts/Helvetica.js +0 -1
- package/.idea/workspace.xml +0 -20
- package/eslint.config.mjs +0 -52
package/src/LayoutBuilder.js
CHANGED
|
@@ -172,14 +172,18 @@ class LayoutBuilder {
|
|
|
172
172
|
docStructure = this.docPreprocessor.preprocessDocument(docStructure);
|
|
173
173
|
docStructure = this.docMeasure.measureDocument(docStructure);
|
|
174
174
|
|
|
175
|
-
this.writer = new PageElementWriter(
|
|
176
|
-
new DocumentContext(this.pageSize, this.pageMargins));
|
|
175
|
+
this.writer = new PageElementWriter(new DocumentContext());
|
|
177
176
|
|
|
178
177
|
this.writer.context().addListener('pageAdded', () => {
|
|
179
178
|
this.addBackground(background);
|
|
180
179
|
});
|
|
181
180
|
|
|
182
|
-
this.
|
|
181
|
+
this.writer.addPage(
|
|
182
|
+
this.pageSize,
|
|
183
|
+
null,
|
|
184
|
+
this.pageMargins
|
|
185
|
+
);
|
|
186
|
+
|
|
183
187
|
this.processNode(docStructure);
|
|
184
188
|
this.addHeadersAndFooters(header, footer);
|
|
185
189
|
if (watermark != null) {
|
|
@@ -206,11 +210,6 @@ class LayoutBuilder {
|
|
|
206
210
|
}
|
|
207
211
|
}
|
|
208
212
|
|
|
209
|
-
addStaticRepeatable(headerOrFooter, sizeFunction) {
|
|
210
|
-
this.addDynamicRepeatable(() => // copy to new object
|
|
211
|
-
JSON.parse(JSON.stringify(headerOrFooter)), sizeFunction);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
213
|
addDynamicRepeatable(nodeGetter, sizeFunction) {
|
|
215
214
|
let pages = this.writer.context().pages;
|
|
216
215
|
|
|
@@ -220,7 +219,7 @@ class LayoutBuilder {
|
|
|
220
219
|
let node = nodeGetter(pageIndex + 1, l, this.writer.context().pages[pageIndex].pageSize);
|
|
221
220
|
|
|
222
221
|
if (node) {
|
|
223
|
-
let sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.pageMargins);
|
|
222
|
+
let sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.writer.context().getCurrentPage().pageMargins);
|
|
224
223
|
this.writer.beginUnbreakableBlock(sizes.width, sizes.height);
|
|
225
224
|
node = this.docPreprocessor.preprocessDocument(node);
|
|
226
225
|
this.processNode(this.docMeasure.measureDocument(node));
|
|
@@ -244,16 +243,12 @@ class LayoutBuilder {
|
|
|
244
243
|
height: pageMargins.bottom
|
|
245
244
|
});
|
|
246
245
|
|
|
247
|
-
if (
|
|
246
|
+
if (header) {
|
|
248
247
|
this.addDynamicRepeatable(header, headerSizeFct);
|
|
249
|
-
} else if (header) {
|
|
250
|
-
this.addStaticRepeatable(header, headerSizeFct);
|
|
251
248
|
}
|
|
252
249
|
|
|
253
|
-
if (
|
|
250
|
+
if (footer) {
|
|
254
251
|
this.addDynamicRepeatable(footer, footerSizeFct);
|
|
255
|
-
} else if (footer) {
|
|
256
|
-
this.addStaticRepeatable(footer, footerSizeFct);
|
|
257
252
|
}
|
|
258
253
|
}
|
|
259
254
|
|
|
@@ -266,36 +261,40 @@ class LayoutBuilder {
|
|
|
266
261
|
return;
|
|
267
262
|
}
|
|
268
263
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
watermark.opacity = isNumber(watermark.opacity) ? watermark.opacity : 0.6;
|
|
273
|
-
watermark.bold = watermark.bold || false;
|
|
274
|
-
watermark.italics = watermark.italics || false;
|
|
275
|
-
watermark.angle = isValue(watermark.angle) ? watermark.angle : null;
|
|
276
|
-
|
|
277
|
-
if (watermark.angle === null) {
|
|
278
|
-
watermark.angle = Math.atan2(this.pageSize.height, this.pageSize.width) * -180 / Math.PI;
|
|
264
|
+
let pages = this.writer.context().pages;
|
|
265
|
+
for (let i = 0, l = pages.length; i < l; i++) {
|
|
266
|
+
pages[i].watermark = getWatermarkObject({ ...watermark }, pages[i].pageSize, pdfDocument, defaultStyle);
|
|
279
267
|
}
|
|
280
268
|
|
|
281
|
-
|
|
282
|
-
watermark.
|
|
283
|
-
|
|
269
|
+
function getWatermarkObject(watermark, pageSize, pdfDocument, defaultStyle) {
|
|
270
|
+
watermark.font = watermark.font || defaultStyle.font || 'Roboto';
|
|
271
|
+
watermark.fontSize = watermark.fontSize || 'auto';
|
|
272
|
+
watermark.color = watermark.color || 'black';
|
|
273
|
+
watermark.opacity = isNumber(watermark.opacity) ? watermark.opacity : 0.6;
|
|
274
|
+
watermark.bold = watermark.bold || false;
|
|
275
|
+
watermark.italics = watermark.italics || false;
|
|
276
|
+
watermark.angle = isValue(watermark.angle) ? watermark.angle : null;
|
|
277
|
+
|
|
278
|
+
if (watermark.angle === null) {
|
|
279
|
+
watermark.angle = Math.atan2(pageSize.height, pageSize.width) * -180 / Math.PI;
|
|
280
|
+
}
|
|
284
281
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
fontSize: watermark.fontSize,
|
|
289
|
-
color: watermark.color,
|
|
290
|
-
opacity: watermark.opacity,
|
|
291
|
-
angle: watermark.angle
|
|
292
|
-
};
|
|
282
|
+
if (watermark.fontSize === 'auto') {
|
|
283
|
+
watermark.fontSize = getWatermarkFontSize(pageSize, watermark, pdfDocument);
|
|
284
|
+
}
|
|
293
285
|
|
|
294
|
-
|
|
286
|
+
let watermarkObject = {
|
|
287
|
+
text: watermark.text,
|
|
288
|
+
font: pdfDocument.provideFont(watermark.font, watermark.bold, watermark.italics),
|
|
289
|
+
fontSize: watermark.fontSize,
|
|
290
|
+
color: watermark.color,
|
|
291
|
+
opacity: watermark.opacity,
|
|
292
|
+
angle: watermark.angle
|
|
293
|
+
};
|
|
295
294
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
295
|
+
watermarkObject._size = getWatermarkSize(watermark, pdfDocument);
|
|
296
|
+
|
|
297
|
+
return watermarkObject;
|
|
299
298
|
}
|
|
300
299
|
|
|
301
300
|
function getWatermarkSize(watermark, pdfDocument) {
|
|
@@ -539,7 +538,15 @@ class LayoutBuilder {
|
|
|
539
538
|
}
|
|
540
539
|
}
|
|
541
540
|
|
|
542
|
-
|
|
541
|
+
/**
|
|
542
|
+
* Searches for a cell in the same row that starts a rowspan and is positioned immediately before the current cell.
|
|
543
|
+
* Alternatively, it finds a cell where the colspan initiating the rowspan extends to the cell just before the current one.
|
|
544
|
+
*
|
|
545
|
+
* @param {Array<object>} arr - An array representing cells in a row.
|
|
546
|
+
* @param {number} i - The index of the current cell to search backward from.
|
|
547
|
+
* @returns {object|null} The starting cell of the rowspan if found; otherwise, `null`.
|
|
548
|
+
*/
|
|
549
|
+
_findStartingRowSpanCell(arr, i) {
|
|
543
550
|
let requiredColspan = 1;
|
|
544
551
|
for (let index = i - 1; index >= 0; index--) {
|
|
545
552
|
if (!arr[index]._span) {
|
|
@@ -554,114 +561,239 @@ class LayoutBuilder {
|
|
|
554
561
|
return null;
|
|
555
562
|
}
|
|
556
563
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
564
|
+
/**
|
|
565
|
+
* Retrieves a page break description for a specified page from a list of page breaks.
|
|
566
|
+
*
|
|
567
|
+
* @param {Array<object>} pageBreaks - An array of page break descriptions, each containing `prevPage` properties.
|
|
568
|
+
* @param {number} page - The page number to find the associated page break for.
|
|
569
|
+
* @returns {object|undefined} The page break description object for the specified page if found; otherwise, `undefined`.
|
|
570
|
+
*/
|
|
571
|
+
_getPageBreak(pageBreaks, page) {
|
|
572
|
+
return pageBreaks.find(desc => desc.prevPage === page);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
_getPageBreakListBySpan(tableNode, page, rowIndex) {
|
|
576
|
+
if (!tableNode || !tableNode._breaksBySpan) {
|
|
577
|
+
return null;
|
|
578
|
+
}
|
|
579
|
+
const breaksList = tableNode._breaksBySpan.filter(desc => desc.prevPage === page && rowIndex <= desc.rowIndexOfSpanEnd);
|
|
580
|
+
|
|
581
|
+
let y = Number.MAX_VALUE,
|
|
582
|
+
prevY = Number.MIN_VALUE;
|
|
583
|
+
|
|
584
|
+
breaksList.forEach(b => {
|
|
585
|
+
prevY = Math.max(b.prevY, prevY);
|
|
586
|
+
y = Math.min(b.y, y);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
return {
|
|
590
|
+
prevPage: page,
|
|
591
|
+
prevY: prevY,
|
|
592
|
+
y: y
|
|
572
593
|
};
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
_findSameRowPageBreakByRowSpanData(breaksBySpan, page, rowIndex) {
|
|
597
|
+
if (!breaksBySpan) {
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
return breaksBySpan.find(desc => desc.prevPage === page && rowIndex === desc.rowIndexOfSpanEnd);
|
|
601
|
+
}
|
|
573
602
|
|
|
574
|
-
|
|
575
|
-
|
|
603
|
+
_updatePageBreaksData(pageBreaks, tableNode, rowIndex) {
|
|
604
|
+
Object.keys(tableNode._bottomByPage).forEach(p => {
|
|
605
|
+
const page = Number(p);
|
|
606
|
+
const pageBreak = this._getPageBreak(pageBreaks, page);
|
|
607
|
+
if (pageBreak) {
|
|
608
|
+
pageBreak.prevY = Math.max(pageBreak.prevY, tableNode._bottomByPage[page]);
|
|
609
|
+
}
|
|
610
|
+
if (tableNode._breaksBySpan && tableNode._breaksBySpan.length > 0) {
|
|
611
|
+
const breaksBySpanList = tableNode._breaksBySpan.filter(pb => pb.prevPage === page && rowIndex <= pb.rowIndexOfSpanEnd);
|
|
576
612
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
break;
|
|
613
|
+
if (breaksBySpanList && breaksBySpanList.length > 0) {
|
|
614
|
+
breaksBySpanList.forEach(b => {
|
|
615
|
+
b.prevY = Math.max(b.prevY, tableNode._bottomByPage[page]);
|
|
616
|
+
});
|
|
582
617
|
}
|
|
583
618
|
}
|
|
619
|
+
});
|
|
620
|
+
}
|
|
584
621
|
|
|
622
|
+
/**
|
|
623
|
+
* Resolves the Y-coordinates for a target object by comparing two break points.
|
|
624
|
+
*
|
|
625
|
+
* @param {object} break1 - The first break point with `prevY` and `y` properties.
|
|
626
|
+
* @param {object} break2 - The second break point with `prevY` and `y` properties.
|
|
627
|
+
* @param {object} target - The target object to be updated with resolved Y-coordinates.
|
|
628
|
+
* @property {number} target.prevY - Updated to the maximum `prevY` value between `break1` and `break2`.
|
|
629
|
+
* @property {number} target.y - Updated to the minimum `y` value between `break1` and `break2`.
|
|
630
|
+
*/
|
|
631
|
+
_resolveBreakY(break1, break2, target) {
|
|
632
|
+
target.prevY = Math.max(break1.prevY, break2.prevY);
|
|
633
|
+
target.y = Math.min(break1.y, break2.y);
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
_storePageBreakData(data, startsRowSpan, pageBreaks, tableNode) {
|
|
637
|
+
if (!startsRowSpan) {
|
|
638
|
+
let pageDesc = this._getPageBreak(pageBreaks, data.prevPage);
|
|
639
|
+
let pageDescBySpan = this._getPageBreakListBySpan(tableNode, data.prevPage, data.rowIndex);
|
|
585
640
|
if (!pageDesc) {
|
|
586
|
-
pageDesc =
|
|
641
|
+
pageDesc = {
|
|
642
|
+
...data
|
|
643
|
+
};
|
|
587
644
|
pageBreaks.push(pageDesc);
|
|
588
645
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
646
|
+
if (pageDescBySpan) {
|
|
647
|
+
this._resolveBreakY(pageDesc, pageDescBySpan, pageDesc);
|
|
648
|
+
}
|
|
649
|
+
this._resolveBreakY(pageDesc, data, pageDesc);
|
|
650
|
+
} else {
|
|
651
|
+
const breaksBySpan = tableNode && tableNode._breaksBySpan || null;
|
|
652
|
+
let pageDescBySpan = this._findSameRowPageBreakByRowSpanData(breaksBySpan, data.prevPage, data.rowIndex);
|
|
653
|
+
if (!pageDescBySpan) {
|
|
654
|
+
pageDescBySpan = {
|
|
655
|
+
...data,
|
|
656
|
+
rowIndexOfSpanEnd: data.rowIndex + data.rowSpan - 1
|
|
657
|
+
};
|
|
658
|
+
if (!tableNode._breaksBySpan) {
|
|
659
|
+
tableNode._breaksBySpan = [];
|
|
660
|
+
}
|
|
661
|
+
tableNode._breaksBySpan.push(pageDescBySpan);
|
|
662
|
+
}
|
|
663
|
+
pageDescBySpan.prevY = Math.max(pageDescBySpan.prevY, data.prevY);
|
|
664
|
+
pageDescBySpan.y = Math.min(pageDescBySpan.y, data.y);
|
|
665
|
+
let pageDesc = this._getPageBreak(pageBreaks, data.prevPage);
|
|
666
|
+
if (pageDesc) {
|
|
667
|
+
this._resolveBreakY(pageDesc, pageDescBySpan, pageDesc);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Calculates the left offset for a column based on the specified gap values.
|
|
674
|
+
*
|
|
675
|
+
* @param {number} i - The index of the column for which the offset is being calculated.
|
|
676
|
+
* @param {Array<number>} gaps - An array of gap values for each column.
|
|
677
|
+
* @returns {number} The left offset for the column. Returns `gaps[i]` if it exists, otherwise `0`.
|
|
678
|
+
*/
|
|
679
|
+
_colLeftOffset(i, gaps) {
|
|
680
|
+
if (gaps && gaps.length > i) {
|
|
681
|
+
return gaps[i];
|
|
682
|
+
}
|
|
683
|
+
return 0;
|
|
684
|
+
}
|
|
592
685
|
|
|
686
|
+
/**
|
|
687
|
+
* Retrieves the ending cell for a row span in case it exists in a specified table column.
|
|
688
|
+
*
|
|
689
|
+
* @param {Array<Array<object>>} tableBody - The table body, represented as a 2D array of cell objects.
|
|
690
|
+
* @param {number} rowIndex - The index of the starting row for the row span.
|
|
691
|
+
* @param {object} column - The column object containing row span information.
|
|
692
|
+
* @param {number} columnIndex - The index of the column within the row.
|
|
693
|
+
* @returns {object|null} The cell at the end of the row span if it exists; otherwise, `null`.
|
|
694
|
+
* @throws {Error} If the row span extends beyond the total row count.
|
|
695
|
+
*/
|
|
696
|
+
_getRowSpanEndingCell(tableBody, rowIndex, column, columnIndex) {
|
|
697
|
+
if (column.rowSpan && column.rowSpan > 1) {
|
|
698
|
+
let endingRow = rowIndex + column.rowSpan - 1;
|
|
699
|
+
if (endingRow >= tableBody.length) {
|
|
700
|
+
throw new Error(`Row span for column ${columnIndex} (with indexes starting from 0) exceeded row count`);
|
|
701
|
+
}
|
|
702
|
+
return tableBody[endingRow][columnIndex];
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
processRow({ marginX = [0, 0], dontBreakRows = false, rowsWithoutPageBreak = 0, cells, widths, gaps, tableNode, tableBody, rowIndex, height }) {
|
|
593
709
|
const isUnbreakableRow = dontBreakRows || rowIndex <= rowsWithoutPageBreak - 1;
|
|
594
710
|
let pageBreaks = [];
|
|
711
|
+
let pageBreaksByRowSpan = [];
|
|
595
712
|
let positions = [];
|
|
596
713
|
let willBreakByHeight = false;
|
|
597
|
-
|
|
714
|
+
widths = widths || cells;
|
|
598
715
|
|
|
599
716
|
// Check if row should break by height
|
|
600
717
|
if (!isUnbreakableRow && height > this.writer.context().availableHeight) {
|
|
601
718
|
willBreakByHeight = true;
|
|
602
719
|
}
|
|
603
720
|
|
|
604
|
-
widths = widths || cells;
|
|
605
721
|
// Use the marginX if we are in a top level table/column (not nested)
|
|
606
722
|
const marginXParent = this.nestedLevel === 1 ? marginX : null;
|
|
607
|
-
|
|
608
|
-
this.writer.context().beginColumnGroup(marginXParent);
|
|
723
|
+
const _bottomByPage = tableNode ? tableNode._bottomByPage : null;
|
|
724
|
+
this.writer.context().beginColumnGroup(marginXParent, _bottomByPage);
|
|
609
725
|
|
|
610
726
|
for (let i = 0, l = cells.length; i < l; i++) {
|
|
611
|
-
let
|
|
727
|
+
let cell = cells[i];
|
|
728
|
+
|
|
729
|
+
// Page change handler
|
|
730
|
+
const storePageBreakClosure = data => {
|
|
731
|
+
const startsRowSpan = cell.rowSpan && cell.rowSpan > 1;
|
|
732
|
+
if (startsRowSpan) {
|
|
733
|
+
data.rowSpan = cell.rowSpan;
|
|
734
|
+
}
|
|
735
|
+
data.rowIndex = rowIndex;
|
|
736
|
+
this._storePageBreakData(data, startsRowSpan, pageBreaks, tableNode);
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
this.writer.addListener('pageChanged', storePageBreakClosure);
|
|
740
|
+
|
|
612
741
|
let width = widths[i]._calcWidth;
|
|
613
|
-
let leftOffset =
|
|
742
|
+
let leftOffset = this._colLeftOffset(i, gaps);
|
|
743
|
+
// Check if exists and retrieve the cell that started the rowspan in case we are in the cell just after
|
|
744
|
+
let startingSpanCell = this._findStartingRowSpanCell(cells, i);
|
|
614
745
|
|
|
615
|
-
if (
|
|
616
|
-
for (let j = 1; j <
|
|
746
|
+
if (cell.colSpan && cell.colSpan > 1) {
|
|
747
|
+
for (let j = 1; j < cell.colSpan; j++) {
|
|
617
748
|
width += widths[++i]._calcWidth + gaps[i];
|
|
618
749
|
}
|
|
619
750
|
}
|
|
620
751
|
|
|
621
752
|
// if rowspan starts in this cell, we retrieve the last cell affected by the rowspan
|
|
622
|
-
const
|
|
623
|
-
if (
|
|
753
|
+
const rowSpanEndingCell = this._getRowSpanEndingCell(tableBody, rowIndex, cell, i);
|
|
754
|
+
if (rowSpanEndingCell) {
|
|
624
755
|
// We store a reference of the ending cell in the first cell of the rowspan
|
|
625
|
-
|
|
626
|
-
|
|
756
|
+
cell._endingCell = rowSpanEndingCell;
|
|
757
|
+
cell._endingCell._startingRowSpanY = cell._startingRowSpanY;
|
|
627
758
|
}
|
|
628
759
|
|
|
629
|
-
//
|
|
630
|
-
let
|
|
631
|
-
let endingSpanCell = null;
|
|
760
|
+
// If we are after a cell that started a rowspan
|
|
761
|
+
let endOfRowSpanCell = null;
|
|
632
762
|
if (startingSpanCell && startingSpanCell._endingCell) {
|
|
633
763
|
// Reference to the last cell of the rowspan
|
|
634
|
-
|
|
764
|
+
endOfRowSpanCell = startingSpanCell._endingCell;
|
|
635
765
|
// Store if we are in an unbreakable block when we save the context and the originalX
|
|
636
766
|
if (this.writer.transactionLevel > 0) {
|
|
637
|
-
|
|
638
|
-
|
|
767
|
+
endOfRowSpanCell._isUnbreakableContext = true;
|
|
768
|
+
endOfRowSpanCell._originalXOffset = this.writer.originalX;
|
|
639
769
|
}
|
|
640
770
|
}
|
|
641
771
|
|
|
642
772
|
// We pass the endingSpanCell reference to store the context just after processing rowspan cell
|
|
643
|
-
this.writer.context().beginColumn(width, leftOffset,
|
|
773
|
+
this.writer.context().beginColumn(width, leftOffset, endOfRowSpanCell);
|
|
644
774
|
|
|
645
|
-
if (!
|
|
646
|
-
this.processNode(
|
|
647
|
-
|
|
648
|
-
|
|
775
|
+
if (!cell._span) {
|
|
776
|
+
this.processNode(cell);
|
|
777
|
+
this.writer.context().updateBottomByPage();
|
|
778
|
+
addAll(positions, cell.positions);
|
|
779
|
+
} else if (cell._columnEndingContext) {
|
|
649
780
|
let discountY = 0;
|
|
650
781
|
if (dontBreakRows) {
|
|
651
782
|
// Calculate how many points we have to discount to Y when dontBreakRows and rowSpan are combined
|
|
652
783
|
const ctxBeforeRowSpanLastRow = this.writer.contextStack[this.writer.contextStack.length - 1];
|
|
653
|
-
discountY = ctxBeforeRowSpanLastRow.y -
|
|
784
|
+
discountY = ctxBeforeRowSpanLastRow.y - cell._startingRowSpanY;
|
|
654
785
|
}
|
|
655
786
|
let originalXOffset = 0;
|
|
656
787
|
// If context was saved from an unbreakable block and we are not in an unbreakable block anymore
|
|
657
788
|
// We have to sum the originalX (X before starting unbreakable block) to X
|
|
658
|
-
if (
|
|
659
|
-
originalXOffset =
|
|
789
|
+
if (cell._isUnbreakableContext && !this.writer.transactionLevel) {
|
|
790
|
+
originalXOffset = cell._originalXOffset;
|
|
660
791
|
}
|
|
661
792
|
// row-span ending
|
|
662
793
|
// Recover the context after processing the rowspanned cell
|
|
663
|
-
this.writer.context().markEnding(
|
|
794
|
+
this.writer.context().markEnding(cell, originalXOffset, discountY);
|
|
664
795
|
}
|
|
796
|
+
this.writer.removeListener('pageChanged', storePageBreakClosure);
|
|
665
797
|
}
|
|
666
798
|
|
|
667
799
|
// Check if last cell is part of a span
|
|
@@ -674,7 +806,7 @@ class LayoutBuilder {
|
|
|
674
806
|
// Previous column cell is part of a span
|
|
675
807
|
} else if (lastColumn._span === true) {
|
|
676
808
|
// We get the cell that started the span where we set a reference to the ending cell
|
|
677
|
-
const startingSpanCell = this.
|
|
809
|
+
const startingSpanCell = this._findStartingRowSpanCell(cells, cells.length);
|
|
678
810
|
if (startingSpanCell) {
|
|
679
811
|
// Context will be stored here (ending cell)
|
|
680
812
|
endingSpanCell = startingSpanCell._endingCell;
|
|
@@ -687,38 +819,25 @@ class LayoutBuilder {
|
|
|
687
819
|
}
|
|
688
820
|
}
|
|
689
821
|
|
|
690
|
-
// If there are page breaks in this row, update data with prevY of last cell
|
|
691
|
-
updatePageBreakData(this.writer.context().page, this.writer.context().y);
|
|
692
|
-
|
|
693
822
|
// If content did not break page, check if we should break by height
|
|
694
|
-
if (!isUnbreakableRow && pageBreaks.length === 0
|
|
823
|
+
if (willBreakByHeight && !isUnbreakableRow && pageBreaks.length === 0) {
|
|
695
824
|
this.writer.context().moveDown(this.writer.context().availableHeight);
|
|
696
825
|
this.writer.moveToNextPage();
|
|
697
826
|
}
|
|
698
827
|
|
|
699
|
-
this.writer.context().completeColumnGroup(height, endingSpanCell);
|
|
700
|
-
this.writer.removeListener('pageChanged', storePageBreakData);
|
|
828
|
+
const bottomByPage = this.writer.context().completeColumnGroup(height, endingSpanCell);
|
|
701
829
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
return gaps[i];
|
|
707
|
-
}
|
|
708
|
-
return 0;
|
|
830
|
+
if (tableNode) {
|
|
831
|
+
tableNode._bottomByPage = bottomByPage;
|
|
832
|
+
// If there are page breaks in this row, update data with prevY of last cell
|
|
833
|
+
this._updatePageBreaksData(pageBreaks, tableNode, rowIndex);
|
|
709
834
|
}
|
|
710
835
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
}
|
|
717
|
-
return tableBody[endingRow][columnIndex];
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
return null;
|
|
721
|
-
}
|
|
836
|
+
return {
|
|
837
|
+
pageBreaksBySpan: pageBreaksByRowSpan,
|
|
838
|
+
pageBreaks: pageBreaks,
|
|
839
|
+
positions: positions
|
|
840
|
+
};
|
|
722
841
|
}
|
|
723
842
|
|
|
724
843
|
// lists
|
|
@@ -799,6 +918,8 @@ class LayoutBuilder {
|
|
|
799
918
|
height = undefined;
|
|
800
919
|
}
|
|
801
920
|
|
|
921
|
+
const pageBeforeProcessing = this.writer.context().page;
|
|
922
|
+
|
|
802
923
|
let result = this.processRow({
|
|
803
924
|
marginX: tableNode._margin ? [tableNode._margin[0], tableNode._margin[2]] : [0, 0],
|
|
804
925
|
dontBreakRows: processor.dontBreakRows,
|
|
@@ -807,12 +928,22 @@ class LayoutBuilder {
|
|
|
807
928
|
widths: tableNode.table.widths,
|
|
808
929
|
gaps: tableNode._offsets.offsets,
|
|
809
930
|
tableBody: tableNode.table.body,
|
|
931
|
+
tableNode,
|
|
810
932
|
rowIndex: i,
|
|
811
933
|
height
|
|
812
934
|
});
|
|
813
935
|
|
|
814
936
|
addAll(tableNode.positions, result.positions);
|
|
815
937
|
|
|
938
|
+
if (!result.pageBreaks || result.pageBreaks.length === 0) {
|
|
939
|
+
const breaksBySpan = tableNode && tableNode._breaksBySpan || null;
|
|
940
|
+
const breakBySpanData = this._findSameRowPageBreakByRowSpanData(breaksBySpan, pageBeforeProcessing, i);
|
|
941
|
+
if (breakBySpanData) {
|
|
942
|
+
const finalBreakBySpanData = this._getPageBreakListBySpan(tableNode, breakBySpanData.prevPage, i);
|
|
943
|
+
result.pageBreaks.push(finalBreakBySpanData);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
816
947
|
processor.endRow(i, this.writer, result.pageBreaks);
|
|
817
948
|
}
|
|
818
949
|
|
package/src/PageElementWriter.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import ElementWriter from './ElementWriter';
|
|
2
|
+
import { normalizePageSize, normalizePageMargin } from './PageSize';
|
|
3
|
+
import DocumentContext from './DocumentContext';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* An extended ElementWriter which can handle:
|
|
@@ -9,6 +11,10 @@ import ElementWriter from './ElementWriter';
|
|
|
9
11
|
* whole block will be rendered on the same page)
|
|
10
12
|
*/
|
|
11
13
|
class PageElementWriter extends ElementWriter {
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {DocumentContext} context
|
|
17
|
+
*/
|
|
12
18
|
constructor(context) {
|
|
13
19
|
super(context);
|
|
14
20
|
this.transactionLevel = 0;
|
|
@@ -77,6 +83,19 @@ class PageElementWriter extends ElementWriter {
|
|
|
77
83
|
});
|
|
78
84
|
}
|
|
79
85
|
|
|
86
|
+
addPage(pageSize, pageOrientation, pageMargin) {
|
|
87
|
+
let prevPage = this.page;
|
|
88
|
+
let prevY = this.y;
|
|
89
|
+
|
|
90
|
+
this.context().addPage(normalizePageSize(pageSize, pageOrientation), normalizePageMargin(pageMargin));
|
|
91
|
+
|
|
92
|
+
this.emit('pageChanged', {
|
|
93
|
+
prevPage: prevPage,
|
|
94
|
+
prevY: prevY,
|
|
95
|
+
y: this.context().y
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
80
99
|
beginUnbreakableBlock(width, height) {
|
|
81
100
|
if (this.transactionLevel++ === 0) {
|
|
82
101
|
this.originalX = this.context().x;
|
package/src/Printer.js
CHANGED
|
@@ -5,6 +5,7 @@ import { normalizePageSize, normalizePageMargin } from './PageSize';
|
|
|
5
5
|
import { tableLayouts } from './tableLayouts';
|
|
6
6
|
import Renderer from './Renderer';
|
|
7
7
|
import { isNumber, isValue } from './helpers/variableType';
|
|
8
|
+
import { convertToDynamicContent } from './helpers/tools';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Printer which turns document definition into a pdf
|
|
@@ -47,17 +48,31 @@ class PdfPrinter {
|
|
|
47
48
|
this.resolveUrls(docDefinition).then(() => {
|
|
48
49
|
try {
|
|
49
50
|
docDefinition.version = docDefinition.version || '1.3';
|
|
51
|
+
docDefinition.subset = docDefinition.subset || undefined;
|
|
52
|
+
docDefinition.tagged = typeof docDefinition.tagged === 'boolean' ? docDefinition.tagged : false;
|
|
53
|
+
docDefinition.displayTitle = typeof docDefinition.displayTitle === 'boolean' ? docDefinition.displayTitle : false;
|
|
50
54
|
docDefinition.compress = typeof docDefinition.compress === 'boolean' ? docDefinition.compress : true;
|
|
51
55
|
docDefinition.images = docDefinition.images || {};
|
|
52
56
|
docDefinition.attachments = docDefinition.attachments || {};
|
|
53
57
|
docDefinition.pageMargins = isValue(docDefinition.pageMargins) ? docDefinition.pageMargins : 40;
|
|
54
58
|
docDefinition.patterns = docDefinition.patterns || {};
|
|
55
59
|
|
|
60
|
+
if (docDefinition.header && typeof docDefinition.header !== 'function') {
|
|
61
|
+
docDefinition.header = convertToDynamicContent(docDefinition.header);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (docDefinition.footer && typeof docDefinition.footer !== 'function') {
|
|
65
|
+
docDefinition.footer = convertToDynamicContent(docDefinition.footer);
|
|
66
|
+
}
|
|
67
|
+
|
|
56
68
|
let pageSize = normalizePageSize(docDefinition.pageSize, docDefinition.pageOrientation);
|
|
57
69
|
|
|
58
70
|
let pdfOptions = {
|
|
59
71
|
size: [pageSize.width, pageSize.height],
|
|
60
72
|
pdfVersion: docDefinition.version,
|
|
73
|
+
subset: docDefinition.subset,
|
|
74
|
+
tagged: docDefinition.tagged,
|
|
75
|
+
displayTitle: docDefinition.displayTitle,
|
|
61
76
|
compress: docDefinition.compress,
|
|
62
77
|
userPassword: docDefinition.userPassword,
|
|
63
78
|
ownerPassword: docDefinition.ownerPassword,
|
package/src/Renderer.js
CHANGED
|
@@ -27,7 +27,7 @@ const findFont = (fonts, requiredFonts, defaultFont) => {
|
|
|
27
27
|
* @returns {number}
|
|
28
28
|
*/
|
|
29
29
|
const offsetText = (y, inline) => {
|
|
30
|
-
|
|
30
|
+
let newY = y;
|
|
31
31
|
if (inline.sup) {
|
|
32
32
|
newY -= inline.fontSize * 0.75;
|
|
33
33
|
}
|
|
@@ -45,7 +45,6 @@ class Renderer {
|
|
|
45
45
|
|
|
46
46
|
renderPages(pages) {
|
|
47
47
|
this.pdfDocument._pdfMakePages = pages; // TODO: Why?
|
|
48
|
-
this.pdfDocument.addPage();
|
|
49
48
|
|
|
50
49
|
let totalItems = 0;
|
|
51
50
|
if (this.progressCallback) {
|
|
@@ -57,10 +56,7 @@ class Renderer {
|
|
|
57
56
|
let renderedItems = 0;
|
|
58
57
|
|
|
59
58
|
for (let i = 0; i < pages.length; i++) {
|
|
60
|
-
|
|
61
|
-
this._updatePageOrientationInOptions(pages[i]);
|
|
62
|
-
this.pdfDocument.addPage(this.pdfDocument.options);
|
|
63
|
-
}
|
|
59
|
+
this.pdfDocument.addPage({ size: [pages[i].pageSize.width, pages[i].pageSize.height] });
|
|
64
60
|
|
|
65
61
|
let page = pages[i];
|
|
66
62
|
for (let ii = 0, il = page.items.length; ii < il; ii++) {
|
|
@@ -348,6 +344,17 @@ class Renderer {
|
|
|
348
344
|
};
|
|
349
345
|
|
|
350
346
|
SVGtoPDF(this.pdfDocument, svg.svg, svg.x, svg.y, options);
|
|
347
|
+
|
|
348
|
+
if (svg.link) {
|
|
349
|
+
this.pdfDocument.link(svg.x, svg.y, svg._width, svg._height, svg.link);
|
|
350
|
+
}
|
|
351
|
+
if (svg.linkToPage) {
|
|
352
|
+
this.pdfDocument.ref({ Type: 'Action', S: 'GoTo', D: [svg.linkToPage, 0, 0] }).end();
|
|
353
|
+
this.pdfDocument.annotate(svg.x, svg.y, svg._width, svg._height, { Subtype: 'Link', Dest: [svg.linkToPage - 1, 'XYZ', null, null, null] });
|
|
354
|
+
}
|
|
355
|
+
if (svg.linkToDestination) {
|
|
356
|
+
this.pdfDocument.goTo(svg.x, svg.y, svg._width, svg._height, svg.linkToDestination);
|
|
357
|
+
}
|
|
351
358
|
}
|
|
352
359
|
|
|
353
360
|
renderAttachment(attachment) {
|
|
@@ -391,15 +398,6 @@ class Renderer {
|
|
|
391
398
|
this.pdfDocument.restore();
|
|
392
399
|
}
|
|
393
400
|
|
|
394
|
-
_updatePageOrientationInOptions(currentPage) {
|
|
395
|
-
let previousPageOrientation = this.pdfDocument.options.size[0] > this.pdfDocument.options.size[1] ? 'landscape' : 'portrait';
|
|
396
|
-
|
|
397
|
-
if (currentPage.pageSize.orientation !== previousPageOrientation) {
|
|
398
|
-
let width = this.pdfDocument.options.size[0];
|
|
399
|
-
let height = this.pdfDocument.options.size[1];
|
|
400
|
-
this.pdfDocument.options.size = [height, width];
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
401
|
}
|
|
404
402
|
|
|
405
403
|
export default Renderer;
|