@digicole/pdfmake-rtl 1.2.0 → 2.1.1

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