@flowaccount/pdfmake 1.0.6-staging.2 → 1.0.6

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