@pdfme/common 6.1.1 → 6.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,8 +1,73 @@
1
1
  import { z } from "zod";
2
2
  import { Buffer } from "buffer";
3
+ import { PDFName } from "@pdfme/pdf-lib";
3
4
  import * as acorn from "acorn";
4
5
  //#region src/version.ts
5
- var PDFME_VERSION = "6.1.1";
6
+ var PDFME_VERSION = "6.1.2";
7
+ //#endregion
8
+ //#region src/pageSize.ts
9
+ var PAGE_SIZE_PRESETS = {
10
+ A3: {
11
+ width: 297,
12
+ height: 420
13
+ },
14
+ A4: {
15
+ width: 210,
16
+ height: 297
17
+ },
18
+ A5: {
19
+ width: 148,
20
+ height: 210
21
+ },
22
+ A6: {
23
+ width: 105,
24
+ height: 148
25
+ },
26
+ B4: {
27
+ width: 250,
28
+ height: 353
29
+ },
30
+ B5: {
31
+ width: 176,
32
+ height: 250
33
+ },
34
+ B6: {
35
+ width: 125,
36
+ height: 176
37
+ },
38
+ Letter: {
39
+ width: 215.9,
40
+ height: 279.4
41
+ },
42
+ Legal: {
43
+ width: 215.9,
44
+ height: 355.6
45
+ },
46
+ Tabloid: {
47
+ width: 279.4,
48
+ height: 431.8
49
+ }
50
+ };
51
+ var resolvePageSize = (size = "A4", orientation = "portrait") => {
52
+ const resolved = typeof size === "string" ? PAGE_SIZE_PRESETS[size] : {
53
+ width: size.width,
54
+ height: size.height
55
+ };
56
+ if (!resolved) throw new Error(`@pdfme/common: unknown page size preset "${String(size)}"`);
57
+ if (orientation === "landscape") return {
58
+ width: resolved.height,
59
+ height: resolved.width
60
+ };
61
+ return {
62
+ width: resolved.width,
63
+ height: resolved.height
64
+ };
65
+ };
66
+ var detectPaperSize = (width, height, tolerance = 2) => {
67
+ const entries = Object.entries(PAGE_SIZE_PRESETS);
68
+ for (const [name, size] of entries) if (Math.abs(width - size.width) <= tolerance && Math.abs(height - size.height) <= tolerance || Math.abs(width - size.height) <= tolerance && Math.abs(height - size.width) <= tolerance) return `${name} ${width < height ? "portrait" : "landscape"}`;
69
+ return null;
70
+ };
6
71
  //#endregion
7
72
  //#region src/constants.ts
8
73
  var PT_TO_PX_RATIO = 1.333;
@@ -13,8 +78,7 @@ var ZOOM = 3.7795275591;
13
78
  * Blank A4 PDF size.
14
79
  */
15
80
  var BLANK_A4_PDF = {
16
- width: 210,
17
- height: 297,
81
+ ...PAGE_SIZE_PRESETS.A4,
18
82
  padding: [
19
83
  10,
20
84
  10,
@@ -104,6 +168,9 @@ z.object({
104
168
  "schemas.text.max": z.string(),
105
169
  "schemas.text.fit": z.string(),
106
170
  "schemas.text.dynamicFontSize": z.string(),
171
+ "schemas.text.overflow": z.string(),
172
+ "schemas.text.overflowVisible": z.string(),
173
+ "schemas.text.overflowExpand": z.string(),
107
174
  "schemas.text.format": z.string(),
108
175
  "schemas.text.plain": z.string(),
109
176
  "schemas.text.inlineMarkdown": z.string(),
@@ -156,6 +223,11 @@ z.object({
156
223
  height: z.number(),
157
224
  width: z.number()
158
225
  });
226
+ var DynamicLayoutSplitRange = z.object({
227
+ unit: z.string().min(1),
228
+ start: z.number(),
229
+ end: z.number().optional()
230
+ });
159
231
  var Schema = z.object({
160
232
  name: z.string(),
161
233
  type: z.string(),
@@ -170,14 +242,7 @@ var Schema = z.object({
170
242
  opacity: z.number().optional(),
171
243
  readOnly: z.boolean().optional(),
172
244
  required: z.boolean().optional(),
173
- __bodyRange: z.object({
174
- start: z.number(),
175
- end: z.number().optional()
176
- }).optional(),
177
- __itemRange: z.object({
178
- start: z.number(),
179
- end: z.number().optional()
180
- }).optional(),
245
+ __splitRange: DynamicLayoutSplitRange.optional(),
181
246
  __isSplit: z.boolean().optional()
182
247
  }).passthrough();
183
248
  var SchemaForUIAdditionalInfo = z.object({ id: z.string() });
@@ -365,6 +430,130 @@ var isUrlSafeToFetch = (urlString) => {
365
430
  }
366
431
  return true;
367
432
  };
433
+ var SAFE_LINK_URI_PROTOCOLS = new Set([
434
+ "http:",
435
+ "https:",
436
+ "mailto:"
437
+ ]);
438
+ var INTERNAL_LINK_CACHE_KEY = "pdfme-internal-link-cache";
439
+ var normalizeSafeLinkUri = (uri) => {
440
+ const trimmed = uri.trim();
441
+ if (!trimmed) return void 0;
442
+ let parsed;
443
+ try {
444
+ parsed = new URL(trimmed);
445
+ } catch {
446
+ return;
447
+ }
448
+ return SAFE_LINK_URI_PROTOCOLS.has(parsed.protocol.toLowerCase()) ? trimmed : void 0;
449
+ };
450
+ var getInternalLinkTarget = (href) => {
451
+ const trimmed = href.trim();
452
+ if (!trimmed.startsWith("#") || trimmed.length === 1) return void 0;
453
+ let target = trimmed.slice(1);
454
+ try {
455
+ target = decodeURIComponent(target);
456
+ } catch {}
457
+ target = target.trim();
458
+ if (!target || Array.from(target).some((char) => {
459
+ const code = char.charCodeAt(0);
460
+ return code < 32 || code === 127;
461
+ })) return;
462
+ return target;
463
+ };
464
+ var normalizeInternalLinkHref = (href) => {
465
+ const target = getInternalLinkTarget(href);
466
+ return target ? `#${target}` : void 0;
467
+ };
468
+ var normalizeLinkHref = (href) => normalizeSafeLinkUri(href) ?? normalizeInternalLinkHref(href);
469
+ var getInternalLinkCache = (_cache) => {
470
+ let cache = _cache.get(INTERNAL_LINK_CACHE_KEY);
471
+ if (!cache) {
472
+ cache = {
473
+ anchors: /* @__PURE__ */ new Map(),
474
+ annotations: []
475
+ };
476
+ _cache.set(INTERNAL_LINK_CACHE_KEY, cache);
477
+ }
478
+ return cache;
479
+ };
480
+ var resetInternalLinkAnnotations = (_cache) => {
481
+ _cache.set(INTERNAL_LINK_CACHE_KEY, {
482
+ anchors: /* @__PURE__ */ new Map(),
483
+ annotations: []
484
+ });
485
+ };
486
+ var registerInternalLinkAnchor = (arg) => {
487
+ const { _cache, name, page, x, y } = arg;
488
+ if (!name) return;
489
+ const cache = getInternalLinkCache(_cache);
490
+ const anchors = cache.anchors.get(name) ?? [];
491
+ anchors.push({
492
+ name,
493
+ page,
494
+ x,
495
+ y
496
+ });
497
+ cache.anchors.set(name, anchors);
498
+ };
499
+ var registerInternalLinkAnnotation = (arg) => {
500
+ const { _cache, page, targetName, rect, borderWidth } = arg;
501
+ if (!targetName || rect.width <= 0 || rect.height <= 0) return;
502
+ getInternalLinkCache(_cache).annotations.push({
503
+ page,
504
+ targetName,
505
+ rect,
506
+ borderWidth
507
+ });
508
+ };
509
+ var addGoToLinkAnnotation = (arg) => {
510
+ const { pdfDoc, page, target, rect, borderWidth = 0 } = arg;
511
+ if (rect.width <= 0 || rect.height <= 0) return;
512
+ const annotationRef = pdfDoc.context.register(pdfDoc.context.obj({
513
+ Type: PDFName.of("Annot"),
514
+ Subtype: PDFName.of("Link"),
515
+ Rect: [
516
+ rect.x,
517
+ rect.y,
518
+ rect.x + rect.width,
519
+ rect.y + rect.height
520
+ ],
521
+ Border: [
522
+ 0,
523
+ 0,
524
+ borderWidth
525
+ ],
526
+ A: {
527
+ Type: PDFName.of("Action"),
528
+ S: PDFName.of("GoTo"),
529
+ D: [
530
+ target.page.ref,
531
+ PDFName.of("XYZ"),
532
+ target.x,
533
+ target.y,
534
+ null
535
+ ]
536
+ }
537
+ }));
538
+ page.node.addAnnot(annotationRef);
539
+ };
540
+ var applyInternalLinkAnnotations = (arg) => {
541
+ const { _cache, pdfDoc } = arg;
542
+ const cache = getInternalLinkCache(_cache);
543
+ cache.annotations.forEach((annotation) => {
544
+ const anchors = cache.anchors.get(annotation.targetName) ?? [];
545
+ if (anchors.length === 0) throw new Error(`[@pdfme/generator] Internal link target "#${annotation.targetName}" was not found.`);
546
+ if (anchors.length > 1) throw new Error(`[@pdfme/generator] Internal link target "#${annotation.targetName}" is ambiguous because multiple schemas use that name.`);
547
+ addGoToLinkAnnotation({
548
+ pdfDoc,
549
+ page: annotation.page,
550
+ target: anchors[0],
551
+ rect: annotation.rect,
552
+ borderWidth: annotation.borderWidth
553
+ });
554
+ });
555
+ resetInternalLinkAnnotations(_cache);
556
+ };
368
557
  var getB64BasePdf = async (customPdf) => {
369
558
  if (typeof customPdf === "string" && !customPdf.startsWith("data:application/pdf;") && typeof window !== "undefined") {
370
559
  if (!isUrlSafeToFetch(customPdf)) throw Error("[@pdfme/common] Invalid or unsafe URL for basePdf. Only http: and https: URLs pointing to public hosts are allowed.");
@@ -446,200 +635,6 @@ var checkGenerateProps = (data) => {
446
635
  checkProps(data, GenerateProps);
447
636
  };
448
637
  //#endregion
449
- //#region src/dynamicTemplate.ts
450
- /** Floating point tolerance for comparisons */
451
- var EPSILON = .01;
452
- /** Calculate the content height of a page (drawable area excluding padding) */
453
- var getContentHeight = (basePdf) => basePdf.height - basePdf.padding[0] - basePdf.padding[2];
454
- /** Get the input value for a schema */
455
- var getSchemaValue = (schema, input) => (schema.readOnly ? schema.content : input?.[schema.name]) || "";
456
- /**
457
- * Normalize schemas within a single page into layout items.
458
- * Returns items sorted by Y coordinate with their order preserved.
459
- */
460
- function normalizePageSchemas(pageSchemas, paddingTop) {
461
- const items = [];
462
- const orderMap = /* @__PURE__ */ new Map();
463
- pageSchemas.forEach((schema, index) => {
464
- const localY = Math.max(0, schema.position.y - paddingTop);
465
- items.push({
466
- schema: cloneDeep(schema),
467
- baseY: localY,
468
- height: schema.height,
469
- dynamicLayout: { heights: [schema.height] }
470
- });
471
- orderMap.set(schema.name, index);
472
- });
473
- items.sort((a, b) => {
474
- if (Math.abs(a.baseY - b.baseY) > EPSILON) return a.baseY - b.baseY;
475
- return (orderMap.get(a.schema.name) ?? 0) - (orderMap.get(b.schema.name) ?? 0);
476
- });
477
- return {
478
- items,
479
- orderMap
480
- };
481
- }
482
- /**
483
- * Place height units on pages, splitting across pages as needed.
484
- * @returns The final global Y coordinate after placement
485
- */
486
- function placeUnitsOnPages(schema, dynamicLayout, startGlobalY, contentHeight, paddingTop, pages) {
487
- const dynamicHeights = dynamicLayout.heights;
488
- let currentUnitIndex = 0;
489
- let currentPageIndex = Math.floor(startGlobalY / contentHeight);
490
- let currentYInPage = startGlobalY % contentHeight;
491
- if (currentYInPage < 0) currentYInPage = 0;
492
- let actualGlobalEndY = 0;
493
- const isSplittable = dynamicHeights.length > 1;
494
- while (currentUnitIndex < dynamicHeights.length) {
495
- while (pages.length <= currentPageIndex) pages.push([]);
496
- const spaceLeft = contentHeight - currentYInPage;
497
- if (dynamicHeights[currentUnitIndex] > spaceLeft + EPSILON) {
498
- if (!(Math.abs(spaceLeft - contentHeight) <= EPSILON)) {
499
- currentPageIndex++;
500
- currentYInPage = 0;
501
- continue;
502
- }
503
- }
504
- let chunkHeight = 0;
505
- const startUnitIndex = currentUnitIndex;
506
- while (currentUnitIndex < dynamicHeights.length) {
507
- const h = dynamicHeights[currentUnitIndex];
508
- if (currentYInPage + chunkHeight + h <= contentHeight + EPSILON) {
509
- chunkHeight += h;
510
- currentUnitIndex++;
511
- } else break;
512
- }
513
- const isAtPageTop = currentYInPage <= EPSILON;
514
- if (dynamicLayout.avoidFirstUnitOnly && isSplittable && startUnitIndex === 0 && currentUnitIndex === 1 && dynamicHeights.length > 1 && !isAtPageTop) {
515
- currentUnitIndex = 0;
516
- currentPageIndex++;
517
- currentYInPage = 0;
518
- continue;
519
- }
520
- if (currentUnitIndex === startUnitIndex) {
521
- chunkHeight += dynamicHeights[currentUnitIndex];
522
- currentUnitIndex++;
523
- }
524
- const patch = dynamicLayout.patchSplitSchema?.({
525
- schema,
526
- start: startUnitIndex,
527
- end: currentUnitIndex,
528
- isSplit: startUnitIndex > 0,
529
- chunkHeight
530
- }) ?? {};
531
- const newSchema = {
532
- ...schema,
533
- ...patch,
534
- height: chunkHeight,
535
- position: {
536
- ...schema.position,
537
- y: currentYInPage + paddingTop
538
- }
539
- };
540
- pages[currentPageIndex].push(newSchema);
541
- currentYInPage += chunkHeight;
542
- if (currentYInPage >= contentHeight - EPSILON) {
543
- currentPageIndex++;
544
- currentYInPage = 0;
545
- }
546
- actualGlobalEndY = currentPageIndex * contentHeight + currentYInPage;
547
- }
548
- return actualGlobalEndY;
549
- }
550
- /** Sort elements within each page by their original order */
551
- function sortPagesByOrder(pages, orderMap) {
552
- pages.forEach((page) => {
553
- page.sort((a, b) => (orderMap.get(a.name) ?? 0) - (orderMap.get(b.name) ?? 0));
554
- });
555
- }
556
- /** Remove trailing empty pages */
557
- function removeTrailingEmptyPages(pages) {
558
- while (pages.length > 1 && pages[pages.length - 1].length === 0) pages.pop();
559
- }
560
- /**
561
- * Process a single template page that has dynamic content.
562
- * Uses the same layout algorithm as the original implementation,
563
- * but scoped to a single page's schemas.
564
- */
565
- function processDynamicPage(items, orderMap, contentHeight, paddingTop) {
566
- const pages = [];
567
- let totalYOffset = 0;
568
- for (const item of items) {
569
- const currentGlobalStartY = item.baseY + totalYOffset;
570
- totalYOffset = placeUnitsOnPages(item.schema, item.dynamicLayout, currentGlobalStartY, contentHeight, paddingTop, pages) - (item.baseY + item.height);
571
- }
572
- sortPagesByOrder(pages, orderMap);
573
- removeTrailingEmptyPages(pages);
574
- return pages;
575
- }
576
- var normalizeDynamicLayoutResult = (result) => {
577
- const dynamicLayout = Array.isArray(result) ? { heights: result } : result;
578
- return {
579
- ...dynamicLayout,
580
- heights: dynamicLayout.heights.length === 0 ? [0] : dynamicLayout.heights
581
- };
582
- };
583
- /**
584
- * Process a template containing tables with dynamic heights
585
- * and generate a new template with proper page breaks.
586
- *
587
- * Processing is done page-by-page:
588
- * - Pages with height changes are processed with full layout calculations
589
- * - Pages without height changes are copied as-is (no offset propagation between pages)
590
- *
591
- * This reduces computation cost by:
592
- * 1. Limiting layout calculations to pages that need them
593
- * 2. Avoiding cross-page offset propagation for static pages
594
- */
595
- var getDynamicTemplate = async (arg) => {
596
- const { template, input, options, _cache, getDynamicHeights } = arg;
597
- const basePdf = template.basePdf;
598
- if (!isBlankPdf(basePdf)) return template;
599
- const contentHeight = getContentHeight(basePdf);
600
- const paddingTop = basePdf.padding[0];
601
- const resultPages = [];
602
- const PARALLEL_LIMIT = 10;
603
- for (let pageIndex = 0; pageIndex < template.schemas.length; pageIndex++) {
604
- const pageSchemas = template.schemas[pageIndex];
605
- const { items, orderMap } = normalizePageSchemas(pageSchemas, paddingTop);
606
- for (let i = 0; i < items.length; i += PARALLEL_LIMIT) {
607
- const chunk = items.slice(i, i + PARALLEL_LIMIT);
608
- const chunkResults = await Promise.all(chunk.map((item) => {
609
- return getDynamicHeights(getSchemaValue(item.schema, input), {
610
- schema: item.schema,
611
- basePdf,
612
- options,
613
- _cache
614
- }).then(normalizeDynamicLayoutResult);
615
- }));
616
- for (let j = 0; j < chunkResults.length; j++) items[i + j].dynamicLayout = chunkResults[j];
617
- }
618
- const processedPages = processDynamicPage(items, orderMap, contentHeight, paddingTop);
619
- resultPages.push(...processedPages);
620
- }
621
- removeTrailingEmptyPages(resultPages);
622
- if (resultPages.length === template.schemas.length) {
623
- let unchanged = true;
624
- for (let i = 0; i < resultPages.length && unchanged; i++) {
625
- if (resultPages[i].length !== template.schemas[i].length) {
626
- unchanged = false;
627
- break;
628
- }
629
- for (let j = 0; j < resultPages[i].length && unchanged; j++) {
630
- const orig = template.schemas[i][j];
631
- const result = resultPages[i][j];
632
- if (Math.abs(orig.height - result.height) > EPSILON || Math.abs(orig.position.y - result.position.y) > EPSILON) unchanged = false;
633
- }
634
- }
635
- if (unchanged) return template;
636
- }
637
- return {
638
- basePdf,
639
- schemas: resultPages
640
- };
641
- };
642
- //#endregion
643
638
  //#region src/expression.ts
644
639
  var expressionCache = /* @__PURE__ */ new Map();
645
640
  /**
@@ -959,6 +954,223 @@ var replacePlaceholders = (arg) => {
959
954
  });
960
955
  };
961
956
  //#endregion
957
+ //#region src/dynamicTemplate.ts
958
+ /** Floating point tolerance for comparisons */
959
+ var EPSILON = .01;
960
+ /** Calculate the content height of a page (drawable area excluding padding) */
961
+ var getContentHeight = (basePdf) => basePdf.height - basePdf.padding[0] - basePdf.padding[2];
962
+ /** Get the input value for a schema */
963
+ var getSchemaValue = (schema, input, schemas) => {
964
+ if (!schema.readOnly) return input?.[schema.name] || "";
965
+ if (schema.type !== "text" && schema.type !== "multiVariableText") return schema.content || "";
966
+ return replacePlaceholders({
967
+ content: schema.content || "",
968
+ variables: input,
969
+ schemas
970
+ });
971
+ };
972
+ /**
973
+ * Normalize schemas within a single page into layout items.
974
+ * Returns items sorted by Y coordinate with their order preserved.
975
+ */
976
+ function normalizePageSchemas(pageSchemas, paddingTop) {
977
+ const items = [];
978
+ const orderMap = /* @__PURE__ */ new Map();
979
+ pageSchemas.forEach((schema, index) => {
980
+ const localY = Math.max(0, schema.position.y - paddingTop);
981
+ items.push({
982
+ schema: cloneDeep(schema),
983
+ baseY: localY,
984
+ height: schema.height,
985
+ dynamicLayout: { heights: [schema.height] }
986
+ });
987
+ orderMap.set(schema.name, index);
988
+ });
989
+ items.sort((a, b) => {
990
+ if (Math.abs(a.baseY - b.baseY) > EPSILON) return a.baseY - b.baseY;
991
+ return (orderMap.get(a.schema.name) ?? 0) - (orderMap.get(b.schema.name) ?? 0);
992
+ });
993
+ return {
994
+ items,
995
+ orderMap
996
+ };
997
+ }
998
+ /**
999
+ * Place height units on pages, splitting across pages as needed.
1000
+ * @returns The final global Y coordinate after placement
1001
+ */
1002
+ function placeUnitsOnPages(schema, dynamicLayout, startGlobalY, contentHeight, paddingTop, pages) {
1003
+ const dynamicHeights = dynamicLayout.heights;
1004
+ let currentUnitIndex = 0;
1005
+ let currentPageIndex = Math.floor(startGlobalY / contentHeight);
1006
+ let currentYInPage = startGlobalY % contentHeight;
1007
+ if (currentYInPage < 0) currentYInPage = 0;
1008
+ let actualGlobalEndY = 0;
1009
+ const isSplittable = dynamicHeights.length > 1;
1010
+ while (currentUnitIndex < dynamicHeights.length) {
1011
+ while (pages.length <= currentPageIndex) pages.push([]);
1012
+ const spaceLeft = contentHeight - currentYInPage;
1013
+ if (dynamicHeights[currentUnitIndex] > spaceLeft + EPSILON) {
1014
+ if (!(Math.abs(spaceLeft - contentHeight) <= EPSILON)) {
1015
+ currentPageIndex++;
1016
+ currentYInPage = 0;
1017
+ continue;
1018
+ }
1019
+ }
1020
+ let chunkHeight = 0;
1021
+ const startUnitIndex = currentUnitIndex;
1022
+ while (currentUnitIndex < dynamicHeights.length) {
1023
+ const h = dynamicHeights[currentUnitIndex];
1024
+ if (currentYInPage + chunkHeight + h <= contentHeight + EPSILON) {
1025
+ chunkHeight += h;
1026
+ currentUnitIndex++;
1027
+ } else break;
1028
+ }
1029
+ const isAtPageTop = currentYInPage <= EPSILON;
1030
+ if (dynamicLayout.avoidFirstUnitOnly && isSplittable && startUnitIndex === 0 && currentUnitIndex === 1 && dynamicHeights.length > 1 && !isAtPageTop) {
1031
+ currentUnitIndex = 0;
1032
+ currentPageIndex++;
1033
+ currentYInPage = 0;
1034
+ continue;
1035
+ }
1036
+ if (currentUnitIndex === startUnitIndex) {
1037
+ chunkHeight += dynamicHeights[currentUnitIndex];
1038
+ currentUnitIndex++;
1039
+ }
1040
+ const patch = dynamicLayout.patchSplitSchema?.({
1041
+ schema,
1042
+ start: startUnitIndex,
1043
+ end: currentUnitIndex,
1044
+ isSplit: startUnitIndex > 0,
1045
+ chunkHeight
1046
+ }) ?? {};
1047
+ const newSchema = {
1048
+ ...schema,
1049
+ ...patch,
1050
+ height: chunkHeight,
1051
+ position: {
1052
+ ...schema.position,
1053
+ y: currentYInPage + paddingTop
1054
+ }
1055
+ };
1056
+ pages[currentPageIndex].push(newSchema);
1057
+ currentYInPage += chunkHeight;
1058
+ if (currentYInPage >= contentHeight - EPSILON) {
1059
+ currentPageIndex++;
1060
+ currentYInPage = 0;
1061
+ }
1062
+ actualGlobalEndY = currentPageIndex * contentHeight + currentYInPage;
1063
+ }
1064
+ return actualGlobalEndY;
1065
+ }
1066
+ /** Sort elements within each page by their original order */
1067
+ function sortPagesByOrder(pages, orderMap) {
1068
+ pages.forEach((page) => {
1069
+ page.sort((a, b) => (orderMap.get(a.name) ?? 0) - (orderMap.get(b.name) ?? 0));
1070
+ });
1071
+ }
1072
+ /** Remove trailing empty pages */
1073
+ function removeTrailingEmptyPages(pages) {
1074
+ while (pages.length > 1 && pages[pages.length - 1].length === 0) pages.pop();
1075
+ }
1076
+ /**
1077
+ * Process a single template page that has dynamic content.
1078
+ * Uses the same layout algorithm as the original implementation,
1079
+ * but scoped to a single page's schemas.
1080
+ */
1081
+ function processDynamicPage(items, orderMap, contentHeight, paddingTop) {
1082
+ const pages = [];
1083
+ let totalYOffset = 0;
1084
+ for (const item of items) {
1085
+ const currentGlobalStartY = item.baseY + totalYOffset;
1086
+ totalYOffset = placeUnitsOnPages(item.schema, item.dynamicLayout, currentGlobalStartY, contentHeight, paddingTop, pages) - (item.baseY + item.height);
1087
+ }
1088
+ sortPagesByOrder(pages, orderMap);
1089
+ removeTrailingEmptyPages(pages);
1090
+ return pages;
1091
+ }
1092
+ var normalizeDynamicLayoutResult = (result) => {
1093
+ const dynamicLayout = Array.isArray(result) ? { heights: result } : result;
1094
+ return {
1095
+ ...dynamicLayout,
1096
+ heights: dynamicLayout.heights.length === 0 ? [0] : dynamicLayout.heights
1097
+ };
1098
+ };
1099
+ /**
1100
+ * Process a template containing tables with dynamic heights
1101
+ * and generate a new template with proper page breaks.
1102
+ *
1103
+ * Processing is done page-by-page:
1104
+ * - Pages with height changes are processed with full layout calculations
1105
+ * - Pages without height changes are copied as-is (no offset propagation between pages)
1106
+ *
1107
+ * This reduces computation cost by:
1108
+ * 1. Limiting layout calculations to pages that need them
1109
+ * 2. Avoiding cross-page offset propagation for static pages
1110
+ */
1111
+ var getDynamicTemplate = async (arg) => {
1112
+ const { template, input, options, _cache, getDynamicHeights } = arg;
1113
+ const basePdf = template.basePdf;
1114
+ if (!isBlankPdf(basePdf)) return template;
1115
+ const contentHeight = getContentHeight(basePdf);
1116
+ const paddingTop = basePdf.padding[0];
1117
+ const resultPages = [];
1118
+ const PARALLEL_LIMIT = 10;
1119
+ for (let pageIndex = 0; pageIndex < template.schemas.length; pageIndex++) {
1120
+ const pageSchemas = template.schemas[pageIndex];
1121
+ const { items, orderMap } = normalizePageSchemas(pageSchemas, paddingTop);
1122
+ for (let i = 0; i < items.length; i += PARALLEL_LIMIT) {
1123
+ const chunk = items.slice(i, i + PARALLEL_LIMIT);
1124
+ const chunkResults = await Promise.all(chunk.map((item) => {
1125
+ return getDynamicHeights(getSchemaValue(item.schema, input, template.schemas), {
1126
+ schema: item.schema,
1127
+ basePdf,
1128
+ options,
1129
+ _cache
1130
+ }).then(normalizeDynamicLayoutResult);
1131
+ }));
1132
+ for (let j = 0; j < chunkResults.length; j++) items[i + j].dynamicLayout = chunkResults[j];
1133
+ }
1134
+ const processedPages = processDynamicPage(items, orderMap, contentHeight, paddingTop);
1135
+ resultPages.push(...processedPages);
1136
+ }
1137
+ removeTrailingEmptyPages(resultPages);
1138
+ if (resultPages.length === template.schemas.length) {
1139
+ let unchanged = true;
1140
+ for (let i = 0; i < resultPages.length && unchanged; i++) {
1141
+ if (resultPages[i].length !== template.schemas[i].length) {
1142
+ unchanged = false;
1143
+ break;
1144
+ }
1145
+ for (let j = 0; j < resultPages[i].length && unchanged; j++) {
1146
+ const orig = template.schemas[i][j];
1147
+ const result = resultPages[i][j];
1148
+ if (Math.abs(orig.height - result.height) > EPSILON || Math.abs(orig.position.y - result.position.y) > EPSILON) unchanged = false;
1149
+ }
1150
+ }
1151
+ if (unchanged) return template;
1152
+ }
1153
+ return {
1154
+ basePdf,
1155
+ schemas: resultPages
1156
+ };
1157
+ };
1158
+ //#endregion
1159
+ //#region src/splitRange.ts
1160
+ var createDynamicLayoutSplitRange = (unit, start, end) => ({
1161
+ unit,
1162
+ start,
1163
+ ...end === void 0 ? {} : { end }
1164
+ });
1165
+ var getDynamicLayoutSplitRange = (schema, unit) => {
1166
+ const range = schema.__splitRange;
1167
+ if (range?.unit !== unit) return void 0;
1168
+ return {
1169
+ start: range.start,
1170
+ ...range.end === void 0 ? {} : { end: range.end }
1171
+ };
1172
+ };
1173
+ //#endregion
962
1174
  //#region src/pluginRegistry.ts
963
1175
  /**
964
1176
  * Wraps plugins collection with utility methods
@@ -985,6 +1197,6 @@ var pluginRegistry = (plugins) => {
985
1197
  };
986
1198
  };
987
1199
  //#endregion
988
- export { BLANK_A4_PDF, BLANK_PDF, CUSTOM_A4_PDF, DEFAULT_FONT_NAME, MM_TO_PT_RATIO, PDFME_VERSION, PT_TO_MM_RATIO, PT_TO_PX_RATIO, ZOOM, b64toUint8Array, checkDesignerProps, checkFont, checkGenerateProps, checkInputs, checkPreviewProps, checkTemplate, checkUIOptions, checkUIProps, cloneDeep, getB64BasePdf, getDefaultFont, getDynamicTemplate, getFallbackFontName, getInputFromTemplate, isBlankPdf, isHexValid, isUrlSafeToFetch, mm2pt, pluginRegistry, pt2mm, pt2px, px2mm, replacePlaceholders };
1200
+ export { BLANK_A4_PDF, BLANK_PDF, CUSTOM_A4_PDF, DEFAULT_FONT_NAME, MM_TO_PT_RATIO, PAGE_SIZE_PRESETS, PDFME_VERSION, PT_TO_MM_RATIO, PT_TO_PX_RATIO, ZOOM, applyInternalLinkAnnotations, b64toUint8Array, checkDesignerProps, checkFont, checkGenerateProps, checkInputs, checkPreviewProps, checkTemplate, checkUIOptions, checkUIProps, cloneDeep, createDynamicLayoutSplitRange, detectPaperSize, getB64BasePdf, getDefaultFont, getDynamicLayoutSplitRange, getDynamicTemplate, getFallbackFontName, getInputFromTemplate, getInternalLinkTarget, isBlankPdf, isHexValid, isUrlSafeToFetch, mm2pt, normalizeInternalLinkHref, normalizeLinkHref, normalizeSafeLinkUri, pluginRegistry, pt2mm, pt2px, px2mm, registerInternalLinkAnchor, registerInternalLinkAnnotation, replacePlaceholders, resetInternalLinkAnnotations, resolvePageSize };
989
1201
 
990
1202
  //# sourceMappingURL=index.js.map