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.
@@ -172,14 +172,18 @@ class LayoutBuilder {
172
172
  docStructure = this.docPreprocessor.preprocessDocument(docStructure);
173
173
  docStructure = this.docMeasure.measureDocument(docStructure);
174
174
 
175
- this.writer = new PageElementWriter(
176
- new DocumentContext(this.pageSize, this.pageMargins));
175
+ this.writer = new PageElementWriter(new DocumentContext());
177
176
 
178
177
  this.writer.context().addListener('pageAdded', () => {
179
178
  this.addBackground(background);
180
179
  });
181
180
 
182
- this.addBackground(background);
181
+ this.writer.addPage(
182
+ this.pageSize,
183
+ null,
184
+ this.pageMargins
185
+ );
186
+
183
187
  this.processNode(docStructure);
184
188
  this.addHeadersAndFooters(header, footer);
185
189
  if (watermark != null) {
@@ -206,11 +210,6 @@ class LayoutBuilder {
206
210
  }
207
211
  }
208
212
 
209
- addStaticRepeatable(headerOrFooter, sizeFunction) {
210
- this.addDynamicRepeatable(() => // copy to new object
211
- JSON.parse(JSON.stringify(headerOrFooter)), sizeFunction);
212
- }
213
-
214
213
  addDynamicRepeatable(nodeGetter, sizeFunction) {
215
214
  let pages = this.writer.context().pages;
216
215
 
@@ -220,7 +219,7 @@ class LayoutBuilder {
220
219
  let node = nodeGetter(pageIndex + 1, l, this.writer.context().pages[pageIndex].pageSize);
221
220
 
222
221
  if (node) {
223
- let sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.pageMargins);
222
+ let sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.writer.context().getCurrentPage().pageMargins);
224
223
  this.writer.beginUnbreakableBlock(sizes.width, sizes.height);
225
224
  node = this.docPreprocessor.preprocessDocument(node);
226
225
  this.processNode(this.docMeasure.measureDocument(node));
@@ -244,16 +243,12 @@ class LayoutBuilder {
244
243
  height: pageMargins.bottom
245
244
  });
246
245
 
247
- if (typeof header === 'function') {
246
+ if (header) {
248
247
  this.addDynamicRepeatable(header, headerSizeFct);
249
- } else if (header) {
250
- this.addStaticRepeatable(header, headerSizeFct);
251
248
  }
252
249
 
253
- if (typeof footer === 'function') {
250
+ if (footer) {
254
251
  this.addDynamicRepeatable(footer, footerSizeFct);
255
- } else if (footer) {
256
- this.addStaticRepeatable(footer, footerSizeFct);
257
252
  }
258
253
  }
259
254
 
@@ -266,36 +261,40 @@ class LayoutBuilder {
266
261
  return;
267
262
  }
268
263
 
269
- watermark.font = watermark.font || defaultStyle.font || 'Roboto';
270
- watermark.fontSize = watermark.fontSize || 'auto';
271
- watermark.color = watermark.color || 'black';
272
- watermark.opacity = isNumber(watermark.opacity) ? watermark.opacity : 0.6;
273
- watermark.bold = watermark.bold || false;
274
- watermark.italics = watermark.italics || false;
275
- watermark.angle = isValue(watermark.angle) ? watermark.angle : null;
276
-
277
- if (watermark.angle === null) {
278
- watermark.angle = Math.atan2(this.pageSize.height, this.pageSize.width) * -180 / Math.PI;
264
+ let pages = this.writer.context().pages;
265
+ for (let i = 0, l = pages.length; i < l; i++) {
266
+ pages[i].watermark = getWatermarkObject({ ...watermark }, pages[i].pageSize, pdfDocument, defaultStyle);
279
267
  }
280
268
 
281
- if (watermark.fontSize === 'auto') {
282
- watermark.fontSize = getWatermarkFontSize(this.pageSize, watermark, pdfDocument);
283
- }
269
+ function getWatermarkObject(watermark, pageSize, pdfDocument, defaultStyle) {
270
+ watermark.font = watermark.font || defaultStyle.font || 'Roboto';
271
+ watermark.fontSize = watermark.fontSize || 'auto';
272
+ watermark.color = watermark.color || 'black';
273
+ watermark.opacity = isNumber(watermark.opacity) ? watermark.opacity : 0.6;
274
+ watermark.bold = watermark.bold || false;
275
+ watermark.italics = watermark.italics || false;
276
+ watermark.angle = isValue(watermark.angle) ? watermark.angle : null;
277
+
278
+ if (watermark.angle === null) {
279
+ watermark.angle = Math.atan2(pageSize.height, pageSize.width) * -180 / Math.PI;
280
+ }
284
281
 
285
- let watermarkObject = {
286
- text: watermark.text,
287
- font: pdfDocument.provideFont(watermark.font, watermark.bold, watermark.italics),
288
- fontSize: watermark.fontSize,
289
- color: watermark.color,
290
- opacity: watermark.opacity,
291
- angle: watermark.angle
292
- };
282
+ if (watermark.fontSize === 'auto') {
283
+ watermark.fontSize = getWatermarkFontSize(pageSize, watermark, pdfDocument);
284
+ }
293
285
 
294
- watermarkObject._size = getWatermarkSize(watermark, pdfDocument);
286
+ let watermarkObject = {
287
+ text: watermark.text,
288
+ font: pdfDocument.provideFont(watermark.font, watermark.bold, watermark.italics),
289
+ fontSize: watermark.fontSize,
290
+ color: watermark.color,
291
+ opacity: watermark.opacity,
292
+ angle: watermark.angle
293
+ };
295
294
 
296
- let pages = this.writer.context().pages;
297
- for (let i = 0, l = pages.length; i < l; i++) {
298
- pages[i].watermark = watermarkObject;
295
+ watermarkObject._size = getWatermarkSize(watermark, pdfDocument);
296
+
297
+ return watermarkObject;
299
298
  }
300
299
 
301
300
  function getWatermarkSize(watermark, pdfDocument) {
@@ -539,7 +538,15 @@ class LayoutBuilder {
539
538
  }
540
539
  }
541
540
 
542
- findStartingSpanCell(arr, i) {
541
+ /**
542
+ * Searches for a cell in the same row that starts a rowspan and is positioned immediately before the current cell.
543
+ * Alternatively, it finds a cell where the colspan initiating the rowspan extends to the cell just before the current one.
544
+ *
545
+ * @param {Array<object>} arr - An array representing cells in a row.
546
+ * @param {number} i - The index of the current cell to search backward from.
547
+ * @returns {object|null} The starting cell of the rowspan if found; otherwise, `null`.
548
+ */
549
+ _findStartingRowSpanCell(arr, i) {
543
550
  let requiredColspan = 1;
544
551
  for (let index = i - 1; index >= 0; index--) {
545
552
  if (!arr[index]._span) {
@@ -554,114 +561,239 @@ class LayoutBuilder {
554
561
  return null;
555
562
  }
556
563
 
557
- processRow({ marginX = [0, 0], dontBreakRows = false, rowsWithoutPageBreak = 0, cells, widths, gaps, tableBody, rowIndex, height }) {
558
- const updatePageBreakData = (page, prevY) => {
559
- let pageDesc;
560
- // Find page break data for this row and page
561
- for (let i = 0, l = pageBreaks.length; i < l; i++) {
562
- let desc = pageBreaks[i];
563
- if (desc.prevPage === page) {
564
- pageDesc = desc;
565
- break;
566
- }
567
- }
568
- // If row has page break in this page, update prevY
569
- if (pageDesc) {
570
- pageDesc.prevY = Math.max(pageDesc.prevY, prevY);
571
- }
564
+ /**
565
+ * Retrieves a page break description for a specified page from a list of page breaks.
566
+ *
567
+ * @param {Array<object>} pageBreaks - An array of page break descriptions, each containing `prevPage` properties.
568
+ * @param {number} page - The page number to find the associated page break for.
569
+ * @returns {object|undefined} The page break description object for the specified page if found; otherwise, `undefined`.
570
+ */
571
+ _getPageBreak(pageBreaks, page) {
572
+ return pageBreaks.find(desc => desc.prevPage === page);
573
+ }
574
+
575
+ _getPageBreakListBySpan(tableNode, page, rowIndex) {
576
+ if (!tableNode || !tableNode._breaksBySpan) {
577
+ return null;
578
+ }
579
+ const breaksList = tableNode._breaksBySpan.filter(desc => desc.prevPage === page && rowIndex <= desc.rowIndexOfSpanEnd);
580
+
581
+ let y = Number.MAX_VALUE,
582
+ prevY = Number.MIN_VALUE;
583
+
584
+ breaksList.forEach(b => {
585
+ prevY = Math.max(b.prevY, prevY);
586
+ y = Math.min(b.y, y);
587
+ });
588
+
589
+ return {
590
+ prevPage: page,
591
+ prevY: prevY,
592
+ y: y
572
593
  };
594
+ }
595
+
596
+ _findSameRowPageBreakByRowSpanData(breaksBySpan, page, rowIndex) {
597
+ if (!breaksBySpan) {
598
+ return null;
599
+ }
600
+ return breaksBySpan.find(desc => desc.prevPage === page && rowIndex === desc.rowIndexOfSpanEnd);
601
+ }
573
602
 
574
- const storePageBreakData = data => {
575
- let pageDesc;
603
+ _updatePageBreaksData(pageBreaks, tableNode, rowIndex) {
604
+ Object.keys(tableNode._bottomByPage).forEach(p => {
605
+ const page = Number(p);
606
+ const pageBreak = this._getPageBreak(pageBreaks, page);
607
+ if (pageBreak) {
608
+ pageBreak.prevY = Math.max(pageBreak.prevY, tableNode._bottomByPage[page]);
609
+ }
610
+ if (tableNode._breaksBySpan && tableNode._breaksBySpan.length > 0) {
611
+ const breaksBySpanList = tableNode._breaksBySpan.filter(pb => pb.prevPage === page && rowIndex <= pb.rowIndexOfSpanEnd);
576
612
 
577
- for (let i = 0, l = pageBreaks.length; i < l; i++) {
578
- let desc = pageBreaks[i];
579
- if (desc.prevPage === data.prevPage) {
580
- pageDesc = desc;
581
- break;
613
+ if (breaksBySpanList && breaksBySpanList.length > 0) {
614
+ breaksBySpanList.forEach(b => {
615
+ b.prevY = Math.max(b.prevY, tableNode._bottomByPage[page]);
616
+ });
582
617
  }
583
618
  }
619
+ });
620
+ }
584
621
 
622
+ /**
623
+ * Resolves the Y-coordinates for a target object by comparing two break points.
624
+ *
625
+ * @param {object} break1 - The first break point with `prevY` and `y` properties.
626
+ * @param {object} break2 - The second break point with `prevY` and `y` properties.
627
+ * @param {object} target - The target object to be updated with resolved Y-coordinates.
628
+ * @property {number} target.prevY - Updated to the maximum `prevY` value between `break1` and `break2`.
629
+ * @property {number} target.y - Updated to the minimum `y` value between `break1` and `break2`.
630
+ */
631
+ _resolveBreakY(break1, break2, target) {
632
+ target.prevY = Math.max(break1.prevY, break2.prevY);
633
+ target.y = Math.min(break1.y, break2.y);
634
+ };
635
+
636
+ _storePageBreakData(data, startsRowSpan, pageBreaks, tableNode) {
637
+ if (!startsRowSpan) {
638
+ let pageDesc = this._getPageBreak(pageBreaks, data.prevPage);
639
+ let pageDescBySpan = this._getPageBreakListBySpan(tableNode, data.prevPage, data.rowIndex);
585
640
  if (!pageDesc) {
586
- pageDesc = data;
641
+ pageDesc = {
642
+ ...data
643
+ };
587
644
  pageBreaks.push(pageDesc);
588
645
  }
589
- pageDesc.prevY = Math.max(pageDesc.prevY, data.prevY);
590
- pageDesc.y = Math.min(pageDesc.y, data.y);
591
- };
646
+ if (pageDescBySpan) {
647
+ this._resolveBreakY(pageDesc, pageDescBySpan, pageDesc);
648
+ }
649
+ this._resolveBreakY(pageDesc, data, pageDesc);
650
+ } else {
651
+ const breaksBySpan = tableNode && tableNode._breaksBySpan || null;
652
+ let pageDescBySpan = this._findSameRowPageBreakByRowSpanData(breaksBySpan, data.prevPage, data.rowIndex);
653
+ if (!pageDescBySpan) {
654
+ pageDescBySpan = {
655
+ ...data,
656
+ rowIndexOfSpanEnd: data.rowIndex + data.rowSpan - 1
657
+ };
658
+ if (!tableNode._breaksBySpan) {
659
+ tableNode._breaksBySpan = [];
660
+ }
661
+ tableNode._breaksBySpan.push(pageDescBySpan);
662
+ }
663
+ pageDescBySpan.prevY = Math.max(pageDescBySpan.prevY, data.prevY);
664
+ pageDescBySpan.y = Math.min(pageDescBySpan.y, data.y);
665
+ let pageDesc = this._getPageBreak(pageBreaks, data.prevPage);
666
+ if (pageDesc) {
667
+ this._resolveBreakY(pageDesc, pageDescBySpan, pageDesc);
668
+ }
669
+ }
670
+ };
671
+
672
+ /**
673
+ * Calculates the left offset for a column based on the specified gap values.
674
+ *
675
+ * @param {number} i - The index of the column for which the offset is being calculated.
676
+ * @param {Array<number>} gaps - An array of gap values for each column.
677
+ * @returns {number} The left offset for the column. Returns `gaps[i]` if it exists, otherwise `0`.
678
+ */
679
+ _colLeftOffset(i, gaps) {
680
+ if (gaps && gaps.length > i) {
681
+ return gaps[i];
682
+ }
683
+ return 0;
684
+ }
592
685
 
686
+ /**
687
+ * Retrieves the ending cell for a row span in case it exists in a specified table column.
688
+ *
689
+ * @param {Array<Array<object>>} tableBody - The table body, represented as a 2D array of cell objects.
690
+ * @param {number} rowIndex - The index of the starting row for the row span.
691
+ * @param {object} column - The column object containing row span information.
692
+ * @param {number} columnIndex - The index of the column within the row.
693
+ * @returns {object|null} The cell at the end of the row span if it exists; otherwise, `null`.
694
+ * @throws {Error} If the row span extends beyond the total row count.
695
+ */
696
+ _getRowSpanEndingCell(tableBody, rowIndex, column, columnIndex) {
697
+ if (column.rowSpan && column.rowSpan > 1) {
698
+ let endingRow = rowIndex + column.rowSpan - 1;
699
+ if (endingRow >= tableBody.length) {
700
+ throw new Error(`Row span for column ${columnIndex} (with indexes starting from 0) exceeded row count`);
701
+ }
702
+ return tableBody[endingRow][columnIndex];
703
+ }
704
+
705
+ return null;
706
+ }
707
+
708
+ processRow({ marginX = [0, 0], dontBreakRows = false, rowsWithoutPageBreak = 0, cells, widths, gaps, tableNode, tableBody, rowIndex, height }) {
593
709
  const isUnbreakableRow = dontBreakRows || rowIndex <= rowsWithoutPageBreak - 1;
594
710
  let pageBreaks = [];
711
+ let pageBreaksByRowSpan = [];
595
712
  let positions = [];
596
713
  let willBreakByHeight = false;
597
- this.writer.addListener('pageChanged', storePageBreakData);
714
+ widths = widths || cells;
598
715
 
599
716
  // Check if row should break by height
600
717
  if (!isUnbreakableRow && height > this.writer.context().availableHeight) {
601
718
  willBreakByHeight = true;
602
719
  }
603
720
 
604
- widths = widths || cells;
605
721
  // Use the marginX if we are in a top level table/column (not nested)
606
722
  const marginXParent = this.nestedLevel === 1 ? marginX : null;
607
-
608
- this.writer.context().beginColumnGroup(marginXParent);
723
+ const _bottomByPage = tableNode ? tableNode._bottomByPage : null;
724
+ this.writer.context().beginColumnGroup(marginXParent, _bottomByPage);
609
725
 
610
726
  for (let i = 0, l = cells.length; i < l; i++) {
611
- let column = cells[i];
727
+ let cell = cells[i];
728
+
729
+ // Page change handler
730
+ const storePageBreakClosure = data => {
731
+ const startsRowSpan = cell.rowSpan && cell.rowSpan > 1;
732
+ if (startsRowSpan) {
733
+ data.rowSpan = cell.rowSpan;
734
+ }
735
+ data.rowIndex = rowIndex;
736
+ this._storePageBreakData(data, startsRowSpan, pageBreaks, tableNode);
737
+ };
738
+
739
+ this.writer.addListener('pageChanged', storePageBreakClosure);
740
+
612
741
  let width = widths[i]._calcWidth;
613
- let leftOffset = colLeftOffset(i);
742
+ let leftOffset = this._colLeftOffset(i, gaps);
743
+ // Check if exists and retrieve the cell that started the rowspan in case we are in the cell just after
744
+ let startingSpanCell = this._findStartingRowSpanCell(cells, i);
614
745
 
615
- if (column.colSpan && column.colSpan > 1) {
616
- for (let j = 1; j < column.colSpan; j++) {
746
+ if (cell.colSpan && cell.colSpan > 1) {
747
+ for (let j = 1; j < cell.colSpan; j++) {
617
748
  width += widths[++i]._calcWidth + gaps[i];
618
749
  }
619
750
  }
620
751
 
621
752
  // if rowspan starts in this cell, we retrieve the last cell affected by the rowspan
622
- const endingCell = getEndingCell(column, i);
623
- if (endingCell) {
753
+ const rowSpanEndingCell = this._getRowSpanEndingCell(tableBody, rowIndex, cell, i);
754
+ if (rowSpanEndingCell) {
624
755
  // We store a reference of the ending cell in the first cell of the rowspan
625
- column._endingCell = endingCell;
626
- column._endingCell._startingRowSpanY = column._startingRowSpanY;
756
+ cell._endingCell = rowSpanEndingCell;
757
+ cell._endingCell._startingRowSpanY = cell._startingRowSpanY;
627
758
  }
628
759
 
629
- // Check if exists and retrieve the cell that started the rowspan in case we are in the cell just after
630
- let startingSpanCell = this.findStartingSpanCell(cells, i);
631
- let endingSpanCell = null;
760
+ // If we are after a cell that started a rowspan
761
+ let endOfRowSpanCell = null;
632
762
  if (startingSpanCell && startingSpanCell._endingCell) {
633
763
  // Reference to the last cell of the rowspan
634
- endingSpanCell = startingSpanCell._endingCell;
764
+ endOfRowSpanCell = startingSpanCell._endingCell;
635
765
  // Store if we are in an unbreakable block when we save the context and the originalX
636
766
  if (this.writer.transactionLevel > 0) {
637
- endingSpanCell._isUnbreakableContext = true;
638
- endingSpanCell._originalXOffset = this.writer.originalX;
767
+ endOfRowSpanCell._isUnbreakableContext = true;
768
+ endOfRowSpanCell._originalXOffset = this.writer.originalX;
639
769
  }
640
770
  }
641
771
 
642
772
  // We pass the endingSpanCell reference to store the context just after processing rowspan cell
643
- this.writer.context().beginColumn(width, leftOffset, endingSpanCell);
773
+ this.writer.context().beginColumn(width, leftOffset, endOfRowSpanCell);
644
774
 
645
- if (!column._span) {
646
- this.processNode(column);
647
- addAll(positions, column.positions);
648
- } else if (column._columnEndingContext) {
775
+ if (!cell._span) {
776
+ this.processNode(cell);
777
+ this.writer.context().updateBottomByPage();
778
+ addAll(positions, cell.positions);
779
+ } else if (cell._columnEndingContext) {
649
780
  let discountY = 0;
650
781
  if (dontBreakRows) {
651
782
  // Calculate how many points we have to discount to Y when dontBreakRows and rowSpan are combined
652
783
  const ctxBeforeRowSpanLastRow = this.writer.contextStack[this.writer.contextStack.length - 1];
653
- discountY = ctxBeforeRowSpanLastRow.y - column._startingRowSpanY;
784
+ discountY = ctxBeforeRowSpanLastRow.y - cell._startingRowSpanY;
654
785
  }
655
786
  let originalXOffset = 0;
656
787
  // If context was saved from an unbreakable block and we are not in an unbreakable block anymore
657
788
  // We have to sum the originalX (X before starting unbreakable block) to X
658
- if (column._isUnbreakableContext && !this.writer.transactionLevel) {
659
- originalXOffset = column._originalXOffset;
789
+ if (cell._isUnbreakableContext && !this.writer.transactionLevel) {
790
+ originalXOffset = cell._originalXOffset;
660
791
  }
661
792
  // row-span ending
662
793
  // Recover the context after processing the rowspanned cell
663
- this.writer.context().markEnding(column, originalXOffset, discountY);
794
+ this.writer.context().markEnding(cell, originalXOffset, discountY);
664
795
  }
796
+ this.writer.removeListener('pageChanged', storePageBreakClosure);
665
797
  }
666
798
 
667
799
  // Check if last cell is part of a span
@@ -674,7 +806,7 @@ class LayoutBuilder {
674
806
  // Previous column cell is part of a span
675
807
  } else if (lastColumn._span === true) {
676
808
  // We get the cell that started the span where we set a reference to the ending cell
677
- const startingSpanCell = this.findStartingSpanCell(cells, cells.length);
809
+ const startingSpanCell = this._findStartingRowSpanCell(cells, cells.length);
678
810
  if (startingSpanCell) {
679
811
  // Context will be stored here (ending cell)
680
812
  endingSpanCell = startingSpanCell._endingCell;
@@ -687,38 +819,25 @@ class LayoutBuilder {
687
819
  }
688
820
  }
689
821
 
690
- // If there are page breaks in this row, update data with prevY of last cell
691
- updatePageBreakData(this.writer.context().page, this.writer.context().y);
692
-
693
822
  // If content did not break page, check if we should break by height
694
- if (!isUnbreakableRow && pageBreaks.length === 0 && willBreakByHeight) {
823
+ if (willBreakByHeight && !isUnbreakableRow && pageBreaks.length === 0) {
695
824
  this.writer.context().moveDown(this.writer.context().availableHeight);
696
825
  this.writer.moveToNextPage();
697
826
  }
698
827
 
699
- this.writer.context().completeColumnGroup(height, endingSpanCell);
700
- this.writer.removeListener('pageChanged', storePageBreakData);
828
+ const bottomByPage = this.writer.context().completeColumnGroup(height, endingSpanCell);
701
829
 
702
- return { pageBreaks: pageBreaks, positions: positions };
703
-
704
- function colLeftOffset(i) {
705
- if (gaps && gaps.length > i) {
706
- return gaps[i];
707
- }
708
- return 0;
830
+ if (tableNode) {
831
+ tableNode._bottomByPage = bottomByPage;
832
+ // If there are page breaks in this row, update data with prevY of last cell
833
+ this._updatePageBreaksData(pageBreaks, tableNode, rowIndex);
709
834
  }
710
835
 
711
- function getEndingCell(column, columnIndex) {
712
- if (column.rowSpan && column.rowSpan > 1) {
713
- let endingRow = rowIndex + column.rowSpan - 1;
714
- if (endingRow >= tableBody.length) {
715
- throw new Error(`Row span for column ${columnIndex} (with indexes starting from 0) exceeded row count`);
716
- }
717
- return tableBody[endingRow][columnIndex];
718
- }
719
-
720
- return null;
721
- }
836
+ return {
837
+ pageBreaksBySpan: pageBreaksByRowSpan,
838
+ pageBreaks: pageBreaks,
839
+ positions: positions
840
+ };
722
841
  }
723
842
 
724
843
  // lists
@@ -799,6 +918,8 @@ class LayoutBuilder {
799
918
  height = undefined;
800
919
  }
801
920
 
921
+ const pageBeforeProcessing = this.writer.context().page;
922
+
802
923
  let result = this.processRow({
803
924
  marginX: tableNode._margin ? [tableNode._margin[0], tableNode._margin[2]] : [0, 0],
804
925
  dontBreakRows: processor.dontBreakRows,
@@ -807,12 +928,22 @@ class LayoutBuilder {
807
928
  widths: tableNode.table.widths,
808
929
  gaps: tableNode._offsets.offsets,
809
930
  tableBody: tableNode.table.body,
931
+ tableNode,
810
932
  rowIndex: i,
811
933
  height
812
934
  });
813
935
 
814
936
  addAll(tableNode.positions, result.positions);
815
937
 
938
+ if (!result.pageBreaks || result.pageBreaks.length === 0) {
939
+ const breaksBySpan = tableNode && tableNode._breaksBySpan || null;
940
+ const breakBySpanData = this._findSameRowPageBreakByRowSpanData(breaksBySpan, pageBeforeProcessing, i);
941
+ if (breakBySpanData) {
942
+ const finalBreakBySpanData = this._getPageBreakListBySpan(tableNode, breakBySpanData.prevPage, i);
943
+ result.pageBreaks.push(finalBreakBySpanData);
944
+ }
945
+ }
946
+
816
947
  processor.endRow(i, this.writer, result.pageBreaks);
817
948
  }
818
949
 
@@ -1,4 +1,6 @@
1
1
  import ElementWriter from './ElementWriter';
2
+ import { normalizePageSize, normalizePageMargin } from './PageSize';
3
+ import DocumentContext from './DocumentContext';
2
4
 
3
5
  /**
4
6
  * An extended ElementWriter which can handle:
@@ -9,6 +11,10 @@ import ElementWriter from './ElementWriter';
9
11
  * whole block will be rendered on the same page)
10
12
  */
11
13
  class PageElementWriter extends ElementWriter {
14
+
15
+ /**
16
+ * @param {DocumentContext} context
17
+ */
12
18
  constructor(context) {
13
19
  super(context);
14
20
  this.transactionLevel = 0;
@@ -77,6 +83,19 @@ class PageElementWriter extends ElementWriter {
77
83
  });
78
84
  }
79
85
 
86
+ addPage(pageSize, pageOrientation, pageMargin) {
87
+ let prevPage = this.page;
88
+ let prevY = this.y;
89
+
90
+ this.context().addPage(normalizePageSize(pageSize, pageOrientation), normalizePageMargin(pageMargin));
91
+
92
+ this.emit('pageChanged', {
93
+ prevPage: prevPage,
94
+ prevY: prevY,
95
+ y: this.context().y
96
+ });
97
+ }
98
+
80
99
  beginUnbreakableBlock(width, height) {
81
100
  if (this.transactionLevel++ === 0) {
82
101
  this.originalX = this.context().x;
package/src/Printer.js CHANGED
@@ -5,6 +5,7 @@ import { normalizePageSize, normalizePageMargin } from './PageSize';
5
5
  import { tableLayouts } from './tableLayouts';
6
6
  import Renderer from './Renderer';
7
7
  import { isNumber, isValue } from './helpers/variableType';
8
+ import { convertToDynamicContent } from './helpers/tools';
8
9
 
9
10
  /**
10
11
  * Printer which turns document definition into a pdf
@@ -47,17 +48,31 @@ class PdfPrinter {
47
48
  this.resolveUrls(docDefinition).then(() => {
48
49
  try {
49
50
  docDefinition.version = docDefinition.version || '1.3';
51
+ docDefinition.subset = docDefinition.subset || undefined;
52
+ docDefinition.tagged = typeof docDefinition.tagged === 'boolean' ? docDefinition.tagged : false;
53
+ docDefinition.displayTitle = typeof docDefinition.displayTitle === 'boolean' ? docDefinition.displayTitle : false;
50
54
  docDefinition.compress = typeof docDefinition.compress === 'boolean' ? docDefinition.compress : true;
51
55
  docDefinition.images = docDefinition.images || {};
52
56
  docDefinition.attachments = docDefinition.attachments || {};
53
57
  docDefinition.pageMargins = isValue(docDefinition.pageMargins) ? docDefinition.pageMargins : 40;
54
58
  docDefinition.patterns = docDefinition.patterns || {};
55
59
 
60
+ if (docDefinition.header && typeof docDefinition.header !== 'function') {
61
+ docDefinition.header = convertToDynamicContent(docDefinition.header);
62
+ }
63
+
64
+ if (docDefinition.footer && typeof docDefinition.footer !== 'function') {
65
+ docDefinition.footer = convertToDynamicContent(docDefinition.footer);
66
+ }
67
+
56
68
  let pageSize = normalizePageSize(docDefinition.pageSize, docDefinition.pageOrientation);
57
69
 
58
70
  let pdfOptions = {
59
71
  size: [pageSize.width, pageSize.height],
60
72
  pdfVersion: docDefinition.version,
73
+ subset: docDefinition.subset,
74
+ tagged: docDefinition.tagged,
75
+ displayTitle: docDefinition.displayTitle,
61
76
  compress: docDefinition.compress,
62
77
  userPassword: docDefinition.userPassword,
63
78
  ownerPassword: docDefinition.ownerPassword,
package/src/Renderer.js CHANGED
@@ -27,7 +27,7 @@ const findFont = (fonts, requiredFonts, defaultFont) => {
27
27
  * @returns {number}
28
28
  */
29
29
  const offsetText = (y, inline) => {
30
- var newY = y;
30
+ let newY = y;
31
31
  if (inline.sup) {
32
32
  newY -= inline.fontSize * 0.75;
33
33
  }
@@ -45,7 +45,6 @@ class Renderer {
45
45
 
46
46
  renderPages(pages) {
47
47
  this.pdfDocument._pdfMakePages = pages; // TODO: Why?
48
- this.pdfDocument.addPage();
49
48
 
50
49
  let totalItems = 0;
51
50
  if (this.progressCallback) {
@@ -57,10 +56,7 @@ class Renderer {
57
56
  let renderedItems = 0;
58
57
 
59
58
  for (let i = 0; i < pages.length; i++) {
60
- if (i > 0) {
61
- this._updatePageOrientationInOptions(pages[i]);
62
- this.pdfDocument.addPage(this.pdfDocument.options);
63
- }
59
+ this.pdfDocument.addPage({ size: [pages[i].pageSize.width, pages[i].pageSize.height] });
64
60
 
65
61
  let page = pages[i];
66
62
  for (let ii = 0, il = page.items.length; ii < il; ii++) {
@@ -348,6 +344,17 @@ class Renderer {
348
344
  };
349
345
 
350
346
  SVGtoPDF(this.pdfDocument, svg.svg, svg.x, svg.y, options);
347
+
348
+ if (svg.link) {
349
+ this.pdfDocument.link(svg.x, svg.y, svg._width, svg._height, svg.link);
350
+ }
351
+ if (svg.linkToPage) {
352
+ this.pdfDocument.ref({ Type: 'Action', S: 'GoTo', D: [svg.linkToPage, 0, 0] }).end();
353
+ this.pdfDocument.annotate(svg.x, svg.y, svg._width, svg._height, { Subtype: 'Link', Dest: [svg.linkToPage - 1, 'XYZ', null, null, null] });
354
+ }
355
+ if (svg.linkToDestination) {
356
+ this.pdfDocument.goTo(svg.x, svg.y, svg._width, svg._height, svg.linkToDestination);
357
+ }
351
358
  }
352
359
 
353
360
  renderAttachment(attachment) {
@@ -391,15 +398,6 @@ class Renderer {
391
398
  this.pdfDocument.restore();
392
399
  }
393
400
 
394
- _updatePageOrientationInOptions(currentPage) {
395
- let previousPageOrientation = this.pdfDocument.options.size[0] > this.pdfDocument.options.size[1] ? 'landscape' : 'portrait';
396
-
397
- if (currentPage.pageSize.orientation !== previousPageOrientation) {
398
- let width = this.pdfDocument.options.size[0];
399
- let height = this.pdfDocument.options.size[1];
400
- this.pdfDocument.options.size = [height, width];
401
- }
402
- }
403
401
  }
404
402
 
405
403
  export default Renderer;