pdfmake 0.3.4 → 0.3.6
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 +26 -1
- package/README.md +1 -0
- package/build/pdfmake.js +612 -104
- package/build/pdfmake.js.map +1 -1
- package/build/pdfmake.min.js +2 -2
- package/build/pdfmake.min.js.map +1 -1
- package/js/DocMeasure.js +7 -2
- package/js/DocumentContext.js +228 -4
- package/js/LayoutBuilder.js +136 -8
- package/js/PDFDocument.js +3 -1
- package/js/PageElementWriter.js +109 -2
- package/js/Printer.js +1 -19
- package/js/Renderer.js +13 -0
- package/js/SVGMeasure.js +4 -2
- package/js/TableProcessor.js +1 -5
- package/js/URLResolver.js +14 -1
- package/js/base.js +19 -2
- package/js/browser-extensions/index.js +0 -2
- package/js/index.js +0 -2
- package/package.json +5 -5
- package/src/DocMeasure.js +8 -2
- package/src/DocumentContext.js +243 -9
- package/src/LayoutBuilder.js +145 -11
- package/src/PDFDocument.js +1 -1
- package/src/PageElementWriter.js +121 -2
- package/src/Printer.js +1 -20
- package/src/Renderer.js +13 -0
- package/src/SVGMeasure.js +2 -2
- package/src/TableProcessor.js +1 -5
- package/src/URLResolver.js +14 -1
- package/src/base.js +24 -2
- package/src/browser-extensions/index.js +0 -2
- package/src/index.js +0 -2
package/build/pdfmake.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! pdfmake v0.3.
|
|
1
|
+
/*! pdfmake v0.3.6, @license MIT, @link http://pdfmake.org */
|
|
2
2
|
(function webpackUniversalModuleDefinition(root, factory) {
|
|
3
3
|
if(typeof exports === 'object' && typeof module === 'object')
|
|
4
4
|
module.exports = factory();
|
|
@@ -23,7 +23,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
// EXTERNAL MODULE: ./node_modules/pdfkit/js/pdfkit.es.js
|
|
26
|
-
var pdfkit_es = __webpack_require__(
|
|
26
|
+
var pdfkit_es = __webpack_require__(1254);
|
|
27
27
|
;// ./src/PDFDocument.js
|
|
28
28
|
/* provided dependency */ var Buffer = __webpack_require__(783)["Buffer"];
|
|
29
29
|
|
|
@@ -136,7 +136,9 @@ class PDFDocument extends pdfkit_es/* default */.A {
|
|
|
136
136
|
throw new Error('No image');
|
|
137
137
|
}
|
|
138
138
|
} catch (error) {
|
|
139
|
-
throw new Error(`Invalid image: ${error.toString()}\nImages dictionary should contain dataURL entries (or local file paths in node.js)
|
|
139
|
+
throw new Error(`Invalid image: ${error.toString()}\nImages dictionary should contain dataURL entries (or local file paths in node.js)`, {
|
|
140
|
+
cause: error
|
|
141
|
+
});
|
|
140
142
|
}
|
|
141
143
|
image.embed(this);
|
|
142
144
|
this._imageRegistry[src] = image;
|
|
@@ -3648,9 +3650,11 @@ class DocMeasure {
|
|
|
3648
3650
|
node._width = node._minWidth = node._maxWidth = node.cover.width;
|
|
3649
3651
|
node._height = node._minHeight = node._maxHeight = node.cover.height;
|
|
3650
3652
|
} else {
|
|
3653
|
+
let nodeWidth = isNumber(node.width) ? node.width : undefined;
|
|
3654
|
+
let nodeHeight = isNumber(node.height) ? node.height : undefined;
|
|
3651
3655
|
let ratio = dimensions.width / dimensions.height;
|
|
3652
|
-
node._width = node._minWidth = node._maxWidth =
|
|
3653
|
-
node._height =
|
|
3656
|
+
node._width = node._minWidth = node._maxWidth = nodeWidth || (nodeHeight ? nodeHeight * ratio : dimensions.width);
|
|
3657
|
+
node._height = nodeHeight || (nodeWidth ? nodeWidth / ratio : dimensions.height);
|
|
3654
3658
|
if (isNumber(node.maxWidth) && node.maxWidth < node._width) {
|
|
3655
3659
|
node._width = node._minWidth = node._maxWidth = node.maxWidth;
|
|
3656
3660
|
node._height = node._width * dimensions.height / dimensions.width;
|
|
@@ -3766,6 +3770,9 @@ class DocMeasure {
|
|
|
3766
3770
|
style: lineNumberStyle,
|
|
3767
3771
|
margin: [0, lineMargin[1], 0, lineMargin[3]]
|
|
3768
3772
|
}]);
|
|
3773
|
+
if (node.toc.outlines) {
|
|
3774
|
+
item._textNodeRef.outline = item._textNodeRef.outline || true;
|
|
3775
|
+
}
|
|
3769
3776
|
}
|
|
3770
3777
|
node.toc._table = {
|
|
3771
3778
|
table: {
|
|
@@ -4240,10 +4247,19 @@ class DocumentContext extends events.EventEmitter {
|
|
|
4240
4247
|
this.snapshots = [];
|
|
4241
4248
|
this.backgroundLength = [];
|
|
4242
4249
|
}
|
|
4243
|
-
beginColumnGroup(marginXTopParent, bottomByPage) {
|
|
4250
|
+
beginColumnGroup(marginXTopParent, bottomByPage, snakingColumns, columnGap, columnWidths) {
|
|
4244
4251
|
if (bottomByPage === void 0) {
|
|
4245
4252
|
bottomByPage = {};
|
|
4246
4253
|
}
|
|
4254
|
+
if (snakingColumns === void 0) {
|
|
4255
|
+
snakingColumns = false;
|
|
4256
|
+
}
|
|
4257
|
+
if (columnGap === void 0) {
|
|
4258
|
+
columnGap = 0;
|
|
4259
|
+
}
|
|
4260
|
+
if (columnWidths === void 0) {
|
|
4261
|
+
columnWidths = null;
|
|
4262
|
+
}
|
|
4247
4263
|
this.snapshots.push({
|
|
4248
4264
|
x: this.x,
|
|
4249
4265
|
y: this.y,
|
|
@@ -4258,7 +4274,10 @@ class DocumentContext extends events.EventEmitter {
|
|
|
4258
4274
|
availableWidth: this.availableWidth,
|
|
4259
4275
|
page: this.page
|
|
4260
4276
|
},
|
|
4261
|
-
lastColumnWidth: this.lastColumnWidth
|
|
4277
|
+
lastColumnWidth: this.lastColumnWidth,
|
|
4278
|
+
snakingColumns: snakingColumns,
|
|
4279
|
+
gap: columnGap,
|
|
4280
|
+
columnWidths: columnWidths
|
|
4262
4281
|
});
|
|
4263
4282
|
this.lastColumnWidth = 0;
|
|
4264
4283
|
if (marginXTopParent) {
|
|
@@ -4267,18 +4286,71 @@ class DocumentContext extends events.EventEmitter {
|
|
|
4267
4286
|
}
|
|
4268
4287
|
updateBottomByPage() {
|
|
4269
4288
|
const lastSnapshot = this.snapshots[this.snapshots.length - 1];
|
|
4289
|
+
if (!lastSnapshot) {
|
|
4290
|
+
return;
|
|
4291
|
+
}
|
|
4270
4292
|
const lastPage = this.page;
|
|
4271
4293
|
let previousBottom = -Number.MIN_VALUE;
|
|
4272
|
-
if (lastSnapshot.bottomByPage[lastPage]) {
|
|
4294
|
+
if (lastSnapshot.bottomByPage && lastSnapshot.bottomByPage[lastPage]) {
|
|
4273
4295
|
previousBottom = lastSnapshot.bottomByPage[lastPage];
|
|
4274
4296
|
}
|
|
4275
|
-
lastSnapshot.bottomByPage
|
|
4297
|
+
if (lastSnapshot.bottomByPage) {
|
|
4298
|
+
lastSnapshot.bottomByPage[lastPage] = Math.max(previousBottom, this.y);
|
|
4299
|
+
}
|
|
4276
4300
|
}
|
|
4277
4301
|
resetMarginXTopParent() {
|
|
4278
4302
|
this.marginXTopParent = null;
|
|
4279
4303
|
}
|
|
4304
|
+
|
|
4305
|
+
/**
|
|
4306
|
+
* Find the most recent (deepest) snaking column group snapshot.
|
|
4307
|
+
* @returns {object|null}
|
|
4308
|
+
*/
|
|
4309
|
+
getSnakingSnapshot() {
|
|
4310
|
+
for (let i = this.snapshots.length - 1; i >= 0; i--) {
|
|
4311
|
+
if (this.snapshots[i].snakingColumns) {
|
|
4312
|
+
return this.snapshots[i];
|
|
4313
|
+
}
|
|
4314
|
+
}
|
|
4315
|
+
return null;
|
|
4316
|
+
}
|
|
4317
|
+
inSnakingColumns() {
|
|
4318
|
+
return !!this.getSnakingSnapshot();
|
|
4319
|
+
}
|
|
4320
|
+
|
|
4321
|
+
/**
|
|
4322
|
+
* Check if we're inside a nested non-snaking column group (e.g., a table row)
|
|
4323
|
+
* within an outer snaking column group. This is used to prevent snaking-specific
|
|
4324
|
+
* breaks inside table cells — the table's own page break mechanism should handle
|
|
4325
|
+
* row breaks, and column breaks should happen between rows.
|
|
4326
|
+
* @returns {boolean}
|
|
4327
|
+
*/
|
|
4328
|
+
isInNestedNonSnakingGroup() {
|
|
4329
|
+
for (let i = this.snapshots.length - 1; i >= 0; i--) {
|
|
4330
|
+
let snap = this.snapshots[i];
|
|
4331
|
+
if (snap.snakingColumns) {
|
|
4332
|
+
return false; // Reached snaking snapshot without finding inner group
|
|
4333
|
+
}
|
|
4334
|
+
if (!snap.overflowed) {
|
|
4335
|
+
return true; // Found non-snaking, non-overflowed inner group
|
|
4336
|
+
}
|
|
4337
|
+
}
|
|
4338
|
+
return false;
|
|
4339
|
+
}
|
|
4280
4340
|
beginColumn(width, offset, endingCell) {
|
|
4341
|
+
// Find the correct snapshot for this column group.
|
|
4342
|
+
// When a snaking column break (moveToNextColumn) occurs during inner column
|
|
4343
|
+
// processing, overflowed snapshots may sit above this column group's snapshot.
|
|
4344
|
+
// We need to skip past those to find the one from our beginColumnGroup call.
|
|
4281
4345
|
let saved = this.snapshots[this.snapshots.length - 1];
|
|
4346
|
+
if (saved && saved.overflowed) {
|
|
4347
|
+
for (let i = this.snapshots.length - 1; i >= 0; i--) {
|
|
4348
|
+
if (!this.snapshots[i].overflowed) {
|
|
4349
|
+
saved = this.snapshots[i];
|
|
4350
|
+
break;
|
|
4351
|
+
}
|
|
4352
|
+
}
|
|
4353
|
+
}
|
|
4282
4354
|
this.calculateBottomMost(saved, endingCell);
|
|
4283
4355
|
this.page = saved.page;
|
|
4284
4356
|
this.x = this.x + this.lastColumnWidth + (offset || 0);
|
|
@@ -4314,6 +4386,40 @@ class DocumentContext extends events.EventEmitter {
|
|
|
4314
4386
|
}
|
|
4315
4387
|
completeColumnGroup(height, endingCell) {
|
|
4316
4388
|
let saved = this.snapshots.pop();
|
|
4389
|
+
|
|
4390
|
+
// Track the maximum bottom position across all columns (including overflowed).
|
|
4391
|
+
// Critical for snaking: content after columns must appear below the tallest column.
|
|
4392
|
+
let maxBottomY = this.y;
|
|
4393
|
+
let maxBottomPage = this.page;
|
|
4394
|
+
let maxBottomAvailableHeight = this.availableHeight;
|
|
4395
|
+
|
|
4396
|
+
// Pop overflowed snapshots created by moveToNextColumn (snaking columns).
|
|
4397
|
+
// Merge their bottomMost values to find the true maximum.
|
|
4398
|
+
while (saved && saved.overflowed) {
|
|
4399
|
+
let bm = bottomMostContext({
|
|
4400
|
+
page: maxBottomPage,
|
|
4401
|
+
y: maxBottomY,
|
|
4402
|
+
availableHeight: maxBottomAvailableHeight
|
|
4403
|
+
}, saved.bottomMost || {});
|
|
4404
|
+
maxBottomPage = bm.page;
|
|
4405
|
+
maxBottomY = bm.y;
|
|
4406
|
+
maxBottomAvailableHeight = bm.availableHeight;
|
|
4407
|
+
saved = this.snapshots.pop();
|
|
4408
|
+
}
|
|
4409
|
+
if (!saved) {
|
|
4410
|
+
return {};
|
|
4411
|
+
}
|
|
4412
|
+
|
|
4413
|
+
// Apply the max bottom from all overflowed columns to this base snapshot
|
|
4414
|
+
if (maxBottomPage > saved.bottomMost.page || maxBottomPage === saved.bottomMost.page && maxBottomY > saved.bottomMost.y) {
|
|
4415
|
+
saved.bottomMost = {
|
|
4416
|
+
x: saved.x,
|
|
4417
|
+
y: maxBottomY,
|
|
4418
|
+
page: maxBottomPage,
|
|
4419
|
+
availableHeight: maxBottomAvailableHeight,
|
|
4420
|
+
availableWidth: saved.availableWidth
|
|
4421
|
+
};
|
|
4422
|
+
}
|
|
4317
4423
|
this.calculateBottomMost(saved, endingCell);
|
|
4318
4424
|
this.x = saved.x;
|
|
4319
4425
|
let y = saved.bottomMost.y;
|
|
@@ -4341,6 +4447,140 @@ class DocumentContext extends events.EventEmitter {
|
|
|
4341
4447
|
this.lastColumnWidth = saved.lastColumnWidth;
|
|
4342
4448
|
return saved.bottomByPage;
|
|
4343
4449
|
}
|
|
4450
|
+
|
|
4451
|
+
/**
|
|
4452
|
+
* Move to the next column in a column group (snaking columns).
|
|
4453
|
+
* Creates an overflowed snapshot to track that we've moved to the next column.
|
|
4454
|
+
* @returns {object} Position info for the new column
|
|
4455
|
+
*/
|
|
4456
|
+
moveToNextColumn() {
|
|
4457
|
+
let prevY = this.y;
|
|
4458
|
+
let snakingSnapshot = this.getSnakingSnapshot();
|
|
4459
|
+
if (!snakingSnapshot) {
|
|
4460
|
+
return {
|
|
4461
|
+
prevY: prevY,
|
|
4462
|
+
y: this.y
|
|
4463
|
+
};
|
|
4464
|
+
}
|
|
4465
|
+
|
|
4466
|
+
// Update snaking snapshot's bottomMost with current position BEFORE resetting.
|
|
4467
|
+
// This captures where content reached in the current column (overflow point).
|
|
4468
|
+
this.calculateBottomMost(snakingSnapshot, null);
|
|
4469
|
+
|
|
4470
|
+
// Calculate new X position: move right by current column width + gap
|
|
4471
|
+
let overflowCount = 0;
|
|
4472
|
+
for (let i = this.snapshots.length - 1; i >= 0; i--) {
|
|
4473
|
+
if (this.snapshots[i].overflowed) {
|
|
4474
|
+
overflowCount++;
|
|
4475
|
+
} else {
|
|
4476
|
+
break;
|
|
4477
|
+
}
|
|
4478
|
+
}
|
|
4479
|
+
let currentColumnWidth = snakingSnapshot.columnWidths && snakingSnapshot.columnWidths[overflowCount] || this.lastColumnWidth || this.availableWidth;
|
|
4480
|
+
let nextColumnWidth = snakingSnapshot.columnWidths && snakingSnapshot.columnWidths[overflowCount + 1] || currentColumnWidth;
|
|
4481
|
+
this.lastColumnWidth = nextColumnWidth;
|
|
4482
|
+
let newX = this.x + (currentColumnWidth || 0) + (snakingSnapshot.gap || 0);
|
|
4483
|
+
let newY = snakingSnapshot.y;
|
|
4484
|
+
this.snapshots.push({
|
|
4485
|
+
x: newX,
|
|
4486
|
+
y: newY,
|
|
4487
|
+
availableHeight: snakingSnapshot.availableHeight,
|
|
4488
|
+
availableWidth: nextColumnWidth,
|
|
4489
|
+
page: this.page,
|
|
4490
|
+
overflowed: true,
|
|
4491
|
+
bottomMost: {
|
|
4492
|
+
x: newX,
|
|
4493
|
+
y: newY,
|
|
4494
|
+
availableHeight: snakingSnapshot.availableHeight,
|
|
4495
|
+
availableWidth: nextColumnWidth,
|
|
4496
|
+
page: this.page
|
|
4497
|
+
},
|
|
4498
|
+
lastColumnWidth: nextColumnWidth,
|
|
4499
|
+
snakingColumns: true,
|
|
4500
|
+
gap: snakingSnapshot.gap,
|
|
4501
|
+
columnWidths: snakingSnapshot.columnWidths
|
|
4502
|
+
});
|
|
4503
|
+
this.x = newX;
|
|
4504
|
+
this.y = newY;
|
|
4505
|
+
this.availableHeight = snakingSnapshot.availableHeight;
|
|
4506
|
+
this.availableWidth = nextColumnWidth;
|
|
4507
|
+
|
|
4508
|
+
// Sync non-overflowed inner snapshots (e.g. inner column groups for
|
|
4509
|
+
// product/price rows) with the new snaking column position.
|
|
4510
|
+
// Without this, inner beginColumn would read stale y/page/x values.
|
|
4511
|
+
for (let i = this.snapshots.length - 2; i >= 0; i--) {
|
|
4512
|
+
let snapshot = this.snapshots[i];
|
|
4513
|
+
if (snapshot.overflowed || snapshot.snakingColumns) {
|
|
4514
|
+
break; // Stop at first overflowed or snaking snapshot
|
|
4515
|
+
}
|
|
4516
|
+
snapshot.x = newX;
|
|
4517
|
+
snapshot.y = newY;
|
|
4518
|
+
snapshot.page = this.page;
|
|
4519
|
+
snapshot.availableHeight = snakingSnapshot.availableHeight;
|
|
4520
|
+
if (snapshot.bottomMost) {
|
|
4521
|
+
snapshot.bottomMost.x = newX;
|
|
4522
|
+
snapshot.bottomMost.y = newY;
|
|
4523
|
+
snapshot.bottomMost.page = this.page;
|
|
4524
|
+
snapshot.bottomMost.availableHeight = snakingSnapshot.availableHeight;
|
|
4525
|
+
}
|
|
4526
|
+
}
|
|
4527
|
+
return {
|
|
4528
|
+
prevY: prevY,
|
|
4529
|
+
y: this.y
|
|
4530
|
+
};
|
|
4531
|
+
}
|
|
4532
|
+
|
|
4533
|
+
/**
|
|
4534
|
+
* Reset snaking column state when moving to a new page.
|
|
4535
|
+
* Clears overflowed snapshots, resets X to left margin, sets width to first column,
|
|
4536
|
+
* and syncs all snapshots to new page coordinates.
|
|
4537
|
+
*/
|
|
4538
|
+
resetSnakingColumnsForNewPage() {
|
|
4539
|
+
let snakingSnapshot = this.getSnakingSnapshot();
|
|
4540
|
+
if (!snakingSnapshot) {
|
|
4541
|
+
return;
|
|
4542
|
+
}
|
|
4543
|
+
let pageTopY = this.pageMargins.top;
|
|
4544
|
+
let pageInnerHeight = this.getCurrentPage().pageSize.height - this.pageMargins.top - this.pageMargins.bottom;
|
|
4545
|
+
|
|
4546
|
+
// When moving to new page, start at first column.
|
|
4547
|
+
// Reset width to FIRST column width, not last column from previous page.
|
|
4548
|
+
let firstColumnWidth = snakingSnapshot.columnWidths ? snakingSnapshot.columnWidths[0] : this.lastColumnWidth || this.availableWidth;
|
|
4549
|
+
|
|
4550
|
+
// Clean up overflowed snapshots
|
|
4551
|
+
while (this.snapshots.length > 1 && this.snapshots[this.snapshots.length - 1].overflowed) {
|
|
4552
|
+
this.snapshots.pop();
|
|
4553
|
+
}
|
|
4554
|
+
|
|
4555
|
+
// Reset X to start of first column (left margin)
|
|
4556
|
+
if (this.marginXTopParent) {
|
|
4557
|
+
this.x = this.pageMargins.left + this.marginXTopParent[0];
|
|
4558
|
+
} else {
|
|
4559
|
+
this.x = this.pageMargins.left;
|
|
4560
|
+
}
|
|
4561
|
+
this.availableWidth = firstColumnWidth;
|
|
4562
|
+
this.lastColumnWidth = firstColumnWidth;
|
|
4563
|
+
|
|
4564
|
+
// Sync all snapshots to new page state.
|
|
4565
|
+
// When page break occurs within snaking columns, update ALL snapshots
|
|
4566
|
+
// (not just snaking column snapshots) to reflect new page coordinates.
|
|
4567
|
+
// This ensures nested structures (like inner product/price columns)
|
|
4568
|
+
// don't retain stale values that would cause layout corruption.
|
|
4569
|
+
for (let i = 0; i < this.snapshots.length; i++) {
|
|
4570
|
+
let snapshot = this.snapshots[i];
|
|
4571
|
+
let isSnakingSnapshot = !!snapshot.snakingColumns;
|
|
4572
|
+
snapshot.x = this.x;
|
|
4573
|
+
snapshot.y = isSnakingSnapshot ? pageTopY : this.y;
|
|
4574
|
+
snapshot.availableHeight = isSnakingSnapshot ? pageInnerHeight : this.availableHeight;
|
|
4575
|
+
snapshot.page = this.page;
|
|
4576
|
+
if (snapshot.bottomMost) {
|
|
4577
|
+
snapshot.bottomMost.x = this.x;
|
|
4578
|
+
snapshot.bottomMost.y = isSnakingSnapshot ? pageTopY : this.y;
|
|
4579
|
+
snapshot.bottomMost.availableHeight = isSnakingSnapshot ? pageInnerHeight : this.availableHeight;
|
|
4580
|
+
snapshot.bottomMost.page = this.page;
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4583
|
+
}
|
|
4344
4584
|
addMargin(left, right) {
|
|
4345
4585
|
this.x += left;
|
|
4346
4586
|
this.availableWidth -= left + (right || 0);
|
|
@@ -5187,11 +5427,118 @@ class PageElementWriter extends src_ElementWriter {
|
|
|
5187
5427
|
popFromRepeatables() {
|
|
5188
5428
|
this.repeatables.pop();
|
|
5189
5429
|
}
|
|
5430
|
+
|
|
5431
|
+
/**
|
|
5432
|
+
* Move to the next column in a column group (snaking columns).
|
|
5433
|
+
* Handles repeatables and emits columnChanged event.
|
|
5434
|
+
*/
|
|
5435
|
+
moveToNextColumn() {
|
|
5436
|
+
let nextColumn = this.context().moveToNextColumn();
|
|
5437
|
+
|
|
5438
|
+
// Handle repeatables (like table headers) for the new column
|
|
5439
|
+
this.repeatables.forEach(function (rep) {
|
|
5440
|
+
// In snaking columns, we WANT headers to repeat.
|
|
5441
|
+
// However, in Standard Page Breaks, headers are drawn using useBlockXOffset=true (original absolute X).
|
|
5442
|
+
// This works for page breaks because margins are consistent.
|
|
5443
|
+
// In Snaking Columns, the X position changes for each column.
|
|
5444
|
+
// If we use true, the header is drawn at the *original* X position (Col 1), overlapping/invisible.
|
|
5445
|
+
// We MUST use false to force drawing relative to the CURRENT context X (new column start).
|
|
5446
|
+
this.addFragment(rep, false);
|
|
5447
|
+
}, this);
|
|
5448
|
+
this.emit('columnChanged', {
|
|
5449
|
+
prevY: nextColumn.prevY,
|
|
5450
|
+
y: this.context().y
|
|
5451
|
+
});
|
|
5452
|
+
}
|
|
5453
|
+
|
|
5454
|
+
/**
|
|
5455
|
+
* Check if currently in a column group that can move to next column.
|
|
5456
|
+
* Only returns true if snakingColumns is enabled for the column group.
|
|
5457
|
+
* @returns {boolean}
|
|
5458
|
+
*/
|
|
5459
|
+
canMoveToNextColumn() {
|
|
5460
|
+
let ctx = this.context();
|
|
5461
|
+
let snakingSnapshot = ctx.getSnakingSnapshot();
|
|
5462
|
+
if (snakingSnapshot) {
|
|
5463
|
+
// Check if we're inside a nested (non-snaking) column group.
|
|
5464
|
+
// If so, don't allow a snaking column move — it would corrupt
|
|
5465
|
+
// the inner row's layout (e.g. product name in col 1, price in col 2).
|
|
5466
|
+
// The inner row should complete via normal page break instead.
|
|
5467
|
+
for (let i = ctx.snapshots.length - 1; i >= 0; i--) {
|
|
5468
|
+
let snap = ctx.snapshots[i];
|
|
5469
|
+
if (snap.snakingColumns) {
|
|
5470
|
+
break; // Reached the snaking snapshot, no inner groups found
|
|
5471
|
+
}
|
|
5472
|
+
if (!snap.overflowed) {
|
|
5473
|
+
return false; // Found a non-snaking, non-overflowed inner group
|
|
5474
|
+
}
|
|
5475
|
+
}
|
|
5476
|
+
let overflowCount = 0;
|
|
5477
|
+
for (let i = ctx.snapshots.length - 1; i >= 0; i--) {
|
|
5478
|
+
if (ctx.snapshots[i].overflowed) {
|
|
5479
|
+
overflowCount++;
|
|
5480
|
+
} else {
|
|
5481
|
+
break;
|
|
5482
|
+
}
|
|
5483
|
+
}
|
|
5484
|
+
if (snakingSnapshot.columnWidths && overflowCount >= snakingSnapshot.columnWidths.length - 1) {
|
|
5485
|
+
return false;
|
|
5486
|
+
}
|
|
5487
|
+
let currentColumnWidth = ctx.availableWidth || ctx.lastColumnWidth || 0;
|
|
5488
|
+
let nextColumnWidth = snakingSnapshot.columnWidths ? snakingSnapshot.columnWidths[overflowCount + 1] : currentColumnWidth;
|
|
5489
|
+
let nextX = ctx.x + currentColumnWidth + (snakingSnapshot.gap || 0);
|
|
5490
|
+
let page = ctx.getCurrentPage();
|
|
5491
|
+
let pageWidth = page.pageSize.width;
|
|
5492
|
+
let rightMargin = page.pageMargins ? page.pageMargins.right : 0;
|
|
5493
|
+
let parentRightMargin = ctx.marginXTopParent ? ctx.marginXTopParent[1] : 0;
|
|
5494
|
+
let rightBoundary = pageWidth - rightMargin - parentRightMargin;
|
|
5495
|
+
return nextX + nextColumnWidth <= rightBoundary + 1;
|
|
5496
|
+
}
|
|
5497
|
+
return false;
|
|
5498
|
+
}
|
|
5190
5499
|
_fitOnPage(addFct) {
|
|
5191
5500
|
let position = addFct();
|
|
5192
5501
|
if (!position) {
|
|
5193
|
-
this.
|
|
5194
|
-
|
|
5502
|
+
if (this.canMoveToNextColumn()) {
|
|
5503
|
+
this.moveToNextColumn();
|
|
5504
|
+
position = addFct();
|
|
5505
|
+
}
|
|
5506
|
+
if (!position) {
|
|
5507
|
+
let ctx = this.context();
|
|
5508
|
+
let snakingSnapshot = ctx.getSnakingSnapshot();
|
|
5509
|
+
if (snakingSnapshot) {
|
|
5510
|
+
if (ctx.isInNestedNonSnakingGroup()) {
|
|
5511
|
+
// Inside a table cell within snaking columns — use standard page break.
|
|
5512
|
+
// Don't reset snaking state; the table handles its own breaks.
|
|
5513
|
+
// Column breaks happen between rows in processTable instead.
|
|
5514
|
+
this.moveToNextPage();
|
|
5515
|
+
} else {
|
|
5516
|
+
this.moveToNextPage();
|
|
5517
|
+
|
|
5518
|
+
// Save lastColumnWidth before reset — if we're inside a nested
|
|
5519
|
+
// column group (e.g. product/price row), the reset would overwrite
|
|
5520
|
+
// it with the snaking column width, corrupting inner column layout.
|
|
5521
|
+
let savedLastColumnWidth = ctx.lastColumnWidth;
|
|
5522
|
+
ctx.resetSnakingColumnsForNewPage();
|
|
5523
|
+
ctx.lastColumnWidth = savedLastColumnWidth;
|
|
5524
|
+
}
|
|
5525
|
+
position = addFct();
|
|
5526
|
+
} else {
|
|
5527
|
+
while (ctx.snapshots.length > 0 && ctx.snapshots[ctx.snapshots.length - 1].overflowed) {
|
|
5528
|
+
let popped = ctx.snapshots.pop();
|
|
5529
|
+
let prevSnapshot = ctx.snapshots[ctx.snapshots.length - 1];
|
|
5530
|
+
if (prevSnapshot) {
|
|
5531
|
+
ctx.x = prevSnapshot.x;
|
|
5532
|
+
ctx.y = prevSnapshot.y;
|
|
5533
|
+
ctx.availableHeight = prevSnapshot.availableHeight;
|
|
5534
|
+
ctx.availableWidth = popped.availableWidth;
|
|
5535
|
+
ctx.lastColumnWidth = prevSnapshot.lastColumnWidth;
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
this.moveToNextPage();
|
|
5539
|
+
position = addFct();
|
|
5540
|
+
}
|
|
5541
|
+
}
|
|
5195
5542
|
}
|
|
5196
5543
|
return position;
|
|
5197
5544
|
}
|
|
@@ -5215,7 +5562,7 @@ class TableProcessor {
|
|
|
5215
5562
|
const prepareRowSpanData = () => {
|
|
5216
5563
|
let rsd = [];
|
|
5217
5564
|
let x = 0;
|
|
5218
|
-
let lastWidth
|
|
5565
|
+
let lastWidth;
|
|
5219
5566
|
rsd.push({
|
|
5220
5567
|
left: 0,
|
|
5221
5568
|
rowSpan: 0
|
|
@@ -5448,7 +5795,6 @@ class TableProcessor {
|
|
|
5448
5795
|
lineColor: borderColor
|
|
5449
5796
|
}, false, isNumber(overrideY), null, forcePage);
|
|
5450
5797
|
currentLine = null;
|
|
5451
|
-
borderColor = null;
|
|
5452
5798
|
cellAbove = null;
|
|
5453
5799
|
currentCell = null;
|
|
5454
5800
|
rowCellAbove = null;
|
|
@@ -5523,9 +5869,6 @@ class TableProcessor {
|
|
|
5523
5869
|
dash: dash,
|
|
5524
5870
|
lineColor: borderColor
|
|
5525
5871
|
}, false, true);
|
|
5526
|
-
cellBefore = null;
|
|
5527
|
-
currentCell = null;
|
|
5528
|
-
borderColor = null;
|
|
5529
5872
|
}
|
|
5530
5873
|
endTable(writer) {
|
|
5531
5874
|
if (this.cleanUpRepeatables) {
|
|
@@ -6220,7 +6563,11 @@ class LayoutBuilder {
|
|
|
6220
6563
|
if (availableHeight - margin[1] < 0) {
|
|
6221
6564
|
// Consume the whole available space
|
|
6222
6565
|
this.writer.context().moveDown(availableHeight);
|
|
6223
|
-
this.writer.
|
|
6566
|
+
if (this.writer.context().inSnakingColumns() && !this.writer.context().isInNestedNonSnakingGroup()) {
|
|
6567
|
+
this.snakingAwarePageBreak(node.pageOrientation);
|
|
6568
|
+
} else {
|
|
6569
|
+
this.writer.moveToNextPage(node.pageOrientation);
|
|
6570
|
+
}
|
|
6224
6571
|
/**
|
|
6225
6572
|
* TODO - Something to consider:
|
|
6226
6573
|
* Right now the node starts at the top of next page (after header)
|
|
@@ -6242,7 +6589,11 @@ class LayoutBuilder {
|
|
|
6242
6589
|
// Necessary for nodes inside tables
|
|
6243
6590
|
if (availableHeight - margin[3] < 0) {
|
|
6244
6591
|
this.writer.context().moveDown(availableHeight);
|
|
6245
|
-
this.writer.
|
|
6592
|
+
if (this.writer.context().inSnakingColumns() && !this.writer.context().isInNestedNonSnakingGroup()) {
|
|
6593
|
+
this.snakingAwarePageBreak(node.pageOrientation);
|
|
6594
|
+
} else {
|
|
6595
|
+
this.writer.moveToNextPage(node.pageOrientation);
|
|
6596
|
+
}
|
|
6246
6597
|
/**
|
|
6247
6598
|
* TODO - Something to consider:
|
|
6248
6599
|
* Right now next node starts at the top of next page (after header)
|
|
@@ -6341,6 +6692,37 @@ class LayoutBuilder {
|
|
|
6341
6692
|
}
|
|
6342
6693
|
}
|
|
6343
6694
|
|
|
6695
|
+
/**
|
|
6696
|
+
* Helper for page breaks that respects snaking column context.
|
|
6697
|
+
* When in snaking columns, first tries moving to next column.
|
|
6698
|
+
* If no columns available, moves to next page and resets x to left margin.
|
|
6699
|
+
* @param {string} pageOrientation - Optional page orientation for the new page
|
|
6700
|
+
*/
|
|
6701
|
+
snakingAwarePageBreak(pageOrientation) {
|
|
6702
|
+
let ctx = this.writer.context();
|
|
6703
|
+
let snakingSnapshot = ctx.getSnakingSnapshot();
|
|
6704
|
+
if (!snakingSnapshot) {
|
|
6705
|
+
return;
|
|
6706
|
+
}
|
|
6707
|
+
|
|
6708
|
+
// Try flowing to next column first
|
|
6709
|
+
if (this.writer.canMoveToNextColumn()) {
|
|
6710
|
+
this.writer.moveToNextColumn();
|
|
6711
|
+
return;
|
|
6712
|
+
}
|
|
6713
|
+
|
|
6714
|
+
// No more columns available, move to new page
|
|
6715
|
+
this.writer.moveToNextPage(pageOrientation);
|
|
6716
|
+
|
|
6717
|
+
// Reset snaking column state for the new page
|
|
6718
|
+
// Save lastColumnWidth before reset — if we're inside a nested
|
|
6719
|
+
// column group (e.g. product/price row), the reset would overwrite
|
|
6720
|
+
// it with the snaking column width, corrupting inner column layout.
|
|
6721
|
+
let savedLastColumnWidth = ctx.lastColumnWidth;
|
|
6722
|
+
ctx.resetSnakingColumnsForNewPage();
|
|
6723
|
+
ctx.lastColumnWidth = savedLastColumnWidth;
|
|
6724
|
+
}
|
|
6725
|
+
|
|
6344
6726
|
// vertical container
|
|
6345
6727
|
processVerticalContainer(node) {
|
|
6346
6728
|
node.stack.forEach(item => {
|
|
@@ -6421,7 +6803,8 @@ class LayoutBuilder {
|
|
|
6421
6803
|
marginX: columnNode._margin ? [columnNode._margin[0], columnNode._margin[2]] : [0, 0],
|
|
6422
6804
|
cells: columns,
|
|
6423
6805
|
widths: columns,
|
|
6424
|
-
gaps
|
|
6806
|
+
gaps,
|
|
6807
|
+
snakingColumns: columnNode.snakingColumns
|
|
6425
6808
|
});
|
|
6426
6809
|
addAll(columnNode.positions, result.positions);
|
|
6427
6810
|
this.nestedLevel--;
|
|
@@ -6608,7 +6991,8 @@ class LayoutBuilder {
|
|
|
6608
6991
|
tableNode,
|
|
6609
6992
|
tableBody,
|
|
6610
6993
|
rowIndex,
|
|
6611
|
-
height
|
|
6994
|
+
height,
|
|
6995
|
+
snakingColumns = false
|
|
6612
6996
|
} = _ref;
|
|
6613
6997
|
const isUnbreakableRow = dontBreakRows || rowIndex <= rowsWithoutPageBreak - 1;
|
|
6614
6998
|
let pageBreaks = [];
|
|
@@ -6626,7 +7010,18 @@ class LayoutBuilder {
|
|
|
6626
7010
|
// Use the marginX if we are in a top level table/column (not nested)
|
|
6627
7011
|
const marginXParent = this.nestedLevel === 1 ? marginX : null;
|
|
6628
7012
|
const _bottomByPage = tableNode ? tableNode._bottomByPage : null;
|
|
6629
|
-
|
|
7013
|
+
// Pass column gap and widths to context snapshot for snaking columns
|
|
7014
|
+
// to advance correctly and reset to first-column width on new pages.
|
|
7015
|
+
const columnGapForGroup = gaps && gaps.length > 1 ? gaps[1] : 0;
|
|
7016
|
+
const columnWidthsForContext = widths.map(w => w._calcWidth);
|
|
7017
|
+
this.writer.context().beginColumnGroup(marginXParent, _bottomByPage, snakingColumns, columnGapForGroup, columnWidthsForContext);
|
|
7018
|
+
|
|
7019
|
+
// IMPORTANT: We iterate ALL columns even when snakingColumns is enabled.
|
|
7020
|
+
// This is intentional — beginColumn() must be called for each column to set up
|
|
7021
|
+
// proper geometry (widths, offsets) and rowspan/colspan tracking. The
|
|
7022
|
+
// completeColumnGroup() call at the end depends on this bookkeeping to compute
|
|
7023
|
+
// heights correctly. Content processing is skipped for columns > 0 via
|
|
7024
|
+
// skipForSnaking below, but the column structure must still be established.
|
|
6630
7025
|
for (let i = 0, l = cells.length; i < l; i++) {
|
|
6631
7026
|
let cell = cells[i];
|
|
6632
7027
|
let cellIndexBegin = i;
|
|
@@ -6679,7 +7074,13 @@ class LayoutBuilder {
|
|
|
6679
7074
|
|
|
6680
7075
|
// We pass the endingSpanCell reference to store the context just after processing rowspan cell
|
|
6681
7076
|
this.writer.context().beginColumn(width, leftOffset, endOfRowSpanCell);
|
|
6682
|
-
|
|
7077
|
+
|
|
7078
|
+
// When snaking, only process content from the first column (i === 0).
|
|
7079
|
+
// Content overflows into subsequent columns via moveToNextColumn().
|
|
7080
|
+
// We skip content processing here but NOT the beginColumn() call above —
|
|
7081
|
+
// the column geometry setup is still needed for proper layout bookkeeping.
|
|
7082
|
+
const skipForSnaking = snakingColumns && i > 0;
|
|
7083
|
+
if (!cell._span && !skipForSnaking) {
|
|
6683
7084
|
this.processNode(cell, true);
|
|
6684
7085
|
this.writer.context().updateBottomByPage();
|
|
6685
7086
|
if (cell.verticalAlignment) {
|
|
@@ -6732,7 +7133,11 @@ class LayoutBuilder {
|
|
|
6732
7133
|
// If content did not break page, check if we should break by height
|
|
6733
7134
|
if (willBreakByHeight && !isUnbreakableRow && pageBreaks.length === 0) {
|
|
6734
7135
|
this.writer.context().moveDown(this.writer.context().availableHeight);
|
|
6735
|
-
|
|
7136
|
+
if (snakingColumns) {
|
|
7137
|
+
this.snakingAwarePageBreak();
|
|
7138
|
+
} else {
|
|
7139
|
+
this.writer.moveToNextPage();
|
|
7140
|
+
}
|
|
6736
7141
|
}
|
|
6737
7142
|
const bottomByPage = this.writer.context().completeColumnGroup(height, endingSpanCell);
|
|
6738
7143
|
if (tableNode) {
|
|
@@ -6822,7 +7227,27 @@ class LayoutBuilder {
|
|
|
6822
7227
|
let processor = new src_TableProcessor(tableNode);
|
|
6823
7228
|
processor.beginTable(this.writer);
|
|
6824
7229
|
let rowHeights = tableNode.table.heights;
|
|
7230
|
+
let lastRowHeight = 0;
|
|
6825
7231
|
for (let i = 0, l = tableNode.table.body.length; i < l; i++) {
|
|
7232
|
+
// Between table rows: check if we should move to the next snaking column.
|
|
7233
|
+
// This must happen AFTER the previous row's endRow (borders drawn) and
|
|
7234
|
+
// BEFORE this row's beginRow. At this point, the table row column group
|
|
7235
|
+
// has been completed, so canMoveToNextColumn() works correctly.
|
|
7236
|
+
if (i > 0 && this.writer.context().inSnakingColumns()) {
|
|
7237
|
+
// Estimate minimum space for next row: use last row's height as heuristic,
|
|
7238
|
+
// or fall back to a minimum of padding + line height + border
|
|
7239
|
+
let minRowHeight = lastRowHeight > 0 ? lastRowHeight : processor.rowPaddingTop + 14 + processor.rowPaddingBottom + processor.bottomLineWidth + processor.topLineWidth;
|
|
7240
|
+
if (this.writer.context().availableHeight < minRowHeight) {
|
|
7241
|
+
this.snakingAwarePageBreak();
|
|
7242
|
+
|
|
7243
|
+
// Skip border when headerRows present (header repeat includes it)
|
|
7244
|
+
if (processor.layout.hLineWhenBroken !== false && !processor.headerRows) {
|
|
7245
|
+
processor.drawHorizontalLine(i, this.writer);
|
|
7246
|
+
}
|
|
7247
|
+
}
|
|
7248
|
+
}
|
|
7249
|
+
let rowYBefore = this.writer.context().y;
|
|
7250
|
+
|
|
6826
7251
|
// if dontBreakRows and row starts a rowspan
|
|
6827
7252
|
// we store the 'y' of the beginning of each rowSpan
|
|
6828
7253
|
if (processor.dontBreakRows) {
|
|
@@ -6867,6 +7292,12 @@ class LayoutBuilder {
|
|
|
6867
7292
|
}
|
|
6868
7293
|
}
|
|
6869
7294
|
processor.endRow(i, this.writer, result.pageBreaks);
|
|
7295
|
+
|
|
7296
|
+
// Track the height of the completed row for the next row's estimate
|
|
7297
|
+
let rowYAfter = this.writer.context().y;
|
|
7298
|
+
if (this.writer.context().page === pageBeforeProcessing) {
|
|
7299
|
+
lastRowHeight = rowYAfter - rowYBefore;
|
|
7300
|
+
}
|
|
6870
7301
|
}
|
|
6871
7302
|
processor.endTable(this.writer);
|
|
6872
7303
|
this.nestedLevel--;
|
|
@@ -6889,6 +7320,26 @@ class LayoutBuilder {
|
|
|
6889
7320
|
line.id = nodeId;
|
|
6890
7321
|
}
|
|
6891
7322
|
}
|
|
7323
|
+
if (node.outline) {
|
|
7324
|
+
line._outline = {
|
|
7325
|
+
id: node.id,
|
|
7326
|
+
parentId: node.outlineParentId,
|
|
7327
|
+
text: node.outlineText || node.text,
|
|
7328
|
+
expanded: node.outlineExpanded || false
|
|
7329
|
+
};
|
|
7330
|
+
} else if (Array.isArray(node.text)) {
|
|
7331
|
+
for (let i = 0, l = node.text.length; i < l; i++) {
|
|
7332
|
+
let item = node.text[i];
|
|
7333
|
+
if (item.outline) {
|
|
7334
|
+
line._outline = {
|
|
7335
|
+
id: item.id,
|
|
7336
|
+
parentId: item.outlineParentId,
|
|
7337
|
+
text: item.outlineText || item.text,
|
|
7338
|
+
expanded: item.outlineExpanded || false
|
|
7339
|
+
};
|
|
7340
|
+
}
|
|
7341
|
+
}
|
|
7342
|
+
}
|
|
6892
7343
|
if (node._tocItemRef) {
|
|
6893
7344
|
line._pageNodeRef = node._tocItemRef;
|
|
6894
7345
|
}
|
|
@@ -6906,6 +7357,27 @@ class LayoutBuilder {
|
|
|
6906
7357
|
}
|
|
6907
7358
|
}
|
|
6908
7359
|
while (line && (maxHeight === -1 || currentHeight < maxHeight)) {
|
|
7360
|
+
// Check if line fits vertically in current context
|
|
7361
|
+
if (line.getHeight() > this.writer.context().availableHeight && this.writer.context().y > this.writer.context().pageMargins.top) {
|
|
7362
|
+
// Line doesn't fit, forced move to next page/column
|
|
7363
|
+
// Only do snaking-specific break if we're in snaking columns AND NOT inside
|
|
7364
|
+
// a nested non-snaking group (like a table row). Table cells should use
|
|
7365
|
+
// standard page breaks — column breaks happen between table rows instead.
|
|
7366
|
+
if (this.writer.context().inSnakingColumns() && !this.writer.context().isInNestedNonSnakingGroup()) {
|
|
7367
|
+
this.snakingAwarePageBreak(node.pageOrientation);
|
|
7368
|
+
|
|
7369
|
+
// Always reflow text after a snaking break (column or page).
|
|
7370
|
+
// This ensures text adapts to the new column width, whether it's narrower or wider.
|
|
7371
|
+
if (line.inlines && line.inlines.length > 0) {
|
|
7372
|
+
node._inlines.unshift(...line.inlines);
|
|
7373
|
+
}
|
|
7374
|
+
// Rebuild line with new width
|
|
7375
|
+
line = this.buildNextLine(node);
|
|
7376
|
+
continue;
|
|
7377
|
+
} else {
|
|
7378
|
+
this.writer.moveToNextPage(node.pageOrientation);
|
|
7379
|
+
}
|
|
7380
|
+
}
|
|
6909
7381
|
let positions = this.writer.addLine(line);
|
|
6910
7382
|
node.positions.push(positions);
|
|
6911
7383
|
line = this.buildNextLine(node);
|
|
@@ -6959,7 +7431,6 @@ class LayoutBuilder {
|
|
|
6959
7431
|
while (textNode._inlines && textNode._inlines.length > 0 && (line.hasEnoughSpaceForInline(textNode._inlines[0], textNode._inlines.slice(1)) || isForceContinue)) {
|
|
6960
7432
|
let isHardWrap = false;
|
|
6961
7433
|
let inline = textNode._inlines.shift();
|
|
6962
|
-
isForceContinue = false;
|
|
6963
7434
|
if (!inline.noWrap && inline.text.length > 1 && inline.width > line.getAvailableWidth()) {
|
|
6964
7435
|
let maxChars = findMaxFitLength(inline.text, line.getAvailableWidth(), txt => textInlines.widthOfText(txt, inline));
|
|
6965
7436
|
if (maxChars < inline.text.length) {
|
|
@@ -7064,8 +7535,10 @@ const parseSVG = svgString => {
|
|
|
7064
7535
|
let doc;
|
|
7065
7536
|
try {
|
|
7066
7537
|
doc = new xmldoc.XmlDocument(svgString);
|
|
7067
|
-
} catch (
|
|
7068
|
-
throw new Error('Invalid svg document (' +
|
|
7538
|
+
} catch (error) {
|
|
7539
|
+
throw new Error('Invalid svg document (' + error + ')', {
|
|
7540
|
+
cause: error
|
|
7541
|
+
});
|
|
7069
7542
|
}
|
|
7070
7543
|
if (doc.name !== "svg") {
|
|
7071
7544
|
throw new Error('Invalid svg document (expected <svg>)');
|
|
@@ -7317,6 +7790,7 @@ class Renderer {
|
|
|
7317
7790
|
constructor(pdfDocument, progressCallback) {
|
|
7318
7791
|
this.pdfDocument = pdfDocument;
|
|
7319
7792
|
this.progressCallback = progressCallback;
|
|
7793
|
+
this.outlineMap = [];
|
|
7320
7794
|
}
|
|
7321
7795
|
renderPages(pages) {
|
|
7322
7796
|
this.pdfDocument._pdfMakePages = pages; // TODO: Why?
|
|
@@ -7396,6 +7870,18 @@ class Renderer {
|
|
|
7396
7870
|
break;
|
|
7397
7871
|
}
|
|
7398
7872
|
}
|
|
7873
|
+
if (line._outline) {
|
|
7874
|
+
let parentOutline = this.pdfDocument.outline;
|
|
7875
|
+
if (line._outline.parentId && this.outlineMap[line._outline.parentId]) {
|
|
7876
|
+
parentOutline = this.outlineMap[line._outline.parentId];
|
|
7877
|
+
}
|
|
7878
|
+
let outline = parentOutline.addItem(line._outline.text, {
|
|
7879
|
+
expanded: line._outline.expanded
|
|
7880
|
+
});
|
|
7881
|
+
if (line._outline.id) {
|
|
7882
|
+
this.outlineMap[line._outline.id] = outline;
|
|
7883
|
+
}
|
|
7884
|
+
}
|
|
7399
7885
|
if (line._pageNodeRef) {
|
|
7400
7886
|
preparePageNodeRefLine(line._pageNodeRef, line.inlines[0]);
|
|
7401
7887
|
}
|
|
@@ -7699,22 +8185,6 @@ class Renderer {
|
|
|
7699
8185
|
|
|
7700
8186
|
|
|
7701
8187
|
|
|
7702
|
-
|
|
7703
|
-
/**
|
|
7704
|
-
* Printer which turns document definition into a pdf
|
|
7705
|
-
*
|
|
7706
|
-
* @example
|
|
7707
|
-
* var fontDescriptors = {
|
|
7708
|
-
* Roboto: {
|
|
7709
|
-
* normal: 'fonts/Roboto-Regular.ttf',
|
|
7710
|
-
* bold: 'fonts/Roboto-Medium.ttf',
|
|
7711
|
-
* italics: 'fonts/Roboto-Italic.ttf',
|
|
7712
|
-
* bolditalics: 'fonts/Roboto-MediumItalic.ttf'
|
|
7713
|
-
* }
|
|
7714
|
-
* };
|
|
7715
|
-
*
|
|
7716
|
-
* var printer = new PdfPrinter(fontDescriptors);
|
|
7717
|
-
*/
|
|
7718
8188
|
class PdfPrinter {
|
|
7719
8189
|
/**
|
|
7720
8190
|
* @param {object} fontDescriptors font definition dictionary
|
|
@@ -7722,12 +8192,6 @@ class PdfPrinter {
|
|
|
7722
8192
|
* @param {object} urlResolver
|
|
7723
8193
|
*/
|
|
7724
8194
|
constructor(fontDescriptors, virtualfs, urlResolver) {
|
|
7725
|
-
if (virtualfs === void 0) {
|
|
7726
|
-
virtualfs = null;
|
|
7727
|
-
}
|
|
7728
|
-
if (urlResolver === void 0) {
|
|
7729
|
-
urlResolver = null;
|
|
7730
|
-
}
|
|
7731
8195
|
this.fontDescriptors = fontDescriptors;
|
|
7732
8196
|
this.virtualfs = virtualfs;
|
|
7733
8197
|
this.urlResolver = urlResolver;
|
|
@@ -7824,9 +8288,6 @@ class PdfPrinter {
|
|
|
7824
8288
|
headers: {}
|
|
7825
8289
|
};
|
|
7826
8290
|
};
|
|
7827
|
-
if (this.urlResolver === null) {
|
|
7828
|
-
return;
|
|
7829
|
-
}
|
|
7830
8291
|
for (let font in this.fontDescriptors) {
|
|
7831
8292
|
if (this.fontDescriptors.hasOwnProperty(font)) {
|
|
7832
8293
|
if (this.fontDescriptors[font].normal) {
|
|
@@ -7986,7 +8447,68 @@ function calculatePageHeight(page, margins) {
|
|
|
7986
8447
|
/* harmony default export */ const Printer = (PdfPrinter);
|
|
7987
8448
|
// EXTERNAL MODULE: ./src/virtual-fs.js
|
|
7988
8449
|
var virtual_fs = __webpack_require__(6811);
|
|
8450
|
+
;// ./src/URLResolver.js
|
|
8451
|
+
async function fetchUrl(url, headers) {
|
|
8452
|
+
if (headers === void 0) {
|
|
8453
|
+
headers = {};
|
|
8454
|
+
}
|
|
8455
|
+
try {
|
|
8456
|
+
const response = await fetch(url, {
|
|
8457
|
+
headers
|
|
8458
|
+
});
|
|
8459
|
+
if (!response.ok) {
|
|
8460
|
+
throw new Error(`Failed to fetch (status code: ${response.status}, url: "${url}")`);
|
|
8461
|
+
}
|
|
8462
|
+
return await response.arrayBuffer();
|
|
8463
|
+
} catch (error) {
|
|
8464
|
+
throw new Error(`Network request failed (url: "${url}", error: ${error.message})`, {
|
|
8465
|
+
cause: error
|
|
8466
|
+
});
|
|
8467
|
+
}
|
|
8468
|
+
}
|
|
8469
|
+
class URLResolver {
|
|
8470
|
+
constructor(fs) {
|
|
8471
|
+
this.fs = fs;
|
|
8472
|
+
this.resolving = {};
|
|
8473
|
+
this.urlAccessPolicy = undefined;
|
|
8474
|
+
}
|
|
8475
|
+
|
|
8476
|
+
/**
|
|
8477
|
+
* @param {(url: string) => boolean} callback
|
|
8478
|
+
*/
|
|
8479
|
+
setUrlAccessPolicy(callback) {
|
|
8480
|
+
this.urlAccessPolicy = callback;
|
|
8481
|
+
}
|
|
8482
|
+
resolve(url, headers) {
|
|
8483
|
+
if (headers === void 0) {
|
|
8484
|
+
headers = {};
|
|
8485
|
+
}
|
|
8486
|
+
const resolveUrlInternal = async () => {
|
|
8487
|
+
if (url.toLowerCase().startsWith('https://') || url.toLowerCase().startsWith('http://')) {
|
|
8488
|
+
if (this.fs.existsSync(url)) {
|
|
8489
|
+
return; // url was downloaded earlier
|
|
8490
|
+
}
|
|
8491
|
+
if (typeof this.urlAccessPolicy !== 'undefined' && this.urlAccessPolicy(url) !== true) {
|
|
8492
|
+
throw new Error(`Access to URL denied by resource access policy: ${url}`);
|
|
8493
|
+
}
|
|
8494
|
+
const buffer = await fetchUrl(url, headers);
|
|
8495
|
+
this.fs.writeFileSync(url, buffer);
|
|
8496
|
+
}
|
|
8497
|
+
// else cannot be resolved
|
|
8498
|
+
};
|
|
8499
|
+
if (!this.resolving[url]) {
|
|
8500
|
+
this.resolving[url] = resolveUrlInternal();
|
|
8501
|
+
}
|
|
8502
|
+
return this.resolving[url];
|
|
8503
|
+
}
|
|
8504
|
+
resolved() {
|
|
8505
|
+
return Promise.all(Object.values(this.resolving));
|
|
8506
|
+
}
|
|
8507
|
+
}
|
|
8508
|
+
/* harmony default export */ const src_URLResolver = (URLResolver);
|
|
7989
8509
|
;// ./src/base.js
|
|
8510
|
+
/* provided dependency */ var process = __webpack_require__(9964);
|
|
8511
|
+
|
|
7990
8512
|
|
|
7991
8513
|
|
|
7992
8514
|
|
|
@@ -7994,7 +8516,7 @@ var virtual_fs = __webpack_require__(6811);
|
|
|
7994
8516
|
class pdfmake {
|
|
7995
8517
|
constructor() {
|
|
7996
8518
|
this.virtualfs = virtual_fs["default"];
|
|
7997
|
-
this.
|
|
8519
|
+
this.urlAccessPolicy = undefined;
|
|
7998
8520
|
}
|
|
7999
8521
|
|
|
8000
8522
|
/**
|
|
@@ -8014,10 +8536,26 @@ class pdfmake {
|
|
|
8014
8536
|
}
|
|
8015
8537
|
options.progressCallback = this.progressCallback;
|
|
8016
8538
|
options.tableLayouts = this.tableLayouts;
|
|
8017
|
-
|
|
8539
|
+
const isServer = typeof process !== 'undefined' && process?.versions?.node;
|
|
8540
|
+
if (typeof this.urlAccessPolicy === 'undefined' && isServer) {
|
|
8541
|
+
console.warn('No URL access policy defined. Consider using setUrlAccessPolicy() to restrict external resource downloads.');
|
|
8542
|
+
}
|
|
8543
|
+
let urlResolver = new src_URLResolver(this.virtualfs);
|
|
8544
|
+
urlResolver.setUrlAccessPolicy(this.urlAccessPolicy);
|
|
8545
|
+
let printer = new Printer(this.fonts, this.virtualfs, urlResolver);
|
|
8018
8546
|
const pdfDocumentPromise = printer.createPdfKitDocument(docDefinition, options);
|
|
8019
8547
|
return this._transformToDocument(pdfDocumentPromise);
|
|
8020
8548
|
}
|
|
8549
|
+
|
|
8550
|
+
/**
|
|
8551
|
+
* @param {(url: string) => boolean} callback
|
|
8552
|
+
*/
|
|
8553
|
+
setUrlAccessPolicy(callback) {
|
|
8554
|
+
if (callback !== undefined && typeof callback !== 'function') {
|
|
8555
|
+
throw new Error("Parameter 'callback' has an invalid type. Function or undefined expected.");
|
|
8556
|
+
}
|
|
8557
|
+
this.urlAccessPolicy = callback;
|
|
8558
|
+
}
|
|
8021
8559
|
setProgressCallback(callback) {
|
|
8022
8560
|
this.progressCallback = callback;
|
|
8023
8561
|
}
|
|
@@ -8107,7 +8645,7 @@ class OutputDocument {
|
|
|
8107
8645
|
}
|
|
8108
8646
|
/* harmony default export */ const src_OutputDocument = (OutputDocument);
|
|
8109
8647
|
// EXTERNAL MODULE: ./node_modules/file-saver/dist/FileSaver.min.js
|
|
8110
|
-
var FileSaver_min = __webpack_require__(
|
|
8648
|
+
var FileSaver_min = __webpack_require__(8667);
|
|
8111
8649
|
;// ./src/browser-extensions/OutputDocumentBrowser.js
|
|
8112
8650
|
|
|
8113
8651
|
|
|
@@ -8196,52 +8734,6 @@ class OutputDocumentBrowser extends src_OutputDocument {
|
|
|
8196
8734
|
}
|
|
8197
8735
|
}
|
|
8198
8736
|
/* harmony default export */ const browser_extensions_OutputDocumentBrowser = (OutputDocumentBrowser);
|
|
8199
|
-
;// ./src/URLResolver.js
|
|
8200
|
-
async function fetchUrl(url, headers) {
|
|
8201
|
-
if (headers === void 0) {
|
|
8202
|
-
headers = {};
|
|
8203
|
-
}
|
|
8204
|
-
try {
|
|
8205
|
-
const response = await fetch(url, {
|
|
8206
|
-
headers
|
|
8207
|
-
});
|
|
8208
|
-
if (!response.ok) {
|
|
8209
|
-
throw new Error(`Failed to fetch (status code: ${response.status}, url: "${url}")`);
|
|
8210
|
-
}
|
|
8211
|
-
return await response.arrayBuffer();
|
|
8212
|
-
} catch (error) {
|
|
8213
|
-
throw new Error(`Network request failed (url: "${url}", error: ${error.message})`);
|
|
8214
|
-
}
|
|
8215
|
-
}
|
|
8216
|
-
class URLResolver {
|
|
8217
|
-
constructor(fs) {
|
|
8218
|
-
this.fs = fs;
|
|
8219
|
-
this.resolving = {};
|
|
8220
|
-
}
|
|
8221
|
-
resolve(url, headers) {
|
|
8222
|
-
if (headers === void 0) {
|
|
8223
|
-
headers = {};
|
|
8224
|
-
}
|
|
8225
|
-
const resolveUrlInternal = async () => {
|
|
8226
|
-
if (url.toLowerCase().startsWith('https://') || url.toLowerCase().startsWith('http://')) {
|
|
8227
|
-
if (this.fs.existsSync(url)) {
|
|
8228
|
-
return; // url was downloaded earlier
|
|
8229
|
-
}
|
|
8230
|
-
const buffer = await fetchUrl(url, headers);
|
|
8231
|
-
this.fs.writeFileSync(url, buffer);
|
|
8232
|
-
}
|
|
8233
|
-
// else cannot be resolved
|
|
8234
|
-
};
|
|
8235
|
-
if (!this.resolving[url]) {
|
|
8236
|
-
this.resolving[url] = resolveUrlInternal();
|
|
8237
|
-
}
|
|
8238
|
-
return this.resolving[url];
|
|
8239
|
-
}
|
|
8240
|
-
resolved() {
|
|
8241
|
-
return Promise.all(Object.values(this.resolving));
|
|
8242
|
-
}
|
|
8243
|
-
}
|
|
8244
|
-
/* harmony default export */ const src_URLResolver = (URLResolver);
|
|
8245
8737
|
// EXTERNAL MODULE: ./src/browser-extensions/virtual-fs-cjs.js
|
|
8246
8738
|
var virtual_fs_cjs = __webpack_require__(2416);
|
|
8247
8739
|
var virtual_fs_cjs_default = /*#__PURE__*/__webpack_require__.n(virtual_fs_cjs);
|
|
@@ -8254,7 +8746,6 @@ var configurator_default = /*#__PURE__*/__webpack_require__.n(configurator);
|
|
|
8254
8746
|
|
|
8255
8747
|
|
|
8256
8748
|
|
|
8257
|
-
|
|
8258
8749
|
// core-js: Polyfills will be used only if natives completely unavailable.
|
|
8259
8750
|
configurator_default()({
|
|
8260
8751
|
useNative: ['Promise']
|
|
@@ -8270,7 +8761,6 @@ let defaultClientFonts = {
|
|
|
8270
8761
|
class browser_extensions_pdfmake extends base {
|
|
8271
8762
|
constructor() {
|
|
8272
8763
|
super();
|
|
8273
|
-
this.urlResolver = () => new src_URLResolver(this.virtualfs);
|
|
8274
8764
|
this.fonts = defaultClientFonts;
|
|
8275
8765
|
}
|
|
8276
8766
|
addFontContainer(fontContainer) {
|
|
@@ -21545,7 +22035,7 @@ module.exports = {
|
|
|
21545
22035
|
|
|
21546
22036
|
/***/ },
|
|
21547
22037
|
|
|
21548
|
-
/***/
|
|
22038
|
+
/***/ 1254
|
|
21549
22039
|
(__unused_webpack_module, exports, __webpack_require__) {
|
|
21550
22040
|
|
|
21551
22041
|
"use strict";
|
|
@@ -42145,6 +42635,9 @@ module.exports = function regexTester(regex) {
|
|
|
42145
42635
|
parser.opt = opt || {}
|
|
42146
42636
|
parser.opt.lowercase = parser.opt.lowercase || parser.opt.lowercasetags
|
|
42147
42637
|
parser.looseCase = parser.opt.lowercase ? 'toLowerCase' : 'toUpperCase'
|
|
42638
|
+
parser.opt.maxEntityCount = parser.opt.maxEntityCount || 512
|
|
42639
|
+
parser.opt.maxEntityDepth = parser.opt.maxEntityDepth || 4
|
|
42640
|
+
parser.entityCount = parser.entityDepth = 0
|
|
42148
42641
|
parser.tags = []
|
|
42149
42642
|
parser.closed = parser.closedRoot = parser.sawRoot = false
|
|
42150
42643
|
parser.tag = parser.error = null
|
|
@@ -43690,9 +44183,24 @@ module.exports = function regexTester(regex) {
|
|
|
43690
44183
|
parser.opt.unparsedEntities &&
|
|
43691
44184
|
!Object.values(sax.XML_ENTITIES).includes(parsedEntity)
|
|
43692
44185
|
) {
|
|
44186
|
+
if ((parser.entityCount += 1) > parser.opt.maxEntityCount) {
|
|
44187
|
+
error(
|
|
44188
|
+
parser,
|
|
44189
|
+
'Parsed entity count exceeds max entity count'
|
|
44190
|
+
)
|
|
44191
|
+
}
|
|
44192
|
+
|
|
44193
|
+
if ((parser.entityDepth += 1) > parser.opt.maxEntityDepth) {
|
|
44194
|
+
error(
|
|
44195
|
+
parser,
|
|
44196
|
+
'Parsed entity depth exceeds max entity depth'
|
|
44197
|
+
)
|
|
44198
|
+
}
|
|
44199
|
+
|
|
43693
44200
|
parser.entity = ''
|
|
43694
44201
|
parser.state = returnState
|
|
43695
44202
|
parser.write(parsedEntity)
|
|
44203
|
+
parser.entityDepth -= 1
|
|
43696
44204
|
} else {
|
|
43697
44205
|
parser[buffer] += parsedEntity
|
|
43698
44206
|
parser.entity = ''
|
|
@@ -48656,7 +49164,7 @@ module.exports = function whichTypedArray(value) {
|
|
|
48656
49164
|
|
|
48657
49165
|
/***/ },
|
|
48658
49166
|
|
|
48659
|
-
/***/
|
|
49167
|
+
/***/ 8667
|
|
48660
49168
|
(module, exports, __webpack_require__) {
|
|
48661
49169
|
|
|
48662
49170
|
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function(a,b){if(true)!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (b),
|