@softwear/latestcollectioncore 1.0.174 → 1.0.176
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/dist/imageBinder.js +18 -12
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -1
- package/dist/pdf.d.ts +67 -0
- package/dist/pdf.js +2 -0
- package/dist/reports.d.ts +35 -0
- package/dist/reports.js +705 -0
- package/package.json +3 -2
- package/src/imageBinder.ts +18 -16
- package/src/index.ts +3 -0
- package/src/pdf.ts +76 -0
- package/src/reports.ts +831 -0
- package/test/imageBinder.spec.js +8 -8
- package/test/imageBinderTestData/brandDataMatched/dreamstar.json +6 -709
- package/test/imageBinderTestData/brandDataMatched/lowa.json +6 -1456
- package/test/imageBinderTestData/brandDataMatched/miraclesuit.json +838 -1721
- package/test/imageBinderTestData/brandDataMatched/vacanzeitaliane.json +1215 -2185
- package/test/imageBinderTestData/brandSkus/dreamstar.json +68 -34
- package/test/imageBinderTestData/brandSkus/lowa.json +168 -84
- package/test/imageBinderTestData/brandSkus/miraclesuit.json +78 -39
- package/test/imageBinderTestData/brandSkus/vacanzeitaliane.json +106 -53
- package/test/reports.spec.ts +74 -0
- package/test.pdf +0 -0
package/dist/reports.js
ADDED
|
@@ -0,0 +1,705 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.genPDF = void 0;
|
|
16
|
+
const jspdf_1 = require("jspdf");
|
|
17
|
+
const date_fns_1 = require("date-fns");
|
|
18
|
+
const deepCopy_1 = __importDefault(require("./deepCopy"));
|
|
19
|
+
// We have to declare some functions that will be called by other functions but also have to call those other functions
|
|
20
|
+
// We overwrite the function definition as soon as those other functions have been declared
|
|
21
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
22
|
+
let drawStaticPartOfPage = function (doc, layout, printBuffer, paperSize, options, context) {
|
|
23
|
+
// to be assigned later
|
|
24
|
+
};
|
|
25
|
+
// Forward declaration - parameters intentionally unused, function is overwritten below
|
|
26
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
27
|
+
let addObjectToPDF = function (_originX, _originY, _doc, _object, _printBuffer, _paperSize, _layout, _options, _rootPrintBuffer, _containerChain, _context) {
|
|
28
|
+
// to be assigned later
|
|
29
|
+
return { x: 0, y: 0 };
|
|
30
|
+
};
|
|
31
|
+
/* eslint-enable @typescript-eslint/no-unused-vars */
|
|
32
|
+
const formatDate = function (date) {
|
|
33
|
+
return (0, date_fns_1.format)(new Date(date), 'MM-dd-yy HH:mm:ss');
|
|
34
|
+
};
|
|
35
|
+
function getProperty(propertyName, object) {
|
|
36
|
+
const parts = propertyName.split('.');
|
|
37
|
+
const length = parts.length;
|
|
38
|
+
let property = object;
|
|
39
|
+
for (let i = 0; i < length; i++) {
|
|
40
|
+
if (property) {
|
|
41
|
+
property = property[parts[i]];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return property;
|
|
45
|
+
}
|
|
46
|
+
/** jsPDF font style strings. fontStyle bitmask: 0=normal, 1=bold, 2=italic, 3=bold+italic */
|
|
47
|
+
const FONT_STYLES = ['normal', 'bold', 'italic', 'bolditalic'];
|
|
48
|
+
/** Recursively collect used custom fonts from layout objects (text/field with fontFamily) and layout default. */
|
|
49
|
+
function collectUsedFontsFromLayout(layout, options) {
|
|
50
|
+
const used = new Set();
|
|
51
|
+
const isCustomPdfFont = options.isCustomPdfFont || (() => false);
|
|
52
|
+
function walk(obj) {
|
|
53
|
+
if ((obj.type === 'text' || obj.type === 'field') && obj.fontFamily && isCustomPdfFont(obj.fontFamily)) {
|
|
54
|
+
used.add(obj.fontFamily);
|
|
55
|
+
}
|
|
56
|
+
if (obj.type === 'container' && obj.children) {
|
|
57
|
+
obj.children.forEach(walk);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
layout.objects.forEach(walk);
|
|
61
|
+
if (layout.defaultFontFamily && isCustomPdfFont(layout.defaultFontFamily)) {
|
|
62
|
+
used.add(layout.defaultFontFamily);
|
|
63
|
+
}
|
|
64
|
+
return used;
|
|
65
|
+
}
|
|
66
|
+
function binaryToBase64(binary) {
|
|
67
|
+
if (typeof btoa === 'function')
|
|
68
|
+
return btoa(binary);
|
|
69
|
+
const buffer = globalThis.Buffer;
|
|
70
|
+
if (buffer)
|
|
71
|
+
return buffer.from(binary, 'binary').toString('base64');
|
|
72
|
+
throw new Error('No base64 encoder available');
|
|
73
|
+
}
|
|
74
|
+
function bytesToBase64(bytes) {
|
|
75
|
+
const buffer = globalThis.Buffer;
|
|
76
|
+
if (buffer)
|
|
77
|
+
return buffer.from(bytes).toString('base64');
|
|
78
|
+
const chunkSize = 8192;
|
|
79
|
+
let binary = '';
|
|
80
|
+
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
81
|
+
const chunk = bytes.subarray(i, Math.min(i + chunkSize, bytes.length));
|
|
82
|
+
binary += String.fromCharCode.apply(null, Array.from(chunk));
|
|
83
|
+
}
|
|
84
|
+
return binaryToBase64(binary);
|
|
85
|
+
}
|
|
86
|
+
function readUint16BE(bytes, offset) {
|
|
87
|
+
return (bytes[offset] << 8) | bytes[offset + 1];
|
|
88
|
+
}
|
|
89
|
+
function readUint32BE(bytes, offset) {
|
|
90
|
+
return bytes[offset] * 0x1000000 + ((bytes[offset + 1] << 16) | (bytes[offset + 2] << 8) | bytes[offset + 3]);
|
|
91
|
+
}
|
|
92
|
+
function isPng(bytes) {
|
|
93
|
+
return bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47;
|
|
94
|
+
}
|
|
95
|
+
function isJpeg(bytes) {
|
|
96
|
+
return bytes[0] === 0xff && bytes[1] === 0xd8;
|
|
97
|
+
}
|
|
98
|
+
function getImageFormat(url, contentType, bytes) {
|
|
99
|
+
const lowerContentType = (contentType === null || contentType === void 0 ? void 0 : contentType.toLowerCase()) || '';
|
|
100
|
+
const lowerUrl = url.toLowerCase();
|
|
101
|
+
if (lowerContentType.includes('png') || isPng(bytes) || lowerUrl.endsWith('.png'))
|
|
102
|
+
return 'PNG';
|
|
103
|
+
return 'JPEG';
|
|
104
|
+
}
|
|
105
|
+
function getImageMimeType(format) {
|
|
106
|
+
return format === 'PNG' ? 'image/png' : 'image/jpeg';
|
|
107
|
+
}
|
|
108
|
+
function getImageDimensions(bytes, format) {
|
|
109
|
+
if (format === 'PNG' && bytes.length >= 24 && isPng(bytes)) {
|
|
110
|
+
return { width: readUint32BE(bytes, 16), height: readUint32BE(bytes, 20) };
|
|
111
|
+
}
|
|
112
|
+
if (format === 'JPEG' && isJpeg(bytes)) {
|
|
113
|
+
let offset = 2;
|
|
114
|
+
while (offset + 9 < bytes.length) {
|
|
115
|
+
if (bytes[offset] !== 0xff) {
|
|
116
|
+
offset++;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const marker = bytes[offset + 1];
|
|
120
|
+
const length = readUint16BE(bytes, offset + 2);
|
|
121
|
+
const isStartOfFrameMarker = (marker >= 0xc0 && marker <= 0xc3) || (marker >= 0xc5 && marker <= 0xc7) || (marker >= 0xc9 && marker <= 0xcb) || (marker >= 0xcd && marker <= 0xcf);
|
|
122
|
+
if (isStartOfFrameMarker) {
|
|
123
|
+
return { height: readUint16BE(bytes, offset + 5), width: readUint16BE(bytes, offset + 7) };
|
|
124
|
+
}
|
|
125
|
+
if (length < 2)
|
|
126
|
+
break;
|
|
127
|
+
offset += 2 + length;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return {};
|
|
131
|
+
}
|
|
132
|
+
function loadImageForPdf(url) {
|
|
133
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
134
|
+
const res = yield fetch(url, { mode: 'cors' });
|
|
135
|
+
if (!res.ok)
|
|
136
|
+
throw new Error(`Image fetch failed: ${url} ${res.status}`);
|
|
137
|
+
const arrayBuffer = yield res.arrayBuffer();
|
|
138
|
+
const bytes = new Uint8Array(arrayBuffer);
|
|
139
|
+
const format = getImageFormat(url, res.headers.get('content-type'), bytes);
|
|
140
|
+
const dimensions = getImageDimensions(bytes, format);
|
|
141
|
+
const dataUrl = `data:${getImageMimeType(format)};base64,${bytesToBase64(bytes)}`;
|
|
142
|
+
return Object.assign({ dataUrl, format }, dimensions);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
function resolveImageUrl(object, printBuffer) {
|
|
146
|
+
if (object.type !== 'image')
|
|
147
|
+
return undefined;
|
|
148
|
+
const imgObj = object;
|
|
149
|
+
return imgObj.source && printBuffer ? getProperty(imgObj.source, printBuffer) : imgObj.url;
|
|
150
|
+
}
|
|
151
|
+
function collectImageUrlsFromLayout(layout, printBuffer) {
|
|
152
|
+
const urls = new Set();
|
|
153
|
+
function walk(objects, currentPrintBuffer) {
|
|
154
|
+
objects.forEach((object) => {
|
|
155
|
+
if (object.type === 'image') {
|
|
156
|
+
const imageUrl = resolveImageUrl(object, currentPrintBuffer);
|
|
157
|
+
if (imageUrl)
|
|
158
|
+
urls.add(String(imageUrl));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (object.type !== 'container')
|
|
162
|
+
return;
|
|
163
|
+
const source = object.printOnlyAtEnd === true && (!object.source || object.source === '') ? [currentPrintBuffer] : getProperty(object.source, currentPrintBuffer);
|
|
164
|
+
if (!Array.isArray(source))
|
|
165
|
+
return;
|
|
166
|
+
source.forEach((containerPrintBuffer) => walk(object.children, containerPrintBuffer));
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
walk(layout.objects, printBuffer);
|
|
170
|
+
return [...urls];
|
|
171
|
+
}
|
|
172
|
+
/** Fetch TTF from URL and return as base64. */
|
|
173
|
+
function fetchFontAsBase64(url) {
|
|
174
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
175
|
+
const res = yield fetch(url, { mode: 'cors' });
|
|
176
|
+
if (!res.ok)
|
|
177
|
+
throw new Error(`Font fetch failed: ${url} ${res.status}`);
|
|
178
|
+
const arrayBuffer = yield res.arrayBuffer();
|
|
179
|
+
const bytes = new Uint8Array(arrayBuffer);
|
|
180
|
+
const chunkSize = 8192;
|
|
181
|
+
let binary = '';
|
|
182
|
+
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
183
|
+
const chunk = bytes.subarray(i, Math.min(i + chunkSize, bytes.length));
|
|
184
|
+
binary += String.fromCharCode.apply(null, Array.from(chunk));
|
|
185
|
+
}
|
|
186
|
+
const base64 = binaryToBase64(binary);
|
|
187
|
+
return base64;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
/** Embed custom fonts into jsPDF doc. Only embeds fonts that are actually used. */
|
|
191
|
+
function embedFontsInDoc(doc, usedFontFamilies, options) {
|
|
192
|
+
var _a;
|
|
193
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
194
|
+
for (const family of usedFontFamilies) {
|
|
195
|
+
const def = (_a = options.getPdfFontDefinition) === null || _a === void 0 ? void 0 : _a.call(options, family);
|
|
196
|
+
if (!def) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (def.variants.length === 0) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
for (const variant of def.variants) {
|
|
203
|
+
const vfsName = `${family}-${variant.style}.ttf`;
|
|
204
|
+
const base64 = yield fetchFontAsBase64(variant.url);
|
|
205
|
+
doc.addFileToVFS(vfsName, base64);
|
|
206
|
+
doc.addFont(vfsName, family, variant.style, 'Identity-H');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function truncateTextToFitWidth(doc, text, maxWidth) {
|
|
212
|
+
if (doc.getTextWidth(text) <= maxWidth)
|
|
213
|
+
return text;
|
|
214
|
+
const ELLIPSIS = '...';
|
|
215
|
+
let truncatedText = '';
|
|
216
|
+
for (let i = 0; i < text.length; i++) {
|
|
217
|
+
const tempText = truncatedText + text[i];
|
|
218
|
+
if (doc.getTextWidth(tempText + ELLIPSIS) > maxWidth)
|
|
219
|
+
break;
|
|
220
|
+
truncatedText = tempText;
|
|
221
|
+
}
|
|
222
|
+
return truncatedText + ELLIPSIS;
|
|
223
|
+
}
|
|
224
|
+
function drawSimpleObject(x, y, doc, object, printBuffer, width, height, options, context) {
|
|
225
|
+
var _a, _b, _c;
|
|
226
|
+
let text = ''; // Will hold text to display for either 'text' or 'field'
|
|
227
|
+
// Get text from object for text objects
|
|
228
|
+
if (object.type == 'text') {
|
|
229
|
+
text = options.translate ? options.translate(object.text) : object.text;
|
|
230
|
+
}
|
|
231
|
+
// Get text from printBuffer for field objects
|
|
232
|
+
if (object.type == 'field') {
|
|
233
|
+
if (object.source === 'pageNumber') {
|
|
234
|
+
text = String(doc.getNumberOfPages());
|
|
235
|
+
}
|
|
236
|
+
else if (object.source === 'pageCount') {
|
|
237
|
+
text = String(context.pageCount || 0);
|
|
238
|
+
}
|
|
239
|
+
else if (object.source === 'page') {
|
|
240
|
+
text = String(doc.getNumberOfPages()) + ' / ' + String(context.pageCount || 0);
|
|
241
|
+
}
|
|
242
|
+
else if (object.source && printBuffer) {
|
|
243
|
+
text = getProperty(object.source, printBuffer);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
text = printBuffer;
|
|
247
|
+
}
|
|
248
|
+
if (typeof text == 'number')
|
|
249
|
+
text = '' + text;
|
|
250
|
+
if (object.format == 'date')
|
|
251
|
+
text = formatDate(parseInt(text));
|
|
252
|
+
if (text === undefined || typeof text != 'string') {
|
|
253
|
+
(_a = options.onAlert) === null || _a === void 0 ? void 0 : _a.call(options, { header: 'fieldnotfound', body: object.source, type: 'warning', timeout: 10000 });
|
|
254
|
+
text = '';
|
|
255
|
+
}
|
|
256
|
+
if (object.format == 'currency')
|
|
257
|
+
text = String.fromCharCode(128) + ' ' + parseFloat(text).toFixed(2);
|
|
258
|
+
}
|
|
259
|
+
// Manipulate case as specified in object
|
|
260
|
+
if ((object.type === 'text' || object.type === 'field') && object.case === 1)
|
|
261
|
+
text = text.toUpperCase();
|
|
262
|
+
if ((object.type === 'text' || object.type === 'field') && object.case === 2)
|
|
263
|
+
text = text.toLowerCase();
|
|
264
|
+
// Skip actual drawing in measurement mode or when measuring for fillContainer
|
|
265
|
+
if (context.mode === 'measurement' || context.measureOnly) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
// Draw rectangle for rectangle objects (supports backgroundColor, borderRadius, strokeWidth, strokeColor)
|
|
269
|
+
// fillContainer rectangles are drawn separately as background - skip here to avoid duplicate 1px strip
|
|
270
|
+
if (object.type == 'rectangle') {
|
|
271
|
+
const rectObj = object;
|
|
272
|
+
if (rectObj.fillContainer)
|
|
273
|
+
return;
|
|
274
|
+
const hasFill = rectObj.backgroundColor;
|
|
275
|
+
const radius = (rectObj.borderRadius || 0) / 10; // layout units to mm
|
|
276
|
+
const hasCustomStroke = rectObj.strokeColor != null || rectObj.strokeWidth != null;
|
|
277
|
+
if (hasCustomStroke) {
|
|
278
|
+
if (rectObj.strokeColor) {
|
|
279
|
+
let hex = String(rectObj.strokeColor).replace(/^#/, '');
|
|
280
|
+
if (hex.length === 3)
|
|
281
|
+
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
282
|
+
doc.setDrawColor(parseInt(hex.slice(0, 2), 16) || 0, parseInt(hex.slice(2, 4), 16) || 0, parseInt(hex.slice(4, 6), 16) || 0);
|
|
283
|
+
}
|
|
284
|
+
if (rectObj.strokeWidth != null)
|
|
285
|
+
doc.setLineWidth(rectObj.strokeWidth);
|
|
286
|
+
}
|
|
287
|
+
if (radius > 0 || hasFill) {
|
|
288
|
+
if (hasFill) {
|
|
289
|
+
let hex = String(rectObj.backgroundColor).replace(/^#/, '');
|
|
290
|
+
if (hex.length === 3)
|
|
291
|
+
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
292
|
+
doc.setFillColor(parseInt(hex.slice(0, 2), 16) || 0, parseInt(hex.slice(2, 4), 16) || 0, parseInt(hex.slice(4, 6), 16) || 0);
|
|
293
|
+
}
|
|
294
|
+
if (typeof doc.roundedRect === 'function') {
|
|
295
|
+
const mode = hasFill ? 'FD' : 'S'; // FD = fill + stroke, S = stroke only
|
|
296
|
+
doc.roundedRect(x, y, width, height, radius, radius, mode);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
if (hasFill)
|
|
300
|
+
doc.rect(x, y, width, height, 'F');
|
|
301
|
+
else
|
|
302
|
+
doc.rect(x, y, width, height);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
doc.rect(x, y, width, height);
|
|
307
|
+
}
|
|
308
|
+
if (hasCustomStroke) {
|
|
309
|
+
doc.setDrawColor(0, 0, 0);
|
|
310
|
+
doc.setLineWidth(0.35);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// Draw image for image objects (static url or dynamic source)
|
|
314
|
+
if (object.type == 'image') {
|
|
315
|
+
const imgObj = object;
|
|
316
|
+
const imgUrl = resolveImageUrl(object, printBuffer);
|
|
317
|
+
if (imgUrl) {
|
|
318
|
+
const urlLower = String(imgUrl).toLowerCase();
|
|
319
|
+
const imageAsset = (_b = context.imageAssets) === null || _b === void 0 ? void 0 : _b.get(String(imgUrl));
|
|
320
|
+
const format = (imageAsset === null || imageAsset === void 0 ? void 0 : imageAsset.format) || (urlLower.endsWith('.png') ? 'PNG' : urlLower.endsWith('.jpg') || urlLower.endsWith('.jpeg') ? 'JPEG' : 'JPEG');
|
|
321
|
+
let drawX = x;
|
|
322
|
+
let drawY = y;
|
|
323
|
+
let drawWidth = width;
|
|
324
|
+
let drawHeight = height;
|
|
325
|
+
// Preserve aspect ratio when objectFit is 'contain' and we have dimensions
|
|
326
|
+
if (imgObj.objectFit === 'contain' && (imageAsset === null || imageAsset === void 0 ? void 0 : imageAsset.width) && (imageAsset === null || imageAsset === void 0 ? void 0 : imageAsset.height)) {
|
|
327
|
+
const scale = Math.min(width / imageAsset.width, height / imageAsset.height);
|
|
328
|
+
drawWidth = imageAsset.width * scale;
|
|
329
|
+
drawHeight = imageAsset.height * scale;
|
|
330
|
+
drawX = x + (width - drawWidth) / 2;
|
|
331
|
+
drawY = y + (height - drawHeight) / 2;
|
|
332
|
+
}
|
|
333
|
+
try {
|
|
334
|
+
doc.addImage((imageAsset === null || imageAsset === void 0 ? void 0 : imageAsset.dataUrl) || imgUrl, format, drawX, drawY, drawWidth, drawHeight);
|
|
335
|
+
}
|
|
336
|
+
catch (_e) {
|
|
337
|
+
// Skip if image fails to load (e.g. CORS, invalid URL)
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// Draw text for text and field objets
|
|
342
|
+
if ((object.type == 'text' || object.type == 'field') && text) {
|
|
343
|
+
let textAlign;
|
|
344
|
+
if (object.textAlign == 1)
|
|
345
|
+
textAlign = 'left';
|
|
346
|
+
if (object.textAlign == 2) {
|
|
347
|
+
textAlign = 'center';
|
|
348
|
+
x = x + width / 2;
|
|
349
|
+
}
|
|
350
|
+
if (object.textAlign == 3) {
|
|
351
|
+
textAlign = 'right';
|
|
352
|
+
x = x + width;
|
|
353
|
+
}
|
|
354
|
+
if (object.name == 'qty' && text == '0')
|
|
355
|
+
return;
|
|
356
|
+
const fontStyleStr = FONT_STYLES[object.fontStyle & 3] || 'normal';
|
|
357
|
+
// Use object font if it's a custom font; otherwise layout default overrides standard fonts (Helvetica etc.)
|
|
358
|
+
const fontFamily = object.fontFamily && ((_c = options.isCustomPdfFont) === null || _c === void 0 ? void 0 : _c.call(options, object.fontFamily)) ? object.fontFamily : context.defaultFontFamily || object.fontFamily || 'Helvetica';
|
|
359
|
+
doc.setFont(fontFamily, fontStyleStr);
|
|
360
|
+
doc.setFontSize(object.fontSize / 4);
|
|
361
|
+
doc.text(truncateTextToFitWidth(doc, text, width), x, y, { align: textAlign, baseline: 'hanging' });
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function addPage(originX, originY, doc, layout, rootPrintBuffer, paperSize, options, containerChain, context) {
|
|
365
|
+
if (context.mode === 'measurement') {
|
|
366
|
+
// Track page count in measurement mode
|
|
367
|
+
if (context.currentPageCount === undefined) {
|
|
368
|
+
context.currentPageCount = 1;
|
|
369
|
+
}
|
|
370
|
+
context.currentPageCount++;
|
|
371
|
+
}
|
|
372
|
+
else if (!context.measureOnly) {
|
|
373
|
+
// Actually add page in rendering mode (skip when measuring for fillContainer)
|
|
374
|
+
doc.addPage();
|
|
375
|
+
}
|
|
376
|
+
if (!context.measureOnly) {
|
|
377
|
+
drawStaticPartOfPage(doc, layout, rootPrintBuffer, paperSize, options, context);
|
|
378
|
+
}
|
|
379
|
+
const absoluteLowerRightHandSide = { x: originX / 10, y: originY / 10 };
|
|
380
|
+
// Now draw all the parent containers of the current container
|
|
381
|
+
if (containerChain.length > 0) {
|
|
382
|
+
// Let origin start at (x,y) of the outermost container
|
|
383
|
+
originX = containerChain[0].object.x;
|
|
384
|
+
originY = containerChain[0].object.y;
|
|
385
|
+
absoluteLowerRightHandSide.x = originX / 10;
|
|
386
|
+
absoluteLowerRightHandSide.y = originY / 10;
|
|
387
|
+
containerChain.forEach((link) => {
|
|
388
|
+
link.object.children.forEach((child) => {
|
|
389
|
+
if (!child.snapToBottom && (child.type != 'container' || child.repeatOnOverflow)) {
|
|
390
|
+
const childLowerRightHandSide = addObjectToPDF(originX, originY, doc, child, link.printBuffer, paperSize, layout, options, rootPrintBuffer, [], context);
|
|
391
|
+
absoluteLowerRightHandSide.x = Math.max(absoluteLowerRightHandSide.x, childLowerRightHandSide.x);
|
|
392
|
+
absoluteLowerRightHandSide.y = Math.max(absoluteLowerRightHandSide.y, childLowerRightHandSide.y);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
if (link.object.repeatContainer == 'horizontal')
|
|
396
|
+
originX = absoluteLowerRightHandSide.x * 10;
|
|
397
|
+
if (link.object.repeatContainer == 'vertical')
|
|
398
|
+
originY = absoluteLowerRightHandSide.y * 10;
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
return { originX: originX, originY: originY, x: absoluteLowerRightHandSide.x, y: absoluteLowerRightHandSide.y };
|
|
402
|
+
}
|
|
403
|
+
function drawBottomDwellers(object, originX, originY, absoluteLowerRightHandSide, bottomChildY, paperSize, doc, layout, rootPrintBuffer, options, newContainerChain, container, context) {
|
|
404
|
+
let childRelativeToBottomDrawn = false;
|
|
405
|
+
let firstBottomRelativeChildY = Math.min(...object.children.filter((child) => child.snapToBottom).map((child) => child.y));
|
|
406
|
+
object.children.forEach(function (child) {
|
|
407
|
+
if (child.snapToBottom) {
|
|
408
|
+
// Objects relative to bottom/right
|
|
409
|
+
if (!childRelativeToBottomDrawn)
|
|
410
|
+
originY = absoluteLowerRightHandSide.y * 10;
|
|
411
|
+
childRelativeToBottomDrawn = true;
|
|
412
|
+
// child.text = '======'+originX+':'+originY+'======'
|
|
413
|
+
if (object.pageBreak || originY + object.height - bottomChildY > paperSize.height - paperSize.footerHeight) {
|
|
414
|
+
// We ran out of paper
|
|
415
|
+
const newOrigin = addPage(originX, originY, doc, layout, rootPrintBuffer, paperSize, options, newContainerChain, context);
|
|
416
|
+
originX = newOrigin.originX;
|
|
417
|
+
originY = newOrigin.originY;
|
|
418
|
+
absoluteLowerRightHandSide = { x: newOrigin.x, y: newOrigin.y };
|
|
419
|
+
firstBottomRelativeChildY = 0;
|
|
420
|
+
}
|
|
421
|
+
// originY = absoluteLowerRightHandSide.y * 10
|
|
422
|
+
const childLowerRightHandSide = addObjectToPDF(originX, originY - firstBottomRelativeChildY, doc, child, container, paperSize, layout, options, rootPrintBuffer, [], context);
|
|
423
|
+
absoluteLowerRightHandSide.x = Math.max(absoluteLowerRightHandSide.x, childLowerRightHandSide.x);
|
|
424
|
+
absoluteLowerRightHandSide.y = Math.max(absoluteLowerRightHandSide.y, childLowerRightHandSide.y);
|
|
425
|
+
absoluteLowerRightHandSide.y = Math.max(absoluteLowerRightHandSide.y, (originY + object.height - firstBottomRelativeChildY) / 10);
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
if (childRelativeToBottomDrawn)
|
|
429
|
+
originY = absoluteLowerRightHandSide.y * 10;
|
|
430
|
+
return { originX: originX, originY: originY, x: absoluteLowerRightHandSide.x, y: absoluteLowerRightHandSide.y };
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Add an object to the PDF document.
|
|
434
|
+
*
|
|
435
|
+
* Use originX,originY as origin to displace the object's own x,y coordinates
|
|
436
|
+
*/
|
|
437
|
+
addObjectToPDF = function (originX, originY, doc, object, printBuffer, paperSize, layout, options, rootPrintBuffer, containerChain, context) {
|
|
438
|
+
var _a;
|
|
439
|
+
const x = (originX + object.x) / 10;
|
|
440
|
+
const y = (originY + object.y) / 10;
|
|
441
|
+
const width = object.width / 10;
|
|
442
|
+
const height = object.height / 10;
|
|
443
|
+
// Keep track of the lower righthandside of all objects in this container
|
|
444
|
+
// We need to return this object so whatever calls addObjectToPDF knows how big our drawing was
|
|
445
|
+
let absoluteLowerRightHandSide = { x: x + width, y: y + height };
|
|
446
|
+
if (!object.active)
|
|
447
|
+
return absoluteLowerRightHandSide;
|
|
448
|
+
if (object.type != 'container')
|
|
449
|
+
drawSimpleObject(x, y, doc, object, printBuffer, width, height, options, context);
|
|
450
|
+
// Recursively draw all child objects from a container object
|
|
451
|
+
if (object.type == 'container') {
|
|
452
|
+
let originX = x * 10, originY = y * 10;
|
|
453
|
+
const isPrintOnlyAtEnd = object.printOnlyAtEnd === true;
|
|
454
|
+
const source = isPrintOnlyAtEnd && (!object.source || object.source === '') ? [printBuffer] : getProperty(object.source, printBuffer);
|
|
455
|
+
if (source === undefined) {
|
|
456
|
+
(_a = options.onAlert) === null || _a === void 0 ? void 0 : _a.call(options, { header: 'containernotfound', body: object.source, type: 'warning', timeout: 10000 });
|
|
457
|
+
return absoluteLowerRightHandSide;
|
|
458
|
+
}
|
|
459
|
+
const nrContainers = source.length;
|
|
460
|
+
for (let containerIndex = 0; containerIndex < nrContainers; containerIndex++) {
|
|
461
|
+
const newContainerChain = containerChain.filter(function () {
|
|
462
|
+
return true;
|
|
463
|
+
});
|
|
464
|
+
const container = source[containerIndex];
|
|
465
|
+
absoluteLowerRightHandSide = { x: originX / 10, y: originY / 10 };
|
|
466
|
+
const minHeightMm = object.minHeightBeforeBreak;
|
|
467
|
+
const useOrphanCheck = minHeightMm != null && minHeightMm > 0;
|
|
468
|
+
if (useOrphanCheck && (object.pageBreak || originY + minHeightMm > paperSize.height - paperSize.footerHeight)) {
|
|
469
|
+
// We ran out of paper
|
|
470
|
+
originX = x * 10;
|
|
471
|
+
originY = y * 10;
|
|
472
|
+
const newOrigin = addPage(originX, originY, doc, layout, rootPrintBuffer, paperSize, options, newContainerChain, context);
|
|
473
|
+
originX = newOrigin.originX;
|
|
474
|
+
originY = newOrigin.originY;
|
|
475
|
+
absoluteLowerRightHandSide = { x: newOrigin.x, y: newOrigin.y };
|
|
476
|
+
}
|
|
477
|
+
// If container has fillContainer rectangles, measure bounds first then draw them as background
|
|
478
|
+
const fillContainerRects = object.children.filter((c) => c.type === 'rectangle' && c.fillContainer);
|
|
479
|
+
if (fillContainerRects.length > 0 && !context.measureOnly) {
|
|
480
|
+
const measureContext = Object.assign(Object.assign({}, context), { measureOnly: true });
|
|
481
|
+
const measureOriginX = originX;
|
|
482
|
+
const measureOriginY = originY;
|
|
483
|
+
const measureBounds = { x: originX / 10, y: originY / 10 };
|
|
484
|
+
let measureBottomChildY = 0;
|
|
485
|
+
object.children.forEach(function (child) {
|
|
486
|
+
if (child.type != 'container' && !child.snapToBottom) {
|
|
487
|
+
measureBottomChildY = Math.max(measureBottomChildY, child.y + child.height);
|
|
488
|
+
const r = addObjectToPDF(measureOriginX, measureOriginY, doc, child, container, paperSize, layout, options, rootPrintBuffer, [], measureContext);
|
|
489
|
+
measureBounds.x = Math.max(measureBounds.x, r.x);
|
|
490
|
+
measureBounds.y = Math.max(measureBounds.y, r.y);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
const measureChain = [...newContainerChain, { object: object, printBuffer: container }];
|
|
494
|
+
object.children.forEach(function (child) {
|
|
495
|
+
if (child.type == 'container' && !child.snapToBottom) {
|
|
496
|
+
measureBottomChildY = Math.max(measureBottomChildY, child.y + child.height);
|
|
497
|
+
const r = addObjectToPDF(measureOriginX, measureOriginY, doc, child, container, paperSize, layout, options, rootPrintBuffer, measureChain, measureContext);
|
|
498
|
+
measureBounds.x = Math.max(measureBounds.x, r.x);
|
|
499
|
+
measureBounds.y = Math.max(measureBounds.y, r.y);
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
const measureBottom = drawBottomDwellers(object, measureOriginX, measureOriginY, { x: measureBounds.x, y: measureBounds.y }, measureBottomChildY, paperSize, doc, layout, rootPrintBuffer, options, measureChain, container, measureContext);
|
|
503
|
+
measureBounds.y = Math.max(measureBounds.y, measureBottom.y);
|
|
504
|
+
// Draw fillContainer rectangles as background
|
|
505
|
+
fillContainerRects.forEach(function (rect) {
|
|
506
|
+
const rx = (measureOriginX + rect.x) / 10;
|
|
507
|
+
const ry = (measureOriginY + rect.y) / 10;
|
|
508
|
+
const rw = rect.width / 10;
|
|
509
|
+
const rh = measureBounds.y - ry;
|
|
510
|
+
if (rh > 0) {
|
|
511
|
+
const rectObj = rect;
|
|
512
|
+
const hasFill = rectObj.backgroundColor;
|
|
513
|
+
const radius = (rectObj.borderRadius || 0) / 10;
|
|
514
|
+
const hasCustomStroke = rectObj.strokeColor != null || rectObj.strokeWidth != null;
|
|
515
|
+
if (hasCustomStroke) {
|
|
516
|
+
if (rectObj.strokeColor) {
|
|
517
|
+
let hex = String(rectObj.strokeColor).replace(/^#/, '');
|
|
518
|
+
if (hex.length === 3)
|
|
519
|
+
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
520
|
+
doc.setDrawColor(parseInt(hex.slice(0, 2), 16) || 0, parseInt(hex.slice(2, 4), 16) || 0, parseInt(hex.slice(4, 6), 16) || 0);
|
|
521
|
+
}
|
|
522
|
+
if (rectObj.strokeWidth != null)
|
|
523
|
+
doc.setLineWidth(rectObj.strokeWidth);
|
|
524
|
+
}
|
|
525
|
+
if (hasFill) {
|
|
526
|
+
let hex = String(rectObj.backgroundColor).replace(/^#/, '');
|
|
527
|
+
if (hex.length === 3)
|
|
528
|
+
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
529
|
+
doc.setFillColor(parseInt(hex.slice(0, 2), 16) || 0, parseInt(hex.slice(2, 4), 16) || 0, parseInt(hex.slice(4, 6), 16) || 0);
|
|
530
|
+
}
|
|
531
|
+
if (typeof doc.roundedRect === 'function') {
|
|
532
|
+
doc.roundedRect(rx, ry, rw, rh, radius, radius, hasFill ? 'FD' : 'S');
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
if (hasFill)
|
|
536
|
+
doc.rect(rx, ry, rw, rh, 'F');
|
|
537
|
+
else
|
|
538
|
+
doc.rect(rx, ry, rw, rh);
|
|
539
|
+
}
|
|
540
|
+
if (hasCustomStroke) {
|
|
541
|
+
doc.setDrawColor(0, 0, 0);
|
|
542
|
+
doc.setLineWidth(0.35);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
// normal objects first, then containers
|
|
548
|
+
let bottomChildY = 0;
|
|
549
|
+
object.children.forEach(function (child) {
|
|
550
|
+
if (child.type != 'container' && !child.snapToBottom) {
|
|
551
|
+
// Objects relative to top/left
|
|
552
|
+
bottomChildY = Math.max(bottomChildY, child.y + child.height);
|
|
553
|
+
const childLowerRightHandSide = addObjectToPDF(originX, originY, doc, child, container, paperSize, layout, options, rootPrintBuffer, [], context);
|
|
554
|
+
absoluteLowerRightHandSide.x = Math.max(absoluteLowerRightHandSide.x, childLowerRightHandSide.x);
|
|
555
|
+
absoluteLowerRightHandSide.y = Math.max(absoluteLowerRightHandSide.y, childLowerRightHandSide.y);
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
newContainerChain.push({ object: object, printBuffer: container });
|
|
559
|
+
object.children.forEach(function (child) {
|
|
560
|
+
if (child.type == 'container' && !child.snapToBottom) {
|
|
561
|
+
bottomChildY = Math.max(bottomChildY, child.y + child.height);
|
|
562
|
+
const childLowerRightHandSide = addObjectToPDF(originX, originY, doc, child, container, paperSize, layout, options, rootPrintBuffer, newContainerChain, context);
|
|
563
|
+
absoluteLowerRightHandSide.x = Math.max(absoluteLowerRightHandSide.x, childLowerRightHandSide.x);
|
|
564
|
+
absoluteLowerRightHandSide.y = Math.max(absoluteLowerRightHandSide.y, childLowerRightHandSide.y);
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
if (containerIndex < nrContainers - 1) {
|
|
568
|
+
if (object.repeatContainer == 'horizontal')
|
|
569
|
+
originX = absoluteLowerRightHandSide.x * 10;
|
|
570
|
+
if (object.repeatContainer == 'vertical')
|
|
571
|
+
originY = absoluteLowerRightHandSide.y * 10;
|
|
572
|
+
if (object.pageBreak || originY + object.height - bottomChildY > paperSize.height - paperSize.footerHeight) {
|
|
573
|
+
// We ran out of paper
|
|
574
|
+
const newOrigin = addPage(originX, originY, doc, layout, rootPrintBuffer, paperSize, options, newContainerChain, context);
|
|
575
|
+
originX = newOrigin.originX;
|
|
576
|
+
originY = newOrigin.originY;
|
|
577
|
+
absoluteLowerRightHandSide = { x: newOrigin.x, y: newOrigin.y };
|
|
578
|
+
const newestOrigin = drawBottomDwellers(object, originX, originY, absoluteLowerRightHandSide, bottomChildY, paperSize, doc, layout, rootPrintBuffer, options, newContainerChain, container, context);
|
|
579
|
+
originX = newestOrigin.originX;
|
|
580
|
+
originY = newestOrigin.originY;
|
|
581
|
+
absoluteLowerRightHandSide = { x: newestOrigin.x, y: newestOrigin.y };
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
const newestOrigin = drawBottomDwellers(object, originX, originY, absoluteLowerRightHandSide, bottomChildY, paperSize, doc, layout, rootPrintBuffer, options, newContainerChain, container, context);
|
|
586
|
+
originX = newestOrigin.originX;
|
|
587
|
+
originY = newestOrigin.originY;
|
|
588
|
+
absoluteLowerRightHandSide = { x: newestOrigin.x, y: newestOrigin.y };
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
return absoluteLowerRightHandSide;
|
|
592
|
+
};
|
|
593
|
+
/**
|
|
594
|
+
* Draw all non-container objects on the page
|
|
595
|
+
*/
|
|
596
|
+
drawStaticPartOfPage = function (doc, layout, printBuffer, paperSize, options, context) {
|
|
597
|
+
layout.objects.forEach(function (object) {
|
|
598
|
+
if (object.type != 'container') {
|
|
599
|
+
addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], context);
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
};
|
|
603
|
+
function sortVertical(objects) {
|
|
604
|
+
objects.sort((a, b) => a.y - b.y);
|
|
605
|
+
objects.forEach((element) => {
|
|
606
|
+
if (element.children)
|
|
607
|
+
sortVertical(element.children);
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
function genPDF(layout, printBuffer, options = {}) {
|
|
611
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
612
|
+
// Make a deep copy of the layout so we can add temporary properties
|
|
613
|
+
layout = (0, deepCopy_1.default)(layout);
|
|
614
|
+
sortVertical(layout.objects);
|
|
615
|
+
// Load images for static images
|
|
616
|
+
layout.objects.forEach((object) => {
|
|
617
|
+
var _a;
|
|
618
|
+
if (object.type != 'image')
|
|
619
|
+
return;
|
|
620
|
+
if (object.name == 'Logo') {
|
|
621
|
+
const logoUrl = (_a = options.resolveLogoUrl) === null || _a === void 0 ? void 0 : _a.call(options);
|
|
622
|
+
if (logoUrl)
|
|
623
|
+
object.url = logoUrl;
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
// Preload image bytes for Node.js and dimensions for objectFit:contain
|
|
627
|
+
const imageAssets = new Map();
|
|
628
|
+
const loadImage = options.loadImage || loadImageForPdf;
|
|
629
|
+
const urls = collectImageUrlsFromLayout(layout, printBuffer);
|
|
630
|
+
yield Promise.all(urls.map((url) => __awaiter(this, void 0, void 0, function* () {
|
|
631
|
+
try {
|
|
632
|
+
const asset = yield loadImage(url);
|
|
633
|
+
if (asset)
|
|
634
|
+
imageAssets.set(url, asset);
|
|
635
|
+
}
|
|
636
|
+
catch (_a) {
|
|
637
|
+
// Ignore load failures; jsPDF will still get the original URL as fallback
|
|
638
|
+
}
|
|
639
|
+
})));
|
|
640
|
+
const paperSize = layout.paperSize;
|
|
641
|
+
const paperSizeCopy = Object.assign({}, paperSize);
|
|
642
|
+
paperSize.width *= 10;
|
|
643
|
+
paperSize.height *= 10;
|
|
644
|
+
paperSize.footerHeight *= 10;
|
|
645
|
+
// First pass: Measurement mode - count pages without rendering
|
|
646
|
+
const measurementContext = {
|
|
647
|
+
mode: 'measurement',
|
|
648
|
+
currentPageCount: 1, // Start with page 1
|
|
649
|
+
};
|
|
650
|
+
// Create a minimal jsPDF instance for measurement (needed for some calculations)
|
|
651
|
+
const measurementDoc = new jspdf_1.jsPDF({
|
|
652
|
+
orientation: paperSizeCopy.width > paperSizeCopy.height ? 'landscape' : 'portrait',
|
|
653
|
+
format: [paperSizeCopy.width, paperSizeCopy.height],
|
|
654
|
+
});
|
|
655
|
+
const usedFonts = collectUsedFontsFromLayout(layout, options);
|
|
656
|
+
if (usedFonts.size > 0) {
|
|
657
|
+
yield embedFontsInDoc(measurementDoc, usedFonts, options);
|
|
658
|
+
}
|
|
659
|
+
drawStaticPartOfPage(measurementDoc, layout, printBuffer, paperSize, options, measurementContext);
|
|
660
|
+
// Draw containers in measurement mode (same order as render: normal first, then printOnlyAtEnd on fresh page)
|
|
661
|
+
layout.objects
|
|
662
|
+
.filter((object) => object.type == 'container' && !object.printOnlyAtEnd)
|
|
663
|
+
.forEach((object) => {
|
|
664
|
+
addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext);
|
|
665
|
+
});
|
|
666
|
+
layout.objects
|
|
667
|
+
.filter((object) => object.type == 'container' && object.printOnlyAtEnd)
|
|
668
|
+
.forEach((object) => {
|
|
669
|
+
addObjectToPDF(0, 0, measurementDoc, object, printBuffer, paperSize, layout, options, printBuffer, [], measurementContext);
|
|
670
|
+
});
|
|
671
|
+
// Get total page count from measurement pass
|
|
672
|
+
const totalPageCount = measurementContext.currentPageCount || 1;
|
|
673
|
+
// Second pass: Rendering mode - actual PDF generation with pageCount available
|
|
674
|
+
const doc = new jspdf_1.jsPDF({
|
|
675
|
+
orientation: paperSizeCopy.width > paperSizeCopy.height ? 'landscape' : 'portrait',
|
|
676
|
+
format: [paperSizeCopy.width, paperSizeCopy.height],
|
|
677
|
+
});
|
|
678
|
+
if (usedFonts.size > 0) {
|
|
679
|
+
yield embedFontsInDoc(doc, usedFonts, options);
|
|
680
|
+
}
|
|
681
|
+
const renderContext = {
|
|
682
|
+
mode: 'rendering',
|
|
683
|
+
pageCount: totalPageCount,
|
|
684
|
+
imageAssets,
|
|
685
|
+
defaultFontFamily: layout.defaultFontFamily,
|
|
686
|
+
};
|
|
687
|
+
drawStaticPartOfPage(doc, layout, printBuffer, paperSize, options, renderContext);
|
|
688
|
+
// Draw containers in rendering mode (normal containers first)
|
|
689
|
+
layout.objects
|
|
690
|
+
.filter((object) => object.type == 'container' && !object.printOnlyAtEnd)
|
|
691
|
+
.forEach((object) => {
|
|
692
|
+
addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext);
|
|
693
|
+
});
|
|
694
|
+
// Draw printOnlyAtEnd containers once at the end (at their layout coordinates on current page)
|
|
695
|
+
const printOnlyAtEndContainers = layout.objects.filter((object) => object.type == 'container' && object.printOnlyAtEnd);
|
|
696
|
+
printOnlyAtEndContainers.forEach((object) => {
|
|
697
|
+
addObjectToPDF(0, 0, doc, object, printBuffer, paperSize, layout, options, printBuffer, [], renderContext);
|
|
698
|
+
});
|
|
699
|
+
return doc;
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
exports.genPDF = genPDF;
|
|
703
|
+
exports.default = {
|
|
704
|
+
genPDF,
|
|
705
|
+
};
|