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/js/DocMeasure.js
CHANGED
|
@@ -117,6 +117,15 @@ class DocMeasure {
|
|
|
117
117
|
width: image.width,
|
|
118
118
|
height: image.height
|
|
119
119
|
};
|
|
120
|
+
if (image.constructor.name === 'JPEG') {
|
|
121
|
+
// If EXIF orientation calls for it, swap width and height
|
|
122
|
+
if (image.orientation > 4) {
|
|
123
|
+
imageSize = {
|
|
124
|
+
width: image.height,
|
|
125
|
+
height: image.width
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
120
129
|
this.measureImageWithDimensions(node, imageSize);
|
|
121
130
|
return node;
|
|
122
131
|
}
|
package/js/DocumentContext.js
CHANGED
|
@@ -9,25 +9,25 @@ var _events = require("events");
|
|
|
9
9
|
* It facilitates column divisions and vertical sync
|
|
10
10
|
*/
|
|
11
11
|
class DocumentContext extends _events.EventEmitter {
|
|
12
|
-
constructor(
|
|
12
|
+
constructor() {
|
|
13
13
|
super();
|
|
14
14
|
this.pages = [];
|
|
15
|
-
this.pageMargins =
|
|
16
|
-
this.x =
|
|
17
|
-
this.availableWidth =
|
|
18
|
-
this.availableHeight =
|
|
15
|
+
this.pageMargins = undefined;
|
|
16
|
+
this.x = undefined;
|
|
17
|
+
this.availableWidth = undefined;
|
|
18
|
+
this.availableHeight = undefined;
|
|
19
19
|
this.page = -1;
|
|
20
20
|
this.snapshots = [];
|
|
21
21
|
this.backgroundLength = [];
|
|
22
|
-
this.addPage(pageSize);
|
|
23
22
|
}
|
|
24
|
-
beginColumnGroup(marginXTopParent) {
|
|
23
|
+
beginColumnGroup(marginXTopParent, bottomByPage = {}) {
|
|
25
24
|
this.snapshots.push({
|
|
26
25
|
x: this.x,
|
|
27
26
|
y: this.y,
|
|
28
27
|
availableHeight: this.availableHeight,
|
|
29
28
|
availableWidth: this.availableWidth,
|
|
30
29
|
page: this.page,
|
|
30
|
+
bottomByPage: bottomByPage ? bottomByPage : {},
|
|
31
31
|
bottomMost: {
|
|
32
32
|
x: this.x,
|
|
33
33
|
y: this.y,
|
|
@@ -42,6 +42,15 @@ class DocumentContext extends _events.EventEmitter {
|
|
|
42
42
|
this.marginXTopParent = marginXTopParent;
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
|
+
updateBottomByPage() {
|
|
46
|
+
const lastSnapshot = this.snapshots[this.snapshots.length - 1];
|
|
47
|
+
const lastPage = this.page;
|
|
48
|
+
let previousBottom = -Number.MIN_VALUE;
|
|
49
|
+
if (lastSnapshot.bottomByPage[lastPage]) {
|
|
50
|
+
previousBottom = lastSnapshot.bottomByPage[lastPage];
|
|
51
|
+
}
|
|
52
|
+
lastSnapshot.bottomByPage[lastPage] = Math.max(previousBottom, this.y);
|
|
53
|
+
}
|
|
45
54
|
resetMarginXTopParent() {
|
|
46
55
|
this.marginXTopParent = null;
|
|
47
56
|
}
|
|
@@ -102,6 +111,7 @@ class DocumentContext extends _events.EventEmitter {
|
|
|
102
111
|
this.availableHeight -= y - saved.bottomMost.y;
|
|
103
112
|
}
|
|
104
113
|
this.lastColumnWidth = saved.lastColumnWidth;
|
|
114
|
+
return saved.bottomByPage;
|
|
105
115
|
}
|
|
106
116
|
addMargin(left, right) {
|
|
107
117
|
this.x += left;
|
|
@@ -209,10 +219,16 @@ class DocumentContext extends _events.EventEmitter {
|
|
|
209
219
|
y: this.y
|
|
210
220
|
};
|
|
211
221
|
}
|
|
212
|
-
addPage(pageSize) {
|
|
222
|
+
addPage(pageSize, pageMargin = null) {
|
|
223
|
+
if (pageMargin !== null) {
|
|
224
|
+
this.pageMargins = pageMargin;
|
|
225
|
+
this.x = pageMargin.left;
|
|
226
|
+
this.availableWidth = pageSize.width - pageMargin.left - pageMargin.right;
|
|
227
|
+
}
|
|
213
228
|
let page = {
|
|
214
229
|
items: [],
|
|
215
|
-
pageSize: pageSize
|
|
230
|
+
pageSize: pageSize,
|
|
231
|
+
pageMargins: this.pageMargins
|
|
216
232
|
};
|
|
217
233
|
this.pages.push(page);
|
|
218
234
|
this.backgroundLength.push(0);
|
package/js/ElementWriter.js
CHANGED
|
@@ -12,11 +12,18 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
12
12
|
* their positions based on the context
|
|
13
13
|
*/
|
|
14
14
|
class ElementWriter extends _events.EventEmitter {
|
|
15
|
+
/**
|
|
16
|
+
* @param {DocumentContext} context
|
|
17
|
+
*/
|
|
15
18
|
constructor(context) {
|
|
16
19
|
super();
|
|
17
20
|
this._context = context;
|
|
18
21
|
this.contextStack = [];
|
|
19
22
|
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @returns {DocumentContext}
|
|
26
|
+
*/
|
|
20
27
|
context() {
|
|
21
28
|
return this._context;
|
|
22
29
|
}
|
|
@@ -306,7 +313,7 @@ class ElementWriter extends _events.EventEmitter {
|
|
|
306
313
|
* pushContext(width, height) - creates and pushes a new context with the specified width and height
|
|
307
314
|
* pushContext() - creates a new context for unbreakable blocks (with current availableWidth and full-page-height)
|
|
308
315
|
*
|
|
309
|
-
* @param {
|
|
316
|
+
* @param {DocumentContext|number} contextOrWidth
|
|
310
317
|
* @param {number} height
|
|
311
318
|
*/
|
|
312
319
|
pushContext(contextOrWidth, height) {
|
|
@@ -315,8 +322,10 @@ class ElementWriter extends _events.EventEmitter {
|
|
|
315
322
|
contextOrWidth = this.context().availableWidth;
|
|
316
323
|
}
|
|
317
324
|
if ((0, _variableType.isNumber)(contextOrWidth)) {
|
|
318
|
-
|
|
319
|
-
|
|
325
|
+
let width = contextOrWidth;
|
|
326
|
+
contextOrWidth = new _DocumentContext.default();
|
|
327
|
+
contextOrWidth.addPage({
|
|
328
|
+
width: width,
|
|
320
329
|
height: height
|
|
321
330
|
}, {
|
|
322
331
|
left: 0,
|
package/js/LayoutBuilder.js
CHANGED
|
@@ -135,11 +135,11 @@ class LayoutBuilder {
|
|
|
135
135
|
this.linearNodeList = [];
|
|
136
136
|
docStructure = this.docPreprocessor.preprocessDocument(docStructure);
|
|
137
137
|
docStructure = this.docMeasure.measureDocument(docStructure);
|
|
138
|
-
this.writer = new _PageElementWriter.default(new _DocumentContext.default(
|
|
138
|
+
this.writer = new _PageElementWriter.default(new _DocumentContext.default());
|
|
139
139
|
this.writer.context().addListener('pageAdded', () => {
|
|
140
140
|
this.addBackground(background);
|
|
141
141
|
});
|
|
142
|
-
this.
|
|
142
|
+
this.writer.addPage(this.pageSize, null, this.pageMargins);
|
|
143
143
|
this.processNode(docStructure);
|
|
144
144
|
this.addHeadersAndFooters(header, footer);
|
|
145
145
|
if (watermark != null) {
|
|
@@ -163,18 +163,13 @@ class LayoutBuilder {
|
|
|
163
163
|
context.backgroundLength[context.page] += pageBackground.positions.length;
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
|
-
addStaticRepeatable(headerOrFooter, sizeFunction) {
|
|
167
|
-
this.addDynamicRepeatable(() =>
|
|
168
|
-
// copy to new object
|
|
169
|
-
JSON.parse(JSON.stringify(headerOrFooter)), sizeFunction);
|
|
170
|
-
}
|
|
171
166
|
addDynamicRepeatable(nodeGetter, sizeFunction) {
|
|
172
167
|
let pages = this.writer.context().pages;
|
|
173
168
|
for (let pageIndex = 0, l = pages.length; pageIndex < l; pageIndex++) {
|
|
174
169
|
this.writer.context().page = pageIndex;
|
|
175
170
|
let node = nodeGetter(pageIndex + 1, l, this.writer.context().pages[pageIndex].pageSize);
|
|
176
171
|
if (node) {
|
|
177
|
-
let sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.pageMargins);
|
|
172
|
+
let sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.writer.context().getCurrentPage().pageMargins);
|
|
178
173
|
this.writer.beginUnbreakableBlock(sizes.width, sizes.height);
|
|
179
174
|
node = this.docPreprocessor.preprocessDocument(node);
|
|
180
175
|
this.processNode(this.docMeasure.measureDocument(node));
|
|
@@ -195,15 +190,11 @@ class LayoutBuilder {
|
|
|
195
190
|
width: pageSize.width,
|
|
196
191
|
height: pageMargins.bottom
|
|
197
192
|
});
|
|
198
|
-
if (
|
|
193
|
+
if (header) {
|
|
199
194
|
this.addDynamicRepeatable(header, headerSizeFct);
|
|
200
|
-
} else if (header) {
|
|
201
|
-
this.addStaticRepeatable(header, headerSizeFct);
|
|
202
195
|
}
|
|
203
|
-
if (
|
|
196
|
+
if (footer) {
|
|
204
197
|
this.addDynamicRepeatable(footer, footerSizeFct);
|
|
205
|
-
} else if (footer) {
|
|
206
|
-
this.addStaticRepeatable(footer, footerSizeFct);
|
|
207
198
|
}
|
|
208
199
|
}
|
|
209
200
|
addWatermark(watermark, pdfDocument, defaultStyle) {
|
|
@@ -216,31 +207,36 @@ class LayoutBuilder {
|
|
|
216
207
|
// empty watermark text
|
|
217
208
|
return;
|
|
218
209
|
}
|
|
219
|
-
watermark.font = watermark.font || defaultStyle.font || 'Roboto';
|
|
220
|
-
watermark.fontSize = watermark.fontSize || 'auto';
|
|
221
|
-
watermark.color = watermark.color || 'black';
|
|
222
|
-
watermark.opacity = (0, _variableType.isNumber)(watermark.opacity) ? watermark.opacity : 0.6;
|
|
223
|
-
watermark.bold = watermark.bold || false;
|
|
224
|
-
watermark.italics = watermark.italics || false;
|
|
225
|
-
watermark.angle = (0, _variableType.isValue)(watermark.angle) ? watermark.angle : null;
|
|
226
|
-
if (watermark.angle === null) {
|
|
227
|
-
watermark.angle = Math.atan2(this.pageSize.height, this.pageSize.width) * -180 / Math.PI;
|
|
228
|
-
}
|
|
229
|
-
if (watermark.fontSize === 'auto') {
|
|
230
|
-
watermark.fontSize = getWatermarkFontSize(this.pageSize, watermark, pdfDocument);
|
|
231
|
-
}
|
|
232
|
-
let watermarkObject = {
|
|
233
|
-
text: watermark.text,
|
|
234
|
-
font: pdfDocument.provideFont(watermark.font, watermark.bold, watermark.italics),
|
|
235
|
-
fontSize: watermark.fontSize,
|
|
236
|
-
color: watermark.color,
|
|
237
|
-
opacity: watermark.opacity,
|
|
238
|
-
angle: watermark.angle
|
|
239
|
-
};
|
|
240
|
-
watermarkObject._size = getWatermarkSize(watermark, pdfDocument);
|
|
241
210
|
let pages = this.writer.context().pages;
|
|
242
211
|
for (let i = 0, l = pages.length; i < l; i++) {
|
|
243
|
-
pages[i].watermark =
|
|
212
|
+
pages[i].watermark = getWatermarkObject({
|
|
213
|
+
...watermark
|
|
214
|
+
}, pages[i].pageSize, pdfDocument, defaultStyle);
|
|
215
|
+
}
|
|
216
|
+
function getWatermarkObject(watermark, pageSize, pdfDocument, defaultStyle) {
|
|
217
|
+
watermark.font = watermark.font || defaultStyle.font || 'Roboto';
|
|
218
|
+
watermark.fontSize = watermark.fontSize || 'auto';
|
|
219
|
+
watermark.color = watermark.color || 'black';
|
|
220
|
+
watermark.opacity = (0, _variableType.isNumber)(watermark.opacity) ? watermark.opacity : 0.6;
|
|
221
|
+
watermark.bold = watermark.bold || false;
|
|
222
|
+
watermark.italics = watermark.italics || false;
|
|
223
|
+
watermark.angle = (0, _variableType.isValue)(watermark.angle) ? watermark.angle : null;
|
|
224
|
+
if (watermark.angle === null) {
|
|
225
|
+
watermark.angle = Math.atan2(pageSize.height, pageSize.width) * -180 / Math.PI;
|
|
226
|
+
}
|
|
227
|
+
if (watermark.fontSize === 'auto') {
|
|
228
|
+
watermark.fontSize = getWatermarkFontSize(pageSize, watermark, pdfDocument);
|
|
229
|
+
}
|
|
230
|
+
let watermarkObject = {
|
|
231
|
+
text: watermark.text,
|
|
232
|
+
font: pdfDocument.provideFont(watermark.font, watermark.bold, watermark.italics),
|
|
233
|
+
fontSize: watermark.fontSize,
|
|
234
|
+
color: watermark.color,
|
|
235
|
+
opacity: watermark.opacity,
|
|
236
|
+
angle: watermark.angle
|
|
237
|
+
};
|
|
238
|
+
watermarkObject._size = getWatermarkSize(watermark, pdfDocument);
|
|
239
|
+
return watermarkObject;
|
|
244
240
|
}
|
|
245
241
|
function getWatermarkSize(watermark, pdfDocument) {
|
|
246
242
|
let textInlines = new _TextInlines.default(pdfDocument);
|
|
@@ -472,7 +468,16 @@ class LayoutBuilder {
|
|
|
472
468
|
return gaps;
|
|
473
469
|
}
|
|
474
470
|
}
|
|
475
|
-
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Searches for a cell in the same row that starts a rowspan and is positioned immediately before the current cell.
|
|
474
|
+
* Alternatively, it finds a cell where the colspan initiating the rowspan extends to the cell just before the current one.
|
|
475
|
+
*
|
|
476
|
+
* @param {Array<object>} arr - An array representing cells in a row.
|
|
477
|
+
* @param {number} i - The index of the current cell to search backward from.
|
|
478
|
+
* @returns {object|null} The starting cell of the rowspan if found; otherwise, `null`.
|
|
479
|
+
*/
|
|
480
|
+
_findStartingRowSpanCell(arr, i) {
|
|
476
481
|
let requiredColspan = 1;
|
|
477
482
|
for (let index = i - 1; index >= 0; index--) {
|
|
478
483
|
if (!arr[index]._span) {
|
|
@@ -486,6 +491,140 @@ class LayoutBuilder {
|
|
|
486
491
|
}
|
|
487
492
|
return null;
|
|
488
493
|
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Retrieves a page break description for a specified page from a list of page breaks.
|
|
497
|
+
*
|
|
498
|
+
* @param {Array<object>} pageBreaks - An array of page break descriptions, each containing `prevPage` properties.
|
|
499
|
+
* @param {number} page - The page number to find the associated page break for.
|
|
500
|
+
* @returns {object|undefined} The page break description object for the specified page if found; otherwise, `undefined`.
|
|
501
|
+
*/
|
|
502
|
+
_getPageBreak(pageBreaks, page) {
|
|
503
|
+
return pageBreaks.find(desc => desc.prevPage === page);
|
|
504
|
+
}
|
|
505
|
+
_getPageBreakListBySpan(tableNode, page, rowIndex) {
|
|
506
|
+
if (!tableNode || !tableNode._breaksBySpan) {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
const breaksList = tableNode._breaksBySpan.filter(desc => desc.prevPage === page && rowIndex <= desc.rowIndexOfSpanEnd);
|
|
510
|
+
let y = Number.MAX_VALUE,
|
|
511
|
+
prevY = Number.MIN_VALUE;
|
|
512
|
+
breaksList.forEach(b => {
|
|
513
|
+
prevY = Math.max(b.prevY, prevY);
|
|
514
|
+
y = Math.min(b.y, y);
|
|
515
|
+
});
|
|
516
|
+
return {
|
|
517
|
+
prevPage: page,
|
|
518
|
+
prevY: prevY,
|
|
519
|
+
y: y
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
_findSameRowPageBreakByRowSpanData(breaksBySpan, page, rowIndex) {
|
|
523
|
+
if (!breaksBySpan) {
|
|
524
|
+
return null;
|
|
525
|
+
}
|
|
526
|
+
return breaksBySpan.find(desc => desc.prevPage === page && rowIndex === desc.rowIndexOfSpanEnd);
|
|
527
|
+
}
|
|
528
|
+
_updatePageBreaksData(pageBreaks, tableNode, rowIndex) {
|
|
529
|
+
Object.keys(tableNode._bottomByPage).forEach(p => {
|
|
530
|
+
const page = Number(p);
|
|
531
|
+
const pageBreak = this._getPageBreak(pageBreaks, page);
|
|
532
|
+
if (pageBreak) {
|
|
533
|
+
pageBreak.prevY = Math.max(pageBreak.prevY, tableNode._bottomByPage[page]);
|
|
534
|
+
}
|
|
535
|
+
if (tableNode._breaksBySpan && tableNode._breaksBySpan.length > 0) {
|
|
536
|
+
const breaksBySpanList = tableNode._breaksBySpan.filter(pb => pb.prevPage === page && rowIndex <= pb.rowIndexOfSpanEnd);
|
|
537
|
+
if (breaksBySpanList && breaksBySpanList.length > 0) {
|
|
538
|
+
breaksBySpanList.forEach(b => {
|
|
539
|
+
b.prevY = Math.max(b.prevY, tableNode._bottomByPage[page]);
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Resolves the Y-coordinates for a target object by comparing two break points.
|
|
548
|
+
*
|
|
549
|
+
* @param {object} break1 - The first break point with `prevY` and `y` properties.
|
|
550
|
+
* @param {object} break2 - The second break point with `prevY` and `y` properties.
|
|
551
|
+
* @param {object} target - The target object to be updated with resolved Y-coordinates.
|
|
552
|
+
* @property {number} target.prevY - Updated to the maximum `prevY` value between `break1` and `break2`.
|
|
553
|
+
* @property {number} target.y - Updated to the minimum `y` value between `break1` and `break2`.
|
|
554
|
+
*/
|
|
555
|
+
_resolveBreakY(break1, break2, target) {
|
|
556
|
+
target.prevY = Math.max(break1.prevY, break2.prevY);
|
|
557
|
+
target.y = Math.min(break1.y, break2.y);
|
|
558
|
+
}
|
|
559
|
+
_storePageBreakData(data, startsRowSpan, pageBreaks, tableNode) {
|
|
560
|
+
if (!startsRowSpan) {
|
|
561
|
+
let pageDesc = this._getPageBreak(pageBreaks, data.prevPage);
|
|
562
|
+
let pageDescBySpan = this._getPageBreakListBySpan(tableNode, data.prevPage, data.rowIndex);
|
|
563
|
+
if (!pageDesc) {
|
|
564
|
+
pageDesc = {
|
|
565
|
+
...data
|
|
566
|
+
};
|
|
567
|
+
pageBreaks.push(pageDesc);
|
|
568
|
+
}
|
|
569
|
+
if (pageDescBySpan) {
|
|
570
|
+
this._resolveBreakY(pageDesc, pageDescBySpan, pageDesc);
|
|
571
|
+
}
|
|
572
|
+
this._resolveBreakY(pageDesc, data, pageDesc);
|
|
573
|
+
} else {
|
|
574
|
+
const breaksBySpan = tableNode && tableNode._breaksBySpan || null;
|
|
575
|
+
let pageDescBySpan = this._findSameRowPageBreakByRowSpanData(breaksBySpan, data.prevPage, data.rowIndex);
|
|
576
|
+
if (!pageDescBySpan) {
|
|
577
|
+
pageDescBySpan = {
|
|
578
|
+
...data,
|
|
579
|
+
rowIndexOfSpanEnd: data.rowIndex + data.rowSpan - 1
|
|
580
|
+
};
|
|
581
|
+
if (!tableNode._breaksBySpan) {
|
|
582
|
+
tableNode._breaksBySpan = [];
|
|
583
|
+
}
|
|
584
|
+
tableNode._breaksBySpan.push(pageDescBySpan);
|
|
585
|
+
}
|
|
586
|
+
pageDescBySpan.prevY = Math.max(pageDescBySpan.prevY, data.prevY);
|
|
587
|
+
pageDescBySpan.y = Math.min(pageDescBySpan.y, data.y);
|
|
588
|
+
let pageDesc = this._getPageBreak(pageBreaks, data.prevPage);
|
|
589
|
+
if (pageDesc) {
|
|
590
|
+
this._resolveBreakY(pageDesc, pageDescBySpan, pageDesc);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Calculates the left offset for a column based on the specified gap values.
|
|
596
|
+
*
|
|
597
|
+
* @param {number} i - The index of the column for which the offset is being calculated.
|
|
598
|
+
* @param {Array<number>} gaps - An array of gap values for each column.
|
|
599
|
+
* @returns {number} The left offset for the column. Returns `gaps[i]` if it exists, otherwise `0`.
|
|
600
|
+
*/
|
|
601
|
+
_colLeftOffset(i, gaps) {
|
|
602
|
+
if (gaps && gaps.length > i) {
|
|
603
|
+
return gaps[i];
|
|
604
|
+
}
|
|
605
|
+
return 0;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Retrieves the ending cell for a row span in case it exists in a specified table column.
|
|
610
|
+
*
|
|
611
|
+
* @param {Array<Array<object>>} tableBody - The table body, represented as a 2D array of cell objects.
|
|
612
|
+
* @param {number} rowIndex - The index of the starting row for the row span.
|
|
613
|
+
* @param {object} column - The column object containing row span information.
|
|
614
|
+
* @param {number} columnIndex - The index of the column within the row.
|
|
615
|
+
* @returns {object|null} The cell at the end of the row span if it exists; otherwise, `null`.
|
|
616
|
+
* @throws {Error} If the row span extends beyond the total row count.
|
|
617
|
+
*/
|
|
618
|
+
_getRowSpanEndingCell(tableBody, rowIndex, column, columnIndex) {
|
|
619
|
+
if (column.rowSpan && column.rowSpan > 1) {
|
|
620
|
+
let endingRow = rowIndex + column.rowSpan - 1;
|
|
621
|
+
if (endingRow >= tableBody.length) {
|
|
622
|
+
throw new Error(`Row span for column ${columnIndex} (with indexes starting from 0) exceeded row count`);
|
|
623
|
+
}
|
|
624
|
+
return tableBody[endingRow][columnIndex];
|
|
625
|
+
}
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
489
628
|
processRow({
|
|
490
629
|
marginX = [0, 0],
|
|
491
630
|
dontBreakRows = false,
|
|
@@ -493,108 +632,94 @@ class LayoutBuilder {
|
|
|
493
632
|
cells,
|
|
494
633
|
widths,
|
|
495
634
|
gaps,
|
|
635
|
+
tableNode,
|
|
496
636
|
tableBody,
|
|
497
637
|
rowIndex,
|
|
498
638
|
height
|
|
499
639
|
}) {
|
|
500
|
-
const updatePageBreakData = (page, prevY) => {
|
|
501
|
-
let pageDesc;
|
|
502
|
-
// Find page break data for this row and page
|
|
503
|
-
for (let i = 0, l = pageBreaks.length; i < l; i++) {
|
|
504
|
-
let desc = pageBreaks[i];
|
|
505
|
-
if (desc.prevPage === page) {
|
|
506
|
-
pageDesc = desc;
|
|
507
|
-
break;
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
// If row has page break in this page, update prevY
|
|
511
|
-
if (pageDesc) {
|
|
512
|
-
pageDesc.prevY = Math.max(pageDesc.prevY, prevY);
|
|
513
|
-
}
|
|
514
|
-
};
|
|
515
|
-
const storePageBreakData = data => {
|
|
516
|
-
let pageDesc;
|
|
517
|
-
for (let i = 0, l = pageBreaks.length; i < l; i++) {
|
|
518
|
-
let desc = pageBreaks[i];
|
|
519
|
-
if (desc.prevPage === data.prevPage) {
|
|
520
|
-
pageDesc = desc;
|
|
521
|
-
break;
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
if (!pageDesc) {
|
|
525
|
-
pageDesc = data;
|
|
526
|
-
pageBreaks.push(pageDesc);
|
|
527
|
-
}
|
|
528
|
-
pageDesc.prevY = Math.max(pageDesc.prevY, data.prevY);
|
|
529
|
-
pageDesc.y = Math.min(pageDesc.y, data.y);
|
|
530
|
-
};
|
|
531
640
|
const isUnbreakableRow = dontBreakRows || rowIndex <= rowsWithoutPageBreak - 1;
|
|
532
641
|
let pageBreaks = [];
|
|
642
|
+
let pageBreaksByRowSpan = [];
|
|
533
643
|
let positions = [];
|
|
534
644
|
let willBreakByHeight = false;
|
|
535
|
-
|
|
645
|
+
widths = widths || cells;
|
|
536
646
|
|
|
537
647
|
// Check if row should break by height
|
|
538
648
|
if (!isUnbreakableRow && height > this.writer.context().availableHeight) {
|
|
539
649
|
willBreakByHeight = true;
|
|
540
650
|
}
|
|
541
|
-
|
|
651
|
+
|
|
542
652
|
// Use the marginX if we are in a top level table/column (not nested)
|
|
543
653
|
const marginXParent = this.nestedLevel === 1 ? marginX : null;
|
|
544
|
-
|
|
654
|
+
const _bottomByPage = tableNode ? tableNode._bottomByPage : null;
|
|
655
|
+
this.writer.context().beginColumnGroup(marginXParent, _bottomByPage);
|
|
545
656
|
for (let i = 0, l = cells.length; i < l; i++) {
|
|
546
|
-
let
|
|
657
|
+
let cell = cells[i];
|
|
658
|
+
|
|
659
|
+
// Page change handler
|
|
660
|
+
const storePageBreakClosure = data => {
|
|
661
|
+
const startsRowSpan = cell.rowSpan && cell.rowSpan > 1;
|
|
662
|
+
if (startsRowSpan) {
|
|
663
|
+
data.rowSpan = cell.rowSpan;
|
|
664
|
+
}
|
|
665
|
+
data.rowIndex = rowIndex;
|
|
666
|
+
this._storePageBreakData(data, startsRowSpan, pageBreaks, tableNode);
|
|
667
|
+
};
|
|
668
|
+
this.writer.addListener('pageChanged', storePageBreakClosure);
|
|
547
669
|
let width = widths[i]._calcWidth;
|
|
548
|
-
let leftOffset =
|
|
549
|
-
if
|
|
550
|
-
|
|
670
|
+
let leftOffset = this._colLeftOffset(i, gaps);
|
|
671
|
+
// Check if exists and retrieve the cell that started the rowspan in case we are in the cell just after
|
|
672
|
+
let startingSpanCell = this._findStartingRowSpanCell(cells, i);
|
|
673
|
+
if (cell.colSpan && cell.colSpan > 1) {
|
|
674
|
+
for (let j = 1; j < cell.colSpan; j++) {
|
|
551
675
|
width += widths[++i]._calcWidth + gaps[i];
|
|
552
676
|
}
|
|
553
677
|
}
|
|
554
678
|
|
|
555
679
|
// if rowspan starts in this cell, we retrieve the last cell affected by the rowspan
|
|
556
|
-
const
|
|
557
|
-
if (
|
|
680
|
+
const rowSpanEndingCell = this._getRowSpanEndingCell(tableBody, rowIndex, cell, i);
|
|
681
|
+
if (rowSpanEndingCell) {
|
|
558
682
|
// We store a reference of the ending cell in the first cell of the rowspan
|
|
559
|
-
|
|
560
|
-
|
|
683
|
+
cell._endingCell = rowSpanEndingCell;
|
|
684
|
+
cell._endingCell._startingRowSpanY = cell._startingRowSpanY;
|
|
561
685
|
}
|
|
562
686
|
|
|
563
|
-
//
|
|
564
|
-
let
|
|
565
|
-
let endingSpanCell = null;
|
|
687
|
+
// If we are after a cell that started a rowspan
|
|
688
|
+
let endOfRowSpanCell = null;
|
|
566
689
|
if (startingSpanCell && startingSpanCell._endingCell) {
|
|
567
690
|
// Reference to the last cell of the rowspan
|
|
568
|
-
|
|
691
|
+
endOfRowSpanCell = startingSpanCell._endingCell;
|
|
569
692
|
// Store if we are in an unbreakable block when we save the context and the originalX
|
|
570
693
|
if (this.writer.transactionLevel > 0) {
|
|
571
|
-
|
|
572
|
-
|
|
694
|
+
endOfRowSpanCell._isUnbreakableContext = true;
|
|
695
|
+
endOfRowSpanCell._originalXOffset = this.writer.originalX;
|
|
573
696
|
}
|
|
574
697
|
}
|
|
575
698
|
|
|
576
699
|
// We pass the endingSpanCell reference to store the context just after processing rowspan cell
|
|
577
|
-
this.writer.context().beginColumn(width, leftOffset,
|
|
578
|
-
if (!
|
|
579
|
-
this.processNode(
|
|
580
|
-
|
|
581
|
-
|
|
700
|
+
this.writer.context().beginColumn(width, leftOffset, endOfRowSpanCell);
|
|
701
|
+
if (!cell._span) {
|
|
702
|
+
this.processNode(cell);
|
|
703
|
+
this.writer.context().updateBottomByPage();
|
|
704
|
+
addAll(positions, cell.positions);
|
|
705
|
+
} else if (cell._columnEndingContext) {
|
|
582
706
|
let discountY = 0;
|
|
583
707
|
if (dontBreakRows) {
|
|
584
708
|
// Calculate how many points we have to discount to Y when dontBreakRows and rowSpan are combined
|
|
585
709
|
const ctxBeforeRowSpanLastRow = this.writer.contextStack[this.writer.contextStack.length - 1];
|
|
586
|
-
discountY = ctxBeforeRowSpanLastRow.y -
|
|
710
|
+
discountY = ctxBeforeRowSpanLastRow.y - cell._startingRowSpanY;
|
|
587
711
|
}
|
|
588
712
|
let originalXOffset = 0;
|
|
589
713
|
// If context was saved from an unbreakable block and we are not in an unbreakable block anymore
|
|
590
714
|
// We have to sum the originalX (X before starting unbreakable block) to X
|
|
591
|
-
if (
|
|
592
|
-
originalXOffset =
|
|
715
|
+
if (cell._isUnbreakableContext && !this.writer.transactionLevel) {
|
|
716
|
+
originalXOffset = cell._originalXOffset;
|
|
593
717
|
}
|
|
594
718
|
// row-span ending
|
|
595
719
|
// Recover the context after processing the rowspanned cell
|
|
596
|
-
this.writer.context().markEnding(
|
|
720
|
+
this.writer.context().markEnding(cell, originalXOffset, discountY);
|
|
597
721
|
}
|
|
722
|
+
this.writer.removeListener('pageChanged', storePageBreakClosure);
|
|
598
723
|
}
|
|
599
724
|
|
|
600
725
|
// Check if last cell is part of a span
|
|
@@ -607,7 +732,7 @@ class LayoutBuilder {
|
|
|
607
732
|
// Previous column cell is part of a span
|
|
608
733
|
} else if (lastColumn._span === true) {
|
|
609
734
|
// We get the cell that started the span where we set a reference to the ending cell
|
|
610
|
-
const startingSpanCell = this.
|
|
735
|
+
const startingSpanCell = this._findStartingRowSpanCell(cells, cells.length);
|
|
611
736
|
if (startingSpanCell) {
|
|
612
737
|
// Context will be stored here (ending cell)
|
|
613
738
|
endingSpanCell = startingSpanCell._endingCell;
|
|
@@ -620,36 +745,22 @@ class LayoutBuilder {
|
|
|
620
745
|
}
|
|
621
746
|
}
|
|
622
747
|
|
|
623
|
-
// If there are page breaks in this row, update data with prevY of last cell
|
|
624
|
-
updatePageBreakData(this.writer.context().page, this.writer.context().y);
|
|
625
|
-
|
|
626
748
|
// If content did not break page, check if we should break by height
|
|
627
|
-
if (!isUnbreakableRow && pageBreaks.length === 0
|
|
749
|
+
if (willBreakByHeight && !isUnbreakableRow && pageBreaks.length === 0) {
|
|
628
750
|
this.writer.context().moveDown(this.writer.context().availableHeight);
|
|
629
751
|
this.writer.moveToNextPage();
|
|
630
752
|
}
|
|
631
|
-
this.writer.context().completeColumnGroup(height, endingSpanCell);
|
|
632
|
-
|
|
753
|
+
const bottomByPage = this.writer.context().completeColumnGroup(height, endingSpanCell);
|
|
754
|
+
if (tableNode) {
|
|
755
|
+
tableNode._bottomByPage = bottomByPage;
|
|
756
|
+
// If there are page breaks in this row, update data with prevY of last cell
|
|
757
|
+
this._updatePageBreaksData(pageBreaks, tableNode, rowIndex);
|
|
758
|
+
}
|
|
633
759
|
return {
|
|
760
|
+
pageBreaksBySpan: pageBreaksByRowSpan,
|
|
634
761
|
pageBreaks: pageBreaks,
|
|
635
762
|
positions: positions
|
|
636
763
|
};
|
|
637
|
-
function colLeftOffset(i) {
|
|
638
|
-
if (gaps && gaps.length > i) {
|
|
639
|
-
return gaps[i];
|
|
640
|
-
}
|
|
641
|
-
return 0;
|
|
642
|
-
}
|
|
643
|
-
function getEndingCell(column, columnIndex) {
|
|
644
|
-
if (column.rowSpan && column.rowSpan > 1) {
|
|
645
|
-
let endingRow = rowIndex + column.rowSpan - 1;
|
|
646
|
-
if (endingRow >= tableBody.length) {
|
|
647
|
-
throw new Error(`Row span for column ${columnIndex} (with indexes starting from 0) exceeded row count`);
|
|
648
|
-
}
|
|
649
|
-
return tableBody[endingRow][columnIndex];
|
|
650
|
-
}
|
|
651
|
-
return null;
|
|
652
|
-
}
|
|
653
764
|
}
|
|
654
765
|
|
|
655
766
|
// lists
|
|
@@ -715,6 +826,7 @@ class LayoutBuilder {
|
|
|
715
826
|
if (height === 'auto') {
|
|
716
827
|
height = undefined;
|
|
717
828
|
}
|
|
829
|
+
const pageBeforeProcessing = this.writer.context().page;
|
|
718
830
|
let result = this.processRow({
|
|
719
831
|
marginX: tableNode._margin ? [tableNode._margin[0], tableNode._margin[2]] : [0, 0],
|
|
720
832
|
dontBreakRows: processor.dontBreakRows,
|
|
@@ -723,10 +835,19 @@ class LayoutBuilder {
|
|
|
723
835
|
widths: tableNode.table.widths,
|
|
724
836
|
gaps: tableNode._offsets.offsets,
|
|
725
837
|
tableBody: tableNode.table.body,
|
|
838
|
+
tableNode,
|
|
726
839
|
rowIndex: i,
|
|
727
840
|
height
|
|
728
841
|
});
|
|
729
842
|
addAll(tableNode.positions, result.positions);
|
|
843
|
+
if (!result.pageBreaks || result.pageBreaks.length === 0) {
|
|
844
|
+
const breaksBySpan = tableNode && tableNode._breaksBySpan || null;
|
|
845
|
+
const breakBySpanData = this._findSameRowPageBreakByRowSpanData(breaksBySpan, pageBeforeProcessing, i);
|
|
846
|
+
if (breakBySpanData) {
|
|
847
|
+
const finalBreakBySpanData = this._getPageBreakListBySpan(tableNode, breakBySpanData.prevPage, i);
|
|
848
|
+
result.pageBreaks.push(finalBreakBySpanData);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
730
851
|
processor.endRow(i, this.writer, result.pageBreaks);
|
|
731
852
|
}
|
|
732
853
|
processor.endTable(this.writer);
|
package/js/PageElementWriter.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
4
|
exports.default = void 0;
|
|
5
5
|
var _ElementWriter = _interopRequireDefault(require("./ElementWriter"));
|
|
6
|
+
var _PageSize = require("./PageSize");
|
|
7
|
+
var _DocumentContext = _interopRequireDefault(require("./DocumentContext"));
|
|
6
8
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
9
|
/**
|
|
8
10
|
* An extended ElementWriter which can handle:
|
|
@@ -13,6 +15,9 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
13
15
|
* whole block will be rendered on the same page)
|
|
14
16
|
*/
|
|
15
17
|
class PageElementWriter extends _ElementWriter.default {
|
|
18
|
+
/**
|
|
19
|
+
* @param {DocumentContext} context
|
|
20
|
+
*/
|
|
16
21
|
constructor(context) {
|
|
17
22
|
super(context);
|
|
18
23
|
this.transactionLevel = 0;
|
|
@@ -68,6 +73,16 @@ class PageElementWriter extends _ElementWriter.default {
|
|
|
68
73
|
y: this.context().y
|
|
69
74
|
});
|
|
70
75
|
}
|
|
76
|
+
addPage(pageSize, pageOrientation, pageMargin) {
|
|
77
|
+
let prevPage = this.page;
|
|
78
|
+
let prevY = this.y;
|
|
79
|
+
this.context().addPage((0, _PageSize.normalizePageSize)(pageSize, pageOrientation), (0, _PageSize.normalizePageMargin)(pageMargin));
|
|
80
|
+
this.emit('pageChanged', {
|
|
81
|
+
prevPage: prevPage,
|
|
82
|
+
prevY: prevY,
|
|
83
|
+
y: this.context().y
|
|
84
|
+
});
|
|
85
|
+
}
|
|
71
86
|
beginUnbreakableBlock(width, height) {
|
|
72
87
|
if (this.transactionLevel++ === 0) {
|
|
73
88
|
this.originalX = this.context().x;
|