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