@softwear/latestcollectioncore 1.0.175 → 1.0.177

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