@jasy/pdf 1.0.0-alpha.1 → 1.0.0-alpha.11

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 (279) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +35 -21
  3. package/dist/api/args.d.ts +1 -1
  4. package/dist/api/args.js +2 -5
  5. package/dist/api/color.d.ts +4 -4
  6. package/dist/api/color.js +11 -17
  7. package/dist/api/content.d.ts +20 -13
  8. package/dist/api/content.js +44 -29
  9. package/dist/api/descriptor.d.ts +2 -2
  10. package/dist/api/descriptor.js +121 -31
  11. package/dist/api/dimension.d.ts +76 -0
  12. package/dist/api/dimension.js +43 -0
  13. package/dist/api/font-url.d.ts +28 -0
  14. package/dist/api/font-url.js +134 -0
  15. package/dist/api/forms.d.ts +347 -0
  16. package/dist/api/forms.js +189 -0
  17. package/dist/api/gradient.d.ts +53 -0
  18. package/dist/api/gradient.js +97 -0
  19. package/dist/api/index.d.ts +13 -8
  20. package/dist/api/index.js +13 -24
  21. package/dist/api/insets.d.ts +19 -12
  22. package/dist/api/insets.js +19 -12
  23. package/dist/api/layout.d.ts +149 -25
  24. package/dist/api/layout.js +182 -51
  25. package/dist/api/page-builder.d.ts +40 -0
  26. package/dist/api/page-builder.js +44 -0
  27. package/dist/api/structure.d.ts +107 -13
  28. package/dist/api/structure.js +187 -88
  29. package/dist/api/table.d.ts +5 -5
  30. package/dist/api/table.js +38 -26
  31. package/dist/api/text.d.ts +90 -4
  32. package/dist/api/text.js +116 -31
  33. package/dist/assets/font-data.d.ts +2 -0
  34. package/dist/assets/font-data.js +21 -0
  35. package/dist/assets/font-data.ts +37 -0
  36. package/dist/common/color.js +1 -5
  37. package/dist/constants/page-sizes.js +3 -6
  38. package/dist/constants/pdf-parts.js +1 -4
  39. package/dist/crypto/legacy-handler.d.ts +43 -0
  40. package/dist/crypto/legacy-handler.js +145 -0
  41. package/dist/crypto/security-handler.d.ts +62 -0
  42. package/dist/crypto/security-handler.js +155 -0
  43. package/dist/crypto/webcrypto.d.ts +11 -0
  44. package/dist/crypto/webcrypto.js +62 -0
  45. package/dist/edit/acroform-reader.d.ts +70 -0
  46. package/dist/edit/acroform-reader.js +126 -0
  47. package/dist/edit/appearance.d.ts +47 -0
  48. package/dist/edit/appearance.js +171 -0
  49. package/dist/edit/document.d.ts +145 -0
  50. package/dist/edit/document.js +642 -0
  51. package/dist/edit/fill.d.ts +65 -0
  52. package/dist/edit/fill.js +424 -0
  53. package/dist/edit/flatten.d.ts +54 -0
  54. package/dist/edit/flatten.js +462 -0
  55. package/dist/edit/index.d.ts +20 -0
  56. package/dist/edit/index.js +18 -0
  57. package/dist/edit/lexer.d.ts +30 -0
  58. package/dist/edit/lexer.js +285 -0
  59. package/dist/edit/objects.d.ts +68 -0
  60. package/dist/edit/objects.js +83 -0
  61. package/dist/edit/writer.d.ts +60 -0
  62. package/dist/edit/writer.js +283 -0
  63. package/dist/elements/container-element.d.ts +50 -5
  64. package/dist/elements/container-element.js +100 -39
  65. package/dist/elements/forms/checkbox-element.d.ts +51 -0
  66. package/dist/elements/forms/checkbox-element.js +48 -0
  67. package/dist/elements/forms/choice-element.d.ts +53 -0
  68. package/dist/elements/forms/choice-element.js +59 -0
  69. package/dist/elements/forms/push-button-element.d.ts +56 -0
  70. package/dist/elements/forms/push-button-element.js +49 -0
  71. package/dist/elements/forms/radio-element.d.ts +52 -0
  72. package/dist/elements/forms/radio-element.js +49 -0
  73. package/dist/elements/forms/signature-element.d.ts +54 -0
  74. package/dist/elements/forms/signature-element.js +47 -0
  75. package/dist/elements/forms/text-field-element.d.ts +61 -0
  76. package/dist/elements/forms/text-field-element.js +54 -0
  77. package/dist/elements/image-element.d.ts +58 -6
  78. package/dist/elements/image-element.js +145 -111
  79. package/dist/elements/index.d.ts +26 -10
  80. package/dist/elements/index.js +26 -28
  81. package/dist/elements/layout/anchor-element.d.ts +23 -0
  82. package/dist/elements/layout/anchor-element.js +27 -0
  83. package/dist/elements/layout/bookmark-element.d.ts +27 -0
  84. package/dist/elements/layout/bookmark-element.js +30 -0
  85. package/dist/elements/layout/default-text-style-element.d.ts +30 -0
  86. package/dist/elements/layout/default-text-style-element.js +47 -0
  87. package/dist/elements/layout/deferred-element.d.ts +3 -3
  88. package/dist/elements/layout/deferred-element.js +4 -8
  89. package/dist/elements/layout/expanded-element.d.ts +6 -4
  90. package/dist/elements/layout/expanded-element.js +22 -17
  91. package/dist/elements/layout/keep-together-element.d.ts +42 -0
  92. package/dist/elements/layout/keep-together-element.js +85 -0
  93. package/dist/elements/layout/link-element.d.ts +33 -0
  94. package/dist/elements/layout/link-element.js +39 -0
  95. package/dist/elements/layout/padding-element.d.ts +10 -5
  96. package/dist/elements/layout/padding-element.js +25 -16
  97. package/dist/elements/layout/page-break-element.d.ts +17 -0
  98. package/dist/elements/layout/page-break-element.js +24 -0
  99. package/dist/elements/layout/page-builder-element.d.ts +32 -0
  100. package/dist/elements/layout/page-builder-element.js +45 -0
  101. package/dist/elements/layout/positioned-element.d.ts +47 -0
  102. package/dist/elements/layout/positioned-element.js +73 -0
  103. package/dist/elements/layout/repeating-header-element.d.ts +3 -3
  104. package/dist/elements/layout/repeating-header-element.js +8 -12
  105. package/dist/elements/layout/rotated-box-element.d.ts +35 -0
  106. package/dist/elements/layout/rotated-box-element.js +54 -0
  107. package/dist/elements/layout/rotated-element.d.ts +32 -0
  108. package/dist/elements/layout/rotated-element.js +41 -0
  109. package/dist/elements/layout/sized-container-element.d.ts +2 -2
  110. package/dist/elements/layout/sized-container-element.js +6 -11
  111. package/dist/elements/layout/struct-group.d.ts +28 -0
  112. package/dist/elements/layout/struct-group.js +42 -0
  113. package/dist/elements/line-element.d.ts +3 -3
  114. package/dist/elements/line-element.js +5 -10
  115. package/dist/elements/page-element.d.ts +26 -6
  116. package/dist/elements/page-element.js +72 -27
  117. package/dist/elements/pdf-document-element.d.ts +10 -4
  118. package/dist/elements/pdf-document-element.js +11 -10
  119. package/dist/elements/pdf-element.d.ts +150 -4
  120. package/dist/elements/pdf-element.js +138 -20
  121. package/dist/elements/rectangle-element.d.ts +52 -11
  122. package/dist/elements/rectangle-element.js +129 -36
  123. package/dist/elements/row-element.d.ts +45 -4
  124. package/dist/elements/row-element.js +66 -18
  125. package/dist/elements/text-element.d.ts +139 -11
  126. package/dist/elements/text-element.js +234 -57
  127. package/dist/forms/acroform.d.ts +63 -0
  128. package/dist/forms/acroform.js +427 -0
  129. package/dist/forms/appearance.d.ts +58 -0
  130. package/dist/forms/appearance.js +204 -0
  131. package/dist/forms/field.d.ts +164 -0
  132. package/dist/forms/field.js +1 -0
  133. package/dist/forms/pdf.d.ts +19 -0
  134. package/dist/forms/pdf.js +28 -0
  135. package/dist/index.d.ts +3 -3
  136. package/dist/index.js +3 -19
  137. package/dist/ir/display-list.d.ts +197 -8
  138. package/dist/ir/display-list.js +8 -2
  139. package/dist/layout/alignment.d.ts +10 -0
  140. package/dist/layout/alignment.js +6 -0
  141. package/dist/layout/box-constraints.d.ts +61 -0
  142. package/dist/layout/box-constraints.js +88 -6
  143. package/dist/layout/collect-images.d.ts +9 -0
  144. package/dist/layout/collect-images.js +34 -0
  145. package/dist/layout/fragmentation.d.ts +20 -1
  146. package/dist/layout/fragmentation.js +74 -18
  147. package/dist/layout/insets.d.ts +28 -0
  148. package/dist/layout/insets.js +20 -0
  149. package/dist/platform/browser-fs.d.ts +2 -0
  150. package/dist/platform/browser-fs.js +9 -0
  151. package/dist/platform/browser-image.d.ts +5 -0
  152. package/dist/platform/browser-image.js +13 -0
  153. package/dist/platform/node-fs.d.ts +2 -0
  154. package/dist/platform/node-fs.js +10 -0
  155. package/dist/platform/node-image.d.ts +5 -0
  156. package/dist/platform/node-image.js +9 -0
  157. package/dist/renderer/anchor-renderer.d.ts +6 -0
  158. package/dist/renderer/anchor-renderer.js +11 -0
  159. package/dist/renderer/bookmark-renderer.d.ts +6 -0
  160. package/dist/renderer/bookmark-renderer.js +11 -0
  161. package/dist/renderer/container-renderer.d.ts +3 -3
  162. package/dist/renderer/container-renderer.js +11 -28
  163. package/dist/renderer/default-text-style-renderer.d.ts +6 -0
  164. package/dist/renderer/default-text-style-renderer.js +10 -0
  165. package/dist/renderer/deferred-renderer.d.ts +3 -3
  166. package/dist/renderer/deferred-renderer.js +8 -23
  167. package/dist/renderer/emoji-image.d.ts +9 -0
  168. package/dist/renderer/emoji-image.js +68 -0
  169. package/dist/renderer/expanded-renderer.d.ts +3 -3
  170. package/dist/renderer/expanded-renderer.js +6 -21
  171. package/dist/renderer/form-field-renderer.d.ts +20 -0
  172. package/dist/renderer/form-field-renderer.js +12 -0
  173. package/dist/renderer/image-renderer.d.ts +4 -4
  174. package/dist/renderer/image-renderer.js +86 -75
  175. package/dist/renderer/index.d.ts +10 -10
  176. package/dist/renderer/index.js +10 -26
  177. package/dist/renderer/keep-together-renderer.d.ts +10 -0
  178. package/dist/renderer/keep-together-renderer.js +12 -0
  179. package/dist/renderer/line-renderer.d.ts +3 -3
  180. package/dist/renderer/line-renderer.js +13 -28
  181. package/dist/renderer/link-renderer.d.ts +6 -0
  182. package/dist/renderer/link-renderer.js +11 -0
  183. package/dist/renderer/padding-renderer.d.ts +3 -3
  184. package/dist/renderer/padding-renderer.js +6 -21
  185. package/dist/renderer/page-break-renderer.d.ts +14 -0
  186. package/dist/renderer/page-break-renderer.js +15 -0
  187. package/dist/renderer/page-builder-renderer.d.ts +6 -0
  188. package/dist/renderer/page-builder-renderer.js +11 -0
  189. package/dist/renderer/page-renderer.d.ts +2 -2
  190. package/dist/renderer/page-renderer.js +115 -77
  191. package/dist/renderer/pdf-backend.d.ts +31 -4
  192. package/dist/renderer/pdf-backend.js +287 -49
  193. package/dist/renderer/pdf-config.js +4 -7
  194. package/dist/renderer/pdf-document-class.d.ts +5 -5
  195. package/dist/renderer/pdf-document-class.js +27 -41
  196. package/dist/renderer/pdf-document-renderer.d.ts +19 -10
  197. package/dist/renderer/pdf-document-renderer.js +135 -91
  198. package/dist/renderer/pdf-renderer.d.ts +2 -2
  199. package/dist/renderer/pdf-renderer.js +165 -90
  200. package/dist/renderer/positioned-renderer.d.ts +6 -0
  201. package/dist/renderer/positioned-renderer.js +10 -0
  202. package/dist/renderer/rectangle-renderer.d.ts +3 -3
  203. package/dist/renderer/rectangle-renderer.js +49 -44
  204. package/dist/renderer/repeating-header-renderer.d.ts +3 -3
  205. package/dist/renderer/repeating-header-renderer.js +10 -26
  206. package/dist/renderer/rotated-renderer.d.ts +7 -0
  207. package/dist/renderer/rotated-renderer.js +29 -0
  208. package/dist/renderer/row-renderer.d.ts +3 -3
  209. package/dist/renderer/row-renderer.js +11 -28
  210. package/dist/renderer/struct-group-renderer.d.ts +10 -0
  211. package/dist/renderer/struct-group-renderer.js +24 -0
  212. package/dist/renderer/text-renderer.d.ts +9 -5
  213. package/dist/renderer/text-renderer.js +437 -89
  214. package/dist/text/advance.d.ts +34 -0
  215. package/dist/text/advance.js +45 -0
  216. package/dist/text/arabic.d.ts +20 -0
  217. package/dist/text/arabic.js +101 -0
  218. package/dist/text/bidi.d.ts +33 -0
  219. package/dist/text/bidi.js +94 -0
  220. package/dist/text/emoji-codepoints.d.ts +1 -0
  221. package/dist/text/emoji-codepoints.js +29 -0
  222. package/dist/text/font-fallback.d.ts +25 -0
  223. package/dist/text/font-fallback.js +38 -0
  224. package/dist/text/glyph-coverage.d.ts +15 -0
  225. package/dist/text/glyph-coverage.js +77 -0
  226. package/dist/text/line-breaker.d.ts +50 -12
  227. package/dist/text/line-breaker.js +200 -54
  228. package/dist/text/line-metrics.d.ts +49 -0
  229. package/dist/text/line-metrics.js +57 -0
  230. package/dist/text/orphans-widows.d.ts +27 -0
  231. package/dist/text/orphans-widows.js +53 -0
  232. package/dist/text/shape.d.ts +29 -0
  233. package/dist/text/shape.js +67 -0
  234. package/dist/text/text-decoration.d.ts +50 -0
  235. package/dist/text/text-decoration.js +62 -0
  236. package/dist/text/text-style.d.ts +60 -0
  237. package/dist/text/text-style.js +79 -0
  238. package/dist/text/whitespace.d.ts +4 -0
  239. package/dist/text/whitespace.js +43 -0
  240. package/dist/utils/afm-parser.d.ts +34 -0
  241. package/dist/utils/afm-parser.js +76 -17
  242. package/dist/utils/bytes.d.ts +24 -0
  243. package/dist/utils/bytes.js +76 -0
  244. package/dist/utils/dest-registry.d.ts +22 -0
  245. package/dist/utils/dest-registry.js +32 -0
  246. package/dist/utils/flex-layout.d.ts +36 -9
  247. package/dist/utils/flex-layout.js +207 -30
  248. package/dist/utils/font-metrics.d.ts +26 -1
  249. package/dist/utils/font-metrics.js +1 -2
  250. package/dist/utils/font-path.js +3 -6
  251. package/dist/utils/gsub.d.ts +36 -0
  252. package/dist/utils/gsub.js +178 -0
  253. package/dist/utils/image-helper.d.ts +6 -5
  254. package/dist/utils/image-helper.js +101 -111
  255. package/dist/utils/inflate.d.ts +16 -0
  256. package/dist/utils/inflate.js +52 -0
  257. package/dist/utils/md5.d.ts +4 -0
  258. package/dist/utils/md5.js +79 -0
  259. package/dist/utils/outline.d.ts +21 -0
  260. package/dist/utils/outline.js +79 -0
  261. package/dist/utils/pdf-object-manager.d.ts +127 -8
  262. package/dist/utils/pdf-object-manager.js +615 -141
  263. package/dist/utils/renderer-registry.d.ts +13 -1
  264. package/dist/utils/renderer-registry.js +23 -6
  265. package/dist/utils/struct-tree.d.ts +44 -0
  266. package/dist/utils/struct-tree.js +129 -0
  267. package/dist/utils/ttf-parser.d.ts +134 -2
  268. package/dist/utils/ttf-parser.js +873 -50
  269. package/dist/utils/ttf-subsetter.d.ts +1 -1
  270. package/dist/utils/ttf-subsetter.js +40 -42
  271. package/dist/utils/ua-xmp.d.ts +9 -0
  272. package/dist/utils/ua-xmp.js +32 -0
  273. package/dist/utils/utf8-to-windows1252-encoder.d.ts +6 -0
  274. package/dist/utils/utf8-to-windows1252-encoder.js +11 -4
  275. package/dist/utils/woff.d.ts +10 -0
  276. package/dist/utils/woff.js +116 -0
  277. package/dist/validators/element-validator.d.ts +2 -2
  278. package/dist/validators/element-validator.js +31 -26
  279. package/package.json +22 -2
@@ -1,5 +1,6 @@
1
- import { Color } from "../common/color";
2
- import { FontStyle } from "../utils/pdf-object-manager";
1
+ import { Color } from "../common/color.js";
2
+ import { FontStyle } from "../utils/pdf-object-manager.js";
3
+ import type { FormFieldSpec, FieldStyle } from "../forms/field.js";
3
4
  /**
4
5
  * Display list - the seam between layout and the PDF backend.
5
6
  *
@@ -11,6 +12,16 @@ import { FontStyle } from "../utils/pdf-object-manager";
11
12
  * Coordinates are in PDF points (1/72"). Which origin they use is still the
12
13
  * producer's concern for now; centralizing the Y-flip at this seam is Phase 3.
13
14
  */
15
+ /**
16
+ * Structure tag for accessible (PDF/UA) tagging. `role` is the PDF structure type the content maps to
17
+ * (`P`, `H1`..`H6`, `Figure`, `TD`, …); `key` groups the IR nodes of one logical element (e.g. every line
18
+ * of a paragraph) into a single structure element; `alt` is the alternate text for a figure. A drawable
19
+ * node WITHOUT a tag is emitted as an Artifact (decoration, skipped by screen readers).
20
+ */
21
+ export interface StructTag {
22
+ role: string;
23
+ key: number;
24
+ }
14
25
  /**
15
26
  * A single positioned run of text in one font / size / color. Line wrapping has
16
27
  * already happened upstream: the producer emits one `TextRun` per laid-out line or
@@ -21,23 +32,43 @@ export interface TextRun {
21
32
  x: number;
22
33
  y: number;
23
34
  text: string;
35
+ /** The glyphs to draw, when the run was SHAPED. The backend emits these instead of encoding `text`,
36
+ * which stays logical for `ToUnicode` and the accessible tree. */
37
+ glyphs?: number[];
24
38
  fontFamily: string;
25
39
  fontStyle: FontStyle;
26
40
  fontSize: number;
27
41
  color: Color;
42
+ letterSpacing?: number;
43
+ tag?: StructTag;
28
44
  }
29
45
  /** An axis-aligned rectangle. An absent `fill` or `stroke` means that part is not drawn. */
46
+ /**
47
+ * Corner radii in points (CSS `border-radius`). Any corner left out falls back to 0. A single number
48
+ * everywhere is the common case and stays a plain `number` on the nodes, so nothing changed for a
49
+ * document that never asks for different corners.
50
+ */
51
+ export interface Radii {
52
+ tl?: number;
53
+ tr?: number;
54
+ br?: number;
55
+ bl?: number;
56
+ }
57
+ /** True when any corner asks for a radius - the gate between a plain rect and a curved path. */
58
+ export declare function isRounded(radius: number | Radii | undefined): boolean;
30
59
  export interface Rect {
31
60
  type: "rect";
32
61
  x: number;
33
62
  y: number;
34
63
  width: number;
35
64
  height: number;
36
- fill?: Color;
65
+ /** Solid colour, or a gradient whose anchor points are in absolute page coordinates. */
66
+ fill?: Color | Gradient;
37
67
  stroke?: Color;
38
68
  strokeWidth: number;
39
- /** Corner radius in points; absent/0 = sharp corners (plain `re`). */
40
- radius?: number;
69
+ /** Corner radius in points - one number for all four, or per corner. Absent/0 = sharp (plain `re`). */
70
+ radius?: number | Radii;
71
+ tag?: StructTag;
41
72
  }
42
73
  /** A straight line segment between two points. */
43
74
  export interface Line {
@@ -48,6 +79,7 @@ export interface Line {
48
79
  y2: number;
49
80
  stroke: Color;
50
81
  strokeWidth: number;
82
+ tag?: StructTag;
51
83
  }
52
84
  /** A raster image, already resolved to bytes by the producer (no `CustomImage` here). */
53
85
  export interface Image {
@@ -60,6 +92,8 @@ export interface Image {
60
92
  intrinsicHeight: number;
61
93
  data: string;
62
94
  imageType: string;
95
+ /** Flate-compressed DeviceGray alpha channel for a transparent PNG, embedded as the XObject's /SMask. */
96
+ smask?: string;
63
97
  /** cover/contain fits clip the placement to the element's original frame. */
64
98
  clip?: {
65
99
  x: number;
@@ -67,8 +101,163 @@ export interface Image {
67
101
  width: number;
68
102
  height: number;
69
103
  };
70
- /** Corner radius in points for the image box; absent/0 = sharp corners. */
71
- radius?: number;
104
+ /** Corner radius in points for the image box - one number or per corner; absent/0 = sharp. */
105
+ radius?: number | Radii;
106
+ tag?: StructTag;
107
+ }
108
+ /**
109
+ * Pushes a clipping region (a rectangle, rounded when `radius` is set). Everything between this and
110
+ * the matching `ClipPop` is clipped to it - what a Box with `overflow: "hidden"` wraps its children
111
+ * in, so a `Positioned` child gets cropped at the box edge instead of spilling over.
112
+ */
113
+ export interface ClipPush {
114
+ type: "clip-push";
115
+ x: number;
116
+ y: number;
117
+ width: number;
118
+ height: number;
119
+ radius?: number | Radii;
120
+ }
121
+ /** Closes the most recent `ClipPush` (restores the graphics state). */
122
+ export interface ClipPop {
123
+ type: "clip-pop";
124
+ }
125
+ /**
126
+ * A hyperlink over a rectangle. Unlike every other IR node this draws NOTHING into the content stream -
127
+ * it becomes a PDF Link ANNOTATION on the page (`/Annots`), a side channel next to the drawing. Exactly
128
+ * one target is set: `href` is an external URL (a `/URI` action); `dest` is a named destination inside
129
+ * this document (a `/GoTo` action, resolved via the catalog `/Names /Dests` tree - see `Anchor`). The
130
+ * rect is the clickable region in top-left engine coords; the Y-flip converts it to PDF page space.
131
+ *
132
+ * NOTE: a Link inside a `TransformPush` is NOT yet rotated with it - the annotation rect lives in page
133
+ * space and ignores the content-stream matrix. Completing that is a planned follow-up (see todo.md).
134
+ */
135
+ export interface Link {
136
+ type: "link";
137
+ x: number;
138
+ y: number;
139
+ width: number;
140
+ height: number;
141
+ href?: string;
142
+ dest?: string;
143
+ }
144
+ /**
145
+ * A named destination anchor - a jump TARGET for an internal `Link({ to })`. Like `Link` it draws
146
+ * NOTHING; it registers `name` -> (this page, `y`) in the catalog `/Names /Dests` name tree. `y` is the
147
+ * top of the target in top-left engine coords (the Y-flip turns it into the page-space scroll target).
148
+ */
149
+ export interface Anchor {
150
+ type: "anchor";
151
+ y: number;
152
+ name: string;
153
+ }
154
+ /**
155
+ * A document-outline (bookmark) anchor. Like `Link` it draws NOTHING - it becomes an entry in the PDF
156
+ * outline tree (`/Outlines`), a side channel the viewer shows in its bookmark panel. `y` is the top of
157
+ * the target in top-left engine coords (the Y-flip turns it into the page-space scroll target); `level`
158
+ * (1-based) nests the entry under the nearest preceding entry of a smaller level.
159
+ */
160
+ export interface Outline {
161
+ type: "outline";
162
+ y: number;
163
+ title: string;
164
+ level: number;
165
+ }
166
+ /**
167
+ * An interactive form field (AcroForm). Like `Link` it draws NOTHING into the content stream - it becomes
168
+ * a Widget ANNOTATION on the page (`/Annots`) and a field in the catalog `/AcroForm`, both built from the
169
+ * shared `FormFieldSpec`. The rect is the field box in top-left engine coords; the Y-flip converts it to
170
+ * page space. See `forms/acroform.ts` for the widget/field assembly.
171
+ */
172
+ export interface FormFieldNode {
173
+ type: "formfield";
174
+ x: number;
175
+ y: number;
176
+ width: number;
177
+ height: number;
178
+ field: FormFieldSpec;
179
+ style: FieldStyle;
180
+ }
181
+ /**
182
+ * Pushes an affine transform (a `q` + `cm` in the content stream): everything between this and the
183
+ * matching `TransformPop` is painted through `matrix`. This is what `Rotated` wraps its child's nodes
184
+ * in, so a subtree draws rotated (or scaled/translated) around a pivot without touching its layout.
185
+ *
186
+ * `matrix` is `[a, b, c, d, e, f]` in the engine's TOP-LEFT coordinates - the same space every other IR
187
+ * node uses. The Y-flip at the IR -> backend seam converts it to PDF's bottom-left once per page, so
188
+ * producers stay coordinate-system-blind (they never see a flipped matrix).
189
+ */
190
+ export interface TransformPush {
191
+ type: "transform-push";
192
+ matrix: [number, number, number, number, number, number];
193
+ }
194
+ /** Closes the most recent `TransformPush` (restores the graphics state). */
195
+ export interface TransformPop {
196
+ type: "transform-pop";
197
+ }
198
+ /**
199
+ * One drawing command of a `Path`, in absolute page coordinates. Curves are CUBIC because PDF
200
+ * content streams have no quadratic operator - a producer converts TrueType quads to cubics before
201
+ * emitting. `z` closes the current subpath (a glyph contour).
202
+ */
203
+ export type PathCommand = {
204
+ op: "m";
205
+ x: number;
206
+ y: number;
207
+ } | {
208
+ op: "l";
209
+ x: number;
210
+ y: number;
211
+ } | {
212
+ op: "c";
213
+ x1: number;
214
+ y1: number;
215
+ x2: number;
216
+ y2: number;
217
+ x: number;
218
+ y: number;
219
+ } | {
220
+ op: "z";
221
+ };
222
+ /** One color stop of a gradient: a position 0..1 along the axis and its color. */
223
+ export interface GradientStop {
224
+ offset: number;
225
+ color: Color;
226
+ }
227
+ /** An axial (linear) gradient between two points, in absolute page coordinates. */
228
+ export interface LinearGradient {
229
+ type: "linear";
230
+ x0: number;
231
+ y0: number;
232
+ x1: number;
233
+ y1: number;
234
+ stops: GradientStop[];
235
+ extend: "pad" | "repeat" | "reflect";
236
+ }
237
+ /** A radial gradient between two circles, in absolute page coordinates. */
238
+ export interface RadialGradient {
239
+ type: "radial";
240
+ x0: number;
241
+ y0: number;
242
+ r0: number;
243
+ x1: number;
244
+ y1: number;
245
+ r1: number;
246
+ stops: GradientStop[];
247
+ extend: "pad" | "repeat" | "reflect";
248
+ }
249
+ export type Gradient = LinearGradient | RadialGradient;
250
+ /**
251
+ * A filled vector path - one or more closed subpaths sharing a single fill, using the nonzero
252
+ * winding rule (so a glyph's inner contour cuts a hole). This is what a color glyph's layers draw
253
+ * onto: each COLR layer is one `Path` filled with a solid `Color` (v0) or a `Gradient` (v1).
254
+ */
255
+ export interface Path {
256
+ type: "path";
257
+ commands: PathCommand[];
258
+ fill: Color | Gradient;
259
+ /** Accessibility structure tag (absent = drawn as an artifact); color-emoji layers stay untagged. */
260
+ tag?: StructTag;
72
261
  }
73
262
  /** The closed set of primitives the PDF backend knows how to draw. */
74
- export type IRNode = TextRun | Rect | Line | Image;
263
+ export type IRNode = TextRun | Rect | Line | Image | ClipPush | ClipPop | Path | Link | Outline | Anchor | FormFieldNode | TransformPush | TransformPop;
@@ -1,2 +1,8 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ /** True when any corner asks for a radius - the gate between a plain rect and a curved path. */
2
+ export function isRounded(radius) {
3
+ if (radius === undefined)
4
+ return false;
5
+ if (typeof radius === "number")
6
+ return radius > 0;
7
+ return ((radius.tl ?? 0) > 0 || (radius.tr ?? 0) > 0 || (radius.br ?? 0) > 0 || (radius.bl ?? 0) > 0);
8
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Flex alignment vocabulary. It lives here rather than in `utils/flex-layout.ts` because the base
3
+ * `PDFElement` needs `CrossAlign` for `alignSelf`, and flex-layout imports the elements - defining it
4
+ * there would close a cycle.
5
+ */
6
+ /** Distribution along the MAIN axis (CSS `justify-content`). */
7
+ export type MainAlign = "start" | "center" | "end" | "between" | "around";
8
+ /** Position/size ACROSS the axis (CSS `align-items` / `align-self`). `stretch` fills the cross extent;
9
+ * the others place the child at its natural cross size. */
10
+ export type CrossAlign = "start" | "center" | "end" | "stretch";
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Flex alignment vocabulary. It lives here rather than in `utils/flex-layout.ts` because the base
3
+ * `PDFElement` needs `CrossAlign` for `alignSelf`, and flex-layout imports the elements - defining it
4
+ * there would close a cycle.
5
+ */
6
+ export {};
@@ -41,9 +41,70 @@ export declare class BoxConstraints {
41
41
  * what a padding/border element hands its child after reserving its own insets.
42
42
  */
43
43
  deflate(horizontal: number, vertical: number): BoxConstraints;
44
+ /**
45
+ * Tightens this box by an element's own `min`/`max` on either axis. A max never widens the offered
46
+ * room, and a min may push past it - `minHeight: 500` in a 300pt region means 500, and pagination
47
+ * deals with the overflow, the same answer CSS gives.
48
+ */
49
+ narrow(minWidth?: number, maxWidth?: number, minHeight?: number, maxHeight?: number): BoxConstraints;
44
50
  /** Returns the constraints clamped to lie within `parent` (Flutter's enforce). */
45
51
  enforce(parent: BoxConstraints): BoxConstraints;
46
52
  }
53
+ /**
54
+ * Resolves a sized element's extent on one axis (relative sizing). An explicit point size (`fixed`)
55
+ * always wins; otherwise a `factor` (0..1) of the offered extent, but ONLY when that axis is bounded -
56
+ * a fraction of an unbounded axis has no meaning (Flutter's FractionallySizedBox under unbounded
57
+ * constraints). Returns `undefined` to mean "no explicit extent", i.e. fill the box or shrink-wrap.
58
+ * Shared by every sized element so `width: "50%"` behaves identically across Box / Column / Row / Image.
59
+ */
60
+ export declare function resolveExtent(fixed: number | undefined, factor: number | undefined, boundedMax: number, hasBounded: boolean): number | undefined;
61
+ /** What one axis of a sized element was asked for: a size, plus the bounds it must stay inside. */
62
+ export interface ExtentSpec {
63
+ fixed?: number;
64
+ factor?: number;
65
+ min?: number;
66
+ minFactor?: number;
67
+ max?: number;
68
+ maxFactor?: number;
69
+ }
70
+ /** The sizing options every sized element accepts. Each `*Factor` is the percentage form of its pair. */
71
+ export interface SizingParams {
72
+ width?: number;
73
+ height?: number;
74
+ widthFactor?: number;
75
+ heightFactor?: number;
76
+ minWidth?: number;
77
+ maxWidth?: number;
78
+ minHeight?: number;
79
+ maxHeight?: number;
80
+ minWidthFactor?: number;
81
+ maxWidthFactor?: number;
82
+ minHeightFactor?: number;
83
+ maxHeightFactor?: number;
84
+ /** width / height. Derives whichever axis is left open; see `resolveSize`. */
85
+ aspectRatio?: number;
86
+ }
87
+ /** Split `SizingParams` into the per-axis form `resolveSize` takes. */
88
+ export declare function extentSpecs(p: SizingParams): {
89
+ width: ExtentSpec;
90
+ height: ExtentSpec;
91
+ };
92
+ /**
93
+ * Resolve both axes of a sized element: relative sizing, then `aspectRatio`, then `min`/`max`.
94
+ *
95
+ * The order follows CSS: a ratio fills in the axis you left open, and min/max clamp afterwards - so an
96
+ * explicit bound wins over the ratio, exactly as `min-height` beats `aspect-ratio` in a browser.
97
+ *
98
+ * `min`/`max` also come back as NARROWED constraints, because an axis with no explicit size still has
99
+ * to obey them. `maxWidth` alone means "fill, but no wider than this", and the caller's own fill and
100
+ * shrink-wrap paths get that for free by using the returned constraints instead of the ones passed in.
101
+ * A `width` of `undefined` keeps its usual meaning: fill or shrink-wrap.
102
+ */
103
+ export declare function resolveSize(width: ExtentSpec, height: ExtentSpec, aspectRatio: number | undefined, constraints: BoxConstraints): {
104
+ width?: number;
105
+ height?: number;
106
+ constraints: BoxConstraints;
107
+ };
47
108
  /** The size an element resolves to and returns UP the tree. */
48
109
  export interface Size {
49
110
  width: number;
@@ -1,6 +1,3 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BoxConstraints = void 0;
4
1
  /**
5
2
  * The size constraints handed DOWN the tree during layout (Flutter's BoxConstraints
6
3
  * model). Pure geometry: a min/max range per axis, no position. An element receives a
@@ -15,7 +12,7 @@ exports.BoxConstraints = void 0;
15
12
  * remaining vertical space becomes a `maxHeight`, and "does this fit?" is a constraint
16
13
  * check, not a special case.
17
14
  */
18
- class BoxConstraints {
15
+ export class BoxConstraints {
19
16
  constructor(minWidth = 0, maxWidth = Infinity, minHeight = 0, maxHeight = Infinity) {
20
17
  this.minWidth = minWidth;
21
18
  this.maxWidth = maxWidth;
@@ -28,7 +25,7 @@ class BoxConstraints {
28
25
  }
29
26
  /** Tight only on the axes given; the others stay unbounded (0..Infinity). */
30
27
  static tightFor({ width, height }) {
31
- return new BoxConstraints(width !== null && width !== void 0 ? width : 0, width !== null && width !== void 0 ? width : Infinity, height !== null && height !== void 0 ? height : 0, height !== null && height !== void 0 ? height : Infinity);
28
+ return new BoxConstraints(width ?? 0, width ?? Infinity, height ?? 0, height ?? Infinity);
32
29
  }
33
30
  /** Caps each axis at `max` but allows anything down to zero (shrink-wrap). */
34
31
  static loose(maxWidth, maxHeight) {
@@ -65,9 +62,94 @@ class BoxConstraints {
65
62
  deflate(horizontal, vertical) {
66
63
  return new BoxConstraints(Math.max(0, this.minWidth - horizontal), Math.max(0, this.maxWidth - horizontal), Math.max(0, this.minHeight - vertical), Math.max(0, this.maxHeight - vertical));
67
64
  }
65
+ /**
66
+ * Tightens this box by an element's own `min`/`max` on either axis. A max never widens the offered
67
+ * room, and a min may push past it - `minHeight: 500` in a 300pt region means 500, and pagination
68
+ * deals with the overflow, the same answer CSS gives.
69
+ */
70
+ narrow(minWidth, maxWidth, minHeight, maxHeight) {
71
+ const lo = (own, current) => Math.max(current, own ?? 0);
72
+ const hi = (own, current) => own === undefined ? current : Math.min(current, own);
73
+ return new BoxConstraints(lo(minWidth, this.minWidth), Math.max(hi(maxWidth, this.maxWidth), lo(minWidth, this.minWidth)), lo(minHeight, this.minHeight), Math.max(hi(maxHeight, this.maxHeight), lo(minHeight, this.minHeight)));
74
+ }
68
75
  /** Returns the constraints clamped to lie within `parent` (Flutter's enforce). */
69
76
  enforce(parent) {
70
77
  return new BoxConstraints(Math.max(parent.minWidth, Math.min(this.minWidth, parent.maxWidth)), Math.max(parent.minWidth, Math.min(this.maxWidth, parent.maxWidth)), Math.max(parent.minHeight, Math.min(this.minHeight, parent.maxHeight)), Math.max(parent.minHeight, Math.min(this.maxHeight, parent.maxHeight)));
71
78
  }
72
79
  }
73
- exports.BoxConstraints = BoxConstraints;
80
+ /**
81
+ * Resolves a sized element's extent on one axis (relative sizing). An explicit point size (`fixed`)
82
+ * always wins; otherwise a `factor` (0..1) of the offered extent, but ONLY when that axis is bounded -
83
+ * a fraction of an unbounded axis has no meaning (Flutter's FractionallySizedBox under unbounded
84
+ * constraints). Returns `undefined` to mean "no explicit extent", i.e. fill the box or shrink-wrap.
85
+ * Shared by every sized element so `width: "50%"` behaves identically across Box / Column / Row / Image.
86
+ */
87
+ export function resolveExtent(fixed, factor, boundedMax, hasBounded) {
88
+ if (fixed !== undefined)
89
+ return fixed;
90
+ if (factor !== undefined && hasBounded)
91
+ return boundedMax * factor;
92
+ return undefined;
93
+ }
94
+ /** Split `SizingParams` into the per-axis form `resolveSize` takes. */
95
+ export function extentSpecs(p) {
96
+ return {
97
+ width: {
98
+ fixed: p.width,
99
+ factor: p.widthFactor,
100
+ min: p.minWidth,
101
+ minFactor: p.minWidthFactor,
102
+ max: p.maxWidth,
103
+ maxFactor: p.maxWidthFactor,
104
+ },
105
+ height: {
106
+ fixed: p.height,
107
+ factor: p.heightFactor,
108
+ min: p.minHeight,
109
+ minFactor: p.minHeightFactor,
110
+ max: p.maxHeight,
111
+ maxFactor: p.maxHeightFactor,
112
+ },
113
+ };
114
+ }
115
+ /**
116
+ * Resolve both axes of a sized element: relative sizing, then `aspectRatio`, then `min`/`max`.
117
+ *
118
+ * The order follows CSS: a ratio fills in the axis you left open, and min/max clamp afterwards - so an
119
+ * explicit bound wins over the ratio, exactly as `min-height` beats `aspect-ratio` in a browser.
120
+ *
121
+ * `min`/`max` also come back as NARROWED constraints, because an axis with no explicit size still has
122
+ * to obey them. `maxWidth` alone means "fill, but no wider than this", and the caller's own fill and
123
+ * shrink-wrap paths get that for free by using the returned constraints instead of the ones passed in.
124
+ * A `width` of `undefined` keeps its usual meaning: fill or shrink-wrap.
125
+ */
126
+ export function resolveSize(width, height, aspectRatio, constraints) {
127
+ const bounds = constraints.narrow(resolveExtent(width.min, width.minFactor, constraints.maxWidth, constraints.hasBoundedWidth), resolveExtent(width.max, width.maxFactor, constraints.maxWidth, constraints.hasBoundedWidth), resolveExtent(height.min, height.minFactor, constraints.maxHeight, constraints.hasBoundedHeight), resolveExtent(height.max, height.maxFactor, constraints.maxHeight, constraints.hasBoundedHeight));
128
+ // Against the OFFERED box, not the narrowed one: `width: "50%"` with `maxWidth: 100` in a 400pt
129
+ // region is 200 capped to 100, not 50% of 100. The clamp below applies the bound.
130
+ let w = resolveExtent(width.fixed, width.factor, constraints.maxWidth, constraints.hasBoundedWidth);
131
+ let h = resolveExtent(height.fixed, height.factor, constraints.maxHeight, constraints.hasBoundedHeight);
132
+ if (aspectRatio !== undefined && aspectRatio > 0) {
133
+ if (w !== undefined && h === undefined)
134
+ h = w / aspectRatio;
135
+ else if (h !== undefined && w === undefined)
136
+ w = h * aspectRatio;
137
+ else if (w === undefined && h === undefined) {
138
+ // Neither axis pinned: take the offered width and let the ratio give the height (CSS block
139
+ // behaviour). Falls back to the height when only that axis is bounded.
140
+ if (bounds.hasBoundedWidth) {
141
+ w = bounds.maxWidth;
142
+ h = w / aspectRatio;
143
+ }
144
+ else if (bounds.hasBoundedHeight) {
145
+ h = bounds.maxHeight;
146
+ w = h * aspectRatio;
147
+ }
148
+ }
149
+ }
150
+ return {
151
+ width: w === undefined ? undefined : bounds.constrainWidth(w),
152
+ height: h === undefined ? undefined : bounds.constrainHeight(h),
153
+ constraints: bounds,
154
+ };
155
+ }
@@ -0,0 +1,9 @@
1
+ import { PDFElement } from "../elements/pdf-element.js";
2
+ import { ImageElement } from "../elements/image-element.js";
3
+ /**
4
+ * Walks the element tree (via `getProps`, the same shallow-reflection style as the validator) and
5
+ * returns every `ImageElement`. A pre-layout pass uses this to resolve each image's intrinsic pixel
6
+ * size asynchronously: layout is synchronous and cannot await jimp, yet it needs the aspect ratio to
7
+ * give a width-only image its proportional height. Cheap - images are few and this runs once.
8
+ */
9
+ export declare function collectImageElements(root: PDFElement): ImageElement[];
@@ -0,0 +1,34 @@
1
+ import { PDFElement, hasChildrenProp, hasChildProp } from "../elements/pdf-element.js";
2
+ import { ImageElement } from "../elements/image-element.js";
3
+ /**
4
+ * Walks the element tree (via `getProps`, the same shallow-reflection style as the validator) and
5
+ * returns every `ImageElement`. A pre-layout pass uses this to resolve each image's intrinsic pixel
6
+ * size asynchronously: layout is synchronous and cannot await jimp, yet it needs the aspect ratio to
7
+ * give a width-only image its proportional height. Cheap - images are few and this runs once.
8
+ */
9
+ export function collectImageElements(root) {
10
+ const out = [];
11
+ const visit = (el) => {
12
+ if (el instanceof ImageElement)
13
+ out.push(el);
14
+ const props = el.getProps();
15
+ if (hasChildrenProp(props))
16
+ for (const c of props.children)
17
+ visit(c);
18
+ else if (hasChildProp(props))
19
+ visit(props.child);
20
+ // Wrappers that hold a subtree outside `children`/`child`: a Page's header/footer and a
21
+ // RepeatingHeaderElement's `body` (a Table with a header wraps its rows there). A
22
+ // DeferredElement's `composed` is null until layout runs, so it cannot be reached from this
23
+ // pre-layout pass; an image inside a deferred subtree just skips aspect pre-resolution.
24
+ const w = props;
25
+ if (w.header instanceof PDFElement)
26
+ visit(w.header);
27
+ if (w.footer instanceof PDFElement)
28
+ visit(w.footer);
29
+ if (w.body instanceof PDFElement)
30
+ visit(w.body);
31
+ };
32
+ visit(root);
33
+ return out;
34
+ }
@@ -1,4 +1,4 @@
1
- import { LayoutContext, PDFElement } from "../elements/pdf-element";
1
+ import { LayoutContext, PDFElement } from "../elements/pdf-element.js";
2
2
  /**
3
3
  * The result of splitting an element against a fragmentation region (a page, later a
4
4
  * column). `fitted` is the part that fits in the region; `remainder` describes what did
@@ -12,6 +12,10 @@ import { LayoutContext, PDFElement } from "../elements/pdf-element";
12
12
  export interface FragmentResult {
13
13
  fitted: PDFElement | null;
14
14
  remainder: PDFElement | null;
15
+ /** The split ended at a FORCED page break (a `PageBreak`), not because the region filled up. The
16
+ * remainder must start a fresh page, and an enclosing flow must stop packing too - so this bubbles
17
+ * up through nested `fragment()` calls. Absent/false for an ordinary height-driven break. */
18
+ forceBreak?: boolean;
15
19
  }
16
20
  /**
17
21
  * An element that knows how to split itself across regions. Width is already finalized
@@ -22,6 +26,20 @@ export interface Fragmentable {
22
26
  fragment(maxHeight: number, width: number, ctx: LayoutContext): FragmentResult;
23
27
  }
24
28
  export declare function isFragmentable(element: PDFElement): element is PDFElement & Fragmentable;
29
+ /**
30
+ * What to do when an element is taller than the whole page region and cannot be broken. We always
31
+ * still place it (clipped) so pagination terminates - this only controls how loud we are about it:
32
+ * `"error"` throws (the default; an unbreakable overflow is almost always a layout bug), `"warn"`
33
+ * logs and clips, `"ignore"` clips silently.
34
+ */
35
+ export type OverflowPolicy = "error" | "warn" | "ignore";
36
+ export declare function reportOverflow(child: PDFElement, childHeight: number, maxHeight: number, policy: OverflowPolicy): void;
37
+ /**
38
+ * True if packing `children` will hit a forced cut: a child that IS a break, contains one deeper in its
39
+ * subtree, or asks to break before/after itself. A flow container reports this from `hasForcedBreak` so
40
+ * its parent fragments it (runs `packChildren`) instead of placing it whole and swallowing the cut.
41
+ */
42
+ export declare function childrenForceBreak(children: PDFElement[]): boolean;
25
43
  /**
26
44
  * Packs a vertical stack of children into `maxHeight`, in order. Children are measured
27
45
  * against `width` (unbounded height) and added until one would overflow; that straddling
@@ -39,4 +57,5 @@ export declare function isFragmentable(element: PDFElement): element is PDFEleme
39
57
  export declare function packChildren(children: PDFElement[], maxHeight: number, width: number, ctx: LayoutContext, gap?: number): {
40
58
  fitted: PDFElement[];
41
59
  remainder: PDFElement[];
60
+ forceBreak: boolean;
42
61
  };