@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.
@@ -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
+ };