pdfmake 0.3.2 → 0.3.4

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 (94) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/LICENSE +21 -21
  3. package/README.md +75 -78
  4. package/build/fonts/Roboto/Roboto-Italic.ttf +0 -0
  5. package/build/fonts/Roboto/Roboto-Medium.ttf +0 -0
  6. package/build/fonts/Roboto/Roboto-MediumItalic.ttf +0 -0
  7. package/build/fonts/Roboto/Roboto-Regular.ttf +0 -0
  8. package/build/fonts/Roboto.js +27 -0
  9. package/build/pdfmake.js +64813 -64584
  10. package/build/pdfmake.js.map +1 -1
  11. package/build/pdfmake.min.js +2 -2
  12. package/build/pdfmake.min.js.map +1 -1
  13. package/build/standard-fonts/Courier.js +27 -27
  14. package/build/standard-fonts/Helvetica.js +27 -27
  15. package/build/standard-fonts/Symbol.js +21 -21
  16. package/build/standard-fonts/Times.js +27 -27
  17. package/build/standard-fonts/ZapfDingbats.js +21 -21
  18. package/build/vfs_fonts.js +5 -5
  19. package/build-vfs.js +44 -44
  20. package/fonts/Roboto.js +8 -8
  21. package/js/3rd-party/svg-to-pdfkit/source.js +1 -1
  22. package/js/DocMeasure.js +11 -6
  23. package/js/DocumentContext.js +8 -3
  24. package/js/ElementWriter.js +42 -16
  25. package/js/LayoutBuilder.js +144 -78
  26. package/js/Line.js +16 -16
  27. package/js/OutputDocument.js +10 -10
  28. package/js/OutputDocumentServer.js +3 -3
  29. package/js/PDFDocument.js +3 -3
  30. package/js/PageElementWriter.js +15 -9
  31. package/js/Printer.js +28 -28
  32. package/js/Renderer.js +40 -8
  33. package/js/SVGMeasure.js +10 -10
  34. package/js/StyleContextStack.js +74 -51
  35. package/js/TableProcessor.js +14 -0
  36. package/js/TextBreaker.js +17 -17
  37. package/js/TextDecorator.js +12 -3
  38. package/js/TextInlines.js +34 -33
  39. package/js/base.js +4 -4
  40. package/js/browser-extensions/OutputDocumentBrowser.js +24 -24
  41. package/js/columnCalculator.js +2 -2
  42. package/js/helpers/node.js +47 -23
  43. package/js/helpers/variableType.js +18 -18
  44. package/js/qrEnc.js +38 -38
  45. package/js/virtual-fs.js +11 -11
  46. package/package.json +12 -12
  47. package/src/3rd-party/svg-to-pdfkit/LICENSE +9 -9
  48. package/src/3rd-party/svg-to-pdfkit/source.js +2745 -2745
  49. package/src/3rd-party/svg-to-pdfkit.js +3 -3
  50. package/src/DocMeasure.js +745 -738
  51. package/src/DocPreprocessor.js +283 -283
  52. package/src/DocumentContext.js +345 -338
  53. package/src/ElementWriter.js +441 -417
  54. package/src/LayoutBuilder.js +1336 -1258
  55. package/src/Line.js +114 -114
  56. package/src/OutputDocument.js +64 -64
  57. package/src/OutputDocumentServer.js +32 -32
  58. package/src/PDFDocument.js +174 -174
  59. package/src/PageElementWriter.js +187 -179
  60. package/src/PageSize.js +53 -53
  61. package/src/Printer.js +306 -306
  62. package/src/Renderer.js +445 -409
  63. package/src/SVGMeasure.js +109 -109
  64. package/src/StyleContextStack.js +208 -179
  65. package/src/TableProcessor.js +620 -602
  66. package/src/TextBreaker.js +168 -168
  67. package/src/TextDecorator.js +175 -161
  68. package/src/TextInlines.js +224 -223
  69. package/src/URLResolver.js +43 -43
  70. package/src/base.js +70 -70
  71. package/src/browser-extensions/OutputDocumentBrowser.js +80 -80
  72. package/src/browser-extensions/fonts/Roboto.js +27 -27
  73. package/src/browser-extensions/index.js +55 -55
  74. package/src/browser-extensions/pdfMake.js +1 -1
  75. package/src/browser-extensions/standard-fonts/Courier.js +27 -27
  76. package/src/browser-extensions/standard-fonts/Helvetica.js +27 -27
  77. package/src/browser-extensions/standard-fonts/Symbol.js +21 -21
  78. package/src/browser-extensions/standard-fonts/Times.js +27 -27
  79. package/src/browser-extensions/standard-fonts/ZapfDingbats.js +21 -21
  80. package/src/browser-extensions/virtual-fs-cjs.js +1 -1
  81. package/src/columnCalculator.js +154 -154
  82. package/src/helpers/node.js +134 -110
  83. package/src/helpers/tools.js +44 -44
  84. package/src/helpers/variableType.js +50 -50
  85. package/src/index.js +16 -16
  86. package/src/qrEnc.js +796 -796
  87. package/src/standardPageSizes.js +52 -52
  88. package/src/tableLayouts.js +100 -100
  89. package/src/virtual-fs.js +66 -66
  90. package/standard-fonts/Courier.js +8 -8
  91. package/standard-fonts/Helvetica.js +8 -8
  92. package/standard-fonts/Symbol.js +5 -5
  93. package/standard-fonts/Times.js +8 -8
  94. package/standard-fonts/ZapfDingbats.js +5 -5
package/src/DocMeasure.js CHANGED
@@ -1,738 +1,745 @@
1
- import TextInlines from './TextInlines';
2
- import StyleContextStack from './StyleContextStack';
3
- import ColumnCalculator from './columnCalculator';
4
- import { defaultTableLayout } from './tableLayouts';
5
- import { isString, isNumber, isObject } from './helpers/variableType';
6
- import { stringifyNode, getNodeId, getNodeMargin } from './helpers/node';
7
- import { pack } from './helpers/tools';
8
- import qrEncoder from './qrEnc.js';
9
-
10
- class DocMeasure {
11
- constructor(
12
- pdfDocument,
13
- styleDictionary,
14
- defaultStyle,
15
- svgMeasure,
16
- tableLayouts
17
- ) {
18
- this.pdfDocument = pdfDocument;
19
- this.textInlines = new TextInlines(pdfDocument);
20
- this.styleStack = new StyleContextStack(styleDictionary, defaultStyle);
21
- this.svgMeasure = svgMeasure;
22
- this.tableLayouts = tableLayouts;
23
- this.autoImageIndex = 1;
24
- }
25
-
26
- /**
27
- * Measures all nodes and sets min/max-width properties required for the second
28
- * layout-pass.
29
- *
30
- * @param {object} docStructure document-definition-object
31
- * @returns {object} document-measurement-object
32
- */
33
- measureDocument(docStructure) {
34
- return this.measureNode(docStructure);
35
- }
36
-
37
- measureBlock(node) {
38
- return this.measureNode(node);
39
- }
40
-
41
- measureNode(node) {
42
- return this.styleStack.auto(node, () => {
43
- // TODO: refactor + rethink whether this is the proper way to handle margins
44
- node._margin = getNodeMargin(node, this.styleStack);
45
-
46
- if (node.section) {
47
- return extendMargins(this.measureSection(node));
48
- } else if (node.columns) {
49
- return extendMargins(this.measureColumns(node));
50
- } else if (node.stack) {
51
- return extendMargins(this.measureVerticalContainer(node));
52
- } else if (node.ul) {
53
- return extendMargins(this.measureUnorderedList(node));
54
- } else if (node.ol) {
55
- return extendMargins(this.measureOrderedList(node));
56
- } else if (node.table) {
57
- return extendMargins(this.measureTable(node));
58
- } else if (node.text !== undefined) {
59
- return extendMargins(this.measureLeaf(node));
60
- } else if (node.toc) {
61
- return extendMargins(this.measureToc(node));
62
- } else if (node.image) {
63
- return extendMargins(this.measureImage(node));
64
- } else if (node.svg) {
65
- return extendMargins(this.measureSVG(node));
66
- } else if (node.canvas) {
67
- return extendMargins(this.measureCanvas(node));
68
- } else if (node.qr) {
69
- return extendMargins(this.measureQr(node));
70
- } else if (node.attachment) {
71
- return extendMargins(this.measureAttachment(node));
72
- } else {
73
- throw new Error(`Unrecognized document structure: ${stringifyNode(node)}`);
74
- }
75
- });
76
-
77
- function extendMargins(node) {
78
- let margin = node._margin;
79
-
80
- if (margin) {
81
- node._minWidth += margin[0] + margin[2];
82
- node._maxWidth += margin[0] + margin[2];
83
- }
84
-
85
- return node;
86
- }
87
- }
88
-
89
- measureImageWithDimensions(node, dimensions) {
90
- if (node.fit) {
91
- let factor = (dimensions.width / dimensions.height > node.fit[0] / node.fit[1]) ? node.fit[0] / dimensions.width : node.fit[1] / dimensions.height;
92
- node._width = node._minWidth = node._maxWidth = dimensions.width * factor;
93
- node._height = dimensions.height * factor;
94
- } else if (node.cover) {
95
- node._width = node._minWidth = node._maxWidth = node.cover.width;
96
- node._height = node._minHeight = node._maxHeight = node.cover.height;
97
- } else {
98
- let ratio = dimensions.width / dimensions.height;
99
-
100
- node._width = node._minWidth = node._maxWidth = node.width || (node.height ? (node.height * ratio) : dimensions.width);
101
- node._height = node.height || (node.width ? node.width / ratio : dimensions.height);
102
-
103
- if (isNumber(node.maxWidth) && node.maxWidth < node._width) {
104
- node._width = node._minWidth = node._maxWidth = node.maxWidth;
105
- node._height = node._width * dimensions.height / dimensions.width;
106
- }
107
-
108
- if (isNumber(node.maxHeight) && node.maxHeight < node._height) {
109
- node._height = node.maxHeight;
110
- node._width = node._minWidth = node._maxWidth = node._height * dimensions.width / dimensions.height;
111
- }
112
-
113
- if (isNumber(node.minWidth) && node.minWidth > node._width) {
114
- node._width = node._minWidth = node._maxWidth = node.minWidth;
115
- node._height = node._width * dimensions.height / dimensions.width;
116
- }
117
-
118
- if (isNumber(node.minHeight) && node.minHeight > node._height) {
119
- node._height = node.minHeight;
120
- node._width = node._minWidth = node._maxWidth = node._height * dimensions.width / dimensions.height;
121
- }
122
- }
123
-
124
- node._alignment = this.styleStack.getProperty('alignment');
125
- }
126
-
127
- convertIfBase64Image(node) {
128
- if (/^data:image\/(jpeg|jpg|png);base64,/.test(node.image)) { // base64 image
129
- let label = `$$pdfmake$$${this.autoImageIndex++}`;
130
- this.pdfDocument.images[label] = node.image;
131
- node.image = label;
132
- }
133
- }
134
-
135
- measureImage(node) {
136
- this.convertIfBase64Image(node);
137
-
138
- let image = this.pdfDocument.provideImage(node.image);
139
-
140
- let imageSize = { width: image.width, height: image.height };
141
-
142
- // If EXIF orientation calls for it, swap width and height
143
- if (image.orientation > 4) {
144
- imageSize = { width: image.height, height: image.width };
145
- }
146
-
147
- this.measureImageWithDimensions(node, imageSize);
148
-
149
- return node;
150
- }
151
-
152
- measureSVG(node) {
153
- let dimensions = this.svgMeasure.measureSVG(node.svg);
154
-
155
- this.measureImageWithDimensions(node, dimensions);
156
-
157
- node.font = this.styleStack.getProperty('font');
158
-
159
- // SVG requires a defined width and height
160
- if (!isNumber(node._width) && !isNumber(node._height)) {
161
- throw new Error('SVG is missing defined width and height.');
162
- } else if (!isNumber(node._width)) {
163
- throw new Error('SVG is missing defined width.');
164
- } else if (!isNumber(node._height)) {
165
- throw new Error('SVG is missing defined height.');
166
- }
167
-
168
- // scale SVG based on final dimension
169
- node.svg = this.svgMeasure.writeDimensions(node.svg, { width: node._width, height: node._height });
170
-
171
- return node;
172
- }
173
-
174
- measureLeaf(node) {
175
-
176
- if (node._textRef && node._textRef._textNodeRef.text) {
177
- node.text = node._textRef._textNodeRef.text;
178
- }
179
-
180
- // Make sure style properties of the node itself are considered when building inlines.
181
- // We could also just pass [node] to buildInlines, but that fails for bullet points.
182
- let styleStack = this.styleStack.clone();
183
- styleStack.push(node);
184
-
185
- let data = this.textInlines.buildInlines(node.text, styleStack);
186
-
187
- node._inlines = data.items;
188
- node._minWidth = data.minWidth;
189
- node._maxWidth = data.maxWidth;
190
-
191
- return node;
192
- }
193
-
194
- measureToc(node) {
195
- if (node.toc.title) {
196
- node.toc.title = this.measureNode(node.toc.title);
197
- }
198
-
199
- if (node.toc._items.length > 0) {
200
- let body = [];
201
- let textStyle = node.toc.textStyle || {};
202
- let numberStyle = node.toc.numberStyle || textStyle;
203
- let textMargin = node.toc.textMargin || [0, 0, 0, 0];
204
- for (let i = 0, l = node.toc._items.length; i < l; i++) {
205
- let item = node.toc._items[i];
206
- let lineStyle = item._textNodeRef.tocStyle || textStyle;
207
- let lineMargin = item._textNodeRef.tocMargin || textMargin;
208
- let lineNumberStyle = item._textNodeRef.tocNumberStyle || numberStyle;
209
- let destination = getNodeId(item._nodeRef);
210
- body.push([
211
- { text: item._textNodeRef.text, linkToDestination: destination, alignment: 'left', style: lineStyle, margin: lineMargin },
212
- { text: '00000', linkToDestination: destination, alignment: 'right', _tocItemRef: item._nodeRef, style: lineNumberStyle, margin: [0, lineMargin[1], 0, lineMargin[3]] }
213
- ]);
214
- }
215
-
216
- node.toc._table = {
217
- table: {
218
- dontBreakRows: true,
219
- widths: ['*', 'auto'],
220
- body: body
221
- },
222
- layout: 'noBorders'
223
- };
224
-
225
- node.toc._table = this.measureNode(node.toc._table);
226
- }
227
-
228
- return node;
229
- }
230
-
231
- measureVerticalContainer(node) {
232
- let items = node.stack;
233
-
234
- node._minWidth = 0;
235
- node._maxWidth = 0;
236
-
237
- for (let i = 0, l = items.length; i < l; i++) {
238
- items[i] = this.measureNode(items[i]);
239
-
240
- node._minWidth = Math.max(node._minWidth, items[i]._minWidth);
241
- node._maxWidth = Math.max(node._maxWidth, items[i]._maxWidth);
242
- }
243
-
244
- return node;
245
- }
246
-
247
- gapSizeForList() {
248
- return this.textInlines.sizeOfText('9. ', this.styleStack);
249
- }
250
-
251
- buildUnorderedMarker(item, styleStack, gapSize, type) {
252
- function buildDisc(gapSize, color) {
253
- // TODO: ascender-based calculations
254
- let radius = gapSize.fontSize / 6;
255
- return {
256
- canvas: [{
257
- x: radius,
258
- y: (gapSize.height / gapSize.lineHeight) + gapSize.descender - gapSize.fontSize / 3,
259
- r1: radius,
260
- r2: radius,
261
- type: 'ellipse',
262
- color: color
263
- }]
264
- };
265
- }
266
-
267
- function buildSquare(gapSize, color) {
268
- // TODO: ascender-based calculations
269
- let size = gapSize.fontSize / 3;
270
- return {
271
- canvas: [{
272
- x: 0,
273
- y: (gapSize.height / gapSize.lineHeight) + gapSize.descender - (gapSize.fontSize / 3) - (size / 2),
274
- h: size,
275
- w: size,
276
- type: 'rect',
277
- color: color
278
- }]
279
- };
280
- }
281
-
282
- function buildCircle(gapSize, color) {
283
- // TODO: ascender-based calculations
284
- let radius = gapSize.fontSize / 6;
285
- return {
286
- canvas: [{
287
- x: radius,
288
- y: (gapSize.height / gapSize.lineHeight) + gapSize.descender - gapSize.fontSize / 3,
289
- r1: radius,
290
- r2: radius,
291
- type: 'ellipse',
292
- lineColor: color
293
- }]
294
- };
295
- }
296
-
297
- let marker;
298
- let color = StyleContextStack.getStyleProperty(item, styleStack, 'markerColor', undefined) || styleStack.getProperty('color') || 'black';
299
-
300
- switch (type) {
301
- case 'circle':
302
- marker = buildCircle(gapSize, color);
303
- break;
304
-
305
- case 'square':
306
- marker = buildSquare(gapSize, color);
307
- break;
308
-
309
- case 'none':
310
- marker = {};
311
- break;
312
-
313
- case 'disc':
314
- default:
315
- marker = buildDisc(gapSize, color);
316
- break;
317
- }
318
-
319
- marker._minWidth = marker._maxWidth = gapSize.width;
320
- marker._minHeight = marker._maxHeight = gapSize.height;
321
-
322
- return marker;
323
- }
324
-
325
- buildOrderedMarker(item, counter, styleStack, type, separator) {
326
- function prepareAlpha(counter) {
327
- function toAlpha(num) {
328
- return (num >= 26 ? toAlpha((num / 26 >> 0) - 1) : '') + 'abcdefghijklmnopqrstuvwxyz'[num % 26 >> 0];
329
- }
330
-
331
- if (counter < 1) {
332
- return counter.toString();
333
- }
334
-
335
- return toAlpha(counter - 1);
336
- }
337
-
338
- function prepareRoman(counter) {
339
- if (counter < 1 || counter > 4999) {
340
- return counter.toString();
341
- }
342
- let num = counter;
343
- let lookup = { M: 1000, CM: 900, D: 500, CD: 400, C: 100, XC: 90, L: 50, XL: 40, X: 10, IX: 9, V: 5, IV: 4, I: 1 };
344
- let roman = '';
345
- for (let i in lookup) {
346
- while (num >= lookup[i]) {
347
- roman += i;
348
- num -= lookup[i];
349
- }
350
- }
351
- return roman;
352
- }
353
-
354
- function prepareDecimal(counter) {
355
- return counter.toString();
356
- }
357
-
358
- let counterText;
359
- switch (type) {
360
- case 'none':
361
- counterText = null;
362
- break;
363
-
364
- case 'upper-alpha':
365
- counterText = prepareAlpha(counter).toUpperCase();
366
- break;
367
-
368
- case 'lower-alpha':
369
- counterText = prepareAlpha(counter);
370
- break;
371
-
372
- case 'upper-roman':
373
- counterText = prepareRoman(counter);
374
- break;
375
-
376
- case 'lower-roman':
377
- counterText = prepareRoman(counter).toLowerCase();
378
- break;
379
-
380
- case 'decimal':
381
- default:
382
- counterText = prepareDecimal(counter);
383
- break;
384
- }
385
-
386
- if (counterText === null) {
387
- return {};
388
- }
389
-
390
- if (separator) {
391
- if (Array.isArray(separator)) {
392
- if (separator[0]) {
393
- counterText = separator[0] + counterText;
394
- }
395
-
396
- if (separator[1]) {
397
- counterText += separator[1];
398
- }
399
- counterText += ' ';
400
- } else {
401
- counterText += `${separator} `;
402
- }
403
- }
404
-
405
- let markerColor = StyleContextStack.getStyleProperty(item, styleStack, 'markerColor', undefined) || styleStack.getProperty('color') || 'black';
406
- let textArray = {
407
- text: counterText,
408
- color: markerColor
409
- };
410
-
411
- return { _inlines: this.textInlines.buildInlines(textArray, styleStack).items };
412
- }
413
-
414
- measureUnorderedList(node) {
415
- let style = this.styleStack.clone();
416
- let items = node.ul;
417
- node.type = node.type || 'disc';
418
- node._gapSize = this.gapSizeForList();
419
- node._minWidth = 0;
420
- node._maxWidth = 0;
421
-
422
- for (let i = 0, l = items.length; i < l; i++) {
423
- let item = items[i] = this.measureNode(items[i]);
424
-
425
- if (!item.ol && !item.ul) {
426
- item.listMarker = this.buildUnorderedMarker(item, style, node._gapSize, item.listType || node.type);
427
- }
428
-
429
- node._minWidth = Math.max(node._minWidth, items[i]._minWidth + node._gapSize.width);
430
- node._maxWidth = Math.max(node._maxWidth, items[i]._maxWidth + node._gapSize.width);
431
- }
432
-
433
- return node;
434
- }
435
-
436
- measureOrderedList(node) {
437
- let style = this.styleStack.clone();
438
- let items = node.ol;
439
- node.type = node.type || 'decimal';
440
- node.separator = node.separator || '.';
441
- node.reversed = node.reversed || false;
442
- if (!isNumber(node.start)) {
443
- node.start = node.reversed ? items.length : 1;
444
- }
445
- node._gapSize = this.gapSizeForList();
446
- node._minWidth = 0;
447
- node._maxWidth = 0;
448
-
449
- let counter = node.start;
450
- for (let i = 0, l = items.length; i < l; i++) {
451
- let item = items[i] = this.measureNode(items[i]);
452
-
453
- if (!item.ol && !item.ul) {
454
- let counterValue = isNumber(item.counter) ? item.counter : counter;
455
- item.listMarker = this.buildOrderedMarker(item, counterValue, style, item.listType || node.type, node.separator);
456
- if (item.listMarker._inlines) {
457
- node._gapSize.width = Math.max(node._gapSize.width, item.listMarker._inlines[0].width);
458
- }
459
-
460
- if (node.reversed) {
461
- counter--;
462
- } else {
463
- counter++;
464
- }
465
- }
466
-
467
- node._minWidth = Math.max(node._minWidth, items[i]._minWidth);
468
- node._maxWidth = Math.max(node._maxWidth, items[i]._maxWidth);
469
-
470
- }
471
-
472
- node._minWidth += node._gapSize.width;
473
- node._maxWidth += node._gapSize.width;
474
-
475
- for (let i = 0, l = items.length; i < l; i++) {
476
- let item = items[i];
477
- if (!item.ol && !item.ul) {
478
- item.listMarker._minWidth = item.listMarker._maxWidth = node._gapSize.width;
479
- }
480
- }
481
-
482
- return node;
483
- }
484
-
485
- measureSection(node) {
486
- // TODO: properties
487
-
488
- node.section = this.measureNode(node.section);
489
-
490
- return node;
491
- }
492
-
493
- measureColumns(node) {
494
- let columns = node.columns;
495
- node._gap = this.styleStack.getProperty('columnGap') || 0;
496
-
497
- for (let i = 0, l = columns.length; i < l; i++) {
498
- columns[i] = this.measureNode(columns[i]);
499
- }
500
-
501
- let measures = ColumnCalculator.measureMinMax(columns);
502
-
503
- let numGaps = (columns.length > 0) ? (columns.length - 1) : 0;
504
- node._minWidth = measures.min + node._gap * numGaps;
505
- node._maxWidth = measures.max + node._gap * numGaps;
506
-
507
- return node;
508
- }
509
-
510
- measureTable(node) {
511
- extendTableWidths(node);
512
- node._layout = getLayout(this.tableLayouts);
513
- node._offsets = getOffsets(node._layout);
514
-
515
- let colSpans = [];
516
- let col;
517
- let row;
518
- let cols;
519
- let rows;
520
-
521
- for (col = 0, cols = node.table.body[0].length; col < cols; col++) {
522
- let c = node.table.widths[col];
523
- c._minWidth = 0;
524
- c._maxWidth = 0;
525
-
526
- for (row = 0, rows = node.table.body.length; row < rows; row++) {
527
- let rowData = node.table.body[row];
528
- let data = rowData[col];
529
- if (data === undefined) {
530
- throw new Error(`Malformed table row, a cell is undefined.\nRow index: ${row}\nColumn index: ${col}\nRow data: ${stringifyNode(rowData)}`);
531
- }
532
- if (data === null) { // transform to object
533
- data = '';
534
- }
535
-
536
- if (!data._span) {
537
- data = rowData[col] = this.styleStack.auto(data, measureCb(this, data));
538
-
539
- if (data.colSpan && data.colSpan > 1) {
540
- markSpans(rowData, col, data.colSpan);
541
- colSpans.push({ col: col, span: data.colSpan, minWidth: data._minWidth, maxWidth: data._maxWidth });
542
- } else {
543
- c._minWidth = Math.max(c._minWidth, data._minWidth);
544
- c._maxWidth = Math.max(c._maxWidth, data._maxWidth);
545
- }
546
- }
547
-
548
- if (data.rowSpan && data.rowSpan > 1) {
549
- markVSpans(node.table, row, col, data.rowSpan);
550
- }
551
- }
552
- }
553
-
554
- extendWidthsForColSpans();
555
-
556
- let measures = ColumnCalculator.measureMinMax(node.table.widths);
557
-
558
- node._minWidth = measures.min + node._offsets.total;
559
- node._maxWidth = measures.max + node._offsets.total;
560
-
561
- return node;
562
-
563
- function measureCb(_this, data) {
564
- return () => {
565
- if (isObject(data)) {
566
- data.fillColor = _this.styleStack.getProperty('fillColor');
567
- data.fillOpacity = _this.styleStack.getProperty('fillOpacity');
568
- }
569
- return _this.measureNode(data);
570
- };
571
- }
572
-
573
- function getLayout(tableLayouts) {
574
- let layout = node.layout;
575
-
576
- if (isString(layout)) {
577
- layout = tableLayouts[layout];
578
- }
579
-
580
- return pack(defaultTableLayout, layout);
581
- }
582
-
583
- function getOffsets(layout) {
584
- let offsets = [];
585
- let totalOffset = 0;
586
- let prevRightPadding = 0;
587
-
588
- for (let i = 0, l = node.table.widths.length; i < l; i++) {
589
- let lOffset = prevRightPadding + layout.vLineWidth(i, node) + layout.paddingLeft(i, node);
590
- offsets.push(lOffset);
591
- totalOffset += lOffset;
592
- prevRightPadding = layout.paddingRight(i, node);
593
- }
594
-
595
- totalOffset += prevRightPadding + layout.vLineWidth(node.table.widths.length, node);
596
-
597
- return {
598
- total: totalOffset,
599
- offsets: offsets
600
- };
601
- }
602
-
603
- function extendWidthsForColSpans() {
604
- let q;
605
- let j;
606
-
607
- for (let i = 0, l = colSpans.length; i < l; i++) {
608
- let span = colSpans[i];
609
-
610
- let currentMinMax = getMinMax(span.col, span.span, node._offsets);
611
- let minDifference = span.minWidth - currentMinMax.minWidth;
612
- let maxDifference = span.maxWidth - currentMinMax.maxWidth;
613
-
614
- if (minDifference > 0) {
615
- q = minDifference / span.span;
616
-
617
- for (j = 0; j < span.span; j++) {
618
- node.table.widths[span.col + j]._minWidth += q;
619
- }
620
- }
621
-
622
- if (maxDifference > 0) {
623
- q = maxDifference / span.span;
624
-
625
- for (j = 0; j < span.span; j++) {
626
- node.table.widths[span.col + j]._maxWidth += q;
627
- }
628
- }
629
- }
630
- }
631
-
632
- function getMinMax(col, span, offsets) {
633
- let result = { minWidth: 0, maxWidth: 0 };
634
-
635
- for (let i = 0; i < span; i++) {
636
- result.minWidth += node.table.widths[col + i]._minWidth + (i ? offsets.offsets[col + i] : 0);
637
- result.maxWidth += node.table.widths[col + i]._maxWidth + (i ? offsets.offsets[col + i] : 0);
638
- }
639
-
640
- return result;
641
- }
642
-
643
- function markSpans(rowData, col, span) {
644
- for (let i = 1; i < span; i++) {
645
- rowData[col + i] = {
646
- _span: true,
647
- _minWidth: 0,
648
- _maxWidth: 0,
649
- rowSpan: rowData[col].rowSpan
650
- };
651
- }
652
- }
653
-
654
- function markVSpans(table, row, col, span) {
655
- for (let i = 1; i < span; i++) {
656
- table.body[row + i][col] = {
657
- _span: true,
658
- _minWidth: 0,
659
- _maxWidth: 0,
660
- fillColor: table.body[row][col].fillColor,
661
- fillOpacity: table.body[row][col].fillOpacity
662
- };
663
- }
664
- }
665
-
666
- function extendTableWidths(node) {
667
- if (!node.table.widths) {
668
- node.table.widths = 'auto';
669
- }
670
-
671
- if (isString(node.table.widths)) {
672
- node.table.widths = [node.table.widths];
673
-
674
- while (node.table.widths.length < node.table.body[0].length) {
675
- node.table.widths.push(node.table.widths[node.table.widths.length - 1]);
676
- }
677
- }
678
-
679
- for (let i = 0, l = node.table.widths.length; i < l; i++) {
680
- let w = node.table.widths[i];
681
- if (isNumber(w) || isString(w)) {
682
- node.table.widths[i] = { width: w };
683
- }
684
- }
685
- }
686
- }
687
-
688
- measureCanvas(node) {
689
- let w = 0;
690
- let h = 0;
691
-
692
- for (let i = 0, l = node.canvas.length; i < l; i++) {
693
- let vector = node.canvas[i];
694
-
695
- switch (vector.type) {
696
- case 'ellipse':
697
- w = Math.max(w, vector.x + vector.r1);
698
- h = Math.max(h, vector.y + vector.r2);
699
- break;
700
- case 'rect':
701
- w = Math.max(w, vector.x + vector.w);
702
- h = Math.max(h, vector.y + vector.h);
703
- break;
704
- case 'line':
705
- w = Math.max(w, vector.x1, vector.x2);
706
- h = Math.max(h, vector.y1, vector.y2);
707
- break;
708
- case 'polyline':
709
- for (let i2 = 0, l2 = vector.points.length; i2 < l2; i2++) {
710
- w = Math.max(w, vector.points[i2].x);
711
- h = Math.max(h, vector.points[i2].y);
712
- }
713
- break;
714
- }
715
- }
716
-
717
- node._minWidth = node._maxWidth = w;
718
- node._minHeight = node._maxHeight = h;
719
- node._alignment = this.styleStack.getProperty('alignment');
720
-
721
- return node;
722
- }
723
-
724
- measureQr(node) {
725
- node = qrEncoder.measure(node);
726
- node._alignment = this.styleStack.getProperty('alignment');
727
- return node;
728
- }
729
-
730
- measureAttachment(node) {
731
- node._width = node.width || 7;
732
- node._height = node.height || 18;
733
-
734
- return node;
735
- }
736
- }
737
-
738
- export default DocMeasure;
1
+ import TextInlines from './TextInlines';
2
+ import StyleContextStack from './StyleContextStack';
3
+ import ColumnCalculator from './columnCalculator';
4
+ import { defaultTableLayout } from './tableLayouts';
5
+ import { isString, isNumber, isObject } from './helpers/variableType';
6
+ import { stringifyNode, getNodeId, getNodeMargin } from './helpers/node';
7
+ import { pack } from './helpers/tools';
8
+ import qrEncoder from './qrEnc.js';
9
+
10
+ class DocMeasure {
11
+ constructor(
12
+ pdfDocument,
13
+ styleDictionary,
14
+ defaultStyle,
15
+ svgMeasure,
16
+ tableLayouts
17
+ ) {
18
+ this.pdfDocument = pdfDocument;
19
+ this.textInlines = new TextInlines(pdfDocument);
20
+ this.styleStack = new StyleContextStack(styleDictionary, defaultStyle);
21
+ this.svgMeasure = svgMeasure;
22
+ this.tableLayouts = tableLayouts;
23
+ this.autoImageIndex = 1;
24
+ }
25
+
26
+ /**
27
+ * Measures all nodes and sets min/max-width properties required for the second
28
+ * layout-pass.
29
+ *
30
+ * @param {object} docStructure document-definition-object
31
+ * @returns {object} document-measurement-object
32
+ */
33
+ measureDocument(docStructure) {
34
+ return this.measureNode(docStructure);
35
+ }
36
+
37
+ measureBlock(node) {
38
+ return this.measureNode(node);
39
+ }
40
+
41
+ measureNode(node) {
42
+ return this.styleStack.auto(node, () => {
43
+ // TODO: refactor + rethink whether this is the proper way to handle margins
44
+ node._margin = getNodeMargin(node, this.styleStack);
45
+
46
+ if (node.section) {
47
+ return extendMargins(this.measureSection(node));
48
+ } else if (node.columns) {
49
+ return extendMargins(this.measureColumns(node));
50
+ } else if (node.stack) {
51
+ return extendMargins(this.measureVerticalContainer(node));
52
+ } else if (node.ul) {
53
+ return extendMargins(this.measureUnorderedList(node));
54
+ } else if (node.ol) {
55
+ return extendMargins(this.measureOrderedList(node));
56
+ } else if (node.table) {
57
+ return extendMargins(this.measureTable(node));
58
+ } else if (node.text !== undefined) {
59
+ return extendMargins(this.measureLeaf(node));
60
+ } else if (node.toc) {
61
+ return extendMargins(this.measureToc(node));
62
+ } else if (node.image) {
63
+ return extendMargins(this.measureImage(node));
64
+ } else if (node.svg) {
65
+ return extendMargins(this.measureSVG(node));
66
+ } else if (node.canvas) {
67
+ return extendMargins(this.measureCanvas(node));
68
+ } else if (node.qr) {
69
+ return extendMargins(this.measureQr(node));
70
+ } else if (node.attachment) {
71
+ return extendMargins(this.measureAttachment(node));
72
+ } else {
73
+ throw new Error(`Unrecognized document structure: ${stringifyNode(node)}`);
74
+ }
75
+ });
76
+
77
+ function extendMargins(node) {
78
+ let margin = node._margin;
79
+
80
+ if (margin) {
81
+ node._minWidth += margin[0] + margin[2];
82
+ node._maxWidth += margin[0] + margin[2];
83
+ }
84
+
85
+ return node;
86
+ }
87
+ }
88
+
89
+ measureImageWithDimensions(node, dimensions) {
90
+ if (node.fit) {
91
+ let factor = (dimensions.width / dimensions.height > node.fit[0] / node.fit[1]) ? node.fit[0] / dimensions.width : node.fit[1] / dimensions.height;
92
+ node._width = node._minWidth = node._maxWidth = dimensions.width * factor;
93
+ node._height = dimensions.height * factor;
94
+ } else if (node.cover) {
95
+ node._width = node._minWidth = node._maxWidth = node.cover.width;
96
+ node._height = node._minHeight = node._maxHeight = node.cover.height;
97
+ } else {
98
+ let ratio = dimensions.width / dimensions.height;
99
+
100
+ node._width = node._minWidth = node._maxWidth = node.width || (node.height ? (node.height * ratio) : dimensions.width);
101
+ node._height = node.height || (node.width ? node.width / ratio : dimensions.height);
102
+
103
+ if (isNumber(node.maxWidth) && node.maxWidth < node._width) {
104
+ node._width = node._minWidth = node._maxWidth = node.maxWidth;
105
+ node._height = node._width * dimensions.height / dimensions.width;
106
+ }
107
+
108
+ if (isNumber(node.maxHeight) && node.maxHeight < node._height) {
109
+ node._height = node.maxHeight;
110
+ node._width = node._minWidth = node._maxWidth = node._height * dimensions.width / dimensions.height;
111
+ }
112
+
113
+ if (isNumber(node.minWidth) && node.minWidth > node._width) {
114
+ node._width = node._minWidth = node._maxWidth = node.minWidth;
115
+ node._height = node._width * dimensions.height / dimensions.width;
116
+ }
117
+
118
+ if (isNumber(node.minHeight) && node.minHeight > node._height) {
119
+ node._height = node.minHeight;
120
+ node._width = node._minWidth = node._maxWidth = node._height * dimensions.width / dimensions.height;
121
+ }
122
+ }
123
+
124
+ node._alignment = this.styleStack.getProperty('alignment');
125
+ }
126
+
127
+ convertIfBase64Image(node) {
128
+ if (/^data:image\/(jpeg|jpg|png);base64,/.test(node.image)) { // base64 image
129
+ let label = `$$pdfmake$$${this.autoImageIndex++}`;
130
+ this.pdfDocument.images[label] = node.image;
131
+ node.image = label;
132
+ }
133
+ }
134
+
135
+ measureImage(node) {
136
+ this.convertIfBase64Image(node);
137
+
138
+ let image = this.pdfDocument.provideImage(node.image);
139
+
140
+ let imageSize = { width: image.width, height: image.height };
141
+
142
+ // If EXIF orientation calls for it, swap width and height
143
+ if (image.orientation > 4) {
144
+ imageSize = { width: image.height, height: image.width };
145
+ }
146
+
147
+ this.measureImageWithDimensions(node, imageSize);
148
+
149
+ return node;
150
+ }
151
+
152
+ measureSVG(node) {
153
+ let dimensions = this.svgMeasure.measureSVG(node.svg);
154
+
155
+ this.measureImageWithDimensions(node, dimensions);
156
+
157
+ node.font = this.styleStack.getProperty('font');
158
+
159
+ // SVG requires a defined width and height
160
+ if (!isNumber(node._width) && !isNumber(node._height)) {
161
+ throw new Error('SVG is missing defined width and height.');
162
+ } else if (!isNumber(node._width)) {
163
+ throw new Error('SVG is missing defined width.');
164
+ } else if (!isNumber(node._height)) {
165
+ throw new Error('SVG is missing defined height.');
166
+ }
167
+
168
+ // scale SVG based on final dimension
169
+ node.svg = this.svgMeasure.writeDimensions(node.svg, { width: node._width, height: node._height });
170
+
171
+ return node;
172
+ }
173
+
174
+ measureLeaf(node) {
175
+
176
+ if (node._textRef && node._textRef._textNodeRef.text) {
177
+ node.text = node._textRef._textNodeRef.text;
178
+ }
179
+
180
+ // Make sure style properties of the node itself are considered when building inlines.
181
+ // We could also just pass [node] to buildInlines, but that fails for bullet points.
182
+ let styleStack = this.styleStack.clone();
183
+ styleStack.push(node);
184
+
185
+ let data = this.textInlines.buildInlines(node.text, styleStack);
186
+
187
+ node._inlines = data.items;
188
+ node._minWidth = data.minWidth;
189
+ node._maxWidth = data.maxWidth;
190
+
191
+ return node;
192
+ }
193
+
194
+ measureToc(node) {
195
+ if (node.toc.title) {
196
+ node.toc.title = this.measureNode(node.toc.title);
197
+ }
198
+
199
+ if (node.toc._items.length > 0) {
200
+ let body = [];
201
+ let textStyle = node.toc.textStyle || {};
202
+ let numberStyle = node.toc.numberStyle || textStyle;
203
+ let textMargin = node.toc.textMargin || [0, 0, 0, 0];
204
+
205
+ if (node.toc.sortBy === 'title') {
206
+ node.toc._items.sort((a, b) => {
207
+ return a._textNodeRef.text.localeCompare(b._textNodeRef.text, node.toc.sortLocale);
208
+ });
209
+ }
210
+
211
+ for (let i = 0, l = node.toc._items.length; i < l; i++) {
212
+ let item = node.toc._items[i];
213
+ let lineStyle = item._textNodeRef.tocStyle || textStyle;
214
+ let lineMargin = item._textNodeRef.tocMargin || textMargin;
215
+ let lineNumberStyle = item._textNodeRef.tocNumberStyle || numberStyle;
216
+ let destination = getNodeId(item._nodeRef);
217
+ body.push([
218
+ { text: item._textNodeRef.text, linkToDestination: destination, alignment: 'left', style: lineStyle, margin: lineMargin },
219
+ { text: '00000', linkToDestination: destination, alignment: 'right', _tocItemRef: item._nodeRef, style: lineNumberStyle, margin: [0, lineMargin[1], 0, lineMargin[3]] }
220
+ ]);
221
+ }
222
+
223
+ node.toc._table = {
224
+ table: {
225
+ dontBreakRows: true,
226
+ widths: ['*', 'auto'],
227
+ body: body
228
+ },
229
+ layout: 'noBorders'
230
+ };
231
+
232
+ node.toc._table = this.measureNode(node.toc._table);
233
+ }
234
+
235
+ return node;
236
+ }
237
+
238
+ measureVerticalContainer(node) {
239
+ let items = node.stack;
240
+
241
+ node._minWidth = 0;
242
+ node._maxWidth = 0;
243
+
244
+ for (let i = 0, l = items.length; i < l; i++) {
245
+ items[i] = this.measureNode(items[i]);
246
+
247
+ node._minWidth = Math.max(node._minWidth, items[i]._minWidth);
248
+ node._maxWidth = Math.max(node._maxWidth, items[i]._maxWidth);
249
+ }
250
+
251
+ return node;
252
+ }
253
+
254
+ gapSizeForList() {
255
+ return this.textInlines.sizeOfText('9. ', this.styleStack);
256
+ }
257
+
258
+ buildUnorderedMarker(item, styleStack, gapSize, type) {
259
+ function buildDisc(gapSize, color) {
260
+ // TODO: ascender-based calculations
261
+ let radius = gapSize.fontSize / 6;
262
+ return {
263
+ canvas: [{
264
+ x: radius,
265
+ y: (gapSize.height / gapSize.lineHeight) + gapSize.descender - gapSize.fontSize / 3,
266
+ r1: radius,
267
+ r2: radius,
268
+ type: 'ellipse',
269
+ color: color
270
+ }]
271
+ };
272
+ }
273
+
274
+ function buildSquare(gapSize, color) {
275
+ // TODO: ascender-based calculations
276
+ let size = gapSize.fontSize / 3;
277
+ return {
278
+ canvas: [{
279
+ x: 0,
280
+ y: (gapSize.height / gapSize.lineHeight) + gapSize.descender - (gapSize.fontSize / 3) - (size / 2),
281
+ h: size,
282
+ w: size,
283
+ type: 'rect',
284
+ color: color
285
+ }]
286
+ };
287
+ }
288
+
289
+ function buildCircle(gapSize, color) {
290
+ // TODO: ascender-based calculations
291
+ let radius = gapSize.fontSize / 6;
292
+ return {
293
+ canvas: [{
294
+ x: radius,
295
+ y: (gapSize.height / gapSize.lineHeight) + gapSize.descender - gapSize.fontSize / 3,
296
+ r1: radius,
297
+ r2: radius,
298
+ type: 'ellipse',
299
+ lineColor: color
300
+ }]
301
+ };
302
+ }
303
+
304
+ let marker;
305
+ let color = StyleContextStack.getStyleProperty(item, styleStack, 'markerColor', undefined) || styleStack.getProperty('color') || 'black';
306
+
307
+ switch (type) {
308
+ case 'circle':
309
+ marker = buildCircle(gapSize, color);
310
+ break;
311
+
312
+ case 'square':
313
+ marker = buildSquare(gapSize, color);
314
+ break;
315
+
316
+ case 'none':
317
+ marker = {};
318
+ break;
319
+
320
+ case 'disc':
321
+ default:
322
+ marker = buildDisc(gapSize, color);
323
+ break;
324
+ }
325
+
326
+ marker._minWidth = marker._maxWidth = gapSize.width;
327
+ marker._minHeight = marker._maxHeight = gapSize.height;
328
+
329
+ return marker;
330
+ }
331
+
332
+ buildOrderedMarker(item, counter, styleStack, type, separator) {
333
+ function prepareAlpha(counter) {
334
+ function toAlpha(num) {
335
+ return (num >= 26 ? toAlpha((num / 26 >> 0) - 1) : '') + 'abcdefghijklmnopqrstuvwxyz'[num % 26 >> 0];
336
+ }
337
+
338
+ if (counter < 1) {
339
+ return counter.toString();
340
+ }
341
+
342
+ return toAlpha(counter - 1);
343
+ }
344
+
345
+ function prepareRoman(counter) {
346
+ if (counter < 1 || counter > 4999) {
347
+ return counter.toString();
348
+ }
349
+ let num = counter;
350
+ let lookup = { M: 1000, CM: 900, D: 500, CD: 400, C: 100, XC: 90, L: 50, XL: 40, X: 10, IX: 9, V: 5, IV: 4, I: 1 };
351
+ let roman = '';
352
+ for (let i in lookup) {
353
+ while (num >= lookup[i]) {
354
+ roman += i;
355
+ num -= lookup[i];
356
+ }
357
+ }
358
+ return roman;
359
+ }
360
+
361
+ function prepareDecimal(counter) {
362
+ return counter.toString();
363
+ }
364
+
365
+ let counterText;
366
+ switch (type) {
367
+ case 'none':
368
+ counterText = null;
369
+ break;
370
+
371
+ case 'upper-alpha':
372
+ counterText = prepareAlpha(counter).toUpperCase();
373
+ break;
374
+
375
+ case 'lower-alpha':
376
+ counterText = prepareAlpha(counter);
377
+ break;
378
+
379
+ case 'upper-roman':
380
+ counterText = prepareRoman(counter);
381
+ break;
382
+
383
+ case 'lower-roman':
384
+ counterText = prepareRoman(counter).toLowerCase();
385
+ break;
386
+
387
+ case 'decimal':
388
+ default:
389
+ counterText = prepareDecimal(counter);
390
+ break;
391
+ }
392
+
393
+ if (counterText === null) {
394
+ return {};
395
+ }
396
+
397
+ if (separator) {
398
+ if (Array.isArray(separator)) {
399
+ if (separator[0]) {
400
+ counterText = separator[0] + counterText;
401
+ }
402
+
403
+ if (separator[1]) {
404
+ counterText += separator[1];
405
+ }
406
+ counterText += ' ';
407
+ } else {
408
+ counterText += `${separator} `;
409
+ }
410
+ }
411
+
412
+ let markerColor = StyleContextStack.getStyleProperty(item, styleStack, 'markerColor', undefined) || styleStack.getProperty('color') || 'black';
413
+ let textArray = {
414
+ text: counterText,
415
+ color: markerColor
416
+ };
417
+
418
+ return { _inlines: this.textInlines.buildInlines(textArray, styleStack).items };
419
+ }
420
+
421
+ measureUnorderedList(node) {
422
+ let style = this.styleStack.clone();
423
+ let items = node.ul;
424
+ node.type = node.type || 'disc';
425
+ node._gapSize = this.gapSizeForList();
426
+ node._minWidth = 0;
427
+ node._maxWidth = 0;
428
+
429
+ for (let i = 0, l = items.length; i < l; i++) {
430
+ let item = items[i] = this.measureNode(items[i]);
431
+
432
+ if (!item.ol && !item.ul) {
433
+ item.listMarker = this.buildUnorderedMarker(item, style, node._gapSize, item.listType || node.type);
434
+ }
435
+
436
+ node._minWidth = Math.max(node._minWidth, items[i]._minWidth + node._gapSize.width);
437
+ node._maxWidth = Math.max(node._maxWidth, items[i]._maxWidth + node._gapSize.width);
438
+ }
439
+
440
+ return node;
441
+ }
442
+
443
+ measureOrderedList(node) {
444
+ let style = this.styleStack.clone();
445
+ let items = node.ol;
446
+ node.type = node.type || 'decimal';
447
+ node.separator = node.separator || '.';
448
+ node.reversed = node.reversed || false;
449
+ if (!isNumber(node.start)) {
450
+ node.start = node.reversed ? items.length : 1;
451
+ }
452
+ node._gapSize = this.gapSizeForList();
453
+ node._minWidth = 0;
454
+ node._maxWidth = 0;
455
+
456
+ let counter = node.start;
457
+ for (let i = 0, l = items.length; i < l; i++) {
458
+ let item = items[i] = this.measureNode(items[i]);
459
+
460
+ if (!item.ol && !item.ul) {
461
+ let counterValue = isNumber(item.counter) ? item.counter : counter;
462
+ item.listMarker = this.buildOrderedMarker(item, counterValue, style, item.listType || node.type, node.separator);
463
+ if (item.listMarker._inlines) {
464
+ node._gapSize.width = Math.max(node._gapSize.width, item.listMarker._inlines[0].width);
465
+ }
466
+
467
+ if (node.reversed) {
468
+ counter--;
469
+ } else {
470
+ counter++;
471
+ }
472
+ }
473
+
474
+ node._minWidth = Math.max(node._minWidth, items[i]._minWidth);
475
+ node._maxWidth = Math.max(node._maxWidth, items[i]._maxWidth);
476
+
477
+ }
478
+
479
+ node._minWidth += node._gapSize.width;
480
+ node._maxWidth += node._gapSize.width;
481
+
482
+ for (let i = 0, l = items.length; i < l; i++) {
483
+ let item = items[i];
484
+ if (!item.ol && !item.ul) {
485
+ item.listMarker._minWidth = item.listMarker._maxWidth = node._gapSize.width;
486
+ }
487
+ }
488
+
489
+ return node;
490
+ }
491
+
492
+ measureSection(node) {
493
+ // TODO: properties
494
+
495
+ node.section = this.measureNode(node.section);
496
+
497
+ return node;
498
+ }
499
+
500
+ measureColumns(node) {
501
+ let columns = node.columns;
502
+ node._gap = this.styleStack.getProperty('columnGap') || 0;
503
+
504
+ for (let i = 0, l = columns.length; i < l; i++) {
505
+ columns[i] = this.measureNode(columns[i]);
506
+ }
507
+
508
+ let measures = ColumnCalculator.measureMinMax(columns);
509
+
510
+ let numGaps = (columns.length > 0) ? (columns.length - 1) : 0;
511
+ node._minWidth = measures.min + node._gap * numGaps;
512
+ node._maxWidth = measures.max + node._gap * numGaps;
513
+
514
+ return node;
515
+ }
516
+
517
+ measureTable(node) {
518
+ extendTableWidths(node);
519
+ node._layout = getLayout(this.tableLayouts);
520
+ node._offsets = getOffsets(node._layout);
521
+
522
+ let colSpans = [];
523
+ let col;
524
+ let row;
525
+ let cols;
526
+ let rows;
527
+
528
+ for (col = 0, cols = node.table.body[0].length; col < cols; col++) {
529
+ let c = node.table.widths[col];
530
+ c._minWidth = 0;
531
+ c._maxWidth = 0;
532
+
533
+ for (row = 0, rows = node.table.body.length; row < rows; row++) {
534
+ let rowData = node.table.body[row];
535
+ let data = rowData[col];
536
+ if (data === undefined) {
537
+ throw new Error(`Malformed table row, a cell is undefined.\nRow index: ${row}\nColumn index: ${col}\nRow data: ${stringifyNode(rowData)}`);
538
+ }
539
+ if (data === null) { // transform to object
540
+ data = '';
541
+ }
542
+
543
+ if (!data._span) {
544
+ data = rowData[col] = this.styleStack.auto(data, measureCb(this, data));
545
+
546
+ if (data.colSpan && data.colSpan > 1) {
547
+ markSpans(rowData, col, data.colSpan);
548
+ colSpans.push({ col: col, span: data.colSpan, minWidth: data._minWidth, maxWidth: data._maxWidth });
549
+ } else {
550
+ c._minWidth = Math.max(c._minWidth, data._minWidth);
551
+ c._maxWidth = Math.max(c._maxWidth, data._maxWidth);
552
+ }
553
+ }
554
+
555
+ if (data.rowSpan && data.rowSpan > 1) {
556
+ markVSpans(node.table, row, col, data.rowSpan);
557
+ }
558
+ }
559
+ }
560
+
561
+ extendWidthsForColSpans();
562
+
563
+ let measures = ColumnCalculator.measureMinMax(node.table.widths);
564
+
565
+ node._minWidth = measures.min + node._offsets.total;
566
+ node._maxWidth = measures.max + node._offsets.total;
567
+
568
+ return node;
569
+
570
+ function measureCb(_this, data) {
571
+ return () => {
572
+ if (isObject(data)) {
573
+ data.fillColor = _this.styleStack.getProperty('fillColor');
574
+ data.fillOpacity = _this.styleStack.getProperty('fillOpacity');
575
+ }
576
+ return _this.measureNode(data);
577
+ };
578
+ }
579
+
580
+ function getLayout(tableLayouts) {
581
+ let layout = node.layout;
582
+
583
+ if (isString(layout)) {
584
+ layout = tableLayouts[layout];
585
+ }
586
+
587
+ return pack(defaultTableLayout, layout);
588
+ }
589
+
590
+ function getOffsets(layout) {
591
+ let offsets = [];
592
+ let totalOffset = 0;
593
+ let prevRightPadding = 0;
594
+
595
+ for (let i = 0, l = node.table.widths.length; i < l; i++) {
596
+ let lOffset = prevRightPadding + layout.vLineWidth(i, node) + layout.paddingLeft(i, node);
597
+ offsets.push(lOffset);
598
+ totalOffset += lOffset;
599
+ prevRightPadding = layout.paddingRight(i, node);
600
+ }
601
+
602
+ totalOffset += prevRightPadding + layout.vLineWidth(node.table.widths.length, node);
603
+
604
+ return {
605
+ total: totalOffset,
606
+ offsets: offsets
607
+ };
608
+ }
609
+
610
+ function extendWidthsForColSpans() {
611
+ let q;
612
+ let j;
613
+
614
+ for (let i = 0, l = colSpans.length; i < l; i++) {
615
+ let span = colSpans[i];
616
+
617
+ let currentMinMax = getMinMax(span.col, span.span, node._offsets);
618
+ let minDifference = span.minWidth - currentMinMax.minWidth;
619
+ let maxDifference = span.maxWidth - currentMinMax.maxWidth;
620
+
621
+ if (minDifference > 0) {
622
+ q = minDifference / span.span;
623
+
624
+ for (j = 0; j < span.span; j++) {
625
+ node.table.widths[span.col + j]._minWidth += q;
626
+ }
627
+ }
628
+
629
+ if (maxDifference > 0) {
630
+ q = maxDifference / span.span;
631
+
632
+ for (j = 0; j < span.span; j++) {
633
+ node.table.widths[span.col + j]._maxWidth += q;
634
+ }
635
+ }
636
+ }
637
+ }
638
+
639
+ function getMinMax(col, span, offsets) {
640
+ let result = { minWidth: 0, maxWidth: 0 };
641
+
642
+ for (let i = 0; i < span; i++) {
643
+ result.minWidth += node.table.widths[col + i]._minWidth + (i ? offsets.offsets[col + i] : 0);
644
+ result.maxWidth += node.table.widths[col + i]._maxWidth + (i ? offsets.offsets[col + i] : 0);
645
+ }
646
+
647
+ return result;
648
+ }
649
+
650
+ function markSpans(rowData, col, span) {
651
+ for (let i = 1; i < span; i++) {
652
+ rowData[col + i] = {
653
+ _span: true,
654
+ _minWidth: 0,
655
+ _maxWidth: 0,
656
+ rowSpan: rowData[col].rowSpan
657
+ };
658
+ }
659
+ }
660
+
661
+ function markVSpans(table, row, col, span) {
662
+ for (let i = 1; i < span; i++) {
663
+ table.body[row + i][col] = {
664
+ _span: true,
665
+ _minWidth: 0,
666
+ _maxWidth: 0,
667
+ fillColor: table.body[row][col].fillColor,
668
+ fillOpacity: table.body[row][col].fillOpacity
669
+ };
670
+ }
671
+ }
672
+
673
+ function extendTableWidths(node) {
674
+ if (!node.table.widths) {
675
+ node.table.widths = 'auto';
676
+ }
677
+
678
+ if (isString(node.table.widths)) {
679
+ node.table.widths = [node.table.widths];
680
+
681
+ while (node.table.widths.length < node.table.body[0].length) {
682
+ node.table.widths.push(node.table.widths[node.table.widths.length - 1]);
683
+ }
684
+ }
685
+
686
+ for (let i = 0, l = node.table.widths.length; i < l; i++) {
687
+ let w = node.table.widths[i];
688
+ if (isNumber(w) || isString(w)) {
689
+ node.table.widths[i] = { width: w };
690
+ }
691
+ }
692
+ }
693
+ }
694
+
695
+ measureCanvas(node) {
696
+ let w = 0;
697
+ let h = 0;
698
+
699
+ for (let i = 0, l = node.canvas.length; i < l; i++) {
700
+ let vector = node.canvas[i];
701
+
702
+ switch (vector.type) {
703
+ case 'ellipse':
704
+ w = Math.max(w, vector.x + vector.r1);
705
+ h = Math.max(h, vector.y + vector.r2);
706
+ break;
707
+ case 'rect':
708
+ w = Math.max(w, vector.x + vector.w);
709
+ h = Math.max(h, vector.y + vector.h);
710
+ break;
711
+ case 'line':
712
+ w = Math.max(w, vector.x1, vector.x2);
713
+ h = Math.max(h, vector.y1, vector.y2);
714
+ break;
715
+ case 'polyline':
716
+ for (let i2 = 0, l2 = vector.points.length; i2 < l2; i2++) {
717
+ w = Math.max(w, vector.points[i2].x);
718
+ h = Math.max(h, vector.points[i2].y);
719
+ }
720
+ break;
721
+ }
722
+ }
723
+
724
+ node._minWidth = node._maxWidth = w;
725
+ node._minHeight = node._maxHeight = h;
726
+ node._alignment = this.styleStack.getProperty('alignment');
727
+
728
+ return node;
729
+ }
730
+
731
+ measureQr(node) {
732
+ node = qrEncoder.measure(node);
733
+ node._alignment = this.styleStack.getProperty('alignment');
734
+ return node;
735
+ }
736
+
737
+ measureAttachment(node) {
738
+ node._width = node.width || 7;
739
+ node._height = node.height || 18;
740
+
741
+ return node;
742
+ }
743
+ }
744
+
745
+ export default DocMeasure;