pdfmake 0.2.10 → 0.2.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdfmake",
3
- "version": "0.2.10",
3
+ "version": "0.2.12",
4
4
  "description": "Client/server side PDF printing in pure JavaScript",
5
5
  "main": "src/printer.js",
6
6
  "browser": "build/pdfmake.js",
@@ -2,7 +2,7 @@
2
2
 
3
3
  var isString = require('./helpers').isString;
4
4
 
5
- function buildColumnWidths(columns, availableWidth) {
5
+ function buildColumnWidths(columns, availableWidth, offsetTotal = 0, tableNode) {
6
6
  var autoColumns = [],
7
7
  autoMin = 0, autoMax = 0,
8
8
  starColumns = [],
@@ -25,10 +25,31 @@ function buildColumnWidths(columns, availableWidth) {
25
25
  }
26
26
  });
27
27
 
28
- fixedColumns.forEach(function (col) {
28
+ fixedColumns.forEach(function (col, colIndex) {
29
29
  // width specified as %
30
30
  if (isString(col.width) && /\d+%/.test(col.width)) {
31
- col.width = parseFloat(col.width) * initial_availableWidth / 100;
31
+ // In tables we have to take into consideration the reserved width for paddings and borders
32
+ var reservedWidth = 0;
33
+ if (tableNode) {
34
+ var paddingLeft = tableNode._layout.paddingLeft(colIndex, tableNode);
35
+ var paddingRight = tableNode._layout.paddingRight(colIndex, tableNode);
36
+ var borderLeft = tableNode._layout.vLineWidth(colIndex, tableNode);
37
+ var borderRight = tableNode._layout.vLineWidth(colIndex + 1, tableNode);
38
+ if (colIndex === 0) {
39
+ // first column assumes whole borderLeft and half of border right
40
+ reservedWidth = paddingLeft + paddingRight + borderLeft + (borderRight / 2);
41
+
42
+ } else if (colIndex === fixedColumns.length - 1) {
43
+ // last column assumes whole borderRight and half of border left
44
+ reservedWidth = paddingLeft + paddingRight + (borderLeft / 2) + borderRight;
45
+
46
+ } else {
47
+ // Columns in the middle assume half of each border
48
+ reservedWidth = paddingLeft + paddingRight + (borderLeft / 2) + (borderRight / 2);
49
+ }
50
+ }
51
+ var totalAvailableWidth = initial_availableWidth + offsetTotal;
52
+ col.width = (parseFloat(col.width) * totalAvailableWidth / 100) - reservedWidth;
32
53
  }
33
54
  if (col.width < (col._minWidth) && col.elasticWidth) {
34
55
  col._calcWidth = col._minWidth;
package/src/docMeasure.js CHANGED
@@ -509,16 +509,16 @@ DocMeasure.prototype.measureOrderedList = function (node) {
509
509
  if (item.listMarker._inlines) {
510
510
  node._gapSize.width = Math.max(node._gapSize.width, item.listMarker._inlines[0].width);
511
511
  }
512
- } // TODO: else - nested lists numbering
512
+
513
+ if (node.reversed) {
514
+ counter--;
515
+ } else {
516
+ counter++;
517
+ }
518
+ }
513
519
 
514
520
  node._minWidth = Math.max(node._minWidth, items[i]._minWidth);
515
521
  node._maxWidth = Math.max(node._maxWidth, items[i]._maxWidth);
516
-
517
- if (node.reversed) {
518
- counter--;
519
- } else {
520
- counter++;
521
- }
522
522
  }
523
523
 
524
524
  node._minWidth += node._gapSize.width;
@@ -19,8 +19,6 @@ function DocumentContext(pageSize, pageMargins) {
19
19
 
20
20
  this.snapshots = [];
21
21
 
22
- this.endingCell = null;
23
-
24
22
  this.tracker = new TraversalTracker();
25
23
 
26
24
  this.backgroundLength = [];
@@ -42,7 +40,6 @@ DocumentContext.prototype.beginColumnGroup = function () {
42
40
  availableWidth: this.availableWidth,
43
41
  page: this.page
44
42
  },
45
- endingCell: this.endingCell,
46
43
  lastColumnWidth: this.lastColumnWidth
47
44
  });
48
45
 
@@ -52,9 +49,8 @@ DocumentContext.prototype.beginColumnGroup = function () {
52
49
  DocumentContext.prototype.beginColumn = function (width, offset, endingCell) {
53
50
  var saved = this.snapshots[this.snapshots.length - 1];
54
51
 
55
- this.calculateBottomMost(saved);
52
+ this.calculateBottomMost(saved, endingCell);
56
53
 
57
- this.endingCell = endingCell;
58
54
  this.page = saved.page;
59
55
  this.x = this.x + this.lastColumnWidth + (offset || 0);
60
56
  this.y = saved.y;
@@ -64,10 +60,9 @@ DocumentContext.prototype.beginColumn = function (width, offset, endingCell) {
64
60
  this.lastColumnWidth = width;
65
61
  };
66
62
 
67
- DocumentContext.prototype.calculateBottomMost = function (destContext) {
68
- if (this.endingCell) {
69
- this.saveContextInEndingCell(this.endingCell);
70
- this.endingCell = null;
63
+ DocumentContext.prototype.calculateBottomMost = function (destContext, endingCell) {
64
+ if (endingCell) {
65
+ this.saveContextInEndingCell(endingCell);
71
66
  } else {
72
67
  destContext.bottomMost = bottomMostContext(this, destContext.bottomMost);
73
68
  }
@@ -93,12 +88,11 @@ DocumentContext.prototype.saveContextInEndingCell = function (endingCell) {
93
88
  };
94
89
  };
95
90
 
96
- DocumentContext.prototype.completeColumnGroup = function (height) {
91
+ DocumentContext.prototype.completeColumnGroup = function (height, endingCell) {
97
92
  var saved = this.snapshots.pop();
98
93
 
99
- this.calculateBottomMost(saved);
94
+ this.calculateBottomMost(saved, endingCell);
100
95
 
101
- this.endingCell = null;
102
96
  this.x = saved.x;
103
97
 
104
98
  var y = saved.bottomMost.y;
@@ -175,7 +169,6 @@ DocumentContext.prototype.beginDetachedBlock = function () {
175
169
  availableHeight: this.availableHeight,
176
170
  availableWidth: this.availableWidth,
177
171
  page: this.page,
178
- endingCell: this.endingCell,
179
172
  lastColumnWidth: this.lastColumnWidth
180
173
  });
181
174
  };
@@ -188,7 +181,6 @@ DocumentContext.prototype.endDetachedBlock = function () {
188
181
  this.availableWidth = saved.availableWidth;
189
182
  this.availableHeight = saved.availableHeight;
190
183
  this.page = saved.page;
191
- this.endingCell = saved.endingCell;
192
184
  this.lastColumnWidth = saved.lastColumnWidth;
193
185
  };
194
186
 
package/src/helpers.js CHANGED
@@ -32,6 +32,17 @@ function isUndefined(variable) {
32
32
  return variable === undefined;
33
33
  }
34
34
 
35
+ /**
36
+ * @param {any} variable
37
+ * @returns {boolean}
38
+ */
39
+ function isPositiveInteger(variable) {
40
+ if (!isNumber(variable) || !Number.isInteger(variable) || variable <= 0) {
41
+ return false;
42
+ }
43
+ return true;
44
+ }
45
+
35
46
  function pack() {
36
47
  var result = {};
37
48
 
@@ -117,6 +128,7 @@ module.exports = {
117
128
  isObject: isObject,
118
129
  isNull: isNull,
119
130
  isUndefined: isUndefined,
131
+ isPositiveInteger: isPositiveInteger,
120
132
  pack: pack,
121
133
  fontStringify: fontStringify,
122
134
  offsetVector: offsetVector,
@@ -1,51 +1,55 @@
1
- 'use strict';
2
-
3
- var fs = require('fs');
4
-
5
- function ImageMeasure(pdfKitDoc, imageDictionary) {
6
- this.pdfKitDoc = pdfKitDoc;
7
- this.imageDictionary = imageDictionary || {};
8
- }
9
-
10
- ImageMeasure.prototype.measureImage = function (src) {
11
- var image;
12
- var that = this;
13
-
14
- if (!this.pdfKitDoc._imageRegistry[src]) {
15
- try {
16
- image = this.pdfKitDoc.openImage(realImageSrc(src));
17
- if (!image) {
18
- throw 'No image';
19
- }
20
- } catch (error) {
21
- throw 'Invalid image: ' + error.toString() + '\nImages dictionary should contain dataURL entries (or local file paths in node.js)';
22
- }
23
- image.embed(this.pdfKitDoc);
24
- this.pdfKitDoc._imageRegistry[src] = image;
25
- } else {
26
- image = this.pdfKitDoc._imageRegistry[src];
27
- }
28
-
29
- return { width: image.width, height: image.height };
30
-
31
- function realImageSrc(src) {
32
- var img = that.imageDictionary[src];
33
-
34
- if (!img) {
35
- return src;
36
- }
37
-
38
- if (fs.existsSync(img)) {
39
- return fs.readFileSync(img);
40
- }
41
-
42
- var index = img.indexOf('base64,');
43
- if (index < 0) {
44
- return that.imageDictionary[src];
45
- }
46
-
47
- return Buffer.from(img.substring(index + 7), 'base64');
48
- }
49
- };
50
-
51
- module.exports = ImageMeasure;
1
+ 'use strict';
2
+
3
+ var fs = require('fs');
4
+
5
+ function ImageMeasure(pdfKitDoc, imageDictionary) {
6
+ this.pdfKitDoc = pdfKitDoc;
7
+ this.imageDictionary = imageDictionary || {};
8
+ }
9
+
10
+ ImageMeasure.prototype.measureImage = function (src) {
11
+ var image;
12
+ var that = this;
13
+
14
+ if (!this.pdfKitDoc._imageRegistry[src]) {
15
+ try {
16
+ image = this.pdfKitDoc.openImage(realImageSrc(src));
17
+ if (!image) {
18
+ throw 'No image';
19
+ }
20
+ } catch (error) {
21
+ throw 'Invalid image: ' + error.toString() + '\nImages dictionary should contain dataURL entries (or local file paths in node.js)';
22
+ }
23
+ image.embed(this.pdfKitDoc);
24
+ this.pdfKitDoc._imageRegistry[src] = image;
25
+ } else {
26
+ image = this.pdfKitDoc._imageRegistry[src];
27
+ }
28
+
29
+ return { width: image.width, height: image.height };
30
+
31
+ function realImageSrc(src) {
32
+ var img = that.imageDictionary[src];
33
+
34
+ if (!img) {
35
+ return src;
36
+ }
37
+
38
+ if (typeof img === 'object') {
39
+ throw 'Not supported image definition: ' + JSON.stringify(img);
40
+ }
41
+
42
+ if (fs.existsSync(img)) {
43
+ return fs.readFileSync(img);
44
+ }
45
+
46
+ var index = img.indexOf('base64,');
47
+ if (index < 0) {
48
+ return that.imageDictionary[src];
49
+ }
50
+
51
+ return Buffer.from(img.substring(index + 7), 'base64');
52
+ }
53
+ };
54
+
55
+ module.exports = ImageMeasure;
@@ -511,6 +511,21 @@ LayoutBuilder.prototype.processColumns = function (columnNode) {
511
511
  }
512
512
  };
513
513
 
514
+ LayoutBuilder.prototype.findStartingSpanCell = function (arr, i) {
515
+ let requiredColspan = 1;
516
+ for (let index = i - 1; index >= 0; index--) {
517
+ if (!arr[index]._span) {
518
+ if (arr[index].rowSpan > 1 && (arr[index].colSpan || 1) === requiredColspan) {
519
+ return arr[index];
520
+ } else {
521
+ return null;
522
+ }
523
+ }
524
+ requiredColspan++;
525
+ }
526
+ return null;
527
+ }
528
+
514
529
  LayoutBuilder.prototype.processRow = function (columns, widths, gaps, tableBody, tableRow, height) {
515
530
  var self = this;
516
531
  var pageBreaks = [], positions = [];
@@ -531,17 +546,52 @@ LayoutBuilder.prototype.processRow = function (columns, widths, gaps, tableBody,
531
546
  }
532
547
  }
533
548
 
534
- self.writer.context().beginColumn(width, leftOffset, getEndingCell(column, i));
549
+ // if rowspan starts in this cell, we retrieve the last cell affected by the rowspan
550
+ var endingCell = getEndingCell(column, i);
551
+ if (endingCell) {
552
+ // We store a reference of the ending cell in the first cell of the rowspan
553
+ column._endingCell = endingCell;
554
+ }
555
+
556
+ // Check if exists and retrieve the cell that started the rowspan in case we are in the cell just after
557
+ var startingSpanCell = self.findStartingSpanCell(columns, i);
558
+ var endingSpanCell = null;
559
+ if (startingSpanCell && startingSpanCell._endingCell) {
560
+ // Reference to the last cell of the rowspan
561
+ endingSpanCell = startingSpanCell._endingCell;
562
+ }
563
+
564
+ // We pass the endingSpanCell reference to store the context just after processing rowspan cell
565
+ self.writer.context().beginColumn(width, leftOffset, endingSpanCell);
535
566
  if (!column._span) {
536
567
  self.processNode(column);
537
568
  addAll(positions, column.positions);
538
569
  } else if (column._columnEndingContext) {
539
570
  // row-span ending
571
+ // Recover the context after processing the rowspanned cell
540
572
  self.writer.context().markEnding(column);
541
573
  }
542
574
  }
543
575
 
544
- self.writer.context().completeColumnGroup(height);
576
+ // Check if last cell is part of a span
577
+ var endingSpanCell = null;
578
+ var lastColumn = columns.length > 0 ? columns[columns.length - 1] : null;
579
+ if (lastColumn) {
580
+ // Previous column cell has a rowspan
581
+ if (lastColumn._endingCell) {
582
+ endingSpanCell = lastColumn._endingCell;
583
+ // Previous column cell is part of a span
584
+ } else if (lastColumn._span === true) {
585
+ // We get the cell that started the span where we set a reference to the ending cell
586
+ var startingSpanCell = self.findStartingSpanCell(columns, columns.length);
587
+ if (startingSpanCell) {
588
+ // Context will be stored here (ending cell)
589
+ endingSpanCell = startingSpanCell._endingCell;
590
+ }
591
+ }
592
+ }
593
+
594
+ self.writer.context().completeColumnGroup(height, endingSpanCell);
545
595
  });
546
596
 
547
597
  return { pageBreaks: pageBreaks, positions: positions };
@@ -84,50 +84,9 @@ StyleContextStack.prototype.autopush = function (item) {
84
84
  this.push(styleNames[i]);
85
85
  }
86
86
 
87
- var styleProperties = [
88
- 'font',
89
- 'fontSize',
90
- 'fontFeatures',
91
- 'bold',
92
- 'italics',
93
- 'alignment',
94
- 'color',
95
- 'columnGap',
96
- 'fillColor',
97
- 'fillOpacity',
98
- 'decoration',
99
- 'decorationStyle',
100
- 'decorationColor',
101
- 'background',
102
- 'lineHeight',
103
- 'characterSpacing',
104
- 'noWrap',
105
- 'markerColor',
106
- 'leadingIndent',
107
- 'sup',
108
- 'sub'
109
- //'tableCellPadding'
110
- // 'cellBorder',
111
- // 'headerCellBorder',
112
- // 'oddRowCellBorder',
113
- // 'evenRowCellBorder',
114
- // 'tableBorder'
115
- ];
116
- var styleOverrideObject = {};
117
- var pushStyleOverrideObject = false;
118
-
119
- styleProperties.forEach(function (key) {
120
- if (!isUndefined(item[key]) && !isNull(item[key])) {
121
- styleOverrideObject[key] = item[key];
122
- pushStyleOverrideObject = true;
123
- }
124
- });
125
-
126
- if (pushStyleOverrideObject) {
127
- this.push(styleOverrideObject);
128
- }
129
-
130
- return styleNames.length + (pushStyleOverrideObject ? 1 : 0);
87
+ // rather than spend significant time making a styleOverrideObject, just add item
88
+ this.push(item);
89
+ return styleNames.length + 1;
131
90
  };
132
91
 
133
92
  /**
@@ -3,6 +3,7 @@
3
3
  var ColumnCalculator = require('./columnCalculator');
4
4
  var isFunction = require('./helpers').isFunction;
5
5
  var isNumber = require('./helpers').isNumber;
6
+ var isPositiveInteger = require('./helpers').isPositiveInteger;
6
7
 
7
8
  function TableProcessor(tableNode) {
8
9
  this.tableNode = tableNode;
@@ -18,18 +19,34 @@ TableProcessor.prototype.beginTable = function (writer) {
18
19
  this.layout = tableNode._layout;
19
20
 
20
21
  availableWidth = writer.context().availableWidth - this.offsets.total;
21
- ColumnCalculator.buildColumnWidths(tableNode.table.widths, availableWidth);
22
+ ColumnCalculator.buildColumnWidths(tableNode.table.widths, availableWidth, this.offsets.total, tableNode);
22
23
 
23
24
  this.tableWidth = tableNode._offsets.total + getTableInnerContentWidth();
24
25
  this.rowSpanData = prepareRowSpanData();
25
26
  this.cleanUpRepeatables = false;
26
27
 
27
- this.headerRows = tableNode.table.headerRows || 0;
28
- if (this.headerRows > tableNode.table.body.length) {
29
- throw new Error(`Too few rows in the table. Property headerRows requires at least ${this.headerRows}, contains only ${tableNode.table.body.length}`);
28
+ // headersRows and rowsWithoutPageBreak (headerRows + keepWithHeaderRows)
29
+ this.headerRows = 0;
30
+ this.rowsWithoutPageBreak = 0;
31
+
32
+ var headerRows = tableNode.table.headerRows;
33
+
34
+ if (isPositiveInteger(headerRows)) {
35
+ this.headerRows = headerRows;
36
+
37
+ if (this.headerRows > tableNode.table.body.length) {
38
+ throw new Error(`Too few rows in the table. Property headerRows requires at least ${this.headerRows}, contains only ${tableNode.table.body.length}`);
39
+ }
40
+
41
+ this.rowsWithoutPageBreak = this.headerRows;
42
+
43
+ const keepWithHeaderRows = tableNode.table.keepWithHeaderRows;
44
+
45
+ if (isPositiveInteger(keepWithHeaderRows)) {
46
+ this.rowsWithoutPageBreak += keepWithHeaderRows;
47
+ }
30
48
  }
31
49
 
32
- this.rowsWithoutPageBreak = this.headerRows + (tableNode.table.keepWithHeaderRows || 0);
33
50
  this.dontBreakRows = tableNode.table.dontBreakRows || false;
34
51
 
35
52
  if (this.rowsWithoutPageBreak) {