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

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 (246) hide show
  1. package/dist/api/color.d.ts +1 -24
  2. package/dist/api/color.js +2 -278
  3. package/dist/api/content.d.ts +10 -5
  4. package/dist/api/content.js +29 -13
  5. package/dist/api/descriptor.js +30 -3
  6. package/dist/api/flow.d.ts +55 -0
  7. package/dist/api/flow.js +71 -0
  8. package/dist/api/font-url.d.ts +1 -2
  9. package/dist/api/font-url.js +8 -74
  10. package/dist/api/forms.d.ts +3 -0
  11. package/dist/api/forms.js +3 -0
  12. package/dist/api/gradient.d.ts +1 -53
  13. package/dist/api/gradient.js +2 -97
  14. package/dist/api/index.d.ts +4 -0
  15. package/dist/api/index.js +3 -0
  16. package/dist/api/layout.d.ts +6 -0
  17. package/dist/api/layout.js +9 -3
  18. package/dist/api/page-builder.d.ts +4 -0
  19. package/dist/api/page-builder.js +4 -0
  20. package/dist/api/structure.d.ts +10 -2
  21. package/dist/api/structure.js +9 -6
  22. package/dist/api/table.d.ts +5 -3
  23. package/dist/api/table.js +101 -22
  24. package/dist/api/text.d.ts +8 -8
  25. package/dist/api/text.js +6 -0
  26. package/dist/canvas/painter.d.ts +2 -2
  27. package/dist/canvas/painter.js +2 -2
  28. package/dist/common/color-input.d.ts +24 -0
  29. package/dist/common/color-input.js +278 -0
  30. package/dist/common/diagnostics.d.ts +61 -0
  31. package/dist/common/diagnostics.js +77 -0
  32. package/dist/common/gradient.d.ts +53 -0
  33. package/dist/common/gradient.js +97 -0
  34. package/dist/edit/acroform-reader.js +7 -7
  35. package/dist/edit/appearance.js +3 -3
  36. package/dist/edit/attachments.d.ts +52 -0
  37. package/dist/edit/attachments.js +178 -0
  38. package/dist/edit/document.d.ts +38 -8
  39. package/dist/edit/document.js +208 -51
  40. package/dist/edit/fill.js +8 -23
  41. package/dist/edit/filters.d.ts +15 -0
  42. package/dist/edit/filters.js +157 -0
  43. package/dist/edit/index.d.ts +4 -1
  44. package/dist/edit/index.js +3 -1
  45. package/dist/edit/objects.d.ts +3 -4
  46. package/dist/edit/objects.js +5 -18
  47. package/dist/elements/canvas-element.d.ts +3 -0
  48. package/dist/elements/canvas-element.js +14 -3
  49. package/dist/elements/container-element.d.ts +8 -1
  50. package/dist/elements/container-element.js +49 -17
  51. package/dist/elements/forms/checkbox-element.d.ts +2 -0
  52. package/dist/elements/forms/checkbox-element.js +12 -3
  53. package/dist/elements/forms/choice-element.d.ts +6 -1
  54. package/dist/elements/forms/choice-element.js +17 -6
  55. package/dist/elements/forms/field-font.d.ts +8 -0
  56. package/dist/elements/forms/field-font.js +5 -0
  57. package/dist/elements/forms/push-button-element.d.ts +6 -1
  58. package/dist/elements/forms/push-button-element.js +16 -4
  59. package/dist/elements/forms/radio-element.d.ts +2 -0
  60. package/dist/elements/forms/radio-element.js +12 -3
  61. package/dist/elements/forms/signature-element.d.ts +6 -1
  62. package/dist/elements/forms/signature-element.js +16 -4
  63. package/dist/elements/forms/text-field-element.d.ts +6 -1
  64. package/dist/elements/forms/text-field-element.js +16 -4
  65. package/dist/elements/image-element.d.ts +70 -21
  66. package/dist/elements/image-element.js +173 -52
  67. package/dist/elements/index.d.ts +3 -0
  68. package/dist/elements/index.js +3 -0
  69. package/dist/elements/layout/anchor-element.d.ts +8 -10
  70. package/dist/elements/layout/anchor-element.js +10 -18
  71. package/dist/elements/layout/bookmark-element.d.ts +9 -11
  72. package/dist/elements/layout/bookmark-element.js +11 -20
  73. package/dist/elements/layout/column-break-element.d.ts +17 -0
  74. package/dist/elements/layout/column-break-element.js +26 -0
  75. package/dist/elements/layout/default-text-style-element.d.ts +11 -13
  76. package/dist/elements/layout/default-text-style-element.js +14 -31
  77. package/dist/elements/layout/deferred-element.d.ts +20 -4
  78. package/dist/elements/layout/deferred-element.js +41 -9
  79. package/dist/elements/layout/expanded-element.d.ts +11 -0
  80. package/dist/elements/layout/expanded-element.js +53 -18
  81. package/dist/elements/layout/flow-element.d.ts +109 -0
  82. package/dist/elements/layout/flow-element.js +317 -0
  83. package/dist/elements/layout/keep-together-element.d.ts +8 -18
  84. package/dist/elements/layout/keep-together-element.js +33 -45
  85. package/dist/elements/layout/link-element.d.ts +8 -13
  86. package/dist/elements/layout/link-element.js +9 -19
  87. package/dist/elements/layout/padding-element.d.ts +18 -1
  88. package/dist/elements/layout/padding-element.js +63 -28
  89. package/dist/elements/layout/page-break-element.d.ts +4 -1
  90. package/dist/elements/layout/page-break-element.js +6 -1
  91. package/dist/elements/layout/page-builder-element.d.ts +5 -0
  92. package/dist/elements/layout/page-builder-element.js +13 -2
  93. package/dist/elements/layout/positioned-element.d.ts +23 -4
  94. package/dist/elements/layout/positioned-element.js +42 -12
  95. package/dist/elements/layout/proxy-element.d.ts +47 -0
  96. package/dist/elements/layout/proxy-element.js +106 -0
  97. package/dist/elements/layout/region-element.d.ts +53 -0
  98. package/dist/elements/layout/region-element.js +108 -0
  99. package/dist/elements/layout/repeating-header-element.d.ts +9 -0
  100. package/dist/elements/layout/repeating-header-element.js +55 -13
  101. package/dist/elements/layout/rotated-box-element.d.ts +6 -0
  102. package/dist/elements/layout/rotated-box-element.js +27 -14
  103. package/dist/elements/layout/rotated-element.d.ts +9 -0
  104. package/dist/elements/layout/rotated-element.js +33 -2
  105. package/dist/elements/layout/struct-group.d.ts +9 -17
  106. package/dist/elements/layout/struct-group.js +10 -35
  107. package/dist/elements/line-element.d.ts +1 -0
  108. package/dist/elements/line-element.js +6 -0
  109. package/dist/elements/page-element.d.ts +10 -3
  110. package/dist/elements/page-element.js +39 -31
  111. package/dist/elements/pdf-document-element.d.ts +3 -0
  112. package/dist/elements/pdf-document-element.js +7 -0
  113. package/dist/elements/pdf-element.d.ts +105 -16
  114. package/dist/elements/pdf-element.js +118 -19
  115. package/dist/elements/rectangle-element.d.ts +22 -2
  116. package/dist/elements/rectangle-element.js +119 -55
  117. package/dist/elements/row-element.d.ts +17 -12
  118. package/dist/elements/row-element.js +89 -9
  119. package/dist/elements/svg-element.d.ts +30 -6
  120. package/dist/elements/svg-element.js +91 -26
  121. package/dist/elements/text-element.d.ts +48 -13
  122. package/dist/elements/text-element.js +161 -116
  123. package/dist/forms/acroform.d.ts +9 -1
  124. package/dist/forms/acroform.js +83 -34
  125. package/dist/forms/appearance.d.ts +17 -6
  126. package/dist/forms/appearance.js +18 -16
  127. package/dist/ir/display-list.d.ts +18 -3
  128. package/dist/layout/box-constraints.d.ts +17 -0
  129. package/dist/layout/box-constraints.js +31 -0
  130. package/dist/layout/collect-images.d.ts +6 -4
  131. package/dist/layout/collect-images.js +14 -8
  132. package/dist/layout/flex-item.d.ts +11 -0
  133. package/dist/layout/flex-item.js +51 -0
  134. package/dist/layout/fragmentation.d.ts +23 -9
  135. package/dist/layout/fragmentation.js +62 -19
  136. package/dist/layout/layout-cache.d.ts +53 -0
  137. package/dist/layout/layout-cache.js +110 -0
  138. package/dist/platform/browser-fs.d.ts +1 -2
  139. package/dist/platform/browser-fs.js +3 -6
  140. package/dist/platform/node-fs.d.ts +0 -1
  141. package/dist/platform/node-fs.js +0 -4
  142. package/dist/renderer/anchor-renderer.d.ts +2 -1
  143. package/dist/renderer/anchor-renderer.js +5 -4
  144. package/dist/renderer/bookmark-renderer.d.ts +2 -1
  145. package/dist/renderer/bookmark-renderer.js +5 -4
  146. package/dist/renderer/canvas-renderer.d.ts +2 -1
  147. package/dist/renderer/canvas-renderer.js +5 -2
  148. package/dist/renderer/column-break-renderer.d.ts +8 -0
  149. package/dist/renderer/column-break-renderer.js +15 -0
  150. package/dist/renderer/container-renderer.d.ts +2 -1
  151. package/dist/renderer/container-renderer.js +6 -5
  152. package/dist/renderer/default-text-style-renderer.d.ts +2 -1
  153. package/dist/renderer/default-text-style-renderer.js +2 -3
  154. package/dist/renderer/deferred-renderer.d.ts +2 -1
  155. package/dist/renderer/deferred-renderer.js +2 -3
  156. package/dist/renderer/emoji-image.js +3 -1
  157. package/dist/renderer/expanded-renderer.d.ts +2 -1
  158. package/dist/renderer/expanded-renderer.js +2 -3
  159. package/dist/renderer/flow-renderer.d.ts +16 -0
  160. package/dist/renderer/flow-renderer.js +21 -0
  161. package/dist/renderer/form-field-renderer.d.ts +2 -1
  162. package/dist/renderer/form-field-renderer.js +16 -3
  163. package/dist/renderer/image-renderer.d.ts +2 -1
  164. package/dist/renderer/image-renderer.js +33 -53
  165. package/dist/renderer/index.d.ts +2 -0
  166. package/dist/renderer/index.js +2 -0
  167. package/dist/renderer/keep-together-renderer.d.ts +2 -1
  168. package/dist/renderer/keep-together-renderer.js +2 -3
  169. package/dist/renderer/line-renderer.d.ts +2 -1
  170. package/dist/renderer/line-renderer.js +7 -2
  171. package/dist/renderer/link-renderer.d.ts +2 -1
  172. package/dist/renderer/link-renderer.js +36 -5
  173. package/dist/renderer/padding-renderer.d.ts +2 -1
  174. package/dist/renderer/padding-renderer.js +5 -5
  175. package/dist/renderer/page-break-renderer.d.ts +6 -5
  176. package/dist/renderer/page-break-renderer.js +14 -7
  177. package/dist/renderer/page-builder-renderer.d.ts +2 -1
  178. package/dist/renderer/page-builder-renderer.js +2 -3
  179. package/dist/renderer/page-renderer.js +30 -10
  180. package/dist/renderer/pdf-backend.d.ts +4 -3
  181. package/dist/renderer/pdf-backend.js +14 -7
  182. package/dist/renderer/pdf-document-renderer.d.ts +1 -8
  183. package/dist/renderer/pdf-document-renderer.js +17 -17
  184. package/dist/renderer/pdf-renderer.js +20 -11
  185. package/dist/renderer/positioned-renderer.d.ts +18 -1
  186. package/dist/renderer/positioned-renderer.js +43 -6
  187. package/dist/renderer/rectangle-renderer.d.ts +2 -1
  188. package/dist/renderer/rectangle-renderer.js +18 -8
  189. package/dist/renderer/repeating-header-renderer.d.ts +2 -1
  190. package/dist/renderer/repeating-header-renderer.js +6 -5
  191. package/dist/renderer/rotated-renderer.d.ts +2 -1
  192. package/dist/renderer/rotated-renderer.js +9 -5
  193. package/dist/renderer/row-renderer.d.ts +2 -1
  194. package/dist/renderer/row-renderer.js +6 -5
  195. package/dist/renderer/struct-group-renderer.d.ts +2 -1
  196. package/dist/renderer/struct-group-renderer.js +2 -3
  197. package/dist/renderer/svg-renderer.d.ts +2 -1
  198. package/dist/renderer/svg-renderer.js +8 -2
  199. package/dist/renderer/text-renderer.d.ts +4 -6
  200. package/dist/renderer/text-renderer.js +114 -127
  201. package/dist/svg/gradients.js +5 -0
  202. package/dist/svg/index.d.ts +1 -1
  203. package/dist/svg/index.js +36 -6
  204. package/dist/svg/style.d.ts +2 -0
  205. package/dist/svg/style.js +12 -1
  206. package/dist/text/advance.d.ts +4 -0
  207. package/dist/text/advance.js +17 -4
  208. package/dist/text/bidi.d.ts +0 -2
  209. package/dist/text/bidi.js +0 -10
  210. package/dist/text/glyph-coverage.d.ts +1 -1
  211. package/dist/text/glyph-coverage.js +7 -3
  212. package/dist/text/hyphen-breaks.d.ts +17 -0
  213. package/dist/text/hyphen-breaks.js +54 -0
  214. package/dist/text/line-breaker.d.ts +55 -29
  215. package/dist/text/line-breaker.js +453 -188
  216. package/dist/text/line-metrics.d.ts +8 -3
  217. package/dist/text/line-metrics.js +11 -2
  218. package/dist/text/pdf-text-string.d.ts +21 -0
  219. package/dist/text/pdf-text-string.js +114 -0
  220. package/dist/text/text-decoration.d.ts +5 -5
  221. package/dist/text/text-decoration.js +5 -5
  222. package/dist/text/word-splitting.d.ts +15 -2
  223. package/dist/text/word-splitting.js +45 -13
  224. package/dist/utils/afm-parser.d.ts +2 -0
  225. package/dist/utils/afm-parser.js +10 -1
  226. package/dist/utils/dest-registry.d.ts +1 -1
  227. package/dist/utils/dest-registry.js +3 -2
  228. package/dist/utils/fetch-bytes.d.ts +19 -0
  229. package/dist/utils/fetch-bytes.js +79 -0
  230. package/dist/utils/flex-layout.d.ts +16 -14
  231. package/dist/utils/flex-layout.js +427 -217
  232. package/dist/utils/font-metrics.d.ts +4 -3
  233. package/dist/utils/image-helper.d.ts +59 -11
  234. package/dist/utils/image-helper.js +209 -81
  235. package/dist/utils/pdf-object-manager.d.ts +17 -6
  236. package/dist/utils/pdf-object-manager.js +70 -20
  237. package/dist/utils/renderer-registry.d.ts +49 -2
  238. package/dist/utils/renderer-registry.js +40 -0
  239. package/dist/utils/struct-tree.d.ts +24 -0
  240. package/dist/utils/struct-tree.js +61 -9
  241. package/dist/utils/ttf-parser.d.ts +4 -0
  242. package/dist/utils/ttf-parser.js +30 -6
  243. package/dist/validators/element-validator.js +2 -1
  244. package/package.json +1 -1
  245. package/dist/elements/layout/sized-container-element.d.ts +0 -14
  246. 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;
@@ -63,6 +63,7 @@ export declare class ContainerElement extends SizedPDFElement implements Fragmen
63
63
  fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
64
64
  private cloneWithChildren;
65
65
  relativeSizeFactor(horizontal: boolean): number | undefined;
66
+ maxExtent(horizontal: boolean): number;
66
67
  /**
67
68
  * A Column stacks vertically, so a flex child of its OWN only ever needs a bounded height. But the need
68
69
  * also propagates: a `Spacer` two levels down is just as unable to resolve against an infinite axis, so
@@ -74,9 +75,15 @@ export declare class ContainerElement extends SizedPDFElement implements Fragmen
74
75
  * every existing layout untouched.
75
76
  */
76
77
  needsBoundedMain(horizontal: boolean): boolean;
78
+ /** A height of its own is a region already; without one, a region-sized child needs the page's. */
79
+ needsRegion(): boolean;
77
80
  /** A Column stacks vertically, so widths overlap and heights add up. */
78
81
  minIntrinsicMain(horizontal: boolean, ctx: LayoutContext): number;
79
82
  calculateLayout(constraints: BoxConstraints, offset: Offset, ctx: LayoutContext): Size;
83
+ protected computeSize(constraints: BoxConstraints, ctx: LayoutContext): Size;
84
+ /** The one sizing path: `layout` places the children (relative to this box), `measure` only sizes them. */
85
+ private sizeWith;
86
+ layoutChildren(): readonly PDFElement[];
80
87
  getProps(): ContainerElementParams;
81
88
  }
82
89
  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";
4
- import { childrenForceBreak, packChildren, } from "../layout/fragmentation.js";
5
- import { FlexiblePDFElement, SizedPDFElement, } from "./pdf-element.js";
5
+ import { BoxConstraints, extentSpecs, resolveSize, } from "../layout/box-constraints.js";
6
+ import { childrenForceBreak, packChildren, packedUnchanged, } from "../layout/fragmentation.js";
7
+ import { SizedPDFElement, withPaddingBase, } 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,20 @@ 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, withPaddingBase(ctx, undefined), 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
- if (remainder.length === 0 && !forceBreak)
53
+ if (remainder.length === 0 && !forceBreak && packedUnchanged(this.children, fitted)) {
47
54
  return { fitted: this, remainder: null };
55
+ }
56
+ // Nothing fit below the content above: move on whole, not as an empty shell left behind.
57
+ if (fitted.length === 0 && !forceBreak)
58
+ return { fitted: null, remainder: this };
48
59
  return {
49
60
  fitted: this.cloneWithChildren(fitted),
50
61
  // Only the CONTINUATION carries `breakAfter`, so a stack that splits across pages still breaks
@@ -58,10 +69,9 @@ export class ContainerElement extends SizedPDFElement {
58
69
  return new ContainerElement({
59
70
  x: this.x,
60
71
  y: this.y,
61
- // Carry the requested WIDTH (a %-width column keeps its width on every page); the height is
72
+ // Carry the WIDTH rules (a %-width or max-width column keeps them on every page); the height is
62
73
  // intentionally dropped so each fragment shrink-wraps to the content it actually holds.
63
- width: this.requested.width,
64
- widthFactor: this.requested.widthFactor,
74
+ ...widthRules(this.requested),
65
75
  children,
66
76
  gap: this.gap,
67
77
  main: this.main,
@@ -70,11 +80,14 @@ export class ContainerElement extends SizedPDFElement {
70
80
  wrap: this.wrap,
71
81
  alignContent: this.alignContent,
72
82
  breakAfter,
73
- });
83
+ }).asPieceOf(this);
74
84
  }
75
85
  relativeSizeFactor(horizontal) {
76
86
  return horizontal ? this.requested.widthFactor : this.requested.heightFactor;
77
87
  }
88
+ maxExtent(horizontal) {
89
+ return (horizontal ? this.requested.maxWidth : this.requested.maxHeight) ?? Infinity;
90
+ }
78
91
  /**
79
92
  * A Column stacks vertically, so a flex child of its OWN only ever needs a bounded height. But the need
80
93
  * also propagates: a `Spacer` two levels down is just as unable to resolve against an infinite axis, so
@@ -91,9 +104,15 @@ export class ContainerElement extends SizedPDFElement {
91
104
  : [this.requested.height, this.requested.heightFactor];
92
105
  if (requested.some((v) => v !== undefined))
93
106
  return false;
94
- const ownFlexChild = !horizontal && this.children.some((c) => c instanceof FlexiblePDFElement);
107
+ const ownFlexChild = !horizontal && this.children.some((c) => flexibleOf(c) !== undefined);
95
108
  return ownFlexChild || this.children.some((c) => c.needsBoundedMain(horizontal));
96
109
  }
110
+ /** A height of its own is a region already; without one, a region-sized child needs the page's. */
111
+ needsRegion() {
112
+ if (this.requested.height !== undefined || this.requested.heightFactor !== undefined)
113
+ return false;
114
+ return this.children.some((c) => c.needsRegion());
115
+ }
97
116
  /** A Column stacks vertically, so widths overlap and heights add up. */
98
117
  minIntrinsicMain(horizontal, ctx) {
99
118
  const content = stackMinIntrinsic(this.children, this.gap, !horizontal, horizontal, ctx, this.wrap);
@@ -102,6 +121,16 @@ export class ContainerElement extends SizedPDFElement {
102
121
  calculateLayout(constraints, offset, ctx) {
103
122
  this.x = offset.x;
104
123
  this.y = offset.y;
124
+ const size = this.sizeWith(constraints, ctx, "layout");
125
+ this.width = size.width;
126
+ this.height = size.height;
127
+ return size;
128
+ }
129
+ computeSize(constraints, ctx) {
130
+ return this.sizeWith(constraints, ctx, "measure");
131
+ }
132
+ /** The one sizing path: `layout` places the children (relative to this box), `measure` only sizes them. */
133
+ sizeWith(constraints, ctx, mode) {
105
134
  // Relative sizing: a pinned extent (fixed points or a fraction of the offered box, clamped into
106
135
  // the constraints) wins; else fill a bounded axis; else stay `undefined` and shrink-wrap below.
107
136
  // A Column nested in a Row gets unbounded width - passing 0 there would collapse children to 0.
@@ -127,21 +156,24 @@ export class ContainerElement extends SizedPDFElement {
127
156
  // Vertical flex stack (main = height, cross = width). The shared helper measures
128
157
  // fixed children, distributes the leftover to flex children, and places everything
129
158
  // in source order.
130
- result = FlexLayoutHelper.layout(this.children, VERTICAL_AXIS, mainAvail, crossAvail, this.y, this.x, {
159
+ result = FlexLayoutHelper.layout(this.children, VERTICAL_AXIS, mainAvail, crossAvail, {
131
160
  gap: this.gap,
132
161
  main: this.main,
133
162
  cross: this.cross,
134
163
  reverse: this.reverse,
135
164
  wrap: this.wrap,
136
165
  alignContent: this.alignContent,
137
- }, ctx);
166
+ },
167
+ // A Row's width is for its own children; these resolve against this column, what it offers.
168
+ withPaddingBase(ctx, undefined), mode);
138
169
  }
139
170
  // Pinned/bounded axis takes that extent; an unbounded one shrink-wraps to the children (height =
140
171
  // the stack, width = the widest child). Top-left coordinates; the container draws nothing, and
141
172
  // 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 };
173
+ return { width: boundedWidth ?? result.crossUsed, height: boundedHeight ?? result.mainUsed };
174
+ }
175
+ layoutChildren() {
176
+ return this.children ?? [];
145
177
  }
146
178
  getProps() {
147
179
  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
  }