pdfmake 0.3.0-beta.11 → 0.3.0-beta.13

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