pdfmake 0.3.0-beta.10 → 0.3.0-beta.12
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/.idea/inspectionProfiles/Project_Default.xml +6 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/pdfmake.iml +11 -0
- package/.idea/vcs.xml +6 -0
- package/CHANGELOG.md +13 -0
- package/build/pdfmake.js +44815 -44689
- 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 +6 -5
- package/eslint.config.mjs +52 -52
- package/js/DocumentContext.js +12 -1
- package/js/ElementWriter.js +4 -1
- package/js/LayoutBuilder.js +206 -81
- package/js/PageElementWriter.js +2 -2
- package/js/Printer.js +6 -0
- package/js/Renderer.js +17 -0
- package/js/TableProcessor.js +37 -13
- package/package.json +14 -14
- package/src/3rd-party/svg-to-pdfkit/LICENSE +9 -9
- package/src/DocumentContext.js +13 -1
- package/src/ElementWriter.js +4 -1
- package/src/LayoutBuilder.js +211 -79
- package/src/PageElementWriter.js +2 -2
- package/src/Printer.js +6 -0
- package/src/Renderer.js +11 -0
- package/src/TableProcessor.js +37 -15
package/js/TableProcessor.js
CHANGED
|
@@ -113,13 +113,18 @@ class TableProcessor {
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
this.dontBreakRows = tableNode.table.dontBreakRows || false;
|
|
116
|
-
if (this.rowsWithoutPageBreak) {
|
|
116
|
+
if (this.rowsWithoutPageBreak || this.dontBreakRows) {
|
|
117
117
|
writer.beginUnbreakableBlock();
|
|
118
|
+
// Draw the top border of the table
|
|
119
|
+
this.drawHorizontalLine(0, writer);
|
|
120
|
+
if (this.rowsWithoutPageBreak && this.dontBreakRows) {
|
|
121
|
+
// We just increase the value of transactionLevel
|
|
122
|
+
writer.beginUnbreakableBlock();
|
|
123
|
+
}
|
|
118
124
|
}
|
|
119
125
|
|
|
120
126
|
// update the border properties of all cells before drawing any lines
|
|
121
127
|
prepareCellBorders(this.tableNode.table.body);
|
|
122
|
-
this.drawHorizontalLine(0, writer);
|
|
123
128
|
}
|
|
124
129
|
onRowBreak(rowIndex, writer) {
|
|
125
130
|
return () => {
|
|
@@ -135,7 +140,12 @@ class TableProcessor {
|
|
|
135
140
|
this.rowPaddingBottom = this.layout.paddingBottom(rowIndex, this.tableNode);
|
|
136
141
|
this.rowCallback = this.onRowBreak(rowIndex, writer);
|
|
137
142
|
writer.addListener('pageChanged', this.rowCallback);
|
|
138
|
-
if (this.dontBreakRows) {
|
|
143
|
+
if (rowIndex == 0 && !this.dontBreakRows && !this.rowsWithoutPageBreak) {
|
|
144
|
+
// We store the 'y' to draw later and if necessary the top border of the table
|
|
145
|
+
this._tableTopBorderY = writer.context().y;
|
|
146
|
+
writer.context().moveDown(this.topLineWidth);
|
|
147
|
+
}
|
|
148
|
+
if (this.dontBreakRows && rowIndex > 0) {
|
|
139
149
|
writer.beginUnbreakableBlock();
|
|
140
150
|
}
|
|
141
151
|
this.rowTopY = writer.context().y;
|
|
@@ -143,7 +153,7 @@ class TableProcessor {
|
|
|
143
153
|
writer.context().availableHeight -= this.reservedAtBottom;
|
|
144
154
|
writer.context().moveDown(this.rowPaddingTop);
|
|
145
155
|
}
|
|
146
|
-
drawHorizontalLine(lineIndex, writer, overrideY) {
|
|
156
|
+
drawHorizontalLine(lineIndex, writer, overrideY, moveDown = true, forcePage) {
|
|
147
157
|
let lineWidth = this.layout.hLineWidth(lineIndex, this.tableNode);
|
|
148
158
|
if (lineWidth) {
|
|
149
159
|
let style = this.layout.hLineStyle(lineIndex, this.tableNode);
|
|
@@ -237,7 +247,7 @@ class TableProcessor {
|
|
|
237
247
|
lineWidth: lineWidth,
|
|
238
248
|
dash: dash,
|
|
239
249
|
lineColor: borderColor
|
|
240
|
-
}, false, overrideY);
|
|
250
|
+
}, false, (0, _variableType.isNumber)(overrideY), null, forcePage);
|
|
241
251
|
currentLine = null;
|
|
242
252
|
borderColor = null;
|
|
243
253
|
cellAbove = null;
|
|
@@ -246,7 +256,9 @@ class TableProcessor {
|
|
|
246
256
|
}
|
|
247
257
|
}
|
|
248
258
|
}
|
|
249
|
-
|
|
259
|
+
if (moveDown) {
|
|
260
|
+
writer.context().moveDown(lineWidth);
|
|
261
|
+
}
|
|
250
262
|
}
|
|
251
263
|
}
|
|
252
264
|
drawVerticalLine(x, y0, y1, vLineColIndex, writer, vLineRowIndex, beforeVLineColIndex) {
|
|
@@ -369,6 +381,15 @@ class TableProcessor {
|
|
|
369
381
|
}
|
|
370
382
|
ys[ys.length - 1].y1 = endingY;
|
|
371
383
|
let skipOrphanePadding = ys[0].y1 - ys[0].y0 === this.rowPaddingTop;
|
|
384
|
+
if (rowIndex === 0 && !skipOrphanePadding && !this.rowsWithoutPageBreak && !this.dontBreakRows) {
|
|
385
|
+
// Draw the top border of the table
|
|
386
|
+
let pageTableStartedAt = null;
|
|
387
|
+
if (pageBreaks && pageBreaks.length > 0) {
|
|
388
|
+
// Get the page where table started at
|
|
389
|
+
pageTableStartedAt = pageBreaks[0].prevPage;
|
|
390
|
+
}
|
|
391
|
+
this.drawHorizontalLine(0, writer, this._tableTopBorderY, false, pageTableStartedAt);
|
|
392
|
+
}
|
|
372
393
|
for (let yi = skipOrphanePadding ? 1 : 0, yl = ys.length; yi < yl; yi++) {
|
|
373
394
|
let willBreak = yi < ys.length - 1;
|
|
374
395
|
let rowBreakWithoutHeader = yi > 0 && !this.headerRows;
|
|
@@ -385,6 +406,14 @@ class TableProcessor {
|
|
|
385
406
|
// TableProcessor should be pageChanged listener, instead of processRow
|
|
386
407
|
this.reservedAtBottom = 0;
|
|
387
408
|
}
|
|
409
|
+
|
|
410
|
+
// Draw horizontal lines before the vertical lines so they are not overridden
|
|
411
|
+
if (willBreak && this.layout.hLineWhenBroken !== false) {
|
|
412
|
+
this.drawHorizontalLine(rowIndex + 1, writer, y2);
|
|
413
|
+
}
|
|
414
|
+
if (rowBreakWithoutHeader && this.layout.hLineWhenBroken !== false) {
|
|
415
|
+
this.drawHorizontalLine(rowIndex, writer, y1);
|
|
416
|
+
}
|
|
388
417
|
for (let i = 0, l = xs.length; i < l; i++) {
|
|
389
418
|
let leftCellBorder = false;
|
|
390
419
|
let rightCellBorder = false;
|
|
@@ -467,12 +496,6 @@ class TableProcessor {
|
|
|
467
496
|
}
|
|
468
497
|
}
|
|
469
498
|
}
|
|
470
|
-
if (willBreak && this.layout.hLineWhenBroken !== false) {
|
|
471
|
-
this.drawHorizontalLine(rowIndex + 1, writer, y2);
|
|
472
|
-
}
|
|
473
|
-
if (rowBreakWithoutHeader && this.layout.hLineWhenBroken !== false) {
|
|
474
|
-
this.drawHorizontalLine(rowIndex, writer, y1);
|
|
475
|
-
}
|
|
476
499
|
}
|
|
477
500
|
writer.context().page = endingPage;
|
|
478
501
|
writer.context().y = endingY;
|
|
@@ -505,7 +528,8 @@ class TableProcessor {
|
|
|
505
528
|
}
|
|
506
529
|
if (this.dontBreakRows) {
|
|
507
530
|
const pageChangedCallback = () => {
|
|
508
|
-
if (!this.headerRows && this.layout.hLineWhenBroken !== false) {
|
|
531
|
+
if (rowIndex > 0 && !this.headerRows && this.layout.hLineWhenBroken !== false) {
|
|
532
|
+
// Draw the top border of the row after a page break
|
|
509
533
|
this.drawHorizontalLine(rowIndex, writer);
|
|
510
534
|
}
|
|
511
535
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pdfmake",
|
|
3
|
-
"version": "0.3.0-beta.
|
|
3
|
+
"version": "0.3.0-beta.12",
|
|
4
4
|
"description": "Client/server side PDF printing in pure JavaScript",
|
|
5
5
|
"main": "js/index.js",
|
|
6
6
|
"esnext": "src/index.js",
|
|
@@ -10,33 +10,33 @@
|
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@foliojs-fork/linebreak": "^1.1.2",
|
|
13
|
-
"@foliojs-fork/pdfkit": "^0.
|
|
13
|
+
"@foliojs-fork/pdfkit": "^0.15.1",
|
|
14
14
|
"iconv-lite": "^0.6.3",
|
|
15
15
|
"xmldoc": "^1.3.0"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@babel/cli": "^7.25.
|
|
19
|
-
"@babel/core": "^7.
|
|
20
|
-
"@babel/plugin-transform-modules-commonjs": "^7.
|
|
21
|
-
"@babel/preset-env": "^7.
|
|
22
|
-
"@eslint/js": "^9.
|
|
18
|
+
"@babel/cli": "^7.25.9",
|
|
19
|
+
"@babel/core": "^7.26.0",
|
|
20
|
+
"@babel/plugin-transform-modules-commonjs": "^7.25.9",
|
|
21
|
+
"@babel/preset-env": "^7.26.0",
|
|
22
|
+
"@eslint/js": "^9.14.0",
|
|
23
23
|
"assert": "^2.1.0",
|
|
24
|
-
"babel-loader": "^9.1
|
|
24
|
+
"babel-loader": "^9.2.1",
|
|
25
25
|
"brfs": "^2.0.2",
|
|
26
26
|
"browserify-zlib": "^0.2.0",
|
|
27
27
|
"buffer": "6.0.3",
|
|
28
28
|
"core-js": "3.19.0",
|
|
29
|
-
"eslint": "^9.
|
|
30
|
-
"eslint-plugin-jsdoc": "^50.
|
|
29
|
+
"eslint": "^9.14.0",
|
|
30
|
+
"eslint-plugin-jsdoc": "^50.4.3",
|
|
31
31
|
"expose-loader": "^5.0.0",
|
|
32
32
|
"file-saver": "^2.0.5",
|
|
33
|
-
"globals": "^15.
|
|
34
|
-
"mocha": "^10.
|
|
33
|
+
"globals": "^15.11.0",
|
|
34
|
+
"mocha": "^10.8.2",
|
|
35
35
|
"npm-run-all": "^4.1.5",
|
|
36
36
|
"process": "^0.11.10",
|
|
37
37
|
"rewire": "^7.0.0",
|
|
38
38
|
"shx": "^0.3.4",
|
|
39
|
-
"sinon": "^
|
|
39
|
+
"sinon": "^19.0.2",
|
|
40
40
|
"source-map-loader": "^5.0.0",
|
|
41
41
|
"stream-browserify": "^3.0.0",
|
|
42
42
|
"string-replace-webpack-plugin": "^0.1.3",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"terser-webpack-plugin": "^5.3.10",
|
|
45
45
|
"transform-loader": "^0.2.4",
|
|
46
46
|
"util": "^0.12.5",
|
|
47
|
-
"webpack": "^5.
|
|
47
|
+
"webpack": "^5.96.1",
|
|
48
48
|
"webpack-cli": "^5.1.4"
|
|
49
49
|
},
|
|
50
50
|
"engines": {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
The MIT License (MIT)
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2019 SVG-to-PDFKit contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
-
|
|
7
|
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
-
|
|
9
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 SVG-to-PDFKit contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/src/DocumentContext.js
CHANGED
|
@@ -23,13 +23,14 @@ class DocumentContext extends EventEmitter {
|
|
|
23
23
|
this.addPage(pageSize);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
beginColumnGroup(marginXTopParent) {
|
|
26
|
+
beginColumnGroup(marginXTopParent, bottomByPage = {}) {
|
|
27
27
|
this.snapshots.push({
|
|
28
28
|
x: this.x,
|
|
29
29
|
y: this.y,
|
|
30
30
|
availableHeight: this.availableHeight,
|
|
31
31
|
availableWidth: this.availableWidth,
|
|
32
32
|
page: this.page,
|
|
33
|
+
bottomByPage: bottomByPage ? bottomByPage : {},
|
|
33
34
|
bottomMost: {
|
|
34
35
|
x: this.x,
|
|
35
36
|
y: this.y,
|
|
@@ -46,6 +47,16 @@ class DocumentContext extends EventEmitter {
|
|
|
46
47
|
}
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
updateBottomByPage () {
|
|
51
|
+
const lastSnapshot = this.snapshots[this.snapshots.length - 1];
|
|
52
|
+
const lastPage = this.page;
|
|
53
|
+
let previousBottom = -Number.MIN_VALUE;
|
|
54
|
+
if (lastSnapshot.bottomByPage[lastPage]) {
|
|
55
|
+
previousBottom = lastSnapshot.bottomByPage[lastPage];
|
|
56
|
+
}
|
|
57
|
+
lastSnapshot.bottomByPage[lastPage] = Math.max(previousBottom, this.y);
|
|
58
|
+
}
|
|
59
|
+
|
|
49
60
|
resetMarginXTopParent() {
|
|
50
61
|
this.marginXTopParent = null;
|
|
51
62
|
}
|
|
@@ -118,6 +129,7 @@ class DocumentContext extends EventEmitter {
|
|
|
118
129
|
this.availableHeight -= (y - saved.bottomMost.y);
|
|
119
130
|
}
|
|
120
131
|
this.lastColumnWidth = saved.lastColumnWidth;
|
|
132
|
+
return saved.bottomByPage;
|
|
121
133
|
}
|
|
122
134
|
|
|
123
135
|
addMargin(left, right) {
|
package/src/ElementWriter.js
CHANGED
|
@@ -259,9 +259,12 @@ class ElementWriter extends EventEmitter {
|
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
addVector(vector, ignoreContextX, ignoreContextY, index) {
|
|
262
|
+
addVector(vector, ignoreContextX, ignoreContextY, index, forcePage) {
|
|
263
263
|
let context = this.context();
|
|
264
264
|
let page = context.getCurrentPage();
|
|
265
|
+
if (isNumber(forcePage)) {
|
|
266
|
+
page = context.pages[forcePage];
|
|
267
|
+
}
|
|
265
268
|
let position = this.getCurrentPositionOnPage();
|
|
266
269
|
|
|
267
270
|
if (page) {
|
package/src/LayoutBuilder.js
CHANGED
|
@@ -539,7 +539,15 @@ class LayoutBuilder {
|
|
|
539
539
|
}
|
|
540
540
|
}
|
|
541
541
|
|
|
542
|
-
|
|
542
|
+
/**
|
|
543
|
+
* Searches for a cell in the same row that starts a rowspan and is positioned immediately before the current cell.
|
|
544
|
+
* Alternatively, it finds a cell where the colspan initiating the rowspan extends to the cell just before the current one.
|
|
545
|
+
*
|
|
546
|
+
* @param {Array<object>} arr - An array representing cells in a row.
|
|
547
|
+
* @param {number} i - The index of the current cell to search backward from.
|
|
548
|
+
* @returns {object|null} The starting cell of the rowspan if found; otherwise, `null`.
|
|
549
|
+
*/
|
|
550
|
+
_findStartingRowSpanCell(arr, i) {
|
|
543
551
|
let requiredColspan = 1;
|
|
544
552
|
for (let index = i - 1; index >= 0; index--) {
|
|
545
553
|
if (!arr[index]._span) {
|
|
@@ -554,114 +562,239 @@ class LayoutBuilder {
|
|
|
554
562
|
return null;
|
|
555
563
|
}
|
|
556
564
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
565
|
+
/**
|
|
566
|
+
* Retrieves a page break description for a specified page from a list of page breaks.
|
|
567
|
+
*
|
|
568
|
+
* @param {Array<object>} pageBreaks - An array of page break descriptions, each containing `prevPage` properties.
|
|
569
|
+
* @param {number} page - The page number to find the associated page break for.
|
|
570
|
+
* @returns {object|undefined} The page break description object for the specified page if found; otherwise, `undefined`.
|
|
571
|
+
*/
|
|
572
|
+
_getPageBreak(pageBreaks, page) {
|
|
573
|
+
return pageBreaks.find(desc => desc.prevPage === page);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
_getPageBreakListBySpan(tableNode, page, rowIndex) {
|
|
577
|
+
if (!tableNode || !tableNode._breaksBySpan) {
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
const breaksList = tableNode._breaksBySpan.filter(desc => desc.prevPage === page && rowIndex <= desc.rowIndexOfSpanEnd);
|
|
581
|
+
|
|
582
|
+
let y = Number.MAX_VALUE,
|
|
583
|
+
prevY = Number.MIN_VALUE;
|
|
584
|
+
|
|
585
|
+
breaksList.forEach(b => {
|
|
586
|
+
prevY = Math.max(b.prevY, prevY);
|
|
587
|
+
y = Math.min(b.y, y);
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
return {
|
|
591
|
+
prevPage: page,
|
|
592
|
+
prevY: prevY,
|
|
593
|
+
y: y
|
|
572
594
|
};
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
_findSameRowPageBreakByRowSpanData(breaksBySpan, page, rowIndex) {
|
|
598
|
+
if (!breaksBySpan) {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
return breaksBySpan.find(desc => desc.prevPage === page && rowIndex === desc.rowIndexOfSpanEnd);
|
|
602
|
+
}
|
|
573
603
|
|
|
574
|
-
|
|
575
|
-
|
|
604
|
+
_updatePageBreaksData(pageBreaks, tableNode, rowIndex) {
|
|
605
|
+
Object.keys(tableNode._bottomByPage).forEach(p => {
|
|
606
|
+
const page = Number(p);
|
|
607
|
+
const pageBreak = this._getPageBreak(pageBreaks, page);
|
|
608
|
+
if (pageBreak) {
|
|
609
|
+
pageBreak.prevY = Math.max(pageBreak.prevY, tableNode._bottomByPage[page]);
|
|
610
|
+
}
|
|
611
|
+
if (tableNode._breaksBySpan && tableNode._breaksBySpan.length > 0) {
|
|
612
|
+
const breaksBySpanList = tableNode._breaksBySpan.filter(pb => pb.prevPage === page && rowIndex <= pb.rowIndexOfSpanEnd);
|
|
576
613
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
break;
|
|
614
|
+
if (breaksBySpanList && breaksBySpanList.length > 0) {
|
|
615
|
+
breaksBySpanList.forEach(b => {
|
|
616
|
+
b.prevY = Math.max(b.prevY, tableNode._bottomByPage[page]);
|
|
617
|
+
});
|
|
582
618
|
}
|
|
583
619
|
}
|
|
620
|
+
});
|
|
621
|
+
}
|
|
584
622
|
|
|
623
|
+
/**
|
|
624
|
+
* Resolves the Y-coordinates for a target object by comparing two break points.
|
|
625
|
+
*
|
|
626
|
+
* @param {object} break1 - The first break point with `prevY` and `y` properties.
|
|
627
|
+
* @param {object} break2 - The second break point with `prevY` and `y` properties.
|
|
628
|
+
* @param {object} target - The target object to be updated with resolved Y-coordinates.
|
|
629
|
+
* @property {number} target.prevY - Updated to the maximum `prevY` value between `break1` and `break2`.
|
|
630
|
+
* @property {number} target.y - Updated to the minimum `y` value between `break1` and `break2`.
|
|
631
|
+
*/
|
|
632
|
+
_resolveBreakY (break1, break2, target) {
|
|
633
|
+
target.prevY = Math.max(break1.prevY, break2.prevY);
|
|
634
|
+
target.y = Math.min(break1.y, break2.y);
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
_storePageBreakData (data, startsRowSpan, pageBreaks, tableNode) {
|
|
638
|
+
if (!startsRowSpan) {
|
|
639
|
+
let pageDesc = this._getPageBreak(pageBreaks, data.prevPage);
|
|
640
|
+
let pageDescBySpan = this._getPageBreakListBySpan(tableNode, data.prevPage, data.rowIndex);
|
|
585
641
|
if (!pageDesc) {
|
|
586
|
-
pageDesc =
|
|
642
|
+
pageDesc = {
|
|
643
|
+
...data
|
|
644
|
+
};
|
|
587
645
|
pageBreaks.push(pageDesc);
|
|
588
646
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
647
|
+
if (pageDescBySpan) {
|
|
648
|
+
this._resolveBreakY(pageDesc, pageDescBySpan, pageDesc);
|
|
649
|
+
}
|
|
650
|
+
this._resolveBreakY(pageDesc, data, pageDesc);
|
|
651
|
+
} else {
|
|
652
|
+
const breaksBySpan = tableNode && tableNode._breaksBySpan || null;
|
|
653
|
+
let pageDescBySpan = this._findSameRowPageBreakByRowSpanData(breaksBySpan, data.prevPage, data.rowIndex);
|
|
654
|
+
if (!pageDescBySpan) {
|
|
655
|
+
pageDescBySpan = {
|
|
656
|
+
...data,
|
|
657
|
+
rowIndexOfSpanEnd: data.rowIndex + data.rowSpan - 1
|
|
658
|
+
};
|
|
659
|
+
if (!tableNode._breaksBySpan) {
|
|
660
|
+
tableNode._breaksBySpan = [];
|
|
661
|
+
}
|
|
662
|
+
tableNode._breaksBySpan.push(pageDescBySpan);
|
|
663
|
+
}
|
|
664
|
+
pageDescBySpan.prevY = Math.max(pageDescBySpan.prevY, data.prevY);
|
|
665
|
+
pageDescBySpan.y = Math.min(pageDescBySpan.y, data.y);
|
|
666
|
+
let pageDesc = this._getPageBreak(pageBreaks, data.prevPage);
|
|
667
|
+
if (pageDesc) {
|
|
668
|
+
this._resolveBreakY(pageDesc, pageDescBySpan, pageDesc);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
};
|
|
592
672
|
|
|
673
|
+
/**
|
|
674
|
+
* Calculates the left offset for a column based on the specified gap values.
|
|
675
|
+
*
|
|
676
|
+
* @param {number} i - The index of the column for which the offset is being calculated.
|
|
677
|
+
* @param {Array<number>} gaps - An array of gap values for each column.
|
|
678
|
+
* @returns {number} The left offset for the column. Returns `gaps[i]` if it exists, otherwise `0`.
|
|
679
|
+
*/
|
|
680
|
+
_colLeftOffset(i, gaps) {
|
|
681
|
+
if (gaps && gaps.length > i) {
|
|
682
|
+
return gaps[i];
|
|
683
|
+
}
|
|
684
|
+
return 0;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Retrieves the ending cell for a row span in case it exists in a specified table column.
|
|
689
|
+
*
|
|
690
|
+
* @param {Array<Array<object>>} tableBody - The table body, represented as a 2D array of cell objects.
|
|
691
|
+
* @param {number} rowIndex - The index of the starting row for the row span.
|
|
692
|
+
* @param {object} column - The column object containing row span information.
|
|
693
|
+
* @param {number} columnIndex - The index of the column within the row.
|
|
694
|
+
* @returns {object|null} The cell at the end of the row span if it exists; otherwise, `null`.
|
|
695
|
+
* @throws {Error} If the row span extends beyond the total row count.
|
|
696
|
+
*/
|
|
697
|
+
_getRowSpanEndingCell(tableBody, rowIndex, column, columnIndex) {
|
|
698
|
+
if (column.rowSpan && column.rowSpan > 1) {
|
|
699
|
+
let endingRow = rowIndex + column.rowSpan - 1;
|
|
700
|
+
if (endingRow >= tableBody.length) {
|
|
701
|
+
throw new Error(`Row span for column ${columnIndex} (with indexes starting from 0) exceeded row count`);
|
|
702
|
+
}
|
|
703
|
+
return tableBody[endingRow][columnIndex];
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
return null;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
processRow({ marginX = [0, 0], dontBreakRows = false, rowsWithoutPageBreak = 0, cells, widths, gaps, tableNode, tableBody, rowIndex, height }) {
|
|
593
710
|
const isUnbreakableRow = dontBreakRows || rowIndex <= rowsWithoutPageBreak - 1;
|
|
594
711
|
let pageBreaks = [];
|
|
712
|
+
let pageBreaksByRowSpan = [];
|
|
595
713
|
let positions = [];
|
|
596
714
|
let willBreakByHeight = false;
|
|
597
|
-
|
|
715
|
+
widths = widths || cells;
|
|
598
716
|
|
|
599
717
|
// Check if row should break by height
|
|
600
718
|
if (!isUnbreakableRow && height > this.writer.context().availableHeight) {
|
|
601
719
|
willBreakByHeight = true;
|
|
602
720
|
}
|
|
603
721
|
|
|
604
|
-
widths = widths || cells;
|
|
605
722
|
// Use the marginX if we are in a top level table/column (not nested)
|
|
606
723
|
const marginXParent = this.nestedLevel === 1 ? marginX : null;
|
|
607
|
-
|
|
608
|
-
this.writer.context().beginColumnGroup(marginXParent);
|
|
724
|
+
const _bottomByPage = tableNode ? tableNode._bottomByPage : null;
|
|
725
|
+
this.writer.context().beginColumnGroup(marginXParent, _bottomByPage);
|
|
609
726
|
|
|
610
727
|
for (let i = 0, l = cells.length; i < l; i++) {
|
|
611
|
-
let
|
|
728
|
+
let cell = cells[i];
|
|
729
|
+
|
|
730
|
+
// Page change handler
|
|
731
|
+
const storePageBreakClosure = data => {
|
|
732
|
+
const startsRowSpan = cell.rowSpan && cell.rowSpan > 1;
|
|
733
|
+
if (startsRowSpan) {
|
|
734
|
+
data.rowSpan = cell.rowSpan;
|
|
735
|
+
}
|
|
736
|
+
data.rowIndex = rowIndex;
|
|
737
|
+
this._storePageBreakData(data, startsRowSpan, pageBreaks, tableNode);
|
|
738
|
+
};
|
|
739
|
+
|
|
740
|
+
this.writer.addListener('pageChanged', storePageBreakClosure);
|
|
741
|
+
|
|
612
742
|
let width = widths[i]._calcWidth;
|
|
613
|
-
let leftOffset =
|
|
743
|
+
let leftOffset = this._colLeftOffset(i, gaps);
|
|
744
|
+
// Check if exists and retrieve the cell that started the rowspan in case we are in the cell just after
|
|
745
|
+
let startingSpanCell = this._findStartingRowSpanCell(cells, i);
|
|
614
746
|
|
|
615
|
-
if (
|
|
616
|
-
for (let j = 1; j <
|
|
747
|
+
if (cell.colSpan && cell.colSpan > 1) {
|
|
748
|
+
for (let j = 1; j < cell.colSpan; j++) {
|
|
617
749
|
width += widths[++i]._calcWidth + gaps[i];
|
|
618
750
|
}
|
|
619
751
|
}
|
|
620
752
|
|
|
621
753
|
// if rowspan starts in this cell, we retrieve the last cell affected by the rowspan
|
|
622
|
-
const
|
|
623
|
-
if (
|
|
754
|
+
const rowSpanEndingCell = this._getRowSpanEndingCell(tableBody, rowIndex, cell, i);
|
|
755
|
+
if (rowSpanEndingCell) {
|
|
624
756
|
// We store a reference of the ending cell in the first cell of the rowspan
|
|
625
|
-
|
|
626
|
-
|
|
757
|
+
cell._endingCell = rowSpanEndingCell;
|
|
758
|
+
cell._endingCell._startingRowSpanY = cell._startingRowSpanY;
|
|
627
759
|
}
|
|
628
760
|
|
|
629
|
-
//
|
|
630
|
-
let
|
|
631
|
-
let endingSpanCell = null;
|
|
761
|
+
// If we are after a cell that started a rowspan
|
|
762
|
+
let endOfRowSpanCell = null;
|
|
632
763
|
if (startingSpanCell && startingSpanCell._endingCell) {
|
|
633
764
|
// Reference to the last cell of the rowspan
|
|
634
|
-
|
|
765
|
+
endOfRowSpanCell = startingSpanCell._endingCell;
|
|
635
766
|
// Store if we are in an unbreakable block when we save the context and the originalX
|
|
636
767
|
if (this.writer.transactionLevel > 0) {
|
|
637
|
-
|
|
638
|
-
|
|
768
|
+
endOfRowSpanCell._isUnbreakableContext = true;
|
|
769
|
+
endOfRowSpanCell._originalXOffset = this.writer.originalX;
|
|
639
770
|
}
|
|
640
771
|
}
|
|
641
772
|
|
|
642
773
|
// We pass the endingSpanCell reference to store the context just after processing rowspan cell
|
|
643
|
-
this.writer.context().beginColumn(width, leftOffset,
|
|
774
|
+
this.writer.context().beginColumn(width, leftOffset, endOfRowSpanCell);
|
|
644
775
|
|
|
645
|
-
if (!
|
|
646
|
-
this.processNode(
|
|
647
|
-
|
|
648
|
-
|
|
776
|
+
if (!cell._span) {
|
|
777
|
+
this.processNode(cell);
|
|
778
|
+
this.writer.context().updateBottomByPage();
|
|
779
|
+
addAll(positions, cell.positions);
|
|
780
|
+
} else if (cell._columnEndingContext) {
|
|
649
781
|
let discountY = 0;
|
|
650
782
|
if (dontBreakRows) {
|
|
651
783
|
// Calculate how many points we have to discount to Y when dontBreakRows and rowSpan are combined
|
|
652
784
|
const ctxBeforeRowSpanLastRow = this.writer.contextStack[this.writer.contextStack.length - 1];
|
|
653
|
-
discountY = ctxBeforeRowSpanLastRow.y -
|
|
785
|
+
discountY = ctxBeforeRowSpanLastRow.y - cell._startingRowSpanY;
|
|
654
786
|
}
|
|
655
787
|
let originalXOffset = 0;
|
|
656
788
|
// If context was saved from an unbreakable block and we are not in an unbreakable block anymore
|
|
657
789
|
// We have to sum the originalX (X before starting unbreakable block) to X
|
|
658
|
-
if (
|
|
659
|
-
originalXOffset =
|
|
790
|
+
if (cell._isUnbreakableContext && !this.writer.transactionLevel) {
|
|
791
|
+
originalXOffset = cell._originalXOffset;
|
|
660
792
|
}
|
|
661
793
|
// row-span ending
|
|
662
794
|
// Recover the context after processing the rowspanned cell
|
|
663
|
-
this.writer.context().markEnding(
|
|
795
|
+
this.writer.context().markEnding(cell, originalXOffset, discountY);
|
|
664
796
|
}
|
|
797
|
+
this.writer.removeListener('pageChanged', storePageBreakClosure);
|
|
665
798
|
}
|
|
666
799
|
|
|
667
800
|
// Check if last cell is part of a span
|
|
@@ -674,7 +807,7 @@ class LayoutBuilder {
|
|
|
674
807
|
// Previous column cell is part of a span
|
|
675
808
|
} else if (lastColumn._span === true) {
|
|
676
809
|
// We get the cell that started the span where we set a reference to the ending cell
|
|
677
|
-
const startingSpanCell = this.
|
|
810
|
+
const startingSpanCell = this._findStartingRowSpanCell(cells, cells.length);
|
|
678
811
|
if (startingSpanCell) {
|
|
679
812
|
// Context will be stored here (ending cell)
|
|
680
813
|
endingSpanCell = startingSpanCell._endingCell;
|
|
@@ -687,38 +820,25 @@ class LayoutBuilder {
|
|
|
687
820
|
}
|
|
688
821
|
}
|
|
689
822
|
|
|
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
823
|
// If content did not break page, check if we should break by height
|
|
694
|
-
if (!isUnbreakableRow && pageBreaks.length === 0
|
|
824
|
+
if (willBreakByHeight && !isUnbreakableRow && pageBreaks.length === 0) {
|
|
695
825
|
this.writer.context().moveDown(this.writer.context().availableHeight);
|
|
696
826
|
this.writer.moveToNextPage();
|
|
697
827
|
}
|
|
698
828
|
|
|
699
|
-
this.writer.context().completeColumnGroup(height, endingSpanCell);
|
|
700
|
-
this.writer.removeListener('pageChanged', storePageBreakData);
|
|
829
|
+
const bottomByPage = this.writer.context().completeColumnGroup(height, endingSpanCell);
|
|
701
830
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
return gaps[i];
|
|
707
|
-
}
|
|
708
|
-
return 0;
|
|
831
|
+
if (tableNode) {
|
|
832
|
+
tableNode._bottomByPage = bottomByPage;
|
|
833
|
+
// If there are page breaks in this row, update data with prevY of last cell
|
|
834
|
+
this._updatePageBreaksData(pageBreaks, tableNode, rowIndex);
|
|
709
835
|
}
|
|
710
836
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
}
|
|
717
|
-
return tableBody[endingRow][columnIndex];
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
return null;
|
|
721
|
-
}
|
|
837
|
+
return {
|
|
838
|
+
pageBreaksBySpan : pageBreaksByRowSpan,
|
|
839
|
+
pageBreaks: pageBreaks,
|
|
840
|
+
positions: positions
|
|
841
|
+
};
|
|
722
842
|
}
|
|
723
843
|
|
|
724
844
|
// lists
|
|
@@ -799,6 +919,8 @@ class LayoutBuilder {
|
|
|
799
919
|
height = undefined;
|
|
800
920
|
}
|
|
801
921
|
|
|
922
|
+
const pageBeforeProcessing = this.writer.context().page;
|
|
923
|
+
|
|
802
924
|
let result = this.processRow({
|
|
803
925
|
marginX: tableNode._margin ? [tableNode._margin[0], tableNode._margin[2]] : [0, 0],
|
|
804
926
|
dontBreakRows: processor.dontBreakRows,
|
|
@@ -807,12 +929,22 @@ class LayoutBuilder {
|
|
|
807
929
|
widths: tableNode.table.widths,
|
|
808
930
|
gaps: tableNode._offsets.offsets,
|
|
809
931
|
tableBody: tableNode.table.body,
|
|
932
|
+
tableNode,
|
|
810
933
|
rowIndex: i,
|
|
811
934
|
height
|
|
812
935
|
});
|
|
813
936
|
|
|
814
937
|
addAll(tableNode.positions, result.positions);
|
|
815
938
|
|
|
939
|
+
if (!result.pageBreaks || result.pageBreaks.length === 0) {
|
|
940
|
+
const breaksBySpan = tableNode && tableNode._breaksBySpan || null;
|
|
941
|
+
const breakBySpanData = this._findSameRowPageBreakByRowSpanData(breaksBySpan, pageBeforeProcessing, i);
|
|
942
|
+
if (breakBySpanData) {
|
|
943
|
+
const finalBreakBySpanData = this._getPageBreakListBySpan(tableNode, breakBySpanData.prevPage, i);
|
|
944
|
+
result.pageBreaks.push(finalBreakBySpanData);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
816
948
|
processor.endRow(i, this.writer, result.pageBreaks);
|
|
817
949
|
}
|
|
818
950
|
|
package/src/PageElementWriter.js
CHANGED
|
@@ -39,8 +39,8 @@ class PageElementWriter extends ElementWriter {
|
|
|
39
39
|
return this._fitOnPage(() => super.addAttachment(attachment, index));
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
addVector(vector, ignoreContextX, ignoreContextY, index) {
|
|
43
|
-
return super.addVector(vector, ignoreContextX, ignoreContextY, index);
|
|
42
|
+
addVector(vector, ignoreContextX, ignoreContextY, index, forcePage) {
|
|
43
|
+
return super.addVector(vector, ignoreContextX, ignoreContextY, index, forcePage);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
beginClip(width, height) {
|