@jasy/pdf 1.0.0-beta.3 → 1.0.0-beta.4

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.
Files changed (210) hide show
  1. package/dist/api/content.d.ts +5 -5
  2. package/dist/api/content.js +12 -6
  3. package/dist/api/font-url.d.ts +1 -2
  4. package/dist/api/font-url.js +8 -74
  5. package/dist/api/forms.d.ts +3 -0
  6. package/dist/api/forms.js +3 -0
  7. package/dist/api/index.d.ts +2 -0
  8. package/dist/api/index.js +1 -0
  9. package/dist/api/layout.d.ts +6 -0
  10. package/dist/api/layout.js +7 -2
  11. package/dist/api/page-builder.d.ts +4 -0
  12. package/dist/api/page-builder.js +4 -0
  13. package/dist/api/structure.d.ts +10 -2
  14. package/dist/api/structure.js +9 -6
  15. package/dist/api/table.d.ts +5 -3
  16. package/dist/api/table.js +99 -22
  17. package/dist/common/diagnostics.d.ts +53 -0
  18. package/dist/common/diagnostics.js +77 -0
  19. package/dist/edit/acroform-reader.js +7 -7
  20. package/dist/edit/appearance.js +3 -3
  21. package/dist/edit/attachments.d.ts +52 -0
  22. package/dist/edit/attachments.js +178 -0
  23. package/dist/edit/document.d.ts +38 -8
  24. package/dist/edit/document.js +208 -51
  25. package/dist/edit/fill.js +8 -23
  26. package/dist/edit/filters.d.ts +15 -0
  27. package/dist/edit/filters.js +157 -0
  28. package/dist/edit/index.d.ts +4 -1
  29. package/dist/edit/index.js +3 -1
  30. package/dist/edit/objects.d.ts +3 -4
  31. package/dist/edit/objects.js +5 -18
  32. package/dist/elements/canvas-element.d.ts +3 -0
  33. package/dist/elements/canvas-element.js +14 -3
  34. package/dist/elements/container-element.d.ts +5 -1
  35. package/dist/elements/container-element.js +35 -15
  36. package/dist/elements/forms/checkbox-element.d.ts +2 -0
  37. package/dist/elements/forms/checkbox-element.js +12 -3
  38. package/dist/elements/forms/choice-element.d.ts +6 -1
  39. package/dist/elements/forms/choice-element.js +17 -6
  40. package/dist/elements/forms/field-font.d.ts +8 -0
  41. package/dist/elements/forms/field-font.js +5 -0
  42. package/dist/elements/forms/push-button-element.d.ts +6 -1
  43. package/dist/elements/forms/push-button-element.js +16 -4
  44. package/dist/elements/forms/radio-element.d.ts +2 -0
  45. package/dist/elements/forms/radio-element.js +12 -3
  46. package/dist/elements/forms/signature-element.d.ts +6 -1
  47. package/dist/elements/forms/signature-element.js +16 -4
  48. package/dist/elements/forms/text-field-element.d.ts +6 -1
  49. package/dist/elements/forms/text-field-element.js +16 -4
  50. package/dist/elements/image-element.d.ts +39 -1
  51. package/dist/elements/image-element.js +95 -12
  52. package/dist/elements/layout/anchor-element.d.ts +8 -10
  53. package/dist/elements/layout/anchor-element.js +10 -18
  54. package/dist/elements/layout/bookmark-element.d.ts +9 -11
  55. package/dist/elements/layout/bookmark-element.js +11 -20
  56. package/dist/elements/layout/default-text-style-element.d.ts +11 -13
  57. package/dist/elements/layout/default-text-style-element.js +14 -31
  58. package/dist/elements/layout/deferred-element.d.ts +12 -4
  59. package/dist/elements/layout/deferred-element.js +24 -7
  60. package/dist/elements/layout/expanded-element.d.ts +7 -0
  61. package/dist/elements/layout/expanded-element.js +40 -18
  62. package/dist/elements/layout/keep-together-element.d.ts +8 -18
  63. package/dist/elements/layout/keep-together-element.js +21 -45
  64. package/dist/elements/layout/link-element.d.ts +8 -13
  65. package/dist/elements/layout/link-element.js +9 -19
  66. package/dist/elements/layout/padding-element.d.ts +15 -1
  67. package/dist/elements/layout/padding-element.js +55 -27
  68. package/dist/elements/layout/page-break-element.d.ts +4 -1
  69. package/dist/elements/layout/page-break-element.js +6 -1
  70. package/dist/elements/layout/page-builder-element.d.ts +5 -0
  71. package/dist/elements/layout/page-builder-element.js +11 -0
  72. package/dist/elements/layout/positioned-element.d.ts +14 -2
  73. package/dist/elements/layout/positioned-element.js +36 -10
  74. package/dist/elements/layout/proxy-element.d.ts +43 -0
  75. package/dist/elements/layout/proxy-element.js +94 -0
  76. package/dist/elements/layout/repeating-header-element.d.ts +6 -0
  77. package/dist/elements/layout/repeating-header-element.js +40 -13
  78. package/dist/elements/layout/rotated-box-element.d.ts +6 -0
  79. package/dist/elements/layout/rotated-box-element.js +27 -14
  80. package/dist/elements/layout/rotated-element.d.ts +7 -0
  81. package/dist/elements/layout/rotated-element.js +25 -2
  82. package/dist/elements/layout/struct-group.d.ts +9 -17
  83. package/dist/elements/layout/struct-group.js +10 -35
  84. package/dist/elements/line-element.d.ts +1 -0
  85. package/dist/elements/line-element.js +6 -0
  86. package/dist/elements/page-element.d.ts +10 -3
  87. package/dist/elements/page-element.js +39 -31
  88. package/dist/elements/pdf-document-element.d.ts +3 -0
  89. package/dist/elements/pdf-document-element.js +7 -0
  90. package/dist/elements/pdf-element.d.ts +75 -15
  91. package/dist/elements/pdf-element.js +68 -18
  92. package/dist/elements/rectangle-element.d.ts +14 -0
  93. package/dist/elements/rectangle-element.js +97 -52
  94. package/dist/elements/row-element.d.ts +11 -0
  95. package/dist/elements/row-element.js +58 -8
  96. package/dist/elements/svg-element.d.ts +30 -6
  97. package/dist/elements/svg-element.js +88 -17
  98. package/dist/elements/text-element.d.ts +32 -13
  99. package/dist/elements/text-element.js +119 -97
  100. package/dist/forms/acroform.d.ts +9 -1
  101. package/dist/forms/acroform.js +83 -34
  102. package/dist/forms/appearance.d.ts +17 -6
  103. package/dist/forms/appearance.js +18 -16
  104. package/dist/ir/display-list.d.ts +12 -2
  105. package/dist/layout/box-constraints.d.ts +5 -0
  106. package/dist/layout/box-constraints.js +14 -0
  107. package/dist/layout/collect-images.d.ts +6 -4
  108. package/dist/layout/collect-images.js +6 -4
  109. package/dist/layout/flex-item.d.ts +9 -0
  110. package/dist/layout/flex-item.js +39 -0
  111. package/dist/layout/fragmentation.d.ts +12 -4
  112. package/dist/layout/fragmentation.js +37 -14
  113. package/dist/layout/layout-cache.d.ts +53 -0
  114. package/dist/layout/layout-cache.js +107 -0
  115. package/dist/renderer/anchor-renderer.d.ts +2 -1
  116. package/dist/renderer/anchor-renderer.js +5 -4
  117. package/dist/renderer/bookmark-renderer.d.ts +2 -1
  118. package/dist/renderer/bookmark-renderer.js +5 -4
  119. package/dist/renderer/canvas-renderer.d.ts +2 -1
  120. package/dist/renderer/canvas-renderer.js +5 -2
  121. package/dist/renderer/container-renderer.d.ts +2 -1
  122. package/dist/renderer/container-renderer.js +6 -5
  123. package/dist/renderer/default-text-style-renderer.d.ts +2 -1
  124. package/dist/renderer/default-text-style-renderer.js +2 -3
  125. package/dist/renderer/deferred-renderer.d.ts +2 -1
  126. package/dist/renderer/deferred-renderer.js +2 -3
  127. package/dist/renderer/expanded-renderer.d.ts +2 -1
  128. package/dist/renderer/expanded-renderer.js +2 -3
  129. package/dist/renderer/form-field-renderer.d.ts +2 -1
  130. package/dist/renderer/form-field-renderer.js +16 -3
  131. package/dist/renderer/image-renderer.d.ts +2 -1
  132. package/dist/renderer/image-renderer.js +5 -2
  133. package/dist/renderer/keep-together-renderer.d.ts +2 -1
  134. package/dist/renderer/keep-together-renderer.js +2 -3
  135. package/dist/renderer/line-renderer.d.ts +2 -1
  136. package/dist/renderer/line-renderer.js +7 -2
  137. package/dist/renderer/link-renderer.d.ts +2 -1
  138. package/dist/renderer/link-renderer.js +36 -5
  139. package/dist/renderer/padding-renderer.d.ts +2 -1
  140. package/dist/renderer/padding-renderer.js +5 -5
  141. package/dist/renderer/page-break-renderer.d.ts +6 -5
  142. package/dist/renderer/page-break-renderer.js +14 -7
  143. package/dist/renderer/page-builder-renderer.d.ts +2 -1
  144. package/dist/renderer/page-builder-renderer.js +2 -3
  145. package/dist/renderer/page-renderer.js +28 -7
  146. package/dist/renderer/pdf-backend.d.ts +4 -3
  147. package/dist/renderer/pdf-backend.js +5 -5
  148. package/dist/renderer/pdf-document-renderer.d.ts +1 -8
  149. package/dist/renderer/pdf-document-renderer.js +15 -16
  150. package/dist/renderer/pdf-renderer.js +11 -11
  151. package/dist/renderer/positioned-renderer.d.ts +6 -1
  152. package/dist/renderer/positioned-renderer.js +6 -5
  153. package/dist/renderer/rectangle-renderer.d.ts +2 -1
  154. package/dist/renderer/rectangle-renderer.js +9 -4
  155. package/dist/renderer/repeating-header-renderer.d.ts +2 -1
  156. package/dist/renderer/repeating-header-renderer.js +6 -5
  157. package/dist/renderer/rotated-renderer.d.ts +2 -1
  158. package/dist/renderer/rotated-renderer.js +8 -5
  159. package/dist/renderer/row-renderer.d.ts +2 -1
  160. package/dist/renderer/row-renderer.js +6 -5
  161. package/dist/renderer/struct-group-renderer.d.ts +2 -1
  162. package/dist/renderer/struct-group-renderer.js +2 -3
  163. package/dist/renderer/svg-renderer.d.ts +2 -1
  164. package/dist/renderer/svg-renderer.js +8 -2
  165. package/dist/renderer/text-renderer.d.ts +4 -6
  166. package/dist/renderer/text-renderer.js +101 -111
  167. package/dist/svg/gradients.js +5 -0
  168. package/dist/svg/index.js +10 -1
  169. package/dist/svg/style.d.ts +2 -0
  170. package/dist/svg/style.js +11 -0
  171. package/dist/text/advance.d.ts +4 -0
  172. package/dist/text/advance.js +17 -4
  173. package/dist/text/bidi.d.ts +0 -2
  174. package/dist/text/bidi.js +0 -10
  175. package/dist/text/glyph-coverage.d.ts +1 -1
  176. package/dist/text/glyph-coverage.js +7 -3
  177. package/dist/text/hyphen-breaks.d.ts +17 -0
  178. package/dist/text/hyphen-breaks.js +54 -0
  179. package/dist/text/line-breaker.d.ts +34 -31
  180. package/dist/text/line-breaker.js +364 -176
  181. package/dist/text/line-metrics.d.ts +6 -3
  182. package/dist/text/line-metrics.js +7 -2
  183. package/dist/text/pdf-text-string.d.ts +21 -0
  184. package/dist/text/pdf-text-string.js +114 -0
  185. package/dist/text/text-decoration.d.ts +5 -5
  186. package/dist/text/text-decoration.js +5 -5
  187. package/dist/text/word-splitting.d.ts +15 -2
  188. package/dist/text/word-splitting.js +34 -8
  189. package/dist/utils/afm-parser.d.ts +2 -0
  190. package/dist/utils/afm-parser.js +10 -1
  191. package/dist/utils/dest-registry.d.ts +1 -1
  192. package/dist/utils/dest-registry.js +3 -2
  193. package/dist/utils/fetch-bytes.d.ts +19 -0
  194. package/dist/utils/fetch-bytes.js +79 -0
  195. package/dist/utils/flex-layout.d.ts +13 -13
  196. package/dist/utils/flex-layout.js +318 -216
  197. package/dist/utils/font-metrics.d.ts +4 -3
  198. package/dist/utils/image-helper.js +2 -1
  199. package/dist/utils/pdf-object-manager.d.ts +11 -3
  200. package/dist/utils/pdf-object-manager.js +39 -13
  201. package/dist/utils/renderer-registry.d.ts +43 -2
  202. package/dist/utils/renderer-registry.js +40 -0
  203. package/dist/utils/struct-tree.d.ts +24 -0
  204. package/dist/utils/struct-tree.js +61 -9
  205. package/dist/utils/ttf-parser.d.ts +4 -0
  206. package/dist/utils/ttf-parser.js +27 -4
  207. package/dist/validators/element-validator.js +2 -1
  208. package/package.json +1 -1
  209. package/dist/elements/layout/sized-container-element.d.ts +0 -14
  210. package/dist/elements/layout/sized-container-element.js +0 -32
@@ -56,11 +56,10 @@ export declare function get(o: PdfObject | undefined, key: string): PdfObject |
56
56
  export declare const nameOf: (o: PdfObject | undefined) => string | undefined;
57
57
  export declare const numberOf: (o: PdfObject | undefined) => number | undefined;
58
58
  /**
59
- * A PDF text string as JS text. Two encodings live side by side in the wild: UTF-16BE, marked by a
60
- * `FE FF` byte-order mark (what pdf-lib writes for every field name), and otherwise PDFDocEncoding,
61
- * which agrees with Latin-1 for everything a field name realistically holds.
59
+ * A PDF text string as JS text: UTF-16BE or UTF-8 by their BOM, otherwise PDFDocEncoding - the same table
60
+ * the writer encodes with (`text/pdf-text-string.ts`), so a value read and written back keeps its meaning.
62
61
  */
63
- export declare function textOf(o: PdfObject | undefined): string | undefined;
62
+ export declare function textStringOf(o: PdfObject | undefined): string | undefined;
64
63
  /**
65
64
  * Every string inside an object, however deep. Both sides of the crypto seam use it: strings are
66
65
  * deciphered on open and must be enciphered again before an object is written back.
@@ -8,7 +8,7 @@
8
8
  * is often UTF-16BE with a BOM - pdf-lib writes every field name that way). Decoding early would lose
9
9
  * the distinction and mangle names.
10
10
  */
11
- import { latin1FromBytes } from "../utils/bytes.js";
11
+ import { decodePdfText } from "../text/pdf-text-string.js";
12
12
  // ---------------------------------------------------------------------------------------------
13
13
  // Narrowing helpers. Reading a foreign PDF means every lookup can legitimately come back missing or
14
14
  // of the wrong type, so these all answer `undefined` rather than throwing - the caller decides what
@@ -35,24 +35,11 @@ export function get(o, key) {
35
35
  export const nameOf = (o) => o !== undefined && isName(o) ? o.name : undefined;
36
36
  export const numberOf = (o) => typeof o === "number" ? o : undefined;
37
37
  /**
38
- * A PDF text string as JS text. Two encodings live side by side in the wild: UTF-16BE, marked by a
39
- * `FE FF` byte-order mark (what pdf-lib writes for every field name), and otherwise PDFDocEncoding,
40
- * which agrees with Latin-1 for everything a field name realistically holds.
38
+ * A PDF text string as JS text: UTF-16BE or UTF-8 by their BOM, otherwise PDFDocEncoding - the same table
39
+ * the writer encodes with (`text/pdf-text-string.ts`), so a value read and written back keeps its meaning.
41
40
  */
42
- export function textOf(o) {
43
- if (o === undefined || !isString(o))
44
- return undefined;
45
- const b = o.bytes;
46
- if (b.length >= 2 && b[0] === 0xfe && b[1] === 0xff) {
47
- let s = "";
48
- for (let i = 2; i + 1 < b.length; i += 2)
49
- s += String.fromCharCode((b[i] << 8) | b[i + 1]);
50
- return s;
51
- }
52
- // latin1FromBytes, NOT TextDecoder("latin1"): the latter is a WHATWG alias for windows-1252, which
53
- // remaps 0x80-0x9F (byte 0x95 becomes a bullet). Mapping each byte to the same code point keeps the
54
- // value round-trippable, which is what a reader that may write the string back needs.
55
- return latin1FromBytes(b);
41
+ export function textStringOf(o) {
42
+ return o !== undefined && isString(o) ? decodePdfText(o.bytes) : undefined;
56
43
  }
57
44
  /**
58
45
  * Every string inside an object, however deep. Both sides of the crypto seam use it: strings are
@@ -34,5 +34,8 @@ export declare class CanvasElement extends SizedPDFElement {
34
34
  alt: string | undefined;
35
35
  };
36
36
  calculateLayout(constraints: BoxConstraints, offset: Offset, _ctx: LayoutContext): Size;
37
+ protected computeSize(constraints: BoxConstraints): Size;
38
+ /** Its size under these constraints - the one computation both `measure` and layout use. */
39
+ private sizeFor;
37
40
  }
38
41
  export {};
@@ -29,8 +29,18 @@ export class CanvasElement extends SizedPDFElement {
29
29
  };
30
30
  }
31
31
  calculateLayout(constraints, offset, _ctx) {
32
+ const size = this.sizeFor(constraints);
32
33
  this.x = offset.x;
33
34
  this.y = offset.y;
35
+ this.width = size.width;
36
+ this.height = size.height;
37
+ return size;
38
+ }
39
+ computeSize(constraints) {
40
+ return this.sizeFor(constraints);
41
+ }
42
+ /** Its size under these constraints - the one computation both `measure` and layout use. */
43
+ sizeFor(constraints) {
34
44
  const specs = extentSpecs({
35
45
  ...this.sizing,
36
46
  width: this.requested.width,
@@ -40,8 +50,9 @@ export class CanvasElement extends SizedPDFElement {
40
50
  });
41
51
  const resolved = resolveSize(specs.width, specs.height, this.sizing.aspectRatio, constraints);
42
52
  const bounds = resolved.constraints;
43
- this.width = resolved.width ?? (bounds.hasBoundedWidth ? bounds.maxWidth : 0);
44
- this.height = resolved.height ?? (bounds.hasBoundedHeight ? bounds.maxHeight : 0);
45
- return { width: this.width, height: this.height };
53
+ return {
54
+ width: resolved.width ?? (bounds.hasBoundedWidth ? bounds.maxWidth : 0),
55
+ height: resolved.height ?? (bounds.hasBoundedHeight ? bounds.maxHeight : 0),
56
+ };
46
57
  }
47
58
  }
@@ -1,7 +1,7 @@
1
1
  import { MainAlign, CrossAlign } from "../utils/flex-layout.js";
2
2
  import { BoxConstraints, Offset, Size } from "../layout/box-constraints.js";
3
3
  import { Fragmentable, FragmentResult } from "../layout/fragmentation.js";
4
- import { LayoutContext, SizedElement, SizedPDFElement, WithChildren } from "./pdf-element.js";
4
+ import { LayoutContext, PDFElement, SizedElement, SizedPDFElement, WithChildren } from "./pdf-element.js";
5
5
  interface ContainerElementParams extends SizedElement, WithChildren {
6
6
  /** Space between children. */
7
7
  gap?: number;
@@ -77,6 +77,10 @@ export declare class ContainerElement extends SizedPDFElement implements Fragmen
77
77
  /** A Column stacks vertically, so widths overlap and heights add up. */
78
78
  minIntrinsicMain(horizontal: boolean, ctx: LayoutContext): number;
79
79
  calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
80
+ protected computeSize(constraints: BoxConstraints, ctx: LayoutContext): Size;
81
+ /** The one sizing path: `layout` places the children (relative to this box), `measure` only sizes them. */
82
+ private sizeWith;
83
+ layoutChildren(): readonly PDFElement[];
80
84
  getProps(): ContainerElementParams;
81
85
  }
82
86
  export {};
@@ -1,8 +1,10 @@
1
- import { FlexLayoutHelper, VERTICAL_AXIS } from "../utils/flex-layout.js";
1
+ import { widthRules } from "../layout/box-constraints.js";
2
+ import { flexibleOf } from "../layout/flex-item.js";
3
+ import { FlexLayoutHelper, VERTICAL_AXIS, } from "../utils/flex-layout.js";
2
4
  import { stackMinIntrinsic, withDeclaredExtent } from "../layout/min-intrinsic.js";
3
- import { extentSpecs, resolveSize, } from "../layout/box-constraints.js";
5
+ import { BoxConstraints, extentSpecs, resolveSize, } from "../layout/box-constraints.js";
4
6
  import { childrenForceBreak, packChildren, } from "../layout/fragmentation.js";
5
- import { FlexiblePDFElement, SizedPDFElement, } from "./pdf-element.js";
7
+ import { SizedPDFElement, } from "./pdf-element.js";
6
8
  export class ContainerElement extends SizedPDFElement {
7
9
  constructor({ x, y, width, height, children, gap, main, cross, reverse, wrap, alignContent, breakBefore, breakAfter, ...sizing }) {
8
10
  super({ x, y, width, height });
@@ -17,8 +19,10 @@ export class ContainerElement extends SizedPDFElement {
17
19
  this.breakBefore = breakBefore ?? false;
18
20
  this.breakAfter = breakAfter ?? false;
19
21
  }
22
+ // A break before the FIRST child is a break before this stack (CSS propagates it): the whole stack
23
+ // moves to the next page, instead of leaving an empty piece - or an empty border - behind.
20
24
  breaksBefore() {
21
- return this.breakBefore;
25
+ return this.breakBefore || (this.children[0]?.breaksBefore() ?? false);
22
26
  }
23
27
  breaksAfter() {
24
28
  return this.breakAfter;
@@ -38,13 +42,19 @@ export class ContainerElement extends SizedPDFElement {
38
42
  return childrenForceBreak(this.children);
39
43
  }
40
44
  fragment(maxHeight, width, ctx) {
41
- const { fitted, remainder, forceBreak } = packChildren(this.children, maxHeight, width, ctx, this.gap);
45
+ // Pack at the width the column is drawn at: its own rules (fixed, %, min/max) resolved against the
46
+ // region, the same sizing path as `measure`. A narrower column has taller children.
47
+ const ownWidth = this.measure(BoxConstraints.loose(width, Infinity), ctx).width;
48
+ const { fitted, remainder, forceBreak } = packChildren(this.children, maxHeight, ownWidth, ctx, this.gap);
42
49
  // Fits as one region with no forced cut: hand the whole container back so the page renders
43
50
  // unchanged (its normal layout distributes flex / fills the page). A forced break is excluded
44
51
  // here even when nothing follows it (a trailing `PageBreak`): return the packed children, which
45
52
  // no longer hold the consumed break marker, so it does not leak into the render pass.
46
53
  if (remainder.length === 0 && !forceBreak)
47
54
  return { fitted: this, remainder: null };
55
+ // Nothing fit below the content above: move on whole, not as an empty shell left behind.
56
+ if (fitted.length === 0 && !forceBreak)
57
+ return { fitted: null, remainder: this };
48
58
  return {
49
59
  fitted: this.cloneWithChildren(fitted),
50
60
  // Only the CONTINUATION carries `breakAfter`, so a stack that splits across pages still breaks
@@ -58,10 +68,9 @@ export class ContainerElement extends SizedPDFElement {
58
68
  return new ContainerElement({
59
69
  x: this.x,
60
70
  y: this.y,
61
- // Carry the requested WIDTH (a %-width column keeps its width on every page); the height is
71
+ // Carry the WIDTH rules (a %-width or max-width column keeps them on every page); the height is
62
72
  // intentionally dropped so each fragment shrink-wraps to the content it actually holds.
63
- width: this.requested.width,
64
- widthFactor: this.requested.widthFactor,
73
+ ...widthRules(this.requested),
65
74
  children,
66
75
  gap: this.gap,
67
76
  main: this.main,
@@ -70,7 +79,7 @@ export class ContainerElement extends SizedPDFElement {
70
79
  wrap: this.wrap,
71
80
  alignContent: this.alignContent,
72
81
  breakAfter,
73
- });
82
+ }).asPieceOf(this);
74
83
  }
75
84
  relativeSizeFactor(horizontal) {
76
85
  return horizontal ? this.requested.widthFactor : this.requested.heightFactor;
@@ -91,7 +100,7 @@ export class ContainerElement extends SizedPDFElement {
91
100
  : [this.requested.height, this.requested.heightFactor];
92
101
  if (requested.some((v) => v !== undefined))
93
102
  return false;
94
- const ownFlexChild = !horizontal && this.children.some((c) => c instanceof FlexiblePDFElement);
103
+ const ownFlexChild = !horizontal && this.children.some((c) => flexibleOf(c) !== undefined);
95
104
  return ownFlexChild || this.children.some((c) => c.needsBoundedMain(horizontal));
96
105
  }
97
106
  /** A Column stacks vertically, so widths overlap and heights add up. */
@@ -102,6 +111,16 @@ export class ContainerElement extends SizedPDFElement {
102
111
  calculateLayout(constraints, offset, ctx) {
103
112
  this.x = offset.x;
104
113
  this.y = offset.y;
114
+ const size = this.sizeWith(constraints, ctx, "layout");
115
+ this.width = size.width;
116
+ this.height = size.height;
117
+ return size;
118
+ }
119
+ computeSize(constraints, ctx) {
120
+ return this.sizeWith(constraints, ctx, "measure");
121
+ }
122
+ /** The one sizing path: `layout` places the children (relative to this box), `measure` only sizes them. */
123
+ sizeWith(constraints, ctx, mode) {
105
124
  // Relative sizing: a pinned extent (fixed points or a fraction of the offered box, clamped into
106
125
  // the constraints) wins; else fill a bounded axis; else stay `undefined` and shrink-wrap below.
107
126
  // A Column nested in a Row gets unbounded width - passing 0 there would collapse children to 0.
@@ -127,21 +146,22 @@ export class ContainerElement extends SizedPDFElement {
127
146
  // Vertical flex stack (main = height, cross = width). The shared helper measures
128
147
  // fixed children, distributes the leftover to flex children, and places everything
129
148
  // in source order.
130
- result = FlexLayoutHelper.layout(this.children, VERTICAL_AXIS, mainAvail, crossAvail, this.y, this.x, {
149
+ result = FlexLayoutHelper.layout(this.children, VERTICAL_AXIS, mainAvail, crossAvail, {
131
150
  gap: this.gap,
132
151
  main: this.main,
133
152
  cross: this.cross,
134
153
  reverse: this.reverse,
135
154
  wrap: this.wrap,
136
155
  alignContent: this.alignContent,
137
- }, ctx);
156
+ }, ctx, mode);
138
157
  }
139
158
  // Pinned/bounded axis takes that extent; an unbounded one shrink-wraps to the children (height =
140
159
  // the stack, width = the widest child). Top-left coordinates; the container draws nothing, and
141
160
  // the Y-flip happens once at the IR -> backend seam.
142
- this.width = boundedWidth ?? result.crossUsed;
143
- this.height = boundedHeight ?? result.mainUsed;
144
- return { width: this.width, height: this.height };
161
+ return { width: boundedWidth ?? result.crossUsed, height: boundedHeight ?? result.mainUsed };
162
+ }
163
+ layoutChildren() {
164
+ return this.children ?? [];
145
165
  }
146
166
  getProps() {
147
167
  return {
@@ -40,6 +40,8 @@ export declare class CheckboxElement extends SizedPDFElement {
40
40
  private size;
41
41
  constructor(p: CheckboxParams);
42
42
  calculateLayout(_constraints: BoxConstraints, offset: Offset, _ctx: LayoutContext): Size;
43
+ protected computeSize(_constraints: BoxConstraints): Size;
44
+ private sizeFor;
43
45
  getProps(): {
44
46
  x: number;
45
47
  y: number;
@@ -29,11 +29,20 @@ export class CheckboxElement extends SizedPDFElement {
29
29
  };
30
30
  }
31
31
  calculateLayout(_constraints, offset, _ctx) {
32
+ const size = this.sizeFor(_constraints);
32
33
  this.x = offset.x;
33
34
  this.y = offset.y;
34
- this.width = this.size;
35
- this.height = this.size;
36
- return { width: this.size, height: this.size };
35
+ this.width = size.width;
36
+ this.height = size.height;
37
+ return size;
38
+ }
39
+ computeSize(_constraints) {
40
+ return this.sizeFor(_constraints);
41
+ }
42
+ sizeFor(_constraints) {
43
+ const width = this.size;
44
+ const height = this.size;
45
+ return { width, height };
37
46
  }
38
47
  getProps() {
39
48
  return {
@@ -1,6 +1,7 @@
1
1
  import { Color } from "../../common/color.js";
2
2
  import { BoxConstraints, Offset, Size } from "../../layout/box-constraints.js";
3
3
  import { LayoutContext, SizedPDFElement } from "../pdf-element.js";
4
+ import { FieldFont } from "./field-font.js";
4
5
  import type { ChoiceOption, ChoiceSpec, FieldAlign, FieldStyle } from "../../forms/field.js";
5
6
  export interface ChoiceParams {
6
7
  name: string;
@@ -41,7 +42,10 @@ export declare class ChoiceElement extends SizedPDFElement {
41
42
  private style;
42
43
  private requested;
43
44
  constructor(p: ChoiceParams);
44
- calculateLayout(constraints: BoxConstraints, offset: Offset, _ctx: LayoutContext): Size;
45
+ private font?;
46
+ calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
47
+ protected computeSize(constraints: BoxConstraints): Size;
48
+ private sizeFor;
45
49
  getProps(): {
46
50
  x: number;
47
51
  y: number;
@@ -49,5 +53,6 @@ export declare class ChoiceElement extends SizedPDFElement {
49
53
  height: number;
50
54
  spec: ChoiceSpec;
51
55
  style: FieldStyle;
56
+ font: FieldFont | undefined;
52
57
  };
53
58
  }
@@ -1,5 +1,6 @@
1
1
  import { Color } from "../../common/color.js";
2
2
  import { SizedPDFElement } from "../pdf-element.js";
3
+ import { inheritedFieldFont } from "./field-font.js";
3
4
  /**
4
5
  * A choice field (AcroForm /Ch) - a dropdown or a list box, chosen by `combo`. A leaf that draws nothing
5
6
  * itself: it reserves its box and emits a `formfield` IR node. The selected value is rendered by the
@@ -34,17 +35,26 @@ export class ChoiceElement extends SizedPDFElement {
34
35
  };
35
36
  this.requested = { width: p.width, height: p.height, fontSize, combo: p.combo };
36
37
  }
37
- calculateLayout(constraints, offset, _ctx) {
38
+ calculateLayout(constraints, offset, ctx) {
39
+ this.font = inheritedFieldFont(ctx);
40
+ const size = this.sizeFor(constraints);
38
41
  this.x = offset.x;
39
42
  this.y = offset.y;
40
- this.width = this.requested.width ?? (constraints.hasBoundedWidth ? constraints.maxWidth : 200);
43
+ this.width = size.width;
44
+ this.height = size.height;
45
+ return size;
46
+ }
47
+ computeSize(constraints) {
48
+ return this.sizeFor(constraints);
49
+ }
50
+ sizeFor(constraints) {
51
+ const width = this.requested.width ?? (constraints.hasBoundedWidth ? constraints.maxWidth : 200);
41
52
  // Dropdown: one line. List box: a few rows tall so several options are visible.
42
53
  const oneLine = this.requested.fontSize + 9;
43
54
  const rows = Math.max(3, Math.min(this.spec.options.length, 5));
44
- this.height =
45
- this.requested.height ??
46
- (this.requested.combo ? oneLine : rows * (this.requested.fontSize + 4));
47
- return { width: this.width, height: this.height };
55
+ const height = this.requested.height ??
56
+ (this.requested.combo ? oneLine : rows * (this.requested.fontSize + 4));
57
+ return { width, height };
48
58
  }
49
59
  getProps() {
50
60
  return {
@@ -54,6 +64,7 @@ export class ChoiceElement extends SizedPDFElement {
54
64
  height: this.height ?? 0,
55
65
  spec: this.spec,
56
66
  style: this.style,
67
+ font: this.font,
57
68
  };
58
69
  }
59
70
  }
@@ -0,0 +1,8 @@
1
+ import type { LayoutContext } from "../pdf-element.js";
2
+ import type { FontStyle } from "../../utils/pdf-object-manager.js";
3
+ /** The font a field's text inherits, like any text: `Document({ font })` or a `DefaultTextStyle`. */
4
+ export interface FieldFont {
5
+ family: string;
6
+ style: FontStyle;
7
+ }
8
+ export declare function inheritedFieldFont(ctx: LayoutContext): FieldFont;
@@ -0,0 +1,5 @@
1
+ import { DEFAULT_TEXT_STYLE } from "../../text/text-style.js";
2
+ export function inheritedFieldFont(ctx) {
3
+ const ts = ctx.textStyle ?? DEFAULT_TEXT_STYLE;
4
+ return { family: ts.fontFamily, style: ts.fontStyle };
5
+ }
@@ -1,6 +1,7 @@
1
1
  import { Color } from "../../common/color.js";
2
2
  import { BoxConstraints, Offset, Size } from "../../layout/box-constraints.js";
3
3
  import { LayoutContext, SizedPDFElement } from "../pdf-element.js";
4
+ import { FieldFont } from "./field-font.js";
4
5
  import type { ButtonAction, FieldStyle, PushButtonSpec } from "../../forms/field.js";
5
6
  export interface PushButtonParams {
6
7
  /** The field name (/T) - unique in the document. */
@@ -44,7 +45,10 @@ export declare class PushButtonElement extends SizedPDFElement {
44
45
  private style;
45
46
  private requested;
46
47
  constructor(p: PushButtonParams);
47
- calculateLayout(constraints: BoxConstraints, offset: Offset, _ctx: LayoutContext): Size;
48
+ private font?;
49
+ calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
50
+ protected computeSize(constraints: BoxConstraints): Size;
51
+ private sizeFor;
48
52
  getProps(): {
49
53
  x: number;
50
54
  y: number;
@@ -52,5 +56,6 @@ export declare class PushButtonElement extends SizedPDFElement {
52
56
  height: number;
53
57
  spec: PushButtonSpec;
54
58
  style: FieldStyle;
59
+ font: FieldFont | undefined;
55
60
  };
56
61
  }
@@ -1,5 +1,6 @@
1
1
  import { Color } from "../../common/color.js";
2
2
  import { SizedPDFElement } from "../pdf-element.js";
3
+ import { inheritedFieldFont } from "./field-font.js";
3
4
  /**
4
5
  * A push button (AcroForm /Btn, Pushbutton flag). Holds no value - it fires an action. Draws nothing
5
6
  * itself: it reserves its box and emits a `formfield` IR node whose caption is baked into an /AP at the
@@ -29,12 +30,22 @@ export class PushButtonElement extends SizedPDFElement {
29
30
  };
30
31
  this.requested = { width: p.width, height: p.height, fontSize };
31
32
  }
32
- calculateLayout(constraints, offset, _ctx) {
33
+ calculateLayout(constraints, offset, ctx) {
34
+ this.font = inheritedFieldFont(ctx);
35
+ const size = this.sizeFor(constraints);
33
36
  this.x = offset.x;
34
37
  this.y = offset.y;
35
- this.width = this.requested.width ?? (constraints.hasBoundedWidth ? constraints.maxWidth : 120);
36
- this.height = this.requested.height ?? this.requested.fontSize + 14;
37
- return { width: this.width, height: this.height };
38
+ this.width = size.width;
39
+ this.height = size.height;
40
+ return size;
41
+ }
42
+ computeSize(constraints) {
43
+ return this.sizeFor(constraints);
44
+ }
45
+ sizeFor(constraints) {
46
+ const width = this.requested.width ?? (constraints.hasBoundedWidth ? constraints.maxWidth : 120);
47
+ const height = this.requested.height ?? this.requested.fontSize + 14;
48
+ return { width, height };
38
49
  }
39
50
  getProps() {
40
51
  return {
@@ -44,6 +55,7 @@ export class PushButtonElement extends SizedPDFElement {
44
55
  height: this.height ?? 0,
45
56
  spec: this.spec,
46
57
  style: this.style,
58
+ font: this.font,
47
59
  };
48
60
  }
49
61
  }
@@ -41,6 +41,8 @@ export declare class RadioElement extends SizedPDFElement {
41
41
  private size;
42
42
  constructor(p: RadioParams);
43
43
  calculateLayout(_constraints: BoxConstraints, offset: Offset, _ctx: LayoutContext): Size;
44
+ protected computeSize(_constraints: BoxConstraints): Size;
45
+ private sizeFor;
44
46
  getProps(): {
45
47
  x: number;
46
48
  y: number;
@@ -30,11 +30,20 @@ export class RadioElement extends SizedPDFElement {
30
30
  };
31
31
  }
32
32
  calculateLayout(_constraints, offset, _ctx) {
33
+ const size = this.sizeFor(_constraints);
33
34
  this.x = offset.x;
34
35
  this.y = offset.y;
35
- this.width = this.size;
36
- this.height = this.size;
37
- return { width: this.size, height: this.size };
36
+ this.width = size.width;
37
+ this.height = size.height;
38
+ return size;
39
+ }
40
+ computeSize(_constraints) {
41
+ return this.sizeFor(_constraints);
42
+ }
43
+ sizeFor(_constraints) {
44
+ const width = this.size;
45
+ const height = this.size;
46
+ return { width, height };
38
47
  }
39
48
  getProps() {
40
49
  return {
@@ -1,6 +1,7 @@
1
1
  import { Color } from "../../common/color.js";
2
2
  import { BoxConstraints, Offset, Size } from "../../layout/box-constraints.js";
3
3
  import { LayoutContext, SizedPDFElement } from "../pdf-element.js";
4
+ import { FieldFont } from "./field-font.js";
4
5
  import type { FieldStyle, SignatureSpec } from "../../forms/field.js";
5
6
  export interface SignatureFieldParams {
6
7
  /** The field name (/T) - unique in the document. */
@@ -42,7 +43,10 @@ export declare class SignatureFieldElement extends SizedPDFElement {
42
43
  private style;
43
44
  private requested;
44
45
  constructor(p: SignatureFieldParams);
45
- calculateLayout(constraints: BoxConstraints, offset: Offset, _ctx: LayoutContext): Size;
46
+ private font?;
47
+ calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
48
+ protected computeSize(constraints: BoxConstraints): Size;
49
+ private sizeFor;
46
50
  getProps(): {
47
51
  x: number;
48
52
  y: number;
@@ -50,5 +54,6 @@ export declare class SignatureFieldElement extends SizedPDFElement {
50
54
  height: number;
51
55
  spec: SignatureSpec;
52
56
  style: FieldStyle;
57
+ font: FieldFont | undefined;
53
58
  };
54
59
  }
@@ -1,5 +1,6 @@
1
1
  import { Color } from "../../common/color.js";
2
2
  import { SizedPDFElement } from "../pdf-element.js";
3
+ import { inheritedFieldFont } from "./field-font.js";
3
4
  /**
4
5
  * A signature field (AcroForm /Sig) - a PLACEHOLDER someone can sign later. jasy creates the field and
5
6
  * its empty "sign here" appearance; it does not sign (that needs a certificate and a byte-range digest -
@@ -27,12 +28,22 @@ export class SignatureFieldElement extends SizedPDFElement {
27
28
  };
28
29
  this.requested = { width: p.width, height: p.height };
29
30
  }
30
- calculateLayout(constraints, offset, _ctx) {
31
+ calculateLayout(constraints, offset, ctx) {
32
+ this.font = inheritedFieldFont(ctx);
33
+ const size = this.sizeFor(constraints);
31
34
  this.x = offset.x;
32
35
  this.y = offset.y;
33
- this.width = this.requested.width ?? (constraints.hasBoundedWidth ? constraints.maxWidth : 240);
34
- this.height = this.requested.height ?? 48;
35
- return { width: this.width, height: this.height };
36
+ this.width = size.width;
37
+ this.height = size.height;
38
+ return size;
39
+ }
40
+ computeSize(constraints) {
41
+ return this.sizeFor(constraints);
42
+ }
43
+ sizeFor(constraints) {
44
+ const width = this.requested.width ?? (constraints.hasBoundedWidth ? constraints.maxWidth : 240);
45
+ const height = this.requested.height ?? 48;
46
+ return { width, height };
36
47
  }
37
48
  getProps() {
38
49
  return {
@@ -42,6 +53,7 @@ export class SignatureFieldElement extends SizedPDFElement {
42
53
  height: this.height ?? 0,
43
54
  spec: this.spec,
44
55
  style: this.style,
56
+ font: this.font,
45
57
  };
46
58
  }
47
59
  }
@@ -1,6 +1,7 @@
1
1
  import { Color } from "../../common/color.js";
2
2
  import { BoxConstraints, Offset, Size } from "../../layout/box-constraints.js";
3
3
  import { LayoutContext, SizedPDFElement } from "../pdf-element.js";
4
+ import { FieldFont } from "./field-font.js";
4
5
  import type { FieldAlign, FieldStyle, TextFieldSpec } from "../../forms/field.js";
5
6
  export interface TextFieldParams {
6
7
  /** The field name (/T) - unique in the document; you read the value back by it. */
@@ -49,7 +50,10 @@ export declare class TextFieldElement extends SizedPDFElement {
49
50
  private style;
50
51
  private requested;
51
52
  constructor(p: TextFieldParams);
52
- calculateLayout(constraints: BoxConstraints, offset: Offset, _ctx: LayoutContext): Size;
53
+ private font?;
54
+ calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
55
+ protected computeSize(constraints: BoxConstraints): Size;
56
+ private sizeFor;
53
57
  getProps(): {
54
58
  x: number;
55
59
  y: number;
@@ -57,5 +61,6 @@ export declare class TextFieldElement extends SizedPDFElement {
57
61
  height: number;
58
62
  spec: TextFieldSpec;
59
63
  style: FieldStyle;
64
+ font: FieldFont | undefined;
60
65
  };
61
66
  }
@@ -1,5 +1,6 @@
1
1
  import { Color } from "../../common/color.js";
2
2
  import { SizedPDFElement } from "../pdf-element.js";
3
+ import { inheritedFieldFont } from "./field-font.js";
3
4
  /**
4
5
  * A text input field (AcroForm /Tx). A leaf that draws NOTHING itself: it reserves its box in the layout
5
6
  * and emits a `formfield` IR node that becomes a Widget annotation + /AcroForm field at the seam.
@@ -31,15 +32,25 @@ export class TextFieldElement extends SizedPDFElement {
31
32
  };
32
33
  this.requested = { width: p.width, height: p.height, fontSize };
33
34
  }
34
- calculateLayout(constraints, offset, _ctx) {
35
+ calculateLayout(constraints, offset, ctx) {
36
+ this.font = inheritedFieldFont(ctx);
37
+ const size = this.sizeFor(constraints);
35
38
  this.x = offset.x;
36
39
  this.y = offset.y;
40
+ this.width = size.width;
41
+ this.height = size.height;
42
+ return size;
43
+ }
44
+ computeSize(constraints) {
45
+ return this.sizeFor(constraints);
46
+ }
47
+ sizeFor(constraints) {
37
48
  // Width: explicit, else fill the offered box (a form field with no width spans the column).
38
- this.width = this.requested.width ?? (constraints.hasBoundedWidth ? constraints.maxWidth : 200);
49
+ const width = this.requested.width ?? (constraints.hasBoundedWidth ? constraints.maxWidth : 200);
39
50
  // Height: explicit, else a comfortable single line (font size + padding). Multiline without an
40
51
  // explicit height still gets the single-line default - the caller should size a multiline box.
41
- this.height = this.requested.height ?? this.requested.fontSize + 9;
42
- return { width: this.width, height: this.height };
52
+ const height = this.requested.height ?? this.requested.fontSize + 9;
53
+ return { width, height };
43
54
  }
44
55
  getProps() {
45
56
  return {
@@ -49,6 +60,7 @@ export class TextFieldElement extends SizedPDFElement {
49
60
  height: this.height ?? 0,
50
61
  spec: this.spec,
51
62
  style: this.style,
63
+ font: this.font,
52
64
  };
53
65
  }
54
66
  }