pdfmake 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/LICENSE +21 -21
  3. package/README.md +75 -78
  4. package/build/fonts/Roboto/Roboto-Italic.ttf +0 -0
  5. package/build/fonts/Roboto/Roboto-Medium.ttf +0 -0
  6. package/build/fonts/Roboto/Roboto-MediumItalic.ttf +0 -0
  7. package/build/fonts/Roboto/Roboto-Regular.ttf +0 -0
  8. package/build/fonts/Roboto.js +27 -0
  9. package/build/pdfmake.js +64813 -64584
  10. package/build/pdfmake.js.map +1 -1
  11. package/build/pdfmake.min.js +2 -2
  12. package/build/pdfmake.min.js.map +1 -1
  13. package/build/standard-fonts/Courier.js +27 -27
  14. package/build/standard-fonts/Helvetica.js +27 -27
  15. package/build/standard-fonts/Symbol.js +21 -21
  16. package/build/standard-fonts/Times.js +27 -27
  17. package/build/standard-fonts/ZapfDingbats.js +21 -21
  18. package/build/vfs_fonts.js +5 -5
  19. package/build-vfs.js +44 -44
  20. package/fonts/Roboto.js +8 -8
  21. package/js/3rd-party/svg-to-pdfkit/source.js +1 -1
  22. package/js/DocMeasure.js +11 -6
  23. package/js/DocumentContext.js +8 -3
  24. package/js/ElementWriter.js +42 -16
  25. package/js/LayoutBuilder.js +144 -78
  26. package/js/Line.js +16 -16
  27. package/js/OutputDocument.js +10 -10
  28. package/js/OutputDocumentServer.js +3 -3
  29. package/js/PDFDocument.js +3 -3
  30. package/js/PageElementWriter.js +15 -9
  31. package/js/Printer.js +28 -28
  32. package/js/Renderer.js +40 -8
  33. package/js/SVGMeasure.js +10 -10
  34. package/js/StyleContextStack.js +74 -51
  35. package/js/TableProcessor.js +14 -0
  36. package/js/TextBreaker.js +17 -17
  37. package/js/TextDecorator.js +12 -3
  38. package/js/TextInlines.js +34 -33
  39. package/js/base.js +4 -4
  40. package/js/browser-extensions/OutputDocumentBrowser.js +24 -24
  41. package/js/columnCalculator.js +2 -2
  42. package/js/helpers/node.js +47 -23
  43. package/js/helpers/variableType.js +18 -18
  44. package/js/qrEnc.js +38 -38
  45. package/js/virtual-fs.js +11 -11
  46. package/package.json +12 -12
  47. package/src/3rd-party/svg-to-pdfkit/LICENSE +9 -9
  48. package/src/3rd-party/svg-to-pdfkit/source.js +2745 -2745
  49. package/src/3rd-party/svg-to-pdfkit.js +3 -3
  50. package/src/DocMeasure.js +745 -738
  51. package/src/DocPreprocessor.js +283 -283
  52. package/src/DocumentContext.js +345 -338
  53. package/src/ElementWriter.js +441 -417
  54. package/src/LayoutBuilder.js +1336 -1258
  55. package/src/Line.js +114 -114
  56. package/src/OutputDocument.js +64 -64
  57. package/src/OutputDocumentServer.js +32 -32
  58. package/src/PDFDocument.js +174 -174
  59. package/src/PageElementWriter.js +187 -179
  60. package/src/PageSize.js +53 -53
  61. package/src/Printer.js +306 -306
  62. package/src/Renderer.js +445 -409
  63. package/src/SVGMeasure.js +109 -109
  64. package/src/StyleContextStack.js +208 -179
  65. package/src/TableProcessor.js +620 -602
  66. package/src/TextBreaker.js +168 -168
  67. package/src/TextDecorator.js +175 -161
  68. package/src/TextInlines.js +224 -223
  69. package/src/URLResolver.js +43 -43
  70. package/src/base.js +70 -70
  71. package/src/browser-extensions/OutputDocumentBrowser.js +80 -80
  72. package/src/browser-extensions/fonts/Roboto.js +27 -27
  73. package/src/browser-extensions/index.js +55 -55
  74. package/src/browser-extensions/pdfMake.js +1 -1
  75. package/src/browser-extensions/standard-fonts/Courier.js +27 -27
  76. package/src/browser-extensions/standard-fonts/Helvetica.js +27 -27
  77. package/src/browser-extensions/standard-fonts/Symbol.js +21 -21
  78. package/src/browser-extensions/standard-fonts/Times.js +27 -27
  79. package/src/browser-extensions/standard-fonts/ZapfDingbats.js +21 -21
  80. package/src/browser-extensions/virtual-fs-cjs.js +1 -1
  81. package/src/columnCalculator.js +154 -154
  82. package/src/helpers/node.js +134 -110
  83. package/src/helpers/tools.js +44 -44
  84. package/src/helpers/variableType.js +50 -50
  85. package/src/index.js +16 -16
  86. package/src/qrEnc.js +796 -796
  87. package/src/standardPageSizes.js +52 -52
  88. package/src/tableLayouts.js +100 -100
  89. package/src/virtual-fs.js +66 -66
  90. package/standard-fonts/Courier.js +8 -8
  91. package/standard-fonts/Helvetica.js +8 -8
  92. package/standard-fonts/Symbol.js +5 -5
  93. package/standard-fonts/Times.js +8 -8
  94. package/standard-fonts/ZapfDingbats.js +5 -5
package/build-vfs.js CHANGED
@@ -1,44 +1,44 @@
1
- const fs = require('fs');
2
-
3
- const vfsBefore = "var vfs = ";
4
- const vfsAfter = "; var _global = typeof window === 'object' ? window : typeof global === 'object' ? global : typeof self === 'object' ? self : this; if (typeof _global.pdfMake !== 'undefined' && typeof _global.pdfMake.addVirtualFileSystem !== 'undefined') { _global.pdfMake.addVirtualFileSystem(vfs); } if (typeof module !== 'undefined') { module.exports = vfs; }";
5
- const sourcePath = process.argv[2];
6
- const vfsFilename = process.argv[3] ? process.argv[3] : './build/vfs_fonts.js';
7
-
8
- var vfs = {};
9
-
10
- if (sourcePath === undefined) {
11
- console.error('Usage: node build-vfs.js path [filename]');
12
- console.log('');
13
- console.log('Parameters:');
14
- console.log(' path Source path with fonts.');
15
- console.log(' filename Optional. Output vfs file. Default: ./build/vfs_fonts.js');
16
- console.log('');
17
- console.log('Examples:');
18
- console.log(' node build-vfs.js "examples/fonts"');
19
- console.log(' node build-vfs.js "examples/fonts" "./build/vfs_fonts.js"');
20
- return;
21
- }
22
-
23
- if (!fs.existsSync(sourcePath)) {
24
- console.error('Source path "' + sourcePath + '" not found.');
25
- return;
26
- }
27
-
28
- console.log('Source path:', sourcePath);
29
- console.log('');
30
-
31
- var files = fs.readdirSync(sourcePath);
32
-
33
- files.forEach(function (file) {
34
- var fileBase64 = fs.readFileSync(sourcePath + '/' + file).toString('base64');
35
- console.log('FILE:', file);
36
-
37
- vfs[file] = fileBase64;
38
- });
39
-
40
- const vfsFileContent = vfsBefore + JSON.stringify(vfs, null, 2) + vfsAfter;
41
- fs.writeFileSync(vfsFilename, vfsFileContent);
42
-
43
- console.log('');
44
- console.log('Built ' + files.length + ' files to ' + vfsFilename + '.');
1
+ const fs = require('fs');
2
+
3
+ const vfsBefore = "var vfs = ";
4
+ const vfsAfter = "; var _global = typeof window === 'object' ? window : typeof global === 'object' ? global : typeof self === 'object' ? self : this; if (typeof _global.pdfMake !== 'undefined' && typeof _global.pdfMake.addVirtualFileSystem !== 'undefined') { _global.pdfMake.addVirtualFileSystem(vfs); } if (typeof module !== 'undefined') { module.exports = vfs; }";
5
+ const sourcePath = process.argv[2];
6
+ const vfsFilename = process.argv[3] ? process.argv[3] : './build/vfs_fonts.js';
7
+
8
+ var vfs = {};
9
+
10
+ if (sourcePath === undefined) {
11
+ console.error('Usage: node build-vfs.js path [filename]');
12
+ console.log('');
13
+ console.log('Parameters:');
14
+ console.log(' path Source path with fonts.');
15
+ console.log(' filename Optional. Output vfs file. Default: ./build/vfs_fonts.js');
16
+ console.log('');
17
+ console.log('Examples:');
18
+ console.log(' node build-vfs.js "examples/fonts"');
19
+ console.log(' node build-vfs.js "examples/fonts" "./build/vfs_fonts.js"');
20
+ return;
21
+ }
22
+
23
+ if (!fs.existsSync(sourcePath)) {
24
+ console.error('Source path "' + sourcePath + '" not found.');
25
+ return;
26
+ }
27
+
28
+ console.log('Source path:', sourcePath);
29
+ console.log('');
30
+
31
+ var files = fs.readdirSync(sourcePath);
32
+
33
+ files.forEach(function (file) {
34
+ var fileBase64 = fs.readFileSync(sourcePath + '/' + file).toString('base64');
35
+ console.log('FILE:', file);
36
+
37
+ vfs[file] = fileBase64;
38
+ });
39
+
40
+ const vfsFileContent = vfsBefore + JSON.stringify(vfs, null, 2) + vfsAfter;
41
+ fs.writeFileSync(vfsFilename, vfsFileContent);
42
+
43
+ console.log('');
44
+ console.log('Built ' + files.length + ' files to ' + vfsFilename + '.');
package/fonts/Roboto.js CHANGED
@@ -1,8 +1,8 @@
1
- module.exports = {
2
- Roboto: {
3
- normal: __dirname + '/Roboto/Roboto-Regular.ttf',
4
- bold: __dirname + '/Roboto/Roboto-Medium.ttf',
5
- italics: __dirname + '/Roboto/Roboto-Italic.ttf',
6
- bolditalics: __dirname + '/Roboto/Roboto-MediumItalic.ttf'
7
- }
8
- };
1
+ module.exports = {
2
+ Roboto: {
3
+ normal: __dirname + '/Roboto/Roboto-Regular.ttf',
4
+ bold: __dirname + '/Roboto/Roboto-Medium.ttf',
5
+ italics: __dirname + '/Roboto/Roboto-Italic.ttf',
6
+ bolditalics: __dirname + '/Roboto/Roboto-MediumItalic.ttf'
7
+ }
8
+ };
@@ -1542,7 +1542,7 @@ var SVGtoPDF = function (doc, svg, x, y, options) {
1542
1542
  }
1543
1543
  }
1544
1544
  for (let i = 0; i < selector.classes.length; i++) {
1545
- if (!elem.classList.contains(selector.classes[i])) {
1545
+ if (elem.classList.indexOf(selector.classes[i]) === -1) {
1546
1546
  return false;
1547
1547
  }
1548
1548
  }
package/js/DocMeasure.js CHANGED
@@ -21,12 +21,12 @@ class DocMeasure {
21
21
  this.autoImageIndex = 1;
22
22
  }
23
23
 
24
- /**
25
- * Measures all nodes and sets min/max-width properties required for the second
26
- * layout-pass.
27
- *
28
- * @param {object} docStructure document-definition-object
29
- * @returns {object} document-measurement-object
24
+ /**
25
+ * Measures all nodes and sets min/max-width properties required for the second
26
+ * layout-pass.
27
+ *
28
+ * @param {object} docStructure document-definition-object
29
+ * @returns {object} document-measurement-object
30
30
  */
31
31
  measureDocument(docStructure) {
32
32
  return this.measureNode(docStructure);
@@ -179,6 +179,11 @@ class DocMeasure {
179
179
  let textStyle = node.toc.textStyle || {};
180
180
  let numberStyle = node.toc.numberStyle || textStyle;
181
181
  let textMargin = node.toc.textMargin || [0, 0, 0, 0];
182
+ if (node.toc.sortBy === 'title') {
183
+ node.toc._items.sort((a, b) => {
184
+ return a._textNodeRef.text.localeCompare(b._textNodeRef.text, node.toc.sortLocale);
185
+ });
186
+ }
182
187
  for (let i = 0, l = node.toc._items.length; i < l; i++) {
183
188
  let item = node.toc._items[i];
184
189
  let lineStyle = item._textNodeRef.tocStyle || textStyle;
@@ -4,9 +4,9 @@ exports.__esModule = true;
4
4
  exports.default = void 0;
5
5
  var _variableType = require("./helpers/variableType");
6
6
  var _events = require("events");
7
- /**
8
- * A store for current x, y positions and available width/height.
9
- * It facilitates column divisions and vertical sync
7
+ /**
8
+ * A store for current x, y positions and available width/height.
9
+ * It facilitates column divisions and vertical sync
10
10
  */
11
11
  class DocumentContext extends _events.EventEmitter {
12
12
  constructor() {
@@ -110,6 +110,11 @@ class DocumentContext extends _events.EventEmitter {
110
110
  if (height) {
111
111
  this.availableHeight -= y - saved.bottomMost.y;
112
112
  }
113
+ if (height && saved.bottomMost.y - saved.y < height) {
114
+ this.height = height;
115
+ } else {
116
+ this.height = saved.bottomMost.y - saved.y;
117
+ }
113
118
  this.lastColumnWidth = saved.lastColumnWidth;
114
119
  return saved.bottomByPage;
115
120
  }
@@ -7,13 +7,13 @@ var _tools = require("./helpers/tools");
7
7
  var _DocumentContext = _interopRequireDefault(require("./DocumentContext"));
8
8
  var _events = require("events");
9
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
- /**
11
- * A line/vector writer, which adds elements to current page and sets
12
- * their positions based on the context
10
+ /**
11
+ * A line/vector writer, which adds elements to current page and sets
12
+ * their positions based on the context
13
13
  */
14
14
  class ElementWriter extends _events.EventEmitter {
15
- /**
16
- * @param {DocumentContext} context
15
+ /**
16
+ * @param {DocumentContext} context
17
17
  */
18
18
  constructor(context) {
19
19
  super();
@@ -21,8 +21,8 @@ class ElementWriter extends _events.EventEmitter {
21
21
  this.contextStack = [];
22
22
  }
23
23
 
24
- /**
25
- * @returns {DocumentContext}
24
+ /**
25
+ * @returns {DocumentContext}
26
26
  */
27
27
  context() {
28
28
  return this._context;
@@ -249,6 +249,28 @@ class ElementWriter extends _events.EventEmitter {
249
249
  });
250
250
  return true;
251
251
  }
252
+ beginVerticalAlignment(verticalAlignment) {
253
+ let page = this.context().getCurrentPage();
254
+ let item = {
255
+ type: 'beginVerticalAlignment',
256
+ item: {
257
+ verticalAlignment: verticalAlignment
258
+ }
259
+ };
260
+ page.items.push(item);
261
+ return item;
262
+ }
263
+ endVerticalAlignment(verticalAlignment) {
264
+ let page = this.context().getCurrentPage();
265
+ let item = {
266
+ type: 'endVerticalAlignment',
267
+ item: {
268
+ verticalAlignment: verticalAlignment
269
+ }
270
+ };
271
+ page.items.push(item);
272
+ return item;
273
+ }
252
274
  addFragment(block, useBlockXOffset, useBlockYOffset, dontUpdateContextPosition) {
253
275
  let ctx = this.context();
254
276
  let page = ctx.getCurrentPage();
@@ -290,6 +312,10 @@ class ElementWriter extends _events.EventEmitter {
290
312
  break;
291
313
  case 'image':
292
314
  case 'svg':
315
+ case 'beginClip':
316
+ case 'endClip':
317
+ case 'beginVerticalAlignment':
318
+ case 'endVerticalAlignment':
293
319
  var img = (0, _tools.pack)(item.item);
294
320
  img.x = (img.x || 0) + (useBlockXOffset ? block.xOffset || 0 : ctx.x);
295
321
  img.y = (img.y || 0) + (useBlockYOffset ? block.yOffset || 0 : ctx.y);
@@ -306,15 +332,15 @@ class ElementWriter extends _events.EventEmitter {
306
332
  return true;
307
333
  }
308
334
 
309
- /**
310
- * Pushes the provided context onto the stack or creates a new one
311
- *
312
- * pushContext(context) - pushes the provided context and makes it current
313
- * pushContext(width, height) - creates and pushes a new context with the specified width and height
314
- * pushContext() - creates a new context for unbreakable blocks (with current availableWidth and full-page-height)
315
- *
316
- * @param {DocumentContext|number} contextOrWidth
317
- * @param {number} height
335
+ /**
336
+ * Pushes the provided context onto the stack or creates a new one
337
+ *
338
+ * pushContext(context) - pushes the provided context and makes it current
339
+ * pushContext(width, height) - creates and pushes a new context with the specified width and height
340
+ * pushContext() - creates a new context for unbreakable blocks (with current availableWidth and full-page-height)
341
+ *
342
+ * @param {DocumentContext|number} contextOrWidth
343
+ * @param {number} height
318
344
  */
319
345
  pushContext(contextOrWidth, height) {
320
346
  if (contextOrWidth === undefined) {
@@ -21,15 +21,15 @@ function addAll(target, otherArray) {
21
21
  });
22
22
  }
23
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
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
27
  */
28
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
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
33
  */
34
34
  constructor(pageSize, pageMargins, svgMeasure) {
35
35
  this.pageSize = pageSize;
@@ -37,25 +37,26 @@ class LayoutBuilder {
37
37
  this.svgMeasure = svgMeasure;
38
38
  this.tableLayouts = {};
39
39
  this.nestedLevel = 0;
40
+ this.verticalAlignmentItemStack = [];
40
41
  }
41
42
  registerTableLayouts(tableLayouts) {
42
43
  this.tableLayouts = (0, _tools.pack)(this.tableLayouts, tableLayouts);
43
44
  }
44
45
 
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
46
+ /**
47
+ * Executes layout engine on document-definition-object and creates an array of pages
48
+ * containing positioned Blocks, Lines and inlines
49
+ *
50
+ * @param {object} docStructure document-definition-object
51
+ * @param {object} pdfDocument pdfkit document
52
+ * @param {object} styleDictionary dictionary with style definitions
53
+ * @param {object} defaultStyle default style definition
54
+ * @param {object} background
55
+ * @param {object} header
56
+ * @param {object} footer
57
+ * @param {object} watermark
58
+ * @param {object} pageBreakBeforeFct
59
+ * @returns {Array} an array of pages
59
60
  */
60
61
  layoutDocument(docStructure, pdfDocument, styleDictionary, defaultStyle, background, header, footer, watermark, pageBreakBeforeFct) {
61
62
  function addPageBreaksIfNecessary(linearNodeList, pages) {
@@ -296,10 +297,10 @@ class LayoutBuilder {
296
297
  });
297
298
  let rotatedSize;
298
299
 
299
- /**
300
- * Binary search the best font size.
301
- * Initial bounds [0, 1000]
302
- * Break when range < 1
300
+ /**
301
+ * Binary search the best font size.
302
+ * Initial bounds [0, 1000]
303
+ * Break when range < 1
303
304
  */
304
305
  let a = 0;
305
306
  let b = 1000;
@@ -323,13 +324,13 @@ class LayoutBuilder {
323
324
  }
324
325
  styleContextStack.pop();
325
326
  }
326
- /*
327
- End binary search
327
+ /*
328
+ End binary search
328
329
  */
329
330
  return c;
330
331
  }
331
332
  }
332
- processNode(node) {
333
+ processNode(node, isVerticalAlignmentAllowed = false) {
333
334
  const applyMargins = callback => {
334
335
  let margin = node._margin;
335
336
  if (node.pageBreak === 'before') {
@@ -356,11 +357,11 @@ class LayoutBuilder {
356
357
  // Consume the whole available space
357
358
  this.writer.context().moveDown(availableHeight);
358
359
  this.writer.moveToNextPage(node.pageOrientation);
359
- /**
360
- * TODO - Something to consider:
361
- * Right now the node starts at the top of next page (after header)
362
- * Another option would be to apply just the top margin that has not been consumed in the page before
363
- * It would something like: this.write.context().moveDown(margin[1] - availableHeight)
360
+ /**
361
+ * TODO - Something to consider:
362
+ * Right now the node starts at the top of next page (after header)
363
+ * Another option would be to apply just the top margin that has not been consumed in the page before
364
+ * It would something like: this.write.context().moveDown(margin[1] - availableHeight)
364
365
  */
365
366
  } else {
366
367
  this.writer.context().moveDown(margin[1]);
@@ -378,11 +379,11 @@ class LayoutBuilder {
378
379
  if (availableHeight - margin[3] < 0) {
379
380
  this.writer.context().moveDown(availableHeight);
380
381
  this.writer.moveToNextPage(node.pageOrientation);
381
- /**
382
- * TODO - Something to consider:
383
- * Right now next node starts at the top of next page (after header)
384
- * Another option would be to apply the bottom margin that has not been consumed in the next page?
385
- * It would something like: this.write.context().moveDown(margin[3] - availableHeight)
382
+ /**
383
+ * TODO - Something to consider:
384
+ * Right now next node starts at the top of next page (after header)
385
+ * Another option would be to apply the bottom margin that has not been consumed in the next page?
386
+ * It would something like: this.write.context().moveDown(margin[3] - availableHeight)
386
387
  */
387
388
  } else {
388
389
  this.writer.context().moveDown(margin[3]);
@@ -406,7 +407,14 @@ class LayoutBuilder {
406
407
  };
407
408
  this.linearNodeList.push(node);
408
409
  decorateNode(node);
410
+ if (this.writer.context().getCurrentPage() !== null) {
411
+ var prevTop = this.writer.context().getCurrentPosition().top;
412
+ }
409
413
  applyMargins(() => {
414
+ let verticalAlignment = node.verticalAlignment;
415
+ if (isVerticalAlignmentAllowed && verticalAlignment) {
416
+ var verticalAlignmentBegin = this.writer.beginVerticalAlignment(verticalAlignment);
417
+ }
410
418
  let unbreakable = node.unbreakable;
411
419
  if (unbreakable) {
412
420
  this.writer.beginUnbreakableBlock();
@@ -456,7 +464,17 @@ class LayoutBuilder {
456
464
  if (unbreakable) {
457
465
  this.writer.commitUnbreakableBlock();
458
466
  }
467
+ if (isVerticalAlignmentAllowed && verticalAlignment) {
468
+ this.verticalAlignmentItemStack.push({
469
+ begin: verticalAlignmentBegin,
470
+ end: this.writer.endVerticalAlignment(verticalAlignment)
471
+ });
472
+ }
459
473
  });
474
+ if (prevTop !== undefined) {
475
+ // TODO: for vertical alignment and does not work (at least) when page break in node
476
+ node.__height = this.writer.context().getCurrentPosition().top - prevTop;
477
+ }
460
478
  }
461
479
 
462
480
  // vertical container
@@ -559,13 +577,13 @@ class LayoutBuilder {
559
577
  }
560
578
  }
561
579
 
562
- /**
563
- * Searches for a cell in the same row that starts a rowspan and is positioned immediately before the current cell.
564
- * Alternatively, it finds a cell where the colspan initiating the rowspan extends to the cell just before the current one.
565
- *
566
- * @param {Array<object>} arr - An array representing cells in a row.
567
- * @param {number} i - The index of the current cell to search backward from.
568
- * @returns {object|null} The starting cell of the rowspan if found; otherwise, `null`.
580
+ /**
581
+ * Searches for a cell in the same row that starts a rowspan and is positioned immediately before the current cell.
582
+ * Alternatively, it finds a cell where the colspan initiating the rowspan extends to the cell just before the current one.
583
+ *
584
+ * @param {Array<object>} arr - An array representing cells in a row.
585
+ * @param {number} i - The index of the current cell to search backward from.
586
+ * @returns {object|null} The starting cell of the rowspan if found; otherwise, `null`.
569
587
  */
570
588
  _findStartingRowSpanCell(arr, i) {
571
589
  let requiredColspan = 1;
@@ -582,12 +600,12 @@ class LayoutBuilder {
582
600
  return null;
583
601
  }
584
602
 
585
- /**
586
- * Retrieves a page break description for a specified page from a list of page breaks.
587
- *
588
- * @param {Array<object>} pageBreaks - An array of page break descriptions, each containing `prevPage` properties.
589
- * @param {number} page - The page number to find the associated page break for.
590
- * @returns {object|undefined} The page break description object for the specified page if found; otherwise, `undefined`.
603
+ /**
604
+ * Retrieves a page break description for a specified page from a list of page breaks.
605
+ *
606
+ * @param {Array<object>} pageBreaks - An array of page break descriptions, each containing `prevPage` properties.
607
+ * @param {number} page - The page number to find the associated page break for.
608
+ * @returns {object|undefined} The page break description object for the specified page if found; otherwise, `undefined`.
591
609
  */
592
610
  _getPageBreak(pageBreaks, page) {
593
611
  return pageBreaks.find(desc => desc.prevPage === page);
@@ -633,14 +651,14 @@ class LayoutBuilder {
633
651
  });
634
652
  }
635
653
 
636
- /**
637
- * Resolves the Y-coordinates for a target object by comparing two break points.
638
- *
639
- * @param {object} break1 - The first break point with `prevY` and `y` properties.
640
- * @param {object} break2 - The second break point with `prevY` and `y` properties.
641
- * @param {object} target - The target object to be updated with resolved Y-coordinates.
642
- * @property {number} target.prevY - Updated to the maximum `prevY` value between `break1` and `break2`.
643
- * @property {number} target.y - Updated to the minimum `y` value between `break1` and `break2`.
654
+ /**
655
+ * Resolves the Y-coordinates for a target object by comparing two break points.
656
+ *
657
+ * @param {object} break1 - The first break point with `prevY` and `y` properties.
658
+ * @param {object} break2 - The second break point with `prevY` and `y` properties.
659
+ * @param {object} target - The target object to be updated with resolved Y-coordinates.
660
+ * @property {number} target.prevY - Updated to the maximum `prevY` value between `break1` and `break2`.
661
+ * @property {number} target.y - Updated to the minimum `y` value between `break1` and `break2`.
644
662
  */
645
663
  _resolveBreakY(break1, break2, target) {
646
664
  target.prevY = Math.max(break1.prevY, break2.prevY);
@@ -681,12 +699,12 @@ class LayoutBuilder {
681
699
  }
682
700
  }
683
701
  }
684
- /**
685
- * Calculates the left offset for a column based on the specified gap values.
686
- *
687
- * @param {number} i - The index of the column for which the offset is being calculated.
688
- * @param {Array<number>} gaps - An array of gap values for each column.
689
- * @returns {number} The left offset for the column. Returns `gaps[i]` if it exists, otherwise `0`.
702
+ /**
703
+ * Calculates the left offset for a column based on the specified gap values.
704
+ *
705
+ * @param {number} i - The index of the column for which the offset is being calculated.
706
+ * @param {Array<number>} gaps - An array of gap values for each column.
707
+ * @returns {number} The left offset for the column. Returns `gaps[i]` if it exists, otherwise `0`.
690
708
  */
691
709
  _colLeftOffset(i, gaps) {
692
710
  if (gaps && gaps.length > i) {
@@ -695,15 +713,15 @@ class LayoutBuilder {
695
713
  return 0;
696
714
  }
697
715
 
698
- /**
699
- * Retrieves the ending cell for a row span in case it exists in a specified table column.
700
- *
701
- * @param {Array<Array<object>>} tableBody - The table body, represented as a 2D array of cell objects.
702
- * @param {number} rowIndex - The index of the starting row for the row span.
703
- * @param {object} column - The column object containing row span information.
704
- * @param {number} columnIndex - The index of the column within the row.
705
- * @returns {object|null} The cell at the end of the row span if it exists; otherwise, `null`.
706
- * @throws {Error} If the row span extends beyond the total row count.
716
+ /**
717
+ * Retrieves the ending cell for a row span in case it exists in a specified table column.
718
+ *
719
+ * @param {Array<Array<object>>} tableBody - The table body, represented as a 2D array of cell objects.
720
+ * @param {number} rowIndex - The index of the starting row for the row span.
721
+ * @param {object} column - The column object containing row span information.
722
+ * @param {number} columnIndex - The index of the column within the row.
723
+ * @returns {object|null} The cell at the end of the row span if it exists; otherwise, `null`.
724
+ * @throws {Error} If the row span extends beyond the total row count.
707
725
  */
708
726
  _getRowSpanEndingCell(tableBody, rowIndex, column, columnIndex) {
709
727
  if (column.rowSpan && column.rowSpan > 1) {
@@ -732,6 +750,7 @@ class LayoutBuilder {
732
750
  let pageBreaksByRowSpan = [];
733
751
  let positions = [];
734
752
  let willBreakByHeight = false;
753
+ let verticalAlignmentCells = {};
735
754
  widths = widths || cells;
736
755
 
737
756
  // Check if row should break by height
@@ -745,6 +764,7 @@ class LayoutBuilder {
745
764
  this.writer.context().beginColumnGroup(marginXParent, _bottomByPage);
746
765
  for (let i = 0, l = cells.length; i < l; i++) {
747
766
  let cell = cells[i];
767
+ let cellIndexBegin = i;
748
768
 
749
769
  // Page change handler
750
770
  const storePageBreakClosure = data => {
@@ -767,12 +787,18 @@ class LayoutBuilder {
767
787
  }
768
788
 
769
789
  // if rowspan starts in this cell, we retrieve the last cell affected by the rowspan
770
- const rowSpanEndingCell = this._getRowSpanEndingCell(tableBody, rowIndex, cell, i);
771
- if (rowSpanEndingCell) {
790
+ const rowSpanRightEndingCell = this._getRowSpanEndingCell(tableBody, rowIndex, cell, i);
791
+ const rowSpanLeftEndingCell = this._getRowSpanEndingCell(tableBody, rowIndex, cell, cellIndexBegin);
792
+ if (rowSpanRightEndingCell) {
772
793
  // We store a reference of the ending cell in the first cell of the rowspan
773
- cell._endingCell = rowSpanEndingCell;
794
+ cell._endingCell = rowSpanRightEndingCell;
774
795
  cell._endingCell._startingRowSpanY = cell._startingRowSpanY;
775
796
  }
797
+ if (rowSpanLeftEndingCell) {
798
+ // We store a reference of the left ending cell in the first cell of the rowspan
799
+ cell._leftEndingCell = rowSpanLeftEndingCell;
800
+ cell._leftEndingCell._startingRowSpanY = cell._startingRowSpanY;
801
+ }
776
802
 
777
803
  // If we are after a cell that started a rowspan
778
804
  let endOfRowSpanCell = null;
@@ -789,8 +815,11 @@ class LayoutBuilder {
789
815
  // We pass the endingSpanCell reference to store the context just after processing rowspan cell
790
816
  this.writer.context().beginColumn(width, leftOffset, endOfRowSpanCell);
791
817
  if (!cell._span) {
792
- this.processNode(cell);
818
+ this.processNode(cell, true);
793
819
  this.writer.context().updateBottomByPage();
820
+ if (cell.verticalAlignment) {
821
+ verticalAlignmentCells[cellIndexBegin] = this.verticalAlignmentItemStack.length - 1;
822
+ }
794
823
  addAll(positions, cell.positions);
795
824
  } else if (cell._columnEndingContext) {
796
825
  let discountY = 0;
@@ -846,6 +875,40 @@ class LayoutBuilder {
846
875
  // If there are page breaks in this row, update data with prevY of last cell
847
876
  this._updatePageBreaksData(pageBreaks, tableNode, rowIndex);
848
877
  }
878
+ let rowHeight = this.writer.context().height;
879
+ for (let i = 0, l = cells.length; i < l; i++) {
880
+ let cell = cells[i];
881
+ if (!cell._span && cell.verticalAlignment) {
882
+ let itemBegin = this.verticalAlignmentItemStack[verticalAlignmentCells[i]].begin.item;
883
+ itemBegin.viewHeight = rowHeight;
884
+ itemBegin.nodeHeight = cell.__height;
885
+ itemBegin.cell = cell;
886
+ itemBegin.bottomY = this.writer.context().y;
887
+ itemBegin.isCellContentMultiPage = !itemBegin.cell.positions.every(item => item.pageNumber === itemBegin.cell.positions[0].pageNumber);
888
+ itemBegin.getViewHeight = function () {
889
+ if (this.cell._willBreak) {
890
+ return this.cell._bottomY - this.cell._rowTopPageY;
891
+ }
892
+ if (this.cell.rowSpan && this.cell.rowSpan > 1) {
893
+ if (dontBreakRows) {
894
+ let rowTopPageY = this.cell._leftEndingCell._startingRowSpanY + this.cell._leftEndingCell._rowTopPageYPadding;
895
+ return this.cell._leftEndingCell._rowTopPageY - rowTopPageY + this.cell._leftEndingCell._bottomY;
896
+ } else {
897
+ if (this.cell.positions[0].pageNumber !== this.cell._leftEndingCell._lastPageNumber) {
898
+ return this.bottomY - this.cell._leftEndingCell._bottomY;
899
+ }
900
+ return this.viewHeight + this.cell._leftEndingCell._bottomY - this.bottomY;
901
+ }
902
+ }
903
+ return this.viewHeight;
904
+ };
905
+ itemBegin.getNodeHeight = function () {
906
+ return this.nodeHeight;
907
+ };
908
+ let itemEnd = this.verticalAlignmentItemStack[verticalAlignmentCells[i]].end.item;
909
+ itemEnd.isCellContentMultiPage = itemBegin.isCellContentMultiPage;
910
+ }
911
+ }
849
912
  return {
850
913
  pageBreaksBySpan: pageBreaksByRowSpan,
851
914
  pageBreaks: pageBreaks,
@@ -987,6 +1050,9 @@ class LayoutBuilder {
987
1050
  }
988
1051
  }
989
1052
  processToc(node) {
1053
+ if (!node.toc._table && node.toc.hideEmpty === true) {
1054
+ return;
1055
+ }
990
1056
  if (node.toc.title) {
991
1057
  this.processNode(node.toc.title);
992
1058
  }