pdfmake 0.2.13 → 0.3.0-beta.10

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