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/build/pdfmake.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! pdfmake v0.3.4, @license MIT, @link http://pdfmake.org */
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__(9325);
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 = node.width || (node.height ? node.height * ratio : dimensions.width);
3653
- node._height = node.height || (node.width ? node.width / ratio : dimensions.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[lastPage] = Math.max(previousBottom, this.y);
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.moveToNextPage();
5194
- position = addFct();
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 = 0;
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.moveToNextPage(node.pageOrientation);
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.moveToNextPage(node.pageOrientation);
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
- this.writer.context().beginColumnGroup(marginXParent, _bottomByPage);
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
- if (!cell._span) {
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
- this.writer.moveToNextPage();
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 (err) {
7068
- throw new Error('Invalid svg document (' + err + ')');
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.urlResolver = null;
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
- let printer = new Printer(this.fonts, this.virtualfs, this.urlResolver());
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__(5957);
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
- /***/ 9325
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
- /***/ 5957
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),