pdfmake 0.2.1 → 0.3.0-beta.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 (130) hide show
  1. package/CHANGELOG.md +8 -14
  2. package/README.md +8 -6
  3. package/build/pdfmake.js +31416 -28217
  4. package/build/pdfmake.js.map +1 -1
  5. package/build/pdfmake.min.js +2 -2
  6. package/build/pdfmake.min.js.map +1 -1
  7. package/build/standard-fonts/Courier.js +27 -0
  8. package/build/standard-fonts/Helvetica.js +27 -0
  9. package/build/standard-fonts/Symbol.js +21 -0
  10. package/build/standard-fonts/Times.js +27 -0
  11. package/build/standard-fonts/ZapfDingbats.js +21 -0
  12. package/build/vfs_fonts.js +2 -2
  13. package/build-vfs.js +3 -3
  14. package/fonts/Roboto/Roboto-Italic.ttf +0 -0
  15. package/fonts/Roboto/Roboto-Medium.ttf +0 -0
  16. package/fonts/Roboto/Roboto-MediumItalic.ttf +0 -0
  17. package/fonts/Roboto/Roboto-Regular.ttf +0 -0
  18. package/fonts/Roboto.js +8 -0
  19. package/js/3rd-party/svg-to-pdfkit/source.js +4301 -0
  20. package/js/3rd-party/svg-to-pdfkit.js +11 -0
  21. package/js/DocMeasure.js +750 -0
  22. package/js/DocPreprocessor.js +285 -0
  23. package/js/DocumentContext.js +306 -0
  24. package/js/ElementWriter.js +377 -0
  25. package/js/LayoutBuilder.js +833 -0
  26. package/js/Line.js +125 -0
  27. package/js/OutputDocument.js +86 -0
  28. package/js/OutputDocumentServer.js +34 -0
  29. package/js/PDFDocument.js +163 -0
  30. package/js/PageElementWriter.js +161 -0
  31. package/js/PageSize.js +88 -0
  32. package/js/Printer.js +238 -0
  33. package/js/Renderer.js +433 -0
  34. package/js/SVGMeasure.js +89 -0
  35. package/js/StyleContextStack.js +206 -0
  36. package/js/TableProcessor.js +590 -0
  37. package/js/TextBreaker.js +182 -0
  38. package/js/TextDecorator.js +181 -0
  39. package/js/TextInlines.js +248 -0
  40. package/js/URLResolver.js +79 -0
  41. package/js/base.js +69 -0
  42. package/js/browser-extensions/OutputDocumentBrowser.js +131 -0
  43. package/js/browser-extensions/URLBrowserResolver.js +89 -0
  44. package/js/browser-extensions/fonts/Roboto.js +42 -0
  45. package/js/browser-extensions/index.js +70 -0
  46. package/js/browser-extensions/pdfMake.js +17 -0
  47. package/js/browser-extensions/standard-fonts/Courier.js +42 -0
  48. package/js/browser-extensions/standard-fonts/Helvetica.js +42 -0
  49. package/js/browser-extensions/standard-fonts/Symbol.js +27 -0
  50. package/js/browser-extensions/standard-fonts/Times.js +42 -0
  51. package/js/browser-extensions/standard-fonts/ZapfDingbats.js +27 -0
  52. package/js/browser-extensions/virtual-fs-cjs.js +3 -0
  53. package/js/columnCalculator.js +142 -0
  54. package/js/helpers/node.js +122 -0
  55. package/js/helpers/tools.js +48 -0
  56. package/js/helpers/variableType.js +52 -0
  57. package/js/index.js +21 -0
  58. package/js/qrEnc.js +810 -0
  59. package/js/standardPageSizes.js +57 -0
  60. package/js/tableLayouts.js +127 -0
  61. package/js/virtual-fs.js +77 -0
  62. package/package.json +31 -25
  63. package/src/3rd-party/svg-to-pdfkit/LICENSE +9 -0
  64. package/src/3rd-party/svg-to-pdfkit/source.js +2552 -0
  65. package/src/3rd-party/svg-to-pdfkit.js +3 -0
  66. package/src/DocMeasure.js +694 -0
  67. package/src/DocPreprocessor.js +258 -0
  68. package/src/DocumentContext.js +309 -0
  69. package/src/ElementWriter.js +368 -0
  70. package/src/LayoutBuilder.js +814 -0
  71. package/src/Line.js +114 -0
  72. package/src/OutputDocument.js +78 -0
  73. package/src/OutputDocumentServer.js +26 -0
  74. package/src/PDFDocument.js +148 -0
  75. package/src/PageElementWriter.js +156 -0
  76. package/src/PageSize.js +53 -0
  77. package/src/Printer.js +220 -0
  78. package/src/Renderer.js +370 -0
  79. package/src/SVGMeasure.js +79 -0
  80. package/src/StyleContextStack.js +216 -0
  81. package/src/TableProcessor.js +558 -0
  82. package/src/TextBreaker.js +149 -0
  83. package/src/TextDecorator.js +161 -0
  84. package/src/TextInlines.js +223 -0
  85. package/src/URLResolver.js +69 -0
  86. package/src/base.js +61 -0
  87. package/src/browser-extensions/OutputDocumentBrowser.js +117 -0
  88. package/src/browser-extensions/URLBrowserResolver.js +46 -53
  89. package/src/browser-extensions/fonts/Roboto.js +27 -0
  90. package/src/browser-extensions/index.js +55 -0
  91. package/src/browser-extensions/pdfMake.js +10 -287
  92. package/src/browser-extensions/standard-fonts/Courier.js +27 -0
  93. package/src/browser-extensions/standard-fonts/Helvetica.js +27 -0
  94. package/src/browser-extensions/standard-fonts/Symbol.js +21 -0
  95. package/src/browser-extensions/standard-fonts/Times.js +27 -0
  96. package/src/browser-extensions/standard-fonts/ZapfDingbats.js +21 -0
  97. package/src/browser-extensions/virtual-fs-cjs.js +1 -0
  98. package/src/columnCalculator.js +29 -32
  99. package/src/helpers/node.js +110 -0
  100. package/src/helpers/tools.js +39 -0
  101. package/src/helpers/variableType.js +39 -0
  102. package/src/index.js +16 -0
  103. package/src/qrEnc.js +15 -10
  104. package/src/standardPageSizes.js +1 -3
  105. package/src/tableLayouts.js +100 -0
  106. package/src/virtual-fs.js +66 -0
  107. package/standard-fonts/Courier.js +8 -0
  108. package/standard-fonts/Helvetica.js +9 -0
  109. package/standard-fonts/Symbol.js +5 -0
  110. package/standard-fonts/Times.js +8 -0
  111. package/standard-fonts/ZapfDingbats.js +5 -0
  112. package/src/browser-extensions/virtual-fs.js +0 -55
  113. package/src/docMeasure.js +0 -807
  114. package/src/docPreprocessor.js +0 -255
  115. package/src/documentContext.js +0 -314
  116. package/src/elementWriter.js +0 -322
  117. package/src/fontProvider.js +0 -68
  118. package/src/helpers.js +0 -113
  119. package/src/imageMeasure.js +0 -51
  120. package/src/layoutBuilder.js +0 -807
  121. package/src/line.js +0 -91
  122. package/src/pageElementWriter.js +0 -174
  123. package/src/pdfKitEngine.js +0 -21
  124. package/src/printer.js +0 -685
  125. package/src/styleContextStack.js +0 -179
  126. package/src/svgMeasure.js +0 -70
  127. package/src/tableProcessor.js +0 -542
  128. package/src/textDecorator.js +0 -151
  129. package/src/textTools.js +0 -373
  130. package/src/traversalTracker.js +0 -47
@@ -0,0 +1,814 @@
1
+ import DocPreprocessor from './DocPreprocessor';
2
+ import DocMeasure from './DocMeasure';
3
+ import DocumentContext from './DocumentContext';
4
+ import PageElementWriter from './PageElementWriter';
5
+ import ColumnCalculator from './columnCalculator';
6
+ import TableProcessor from './TableProcessor';
7
+ import Line from './Line';
8
+ import { isString, isValue, isNumber } from './helpers/variableType';
9
+ import { stringifyNode, getNodeId } from './helpers/node';
10
+ import { pack, offsetVector } from './helpers/tools';
11
+ import TextInlines from './TextInlines';
12
+ import StyleContextStack from './StyleContextStack';
13
+
14
+ function addAll(target, otherArray) {
15
+ otherArray.forEach(item => {
16
+ target.push(item);
17
+ });
18
+ }
19
+
20
+ /**
21
+ * Layout engine which turns document-definition-object into a set of pages, lines, inlines
22
+ * and vectors ready to be rendered into a PDF
23
+ */
24
+ class LayoutBuilder {
25
+ /**
26
+ * @param {object} pageSize - an object defining page width and height
27
+ * @param {object} pageMargins - an object defining top, left, right and bottom margins
28
+ * @param {object} svgMeasure
29
+ */
30
+ constructor(pageSize, pageMargins, svgMeasure) {
31
+ this.pageSize = pageSize;
32
+ this.pageMargins = pageMargins;
33
+ this.svgMeasure = svgMeasure;
34
+ this.tableLayouts = {};
35
+ }
36
+
37
+ registerTableLayouts(tableLayouts) {
38
+ this.tableLayouts = pack(this.tableLayouts, tableLayouts);
39
+ }
40
+
41
+ /**
42
+ * Executes layout engine on document-definition-object and creates an array of pages
43
+ * containing positioned Blocks, Lines and inlines
44
+ *
45
+ * @param {object} docStructure document-definition-object
46
+ * @param {object} pdfDocument pdfkit document
47
+ * @param {object} styleDictionary dictionary with style definitions
48
+ * @param {object} defaultStyle default style definition
49
+ * @param {object} background
50
+ * @param {object} header
51
+ * @param {object} footer
52
+ * @param {object} watermark
53
+ * @param {object} pageBreakBeforeFct
54
+ * @returns {Array} an array of pages
55
+ */
56
+ layoutDocument(
57
+ docStructure,
58
+ pdfDocument,
59
+ styleDictionary,
60
+ defaultStyle,
61
+ background,
62
+ header,
63
+ footer,
64
+ watermark,
65
+ pageBreakBeforeFct
66
+ ) {
67
+
68
+ function addPageBreaksIfNecessary(linearNodeList, pages) {
69
+
70
+ if (typeof pageBreakBeforeFct !== 'function') {
71
+ return false;
72
+ }
73
+
74
+ linearNodeList = linearNodeList.filter(node => node.positions.length > 0);
75
+
76
+ linearNodeList.forEach(node => {
77
+ let nodeInfo = {};
78
+ [
79
+ 'id', 'text', 'ul', 'ol', 'table', 'image', 'qr', 'canvas', 'svg', 'columns',
80
+ 'headlineLevel', 'style', 'pageBreak', 'pageOrientation',
81
+ 'width', 'height'
82
+ ].forEach(key => {
83
+ if (node[key] !== undefined) {
84
+ nodeInfo[key] = node[key];
85
+ }
86
+ });
87
+ nodeInfo.startPosition = node.positions[0];
88
+ nodeInfo.pageNumbers = Array.from(new Set(node.positions.map(node => node.pageNumber)));
89
+ nodeInfo.pages = pages.length;
90
+ nodeInfo.stack = Array.isArray(node.stack);
91
+
92
+ node.nodeInfo = nodeInfo;
93
+ });
94
+
95
+ for (let index = 0; index < linearNodeList.length; index++) {
96
+ let node = linearNodeList[index];
97
+ if (node.pageBreak !== 'before' && !node.pageBreakCalculated) {
98
+ node.pageBreakCalculated = true;
99
+ let pageNumber = node.nodeInfo.pageNumbers[0];
100
+
101
+ if (
102
+ pageBreakBeforeFct(node.nodeInfo, {
103
+ getFollowingNodesOnPage: () => {
104
+ let followingNodesOnPage = [];
105
+ for (let ii = index + 1, l = linearNodeList.length; ii < l; ii++) {
106
+ if (linearNodeList[ii].nodeInfo.pageNumbers.indexOf(pageNumber) > -1) {
107
+ followingNodesOnPage.push(linearNodeList[ii].nodeInfo);
108
+ }
109
+ }
110
+ return followingNodesOnPage;
111
+ },
112
+ getNodesOnNextPage: () => {
113
+ let nodesOnNextPage = [];
114
+ for (let ii = index + 1, l = linearNodeList.length; ii < l; ii++) {
115
+ if (linearNodeList[ii].nodeInfo.pageNumbers.indexOf(pageNumber + 1) > -1) {
116
+ nodesOnNextPage.push(linearNodeList[ii].nodeInfo);
117
+ }
118
+ }
119
+ return nodesOnNextPage;
120
+ },
121
+ getPreviousNodesOnPage: () => {
122
+ let previousNodesOnPage = [];
123
+ for (let ii = 0; ii < index; ii++) {
124
+ if (linearNodeList[ii].nodeInfo.pageNumbers.indexOf(pageNumber) > -1) {
125
+ previousNodesOnPage.push(linearNodeList[ii].nodeInfo);
126
+ }
127
+ }
128
+ return previousNodesOnPage;
129
+ },
130
+ })
131
+ ) {
132
+ node.pageBreak = 'before';
133
+ return true;
134
+ }
135
+ }
136
+ }
137
+
138
+ return false;
139
+ }
140
+
141
+ this.docPreprocessor = new DocPreprocessor();
142
+ this.docMeasure = new DocMeasure(pdfDocument, styleDictionary, defaultStyle, this.svgMeasure, this.tableLayouts);
143
+
144
+ function resetXYs(result) {
145
+ result.linearNodeList.forEach(node => {
146
+ node.resetXY();
147
+ });
148
+ }
149
+
150
+ let result = this.tryLayoutDocument(docStructure, pdfDocument, styleDictionary, defaultStyle, background, header, footer, watermark);
151
+ while (addPageBreaksIfNecessary(result.linearNodeList, result.pages)) {
152
+ resetXYs(result);
153
+ result = this.tryLayoutDocument(docStructure, pdfDocument, styleDictionary, defaultStyle, background, header, footer, watermark);
154
+ }
155
+
156
+ return result.pages;
157
+ }
158
+
159
+ tryLayoutDocument(
160
+ docStructure,
161
+ pdfDocument,
162
+ styleDictionary,
163
+ defaultStyle,
164
+ background,
165
+ header,
166
+ footer,
167
+ watermark
168
+ ) {
169
+
170
+ this.linearNodeList = [];
171
+ docStructure = this.docPreprocessor.preprocessDocument(docStructure);
172
+ docStructure = this.docMeasure.measureDocument(docStructure);
173
+
174
+ this.writer = new PageElementWriter(
175
+ new DocumentContext(this.pageSize, this.pageMargins));
176
+
177
+ this.writer.context().addListener('pageAdded', () => {
178
+ this.addBackground(background);
179
+ });
180
+
181
+ this.addBackground(background);
182
+ this.processNode(docStructure);
183
+ this.addHeadersAndFooters(header, footer);
184
+ if (watermark != null) {
185
+ this.addWatermark(watermark, pdfDocument, defaultStyle);
186
+ }
187
+
188
+ return { pages: this.writer.context().pages, linearNodeList: this.linearNodeList };
189
+ }
190
+
191
+ addBackground(background) {
192
+ let backgroundGetter = typeof background === 'function' ? background : () => background;
193
+
194
+ let context = this.writer.context();
195
+ let pageSize = context.getCurrentPage().pageSize;
196
+
197
+ let pageBackground = backgroundGetter(context.page + 1, pageSize);
198
+
199
+ if (pageBackground) {
200
+ this.writer.beginUnbreakableBlock(pageSize.width, pageSize.height);
201
+ pageBackground = this.docPreprocessor.preprocessDocument(pageBackground);
202
+ this.processNode(this.docMeasure.measureDocument(pageBackground));
203
+ this.writer.commitUnbreakableBlock(0, 0);
204
+ context.backgroundLength[context.page] += pageBackground.positions.length;
205
+ }
206
+ }
207
+
208
+ addStaticRepeatable(headerOrFooter, sizeFunction) {
209
+ this.addDynamicRepeatable(() => // copy to new object
210
+ JSON.parse(JSON.stringify(headerOrFooter)), sizeFunction);
211
+ }
212
+
213
+ addDynamicRepeatable(nodeGetter, sizeFunction) {
214
+ let pages = this.writer.context().pages;
215
+
216
+ for (let pageIndex = 0, l = pages.length; pageIndex < l; pageIndex++) {
217
+ this.writer.context().page = pageIndex;
218
+
219
+ let node = nodeGetter(pageIndex + 1, l, this.writer.context().pages[pageIndex].pageSize);
220
+
221
+ if (node) {
222
+ let sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.pageMargins);
223
+ this.writer.beginUnbreakableBlock(sizes.width, sizes.height);
224
+ node = this.docPreprocessor.preprocessDocument(node);
225
+ this.processNode(this.docMeasure.measureDocument(node));
226
+ this.writer.commitUnbreakableBlock(sizes.x, sizes.y);
227
+ }
228
+ }
229
+ }
230
+
231
+ addHeadersAndFooters(header, footer) {
232
+ const headerSizeFct = (pageSize, pageMargins) => ({
233
+ x: 0,
234
+ y: 0,
235
+ width: pageSize.width,
236
+ height: pageMargins.top
237
+ });
238
+
239
+ const footerSizeFct = (pageSize, pageMargins) => ({
240
+ x: 0,
241
+ y: pageSize.height - pageMargins.bottom,
242
+ width: pageSize.width,
243
+ height: pageMargins.bottom
244
+ });
245
+
246
+ if (typeof header === 'function') {
247
+ this.addDynamicRepeatable(header, headerSizeFct);
248
+ } else if (header) {
249
+ this.addStaticRepeatable(header, headerSizeFct);
250
+ }
251
+
252
+ if (typeof footer === 'function') {
253
+ this.addDynamicRepeatable(footer, footerSizeFct);
254
+ } else if (footer) {
255
+ this.addStaticRepeatable(footer, footerSizeFct);
256
+ }
257
+ }
258
+
259
+ addWatermark(watermark, pdfDocument, defaultStyle) {
260
+ if (isString(watermark)) {
261
+ watermark = { 'text': watermark };
262
+ }
263
+
264
+ if (!watermark.text) { // empty watermark text
265
+ return;
266
+ }
267
+
268
+ watermark.font = watermark.font || defaultStyle.font || 'Roboto';
269
+ watermark.fontSize = watermark.fontSize || 'auto';
270
+ watermark.color = watermark.color || 'black';
271
+ watermark.opacity = isNumber(watermark.opacity) ? watermark.opacity : 0.6;
272
+ watermark.bold = watermark.bold || false;
273
+ watermark.italics = watermark.italics || false;
274
+ watermark.angle = isValue(watermark.angle) ? watermark.angle : null;
275
+
276
+ if (watermark.angle === null) {
277
+ watermark.angle = Math.atan2(this.pageSize.height, this.pageSize.width) * -180 / Math.PI;
278
+ }
279
+
280
+ if (watermark.fontSize === 'auto') {
281
+ watermark.fontSize = getWatermarkFontSize(this.pageSize, watermark, pdfDocument);
282
+ }
283
+
284
+ let watermarkObject = {
285
+ text: watermark.text,
286
+ font: pdfDocument.provideFont(watermark.font, watermark.bold, watermark.italics),
287
+ fontSize: watermark.fontSize,
288
+ color: watermark.color,
289
+ opacity: watermark.opacity,
290
+ angle: watermark.angle
291
+ };
292
+
293
+ watermarkObject._size = getWatermarkSize(watermark, pdfDocument);
294
+
295
+ let pages = this.writer.context().pages;
296
+ for (let i = 0, l = pages.length; i < l; i++) {
297
+ pages[i].watermark = watermarkObject;
298
+ }
299
+
300
+ function getWatermarkSize(watermark, pdfDocument) {
301
+ let textInlines = new TextInlines(pdfDocument);
302
+ let styleContextStack = new StyleContextStack(null, { font: watermark.font, bold: watermark.bold, italics: watermark.italics });
303
+
304
+ styleContextStack.push({
305
+ fontSize: watermark.fontSize
306
+ });
307
+
308
+ let size = textInlines.sizeOfText(watermark.text, styleContextStack);
309
+ let rotatedSize = textInlines.sizeOfRotatedText(watermark.text, watermark.angle, styleContextStack);
310
+
311
+ return { size: size, rotatedSize: rotatedSize };
312
+ }
313
+
314
+ function getWatermarkFontSize(pageSize, watermark, pdfDocument) {
315
+ let textInlines = new TextInlines(pdfDocument);
316
+ let styleContextStack = new StyleContextStack(null, { font: watermark.font, bold: watermark.bold, italics: watermark.italics });
317
+ let rotatedSize;
318
+
319
+ /**
320
+ * Binary search the best font size.
321
+ * Initial bounds [0, 1000]
322
+ * Break when range < 1
323
+ */
324
+ let a = 0;
325
+ let b = 1000;
326
+ let c = (a + b) / 2;
327
+ while (Math.abs(a - b) > 1) {
328
+ styleContextStack.push({
329
+ fontSize: c
330
+ });
331
+ rotatedSize = textInlines.sizeOfRotatedText(watermark.text, watermark.angle, styleContextStack);
332
+
333
+ if (rotatedSize.width > pageSize.width) {
334
+ b = c;
335
+ c = (a + b) / 2;
336
+ } else if (rotatedSize.width < pageSize.width) {
337
+ if (rotatedSize.height > pageSize.height) {
338
+ b = c;
339
+ c = (a + b) / 2;
340
+ } else {
341
+ a = c;
342
+ c = (a + b) / 2;
343
+ }
344
+ }
345
+ styleContextStack.pop();
346
+ }
347
+ /*
348
+ End binary search
349
+ */
350
+ return c;
351
+ }
352
+ }
353
+
354
+ processNode(node) {
355
+ const applyMargins = callback => {
356
+ let margin = node._margin;
357
+
358
+ if (node.pageBreak === 'before') {
359
+ this.writer.moveToNextPage(node.pageOrientation);
360
+ } else if (node.pageBreak === 'beforeOdd') {
361
+ this.writer.moveToNextPage(node.pageOrientation);
362
+ if ((this.writer.context().page + 1) % 2 === 1) {
363
+ this.writer.moveToNextPage(node.pageOrientation);
364
+ }
365
+ } else if (node.pageBreak === 'beforeEven') {
366
+ this.writer.moveToNextPage(node.pageOrientation);
367
+ if ((this.writer.context().page + 1) % 2 === 0) {
368
+ this.writer.moveToNextPage(node.pageOrientation);
369
+ }
370
+ }
371
+
372
+ if (margin) {
373
+ this.writer.context().moveDown(margin[1]);
374
+ this.writer.context().addMargin(margin[0], margin[2]);
375
+ }
376
+
377
+ callback();
378
+
379
+ if (margin) {
380
+ this.writer.context().addMargin(-margin[0], -margin[2]);
381
+ this.writer.context().moveDown(margin[3]);
382
+ }
383
+
384
+ if (node.pageBreak === 'after') {
385
+ this.writer.moveToNextPage(node.pageOrientation);
386
+ } else if (node.pageBreak === 'afterOdd') {
387
+ this.writer.moveToNextPage(node.pageOrientation);
388
+ if ((this.writer.context().page + 1) % 2 === 1) {
389
+ this.writer.moveToNextPage(node.pageOrientation);
390
+ }
391
+ } else if (node.pageBreak === 'afterEven') {
392
+ this.writer.moveToNextPage(node.pageOrientation);
393
+ if ((this.writer.context().page + 1) % 2 === 0) {
394
+ this.writer.moveToNextPage(node.pageOrientation);
395
+ }
396
+ }
397
+ };
398
+
399
+ this.linearNodeList.push(node);
400
+ decorateNode(node);
401
+
402
+ applyMargins(() => {
403
+ let unbreakable = node.unbreakable;
404
+ if (unbreakable) {
405
+ this.writer.beginUnbreakableBlock();
406
+ }
407
+
408
+ let absPosition = node.absolutePosition;
409
+ if (absPosition) {
410
+ this.writer.context().beginDetachedBlock();
411
+ this.writer.context().moveTo(absPosition.x || 0, absPosition.y || 0);
412
+ }
413
+
414
+ let relPosition = node.relativePosition;
415
+ if (relPosition) {
416
+ this.writer.context().beginDetachedBlock();
417
+ this.writer.context().moveToRelative(relPosition.x || 0, relPosition.y || 0);
418
+ }
419
+
420
+ if (node.stack) {
421
+ this.processVerticalContainer(node);
422
+ } else if (node.columns) {
423
+ this.processColumns(node);
424
+ } else if (node.ul) {
425
+ this.processList(false, node);
426
+ } else if (node.ol) {
427
+ this.processList(true, node);
428
+ } else if (node.table) {
429
+ this.processTable(node);
430
+ } else if (node.text !== undefined) {
431
+ this.processLeaf(node);
432
+ } else if (node.toc) {
433
+ this.processToc(node);
434
+ } else if (node.image) {
435
+ this.processImage(node);
436
+ } else if (node.svg) {
437
+ this.processSVG(node);
438
+ } else if (node.canvas) {
439
+ this.processCanvas(node);
440
+ } else if (node.qr) {
441
+ this.processQr(node);
442
+ } else if (!node._span) {
443
+ throw new Error(`Unrecognized document structure: ${stringifyNode(node)}`);
444
+ }
445
+
446
+ if (absPosition || relPosition) {
447
+ this.writer.context().endDetachedBlock();
448
+ }
449
+
450
+ if (unbreakable) {
451
+ this.writer.commitUnbreakableBlock();
452
+ }
453
+ });
454
+ }
455
+
456
+ // vertical container
457
+ processVerticalContainer(node) {
458
+ node.stack.forEach(item => {
459
+ this.processNode(item);
460
+ addAll(node.positions, item.positions);
461
+
462
+ //TODO: paragraph gap
463
+ }, this);
464
+ }
465
+
466
+ // columns
467
+ processColumns(columnNode) {
468
+ let columns = columnNode.columns;
469
+ let availableWidth = this.writer.context().availableWidth;
470
+ let gaps = gapArray(columnNode._gap);
471
+
472
+ if (gaps) {
473
+ availableWidth -= (gaps.length - 1) * columnNode._gap;
474
+ }
475
+
476
+ ColumnCalculator.buildColumnWidths(columns, availableWidth);
477
+ let result = this.processRow(columns, columns, gaps);
478
+ addAll(columnNode.positions, result.positions);
479
+
480
+ function gapArray(gap) {
481
+ if (!gap) {
482
+ return null;
483
+ }
484
+
485
+ let gaps = [];
486
+ gaps.push(0);
487
+
488
+ for (let i = columns.length - 1; i > 0; i--) {
489
+ gaps.push(gap);
490
+ }
491
+
492
+ return gaps;
493
+ }
494
+ }
495
+
496
+ processRow(columns, widths, gaps, tableBody, tableRow, height) {
497
+ const storePageBreakData = data => {
498
+ let pageDesc;
499
+
500
+ for (let i = 0, l = pageBreaks.length; i < l; i++) {
501
+ let desc = pageBreaks[i];
502
+ if (desc.prevPage === data.prevPage) {
503
+ pageDesc = desc;
504
+ break;
505
+ }
506
+ }
507
+
508
+ if (!pageDesc) {
509
+ pageDesc = data;
510
+ pageBreaks.push(pageDesc);
511
+ }
512
+ pageDesc.prevY = Math.max(pageDesc.prevY, data.prevY);
513
+ pageDesc.y = Math.min(pageDesc.y, data.y);
514
+ };
515
+
516
+ let pageBreaks = [];
517
+ let positions = [];
518
+
519
+ this.writer.addListener('pageChanged', storePageBreakData);
520
+
521
+ widths = widths || columns;
522
+
523
+ this.writer.context().beginColumnGroup();
524
+
525
+ for (let i = 0, l = columns.length; i < l; i++) {
526
+ let column = columns[i];
527
+ let width = widths[i]._calcWidth;
528
+ let leftOffset = colLeftOffset(i);
529
+
530
+ if (column.colSpan && column.colSpan > 1) {
531
+ for (let j = 1; j < column.colSpan; j++) {
532
+ width += widths[++i]._calcWidth + gaps[i];
533
+ }
534
+ }
535
+
536
+ this.writer.context().beginColumn(width, leftOffset, getEndingCell(column, i));
537
+ if (!column._span) {
538
+ this.processNode(column);
539
+ addAll(positions, column.positions);
540
+ } else if (column._columnEndingContext) {
541
+ // row-span ending
542
+ this.writer.context().markEnding(column);
543
+ }
544
+ }
545
+
546
+ this.writer.context().completeColumnGroup(height);
547
+
548
+ this.writer.removeListener('pageChanged', storePageBreakData);
549
+
550
+ return { pageBreaks: pageBreaks, positions: positions };
551
+
552
+ function colLeftOffset(i) {
553
+ if (gaps && gaps.length > i) {
554
+ return gaps[i];
555
+ }
556
+ return 0;
557
+ }
558
+
559
+ function getEndingCell(column, columnIndex) {
560
+ if (column.rowSpan && column.rowSpan > 1) {
561
+ let endingRow = tableRow + column.rowSpan - 1;
562
+ if (endingRow >= tableBody.length) {
563
+ throw new Error(`Row span for column ${columnIndex} (with indexes starting from 0) exceeded row count`);
564
+ }
565
+ return tableBody[endingRow][columnIndex];
566
+ }
567
+
568
+ return null;
569
+ }
570
+ }
571
+
572
+ // lists
573
+ processList(orderedList, node) {
574
+ const addMarkerToFirstLeaf = line => {
575
+ // I'm not very happy with the way list processing is implemented
576
+ // (both code and algorithm should be rethinked)
577
+ if (nextMarker) {
578
+ let marker = nextMarker;
579
+ nextMarker = null;
580
+
581
+ if (marker.canvas) {
582
+ let vector = marker.canvas[0];
583
+
584
+ offsetVector(vector, -marker._minWidth, 0);
585
+ this.writer.addVector(vector);
586
+ } else if (marker._inlines) {
587
+ let markerLine = new Line(this.pageSize.width);
588
+ markerLine.addInline(marker._inlines[0]);
589
+ markerLine.x = -marker._minWidth;
590
+ markerLine.y = line.getAscenderHeight() - markerLine.getAscenderHeight();
591
+ this.writer.addLine(markerLine, true);
592
+ }
593
+ }
594
+ };
595
+
596
+ let items = orderedList ? node.ol : node.ul;
597
+ let gapSize = node._gapSize;
598
+
599
+ this.writer.context().addMargin(gapSize.width);
600
+
601
+ let nextMarker;
602
+
603
+ this.writer.addListener('lineAdded', addMarkerToFirstLeaf);
604
+
605
+ items.forEach(item => {
606
+ nextMarker = item.listMarker;
607
+ this.processNode(item);
608
+ addAll(node.positions, item.positions);
609
+ });
610
+
611
+ this.writer.removeListener('lineAdded', addMarkerToFirstLeaf);
612
+
613
+ this.writer.context().addMargin(-gapSize.width);
614
+ }
615
+
616
+ // tables
617
+ processTable(tableNode) {
618
+ let processor = new TableProcessor(tableNode);
619
+
620
+ processor.beginTable(this.writer);
621
+
622
+ let rowHeights = tableNode.table.heights;
623
+ for (let i = 0, l = tableNode.table.body.length; i < l; i++) {
624
+ processor.beginRow(i, this.writer);
625
+
626
+ let height;
627
+ if (typeof rowHeights === 'function') {
628
+ height = rowHeights(i);
629
+ } else if (Array.isArray(rowHeights)) {
630
+ height = rowHeights[i];
631
+ } else {
632
+ height = rowHeights;
633
+ }
634
+
635
+ if (height === 'auto') {
636
+ height = undefined;
637
+ }
638
+
639
+ let result = this.processRow(tableNode.table.body[i], tableNode.table.widths, tableNode._offsets.offsets, tableNode.table.body, i, height);
640
+ addAll(tableNode.positions, result.positions);
641
+
642
+ processor.endRow(i, this.writer, result.pageBreaks);
643
+ }
644
+
645
+ processor.endTable(this.writer);
646
+ }
647
+
648
+ // leafs (texts)
649
+ processLeaf(node) {
650
+ let line = this.buildNextLine(node);
651
+ if (line && (node.tocItem || node.id)) {
652
+ line._node = node;
653
+ }
654
+ let currentHeight = (line) ? line.getHeight() : 0;
655
+ let maxHeight = node.maxHeight || -1;
656
+
657
+ if (line) {
658
+ let nodeId = getNodeId(node);
659
+ if (nodeId) {
660
+ line.id = nodeId;
661
+ }
662
+ }
663
+
664
+ if (node._tocItemRef) {
665
+ line._pageNodeRef = node._tocItemRef;
666
+ }
667
+
668
+ if (node._pageRef) {
669
+ line._pageNodeRef = node._pageRef._nodeRef;
670
+ }
671
+
672
+ if (line && line.inlines && Array.isArray(line.inlines)) {
673
+ for (let i = 0, l = line.inlines.length; i < l; i++) {
674
+ if (line.inlines[i]._tocItemRef) {
675
+ line.inlines[i]._pageNodeRef = line.inlines[i]._tocItemRef;
676
+ }
677
+
678
+ if (line.inlines[i]._pageRef) {
679
+ line.inlines[i]._pageNodeRef = line.inlines[i]._pageRef._nodeRef;
680
+ }
681
+ }
682
+ }
683
+
684
+ while (line && (maxHeight === -1 || currentHeight < maxHeight)) {
685
+ let positions = this.writer.addLine(line);
686
+ node.positions.push(positions);
687
+ line = this.buildNextLine(node);
688
+ if (line) {
689
+ currentHeight += line.getHeight();
690
+ }
691
+ }
692
+ }
693
+
694
+ processToc(node) {
695
+ if (node.toc.title) {
696
+ this.processNode(node.toc.title);
697
+ }
698
+ if (node.toc._table) {
699
+ this.processNode(node.toc._table);
700
+ }
701
+ }
702
+
703
+ buildNextLine(textNode) {
704
+
705
+ function cloneInline(inline) {
706
+ let newInline = inline.constructor();
707
+ for (let key in inline) {
708
+ newInline[key] = inline[key];
709
+ }
710
+ return newInline;
711
+ }
712
+
713
+ if (!textNode._inlines || textNode._inlines.length === 0) {
714
+ return null;
715
+ }
716
+
717
+ let line = new Line(this.writer.context().availableWidth);
718
+ const textInlines = new TextInlines(null);
719
+
720
+ let isForceContinue = false;
721
+ while (textNode._inlines && textNode._inlines.length > 0 &&
722
+ (line.hasEnoughSpaceForInline(textNode._inlines[0], textNode._inlines.slice(1)) || isForceContinue)) {
723
+ let isHardWrap = false;
724
+ let inline = textNode._inlines.shift();
725
+ isForceContinue = false;
726
+
727
+ if (!inline.noWrap && inline.text.length > 1 && inline.width > line.getAvailableWidth()) {
728
+ let widthPerChar = inline.width / inline.text.length;
729
+ let maxChars = Math.floor(line.getAvailableWidth() / widthPerChar);
730
+ if (maxChars < 1) {
731
+ maxChars = 1;
732
+ }
733
+ if (maxChars < inline.text.length) {
734
+ let newInline = cloneInline(inline);
735
+
736
+ newInline.text = inline.text.substr(maxChars);
737
+ inline.text = inline.text.substr(0, maxChars);
738
+
739
+ newInline.width = textInlines.widthOfText(newInline.text, newInline);
740
+ inline.width = textInlines.widthOfText(inline.text, inline);
741
+
742
+ textNode._inlines.unshift(newInline);
743
+ isHardWrap = true;
744
+ }
745
+ }
746
+
747
+ line.addInline(inline);
748
+
749
+ isForceContinue = inline.noNewLine && !isHardWrap;
750
+ }
751
+
752
+ line.lastLineInParagraph = textNode._inlines.length === 0;
753
+
754
+ return line;
755
+ }
756
+
757
+ // images
758
+ processImage(node) {
759
+ let position = this.writer.addImage(node);
760
+ node.positions.push(position);
761
+ }
762
+
763
+ processCanvas(node) {
764
+ let positions = this.writer.addCanvas(node);
765
+ addAll(node.positions, positions);
766
+ }
767
+
768
+ processSVG(node) {
769
+ let position = this.writer.addSVG(node);
770
+ node.positions.push(position);
771
+ }
772
+
773
+ processQr(node) {
774
+ let position = this.writer.addQr(node);
775
+ node.positions.push(position);
776
+ }
777
+ }
778
+
779
+ function decorateNode(node) {
780
+ let x = node.x;
781
+ let y = node.y;
782
+ node.positions = [];
783
+
784
+ if (Array.isArray(node.canvas)) {
785
+ node.canvas.forEach(vector => {
786
+ let x = vector.x;
787
+ let y = vector.y;
788
+ let x1 = vector.x1;
789
+ let y1 = vector.y1;
790
+ let x2 = vector.x2;
791
+ let y2 = vector.y2;
792
+ vector.resetXY = () => {
793
+ vector.x = x;
794
+ vector.y = y;
795
+ vector.x1 = x1;
796
+ vector.y1 = y1;
797
+ vector.x2 = x2;
798
+ vector.y2 = y2;
799
+ };
800
+ });
801
+ }
802
+
803
+ node.resetXY = () => {
804
+ node.x = x;
805
+ node.y = y;
806
+ if (Array.isArray(node.canvas)) {
807
+ node.canvas.forEach(vector => {
808
+ vector.resetXY();
809
+ });
810
+ }
811
+ };
812
+ }
813
+
814
+ export default LayoutBuilder;