pdfmake 0.3.0-beta.17 → 0.3.0-beta.19

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +2 -7
  3. package/build/pdfmake.js +33324 -33106
  4. package/build/pdfmake.js.map +1 -1
  5. package/build/pdfmake.min.js +2 -2
  6. package/build/pdfmake.min.js.map +1 -1
  7. package/build/vfs_fonts.js +4 -4
  8. package/fonts/Roboto/Roboto-Italic.ttf +0 -0
  9. package/fonts/Roboto/Roboto-Medium.ttf +0 -0
  10. package/fonts/Roboto/Roboto-MediumItalic.ttf +0 -0
  11. package/fonts/Roboto/Roboto-Regular.ttf +0 -0
  12. package/js/DocMeasure.js +12 -1
  13. package/js/DocPreprocessor.js +21 -6
  14. package/js/DocumentContext.js +5 -4
  15. package/js/LayoutBuilder.js +137 -34
  16. package/js/OutputDocument.js +22 -34
  17. package/js/OutputDocumentServer.js +6 -11
  18. package/js/PageElementWriter.js +2 -2
  19. package/js/Printer.js +132 -145
  20. package/js/StyleContextStack.js +4 -0
  21. package/js/TextBreaker.js +30 -3
  22. package/js/URLResolver.js +23 -62
  23. package/js/browser-extensions/OutputDocumentBrowser.js +35 -72
  24. package/js/browser-extensions/index.js +2 -2
  25. package/package.json +18 -18
  26. package/src/DocMeasure.js +15 -1
  27. package/src/DocPreprocessor.js +25 -6
  28. package/src/DocumentContext.js +4 -4
  29. package/src/LayoutBuilder.js +169 -39
  30. package/src/OutputDocument.js +23 -37
  31. package/src/OutputDocumentServer.js +6 -11
  32. package/src/PageElementWriter.js +2 -2
  33. package/src/Printer.js +131 -143
  34. package/src/StyleContextStack.js +4 -0
  35. package/src/TextBreaker.js +23 -4
  36. package/src/URLResolver.js +23 -68
  37. package/src/browser-extensions/OutputDocumentBrowser.js +33 -71
  38. package/src/browser-extensions/index.js +2 -2
  39. package/js/browser-extensions/URLBrowserResolver.js +0 -81
  40. package/src/browser-extensions/URLBrowserResolver.js +0 -89
Binary file
Binary file
Binary file
package/js/DocMeasure.js CHANGED
@@ -31,11 +31,16 @@ class DocMeasure {
31
31
  measureDocument(docStructure) {
32
32
  return this.measureNode(docStructure);
33
33
  }
34
+ measureBlock(node) {
35
+ return this.measureNode(node);
36
+ }
34
37
  measureNode(node) {
35
38
  return this.styleStack.auto(node, () => {
36
39
  // TODO: refactor + rethink whether this is the proper way to handle margins
37
40
  node._margin = (0, _node.getNodeMargin)(node, this.styleStack);
38
- if (node.columns) {
41
+ if (node.section) {
42
+ return extendMargins(this.measureSection(node));
43
+ } else if (node.columns) {
39
44
  return extendMargins(this.measureColumns(node));
40
45
  } else if (node.stack) {
41
46
  return extendMargins(this.measureVerticalContainer(node));
@@ -423,6 +428,12 @@ class DocMeasure {
423
428
  }
424
429
  return node;
425
430
  }
431
+ measureSection(node) {
432
+ // TODO: properties
433
+
434
+ node.section = this.measureNode(node.section);
435
+ return node;
436
+ }
426
437
  measureColumns(node) {
427
438
  let columns = node.columns;
428
439
  node._gap = this.styleStack.getProperty('columnGap') || 0;
@@ -22,9 +22,15 @@ class DocPreprocessor {
22
22
  this.parentNode = null;
23
23
  this.tocs = [];
24
24
  this.nodeReferences = [];
25
- return this.preprocessNode(docStructure);
25
+ return this.preprocessNode(docStructure, true);
26
26
  }
27
- preprocessNode(node) {
27
+ preprocessBlock(node) {
28
+ this.parentNode = null;
29
+ this.tocs = [];
30
+ this.nodeReferences = [];
31
+ return this.preprocessNode(node);
32
+ }
33
+ preprocessNode(node, isSectionAllowed = false) {
28
34
  // expand shortcuts and casting values
29
35
  if (Array.isArray(node)) {
30
36
  node = {
@@ -39,10 +45,15 @@ class DocPreprocessor {
39
45
  // cast value in text property
40
46
  node.text = convertValueToString(node.text);
41
47
  }
42
- if (node.columns) {
48
+ if (node.section) {
49
+ if (!isSectionAllowed) {
50
+ throw new Error(`Incorrect document structure, section node is only allowed at the root level of document structure: ${(0, _node.stringifyNode)(node)}`);
51
+ }
52
+ return this.preprocessSection(node);
53
+ } else if (node.columns) {
43
54
  return this.preprocessColumns(node);
44
55
  } else if (node.stack) {
45
- return this.preprocessVerticalContainer(node);
56
+ return this.preprocessVerticalContainer(node, isSectionAllowed);
46
57
  } else if (node.ul) {
47
58
  return this.preprocessList(node);
48
59
  } else if (node.ol) {
@@ -69,6 +80,10 @@ class DocPreprocessor {
69
80
  throw new Error(`Unrecognized document structure: ${(0, _node.stringifyNode)(node)}`);
70
81
  }
71
82
  }
83
+ preprocessSection(node) {
84
+ node.section = this.preprocessNode(node.section);
85
+ return node;
86
+ }
72
87
  preprocessColumns(node) {
73
88
  let columns = node.columns;
74
89
  for (let i = 0, l = columns.length; i < l; i++) {
@@ -76,10 +91,10 @@ class DocPreprocessor {
76
91
  }
77
92
  return node;
78
93
  }
79
- preprocessVerticalContainer(node) {
94
+ preprocessVerticalContainer(node, isSectionAllowed) {
80
95
  let items = node.stack;
81
96
  for (let i = 0, l = items.length; i < l; i++) {
82
- items[i] = this.preprocessNode(items[i]);
97
+ items[i] = this.preprocessNode(items[i], isSectionAllowed);
83
98
  }
84
99
  return node;
85
100
  }
@@ -204,7 +204,7 @@ class DocumentContext extends _events.EventEmitter {
204
204
  let currentAvailableWidth = this.availableWidth;
205
205
  let currentPageOrientation = this.getCurrentPage().pageSize.orientation;
206
206
  let pageSize = getPageSize(this.getCurrentPage(), pageOrientation);
207
- this.addPage(pageSize);
207
+ this.addPage(pageSize, null, this.getCurrentPage().customProperties);
208
208
  if (currentPageOrientation === pageSize.orientation) {
209
209
  this.availableWidth = currentAvailableWidth;
210
210
  }
@@ -219,7 +219,7 @@ class DocumentContext extends _events.EventEmitter {
219
219
  y: this.y
220
220
  };
221
221
  }
222
- addPage(pageSize, pageMargin = null) {
222
+ addPage(pageSize, pageMargin = null, customProperties = {}) {
223
223
  if (pageMargin !== null) {
224
224
  this.pageMargins = pageMargin;
225
225
  this.x = pageMargin.left;
@@ -228,13 +228,14 @@ class DocumentContext extends _events.EventEmitter {
228
228
  let page = {
229
229
  items: [],
230
230
  pageSize: pageSize,
231
- pageMargins: this.pageMargins
231
+ pageMargins: this.pageMargins,
232
+ customProperties: customProperties
232
233
  };
233
234
  this.pages.push(page);
234
235
  this.backgroundLength.push(0);
235
236
  this.page = this.pages.length - 1;
236
237
  this.initializePage();
237
- this.emit('pageAdded');
238
+ this.emit('pageAdded', page);
238
239
  return page;
239
240
  }
240
241
  getCurrentPage() {
@@ -62,7 +62,16 @@ class LayoutBuilder {
62
62
  if (typeof pageBreakBeforeFct !== 'function') {
63
63
  return false;
64
64
  }
65
- linearNodeList = linearNodeList.filter(node => node.positions.length > 0);
65
+ const hasRenderableContent = node => {
66
+ if (!node || node.positions.length === 0) {
67
+ return false;
68
+ }
69
+ if (node.text === '' && !node.listMarker) {
70
+ return false;
71
+ }
72
+ return true;
73
+ };
74
+ linearNodeList = linearNodeList.filter(hasRenderableContent);
66
75
  linearNodeList.forEach(node => {
67
76
  let nodeInfo = {};
68
77
  ['id', 'text', 'ul', 'ol', 'table', 'image', 'qr', 'canvas', 'svg', 'columns', 'headlineLevel', 'style', 'pageBreak', 'pageOrientation', 'width', 'height'].forEach(key => {
@@ -132,19 +141,31 @@ class LayoutBuilder {
132
141
  return result.pages;
133
142
  }
134
143
  tryLayoutDocument(docStructure, pdfDocument, styleDictionary, defaultStyle, background, header, footer, watermark) {
144
+ const isNecessaryAddFirstPage = docStructure => {
145
+ if (docStructure.stack && docStructure.stack.length > 0 && docStructure.stack[0].section) {
146
+ return false;
147
+ } else if (docStructure.section) {
148
+ return false;
149
+ }
150
+ return true;
151
+ };
135
152
  this.linearNodeList = [];
136
153
  docStructure = this.docPreprocessor.preprocessDocument(docStructure);
137
154
  docStructure = this.docMeasure.measureDocument(docStructure);
138
155
  this.writer = new _PageElementWriter.default(new _DocumentContext.default());
139
- this.writer.context().addListener('pageAdded', () => {
140
- this.addBackground(background);
156
+ this.writer.context().addListener('pageAdded', page => {
157
+ let backgroundGetter = background;
158
+ if (page.customProperties['background'] || page.customProperties['background'] === null) {
159
+ backgroundGetter = page.customProperties['background'];
160
+ }
161
+ this.addBackground(backgroundGetter);
141
162
  });
142
- this.writer.addPage(this.pageSize, null, this.pageMargins);
163
+ if (isNecessaryAddFirstPage(docStructure)) {
164
+ this.writer.addPage(this.pageSize, null, this.pageMargins);
165
+ }
143
166
  this.processNode(docStructure);
144
167
  this.addHeadersAndFooters(header, footer);
145
- if (watermark != null) {
146
- this.addWatermark(watermark, pdfDocument, defaultStyle);
147
- }
168
+ this.addWatermark(watermark, pdfDocument, defaultStyle);
148
169
  return {
149
170
  pages: this.writer.context().pages,
150
171
  linearNodeList: this.linearNodeList
@@ -157,22 +178,30 @@ class LayoutBuilder {
157
178
  let pageBackground = backgroundGetter(context.page + 1, pageSize);
158
179
  if (pageBackground) {
159
180
  this.writer.beginUnbreakableBlock(pageSize.width, pageSize.height);
160
- pageBackground = this.docPreprocessor.preprocessDocument(pageBackground);
161
- this.processNode(this.docMeasure.measureDocument(pageBackground));
181
+ pageBackground = this.docPreprocessor.preprocessBlock(pageBackground);
182
+ this.processNode(this.docMeasure.measureBlock(pageBackground));
162
183
  this.writer.commitUnbreakableBlock(0, 0);
163
184
  context.backgroundLength[context.page] += pageBackground.positions.length;
164
185
  }
165
186
  }
166
- addDynamicRepeatable(nodeGetter, sizeFunction) {
187
+ addDynamicRepeatable(nodeGetter, sizeFunction, customPropertyName) {
167
188
  let pages = this.writer.context().pages;
168
189
  for (let pageIndex = 0, l = pages.length; pageIndex < l; pageIndex++) {
169
190
  this.writer.context().page = pageIndex;
170
- let node = nodeGetter(pageIndex + 1, l, this.writer.context().pages[pageIndex].pageSize);
191
+ let customProperties = this.writer.context().getCurrentPage().customProperties;
192
+ let pageNodeGetter = nodeGetter;
193
+ if (customProperties[customPropertyName] || customProperties[customPropertyName] === null) {
194
+ pageNodeGetter = customProperties[customPropertyName];
195
+ }
196
+ if (typeof pageNodeGetter === 'undefined' || pageNodeGetter === null) {
197
+ continue;
198
+ }
199
+ let node = pageNodeGetter(pageIndex + 1, l, this.writer.context().pages[pageIndex].pageSize);
171
200
  if (node) {
172
201
  let sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.writer.context().getCurrentPage().pageMargins);
173
202
  this.writer.beginUnbreakableBlock(sizes.width, sizes.height);
174
- node = this.docPreprocessor.preprocessDocument(node);
175
- this.processNode(this.docMeasure.measureDocument(node));
203
+ node = this.docPreprocessor.preprocessBlock(node);
204
+ this.processNode(this.docMeasure.measureBlock(node));
176
205
  this.writer.commitUnbreakableBlock(sizes.x, sizes.y);
177
206
  }
178
207
  }
@@ -190,27 +219,30 @@ class LayoutBuilder {
190
219
  width: pageSize.width,
191
220
  height: pageMargins.bottom
192
221
  });
193
- if (header) {
194
- this.addDynamicRepeatable(header, headerSizeFct);
195
- }
196
- if (footer) {
197
- this.addDynamicRepeatable(footer, footerSizeFct);
198
- }
222
+ this.addDynamicRepeatable(header, headerSizeFct, 'header');
223
+ this.addDynamicRepeatable(footer, footerSizeFct, 'footer');
199
224
  }
200
225
  addWatermark(watermark, pdfDocument, defaultStyle) {
201
- if ((0, _variableType.isString)(watermark)) {
202
- watermark = {
203
- 'text': watermark
204
- };
205
- }
206
- if (!watermark.text) {
207
- // empty watermark text
208
- return;
209
- }
210
226
  let pages = this.writer.context().pages;
211
227
  for (let i = 0, l = pages.length; i < l; i++) {
228
+ let pageWatermark = watermark;
229
+ if (pages[i].customProperties['watermark'] || pages[i].customProperties['watermark'] === null) {
230
+ pageWatermark = pages[i].customProperties['watermark'];
231
+ }
232
+ if (pageWatermark === undefined || pageWatermark === null) {
233
+ continue;
234
+ }
235
+ if ((0, _variableType.isString)(pageWatermark)) {
236
+ pageWatermark = {
237
+ 'text': pageWatermark
238
+ };
239
+ }
240
+ if (!pageWatermark.text) {
241
+ // empty watermark text
242
+ continue;
243
+ }
212
244
  pages[i].watermark = getWatermarkObject({
213
- ...watermark
245
+ ...pageWatermark
214
246
  }, pages[i].pageSize, pdfDocument, defaultStyle);
215
247
  }
216
248
  function getWatermarkObject(watermark, pageSize, pdfDocument, defaultStyle) {
@@ -391,6 +423,8 @@ class LayoutBuilder {
391
423
  }
392
424
  if (node.stack) {
393
425
  this.processVerticalContainer(node);
426
+ } else if (node.section) {
427
+ this.processSection(node);
394
428
  } else if (node.columns) {
395
429
  this.processColumns(node);
396
430
  } else if (node.ul) {
@@ -435,6 +469,62 @@ class LayoutBuilder {
435
469
  }, this);
436
470
  }
437
471
 
472
+ // section
473
+ processSection(sectionNode) {
474
+ // TODO: properties
475
+
476
+ let page = this.writer.context().getCurrentPage();
477
+ if (!page || page && page.items.length) {
478
+ // move to new empty page
479
+ // page definition inherit from current page
480
+ if (sectionNode.pageSize === 'inherit') {
481
+ sectionNode.pageSize = page ? {
482
+ width: page.pageSize.width,
483
+ height: page.pageSize.height
484
+ } : undefined;
485
+ }
486
+ if (sectionNode.pageOrientation === 'inherit') {
487
+ sectionNode.pageOrientation = page ? page.pageSize.orientation : undefined;
488
+ }
489
+ if (sectionNode.pageMargins === 'inherit') {
490
+ sectionNode.pageMargins = page ? page.pageMargins : undefined;
491
+ }
492
+ if (sectionNode.header === 'inherit') {
493
+ sectionNode.header = page ? page.customProperties.header : undefined;
494
+ }
495
+ if (sectionNode.footer === 'inherit') {
496
+ sectionNode.footer = page ? page.customProperties.footer : undefined;
497
+ }
498
+ if (sectionNode.background === 'inherit') {
499
+ sectionNode.background = page ? page.customProperties.background : undefined;
500
+ }
501
+ if (sectionNode.watermark === 'inherit') {
502
+ sectionNode.watermark = page ? page.customProperties.watermark : undefined;
503
+ }
504
+ if (sectionNode.header && typeof sectionNode.header !== 'function' && sectionNode.header !== null) {
505
+ sectionNode.header = (0, _tools.convertToDynamicContent)(sectionNode.header);
506
+ }
507
+ if (sectionNode.footer && typeof sectionNode.footer !== 'function' && sectionNode.footer !== null) {
508
+ sectionNode.footer = (0, _tools.convertToDynamicContent)(sectionNode.footer);
509
+ }
510
+ let customProperties = {};
511
+ if (typeof sectionNode.header !== 'undefined') {
512
+ customProperties.header = sectionNode.header;
513
+ }
514
+ if (typeof sectionNode.footer !== 'undefined') {
515
+ customProperties.footer = sectionNode.footer;
516
+ }
517
+ if (typeof sectionNode.background !== 'undefined') {
518
+ customProperties.background = sectionNode.background;
519
+ }
520
+ if (typeof sectionNode.watermark !== 'undefined') {
521
+ customProperties.watermark = sectionNode.watermark;
522
+ }
523
+ this.writer.addPage(sectionNode.pageSize || this.pageSize, sectionNode.pageOrientation, sectionNode.pageMargins || this.pageMargins, customProperties);
524
+ }
525
+ this.processNode(sectionNode.section);
526
+ }
527
+
438
528
  // columns
439
529
  processColumns(columnNode) {
440
530
  this.nestedLevel++;
@@ -912,6 +1002,23 @@ class LayoutBuilder {
912
1002
  }
913
1003
  return newInline;
914
1004
  }
1005
+ function findMaxFitLength(text, maxWidth, measureFn) {
1006
+ let low = 1;
1007
+ let high = text.length;
1008
+ let bestFit = 1;
1009
+ while (low <= high) {
1010
+ const mid = Math.floor((low + high) / 2);
1011
+ const part = text.substring(0, mid);
1012
+ const width = measureFn(part);
1013
+ if (width <= maxWidth) {
1014
+ bestFit = mid;
1015
+ low = mid + 1;
1016
+ } else {
1017
+ high = mid - 1;
1018
+ }
1019
+ }
1020
+ return bestFit;
1021
+ }
915
1022
  if (!textNode._inlines || textNode._inlines.length === 0) {
916
1023
  return null;
917
1024
  }
@@ -923,11 +1030,7 @@ class LayoutBuilder {
923
1030
  let inline = textNode._inlines.shift();
924
1031
  isForceContinue = false;
925
1032
  if (!inline.noWrap && inline.text.length > 1 && inline.width > line.getAvailableWidth()) {
926
- let widthPerChar = inline.width / inline.text.length;
927
- let maxChars = Math.floor(line.getAvailableWidth() / widthPerChar);
928
- if (maxChars < 1) {
929
- maxChars = 1;
930
- }
1033
+ let maxChars = findMaxFitLength(inline.text, line.getAvailableWidth(), txt => textInlines.widthOfText(txt, inline));
931
1034
  if (maxChars < inline.text.length) {
932
1035
  let newInline = cloneInline(inline);
933
1036
  newInline.text = inline.text.substr(maxChars);
@@ -23,26 +23,24 @@ class OutputDocument {
23
23
  * @returns {Promise<Buffer>}
24
24
  */
25
25
  getBuffer() {
26
- if (this.bufferPromise === null) {
27
- this.bufferPromise = new Promise((resolve, reject) => {
28
- this.getStream().then(stream => {
29
- let chunks = [];
30
- let result;
31
- stream.on('readable', () => {
32
- let chunk;
33
- while ((chunk = stream.read(this.bufferSize)) !== null) {
34
- chunks.push(chunk);
35
- }
36
- });
37
- stream.on('end', () => {
38
- result = Buffer.concat(chunks);
39
- resolve(result);
40
- });
41
- stream.end();
42
- }, result => {
43
- reject(result);
26
+ const getBufferInternal = async () => {
27
+ const stream = await this.getStream();
28
+ return new Promise(resolve => {
29
+ let chunks = [];
30
+ stream.on('readable', () => {
31
+ let chunk;
32
+ while ((chunk = stream.read(this.bufferSize)) !== null) {
33
+ chunks.push(chunk);
34
+ }
35
+ });
36
+ stream.on('end', () => {
37
+ resolve(Buffer.concat(chunks));
44
38
  });
39
+ stream.end();
45
40
  });
41
+ };
42
+ if (this.bufferPromise === null) {
43
+ this.bufferPromise = getBufferInternal();
46
44
  }
47
45
  return this.bufferPromise;
48
46
  }
@@ -50,27 +48,17 @@ class OutputDocument {
50
48
  /**
51
49
  * @returns {Promise<string>}
52
50
  */
53
- getBase64() {
54
- return new Promise((resolve, reject) => {
55
- this.getBuffer().then(buffer => {
56
- resolve(buffer.toString('base64'));
57
- }, result => {
58
- reject(result);
59
- });
60
- });
51
+ async getBase64() {
52
+ const buffer = await this.getBuffer();
53
+ return buffer.toString('base64');
61
54
  }
62
55
 
63
56
  /**
64
57
  * @returns {Promise<string>}
65
58
  */
66
- getDataUrl() {
67
- return new Promise((resolve, reject) => {
68
- this.getBase64().then(data => {
69
- resolve('data:application/pdf;base64,' + data);
70
- }, result => {
71
- reject(result);
72
- });
73
- });
59
+ async getDataUrl() {
60
+ const data = await this.getBase64();
61
+ return 'data:application/pdf;base64,' + data;
74
62
  }
75
63
  }
76
64
  var _default = exports.default = OutputDocument;
@@ -10,17 +10,12 @@ class OutputDocumentServer extends _OutputDocument.default {
10
10
  * @param {string} filename
11
11
  * @returns {Promise}
12
12
  */
13
- write(filename) {
14
- return new Promise((resolve, reject) => {
15
- this.getStream().then(stream => {
16
- stream.pipe(_fs.default.createWriteStream(filename));
17
- stream.on('end', () => {
18
- resolve();
19
- });
20
- stream.end();
21
- }, result => {
22
- reject(result);
23
- });
13
+ async write(filename) {
14
+ const stream = await this.getStream();
15
+ return new Promise(resolve => {
16
+ stream.pipe(_fs.default.createWriteStream(filename));
17
+ stream.on('end', resolve);
18
+ stream.end();
24
19
  });
25
20
  }
26
21
  }
@@ -73,10 +73,10 @@ class PageElementWriter extends _ElementWriter.default {
73
73
  y: this.context().y
74
74
  });
75
75
  }
76
- addPage(pageSize, pageOrientation, pageMargin) {
76
+ addPage(pageSize, pageOrientation, pageMargin, customProperties = {}) {
77
77
  let prevPage = this.page;
78
78
  let prevY = this.y;
79
- this.context().addPage((0, _PageSize.normalizePageSize)(pageSize, pageOrientation), (0, _PageSize.normalizePageMargin)(pageMargin));
79
+ this.context().addPage((0, _PageSize.normalizePageSize)(pageSize, pageOrientation), (0, _PageSize.normalizePageMargin)(pageMargin), customProperties);
80
80
  this.emit('pageChanged', {
81
81
  prevPage: prevPage,
82
82
  prevY: prevY,