pdfmake 0.3.0-beta.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -40
- package/LICENSE +1 -1
- package/README.md +78 -85
- package/build/pdfmake.js +60308 -68400
- 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 +4 -4
- package/fonts/Roboto/Roboto-Italic.ttf +0 -0
- package/fonts/Roboto/Roboto-Medium.ttf +0 -0
- package/fonts/Roboto/Roboto-MediumItalic.ttf +0 -0
- package/fonts/Roboto/Roboto-Regular.ttf +0 -0
- package/js/3rd-party/svg-to-pdfkit/source.js +3626 -0
- package/js/3rd-party/svg-to-pdfkit.js +7 -0
- package/js/DocMeasure.js +645 -0
- package/js/DocPreprocessor.js +253 -0
- package/js/DocumentContext.js +305 -0
- package/js/ElementWriter.js +354 -0
- package/js/LayoutBuilder.js +1105 -0
- package/js/Line.js +105 -0
- package/js/OutputDocument.js +64 -0
- package/js/OutputDocumentServer.js +22 -0
- package/js/PDFDocument.js +144 -0
- package/js/PageElementWriter.js +155 -0
- package/js/PageSize.js +74 -0
- package/js/Printer.js +291 -0
- package/js/Renderer.js +383 -0
- package/js/SVGMeasure.js +69 -0
- package/js/StyleContextStack.js +168 -0
- package/js/TableProcessor.js +548 -0
- package/js/TextBreaker.js +166 -0
- package/js/TextDecorator.js +143 -0
- package/js/TextInlines.js +206 -0
- package/js/URLResolver.js +43 -0
- package/js/base.js +52 -0
- package/js/browser-extensions/OutputDocumentBrowser.js +81 -0
- package/js/browser-extensions/fonts/Roboto.js +38 -0
- package/js/browser-extensions/index.js +53 -0
- package/js/browser-extensions/pdfMake.js +3 -0
- package/js/browser-extensions/standard-fonts/Courier.js +38 -0
- package/js/browser-extensions/standard-fonts/Helvetica.js +38 -0
- package/js/browser-extensions/standard-fonts/Symbol.js +23 -0
- package/js/browser-extensions/standard-fonts/Times.js +38 -0
- package/js/browser-extensions/standard-fonts/ZapfDingbats.js +23 -0
- package/js/browser-extensions/virtual-fs-cjs.js +3 -0
- package/js/columnCalculator.js +148 -0
- package/js/helpers/node.js +98 -0
- package/js/helpers/tools.js +46 -0
- package/js/helpers/variableType.js +59 -0
- package/js/index.js +15 -0
- package/js/qrEnc.js +721 -0
- package/js/standardPageSizes.js +56 -0
- package/js/tableLayouts.js +98 -0
- package/js/virtual-fs.js +60 -0
- package/package.json +25 -24
- package/src/DocMeasure.js +28 -7
- package/src/DocPreprocessor.js +25 -6
- package/src/DocumentContext.js +62 -33
- package/src/ElementWriter.js +30 -7
- package/src/LayoutBuilder.js +557 -120
- package/src/OutputDocument.js +23 -37
- package/src/OutputDocumentServer.js +6 -11
- package/src/PDFDocument.js +1 -1
- package/src/PageElementWriter.js +21 -2
- package/src/Printer.js +134 -131
- package/src/Renderer.js +13 -15
- package/src/SVGMeasure.js +2 -2
- package/src/StyleContextStack.js +7 -44
- package/src/TableProcessor.js +62 -22
- package/src/TextBreaker.js +24 -5
- package/src/TextInlines.js +1 -1
- package/src/URLResolver.js +24 -58
- package/src/base.js +1 -1
- package/src/browser-extensions/OutputDocumentBrowser.js +33 -71
- package/src/browser-extensions/index.js +3 -3
- package/src/browser-extensions/pdfMake.js +0 -14
- package/src/browser-extensions/standard-fonts/Courier.js +4 -4
- package/src/browser-extensions/standard-fonts/Helvetica.js +4 -4
- package/src/browser-extensions/standard-fonts/Symbol.js +1 -1
- package/src/browser-extensions/standard-fonts/Times.js +4 -4
- package/src/browser-extensions/standard-fonts/ZapfDingbats.js +1 -1
- package/src/columnCalculator.js +24 -3
- package/src/helpers/tools.js +5 -0
- package/src/helpers/variableType.js +11 -0
- package/src/index.js +1 -1
- package/standard-fonts/Helvetica.js +0 -1
- package/src/browser-extensions/URLBrowserResolver.js +0 -84
package/src/LayoutBuilder.js
CHANGED
|
@@ -7,7 +7,7 @@ import TableProcessor from './TableProcessor';
|
|
|
7
7
|
import Line from './Line';
|
|
8
8
|
import { isString, isValue, isNumber } from './helpers/variableType';
|
|
9
9
|
import { stringifyNode, getNodeId } from './helpers/node';
|
|
10
|
-
import { pack, offsetVector } from './helpers/tools';
|
|
10
|
+
import { pack, offsetVector, convertToDynamicContent } from './helpers/tools';
|
|
11
11
|
import TextInlines from './TextInlines';
|
|
12
12
|
import StyleContextStack from './StyleContextStack';
|
|
13
13
|
|
|
@@ -32,6 +32,7 @@ class LayoutBuilder {
|
|
|
32
32
|
this.pageMargins = pageMargins;
|
|
33
33
|
this.svgMeasure = svgMeasure;
|
|
34
34
|
this.tableLayouts = {};
|
|
35
|
+
this.nestedLevel = 0;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
registerTableLayouts(tableLayouts) {
|
|
@@ -71,7 +72,17 @@ class LayoutBuilder {
|
|
|
71
72
|
return false;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
|
|
75
|
+
const hasRenderableContent = node => {
|
|
76
|
+
if (!node || node.positions.length === 0) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
if (node.text === '' && !node.listMarker) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
return true;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
linearNodeList = linearNodeList.filter(hasRenderableContent);
|
|
75
86
|
|
|
76
87
|
linearNodeList.forEach(node => {
|
|
77
88
|
let nodeInfo = {};
|
|
@@ -167,23 +178,42 @@ class LayoutBuilder {
|
|
|
167
178
|
watermark
|
|
168
179
|
) {
|
|
169
180
|
|
|
181
|
+
const isNecessaryAddFirstPage = (docStructure) => {
|
|
182
|
+
if (docStructure.stack && docStructure.stack.length > 0 && docStructure.stack[0].section) {
|
|
183
|
+
return false;
|
|
184
|
+
} else if (docStructure.section) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return true;
|
|
189
|
+
};
|
|
190
|
+
|
|
170
191
|
this.linearNodeList = [];
|
|
171
192
|
docStructure = this.docPreprocessor.preprocessDocument(docStructure);
|
|
172
193
|
docStructure = this.docMeasure.measureDocument(docStructure);
|
|
173
194
|
|
|
174
|
-
this.writer = new PageElementWriter(
|
|
175
|
-
new DocumentContext(this.pageSize, this.pageMargins));
|
|
195
|
+
this.writer = new PageElementWriter(new DocumentContext());
|
|
176
196
|
|
|
177
|
-
this.writer.context().addListener('pageAdded', () => {
|
|
178
|
-
|
|
197
|
+
this.writer.context().addListener('pageAdded', (page) => {
|
|
198
|
+
let backgroundGetter = background;
|
|
199
|
+
if (page.customProperties['background'] || page.customProperties['background'] === null) {
|
|
200
|
+
backgroundGetter = page.customProperties['background'];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
this.addBackground(backgroundGetter);
|
|
179
204
|
});
|
|
180
205
|
|
|
181
|
-
|
|
206
|
+
if (isNecessaryAddFirstPage(docStructure)) {
|
|
207
|
+
this.writer.addPage(
|
|
208
|
+
this.pageSize,
|
|
209
|
+
null,
|
|
210
|
+
this.pageMargins
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
182
214
|
this.processNode(docStructure);
|
|
183
215
|
this.addHeadersAndFooters(header, footer);
|
|
184
|
-
|
|
185
|
-
this.addWatermark(watermark, pdfDocument, defaultStyle);
|
|
186
|
-
}
|
|
216
|
+
this.addWatermark(watermark, pdfDocument, defaultStyle);
|
|
187
217
|
|
|
188
218
|
return { pages: this.writer.context().pages, linearNodeList: this.linearNodeList };
|
|
189
219
|
}
|
|
@@ -198,31 +228,37 @@ class LayoutBuilder {
|
|
|
198
228
|
|
|
199
229
|
if (pageBackground) {
|
|
200
230
|
this.writer.beginUnbreakableBlock(pageSize.width, pageSize.height);
|
|
201
|
-
pageBackground = this.docPreprocessor.
|
|
202
|
-
this.processNode(this.docMeasure.
|
|
231
|
+
pageBackground = this.docPreprocessor.preprocessBlock(pageBackground);
|
|
232
|
+
this.processNode(this.docMeasure.measureBlock(pageBackground));
|
|
203
233
|
this.writer.commitUnbreakableBlock(0, 0);
|
|
204
234
|
context.backgroundLength[context.page] += pageBackground.positions.length;
|
|
205
235
|
}
|
|
206
236
|
}
|
|
207
237
|
|
|
208
|
-
|
|
209
|
-
this.addDynamicRepeatable(() => // copy to new object
|
|
210
|
-
JSON.parse(JSON.stringify(headerOrFooter)), sizeFunction);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
addDynamicRepeatable(nodeGetter, sizeFunction) {
|
|
238
|
+
addDynamicRepeatable(nodeGetter, sizeFunction, customPropertyName) {
|
|
214
239
|
let pages = this.writer.context().pages;
|
|
215
240
|
|
|
216
241
|
for (let pageIndex = 0, l = pages.length; pageIndex < l; pageIndex++) {
|
|
217
242
|
this.writer.context().page = pageIndex;
|
|
218
243
|
|
|
219
|
-
let
|
|
244
|
+
let customProperties = this.writer.context().getCurrentPage().customProperties;
|
|
245
|
+
|
|
246
|
+
let pageNodeGetter = nodeGetter;
|
|
247
|
+
if (customProperties[customPropertyName] || customProperties[customPropertyName] === null) {
|
|
248
|
+
pageNodeGetter = customProperties[customPropertyName];
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if ((typeof pageNodeGetter === 'undefined') || (pageNodeGetter === null)) {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
let node = pageNodeGetter(pageIndex + 1, l, this.writer.context().pages[pageIndex].pageSize);
|
|
220
256
|
|
|
221
257
|
if (node) {
|
|
222
|
-
let sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.pageMargins);
|
|
258
|
+
let sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.writer.context().getCurrentPage().pageMargins);
|
|
223
259
|
this.writer.beginUnbreakableBlock(sizes.width, sizes.height);
|
|
224
|
-
node = this.docPreprocessor.
|
|
225
|
-
this.processNode(this.docMeasure.
|
|
260
|
+
node = this.docPreprocessor.preprocessBlock(node);
|
|
261
|
+
this.processNode(this.docMeasure.measureBlock(node));
|
|
226
262
|
this.writer.commitUnbreakableBlock(sizes.x, sizes.y);
|
|
227
263
|
}
|
|
228
264
|
}
|
|
@@ -243,58 +279,62 @@ class LayoutBuilder {
|
|
|
243
279
|
height: pageMargins.bottom
|
|
244
280
|
});
|
|
245
281
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
} else if (header) {
|
|
249
|
-
this.addStaticRepeatable(header, headerSizeFct);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (typeof footer === 'function') {
|
|
253
|
-
this.addDynamicRepeatable(footer, footerSizeFct);
|
|
254
|
-
} else if (footer) {
|
|
255
|
-
this.addStaticRepeatable(footer, footerSizeFct);
|
|
256
|
-
}
|
|
282
|
+
this.addDynamicRepeatable(header, headerSizeFct, 'header');
|
|
283
|
+
this.addDynamicRepeatable(footer, footerSizeFct, 'footer');
|
|
257
284
|
}
|
|
258
285
|
|
|
259
286
|
addWatermark(watermark, pdfDocument, defaultStyle) {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
287
|
+
let pages = this.writer.context().pages;
|
|
288
|
+
for (let i = 0, l = pages.length; i < l; i++) {
|
|
289
|
+
let pageWatermark = watermark;
|
|
290
|
+
if (pages[i].customProperties['watermark'] || pages[i].customProperties['watermark'] === null) {
|
|
291
|
+
pageWatermark = pages[i].customProperties['watermark'];
|
|
292
|
+
}
|
|
263
293
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
294
|
+
if (pageWatermark === undefined || pageWatermark === null) {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
267
297
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
watermark.opacity = isNumber(watermark.opacity) ? watermark.opacity : 0.6;
|
|
272
|
-
watermark.bold = watermark.bold || false;
|
|
273
|
-
watermark.italics = watermark.italics || false;
|
|
274
|
-
watermark.angle = isValue(watermark.angle) ? watermark.angle : null;
|
|
298
|
+
if (isString(pageWatermark)) {
|
|
299
|
+
pageWatermark = { 'text': pageWatermark };
|
|
300
|
+
}
|
|
275
301
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
302
|
+
if (!pageWatermark.text) { // empty watermark text
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
279
305
|
|
|
280
|
-
|
|
281
|
-
watermark.fontSize = getWatermarkFontSize(this.pageSize, watermark, pdfDocument);
|
|
306
|
+
pages[i].watermark = getWatermarkObject({ ...pageWatermark }, pages[i].pageSize, pdfDocument, defaultStyle);
|
|
282
307
|
}
|
|
283
308
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
309
|
+
function getWatermarkObject(watermark, pageSize, pdfDocument, defaultStyle) {
|
|
310
|
+
watermark.font = watermark.font || defaultStyle.font || 'Roboto';
|
|
311
|
+
watermark.fontSize = watermark.fontSize || 'auto';
|
|
312
|
+
watermark.color = watermark.color || 'black';
|
|
313
|
+
watermark.opacity = isNumber(watermark.opacity) ? watermark.opacity : 0.6;
|
|
314
|
+
watermark.bold = watermark.bold || false;
|
|
315
|
+
watermark.italics = watermark.italics || false;
|
|
316
|
+
watermark.angle = isValue(watermark.angle) ? watermark.angle : null;
|
|
317
|
+
|
|
318
|
+
if (watermark.angle === null) {
|
|
319
|
+
watermark.angle = Math.atan2(pageSize.height, pageSize.width) * -180 / Math.PI;
|
|
320
|
+
}
|
|
292
321
|
|
|
293
|
-
|
|
322
|
+
if (watermark.fontSize === 'auto') {
|
|
323
|
+
watermark.fontSize = getWatermarkFontSize(pageSize, watermark, pdfDocument);
|
|
324
|
+
}
|
|
294
325
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
326
|
+
let watermarkObject = {
|
|
327
|
+
text: watermark.text,
|
|
328
|
+
font: pdfDocument.provideFont(watermark.font, watermark.bold, watermark.italics),
|
|
329
|
+
fontSize: watermark.fontSize,
|
|
330
|
+
color: watermark.color,
|
|
331
|
+
opacity: watermark.opacity,
|
|
332
|
+
angle: watermark.angle
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
watermarkObject._size = getWatermarkSize(watermark, pdfDocument);
|
|
336
|
+
|
|
337
|
+
return watermarkObject;
|
|
298
338
|
}
|
|
299
339
|
|
|
300
340
|
function getWatermarkSize(watermark, pdfDocument) {
|
|
@@ -369,16 +409,50 @@ class LayoutBuilder {
|
|
|
369
409
|
}
|
|
370
410
|
}
|
|
371
411
|
|
|
372
|
-
|
|
373
|
-
|
|
412
|
+
const isDetachedBlock = node.relativePosition || node.absolutePosition;
|
|
413
|
+
|
|
414
|
+
// Detached nodes have no margins, their position is only determined by 'x' and 'y'
|
|
415
|
+
if (margin && !isDetachedBlock) {
|
|
416
|
+
const availableHeight = this.writer.context().availableHeight;
|
|
417
|
+
// If top margin is bigger than available space, move to next page
|
|
418
|
+
// Necessary for nodes inside tables
|
|
419
|
+
if (availableHeight - margin[1] < 0) {
|
|
420
|
+
// Consume the whole available space
|
|
421
|
+
this.writer.context().moveDown(availableHeight);
|
|
422
|
+
this.writer.moveToNextPage(node.pageOrientation);
|
|
423
|
+
/**
|
|
424
|
+
* TODO - Something to consider:
|
|
425
|
+
* Right now the node starts at the top of next page (after header)
|
|
426
|
+
* Another option would be to apply just the top margin that has not been consumed in the page before
|
|
427
|
+
* It would something like: this.write.context().moveDown(margin[1] - availableHeight)
|
|
428
|
+
*/
|
|
429
|
+
} else {
|
|
430
|
+
this.writer.context().moveDown(margin[1]);
|
|
431
|
+
}
|
|
432
|
+
// Apply lateral margins
|
|
374
433
|
this.writer.context().addMargin(margin[0], margin[2]);
|
|
375
434
|
}
|
|
376
|
-
|
|
377
435
|
callback();
|
|
378
436
|
|
|
379
|
-
|
|
437
|
+
// Detached nodes have no margins, their position is only determined by 'x' and 'y'
|
|
438
|
+
if (margin && !isDetachedBlock) {
|
|
439
|
+
const availableHeight = this.writer.context().availableHeight;
|
|
440
|
+
// If bottom margin is bigger than available space, move to next page
|
|
441
|
+
// Necessary for nodes inside tables
|
|
442
|
+
if (availableHeight - margin[3] < 0) {
|
|
443
|
+
this.writer.context().moveDown(availableHeight);
|
|
444
|
+
this.writer.moveToNextPage(node.pageOrientation);
|
|
445
|
+
/**
|
|
446
|
+
* TODO - Something to consider:
|
|
447
|
+
* Right now next node starts at the top of next page (after header)
|
|
448
|
+
* Another option would be to apply the bottom margin that has not been consumed in the next page?
|
|
449
|
+
* It would something like: this.write.context().moveDown(margin[3] - availableHeight)
|
|
450
|
+
*/
|
|
451
|
+
} else {
|
|
452
|
+
this.writer.context().moveDown(margin[3]);
|
|
453
|
+
}
|
|
454
|
+
// Apply lateral margins
|
|
380
455
|
this.writer.context().addMargin(-margin[0], -margin[2]);
|
|
381
|
-
this.writer.context().moveDown(margin[3]);
|
|
382
456
|
}
|
|
383
457
|
|
|
384
458
|
if (node.pageBreak === 'after') {
|
|
@@ -419,6 +493,8 @@ class LayoutBuilder {
|
|
|
419
493
|
|
|
420
494
|
if (node.stack) {
|
|
421
495
|
this.processVerticalContainer(node);
|
|
496
|
+
} else if (node.section) {
|
|
497
|
+
this.processSection(node);
|
|
422
498
|
} else if (node.columns) {
|
|
423
499
|
this.processColumns(node);
|
|
424
500
|
} else if (node.ul) {
|
|
@@ -465,8 +541,78 @@ class LayoutBuilder {
|
|
|
465
541
|
}, this);
|
|
466
542
|
}
|
|
467
543
|
|
|
544
|
+
// section
|
|
545
|
+
processSection(sectionNode) {
|
|
546
|
+
// TODO: properties
|
|
547
|
+
|
|
548
|
+
let page = this.writer.context().getCurrentPage();
|
|
549
|
+
if (!page || (page && page.items.length)) { // move to new empty page
|
|
550
|
+
// page definition inherit from current page
|
|
551
|
+
if (sectionNode.pageSize === 'inherit') {
|
|
552
|
+
sectionNode.pageSize = page ? { width: page.pageSize.width, height: page.pageSize.height } : undefined;
|
|
553
|
+
}
|
|
554
|
+
if (sectionNode.pageOrientation === 'inherit') {
|
|
555
|
+
sectionNode.pageOrientation = page ? page.pageSize.orientation : undefined;
|
|
556
|
+
}
|
|
557
|
+
if (sectionNode.pageMargins === 'inherit') {
|
|
558
|
+
sectionNode.pageMargins = page ? page.pageMargins : undefined;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (sectionNode.header === 'inherit') {
|
|
562
|
+
sectionNode.header = page ? page.customProperties.header : undefined;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (sectionNode.footer === 'inherit') {
|
|
566
|
+
sectionNode.footer = page ? page.customProperties.footer : undefined;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if (sectionNode.background === 'inherit') {
|
|
570
|
+
sectionNode.background = page ? page.customProperties.background : undefined;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (sectionNode.watermark === 'inherit') {
|
|
574
|
+
sectionNode.watermark = page ? page.customProperties.watermark : undefined;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (sectionNode.header && typeof sectionNode.header !== 'function' && sectionNode.header !== null) {
|
|
578
|
+
sectionNode.header = convertToDynamicContent(sectionNode.header);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (sectionNode.footer && typeof sectionNode.footer !== 'function' && sectionNode.footer !== null) {
|
|
582
|
+
sectionNode.footer = convertToDynamicContent(sectionNode.footer);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
let customProperties = {};
|
|
586
|
+
if (typeof sectionNode.header !== 'undefined') {
|
|
587
|
+
customProperties.header = sectionNode.header;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (typeof sectionNode.footer !== 'undefined') {
|
|
591
|
+
customProperties.footer = sectionNode.footer;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
if (typeof sectionNode.background !== 'undefined') {
|
|
595
|
+
customProperties.background = sectionNode.background;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (typeof sectionNode.watermark !== 'undefined') {
|
|
599
|
+
customProperties.watermark = sectionNode.watermark;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
this.writer.addPage(
|
|
603
|
+
sectionNode.pageSize || this.pageSize,
|
|
604
|
+
sectionNode.pageOrientation,
|
|
605
|
+
sectionNode.pageMargins || this.pageMargins,
|
|
606
|
+
customProperties
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
this.processNode(sectionNode.section);
|
|
611
|
+
}
|
|
612
|
+
|
|
468
613
|
// columns
|
|
469
614
|
processColumns(columnNode) {
|
|
615
|
+
this.nestedLevel++;
|
|
470
616
|
let columns = columnNode.columns;
|
|
471
617
|
let availableWidth = this.writer.context().availableWidth;
|
|
472
618
|
let gaps = gapArray(columnNode._gap);
|
|
@@ -476,9 +622,17 @@ class LayoutBuilder {
|
|
|
476
622
|
}
|
|
477
623
|
|
|
478
624
|
ColumnCalculator.buildColumnWidths(columns, availableWidth);
|
|
479
|
-
let result = this.processRow(
|
|
625
|
+
let result = this.processRow({
|
|
626
|
+
marginX: columnNode._margin ? [columnNode._margin[0], columnNode._margin[2]] : [0, 0],
|
|
627
|
+
cells: columns,
|
|
628
|
+
widths: columns,
|
|
629
|
+
gaps
|
|
630
|
+
});
|
|
480
631
|
addAll(columnNode.positions, result.positions);
|
|
481
|
-
|
|
632
|
+
this.nestedLevel--;
|
|
633
|
+
if (this.nestedLevel === 0) {
|
|
634
|
+
this.writer.context().resetMarginXTopParent();
|
|
635
|
+
}
|
|
482
636
|
function gapArray(gap) {
|
|
483
637
|
if (!gap) {
|
|
484
638
|
return null;
|
|
@@ -495,80 +649,306 @@ class LayoutBuilder {
|
|
|
495
649
|
}
|
|
496
650
|
}
|
|
497
651
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
652
|
+
/**
|
|
653
|
+
* Searches for a cell in the same row that starts a rowspan and is positioned immediately before the current cell.
|
|
654
|
+
* Alternatively, it finds a cell where the colspan initiating the rowspan extends to the cell just before the current one.
|
|
655
|
+
*
|
|
656
|
+
* @param {Array<object>} arr - An array representing cells in a row.
|
|
657
|
+
* @param {number} i - The index of the current cell to search backward from.
|
|
658
|
+
* @returns {object|null} The starting cell of the rowspan if found; otherwise, `null`.
|
|
659
|
+
*/
|
|
660
|
+
_findStartingRowSpanCell(arr, i) {
|
|
661
|
+
let requiredColspan = 1;
|
|
662
|
+
for (let index = i - 1; index >= 0; index--) {
|
|
663
|
+
if (!arr[index]._span) {
|
|
664
|
+
if (arr[index].rowSpan > 1 && (arr[index].colSpan || 1) === requiredColspan) {
|
|
665
|
+
return arr[index];
|
|
666
|
+
} else {
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
requiredColspan++;
|
|
671
|
+
}
|
|
672
|
+
return null;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Retrieves a page break description for a specified page from a list of page breaks.
|
|
677
|
+
*
|
|
678
|
+
* @param {Array<object>} pageBreaks - An array of page break descriptions, each containing `prevPage` properties.
|
|
679
|
+
* @param {number} page - The page number to find the associated page break for.
|
|
680
|
+
* @returns {object|undefined} The page break description object for the specified page if found; otherwise, `undefined`.
|
|
681
|
+
*/
|
|
682
|
+
_getPageBreak(pageBreaks, page) {
|
|
683
|
+
return pageBreaks.find(desc => desc.prevPage === page);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
_getPageBreakListBySpan(tableNode, page, rowIndex) {
|
|
687
|
+
if (!tableNode || !tableNode._breaksBySpan) {
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
const breaksList = tableNode._breaksBySpan.filter(desc => desc.prevPage === page && rowIndex <= desc.rowIndexOfSpanEnd);
|
|
691
|
+
|
|
692
|
+
let y = Number.MAX_VALUE,
|
|
693
|
+
prevY = Number.MIN_VALUE;
|
|
694
|
+
|
|
695
|
+
breaksList.forEach(b => {
|
|
696
|
+
prevY = Math.max(b.prevY, prevY);
|
|
697
|
+
y = Math.min(b.y, y);
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
return {
|
|
701
|
+
prevPage: page,
|
|
702
|
+
prevY: prevY,
|
|
703
|
+
y: y
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
_findSameRowPageBreakByRowSpanData(breaksBySpan, page, rowIndex) {
|
|
708
|
+
if (!breaksBySpan) {
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
711
|
+
return breaksBySpan.find(desc => desc.prevPage === page && rowIndex === desc.rowIndexOfSpanEnd);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
_updatePageBreaksData(pageBreaks, tableNode, rowIndex) {
|
|
715
|
+
Object.keys(tableNode._bottomByPage).forEach(p => {
|
|
716
|
+
const page = Number(p);
|
|
717
|
+
const pageBreak = this._getPageBreak(pageBreaks, page);
|
|
718
|
+
if (pageBreak) {
|
|
719
|
+
pageBreak.prevY = Math.max(pageBreak.prevY, tableNode._bottomByPage[page]);
|
|
720
|
+
}
|
|
721
|
+
if (tableNode._breaksBySpan && tableNode._breaksBySpan.length > 0) {
|
|
722
|
+
const breaksBySpanList = tableNode._breaksBySpan.filter(pb => pb.prevPage === page && rowIndex <= pb.rowIndexOfSpanEnd);
|
|
501
723
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
break;
|
|
724
|
+
if (breaksBySpanList && breaksBySpanList.length > 0) {
|
|
725
|
+
breaksBySpanList.forEach(b => {
|
|
726
|
+
b.prevY = Math.max(b.prevY, tableNode._bottomByPage[page]);
|
|
727
|
+
});
|
|
507
728
|
}
|
|
508
729
|
}
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Resolves the Y-coordinates for a target object by comparing two break points.
|
|
735
|
+
*
|
|
736
|
+
* @param {object} break1 - The first break point with `prevY` and `y` properties.
|
|
737
|
+
* @param {object} break2 - The second break point with `prevY` and `y` properties.
|
|
738
|
+
* @param {object} target - The target object to be updated with resolved Y-coordinates.
|
|
739
|
+
* @property {number} target.prevY - Updated to the maximum `prevY` value between `break1` and `break2`.
|
|
740
|
+
* @property {number} target.y - Updated to the minimum `y` value between `break1` and `break2`.
|
|
741
|
+
*/
|
|
742
|
+
_resolveBreakY(break1, break2, target) {
|
|
743
|
+
target.prevY = Math.max(break1.prevY, break2.prevY);
|
|
744
|
+
target.y = Math.min(break1.y, break2.y);
|
|
745
|
+
};
|
|
509
746
|
|
|
747
|
+
_storePageBreakData(data, startsRowSpan, pageBreaks, tableNode) {
|
|
748
|
+
if (!startsRowSpan) {
|
|
749
|
+
let pageDesc = this._getPageBreak(pageBreaks, data.prevPage);
|
|
750
|
+
let pageDescBySpan = this._getPageBreakListBySpan(tableNode, data.prevPage, data.rowIndex);
|
|
510
751
|
if (!pageDesc) {
|
|
511
|
-
pageDesc =
|
|
752
|
+
pageDesc = {
|
|
753
|
+
...data
|
|
754
|
+
};
|
|
512
755
|
pageBreaks.push(pageDesc);
|
|
513
756
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
757
|
+
if (pageDescBySpan) {
|
|
758
|
+
this._resolveBreakY(pageDesc, pageDescBySpan, pageDesc);
|
|
759
|
+
}
|
|
760
|
+
this._resolveBreakY(pageDesc, data, pageDesc);
|
|
761
|
+
} else {
|
|
762
|
+
const breaksBySpan = tableNode && tableNode._breaksBySpan || null;
|
|
763
|
+
let pageDescBySpan = this._findSameRowPageBreakByRowSpanData(breaksBySpan, data.prevPage, data.rowIndex);
|
|
764
|
+
if (!pageDescBySpan) {
|
|
765
|
+
pageDescBySpan = {
|
|
766
|
+
...data,
|
|
767
|
+
rowIndexOfSpanEnd: data.rowIndex + data.rowSpan - 1
|
|
768
|
+
};
|
|
769
|
+
if (!tableNode._breaksBySpan) {
|
|
770
|
+
tableNode._breaksBySpan = [];
|
|
771
|
+
}
|
|
772
|
+
tableNode._breaksBySpan.push(pageDescBySpan);
|
|
773
|
+
}
|
|
774
|
+
pageDescBySpan.prevY = Math.max(pageDescBySpan.prevY, data.prevY);
|
|
775
|
+
pageDescBySpan.y = Math.min(pageDescBySpan.y, data.y);
|
|
776
|
+
let pageDesc = this._getPageBreak(pageBreaks, data.prevPage);
|
|
777
|
+
if (pageDesc) {
|
|
778
|
+
this._resolveBreakY(pageDesc, pageDescBySpan, pageDesc);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Calculates the left offset for a column based on the specified gap values.
|
|
785
|
+
*
|
|
786
|
+
* @param {number} i - The index of the column for which the offset is being calculated.
|
|
787
|
+
* @param {Array<number>} gaps - An array of gap values for each column.
|
|
788
|
+
* @returns {number} The left offset for the column. Returns `gaps[i]` if it exists, otherwise `0`.
|
|
789
|
+
*/
|
|
790
|
+
_colLeftOffset(i, gaps) {
|
|
791
|
+
if (gaps && gaps.length > i) {
|
|
792
|
+
return gaps[i];
|
|
793
|
+
}
|
|
794
|
+
return 0;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* Retrieves the ending cell for a row span in case it exists in a specified table column.
|
|
799
|
+
*
|
|
800
|
+
* @param {Array<Array<object>>} tableBody - The table body, represented as a 2D array of cell objects.
|
|
801
|
+
* @param {number} rowIndex - The index of the starting row for the row span.
|
|
802
|
+
* @param {object} column - The column object containing row span information.
|
|
803
|
+
* @param {number} columnIndex - The index of the column within the row.
|
|
804
|
+
* @returns {object|null} The cell at the end of the row span if it exists; otherwise, `null`.
|
|
805
|
+
* @throws {Error} If the row span extends beyond the total row count.
|
|
806
|
+
*/
|
|
807
|
+
_getRowSpanEndingCell(tableBody, rowIndex, column, columnIndex) {
|
|
808
|
+
if (column.rowSpan && column.rowSpan > 1) {
|
|
809
|
+
let endingRow = rowIndex + column.rowSpan - 1;
|
|
810
|
+
if (endingRow >= tableBody.length) {
|
|
811
|
+
throw new Error(`Row span for column ${columnIndex} (with indexes starting from 0) exceeded row count`);
|
|
812
|
+
}
|
|
813
|
+
return tableBody[endingRow][columnIndex];
|
|
814
|
+
}
|
|
517
815
|
|
|
816
|
+
return null;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
processRow({ marginX = [0, 0], dontBreakRows = false, rowsWithoutPageBreak = 0, cells, widths, gaps, tableNode, tableBody, rowIndex, height }) {
|
|
820
|
+
const isUnbreakableRow = dontBreakRows || rowIndex <= rowsWithoutPageBreak - 1;
|
|
518
821
|
let pageBreaks = [];
|
|
822
|
+
let pageBreaksByRowSpan = [];
|
|
519
823
|
let positions = [];
|
|
824
|
+
let willBreakByHeight = false;
|
|
825
|
+
widths = widths || cells;
|
|
826
|
+
|
|
827
|
+
// Check if row should break by height
|
|
828
|
+
if (!isUnbreakableRow && height > this.writer.context().availableHeight) {
|
|
829
|
+
willBreakByHeight = true;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// Use the marginX if we are in a top level table/column (not nested)
|
|
833
|
+
const marginXParent = this.nestedLevel === 1 ? marginX : null;
|
|
834
|
+
const _bottomByPage = tableNode ? tableNode._bottomByPage : null;
|
|
835
|
+
this.writer.context().beginColumnGroup(marginXParent, _bottomByPage);
|
|
520
836
|
|
|
521
|
-
|
|
837
|
+
for (let i = 0, l = cells.length; i < l; i++) {
|
|
838
|
+
let cell = cells[i];
|
|
522
839
|
|
|
523
|
-
|
|
840
|
+
// Page change handler
|
|
841
|
+
const storePageBreakClosure = data => {
|
|
842
|
+
const startsRowSpan = cell.rowSpan && cell.rowSpan > 1;
|
|
843
|
+
if (startsRowSpan) {
|
|
844
|
+
data.rowSpan = cell.rowSpan;
|
|
845
|
+
}
|
|
846
|
+
data.rowIndex = rowIndex;
|
|
847
|
+
this._storePageBreakData(data, startsRowSpan, pageBreaks, tableNode);
|
|
848
|
+
};
|
|
524
849
|
|
|
525
|
-
|
|
850
|
+
this.writer.addListener('pageChanged', storePageBreakClosure);
|
|
526
851
|
|
|
527
|
-
for (let i = 0, l = columns.length; i < l; i++) {
|
|
528
|
-
let column = columns[i];
|
|
529
852
|
let width = widths[i]._calcWidth;
|
|
530
|
-
let leftOffset =
|
|
853
|
+
let leftOffset = this._colLeftOffset(i, gaps);
|
|
854
|
+
// Check if exists and retrieve the cell that started the rowspan in case we are in the cell just after
|
|
855
|
+
let startingSpanCell = this._findStartingRowSpanCell(cells, i);
|
|
531
856
|
|
|
532
|
-
if (
|
|
533
|
-
for (let j = 1; j <
|
|
857
|
+
if (cell.colSpan && cell.colSpan > 1) {
|
|
858
|
+
for (let j = 1; j < cell.colSpan; j++) {
|
|
534
859
|
width += widths[++i]._calcWidth + gaps[i];
|
|
535
860
|
}
|
|
536
861
|
}
|
|
537
862
|
|
|
538
|
-
this
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
this.writer.context().markEnding(column);
|
|
863
|
+
// if rowspan starts in this cell, we retrieve the last cell affected by the rowspan
|
|
864
|
+
const rowSpanEndingCell = this._getRowSpanEndingCell(tableBody, rowIndex, cell, i);
|
|
865
|
+
if (rowSpanEndingCell) {
|
|
866
|
+
// We store a reference of the ending cell in the first cell of the rowspan
|
|
867
|
+
cell._endingCell = rowSpanEndingCell;
|
|
868
|
+
cell._endingCell._startingRowSpanY = cell._startingRowSpanY;
|
|
545
869
|
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
this.writer.context().completeColumnGroup(height);
|
|
549
870
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
871
|
+
// If we are after a cell that started a rowspan
|
|
872
|
+
let endOfRowSpanCell = null;
|
|
873
|
+
if (startingSpanCell && startingSpanCell._endingCell) {
|
|
874
|
+
// Reference to the last cell of the rowspan
|
|
875
|
+
endOfRowSpanCell = startingSpanCell._endingCell;
|
|
876
|
+
// Store if we are in an unbreakable block when we save the context and the originalX
|
|
877
|
+
if (this.writer.transactionLevel > 0) {
|
|
878
|
+
endOfRowSpanCell._isUnbreakableContext = true;
|
|
879
|
+
endOfRowSpanCell._originalXOffset = this.writer.originalX;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
553
882
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
883
|
+
// We pass the endingSpanCell reference to store the context just after processing rowspan cell
|
|
884
|
+
this.writer.context().beginColumn(width, leftOffset, endOfRowSpanCell);
|
|
885
|
+
|
|
886
|
+
if (!cell._span) {
|
|
887
|
+
this.processNode(cell);
|
|
888
|
+
this.writer.context().updateBottomByPage();
|
|
889
|
+
addAll(positions, cell.positions);
|
|
890
|
+
} else if (cell._columnEndingContext) {
|
|
891
|
+
let discountY = 0;
|
|
892
|
+
if (dontBreakRows) {
|
|
893
|
+
// Calculate how many points we have to discount to Y when dontBreakRows and rowSpan are combined
|
|
894
|
+
const ctxBeforeRowSpanLastRow = this.writer.contextStack[this.writer.contextStack.length - 1];
|
|
895
|
+
discountY = ctxBeforeRowSpanLastRow.y - cell._startingRowSpanY;
|
|
896
|
+
}
|
|
897
|
+
let originalXOffset = 0;
|
|
898
|
+
// If context was saved from an unbreakable block and we are not in an unbreakable block anymore
|
|
899
|
+
// We have to sum the originalX (X before starting unbreakable block) to X
|
|
900
|
+
if (cell._isUnbreakableContext && !this.writer.transactionLevel) {
|
|
901
|
+
originalXOffset = cell._originalXOffset;
|
|
902
|
+
}
|
|
903
|
+
// row-span ending
|
|
904
|
+
// Recover the context after processing the rowspanned cell
|
|
905
|
+
this.writer.context().markEnding(cell, originalXOffset, discountY);
|
|
557
906
|
}
|
|
558
|
-
|
|
907
|
+
this.writer.removeListener('pageChanged', storePageBreakClosure);
|
|
559
908
|
}
|
|
560
909
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
910
|
+
// Check if last cell is part of a span
|
|
911
|
+
let endingSpanCell = null;
|
|
912
|
+
const lastColumn = cells.length > 0 ? cells[cells.length - 1] : null;
|
|
913
|
+
if (lastColumn) {
|
|
914
|
+
// Previous column cell has a rowspan
|
|
915
|
+
if (lastColumn._endingCell) {
|
|
916
|
+
endingSpanCell = lastColumn._endingCell;
|
|
917
|
+
// Previous column cell is part of a span
|
|
918
|
+
} else if (lastColumn._span === true) {
|
|
919
|
+
// We get the cell that started the span where we set a reference to the ending cell
|
|
920
|
+
const startingSpanCell = this._findStartingRowSpanCell(cells, cells.length);
|
|
921
|
+
if (startingSpanCell) {
|
|
922
|
+
// Context will be stored here (ending cell)
|
|
923
|
+
endingSpanCell = startingSpanCell._endingCell;
|
|
924
|
+
// Store if we are in an unbreakable block when we save the context and the originalX
|
|
925
|
+
if (this.writer.transactionLevel > 0) {
|
|
926
|
+
endingSpanCell._isUnbreakableContext = true;
|
|
927
|
+
endingSpanCell._originalXOffset = this.writer.originalX;
|
|
928
|
+
}
|
|
566
929
|
}
|
|
567
|
-
return tableBody[endingRow][columnIndex];
|
|
568
930
|
}
|
|
931
|
+
}
|
|
569
932
|
|
|
570
|
-
|
|
933
|
+
// If content did not break page, check if we should break by height
|
|
934
|
+
if (willBreakByHeight && !isUnbreakableRow && pageBreaks.length === 0) {
|
|
935
|
+
this.writer.context().moveDown(this.writer.context().availableHeight);
|
|
936
|
+
this.writer.moveToNextPage();
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
const bottomByPage = this.writer.context().completeColumnGroup(height, endingSpanCell);
|
|
940
|
+
|
|
941
|
+
if (tableNode) {
|
|
942
|
+
tableNode._bottomByPage = bottomByPage;
|
|
943
|
+
// If there are page breaks in this row, update data with prevY of last cell
|
|
944
|
+
this._updatePageBreaksData(pageBreaks, tableNode, rowIndex);
|
|
571
945
|
}
|
|
946
|
+
|
|
947
|
+
return {
|
|
948
|
+
pageBreaksBySpan: pageBreaksByRowSpan,
|
|
949
|
+
pageBreaks: pageBreaks,
|
|
950
|
+
positions: positions
|
|
951
|
+
};
|
|
572
952
|
}
|
|
573
953
|
|
|
574
954
|
// lists
|
|
@@ -617,12 +997,23 @@ class LayoutBuilder {
|
|
|
617
997
|
|
|
618
998
|
// tables
|
|
619
999
|
processTable(tableNode) {
|
|
1000
|
+
this.nestedLevel++;
|
|
620
1001
|
let processor = new TableProcessor(tableNode);
|
|
621
1002
|
|
|
622
1003
|
processor.beginTable(this.writer);
|
|
623
1004
|
|
|
624
1005
|
let rowHeights = tableNode.table.heights;
|
|
625
1006
|
for (let i = 0, l = tableNode.table.body.length; i < l; i++) {
|
|
1007
|
+
// if dontBreakRows and row starts a rowspan
|
|
1008
|
+
// we store the 'y' of the beginning of each rowSpan
|
|
1009
|
+
if (processor.dontBreakRows) {
|
|
1010
|
+
tableNode.table.body[i].forEach(cell => {
|
|
1011
|
+
if (cell.rowSpan && cell.rowSpan > 1) {
|
|
1012
|
+
cell._startingRowSpanY = this.writer.context().y;
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
|
|
626
1017
|
processor.beginRow(i, this.writer);
|
|
627
1018
|
|
|
628
1019
|
let height;
|
|
@@ -638,13 +1029,40 @@ class LayoutBuilder {
|
|
|
638
1029
|
height = undefined;
|
|
639
1030
|
}
|
|
640
1031
|
|
|
641
|
-
|
|
1032
|
+
const pageBeforeProcessing = this.writer.context().page;
|
|
1033
|
+
|
|
1034
|
+
let result = this.processRow({
|
|
1035
|
+
marginX: tableNode._margin ? [tableNode._margin[0], tableNode._margin[2]] : [0, 0],
|
|
1036
|
+
dontBreakRows: processor.dontBreakRows,
|
|
1037
|
+
rowsWithoutPageBreak: processor.rowsWithoutPageBreak,
|
|
1038
|
+
cells: tableNode.table.body[i],
|
|
1039
|
+
widths: tableNode.table.widths,
|
|
1040
|
+
gaps: tableNode._offsets.offsets,
|
|
1041
|
+
tableBody: tableNode.table.body,
|
|
1042
|
+
tableNode,
|
|
1043
|
+
rowIndex: i,
|
|
1044
|
+
height
|
|
1045
|
+
});
|
|
1046
|
+
|
|
642
1047
|
addAll(tableNode.positions, result.positions);
|
|
643
1048
|
|
|
1049
|
+
if (!result.pageBreaks || result.pageBreaks.length === 0) {
|
|
1050
|
+
const breaksBySpan = tableNode && tableNode._breaksBySpan || null;
|
|
1051
|
+
const breakBySpanData = this._findSameRowPageBreakByRowSpanData(breaksBySpan, pageBeforeProcessing, i);
|
|
1052
|
+
if (breakBySpanData) {
|
|
1053
|
+
const finalBreakBySpanData = this._getPageBreakListBySpan(tableNode, breakBySpanData.prevPage, i);
|
|
1054
|
+
result.pageBreaks.push(finalBreakBySpanData);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
|
|
644
1058
|
processor.endRow(i, this.writer, result.pageBreaks);
|
|
645
1059
|
}
|
|
646
1060
|
|
|
647
1061
|
processor.endTable(this.writer);
|
|
1062
|
+
this.nestedLevel--;
|
|
1063
|
+
if (this.nestedLevel === 0) {
|
|
1064
|
+
this.writer.context().resetMarginXTopParent();
|
|
1065
|
+
}
|
|
648
1066
|
}
|
|
649
1067
|
|
|
650
1068
|
// leafs (texts)
|
|
@@ -712,6 +1130,27 @@ class LayoutBuilder {
|
|
|
712
1130
|
return newInline;
|
|
713
1131
|
}
|
|
714
1132
|
|
|
1133
|
+
function findMaxFitLength(text, maxWidth, measureFn) {
|
|
1134
|
+
let low = 1;
|
|
1135
|
+
let high = text.length;
|
|
1136
|
+
let bestFit = 1;
|
|
1137
|
+
|
|
1138
|
+
while (low <= high) {
|
|
1139
|
+
const mid = Math.floor((low + high) / 2);
|
|
1140
|
+
const part = text.substring(0, mid);
|
|
1141
|
+
const width = measureFn(part);
|
|
1142
|
+
|
|
1143
|
+
if (width <= maxWidth) {
|
|
1144
|
+
bestFit = mid;
|
|
1145
|
+
low = mid + 1;
|
|
1146
|
+
} else {
|
|
1147
|
+
high = mid - 1;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
return bestFit;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
715
1154
|
if (!textNode._inlines || textNode._inlines.length === 0) {
|
|
716
1155
|
return null;
|
|
717
1156
|
}
|
|
@@ -727,11 +1166,9 @@ class LayoutBuilder {
|
|
|
727
1166
|
isForceContinue = false;
|
|
728
1167
|
|
|
729
1168
|
if (!inline.noWrap && inline.text.length > 1 && inline.width > line.getAvailableWidth()) {
|
|
730
|
-
let
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
maxChars = 1;
|
|
734
|
-
}
|
|
1169
|
+
let maxChars = findMaxFitLength(inline.text, line.getAvailableWidth(), (txt) =>
|
|
1170
|
+
textInlines.widthOfText(txt, inline)
|
|
1171
|
+
);
|
|
735
1172
|
if (maxChars < inline.text.length) {
|
|
736
1173
|
let newInline = cloneInline(inline);
|
|
737
1174
|
|