@flowaccount/pdfmake 1.0.5-staging.0 → 1.0.6-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +297 -297
- package/build/pdfmake.js +49709 -49954
- package/build/pdfmake.min.js +2 -2
- package/build/pdfmake.min.js.map +1 -1
- package/build/vfs_fonts.js +6 -6
- package/package.json +110 -110
- package/src/3rd-party/svg-to-pdfkit.js +3 -3
- package/src/browser-extensions/URLBrowserResolver.js +96 -96
- package/src/browser-extensions/pdfMake.js +361 -361
- package/src/browser-extensions/tokenizer-shim.js +15 -15
- package/src/browser-extensions/virtual-fs.js +55 -55
- package/src/columnCalculator.js +157 -157
- package/src/docMeasure.js +831 -831
- package/src/docPreprocessor.js +277 -277
- package/src/documentContext.js +383 -383
- package/src/elementWriter.js +442 -434
- package/src/fontProvider.js +68 -68
- package/src/helpers.js +138 -138
- package/src/imageMeasure.js +70 -70
- package/src/layoutBuilder.js +1998 -1770
- package/src/line.js +91 -91
- package/src/pageElementWriter.js +362 -362
- package/src/pdfKitEngine.js +21 -21
- package/src/printer.js +1191 -1191
- package/src/qrEnc.js +790 -790
- package/src/standardPageSizes.js +54 -54
- package/src/styleContextStack.js +138 -138
- package/src/svgMeasure.js +70 -70
- package/src/tableProcessor.js +791 -789
- package/src/textDecorator.js +157 -157
- package/src/textTools.js +442 -442
- package/src/traversalTracker.js +47 -47
package/src/layoutBuilder.js
CHANGED
|
@@ -1,1770 +1,1998 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var cloneDeep = require('lodash/cloneDeep');
|
|
4
|
-
var TraversalTracker = require('./traversalTracker');
|
|
5
|
-
var DocPreprocessor = require('./docPreprocessor');
|
|
6
|
-
var DocMeasure = require('./docMeasure');
|
|
7
|
-
var DocumentContext = require('./documentContext');
|
|
8
|
-
var PageElementWriter = require('./pageElementWriter');
|
|
9
|
-
var ColumnCalculator = require('./columnCalculator');
|
|
10
|
-
var TableProcessor = require('./tableProcessor');
|
|
11
|
-
var Line = require('./line');
|
|
12
|
-
var isString = require('./helpers').isString;
|
|
13
|
-
var isArray = require('./helpers').isArray;
|
|
14
|
-
var isUndefined = require('./helpers').isUndefined;
|
|
15
|
-
var isNull = require('./helpers').isNull;
|
|
16
|
-
var pack = require('./helpers').pack;
|
|
17
|
-
var offsetVector = require('./helpers').offsetVector;
|
|
18
|
-
var fontStringify = require('./helpers').fontStringify;
|
|
19
|
-
var getNodeId = require('./helpers').getNodeId;
|
|
20
|
-
var isFunction = require('./helpers').isFunction;
|
|
21
|
-
var TextTools = require('./textTools');
|
|
22
|
-
var StyleContextStack = require('./styleContextStack');
|
|
23
|
-
var isNumber = require('./helpers').isNumber;
|
|
24
|
-
|
|
25
|
-
var footerBreak = false;
|
|
26
|
-
var testTracker;
|
|
27
|
-
var testWriter;
|
|
28
|
-
var testVerticalAlignStack = [];
|
|
29
|
-
var testResult = false;
|
|
30
|
-
var currentLayoutBuilder;
|
|
31
|
-
|
|
32
|
-
function addAll(target, otherArray) {
|
|
33
|
-
if (!isArray(target) || !isArray(otherArray) || otherArray.length === 0) {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
otherArray.forEach(function (item) {
|
|
38
|
-
target.push(item);
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Creates an instance of LayoutBuilder - layout engine which turns document-definition-object
|
|
44
|
-
* into a set of pages, lines, inlines and vectors ready to be rendered into a PDF
|
|
45
|
-
*
|
|
46
|
-
* @param {Object} pageSize - an object defining page width and height
|
|
47
|
-
* @param {Object} pageMargins - an object defining top, left, right and bottom margins
|
|
48
|
-
*/
|
|
49
|
-
function LayoutBuilder(pageSize, pageMargins, imageMeasure, svgMeasure) {
|
|
50
|
-
this.pageSize = pageSize;
|
|
51
|
-
this.pageMargins = pageMargins;
|
|
52
|
-
this.tracker = new TraversalTracker();
|
|
53
|
-
this.imageMeasure = imageMeasure;
|
|
54
|
-
this.svgMeasure = svgMeasure;
|
|
55
|
-
this.tableLayouts = {};
|
|
56
|
-
this.nestedLevel = 0;
|
|
57
|
-
this.verticalAlignItemStack = [];
|
|
58
|
-
this.heightHeaderAndFooter = {};
|
|
59
|
-
|
|
60
|
-
this._footerColumnGuides = null;
|
|
61
|
-
this._footerGapOption = null;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
LayoutBuilder.prototype.registerTableLayouts = function (tableLayouts) {
|
|
65
|
-
this.tableLayouts = pack(this.tableLayouts, tableLayouts);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Executes layout engine on document-definition-object and creates an array of pages
|
|
70
|
-
* containing positioned Blocks, Lines and inlines
|
|
71
|
-
*
|
|
72
|
-
* @param {Object} docStructure document-definition-object
|
|
73
|
-
* @param {Object} fontProvider font provider
|
|
74
|
-
* @param {Object} styleDictionary dictionary with style definitions
|
|
75
|
-
* @param {Object} defaultStyle default style definition
|
|
76
|
-
* @return {Array} an array of pages
|
|
77
|
-
*/
|
|
78
|
-
LayoutBuilder.prototype.layoutDocument = function (docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark, pageBreakBeforeFct) {
|
|
79
|
-
|
|
80
|
-
function addPageBreaksIfNecessary(linearNodeList, pages) {
|
|
81
|
-
|
|
82
|
-
if (!isFunction(pageBreakBeforeFct)) {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
linearNodeList = linearNodeList.filter(function (node) {
|
|
87
|
-
return node.positions.length > 0;
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
linearNodeList.forEach(function (node) {
|
|
91
|
-
var nodeInfo = {};
|
|
92
|
-
[
|
|
93
|
-
'id', 'text', 'ul', 'ol', 'table', 'image', 'qr', 'canvas', 'svg', 'columns', 'layers',
|
|
94
|
-
'headlineLevel', 'style', 'pageBreak', 'pageOrientation',
|
|
95
|
-
'width', 'height'
|
|
96
|
-
].forEach(function (key) {
|
|
97
|
-
if (node[key] !== undefined) {
|
|
98
|
-
nodeInfo[key] = node[key];
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
nodeInfo.startPosition = node.positions[0];
|
|
102
|
-
nodeInfo.pageNumbers = Array.from(new Set(node.positions.map(function (node) { return node.pageNumber; })));
|
|
103
|
-
nodeInfo.pages = pages.length;
|
|
104
|
-
nodeInfo.stack = isArray(node.stack);
|
|
105
|
-
nodeInfo.layers = isArray(node.layers);
|
|
106
|
-
|
|
107
|
-
node.nodeInfo = nodeInfo;
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
for (var index = 0; index < linearNodeList.length; index++) {
|
|
111
|
-
var node = linearNodeList[index];
|
|
112
|
-
if (node.pageBreak !== 'before' && !node.pageBreakCalculated) {
|
|
113
|
-
node.pageBreakCalculated = true;
|
|
114
|
-
var pageNumber = node.nodeInfo.pageNumbers[0];
|
|
115
|
-
var followingNodesOnPage = [];
|
|
116
|
-
var nodesOnNextPage = [];
|
|
117
|
-
var previousNodesOnPage = [];
|
|
118
|
-
if (pageBreakBeforeFct.length > 1) {
|
|
119
|
-
for (var ii = index + 1, l = linearNodeList.length; ii < l; ii++) {
|
|
120
|
-
if (linearNodeList[ii].nodeInfo.pageNumbers.indexOf(pageNumber) > -1) {
|
|
121
|
-
followingNodesOnPage.push(linearNodeList[ii].nodeInfo);
|
|
122
|
-
}
|
|
123
|
-
if (pageBreakBeforeFct.length > 2 && linearNodeList[ii].nodeInfo.pageNumbers.indexOf(pageNumber + 1) > -1) {
|
|
124
|
-
nodesOnNextPage.push(linearNodeList[ii].nodeInfo);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
if (pageBreakBeforeFct.length > 3) {
|
|
129
|
-
for (var jj = 0; jj < index; jj++) {
|
|
130
|
-
if (linearNodeList[jj].nodeInfo.pageNumbers.indexOf(pageNumber) > -1) {
|
|
131
|
-
previousNodesOnPage.push(linearNodeList[jj].nodeInfo);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
if (pageBreakBeforeFct(node.nodeInfo, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage)) {
|
|
136
|
-
node.pageBreak = 'before';
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
this.docPreprocessor = new DocPreprocessor();
|
|
146
|
-
this.docMeasure = new DocMeasure(fontProvider, styleDictionary, defaultStyle, this.imageMeasure, this.svgMeasure, this.tableLayouts, images);
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
function resetXYs(result) {
|
|
150
|
-
result.linearNodeList.forEach(function (node) {
|
|
151
|
-
node.resetXY();
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
var result = this.tryLayoutDocument(docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark);
|
|
156
|
-
while (addPageBreaksIfNecessary(result.linearNodeList, result.pages)) {
|
|
157
|
-
resetXYs(result);
|
|
158
|
-
result = this.tryLayoutDocument(docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return result.pages;
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
LayoutBuilder.prototype.tryLayoutDocument = function (docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark) {
|
|
165
|
-
footerBreak = false;
|
|
166
|
-
|
|
167
|
-
this.verticalAlignItemStack = this.verticalAlignItemStack || [];
|
|
168
|
-
this.linearNodeList = [];
|
|
169
|
-
this.writer = new PageElementWriter(
|
|
170
|
-
new DocumentContext(this.pageSize, this.pageMargins, this._footerGapOption), this.tracker);
|
|
171
|
-
|
|
172
|
-
this.heightHeaderAndFooter = this.addHeadersAndFooters(header, footer) || {};
|
|
173
|
-
if (!isUndefined(this.heightHeaderAndFooter.header)) {
|
|
174
|
-
this.pageMargins.top = this.heightHeaderAndFooter.header + 1;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (isArray(docStructure) && docStructure[2] && isArray(docStructure[2]) && docStructure[2][0] && docStructure[2][0].remark) {
|
|
178
|
-
var tableRemark = docStructure[2][0].remark;
|
|
179
|
-
var remarkLabel = docStructure[2][0];
|
|
180
|
-
var remarkDetail = docStructure[2][1] && docStructure[2][1].text;
|
|
181
|
-
|
|
182
|
-
docStructure[2].splice(0, 1);
|
|
183
|
-
if (docStructure[2].length > 0) {
|
|
184
|
-
docStructure[2].splice(0, 1);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
var labelRow = [];
|
|
188
|
-
var detailRow = [];
|
|
189
|
-
|
|
190
|
-
labelRow.push(remarkLabel);
|
|
191
|
-
detailRow.push({ remarktest: true, text: remarkDetail });
|
|
192
|
-
|
|
193
|
-
tableRemark.table.body.push(labelRow);
|
|
194
|
-
tableRemark.table.body.push(detailRow);
|
|
195
|
-
|
|
196
|
-
tableRemark.table.headerRows = 1;
|
|
197
|
-
|
|
198
|
-
docStructure[2].push(tableRemark);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
this.linearNodeList = [];
|
|
202
|
-
docStructure = this.docPreprocessor.preprocessDocument(docStructure);
|
|
203
|
-
docStructure = this.docMeasure.measureDocument(docStructure);
|
|
204
|
-
|
|
205
|
-
this.verticalAlignItemStack = [];
|
|
206
|
-
this.writer = new PageElementWriter(
|
|
207
|
-
new DocumentContext(this.pageSize, this.pageMargins, this._footerGapOption), this.tracker);
|
|
208
|
-
|
|
209
|
-
var _this = this;
|
|
210
|
-
this.writer.context().tracker.startTracking('pageAdded', function () {
|
|
211
|
-
_this.addBackground(background);
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
this.addBackground(background);
|
|
215
|
-
this.processNode(docStructure);
|
|
216
|
-
this.addHeadersAndFooters(header, footer,
|
|
217
|
-
(this.heightHeaderAndFooter.header || 0) + 1,
|
|
218
|
-
(this.heightHeaderAndFooter.footer || 0) + 1);
|
|
219
|
-
if (watermark != null) {
|
|
220
|
-
this.addWatermark(watermark, fontProvider, defaultStyle);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
return { pages: this.writer.context().pages, linearNodeList: this.linearNodeList };
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
LayoutBuilder.prototype.applyFooterGapOption = function(opt) {
|
|
227
|
-
if (opt === true) {
|
|
228
|
-
opt = { enabled: true };
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (!opt) return;
|
|
232
|
-
|
|
233
|
-
if (typeof opt !== 'object') {
|
|
234
|
-
this._footerGapOption = { enabled: true, forcePageBreakForAllRows: false };
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
this._footerGapOption = {
|
|
239
|
-
enabled: opt.enabled !== false,
|
|
240
|
-
minRowHeight: typeof opt.minRowHeight === 'number' ? opt.minRowHeight : undefined, // Optional fallback - system auto-calculates from cell content if not provided
|
|
241
|
-
forcePageBreakForAllRows: opt.forcePageBreakForAllRows === true, // Force page break for all rows (not just inline images)
|
|
242
|
-
columns: opt.columns ? {
|
|
243
|
-
widths: Array.isArray(opt.columns.widths) ? opt.columns.widths.slice() : undefined,
|
|
244
|
-
widthLength: opt.columns.widths.length || 0,
|
|
245
|
-
stops: Array.isArray(opt.columns.stops) ? opt.columns.stops.slice() : undefined,
|
|
246
|
-
style: opt.columns.style ? Object.assign({}, opt.columns.style) : {},
|
|
247
|
-
includeOuter: opt.columns.includeOuter !== false
|
|
248
|
-
} : null
|
|
249
|
-
};
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
LayoutBuilder.prototype.addBackground = function (background) {
|
|
253
|
-
var backgroundGetter = isFunction(background) ? background : function () {
|
|
254
|
-
return background;
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
var context = this.writer.context();
|
|
258
|
-
var pageSize = context.getCurrentPage().pageSize;
|
|
259
|
-
|
|
260
|
-
var pageBackground = backgroundGetter(context.page + 1, pageSize);
|
|
261
|
-
|
|
262
|
-
if (pageBackground) {
|
|
263
|
-
this.writer.beginUnbreakableBlock(pageSize.width, pageSize.height);
|
|
264
|
-
pageBackground = this.docPreprocessor.preprocessDocument(pageBackground);
|
|
265
|
-
this.processNode(this.docMeasure.measureDocument(pageBackground));
|
|
266
|
-
this.writer.commitUnbreakableBlock(0, 0);
|
|
267
|
-
context.backgroundLength[context.page] += pageBackground.positions.length;
|
|
268
|
-
}
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
LayoutBuilder.prototype.addStaticRepeatable = function (headerOrFooter, sizeFunction) {
|
|
272
|
-
return this.addDynamicRepeatable(function () {
|
|
273
|
-
return JSON.parse(JSON.stringify(headerOrFooter)); // copy to new object
|
|
274
|
-
}, sizeFunction);
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
LayoutBuilder.prototype.addDynamicRepeatable = function (nodeGetter, sizeFunction) {
|
|
278
|
-
var pages = this.writer.context().pages;
|
|
279
|
-
var measuredHeight;
|
|
280
|
-
|
|
281
|
-
for (var pageIndex = 0, l = pages.length; pageIndex < l; pageIndex++) {
|
|
282
|
-
this.writer.context().page = pageIndex;
|
|
283
|
-
|
|
284
|
-
var node = nodeGetter(pageIndex + 1, l, this.writer.context().pages[pageIndex].pageSize);
|
|
285
|
-
|
|
286
|
-
if (node) {
|
|
287
|
-
var sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.pageMargins);
|
|
288
|
-
this.writer.beginUnbreakableBlock(sizes.width, sizes.height);
|
|
289
|
-
node = this.docPreprocessor.preprocessDocument(node);
|
|
290
|
-
this.processNode(this.docMeasure.measureDocument(node));
|
|
291
|
-
this.writer.commitUnbreakableBlock(sizes.x, sizes.y);
|
|
292
|
-
if (!isUndefined(node._height)) {
|
|
293
|
-
measuredHeight = node._height;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
return measuredHeight;
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
LayoutBuilder.prototype.addHeadersAndFooters = function (header, footer, headerHeight, footerHeight) {
|
|
302
|
-
var measured = { header: undefined, footer: undefined };
|
|
303
|
-
|
|
304
|
-
var headerSizeFct = function (pageSize) {
|
|
305
|
-
var effectiveHeight = headerHeight;
|
|
306
|
-
if (isUndefined(effectiveHeight)) {
|
|
307
|
-
effectiveHeight = pageSize.height;
|
|
308
|
-
}
|
|
309
|
-
return {
|
|
310
|
-
x: 0,
|
|
311
|
-
y: 0,
|
|
312
|
-
width: pageSize.width,
|
|
313
|
-
height: effectiveHeight
|
|
314
|
-
};
|
|
315
|
-
};
|
|
316
|
-
|
|
317
|
-
var footerSizeFct = function (pageSize) {
|
|
318
|
-
var effectiveHeight = footerHeight;
|
|
319
|
-
if (isUndefined(effectiveHeight)) {
|
|
320
|
-
effectiveHeight = pageSize.height;
|
|
321
|
-
}
|
|
322
|
-
return {
|
|
323
|
-
x: 0,
|
|
324
|
-
y: pageSize.height - effectiveHeight,
|
|
325
|
-
width: pageSize.width,
|
|
326
|
-
height: effectiveHeight
|
|
327
|
-
};
|
|
328
|
-
};
|
|
329
|
-
|
|
330
|
-
if (this._footerGapOption && !this.writer.context()._footerGapOption) {
|
|
331
|
-
this.writer.context()._footerGapOption = this._footerGapOption;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (isFunction(header)) {
|
|
335
|
-
measured.header = this.addDynamicRepeatable(header, headerSizeFct);
|
|
336
|
-
} else if (header) {
|
|
337
|
-
measured.header = this.addStaticRepeatable(header, headerSizeFct);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
if (isFunction(footer)) {
|
|
341
|
-
measured.footer = this.addDynamicRepeatable(footer, footerSizeFct);
|
|
342
|
-
} else if (footer) {
|
|
343
|
-
measured.footer = this.addStaticRepeatable(footer, footerSizeFct);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
return measured;
|
|
347
|
-
};
|
|
348
|
-
|
|
349
|
-
LayoutBuilder.prototype.addWatermark = function (watermark, fontProvider, defaultStyle) {
|
|
350
|
-
if (isString(watermark)) {
|
|
351
|
-
watermark = { 'text': watermark };
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
if (!watermark.text) { // empty watermark text
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
var pages = this.writer.context().pages;
|
|
359
|
-
for (var i = 0, l = pages.length; i < l; i++) {
|
|
360
|
-
pages[i].watermark = getWatermarkObject({ ...watermark }, pages[i].pageSize, fontProvider, defaultStyle);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
function getWatermarkObject(watermark, pageSize, fontProvider, defaultStyle) {
|
|
364
|
-
watermark.font = watermark.font || defaultStyle.font || 'Roboto';
|
|
365
|
-
watermark.fontSize = watermark.fontSize || 'auto';
|
|
366
|
-
watermark.color = watermark.color || 'black';
|
|
367
|
-
watermark.opacity = isNumber(watermark.opacity) ? watermark.opacity : 0.6;
|
|
368
|
-
watermark.bold = watermark.bold || false;
|
|
369
|
-
watermark.italics = watermark.italics || false;
|
|
370
|
-
watermark.angle = !isUndefined(watermark.angle) && !isNull(watermark.angle) ? watermark.angle : null;
|
|
371
|
-
|
|
372
|
-
if (watermark.angle === null) {
|
|
373
|
-
watermark.angle = Math.atan2(pageSize.height, pageSize.width) * -180 / Math.PI;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
if (watermark.fontSize === 'auto') {
|
|
377
|
-
watermark.fontSize = getWatermarkFontSize(pageSize, watermark, fontProvider);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
var watermarkObject = {
|
|
381
|
-
text: watermark.text,
|
|
382
|
-
font: fontProvider.provideFont(watermark.font, watermark.bold, watermark.italics),
|
|
383
|
-
fontSize: watermark.fontSize,
|
|
384
|
-
color: watermark.color,
|
|
385
|
-
opacity: watermark.opacity,
|
|
386
|
-
angle: watermark.angle
|
|
387
|
-
};
|
|
388
|
-
|
|
389
|
-
watermarkObject._size = getWatermarkSize(watermark, fontProvider);
|
|
390
|
-
|
|
391
|
-
return watermarkObject;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
function getWatermarkSize(watermark, fontProvider) {
|
|
395
|
-
var textTools = new TextTools(fontProvider);
|
|
396
|
-
var styleContextStack = new StyleContextStack(null, { font: watermark.font, bold: watermark.bold, italics: watermark.italics });
|
|
397
|
-
|
|
398
|
-
styleContextStack.push({
|
|
399
|
-
fontSize: watermark.fontSize
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
var size = textTools.sizeOfString(watermark.text, styleContextStack);
|
|
403
|
-
var rotatedSize = textTools.sizeOfRotatedText(watermark.text, watermark.angle, styleContextStack);
|
|
404
|
-
|
|
405
|
-
return { size: size, rotatedSize: rotatedSize };
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
function getWatermarkFontSize(pageSize, watermark, fontProvider) {
|
|
409
|
-
var textTools = new TextTools(fontProvider);
|
|
410
|
-
var styleContextStack = new StyleContextStack(null, { font: watermark.font, bold: watermark.bold, italics: watermark.italics });
|
|
411
|
-
var rotatedSize;
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
* Binary search the best font size.
|
|
415
|
-
* Initial bounds [0, 1000]
|
|
416
|
-
* Break when range < 1
|
|
417
|
-
*/
|
|
418
|
-
var a = 0;
|
|
419
|
-
var b = 1000;
|
|
420
|
-
var c = (a + b) / 2;
|
|
421
|
-
while (Math.abs(a - b) > 1) {
|
|
422
|
-
styleContextStack.push({
|
|
423
|
-
fontSize: c
|
|
424
|
-
});
|
|
425
|
-
rotatedSize = textTools.sizeOfRotatedText(watermark.text, watermark.angle, styleContextStack);
|
|
426
|
-
if (rotatedSize.width > pageSize.width) {
|
|
427
|
-
b = c;
|
|
428
|
-
c = (a + b) / 2;
|
|
429
|
-
} else if (rotatedSize.width < pageSize.width) {
|
|
430
|
-
if (rotatedSize.height > pageSize.height) {
|
|
431
|
-
b = c;
|
|
432
|
-
c = (a + b) / 2;
|
|
433
|
-
} else {
|
|
434
|
-
a = c;
|
|
435
|
-
c = (a + b) / 2;
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
styleContextStack.pop();
|
|
439
|
-
}
|
|
440
|
-
/*
|
|
441
|
-
End binary search
|
|
442
|
-
*/
|
|
443
|
-
return c;
|
|
444
|
-
}
|
|
445
|
-
};
|
|
446
|
-
|
|
447
|
-
function decorateNode(node) {
|
|
448
|
-
var x = node.x, y = node.y;
|
|
449
|
-
node.positions = [];
|
|
450
|
-
|
|
451
|
-
if (isArray(node.canvas)) {
|
|
452
|
-
node.canvas.forEach(function (vector) {
|
|
453
|
-
var x = vector.x, y = vector.y, x1 = vector.x1, y1 = vector.y1, x2 = vector.x2, y2 = vector.y2;
|
|
454
|
-
vector.resetXY = function () {
|
|
455
|
-
vector.x = x;
|
|
456
|
-
vector.y = y;
|
|
457
|
-
vector.x1 = x1;
|
|
458
|
-
vector.y1 = y1;
|
|
459
|
-
vector.x2 = x2;
|
|
460
|
-
vector.y2 = y2;
|
|
461
|
-
};
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
node.resetXY = function () {
|
|
466
|
-
node.x = x;
|
|
467
|
-
node.y = y;
|
|
468
|
-
if (isArray(node.canvas)) {
|
|
469
|
-
node.canvas.forEach(function (vector) {
|
|
470
|
-
vector.resetXY();
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
LayoutBuilder.prototype.processNode = function (node) {
|
|
477
|
-
var self = this;
|
|
478
|
-
|
|
479
|
-
if (footerBreak && (node.footerBreak || node.footer)) {
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
if (node && node.unbreakable && node.summary && node.table && node.table.body &&
|
|
484
|
-
node.table.body[0] && node.table.body[0][0] && node.table.body[0][0].summaryBreak) {
|
|
485
|
-
testTracker = new TraversalTracker();
|
|
486
|
-
testWriter = new PageElementWriter(self.writer.context(), testTracker);
|
|
487
|
-
testVerticalAlignStack = self.verticalAlignItemStack.slice();
|
|
488
|
-
currentLayoutBuilder = self;
|
|
489
|
-
testResult = false;
|
|
490
|
-
var nodeForTest = cloneDeep(node);
|
|
491
|
-
if (nodeForTest.table.body[0]) {
|
|
492
|
-
nodeForTest.table.body[0].splice(0, 1);
|
|
493
|
-
}
|
|
494
|
-
processNode_test(nodeForTest);
|
|
495
|
-
currentLayoutBuilder = null;
|
|
496
|
-
if (testResult && node.table.body[0]) {
|
|
497
|
-
node.table.body[0].splice(0, 1);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
this.linearNodeList.push(node);
|
|
502
|
-
decorateNode(node);
|
|
503
|
-
|
|
504
|
-
var prevTop = self.writer.context().getCurrentPosition().top;
|
|
505
|
-
|
|
506
|
-
applyMargins(function () {
|
|
507
|
-
var unbreakable = node.unbreakable;
|
|
508
|
-
if (unbreakable) {
|
|
509
|
-
self.writer.beginUnbreakableBlock();
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
var absPosition = node.absolutePosition;
|
|
513
|
-
if (absPosition) {
|
|
514
|
-
self.writer.context().beginDetachedBlock();
|
|
515
|
-
self.writer.context().moveTo(absPosition.x || 0, absPosition.y || 0);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
var relPosition = node.relativePosition;
|
|
519
|
-
if (relPosition) {
|
|
520
|
-
self.writer.context().beginDetachedBlock();
|
|
521
|
-
self.writer.context().moveToRelative(relPosition.x || 0, relPosition.y || 0);
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
var verticalAlignBegin;
|
|
525
|
-
if (node.verticalAlign) {
|
|
526
|
-
verticalAlignBegin = self.writer.beginVerticalAlign(node.verticalAlign);
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
if (node.stack) {
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
self.
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
if (
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
var
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
//
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
}
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
var
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
widths
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
if (
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
}
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
};
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
} else if (
|
|
1557
|
-
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
}
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
for (var i = 0, l =
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
if (
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
if (!
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
}
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
var
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
}
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
}
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var cloneDeep = require('lodash/cloneDeep');
|
|
4
|
+
var TraversalTracker = require('./traversalTracker');
|
|
5
|
+
var DocPreprocessor = require('./docPreprocessor');
|
|
6
|
+
var DocMeasure = require('./docMeasure');
|
|
7
|
+
var DocumentContext = require('./documentContext');
|
|
8
|
+
var PageElementWriter = require('./pageElementWriter');
|
|
9
|
+
var ColumnCalculator = require('./columnCalculator');
|
|
10
|
+
var TableProcessor = require('./tableProcessor');
|
|
11
|
+
var Line = require('./line');
|
|
12
|
+
var isString = require('./helpers').isString;
|
|
13
|
+
var isArray = require('./helpers').isArray;
|
|
14
|
+
var isUndefined = require('./helpers').isUndefined;
|
|
15
|
+
var isNull = require('./helpers').isNull;
|
|
16
|
+
var pack = require('./helpers').pack;
|
|
17
|
+
var offsetVector = require('./helpers').offsetVector;
|
|
18
|
+
var fontStringify = require('./helpers').fontStringify;
|
|
19
|
+
var getNodeId = require('./helpers').getNodeId;
|
|
20
|
+
var isFunction = require('./helpers').isFunction;
|
|
21
|
+
var TextTools = require('./textTools');
|
|
22
|
+
var StyleContextStack = require('./styleContextStack');
|
|
23
|
+
var isNumber = require('./helpers').isNumber;
|
|
24
|
+
|
|
25
|
+
var footerBreak = false;
|
|
26
|
+
var testTracker;
|
|
27
|
+
var testWriter;
|
|
28
|
+
var testVerticalAlignStack = [];
|
|
29
|
+
var testResult = false;
|
|
30
|
+
var currentLayoutBuilder;
|
|
31
|
+
|
|
32
|
+
function addAll(target, otherArray) {
|
|
33
|
+
if (!isArray(target) || !isArray(otherArray) || otherArray.length === 0) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
otherArray.forEach(function (item) {
|
|
38
|
+
target.push(item);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Creates an instance of LayoutBuilder - layout engine which turns document-definition-object
|
|
44
|
+
* into a set of pages, lines, inlines and vectors ready to be rendered into a PDF
|
|
45
|
+
*
|
|
46
|
+
* @param {Object} pageSize - an object defining page width and height
|
|
47
|
+
* @param {Object} pageMargins - an object defining top, left, right and bottom margins
|
|
48
|
+
*/
|
|
49
|
+
function LayoutBuilder(pageSize, pageMargins, imageMeasure, svgMeasure) {
|
|
50
|
+
this.pageSize = pageSize;
|
|
51
|
+
this.pageMargins = pageMargins;
|
|
52
|
+
this.tracker = new TraversalTracker();
|
|
53
|
+
this.imageMeasure = imageMeasure;
|
|
54
|
+
this.svgMeasure = svgMeasure;
|
|
55
|
+
this.tableLayouts = {};
|
|
56
|
+
this.nestedLevel = 0;
|
|
57
|
+
this.verticalAlignItemStack = [];
|
|
58
|
+
this.heightHeaderAndFooter = {};
|
|
59
|
+
|
|
60
|
+
this._footerColumnGuides = null;
|
|
61
|
+
this._footerGapOption = null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
LayoutBuilder.prototype.registerTableLayouts = function (tableLayouts) {
|
|
65
|
+
this.tableLayouts = pack(this.tableLayouts, tableLayouts);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Executes layout engine on document-definition-object and creates an array of pages
|
|
70
|
+
* containing positioned Blocks, Lines and inlines
|
|
71
|
+
*
|
|
72
|
+
* @param {Object} docStructure document-definition-object
|
|
73
|
+
* @param {Object} fontProvider font provider
|
|
74
|
+
* @param {Object} styleDictionary dictionary with style definitions
|
|
75
|
+
* @param {Object} defaultStyle default style definition
|
|
76
|
+
* @return {Array} an array of pages
|
|
77
|
+
*/
|
|
78
|
+
LayoutBuilder.prototype.layoutDocument = function (docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark, pageBreakBeforeFct) {
|
|
79
|
+
|
|
80
|
+
function addPageBreaksIfNecessary(linearNodeList, pages) {
|
|
81
|
+
|
|
82
|
+
if (!isFunction(pageBreakBeforeFct)) {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
linearNodeList = linearNodeList.filter(function (node) {
|
|
87
|
+
return node.positions.length > 0;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
linearNodeList.forEach(function (node) {
|
|
91
|
+
var nodeInfo = {};
|
|
92
|
+
[
|
|
93
|
+
'id', 'text', 'ul', 'ol', 'table', 'image', 'qr', 'canvas', 'svg', 'columns', 'layers',
|
|
94
|
+
'headlineLevel', 'style', 'pageBreak', 'pageOrientation',
|
|
95
|
+
'width', 'height'
|
|
96
|
+
].forEach(function (key) {
|
|
97
|
+
if (node[key] !== undefined) {
|
|
98
|
+
nodeInfo[key] = node[key];
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
nodeInfo.startPosition = node.positions[0];
|
|
102
|
+
nodeInfo.pageNumbers = Array.from(new Set(node.positions.map(function (node) { return node.pageNumber; })));
|
|
103
|
+
nodeInfo.pages = pages.length;
|
|
104
|
+
nodeInfo.stack = isArray(node.stack);
|
|
105
|
+
nodeInfo.layers = isArray(node.layers);
|
|
106
|
+
|
|
107
|
+
node.nodeInfo = nodeInfo;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
for (var index = 0; index < linearNodeList.length; index++) {
|
|
111
|
+
var node = linearNodeList[index];
|
|
112
|
+
if (node.pageBreak !== 'before' && !node.pageBreakCalculated) {
|
|
113
|
+
node.pageBreakCalculated = true;
|
|
114
|
+
var pageNumber = node.nodeInfo.pageNumbers[0];
|
|
115
|
+
var followingNodesOnPage = [];
|
|
116
|
+
var nodesOnNextPage = [];
|
|
117
|
+
var previousNodesOnPage = [];
|
|
118
|
+
if (pageBreakBeforeFct.length > 1) {
|
|
119
|
+
for (var ii = index + 1, l = linearNodeList.length; ii < l; ii++) {
|
|
120
|
+
if (linearNodeList[ii].nodeInfo.pageNumbers.indexOf(pageNumber) > -1) {
|
|
121
|
+
followingNodesOnPage.push(linearNodeList[ii].nodeInfo);
|
|
122
|
+
}
|
|
123
|
+
if (pageBreakBeforeFct.length > 2 && linearNodeList[ii].nodeInfo.pageNumbers.indexOf(pageNumber + 1) > -1) {
|
|
124
|
+
nodesOnNextPage.push(linearNodeList[ii].nodeInfo);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (pageBreakBeforeFct.length > 3) {
|
|
129
|
+
for (var jj = 0; jj < index; jj++) {
|
|
130
|
+
if (linearNodeList[jj].nodeInfo.pageNumbers.indexOf(pageNumber) > -1) {
|
|
131
|
+
previousNodesOnPage.push(linearNodeList[jj].nodeInfo);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (pageBreakBeforeFct(node.nodeInfo, followingNodesOnPage, nodesOnNextPage, previousNodesOnPage)) {
|
|
136
|
+
node.pageBreak = 'before';
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
this.docPreprocessor = new DocPreprocessor();
|
|
146
|
+
this.docMeasure = new DocMeasure(fontProvider, styleDictionary, defaultStyle, this.imageMeasure, this.svgMeasure, this.tableLayouts, images);
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
function resetXYs(result) {
|
|
150
|
+
result.linearNodeList.forEach(function (node) {
|
|
151
|
+
node.resetXY();
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
var result = this.tryLayoutDocument(docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark);
|
|
156
|
+
while (addPageBreaksIfNecessary(result.linearNodeList, result.pages)) {
|
|
157
|
+
resetXYs(result);
|
|
158
|
+
result = this.tryLayoutDocument(docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return result.pages;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
LayoutBuilder.prototype.tryLayoutDocument = function (docStructure, fontProvider, styleDictionary, defaultStyle, background, header, footer, images, watermark) {
|
|
165
|
+
footerBreak = false;
|
|
166
|
+
|
|
167
|
+
this.verticalAlignItemStack = this.verticalAlignItemStack || [];
|
|
168
|
+
this.linearNodeList = [];
|
|
169
|
+
this.writer = new PageElementWriter(
|
|
170
|
+
new DocumentContext(this.pageSize, this.pageMargins, this._footerGapOption), this.tracker);
|
|
171
|
+
|
|
172
|
+
this.heightHeaderAndFooter = this.addHeadersAndFooters(header, footer) || {};
|
|
173
|
+
if (!isUndefined(this.heightHeaderAndFooter.header)) {
|
|
174
|
+
this.pageMargins.top = this.heightHeaderAndFooter.header + 1;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (isArray(docStructure) && docStructure[2] && isArray(docStructure[2]) && docStructure[2][0] && docStructure[2][0].remark) {
|
|
178
|
+
var tableRemark = docStructure[2][0].remark;
|
|
179
|
+
var remarkLabel = docStructure[2][0];
|
|
180
|
+
var remarkDetail = docStructure[2][1] && docStructure[2][1].text;
|
|
181
|
+
|
|
182
|
+
docStructure[2].splice(0, 1);
|
|
183
|
+
if (docStructure[2].length > 0) {
|
|
184
|
+
docStructure[2].splice(0, 1);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
var labelRow = [];
|
|
188
|
+
var detailRow = [];
|
|
189
|
+
|
|
190
|
+
labelRow.push(remarkLabel);
|
|
191
|
+
detailRow.push({ remarktest: true, text: remarkDetail });
|
|
192
|
+
|
|
193
|
+
tableRemark.table.body.push(labelRow);
|
|
194
|
+
tableRemark.table.body.push(detailRow);
|
|
195
|
+
|
|
196
|
+
tableRemark.table.headerRows = 1;
|
|
197
|
+
|
|
198
|
+
docStructure[2].push(tableRemark);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
this.linearNodeList = [];
|
|
202
|
+
docStructure = this.docPreprocessor.preprocessDocument(docStructure);
|
|
203
|
+
docStructure = this.docMeasure.measureDocument(docStructure);
|
|
204
|
+
|
|
205
|
+
this.verticalAlignItemStack = [];
|
|
206
|
+
this.writer = new PageElementWriter(
|
|
207
|
+
new DocumentContext(this.pageSize, this.pageMargins, this._footerGapOption), this.tracker);
|
|
208
|
+
|
|
209
|
+
var _this = this;
|
|
210
|
+
this.writer.context().tracker.startTracking('pageAdded', function () {
|
|
211
|
+
_this.addBackground(background);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
this.addBackground(background);
|
|
215
|
+
this.processNode(docStructure);
|
|
216
|
+
this.addHeadersAndFooters(header, footer,
|
|
217
|
+
(this.heightHeaderAndFooter.header || 0) + 1,
|
|
218
|
+
(this.heightHeaderAndFooter.footer || 0) + 1);
|
|
219
|
+
if (watermark != null) {
|
|
220
|
+
this.addWatermark(watermark, fontProvider, defaultStyle);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return { pages: this.writer.context().pages, linearNodeList: this.linearNodeList };
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
LayoutBuilder.prototype.applyFooterGapOption = function(opt) {
|
|
227
|
+
if (opt === true) {
|
|
228
|
+
opt = { enabled: true };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (!opt) return;
|
|
232
|
+
|
|
233
|
+
if (typeof opt !== 'object') {
|
|
234
|
+
this._footerGapOption = { enabled: true, forcePageBreakForAllRows: false };
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
this._footerGapOption = {
|
|
239
|
+
enabled: opt.enabled !== false,
|
|
240
|
+
minRowHeight: typeof opt.minRowHeight === 'number' ? opt.minRowHeight : undefined, // Optional fallback - system auto-calculates from cell content if not provided
|
|
241
|
+
forcePageBreakForAllRows: opt.forcePageBreakForAllRows === true, // Force page break for all rows (not just inline images)
|
|
242
|
+
columns: opt.columns ? {
|
|
243
|
+
widths: Array.isArray(opt.columns.widths) ? opt.columns.widths.slice() : undefined,
|
|
244
|
+
widthLength: opt.columns.widths.length || 0,
|
|
245
|
+
stops: Array.isArray(opt.columns.stops) ? opt.columns.stops.slice() : undefined,
|
|
246
|
+
style: opt.columns.style ? Object.assign({}, opt.columns.style) : {},
|
|
247
|
+
includeOuter: opt.columns.includeOuter !== false
|
|
248
|
+
} : null
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
LayoutBuilder.prototype.addBackground = function (background) {
|
|
253
|
+
var backgroundGetter = isFunction(background) ? background : function () {
|
|
254
|
+
return background;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
var context = this.writer.context();
|
|
258
|
+
var pageSize = context.getCurrentPage().pageSize;
|
|
259
|
+
|
|
260
|
+
var pageBackground = backgroundGetter(context.page + 1, pageSize);
|
|
261
|
+
|
|
262
|
+
if (pageBackground) {
|
|
263
|
+
this.writer.beginUnbreakableBlock(pageSize.width, pageSize.height);
|
|
264
|
+
pageBackground = this.docPreprocessor.preprocessDocument(pageBackground);
|
|
265
|
+
this.processNode(this.docMeasure.measureDocument(pageBackground));
|
|
266
|
+
this.writer.commitUnbreakableBlock(0, 0);
|
|
267
|
+
context.backgroundLength[context.page] += pageBackground.positions.length;
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
LayoutBuilder.prototype.addStaticRepeatable = function (headerOrFooter, sizeFunction) {
|
|
272
|
+
return this.addDynamicRepeatable(function () {
|
|
273
|
+
return JSON.parse(JSON.stringify(headerOrFooter)); // copy to new object
|
|
274
|
+
}, sizeFunction);
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
LayoutBuilder.prototype.addDynamicRepeatable = function (nodeGetter, sizeFunction) {
|
|
278
|
+
var pages = this.writer.context().pages;
|
|
279
|
+
var measuredHeight;
|
|
280
|
+
|
|
281
|
+
for (var pageIndex = 0, l = pages.length; pageIndex < l; pageIndex++) {
|
|
282
|
+
this.writer.context().page = pageIndex;
|
|
283
|
+
|
|
284
|
+
var node = nodeGetter(pageIndex + 1, l, this.writer.context().pages[pageIndex].pageSize);
|
|
285
|
+
|
|
286
|
+
if (node) {
|
|
287
|
+
var sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.pageMargins);
|
|
288
|
+
this.writer.beginUnbreakableBlock(sizes.width, sizes.height);
|
|
289
|
+
node = this.docPreprocessor.preprocessDocument(node);
|
|
290
|
+
this.processNode(this.docMeasure.measureDocument(node));
|
|
291
|
+
this.writer.commitUnbreakableBlock(sizes.x, sizes.y);
|
|
292
|
+
if (!isUndefined(node._height)) {
|
|
293
|
+
measuredHeight = node._height;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return measuredHeight;
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
LayoutBuilder.prototype.addHeadersAndFooters = function (header, footer, headerHeight, footerHeight) {
|
|
302
|
+
var measured = { header: undefined, footer: undefined };
|
|
303
|
+
|
|
304
|
+
var headerSizeFct = function (pageSize) {
|
|
305
|
+
var effectiveHeight = headerHeight;
|
|
306
|
+
if (isUndefined(effectiveHeight)) {
|
|
307
|
+
effectiveHeight = pageSize.height;
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
x: 0,
|
|
311
|
+
y: 0,
|
|
312
|
+
width: pageSize.width,
|
|
313
|
+
height: effectiveHeight
|
|
314
|
+
};
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
var footerSizeFct = function (pageSize) {
|
|
318
|
+
var effectiveHeight = footerHeight;
|
|
319
|
+
if (isUndefined(effectiveHeight)) {
|
|
320
|
+
effectiveHeight = pageSize.height;
|
|
321
|
+
}
|
|
322
|
+
return {
|
|
323
|
+
x: 0,
|
|
324
|
+
y: pageSize.height - effectiveHeight,
|
|
325
|
+
width: pageSize.width,
|
|
326
|
+
height: effectiveHeight
|
|
327
|
+
};
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
if (this._footerGapOption && !this.writer.context()._footerGapOption) {
|
|
331
|
+
this.writer.context()._footerGapOption = this._footerGapOption;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (isFunction(header)) {
|
|
335
|
+
measured.header = this.addDynamicRepeatable(header, headerSizeFct);
|
|
336
|
+
} else if (header) {
|
|
337
|
+
measured.header = this.addStaticRepeatable(header, headerSizeFct);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (isFunction(footer)) {
|
|
341
|
+
measured.footer = this.addDynamicRepeatable(footer, footerSizeFct);
|
|
342
|
+
} else if (footer) {
|
|
343
|
+
measured.footer = this.addStaticRepeatable(footer, footerSizeFct);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return measured;
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
LayoutBuilder.prototype.addWatermark = function (watermark, fontProvider, defaultStyle) {
|
|
350
|
+
if (isString(watermark)) {
|
|
351
|
+
watermark = { 'text': watermark };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (!watermark.text) { // empty watermark text
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
var pages = this.writer.context().pages;
|
|
359
|
+
for (var i = 0, l = pages.length; i < l; i++) {
|
|
360
|
+
pages[i].watermark = getWatermarkObject({ ...watermark }, pages[i].pageSize, fontProvider, defaultStyle);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function getWatermarkObject(watermark, pageSize, fontProvider, defaultStyle) {
|
|
364
|
+
watermark.font = watermark.font || defaultStyle.font || 'Roboto';
|
|
365
|
+
watermark.fontSize = watermark.fontSize || 'auto';
|
|
366
|
+
watermark.color = watermark.color || 'black';
|
|
367
|
+
watermark.opacity = isNumber(watermark.opacity) ? watermark.opacity : 0.6;
|
|
368
|
+
watermark.bold = watermark.bold || false;
|
|
369
|
+
watermark.italics = watermark.italics || false;
|
|
370
|
+
watermark.angle = !isUndefined(watermark.angle) && !isNull(watermark.angle) ? watermark.angle : null;
|
|
371
|
+
|
|
372
|
+
if (watermark.angle === null) {
|
|
373
|
+
watermark.angle = Math.atan2(pageSize.height, pageSize.width) * -180 / Math.PI;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (watermark.fontSize === 'auto') {
|
|
377
|
+
watermark.fontSize = getWatermarkFontSize(pageSize, watermark, fontProvider);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
var watermarkObject = {
|
|
381
|
+
text: watermark.text,
|
|
382
|
+
font: fontProvider.provideFont(watermark.font, watermark.bold, watermark.italics),
|
|
383
|
+
fontSize: watermark.fontSize,
|
|
384
|
+
color: watermark.color,
|
|
385
|
+
opacity: watermark.opacity,
|
|
386
|
+
angle: watermark.angle
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
watermarkObject._size = getWatermarkSize(watermark, fontProvider);
|
|
390
|
+
|
|
391
|
+
return watermarkObject;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function getWatermarkSize(watermark, fontProvider) {
|
|
395
|
+
var textTools = new TextTools(fontProvider);
|
|
396
|
+
var styleContextStack = new StyleContextStack(null, { font: watermark.font, bold: watermark.bold, italics: watermark.italics });
|
|
397
|
+
|
|
398
|
+
styleContextStack.push({
|
|
399
|
+
fontSize: watermark.fontSize
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
var size = textTools.sizeOfString(watermark.text, styleContextStack);
|
|
403
|
+
var rotatedSize = textTools.sizeOfRotatedText(watermark.text, watermark.angle, styleContextStack);
|
|
404
|
+
|
|
405
|
+
return { size: size, rotatedSize: rotatedSize };
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function getWatermarkFontSize(pageSize, watermark, fontProvider) {
|
|
409
|
+
var textTools = new TextTools(fontProvider);
|
|
410
|
+
var styleContextStack = new StyleContextStack(null, { font: watermark.font, bold: watermark.bold, italics: watermark.italics });
|
|
411
|
+
var rotatedSize;
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Binary search the best font size.
|
|
415
|
+
* Initial bounds [0, 1000]
|
|
416
|
+
* Break when range < 1
|
|
417
|
+
*/
|
|
418
|
+
var a = 0;
|
|
419
|
+
var b = 1000;
|
|
420
|
+
var c = (a + b) / 2;
|
|
421
|
+
while (Math.abs(a - b) > 1) {
|
|
422
|
+
styleContextStack.push({
|
|
423
|
+
fontSize: c
|
|
424
|
+
});
|
|
425
|
+
rotatedSize = textTools.sizeOfRotatedText(watermark.text, watermark.angle, styleContextStack);
|
|
426
|
+
if (rotatedSize.width > pageSize.width) {
|
|
427
|
+
b = c;
|
|
428
|
+
c = (a + b) / 2;
|
|
429
|
+
} else if (rotatedSize.width < pageSize.width) {
|
|
430
|
+
if (rotatedSize.height > pageSize.height) {
|
|
431
|
+
b = c;
|
|
432
|
+
c = (a + b) / 2;
|
|
433
|
+
} else {
|
|
434
|
+
a = c;
|
|
435
|
+
c = (a + b) / 2;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
styleContextStack.pop();
|
|
439
|
+
}
|
|
440
|
+
/*
|
|
441
|
+
End binary search
|
|
442
|
+
*/
|
|
443
|
+
return c;
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
function decorateNode(node) {
|
|
448
|
+
var x = node.x, y = node.y;
|
|
449
|
+
node.positions = [];
|
|
450
|
+
|
|
451
|
+
if (isArray(node.canvas)) {
|
|
452
|
+
node.canvas.forEach(function (vector) {
|
|
453
|
+
var x = vector.x, y = vector.y, x1 = vector.x1, y1 = vector.y1, x2 = vector.x2, y2 = vector.y2;
|
|
454
|
+
vector.resetXY = function () {
|
|
455
|
+
vector.x = x;
|
|
456
|
+
vector.y = y;
|
|
457
|
+
vector.x1 = x1;
|
|
458
|
+
vector.y1 = y1;
|
|
459
|
+
vector.x2 = x2;
|
|
460
|
+
vector.y2 = y2;
|
|
461
|
+
};
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
node.resetXY = function () {
|
|
466
|
+
node.x = x;
|
|
467
|
+
node.y = y;
|
|
468
|
+
if (isArray(node.canvas)) {
|
|
469
|
+
node.canvas.forEach(function (vector) {
|
|
470
|
+
vector.resetXY();
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
LayoutBuilder.prototype.processNode = function (node) {
|
|
477
|
+
var self = this;
|
|
478
|
+
|
|
479
|
+
if (footerBreak && (node.footerBreak || node.footer)) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (node && node.unbreakable && node.summary && node.table && node.table.body &&
|
|
484
|
+
node.table.body[0] && node.table.body[0][0] && node.table.body[0][0].summaryBreak) {
|
|
485
|
+
testTracker = new TraversalTracker();
|
|
486
|
+
testWriter = new PageElementWriter(self.writer.context(), testTracker);
|
|
487
|
+
testVerticalAlignStack = self.verticalAlignItemStack.slice();
|
|
488
|
+
currentLayoutBuilder = self;
|
|
489
|
+
testResult = false;
|
|
490
|
+
var nodeForTest = cloneDeep(node);
|
|
491
|
+
if (nodeForTest.table.body[0]) {
|
|
492
|
+
nodeForTest.table.body[0].splice(0, 1);
|
|
493
|
+
}
|
|
494
|
+
processNode_test(nodeForTest);
|
|
495
|
+
currentLayoutBuilder = null;
|
|
496
|
+
if (testResult && node.table.body[0]) {
|
|
497
|
+
node.table.body[0].splice(0, 1);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
this.linearNodeList.push(node);
|
|
502
|
+
decorateNode(node);
|
|
503
|
+
|
|
504
|
+
var prevTop = self.writer.context().getCurrentPosition().top;
|
|
505
|
+
|
|
506
|
+
applyMargins(function () {
|
|
507
|
+
var unbreakable = node.unbreakable;
|
|
508
|
+
if (unbreakable) {
|
|
509
|
+
self.writer.beginUnbreakableBlock();
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
var absPosition = node.absolutePosition;
|
|
513
|
+
if (absPosition) {
|
|
514
|
+
self.writer.context().beginDetachedBlock();
|
|
515
|
+
self.writer.context().moveTo(absPosition.x || 0, absPosition.y || 0);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
var relPosition = node.relativePosition;
|
|
519
|
+
if (relPosition) {
|
|
520
|
+
self.writer.context().beginDetachedBlock();
|
|
521
|
+
self.writer.context().moveToRelative(relPosition.x || 0, relPosition.y || 0);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
var verticalAlignBegin;
|
|
525
|
+
if (node.verticalAlign) {
|
|
526
|
+
verticalAlignBegin = self.writer.beginVerticalAlign(node.verticalAlign);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (node.stack) {
|
|
530
|
+
// Handle _footerGapOption for breakable stacks
|
|
531
|
+
var footerGapEnabled = !unbreakable && node.footer && node._footerGapOption && node._footerGapOption.enabled;
|
|
532
|
+
var preRenderState = null;
|
|
533
|
+
|
|
534
|
+
if (footerGapEnabled) {
|
|
535
|
+
var ctx = self.writer.context();
|
|
536
|
+
var currentPage = ctx.getCurrentPage();
|
|
537
|
+
preRenderState = {
|
|
538
|
+
startY: ctx.y,
|
|
539
|
+
startPage: ctx.page,
|
|
540
|
+
availableHeight: ctx.availableHeight,
|
|
541
|
+
itemCount: currentPage ? currentPage.items.length : 0
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Process the stack
|
|
546
|
+
self.processVerticalContainer(node);
|
|
547
|
+
|
|
548
|
+
// Post-render repositioning
|
|
549
|
+
if (footerGapEnabled && preRenderState) {
|
|
550
|
+
var ctx = self.writer.context();
|
|
551
|
+
|
|
552
|
+
// Only reposition if on same page (single-page content)
|
|
553
|
+
if (ctx.page === preRenderState.startPage) {
|
|
554
|
+
var renderedHeight = ctx.y - preRenderState.startY;
|
|
555
|
+
var gapHeight = preRenderState.availableHeight - renderedHeight;
|
|
556
|
+
|
|
557
|
+
if (gapHeight > 0) {
|
|
558
|
+
var currentPage = ctx.getCurrentPage();
|
|
559
|
+
|
|
560
|
+
// SINGLE-PAGE: Draw guide lines in the gap area
|
|
561
|
+
var gapTopY = preRenderState.startY;
|
|
562
|
+
var colSpec = node._footerGapOption.columns || self._footerGapOption.columns || null;
|
|
563
|
+
if (colSpec) {
|
|
564
|
+
var rawWidths = colSpec.content.vLines || [];
|
|
565
|
+
if (rawWidths && rawWidths.length > 1) {
|
|
566
|
+
var style = (colSpec.style || {});
|
|
567
|
+
var lw = style.lineWidth != null ? style.lineWidth : 0.5;
|
|
568
|
+
var lc = style.color || '#000000';
|
|
569
|
+
var dashCfg = style.dash;
|
|
570
|
+
var includeOuter = colSpec.includeOuter !== false;
|
|
571
|
+
var startIndex = includeOuter ? 0 : 1;
|
|
572
|
+
var endIndex = includeOuter ? rawWidths.length : rawWidths.length - 1;
|
|
573
|
+
|
|
574
|
+
for (var ci = startIndex; ci < endIndex; ci++) {
|
|
575
|
+
var xGuide = ctx.x + rawWidths[ci] - 0.25;
|
|
576
|
+
currentPage.items.push({
|
|
577
|
+
type: 'vector',
|
|
578
|
+
item: {
|
|
579
|
+
type: 'line',
|
|
580
|
+
x1: xGuide,
|
|
581
|
+
y1: gapTopY,
|
|
582
|
+
x2: xGuide,
|
|
583
|
+
y2: gapTopY + gapHeight,
|
|
584
|
+
lineWidth: lw,
|
|
585
|
+
lineColor: lc,
|
|
586
|
+
dash: dashCfg ? {
|
|
587
|
+
length: dashCfg.length,
|
|
588
|
+
space: dashCfg.space != null ? dashCfg.space : dashCfg.gap
|
|
589
|
+
} : undefined,
|
|
590
|
+
_footerGuideLine: true
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Reposition only items that belong to footer stack content
|
|
598
|
+
// (filter by Y position to exclude outer table border lines)
|
|
599
|
+
for (var i = preRenderState.itemCount; i < currentPage.items.length; i++) {
|
|
600
|
+
var item = currentPage.items[i];
|
|
601
|
+
|
|
602
|
+
// Skip the guide lines we just added (they're already positioned correctly)
|
|
603
|
+
if (item.item && item.item._footerGuideLine) {
|
|
604
|
+
continue;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Get the item's starting Y position
|
|
608
|
+
var itemStartY = null;
|
|
609
|
+
if (item.item && typeof item.item.y1 === 'number') {
|
|
610
|
+
itemStartY = item.item.y1;
|
|
611
|
+
} else if (item.item && typeof item.item.y === 'number') {
|
|
612
|
+
itemStartY = item.item.y;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Only reposition items that START at or after footer stack's Y position
|
|
616
|
+
// This excludes outer table border lines that extend from earlier rows
|
|
617
|
+
if (itemStartY !== null && itemStartY < preRenderState.startY) {
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// Reposition this item
|
|
622
|
+
if (item.item && typeof item.item.y === 'number') {
|
|
623
|
+
item.item.y += gapHeight;
|
|
624
|
+
}
|
|
625
|
+
if (item.item && typeof item.item.y1 === 'number') {
|
|
626
|
+
item.item.y1 += gapHeight;
|
|
627
|
+
}
|
|
628
|
+
if (item.item && typeof item.item.y2 === 'number') {
|
|
629
|
+
item.item.y2 += gapHeight;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
ctx.moveDown(gapHeight);
|
|
633
|
+
}
|
|
634
|
+
} else {
|
|
635
|
+
// MULTI-PAGE: keep remark at top, move footer to bottom, draw guide lines in gap
|
|
636
|
+
var currentPage = ctx.getCurrentPage();
|
|
637
|
+
var gapHeight = ctx.availableHeight;
|
|
638
|
+
|
|
639
|
+
// Helper function to recursively find node with _isFooterTable
|
|
640
|
+
function findFooterTableNode(n) {
|
|
641
|
+
if (!n) return null;
|
|
642
|
+
if (n._isFooterTable) return n;
|
|
643
|
+
|
|
644
|
+
// Check in stack array
|
|
645
|
+
if (n.stack && Array.isArray(n.stack)) {
|
|
646
|
+
for (var i = 0; i < n.stack.length; i++) {
|
|
647
|
+
var found = findFooterTableNode(n.stack[i]);
|
|
648
|
+
if (found) return found;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// Check if node is an array (footer might be array of items)
|
|
653
|
+
if (Array.isArray(n)) {
|
|
654
|
+
for (var i = 0; i < n.length; i++) {
|
|
655
|
+
var found = findFooterTableNode(n[i]);
|
|
656
|
+
if (found) return found;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
return null;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
if (gapHeight > 0) {
|
|
664
|
+
// Find remark bottom Y position (footer starts after remark)
|
|
665
|
+
// The remark node has _isRemarkContent marker
|
|
666
|
+
var remarkBottomY = 0;
|
|
667
|
+
var remarkNode = null;
|
|
668
|
+
|
|
669
|
+
// Find the remark node and footer table node in the stack
|
|
670
|
+
var footerTableNode = null;
|
|
671
|
+
if (node.stack && node.stack.length > 0) {
|
|
672
|
+
for (var si = 0; si < node.stack.length; si++) {
|
|
673
|
+
if (node.stack[si] && node.stack[si]._isRemarkContent && node.stack[si].remark) {
|
|
674
|
+
remarkNode = node.stack[si];
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
// Footer table has _isFooterTable marker - search recursively
|
|
678
|
+
for (var si = node.stack.length - 1; si >= 0; si--) {
|
|
679
|
+
var found = findFooterTableNode(node.stack[si]);
|
|
680
|
+
if (found) {
|
|
681
|
+
footerTableNode = found;
|
|
682
|
+
break;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Calculate remarkBottomY using footerTableNode._height (correct for multi-page)
|
|
688
|
+
// Footer is unbreakable, so its height is always on current page
|
|
689
|
+
// remarkNode._height is TOTAL height across all pages - NOT useful for multi-page
|
|
690
|
+
var footerStartY = ctx.y; // Default
|
|
691
|
+
|
|
692
|
+
if (footerTableNode && (footerTableNode._height || footerTableNode._minHeight)) {
|
|
693
|
+
// Use footer table height - this is reliable for multi-page
|
|
694
|
+
var footerHeight = footerTableNode._height || footerTableNode._minHeight;
|
|
695
|
+
footerStartY = ctx.y - footerHeight;
|
|
696
|
+
remarkBottomY = footerStartY; // Remark ends where footer starts
|
|
697
|
+
} else {
|
|
698
|
+
// Fallback: find lowest text line that's NOT inside footer table
|
|
699
|
+
var firstVectorY = ctx.y;
|
|
700
|
+
for (var fi = 0; fi < currentPage.items.length; fi++) {
|
|
701
|
+
var fitem = currentPage.items[fi];
|
|
702
|
+
if (fitem.type === 'vector' && fitem.item && fitem.item.type === 'line') {
|
|
703
|
+
var vecY = fitem.item.y1;
|
|
704
|
+
if (typeof vecY === 'number' && vecY < firstVectorY) {
|
|
705
|
+
firstVectorY = vecY;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
for (var fi = 0; fi < currentPage.items.length; fi++) {
|
|
710
|
+
var fitem = currentPage.items[fi];
|
|
711
|
+
if (fitem.type === 'line' && fitem.item) {
|
|
712
|
+
var lineY = fitem.item.y || 0;
|
|
713
|
+
var lineHeight = fitem.item.getHeight ? fitem.item.getHeight() : 15;
|
|
714
|
+
var lineBottom = lineY + lineHeight;
|
|
715
|
+
if (lineBottom <= firstVectorY && lineBottom > remarkBottomY) {
|
|
716
|
+
remarkBottomY = lineBottom;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
footerStartY = remarkBottomY > 0 ? remarkBottomY : firstVectorY;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// MULTI-PAGE: No guide lines - outer table borders extend through blank space
|
|
724
|
+
// Only reposition FOOTER items (items at Y >= footerStartY)
|
|
725
|
+
for (var i = 0; i < currentPage.items.length; i++) {
|
|
726
|
+
var item = currentPage.items[i];
|
|
727
|
+
|
|
728
|
+
// Skip guide lines we just added
|
|
729
|
+
if (item.item && item.item._footerGuideLine) continue;
|
|
730
|
+
|
|
731
|
+
// Get item's starting Y position
|
|
732
|
+
var itemY = null;
|
|
733
|
+
if (item.item && typeof item.item.y1 === 'number') {
|
|
734
|
+
itemY = item.item.y1;
|
|
735
|
+
} else if (item.item && typeof item.item.y === 'number') {
|
|
736
|
+
itemY = item.item.y;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// Only reposition if item is part of footer (Y >= footerStartY)
|
|
740
|
+
if (itemY === null || itemY < footerStartY) {
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// Reposition this item
|
|
745
|
+
if (item.item && typeof item.item.y === 'number') {
|
|
746
|
+
item.item.y += gapHeight;
|
|
747
|
+
}
|
|
748
|
+
if (item.item && typeof item.item.y1 === 'number') {
|
|
749
|
+
item.item.y1 += gapHeight;
|
|
750
|
+
}
|
|
751
|
+
if (item.item && typeof item.item.y2 === 'number') {
|
|
752
|
+
item.item.y2 += gapHeight;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
ctx.moveDown(gapHeight);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
} else if (node.layers) {
|
|
760
|
+
self.processLayers(node);
|
|
761
|
+
} else if (node.columns) {
|
|
762
|
+
self.processColumns(node);
|
|
763
|
+
} else if (node.ul) {
|
|
764
|
+
self.processList(false, node);
|
|
765
|
+
} else if (node.ol) {
|
|
766
|
+
self.processList(true, node);
|
|
767
|
+
} else if (node.table) {
|
|
768
|
+
self.processTable(node);
|
|
769
|
+
} else if (node.text !== undefined) {
|
|
770
|
+
self.processLeaf(node);
|
|
771
|
+
} else if (node.toc) {
|
|
772
|
+
self.processToc(node);
|
|
773
|
+
} else if (node.image) {
|
|
774
|
+
self.processImage(node);
|
|
775
|
+
} else if (node.svg) {
|
|
776
|
+
self.processSVG(node);
|
|
777
|
+
} else if (node.canvas) {
|
|
778
|
+
self.processCanvas(node);
|
|
779
|
+
} else if (node.qr) {
|
|
780
|
+
self.processQr(node);
|
|
781
|
+
} else if (!node._span) {
|
|
782
|
+
throw new Error('Unrecognized document structure: ' + JSON.stringify(node, fontStringify));
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if ((absPosition || relPosition) && !node.absoluteRepeatable) {
|
|
786
|
+
self.writer.context().endDetachedBlock();
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if (unbreakable) {
|
|
790
|
+
if (node.footer) {
|
|
791
|
+
footerBreak = self.writer.commitUnbreakableBlock(undefined, undefined, node.footer);
|
|
792
|
+
} else {
|
|
793
|
+
self.writer.commitUnbreakableBlock();
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
if (node.verticalAlign) {
|
|
798
|
+
var stackEntry = {
|
|
799
|
+
begin: verticalAlignBegin,
|
|
800
|
+
end: self.writer.endVerticalAlign(node.verticalAlign)
|
|
801
|
+
};
|
|
802
|
+
self.verticalAlignItemStack.push(stackEntry);
|
|
803
|
+
node._verticalAlignIdx = self.verticalAlignItemStack.length - 1;
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
node._height = self.writer.context().getCurrentPosition().top - prevTop;
|
|
808
|
+
|
|
809
|
+
function applyMargins(callback) {
|
|
810
|
+
var margin = node._margin;
|
|
811
|
+
|
|
812
|
+
if (node.pageBreak === 'before') {
|
|
813
|
+
self.writer.moveToNextPage(node.pageOrientation);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
if (margin) {
|
|
817
|
+
self.writer.context().moveDown(margin[1]);
|
|
818
|
+
self.writer.context().addMargin(margin[0], margin[2]);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
callback();
|
|
822
|
+
|
|
823
|
+
if (margin) {
|
|
824
|
+
self.writer.context().addMargin(-margin[0], -margin[2]);
|
|
825
|
+
self.writer.context().moveDown(margin[3]);
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
if (node.pageBreak === 'after') {
|
|
829
|
+
self.writer.moveToNextPage(node.pageOrientation);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
// vertical container
|
|
835
|
+
LayoutBuilder.prototype.processVerticalContainer = function (node) {
|
|
836
|
+
var self = this;
|
|
837
|
+
node.stack.forEach(function (item) {
|
|
838
|
+
self.processNode(item);
|
|
839
|
+
addAll(node.positions, item.positions);
|
|
840
|
+
|
|
841
|
+
//TODO: paragraph gap
|
|
842
|
+
});
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
// layers
|
|
846
|
+
LayoutBuilder.prototype.processLayers = function(node) {
|
|
847
|
+
var self = this;
|
|
848
|
+
var ctxX = self.writer.context().x;
|
|
849
|
+
var ctxY = self.writer.context().y;
|
|
850
|
+
var maxX = ctxX;
|
|
851
|
+
var maxY = ctxY;
|
|
852
|
+
node.layers.forEach(function(item, i) {
|
|
853
|
+
self.writer.context().x = ctxX;
|
|
854
|
+
self.writer.context().y = ctxY;
|
|
855
|
+
self.processNode(item);
|
|
856
|
+
item._verticalAlignIdx = self.verticalAlignItemStack.length - 1;
|
|
857
|
+
addAll(node.positions, item.positions);
|
|
858
|
+
maxX = self.writer.context().x > maxX ? self.writer.context().x : maxX;
|
|
859
|
+
maxY = self.writer.context().y > maxY ? self.writer.context().y : maxY;
|
|
860
|
+
});
|
|
861
|
+
self.writer.context().x = maxX;
|
|
862
|
+
self.writer.context().y = maxY;
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
// columns
|
|
866
|
+
LayoutBuilder.prototype.processColumns = function (columnNode) {
|
|
867
|
+
this.nestedLevel++;
|
|
868
|
+
var columns = columnNode.columns;
|
|
869
|
+
var availableWidth = this.writer.context().availableWidth;
|
|
870
|
+
var gaps = gapArray(columnNode._gap);
|
|
871
|
+
|
|
872
|
+
if (gaps) {
|
|
873
|
+
availableWidth -= (gaps.length - 1) * columnNode._gap;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
ColumnCalculator.buildColumnWidths(columns, availableWidth);
|
|
877
|
+
var result = this.processRow({
|
|
878
|
+
marginX: columnNode._margin ? [columnNode._margin[0], columnNode._margin[2]] : [0, 0],
|
|
879
|
+
cells: columns,
|
|
880
|
+
widths: columns,
|
|
881
|
+
gaps
|
|
882
|
+
});
|
|
883
|
+
addAll(columnNode.positions, result.positions);
|
|
884
|
+
|
|
885
|
+
this.nestedLevel--;
|
|
886
|
+
if (this.nestedLevel === 0) {
|
|
887
|
+
this.writer.context().resetMarginXTopParent();
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
function gapArray(gap) {
|
|
891
|
+
if (!gap) {
|
|
892
|
+
return null;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
var gaps = [];
|
|
896
|
+
gaps.push(0);
|
|
897
|
+
|
|
898
|
+
for (var i = columns.length - 1; i > 0; i--) {
|
|
899
|
+
gaps.push(gap);
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
return gaps;
|
|
903
|
+
}
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Searches for a cell in the same row that starts a rowspan and is positioned immediately before the current cell.
|
|
908
|
+
* Alternatively, it finds a cell where the colspan initiating the rowspan extends to the cell just before the current one.
|
|
909
|
+
*
|
|
910
|
+
* @param {Array<object>} arr - An array representing cells in a row.
|
|
911
|
+
* @param {number} i - The index of the current cell to search backward from.
|
|
912
|
+
* @returns {object|null} The starting cell of the rowspan if found; otherwise, `null`.
|
|
913
|
+
*/
|
|
914
|
+
LayoutBuilder.prototype._findStartingRowSpanCell = function (arr, i) {
|
|
915
|
+
var requiredColspan = 1;
|
|
916
|
+
for (var index = i - 1; index >= 0; index--) {
|
|
917
|
+
if (!arr[index]._span) {
|
|
918
|
+
if (arr[index].rowSpan > 1 && (arr[index].colSpan || 1) === requiredColspan) {
|
|
919
|
+
return arr[index];
|
|
920
|
+
} else {
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
requiredColspan++;
|
|
925
|
+
}
|
|
926
|
+
return null;
|
|
927
|
+
};
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* Retrieves a page break description for a specified page from a list of page breaks.
|
|
931
|
+
*
|
|
932
|
+
* @param {Array<object>} pageBreaks - An array of page break descriptions, each containing `prevPage` properties.
|
|
933
|
+
* @param {number} page - The page number to find the associated page break for.
|
|
934
|
+
* @returns {object|undefined} The page break description object for the specified page if found; otherwise, `undefined`.
|
|
935
|
+
*/
|
|
936
|
+
LayoutBuilder.prototype._getPageBreak = function (pageBreaks, page) {
|
|
937
|
+
return pageBreaks.find(desc => desc.prevPage === page);
|
|
938
|
+
};
|
|
939
|
+
|
|
940
|
+
LayoutBuilder.prototype._getPageBreakListBySpan = function (tableNode, page, rowIndex) {
|
|
941
|
+
if (!tableNode || !tableNode._breaksBySpan) {
|
|
942
|
+
return null;
|
|
943
|
+
}
|
|
944
|
+
const breaksList = tableNode._breaksBySpan.filter(desc => desc.prevPage === page && rowIndex <= desc.rowIndexOfSpanEnd);
|
|
945
|
+
|
|
946
|
+
var y = Number.MAX_VALUE,
|
|
947
|
+
prevY = Number.MIN_VALUE;
|
|
948
|
+
|
|
949
|
+
breaksList.forEach(b => {
|
|
950
|
+
prevY = Math.max(b.prevY, prevY);
|
|
951
|
+
y = Math.min(b.y, y);
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
return {
|
|
955
|
+
prevPage: page,
|
|
956
|
+
prevY: prevY,
|
|
957
|
+
y: y
|
|
958
|
+
};
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
LayoutBuilder.prototype._findSameRowPageBreakByRowSpanData = function (breaksBySpan, page, rowIndex) {
|
|
962
|
+
if (!breaksBySpan) {
|
|
963
|
+
return null;
|
|
964
|
+
}
|
|
965
|
+
return breaksBySpan.find(desc => desc.prevPage === page && rowIndex === desc.rowIndexOfSpanEnd);
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
LayoutBuilder.prototype._updatePageBreaksData = function (pageBreaks, tableNode, rowIndex) {
|
|
969
|
+
Object.keys(tableNode._bottomByPage).forEach(p => {
|
|
970
|
+
const page = Number(p);
|
|
971
|
+
const pageBreak = this._getPageBreak(pageBreaks, page);
|
|
972
|
+
if (pageBreak) {
|
|
973
|
+
pageBreak.prevY = Math.max(pageBreak.prevY, tableNode._bottomByPage[page]);
|
|
974
|
+
}
|
|
975
|
+
if (tableNode._breaksBySpan && tableNode._breaksBySpan.length > 0) {
|
|
976
|
+
const breaksBySpanList = tableNode._breaksBySpan.filter(pb => pb.prevPage === page && rowIndex <= pb.rowIndexOfSpanEnd);
|
|
977
|
+
if (breaksBySpanList && breaksBySpanList.length > 0) {
|
|
978
|
+
breaksBySpanList.forEach(b => {
|
|
979
|
+
b.prevY = Math.max(b.prevY, tableNode._bottomByPage[page]);
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
};
|
|
985
|
+
|
|
986
|
+
/**
|
|
987
|
+
* Resolves the Y-coordinates for a target object by comparing two break points.
|
|
988
|
+
*
|
|
989
|
+
* @param {object} break1 - The first break point with `prevY` and `y` properties.
|
|
990
|
+
* @param {object} break2 - The second break point with `prevY` and `y` properties.
|
|
991
|
+
* @param {object} target - The target object to be updated with resolved Y-coordinates.
|
|
992
|
+
* @property {number} target.prevY - Updated to the maximum `prevY` value between `break1` and `break2`.
|
|
993
|
+
* @property {number} target.y - Updated to the minimum `y` value between `break1` and `break2`.
|
|
994
|
+
*/
|
|
995
|
+
LayoutBuilder.prototype._resolveBreakY = function (break1, break2, target) {
|
|
996
|
+
target.prevY = Math.max(break1.prevY, break2.prevY);
|
|
997
|
+
target.y = Math.min(break1.y, break2.y);
|
|
998
|
+
};
|
|
999
|
+
|
|
1000
|
+
LayoutBuilder.prototype._storePageBreakData = function (data, startsRowSpan, pageBreaks, tableNode) {
|
|
1001
|
+
var pageDesc;
|
|
1002
|
+
var pageDescBySpan;
|
|
1003
|
+
|
|
1004
|
+
if (!startsRowSpan) {
|
|
1005
|
+
pageDesc = this._getPageBreak(pageBreaks, data.prevPage);
|
|
1006
|
+
pageDescBySpan = this._getPageBreakListBySpan(tableNode, data.prevPage, data.rowIndex);
|
|
1007
|
+
if (!pageDesc) {
|
|
1008
|
+
pageDesc = Object.assign({}, data);
|
|
1009
|
+
pageBreaks.push(pageDesc);
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
if (pageDescBySpan) {
|
|
1013
|
+
this._resolveBreakY(pageDesc, pageDescBySpan, pageDesc);
|
|
1014
|
+
}
|
|
1015
|
+
this._resolveBreakY(pageDesc, data, pageDesc);
|
|
1016
|
+
} else {
|
|
1017
|
+
var breaksBySpan = tableNode && tableNode._breaksBySpan || null;
|
|
1018
|
+
pageDescBySpan = this._findSameRowPageBreakByRowSpanData(breaksBySpan, data.prevPage, data.rowIndex);
|
|
1019
|
+
if (!pageDescBySpan) {
|
|
1020
|
+
pageDescBySpan = Object.assign({}, data, {
|
|
1021
|
+
rowIndexOfSpanEnd: data.rowIndex + data.rowSpan - 1
|
|
1022
|
+
});
|
|
1023
|
+
if (!tableNode._breaksBySpan) {
|
|
1024
|
+
tableNode._breaksBySpan = [];
|
|
1025
|
+
}
|
|
1026
|
+
tableNode._breaksBySpan.push(pageDescBySpan);
|
|
1027
|
+
}
|
|
1028
|
+
pageDescBySpan.prevY = Math.max(pageDescBySpan.prevY, data.prevY);
|
|
1029
|
+
pageDescBySpan.y = Math.min(pageDescBySpan.y, data.y);
|
|
1030
|
+
pageDesc = this._getPageBreak(pageBreaks, data.prevPage);
|
|
1031
|
+
if (pageDesc) {
|
|
1032
|
+
this._resolveBreakY(pageDesc, pageDescBySpan, pageDesc);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
};
|
|
1036
|
+
|
|
1037
|
+
/**
|
|
1038
|
+
* Calculates the left offset for a column based on the specified gap values.
|
|
1039
|
+
*
|
|
1040
|
+
* @param {number} i - The index of the column for which the offset is being calculated.
|
|
1041
|
+
* @param {Array<number>} gaps - An array of gap values for each column.
|
|
1042
|
+
* @returns {number} The left offset for the column. Returns `gaps[i]` if it exists, otherwise `0`.
|
|
1043
|
+
*/
|
|
1044
|
+
LayoutBuilder.prototype._colLeftOffset = function (i, gaps) {
|
|
1045
|
+
if (gaps && gaps.length > i) {
|
|
1046
|
+
return gaps[i];
|
|
1047
|
+
}
|
|
1048
|
+
return 0;
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1051
|
+
/**
|
|
1052
|
+
* Checks if a cell or node contains an inline image.
|
|
1053
|
+
*
|
|
1054
|
+
* @param {object} node - The node to check for inline images.
|
|
1055
|
+
* @returns {boolean} True if the node contains an inline image; otherwise, false.
|
|
1056
|
+
*/
|
|
1057
|
+
LayoutBuilder.prototype._containsInlineImage = function (node) {
|
|
1058
|
+
if (!node) {
|
|
1059
|
+
return false;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// Direct image node
|
|
1063
|
+
if (node.image) {
|
|
1064
|
+
return true;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
// Check in table
|
|
1069
|
+
if (node.table && isArray(node.table.body)) {
|
|
1070
|
+
for (var r = 0; r < node.table.body.length; r++) {
|
|
1071
|
+
if (isArray(node.table.body[r])) {
|
|
1072
|
+
for (var c = 0; c < node.table.body[r].length; c++) {
|
|
1073
|
+
if (this._containsInlineImage(node.table.body[r][c])) {
|
|
1074
|
+
return true;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
return false;
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
|
|
1085
|
+
/**
|
|
1086
|
+
* Gets the maximum image height from cells in a row.
|
|
1087
|
+
*
|
|
1088
|
+
* @param {Array<object>} cells - Array of cell objects in a row.
|
|
1089
|
+
* @returns {number} The maximum image height found in the cells.
|
|
1090
|
+
*/
|
|
1091
|
+
LayoutBuilder.prototype._getMaxImageHeight = function (cells) {
|
|
1092
|
+
var maxHeight = 0;
|
|
1093
|
+
|
|
1094
|
+
for (var i = 0; i < cells.length; i++) {
|
|
1095
|
+
var cellHeight = this._getImageHeightFromNode(cells[i]);
|
|
1096
|
+
if (cellHeight > maxHeight) {
|
|
1097
|
+
maxHeight = cellHeight;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
return maxHeight;
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* Gets the maximum estimated height from cells in a row.
|
|
1106
|
+
* Checks for measured heights (_height property) and content-based heights.
|
|
1107
|
+
*
|
|
1108
|
+
* @param {Array<object>} cells - Array of cell objects in a row.
|
|
1109
|
+
* @returns {number} The maximum estimated height found in the cells, or 0 if cannot estimate.
|
|
1110
|
+
*/
|
|
1111
|
+
LayoutBuilder.prototype._getMaxCellHeight = function (cells) {
|
|
1112
|
+
var maxHeight = 0;
|
|
1113
|
+
|
|
1114
|
+
for (var i = 0; i < cells.length; i++) {
|
|
1115
|
+
var cell = cells[i];
|
|
1116
|
+
if (!cell || cell._span) {
|
|
1117
|
+
continue; // Skip null cells and span placeholders
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
var cellHeight = 0;
|
|
1121
|
+
|
|
1122
|
+
// Check if cell has measured height from docMeasure phase
|
|
1123
|
+
if (cell._height) {
|
|
1124
|
+
cellHeight = cell._height;
|
|
1125
|
+
}
|
|
1126
|
+
// Check for image content
|
|
1127
|
+
else if (cell.image && cell._maxHeight) {
|
|
1128
|
+
cellHeight = cell._maxHeight;
|
|
1129
|
+
}
|
|
1130
|
+
// Check for nested content with height
|
|
1131
|
+
else {
|
|
1132
|
+
cellHeight = this._getImageHeightFromNode(cell);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
if (cellHeight > maxHeight) {
|
|
1136
|
+
maxHeight = cellHeight;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
return maxHeight;
|
|
1141
|
+
};
|
|
1142
|
+
|
|
1143
|
+
/**
|
|
1144
|
+
* Recursively gets image height from a node.
|
|
1145
|
+
*
|
|
1146
|
+
* @param {object} node - The node to extract image height from.
|
|
1147
|
+
* @returns {number} The image height if found; otherwise, 0.
|
|
1148
|
+
*/
|
|
1149
|
+
LayoutBuilder.prototype._getImageHeightFromNode = function (node) {
|
|
1150
|
+
if (!node) {
|
|
1151
|
+
return 0;
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
// Direct image node with height
|
|
1155
|
+
if (node.image && node._height) {
|
|
1156
|
+
return node._height;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
var maxHeight = 0;
|
|
1160
|
+
|
|
1161
|
+
// Check in stack
|
|
1162
|
+
if (isArray(node.stack)) {
|
|
1163
|
+
for (var i = 0; i < node.stack.length; i++) {
|
|
1164
|
+
var h = this._getImageHeightFromNode(node.stack[i]);
|
|
1165
|
+
if (h > maxHeight) {
|
|
1166
|
+
maxHeight = h;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
// Check in columns
|
|
1172
|
+
if (isArray(node.columns)) {
|
|
1173
|
+
for (var j = 0; j < node.columns.length; j++) {
|
|
1174
|
+
var h2 = this._getImageHeightFromNode(node.columns[j]);
|
|
1175
|
+
if (h2 > maxHeight) {
|
|
1176
|
+
maxHeight = h2;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
|
|
1181
|
+
// Check in table
|
|
1182
|
+
if (node.table && isArray(node.table.body)) {
|
|
1183
|
+
for (var r = 0; r < node.table.body.length; r++) {
|
|
1184
|
+
if (isArray(node.table.body[r])) {
|
|
1185
|
+
for (var c = 0; c < node.table.body[r].length; c++) {
|
|
1186
|
+
var h3 = this._getImageHeightFromNode(node.table.body[r][c]);
|
|
1187
|
+
if (h3 > maxHeight) {
|
|
1188
|
+
maxHeight = h3;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
return maxHeight;
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
/**
|
|
1200
|
+
* Retrieves the ending cell for a row span in case it exists in a specified table column.
|
|
1201
|
+
*
|
|
1202
|
+
* @param {Array<Array<object>>} tableBody - The table body, represented as a 2D array of cell objects.
|
|
1203
|
+
* @param {number} rowIndex - The index of the starting row for the row span.
|
|
1204
|
+
* @param {object} column - The column object containing row span information.
|
|
1205
|
+
* @param {number} columnIndex - The index of the column within the row.
|
|
1206
|
+
* @returns {object|null} The cell at the end of the row span if it exists; otherwise, `null`.
|
|
1207
|
+
* @throws {Error} If the row span extends beyond the total row count.
|
|
1208
|
+
*/
|
|
1209
|
+
LayoutBuilder.prototype._getRowSpanEndingCell = function (tableBody, rowIndex, column, columnIndex) {
|
|
1210
|
+
if (column.rowSpan && column.rowSpan > 1) {
|
|
1211
|
+
var endingRow = rowIndex + column.rowSpan - 1;
|
|
1212
|
+
if (endingRow >= tableBody.length) {
|
|
1213
|
+
throw new Error(`Row span for column ${columnIndex} (with indexes starting from 0) exceeded row count`);
|
|
1214
|
+
}
|
|
1215
|
+
return tableBody[endingRow][columnIndex];
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
return null;
|
|
1219
|
+
};
|
|
1220
|
+
|
|
1221
|
+
LayoutBuilder.prototype.processRow = function ({ marginX = [0, 0], dontBreakRows = false, rowsWithoutPageBreak = 0, cells, widths, gaps, tableNode, tableBody, rowIndex, height, heightOffset = 0 }) {
|
|
1222
|
+
var self = this;
|
|
1223
|
+
var isUnbreakableRow = dontBreakRows || rowIndex <= rowsWithoutPageBreak - 1;
|
|
1224
|
+
var pageBreaks = [];
|
|
1225
|
+
var pageBreaksByRowSpan = [];
|
|
1226
|
+
var positions = [];
|
|
1227
|
+
var willBreakByHeight = false;
|
|
1228
|
+
var columnAlignIndexes = {};
|
|
1229
|
+
var hasInlineImage = false;
|
|
1230
|
+
widths = widths || cells;
|
|
1231
|
+
|
|
1232
|
+
// Check if row contains inline images
|
|
1233
|
+
if (!isUnbreakableRow) {
|
|
1234
|
+
for (var cellIdx = 0; cellIdx < cells.length; cellIdx++) {
|
|
1235
|
+
if (self._containsInlineImage(cells[cellIdx])) {
|
|
1236
|
+
hasInlineImage = true;
|
|
1237
|
+
break;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
// Check if row would cause page break and force move to next page first
|
|
1243
|
+
// This keeps the entire row together on the new page
|
|
1244
|
+
// Apply when: forcePageBreakForAllRows is enabled OR row has inline images
|
|
1245
|
+
|
|
1246
|
+
// Priority for forcePageBreakForAllRows setting:
|
|
1247
|
+
// 1. Table-specific layout.forcePageBreakForAllRows
|
|
1248
|
+
// 2. Tables with footerGapCollect: 'product-items' (auto-enabled)
|
|
1249
|
+
// 3. Global footerGapOption.forcePageBreakForAllRows
|
|
1250
|
+
var tableLayout = tableNode && tableNode._layout;
|
|
1251
|
+
var footerGapOpt = self.writer.context()._footerGapOption;
|
|
1252
|
+
var shouldForcePageBreak = false;
|
|
1253
|
+
|
|
1254
|
+
if (tableLayout && tableLayout.forcePageBreakForAllRows !== undefined) {
|
|
1255
|
+
// Table-specific setting takes precedence
|
|
1256
|
+
shouldForcePageBreak = tableLayout.forcePageBreakForAllRows === true;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
if (!isUnbreakableRow && (shouldForcePageBreak || hasInlineImage)) {
|
|
1260
|
+
var availableHeight = self.writer.context().availableHeight;
|
|
1261
|
+
|
|
1262
|
+
// Calculate estimated height from actual cell content
|
|
1263
|
+
var estimatedHeight = height; // Use provided height if available
|
|
1264
|
+
|
|
1265
|
+
if (!estimatedHeight) {
|
|
1266
|
+
// Try to get maximum cell height from measured content
|
|
1267
|
+
var maxCellHeight = self._getMaxCellHeight(cells);
|
|
1268
|
+
|
|
1269
|
+
if (maxCellHeight > 0) {
|
|
1270
|
+
// Add padding for table borders and cell padding (approximate)
|
|
1271
|
+
// Using smaller padding to avoid overly conservative page break detection
|
|
1272
|
+
var tablePadding = 10; // Account for row padding and borders
|
|
1273
|
+
estimatedHeight = maxCellHeight + tablePadding;
|
|
1274
|
+
} else {
|
|
1275
|
+
// Fallback: use minRowHeight from table layout or global config if provided
|
|
1276
|
+
// Priority: table-specific layout > global footerGapOption > default 80
|
|
1277
|
+
// Using higher default (80px) to handle text rows with wrapping and multiple lines
|
|
1278
|
+
// This is conservative but prevents text rows from being split across pages
|
|
1279
|
+
var minRowHeight = (tableLayout && tableLayout.minRowHeight) || (footerGapOpt && footerGapOpt.minRowHeight) || 80;
|
|
1280
|
+
estimatedHeight = minRowHeight;
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
// Apply heightOffset from table definition to adjust page break calculation
|
|
1285
|
+
// This allows fine-tuning of page break detection for specific tables
|
|
1286
|
+
// heightOffset is passed as parameter from processTable
|
|
1287
|
+
if (heightOffset) {
|
|
1288
|
+
estimatedHeight = (estimatedHeight || 0) + heightOffset;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// Check if row won't fit on current page
|
|
1292
|
+
// Strategy: Force break if row won't fit AND we're not too close to page boundary
|
|
1293
|
+
// "Too close" means availableHeight is very small (< 5px) - at that point forcing
|
|
1294
|
+
// a break would create a nearly-blank page
|
|
1295
|
+
var minSpaceThreshold = 5; // Only skip forced break if < 5px space left
|
|
1296
|
+
|
|
1297
|
+
if (estimatedHeight > availableHeight && availableHeight > minSpaceThreshold) {
|
|
1298
|
+
var currentPage = self.writer.context().page;
|
|
1299
|
+
var currentY = self.writer.context().y;
|
|
1300
|
+
|
|
1301
|
+
// Draw vertical lines to fill the gap from current position to page break
|
|
1302
|
+
// This ensures vertical lines extend all the way to the bottom of the page
|
|
1303
|
+
if (tableNode && tableNode._tableProcessor && rowIndex > 0) {
|
|
1304
|
+
tableNode._tableProcessor.drawVerticalLinesForForcedPageBreak(
|
|
1305
|
+
rowIndex,
|
|
1306
|
+
self.writer,
|
|
1307
|
+
currentY,
|
|
1308
|
+
currentY + availableHeight
|
|
1309
|
+
);
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
// Move to next page before processing row
|
|
1313
|
+
self.writer.context().moveDown(availableHeight);
|
|
1314
|
+
self.writer.moveToNextPage();
|
|
1315
|
+
|
|
1316
|
+
// Track this page break so tableProcessor can draw borders correctly
|
|
1317
|
+
pageBreaks.push({
|
|
1318
|
+
prevPage: currentPage,
|
|
1319
|
+
prevY: currentY + availableHeight,
|
|
1320
|
+
y: self.writer.context().y,
|
|
1321
|
+
page: self.writer.context().page,
|
|
1322
|
+
forced: true // Mark as forced page break
|
|
1323
|
+
});
|
|
1324
|
+
|
|
1325
|
+
// Mark that this row should not break anymore
|
|
1326
|
+
isUnbreakableRow = true;
|
|
1327
|
+
dontBreakRows = true;
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
// Check if row should break by height
|
|
1332
|
+
if (!isUnbreakableRow && height > self.writer.context().availableHeight) {
|
|
1333
|
+
willBreakByHeight = true;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
// Use the marginX if we are in a top level table/column (not nested)
|
|
1337
|
+
const marginXParent = self.nestedLevel === 1 ? marginX : null;
|
|
1338
|
+
const _bottomByPage = tableNode ? tableNode._bottomByPage : null;
|
|
1339
|
+
this.writer.context().beginColumnGroup(marginXParent, _bottomByPage);
|
|
1340
|
+
|
|
1341
|
+
for (var i = 0, l = cells.length; i < l; i++) {
|
|
1342
|
+
var cell = cells[i];
|
|
1343
|
+
|
|
1344
|
+
// Page change handler
|
|
1345
|
+
|
|
1346
|
+
this.tracker.auto('pageChanged', storePageBreakClosure, function () {
|
|
1347
|
+
var width = widths[i]._calcWidth;
|
|
1348
|
+
var leftOffset = self._colLeftOffset(i, gaps);
|
|
1349
|
+
// Check if exists and retrieve the cell that started the rowspan in case we are in the cell just after
|
|
1350
|
+
var startingSpanCell = self._findStartingRowSpanCell(cells, i);
|
|
1351
|
+
|
|
1352
|
+
if (cell.colSpan && cell.colSpan > 1) {
|
|
1353
|
+
for (var j = 1; j < cell.colSpan; j++) {
|
|
1354
|
+
width += widths[++i]._calcWidth + gaps[i];
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
// if rowspan starts in this cell, we retrieve the last cell affected by the rowspan
|
|
1359
|
+
const rowSpanEndingCell = self._getRowSpanEndingCell(tableBody, rowIndex, cell, i);
|
|
1360
|
+
if (rowSpanEndingCell) {
|
|
1361
|
+
// We store a reference of the ending cell in the first cell of the rowspan
|
|
1362
|
+
cell._endingCell = rowSpanEndingCell;
|
|
1363
|
+
cell._endingCell._startingRowSpanY = cell._startingRowSpanY;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
// If we are after a cell that started a rowspan
|
|
1367
|
+
var endOfRowSpanCell = null;
|
|
1368
|
+
if (startingSpanCell && startingSpanCell._endingCell) {
|
|
1369
|
+
// Reference to the last cell of the rowspan
|
|
1370
|
+
endOfRowSpanCell = startingSpanCell._endingCell;
|
|
1371
|
+
// Store if we are in an unbreakable block when we save the context and the originalX
|
|
1372
|
+
if (self.writer.transactionLevel > 0) {
|
|
1373
|
+
endOfRowSpanCell._isUnbreakableContext = true;
|
|
1374
|
+
endOfRowSpanCell._originalXOffset = self.writer.originalX;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
// We pass the endingSpanCell reference to store the context just after processing rowspan cell
|
|
1379
|
+
self.writer.context().beginColumn(width, leftOffset, endOfRowSpanCell, heightOffset);
|
|
1380
|
+
|
|
1381
|
+
if (!cell._span) {
|
|
1382
|
+
self.processNode(cell);
|
|
1383
|
+
self.writer.context().updateBottomByPage();
|
|
1384
|
+
addAll(positions, cell.positions);
|
|
1385
|
+
if (cell.verticalAlign && cell._verticalAlignIdx !== undefined) {
|
|
1386
|
+
columnAlignIndexes[i] = cell._verticalAlignIdx;
|
|
1387
|
+
}
|
|
1388
|
+
} else if (cell._columnEndingContext) {
|
|
1389
|
+
var discountY = 0;
|
|
1390
|
+
if (dontBreakRows) {
|
|
1391
|
+
// Calculate how many points we have to discount to Y when dontBreakRows and rowSpan are combined
|
|
1392
|
+
const ctxBeforeRowSpanLastRow = self.writer.writer.contextStack[self.writer.writer.contextStack.length - 1];
|
|
1393
|
+
discountY = ctxBeforeRowSpanLastRow.y - cell._startingRowSpanY;
|
|
1394
|
+
}
|
|
1395
|
+
var originalXOffset = 0;
|
|
1396
|
+
// If context was saved from an unbreakable block and we are not in an unbreakable block anymore
|
|
1397
|
+
// We have to sum the originalX (X before starting unbreakable block) to X
|
|
1398
|
+
if (cell._isUnbreakableContext && !self.writer.transactionLevel) {
|
|
1399
|
+
originalXOffset = cell._originalXOffset;
|
|
1400
|
+
}
|
|
1401
|
+
// row-span ending
|
|
1402
|
+
// Recover the context after processing the rowspanned cell
|
|
1403
|
+
self.writer.context().markEnding(cell, originalXOffset, discountY);
|
|
1404
|
+
}
|
|
1405
|
+
});
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
// Check if last cell is part of a span
|
|
1409
|
+
var endingSpanCell = null;
|
|
1410
|
+
var lastColumn = cells.length > 0 ? cells[cells.length - 1] : null;
|
|
1411
|
+
if (lastColumn) {
|
|
1412
|
+
// Previous column cell has a rowspan
|
|
1413
|
+
if (lastColumn._endingCell) {
|
|
1414
|
+
endingSpanCell = lastColumn._endingCell;
|
|
1415
|
+
// Previous column cell is part of a span
|
|
1416
|
+
} else if (lastColumn._span === true) {
|
|
1417
|
+
// We get the cell that started the span where we set a reference to the ending cell
|
|
1418
|
+
const startingSpanCell = this._findStartingRowSpanCell(cells, cells.length);
|
|
1419
|
+
if (startingSpanCell) {
|
|
1420
|
+
// Context will be stored here (ending cell)
|
|
1421
|
+
endingSpanCell = startingSpanCell._endingCell;
|
|
1422
|
+
// Store if we are in an unbreakable block when we save the context and the originalX
|
|
1423
|
+
if (this.writer.transactionLevel > 0) {
|
|
1424
|
+
endingSpanCell._isUnbreakableContext = true;
|
|
1425
|
+
endingSpanCell._originalXOffset = this.writer.originalX;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
// If content did not break page, check if we should break by height
|
|
1432
|
+
if (willBreakByHeight && !isUnbreakableRow && pageBreaks.length === 0) {
|
|
1433
|
+
this.writer.context().moveDown(this.writer.context().availableHeight);
|
|
1434
|
+
this.writer.moveToNextPage();
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
var bottomByPage = this.writer.context().completeColumnGroup(height, endingSpanCell);
|
|
1438
|
+
var rowHeight = this.writer.context().height;
|
|
1439
|
+
for (var colIndex = 0, columnsLength = cells.length; colIndex < columnsLength; colIndex++) {
|
|
1440
|
+
var columnNode = cells[colIndex];
|
|
1441
|
+
if (columnNode._span) {
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
if (columnNode.verticalAlign && columnAlignIndexes[colIndex] !== undefined) {
|
|
1445
|
+
var alignEntry = self.verticalAlignItemStack[columnAlignIndexes[colIndex]];
|
|
1446
|
+
if (alignEntry && alignEntry.begin && alignEntry.begin.item) {
|
|
1447
|
+
alignEntry.begin.item.viewHeight = rowHeight;
|
|
1448
|
+
alignEntry.begin.item.nodeHeight = columnNode._height;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
if (columnNode.layers) {
|
|
1452
|
+
columnNode.layers.forEach(function (layer) {
|
|
1453
|
+
if (layer.verticalAlign && layer._verticalAlignIdx !== undefined) {
|
|
1454
|
+
var layerEntry = self.verticalAlignItemStack[layer._verticalAlignIdx];
|
|
1455
|
+
if (layerEntry && layerEntry.begin && layerEntry.begin.item) {
|
|
1456
|
+
layerEntry.begin.item.viewHeight = rowHeight;
|
|
1457
|
+
layerEntry.begin.item.nodeHeight = layer._height;
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
if (tableNode) {
|
|
1465
|
+
tableNode._bottomByPage = bottomByPage;
|
|
1466
|
+
// If there are page breaks in this row, update data with prevY of last cell
|
|
1467
|
+
this._updatePageBreaksData(pageBreaks, tableNode, rowIndex);
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
return {
|
|
1471
|
+
pageBreaksBySpan: pageBreaksByRowSpan,
|
|
1472
|
+
pageBreaks: pageBreaks,
|
|
1473
|
+
positions: positions
|
|
1474
|
+
};
|
|
1475
|
+
|
|
1476
|
+
function storePageBreakClosure(data) {
|
|
1477
|
+
const startsRowSpan = cell.rowSpan && cell.rowSpan > 1;
|
|
1478
|
+
if (startsRowSpan) {
|
|
1479
|
+
data.rowSpan = cell.rowSpan;
|
|
1480
|
+
}
|
|
1481
|
+
data.rowIndex = rowIndex;
|
|
1482
|
+
self._storePageBreakData(data, startsRowSpan, pageBreaks, tableNode);
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
};
|
|
1486
|
+
|
|
1487
|
+
// lists
|
|
1488
|
+
LayoutBuilder.prototype.processList = function (orderedList, node) {
|
|
1489
|
+
var self = this,
|
|
1490
|
+
items = orderedList ? node.ol : node.ul,
|
|
1491
|
+
gapSize = node._gapSize;
|
|
1492
|
+
|
|
1493
|
+
this.writer.context().addMargin(gapSize.width);
|
|
1494
|
+
|
|
1495
|
+
var nextMarker;
|
|
1496
|
+
this.tracker.auto('lineAdded', addMarkerToFirstLeaf, function () {
|
|
1497
|
+
items.forEach(function (item) {
|
|
1498
|
+
nextMarker = item.listMarker;
|
|
1499
|
+
self.processNode(item);
|
|
1500
|
+
addAll(node.positions, item.positions);
|
|
1501
|
+
});
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
this.writer.context().addMargin(-gapSize.width);
|
|
1505
|
+
|
|
1506
|
+
function addMarkerToFirstLeaf(line) {
|
|
1507
|
+
// I'm not very happy with the way list processing is implemented
|
|
1508
|
+
// (both code and algorithm should be rethinked)
|
|
1509
|
+
if (nextMarker) {
|
|
1510
|
+
var marker = nextMarker;
|
|
1511
|
+
nextMarker = null;
|
|
1512
|
+
|
|
1513
|
+
if (marker.canvas) {
|
|
1514
|
+
var vector = marker.canvas[0];
|
|
1515
|
+
|
|
1516
|
+
offsetVector(vector, -marker._minWidth, 0);
|
|
1517
|
+
self.writer.addVector(vector);
|
|
1518
|
+
} else if (marker._inlines) {
|
|
1519
|
+
var markerLine = new Line(self.pageSize.width);
|
|
1520
|
+
markerLine.addInline(marker._inlines[0]);
|
|
1521
|
+
markerLine.x = -marker._minWidth;
|
|
1522
|
+
markerLine.y = line.getAscenderHeight() - markerLine.getAscenderHeight();
|
|
1523
|
+
self.writer.addLine(markerLine, true);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
|
|
1529
|
+
// tables
|
|
1530
|
+
LayoutBuilder.prototype.processTable = function (tableNode) {
|
|
1531
|
+
this.nestedLevel++;
|
|
1532
|
+
var processor = new TableProcessor(tableNode);
|
|
1533
|
+
|
|
1534
|
+
// Store processor reference for forced page break vertical line drawing
|
|
1535
|
+
tableNode._tableProcessor = processor;
|
|
1536
|
+
|
|
1537
|
+
processor.beginTable(this.writer);
|
|
1538
|
+
|
|
1539
|
+
var rowHeights = tableNode.table.heights;
|
|
1540
|
+
for (var i = 0, l = tableNode.table.body.length; i < l; i++) {
|
|
1541
|
+
// if dontBreakRows and row starts a rowspan
|
|
1542
|
+
// we store the 'y' of the beginning of each rowSpan
|
|
1543
|
+
if (processor.dontBreakRows) {
|
|
1544
|
+
tableNode.table.body[i].forEach(cell => {
|
|
1545
|
+
if (cell.rowSpan && cell.rowSpan > 1) {
|
|
1546
|
+
cell._startingRowSpanY = this.writer.context().y;
|
|
1547
|
+
}
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
processor.beginRow(i, this.writer);
|
|
1552
|
+
|
|
1553
|
+
var height;
|
|
1554
|
+
if (isFunction(rowHeights)) {
|
|
1555
|
+
height = rowHeights(i);
|
|
1556
|
+
} else if (isArray(rowHeights)) {
|
|
1557
|
+
height = rowHeights[i];
|
|
1558
|
+
} else {
|
|
1559
|
+
height = rowHeights;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
if (height === 'auto') {
|
|
1563
|
+
height = undefined;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
var heightOffset = tableNode.heightOffset != undefined ? tableNode.heightOffset : 0;
|
|
1567
|
+
|
|
1568
|
+
var pageBeforeProcessing = this.writer.context().page;
|
|
1569
|
+
|
|
1570
|
+
var result = this.processRow({
|
|
1571
|
+
marginX: tableNode._margin ? [tableNode._margin[0], tableNode._margin[2]] : [0, 0],
|
|
1572
|
+
dontBreakRows: processor.dontBreakRows,
|
|
1573
|
+
rowsWithoutPageBreak: processor.rowsWithoutPageBreak,
|
|
1574
|
+
cells: tableNode.table.body[i],
|
|
1575
|
+
widths: tableNode.table.widths,
|
|
1576
|
+
gaps: tableNode._offsets.offsets,
|
|
1577
|
+
tableBody: tableNode.table.body,
|
|
1578
|
+
tableNode,
|
|
1579
|
+
rowIndex: i,
|
|
1580
|
+
height,
|
|
1581
|
+
heightOffset
|
|
1582
|
+
});
|
|
1583
|
+
addAll(tableNode.positions, result.positions);
|
|
1584
|
+
|
|
1585
|
+
if (!result.pageBreaks || result.pageBreaks.length === 0) {
|
|
1586
|
+
var breaksBySpan = tableNode && tableNode._breaksBySpan || null;
|
|
1587
|
+
var breakBySpanData = this._findSameRowPageBreakByRowSpanData(breaksBySpan, pageBeforeProcessing, i);
|
|
1588
|
+
if (breakBySpanData) {
|
|
1589
|
+
var finalBreakBySpanData = this._getPageBreakListBySpan(tableNode, breakBySpanData.prevPage, i);
|
|
1590
|
+
result.pageBreaks.push(finalBreakBySpanData);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
// Get next row cells for look-ahead page break detection
|
|
1595
|
+
var nextRowCells = (i + 1 < tableNode.table.body.length) ? tableNode.table.body[i + 1] : null;
|
|
1596
|
+
|
|
1597
|
+
processor.endRow(i, this.writer, result.pageBreaks, nextRowCells, this);
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
processor.endTable(this.writer);
|
|
1601
|
+
this.nestedLevel--;
|
|
1602
|
+
if (this.nestedLevel === 0) {
|
|
1603
|
+
this.writer.context().resetMarginXTopParent();
|
|
1604
|
+
}
|
|
1605
|
+
};
|
|
1606
|
+
|
|
1607
|
+
// leafs (texts)
|
|
1608
|
+
LayoutBuilder.prototype.processLeaf = function (node) {
|
|
1609
|
+
var line = this.buildNextLine(node);
|
|
1610
|
+
if (line && (node.tocItem || node.id)) {
|
|
1611
|
+
line._node = node;
|
|
1612
|
+
}
|
|
1613
|
+
var currentHeight = (line) ? line.getHeight() : 0;
|
|
1614
|
+
var maxHeight = node.maxHeight || -1;
|
|
1615
|
+
|
|
1616
|
+
if (line) {
|
|
1617
|
+
var nodeId = getNodeId(node);
|
|
1618
|
+
if (nodeId) {
|
|
1619
|
+
line.id = nodeId;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
if (node._tocItemRef) {
|
|
1624
|
+
line._pageNodeRef = node._tocItemRef;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
if (node._pageRef) {
|
|
1628
|
+
line._pageNodeRef = node._pageRef._nodeRef;
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
if (line && line.inlines && isArray(line.inlines)) {
|
|
1632
|
+
for (var i = 0, l = line.inlines.length; i < l; i++) {
|
|
1633
|
+
if (line.inlines[i]._tocItemRef) {
|
|
1634
|
+
line.inlines[i]._pageNodeRef = line.inlines[i]._tocItemRef;
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
if (line.inlines[i]._pageRef) {
|
|
1638
|
+
line.inlines[i]._pageNodeRef = line.inlines[i]._pageRef._nodeRef;
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
while (line && (maxHeight === -1 || currentHeight < maxHeight)) {
|
|
1644
|
+
var positions = this.writer.addLine(line);
|
|
1645
|
+
node.positions.push(positions);
|
|
1646
|
+
line = this.buildNextLine(node);
|
|
1647
|
+
if (line) {
|
|
1648
|
+
currentHeight += line.getHeight();
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
|
|
1653
|
+
LayoutBuilder.prototype.processToc = function (node) {
|
|
1654
|
+
if (node.toc.title) {
|
|
1655
|
+
this.processNode(node.toc.title);
|
|
1656
|
+
}
|
|
1657
|
+
if (node.toc._table) {
|
|
1658
|
+
this.processNode(node.toc._table);
|
|
1659
|
+
}
|
|
1660
|
+
};
|
|
1661
|
+
|
|
1662
|
+
LayoutBuilder.prototype.buildNextLine = function (textNode) {
|
|
1663
|
+
|
|
1664
|
+
function cloneInline(inline) {
|
|
1665
|
+
var newInline = inline.constructor();
|
|
1666
|
+
for (var key in inline) {
|
|
1667
|
+
newInline[key] = inline[key];
|
|
1668
|
+
}
|
|
1669
|
+
return newInline;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
if (!textNode._inlines || textNode._inlines.length === 0) {
|
|
1673
|
+
return null;
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
var line = new Line(this.writer.context().availableWidth);
|
|
1677
|
+
var textTools = new TextTools(null);
|
|
1678
|
+
|
|
1679
|
+
while (textNode._inlines && textNode._inlines.length > 0 && line.hasEnoughSpaceForInline(textNode._inlines[0])) {
|
|
1680
|
+
var inline = textNode._inlines.shift();
|
|
1681
|
+
|
|
1682
|
+
if (!inline.noWrap && inline.text.length > 1 && inline.width > line.maxWidth) {
|
|
1683
|
+
var widthPerChar = inline.width / inline.text.length;
|
|
1684
|
+
var maxChars = Math.floor(line.maxWidth / widthPerChar);
|
|
1685
|
+
if (maxChars < 1) {
|
|
1686
|
+
maxChars = 1;
|
|
1687
|
+
}
|
|
1688
|
+
if (maxChars < inline.text.length) {
|
|
1689
|
+
var newInline = cloneInline(inline);
|
|
1690
|
+
|
|
1691
|
+
newInline.text = inline.text.substr(maxChars);
|
|
1692
|
+
inline.text = inline.text.substr(0, maxChars);
|
|
1693
|
+
|
|
1694
|
+
newInline.width = textTools.widthOfString(newInline.text, newInline.font, newInline.fontSize, newInline.characterSpacing);
|
|
1695
|
+
inline.width = textTools.widthOfString(inline.text, inline.font, inline.fontSize, inline.characterSpacing);
|
|
1696
|
+
|
|
1697
|
+
textNode._inlines.unshift(newInline);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
line.addInline(inline);
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
line.lastLineInParagraph = textNode._inlines.length === 0;
|
|
1705
|
+
|
|
1706
|
+
return line;
|
|
1707
|
+
};
|
|
1708
|
+
|
|
1709
|
+
// images
|
|
1710
|
+
LayoutBuilder.prototype.processImage = function (node) {
|
|
1711
|
+
var position = this.writer.addImage(node);
|
|
1712
|
+
node.positions.push(position);
|
|
1713
|
+
};
|
|
1714
|
+
|
|
1715
|
+
LayoutBuilder.prototype.processSVG = function (node) {
|
|
1716
|
+
var position = this.writer.addSVG(node);
|
|
1717
|
+
node.positions.push(position);
|
|
1718
|
+
};
|
|
1719
|
+
|
|
1720
|
+
LayoutBuilder.prototype.processCanvas = function (node) {
|
|
1721
|
+
var height = node._minHeight;
|
|
1722
|
+
|
|
1723
|
+
if (node.absolutePosition === undefined && this.writer.context().availableHeight < height) {
|
|
1724
|
+
// TODO: support for canvas larger than a page
|
|
1725
|
+
// TODO: support for other overflow methods
|
|
1726
|
+
|
|
1727
|
+
this.writer.moveToNextPage();
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
this.writer.alignCanvas(node);
|
|
1731
|
+
|
|
1732
|
+
node.canvas.forEach(function (vector) {
|
|
1733
|
+
var position = this.writer.addVector(vector);
|
|
1734
|
+
node.positions.push(position);
|
|
1735
|
+
}, this);
|
|
1736
|
+
|
|
1737
|
+
this.writer.context().moveDown(height);
|
|
1738
|
+
};
|
|
1739
|
+
|
|
1740
|
+
LayoutBuilder.prototype.processQr = function (node) {
|
|
1741
|
+
var position = this.writer.addQr(node);
|
|
1742
|
+
node.positions.push(position);
|
|
1743
|
+
};
|
|
1744
|
+
|
|
1745
|
+
function processNode_test(node) {
|
|
1746
|
+
decorateNode(node);
|
|
1747
|
+
|
|
1748
|
+
var prevTop = testWriter.context().getCurrentPosition().top;
|
|
1749
|
+
|
|
1750
|
+
applyMargins(function () {
|
|
1751
|
+
var unbreakable = node.unbreakable;
|
|
1752
|
+
if (unbreakable) {
|
|
1753
|
+
testWriter.beginUnbreakableBlock();
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
var absPosition = node.absolutePosition;
|
|
1757
|
+
if (absPosition) {
|
|
1758
|
+
testWriter.context().beginDetachedBlock();
|
|
1759
|
+
testWriter.context().moveTo(absPosition.x || 0, absPosition.y || 0);
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
var relPosition = node.relativePosition;
|
|
1763
|
+
if (relPosition) {
|
|
1764
|
+
testWriter.context().beginDetachedBlock();
|
|
1765
|
+
if (typeof testWriter.context().moveToRelative === 'function') {
|
|
1766
|
+
testWriter.context().moveToRelative(relPosition.x || 0, relPosition.y || 0);
|
|
1767
|
+
} else if (currentLayoutBuilder && currentLayoutBuilder.writer) {
|
|
1768
|
+
testWriter.context().moveTo(
|
|
1769
|
+
(relPosition.x || 0) + currentLayoutBuilder.writer.context().x,
|
|
1770
|
+
(relPosition.y || 0) + currentLayoutBuilder.writer.context().y
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
var verticalAlignBegin;
|
|
1776
|
+
if (node.verticalAlign) {
|
|
1777
|
+
verticalAlignBegin = testWriter.beginVerticalAlign(node.verticalAlign);
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
if (node.stack) {
|
|
1781
|
+
processVerticalContainer_test(node);
|
|
1782
|
+
} else if (node.table) {
|
|
1783
|
+
processTable_test(node);
|
|
1784
|
+
} else if (node.text !== undefined) {
|
|
1785
|
+
processLeaf_test(node);
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
if (absPosition || relPosition) {
|
|
1789
|
+
testWriter.context().endDetachedBlock();
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
if (unbreakable) {
|
|
1793
|
+
testResult = testWriter.commitUnbreakableBlock_test();
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
if (node.verticalAlign) {
|
|
1797
|
+
testVerticalAlignStack.push({ begin: verticalAlignBegin, end: testWriter.endVerticalAlign(node.verticalAlign) });
|
|
1798
|
+
}
|
|
1799
|
+
});
|
|
1800
|
+
|
|
1801
|
+
node._height = testWriter.context().getCurrentPosition().top - prevTop;
|
|
1802
|
+
|
|
1803
|
+
function applyMargins(callback) {
|
|
1804
|
+
var margin = node._margin;
|
|
1805
|
+
|
|
1806
|
+
if (node.pageBreak === 'before') {
|
|
1807
|
+
testWriter.moveToNextPage(node.pageOrientation);
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
if (margin) {
|
|
1811
|
+
testWriter.context().moveDown(margin[1]);
|
|
1812
|
+
testWriter.context().addMargin(margin[0], margin[2]);
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
callback();
|
|
1816
|
+
|
|
1817
|
+
if (margin) {
|
|
1818
|
+
testWriter.context().addMargin(-margin[0], -margin[2]);
|
|
1819
|
+
testWriter.context().moveDown(margin[3]);
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
if (node.pageBreak === 'after') {
|
|
1823
|
+
testWriter.moveToNextPage(node.pageOrientation);
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
function processVerticalContainer_test(node) {
|
|
1829
|
+
node.stack.forEach(function (item) {
|
|
1830
|
+
processNode_test(item);
|
|
1831
|
+
addAll(node.positions, item.positions);
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
function processTable_test(tableNode) {
|
|
1836
|
+
var processor = new TableProcessor(tableNode);
|
|
1837
|
+
processor.beginTable(testWriter);
|
|
1838
|
+
|
|
1839
|
+
for (var i = 0, l = tableNode.table.body.length; i < l; i++) {
|
|
1840
|
+
processor.beginRow(i, testWriter);
|
|
1841
|
+
var result = processRow_test(tableNode.table.body[i], tableNode.table.widths, tableNode._offsets ? tableNode._offsets.offsets : null, tableNode.table.body, i);
|
|
1842
|
+
addAll(tableNode.positions, result.positions);
|
|
1843
|
+
processor.endRow(i, testWriter, result.pageBreaks);
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
processor.endTable(testWriter);
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
function processRow_test(columns, widths, gaps, tableBody, tableRow) {
|
|
1850
|
+
var pageBreaks = [];
|
|
1851
|
+
var positions = [];
|
|
1852
|
+
|
|
1853
|
+
testTracker.auto('pageChanged', storePageBreakData, function () {
|
|
1854
|
+
widths = widths || columns;
|
|
1855
|
+
|
|
1856
|
+
testWriter.context().beginColumnGroup();
|
|
1857
|
+
|
|
1858
|
+
var verticalAlignCols = {};
|
|
1859
|
+
|
|
1860
|
+
for (var i = 0, l = columns.length; i < l; i++) {
|
|
1861
|
+
var column = columns[i];
|
|
1862
|
+
var width = widths[i]._calcWidth || widths[i];
|
|
1863
|
+
var leftOffset = colLeftOffset(i);
|
|
1864
|
+
var colIndex = i;
|
|
1865
|
+
if (column.colSpan && column.colSpan > 1) {
|
|
1866
|
+
for (var j = 1; j < column.colSpan; j++) {
|
|
1867
|
+
width += (widths[++i]._calcWidth || widths[i]) + (gaps ? gaps[i] : 0);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
testWriter.context().beginColumn(width, leftOffset, getEndingCell(column, i));
|
|
1872
|
+
|
|
1873
|
+
if (!column._span) {
|
|
1874
|
+
processNode_test(column);
|
|
1875
|
+
verticalAlignCols[colIndex] = testVerticalAlignStack.length - 1;
|
|
1876
|
+
addAll(positions, column.positions);
|
|
1877
|
+
} else if (column._columnEndingContext) {
|
|
1878
|
+
testWriter.context().markEnding(column);
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
testWriter.context().completeColumnGroup();
|
|
1883
|
+
|
|
1884
|
+
var rowHeight = testWriter.context().height;
|
|
1885
|
+
for (var c = 0, clen = columns.length; c < clen; c++) {
|
|
1886
|
+
var col = columns[c];
|
|
1887
|
+
if (col._span) {
|
|
1888
|
+
continue;
|
|
1889
|
+
}
|
|
1890
|
+
if (col.verticalAlign && verticalAlignCols[c] !== undefined) {
|
|
1891
|
+
var alignItem = testVerticalAlignStack[verticalAlignCols[c]].begin.item;
|
|
1892
|
+
alignItem.viewHeight = rowHeight;
|
|
1893
|
+
alignItem.nodeHeight = col._height;
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
});
|
|
1897
|
+
|
|
1898
|
+
return { pageBreaks: pageBreaks, positions: positions };
|
|
1899
|
+
|
|
1900
|
+
function storePageBreakData(data) {
|
|
1901
|
+
var pageDesc;
|
|
1902
|
+
for (var idx = 0, len = pageBreaks.length; idx < len; idx++) {
|
|
1903
|
+
var desc = pageBreaks[idx];
|
|
1904
|
+
if (desc.prevPage === data.prevPage) {
|
|
1905
|
+
pageDesc = desc;
|
|
1906
|
+
break;
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1910
|
+
if (!pageDesc) {
|
|
1911
|
+
pageDesc = data;
|
|
1912
|
+
pageBreaks.push(pageDesc);
|
|
1913
|
+
}
|
|
1914
|
+
pageDesc.prevY = Math.max(pageDesc.prevY, data.prevY);
|
|
1915
|
+
pageDesc.y = Math.min(pageDesc.y, data.y);
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
function colLeftOffset(i) {
|
|
1919
|
+
if (gaps && gaps.length > i) {
|
|
1920
|
+
return gaps[i];
|
|
1921
|
+
}
|
|
1922
|
+
return 0;
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
function getEndingCell(column, columnIndex) {
|
|
1926
|
+
if (column.rowSpan && column.rowSpan > 1) {
|
|
1927
|
+
var endingRow = tableRow + column.rowSpan - 1;
|
|
1928
|
+
if (endingRow >= tableBody.length) {
|
|
1929
|
+
throw new Error('Row span for column ' + columnIndex + ' (with indexes starting from 0) exceeded row count');
|
|
1930
|
+
}
|
|
1931
|
+
return tableBody[endingRow][columnIndex];
|
|
1932
|
+
}
|
|
1933
|
+
return null;
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
function processLeaf_test(node) {
|
|
1938
|
+
var line = buildNextLine_test(node);
|
|
1939
|
+
var currentHeight = line ? line.getHeight() : 0;
|
|
1940
|
+
var maxHeight = node.maxHeight || -1;
|
|
1941
|
+
|
|
1942
|
+
while (line && (maxHeight === -1 || currentHeight < maxHeight)) {
|
|
1943
|
+
var positions = testWriter.addLine(line);
|
|
1944
|
+
node.positions.push(positions);
|
|
1945
|
+
line = buildNextLine_test(node);
|
|
1946
|
+
if (line) {
|
|
1947
|
+
currentHeight += line.getHeight();
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
function buildNextLine_test(textNode) {
|
|
1953
|
+
function cloneInline(inline) {
|
|
1954
|
+
var newInline = inline.constructor();
|
|
1955
|
+
for (var key in inline) {
|
|
1956
|
+
newInline[key] = inline[key];
|
|
1957
|
+
}
|
|
1958
|
+
return newInline;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
if (!textNode._inlines || textNode._inlines.length === 0) {
|
|
1962
|
+
return null;
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
var line = new Line(testWriter.context().availableWidth);
|
|
1966
|
+
var textTools = new TextTools(null);
|
|
1967
|
+
|
|
1968
|
+
while (textNode._inlines && textNode._inlines.length > 0 && line.hasEnoughSpaceForInline(textNode._inlines[0])) {
|
|
1969
|
+
var inline = textNode._inlines.shift();
|
|
1970
|
+
|
|
1971
|
+
if (!inline.noWrap && inline.text.length > 1 && inline.width > line.maxWidth) {
|
|
1972
|
+
var widthPerChar = inline.width / inline.text.length;
|
|
1973
|
+
var maxChars = Math.floor(line.maxWidth / widthPerChar);
|
|
1974
|
+
if (maxChars < 1) {
|
|
1975
|
+
maxChars = 1;
|
|
1976
|
+
}
|
|
1977
|
+
if (maxChars < inline.text.length) {
|
|
1978
|
+
var newInline = cloneInline(inline);
|
|
1979
|
+
|
|
1980
|
+
newInline.text = inline.text.substr(maxChars);
|
|
1981
|
+
inline.text = inline.text.substr(0, maxChars);
|
|
1982
|
+
|
|
1983
|
+
newInline.width = textTools.widthOfString(newInline.text, newInline.font, newInline.fontSize, newInline.characterSpacing);
|
|
1984
|
+
inline.width = textTools.widthOfString(inline.text, inline.font, inline.fontSize, inline.characterSpacing);
|
|
1985
|
+
|
|
1986
|
+
textNode._inlines.unshift(newInline);
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
line.addInline(inline);
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
line.lastLineInParagraph = textNode._inlines.length === 0;
|
|
1994
|
+
|
|
1995
|
+
return line;
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
module.exports = LayoutBuilder;
|