@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,11 +1,25 @@
1
- import { PDFDocumentElement } from "../elements/pdf-document-element";
2
- import { PageElement } from "../elements/page-element";
3
- import { PDFElement } from "../elements/pdf-element";
4
- import { PageSize } from "../constants/page-sizes";
5
- import { StackOptions } from "./layout";
6
- import { Insets } from "./insets";
7
- /** A page size: a `PageSize` enum, or a friendly name like `"A4"` / `"letter"` (any case). */
8
- export type PageSizeInput = PageSize | string;
1
+ import { PDFDocumentElement } from "../elements/pdf-document-element.js";
2
+ import { PageElement } from "../elements/page-element.js";
3
+ import { PDFElement } from "../elements/pdf-element.js";
4
+ import { DefaultTextStyleElement } from "../elements/layout/default-text-style-element.js";
5
+ import type { OverflowPolicy } from "../layout/fragmentation.js";
6
+ import { PageSize } from "../constants/page-sizes.js";
7
+ import { type EncryptOptions } from "../crypto/security-handler.js";
8
+ export type { EncryptOptions, Permissions } from "../crypto/security-handler.js";
9
+ import { StackOptions } from "./layout.js";
10
+ import { type UrlFontSource } from "./font-url.js";
11
+ import { Insets } from "./insets.js";
12
+ import { TextDefaults } from "./text.js";
13
+ /** A custom page size. Use the `mm()` helper for millimetres, or pass points directly. */
14
+ export interface CustomSize {
15
+ width: number;
16
+ height: number;
17
+ unit?: "pt" | "mm";
18
+ }
19
+ /** Millimetres → a custom page size, e.g. a 50×65 mm label: `Page({ size: mm(50, 65) }, …)`. */
20
+ export declare function mm(width: number, height: number): CustomSize;
21
+ /** A page size: a named `PageSize`/string (`"A4"`, `"letter"`, any case), or a `CustomSize`. */
22
+ export type PageSizeInput = PageSize | string | CustomSize;
9
23
  export interface PageOptions extends StackOptions {
10
24
  /** Page size (default A4). */
11
25
  size?: PageSizeInput;
@@ -24,17 +38,67 @@ export interface PageOptions extends StackOptions {
24
38
  */
25
39
  export declare function Page(children: PDFElement[]): PageElement;
26
40
  export declare function Page(opts: PageOptions, children: PDFElement[]): PageElement;
27
- export interface DocumentOptions {
41
+ /** Document options: PDF metadata plus the inheritable text defaults (font, size, color, lineHeight,
42
+ * align, bold/italic) every `Text` inherits unless it sets its own - Flutter's `DefaultTextStyle`. */
43
+ /** Where color emoji come from when a code point isn't in the current text font, so `Text("Hi 😅")`
44
+ * works in one string. Either a **font** (native vector, offline - recommended; register it in
45
+ * `fonts`) as a string name or `{ font }`, or an **image/CDN source** `{ url, format }` (react-pdf
46
+ * style: fetches `${url}${hexCodePoint}.${format}`, e.g. a Twemoji CDN). The font source is better
47
+ * (vector, no network); the image source exists for parity + as an escape hatch. */
48
+ export type EmojiSource = string | {
49
+ font: string;
50
+ } | {
51
+ url: string;
52
+ format?: "png";
53
+ };
54
+ export interface DocumentOptions extends TextDefaults {
28
55
  /** PDF metadata. */
29
56
  meta?: {
30
57
  title?: string;
31
58
  author?: string;
32
59
  };
60
+ /** Color-emoji fallback: emoji code points not in the text font render from this source. */
61
+ emoji?: EmojiSource;
62
+ }
63
+ /** A single font file for `addFont`: a `.ttf` path (read on Node) or its raw bytes (e.g. a browser
64
+ * upload). */
65
+ export type FontFileSource = string | Uint8Array;
66
+ /** A styled font family for `addFont`: one file per style, only `normal` required. `Text({ bold,
67
+ * italic })` then picks the right face, falling back to `normal`. */
68
+ export interface FontFamilyInput {
69
+ normal: FontFileSource;
70
+ bold?: FontFileSource;
71
+ italic?: FontFileSource;
72
+ boldItalic?: FontFileSource;
73
+ }
74
+ /** What `addFont` accepts: one file (path or bytes), or a styled family. */
75
+ export type FontSource = FontFileSource | FontFamilyInput;
76
+ /** The object the `Document(...)` factory returns: the element tree plus a managed font registry. */
77
+ export interface JasyDocument extends PDFDocumentElement {
78
+ /** Register a font under `name`, then use it via `Text({ font: name })`. The source is a `.ttf`
79
+ * path (read now, on Node), raw bytes, or a styled family. Re-adding a name overwrites it.
80
+ * A registered font that no `Text` actually uses is dropped at render and costs nothing. */
81
+ addFont(name: string, source: FontSource): this;
82
+ /** The same, from a URL. Async because the fetch happens NOW, exactly as `addFont` reads a path
83
+ * now - so a broken link fails where you asked for the font, not inside a later render.
84
+ * `await doc.addFontFromUrl("Inter", "https://…/Inter-Regular.ttf")`, or a styled family of URLs. */
85
+ addFontFromUrl(name: string, source: UrlFontSource): Promise<this>;
86
+ /** The names of the registered fonts. */
87
+ getFonts(): string[];
88
+ /** Whether a font is registered under `name`. */
89
+ hasFont(name: string): boolean;
33
90
  }
34
91
  /** The document root. `Document(pages)` or `Document(opts, pages)`. */
35
- export declare function Document(pages: PageElement[]): PDFDocumentElement;
36
- export declare function Document(opts: DocumentOptions, pages: PageElement[]): PDFDocumentElement;
37
- export type FontBytes = Buffer | Uint8Array;
92
+ export declare function Document(pages: PageElement[]): JasyDocument;
93
+ export declare function Document(opts: DocumentOptions, pages: PageElement[]): JasyDocument;
94
+ /**
95
+ * Sets default text properties (font/size/color/lineHeight/align/weight) for a whole subtree -
96
+ * Flutter's `DefaultTextStyle`, the per-section counterpart to the `Document` defaults. Children
97
+ * inherit them unless they set their own, and they layer onto whatever is inherited from above.
98
+ * `DefaultTextStyle(opts, children)`.
99
+ */
100
+ export declare function DefaultTextStyle(opts: TextDefaults, children: PDFElement[]): DefaultTextStyleElement;
101
+ export type FontBytes = Uint8Array;
38
102
  /** A font family: one `.ttf` per style. Only `normal` is required; `bold`/`italic`/`boldItalic`
39
103
  * are picked up automatically by `Text({ bold, italic })`, falling back to `normal` if absent. */
40
104
  export interface FontFamily {
@@ -73,8 +137,38 @@ export interface RenderOptions {
73
137
  standardFonts?: boolean;
74
138
  /** FlateDecode-compress the streams (default true). Set false for a greppable, uncompressed PDF. */
75
139
  compress?: boolean;
140
+ /** Kern the text - ON by default (still labelled experimental while we validate against react-pdf).
141
+ * Emits `TJ` adjustments from the font's own pairs: standard-14 (AFM), embedded `kern` table, and
142
+ * embedded `GPOS` Type 2, so every font path kerns. Set `false` to opt out (byte-identical to the
143
+ * pre-kerning output). Verified against headless Chrome and measured == drawn to the thousandth. */
144
+ kerning?: boolean;
145
+ /** Bake a form field's VALUE into its own appearance stream (`/AP`) - ON by default. Baked values
146
+ * render identically in every viewer and in print, and are what flattening (later) draws from. Set
147
+ * `false` to leave text and choice values to the viewer instead (`/NeedAppearances`, what
148
+ * react-pdf/pdfkit always do). Checkboxes, radios, buttons and signature boxes always carry their
149
+ * own appearance either way - there is no value to defer. Only meaningful with form fields present. */
150
+ fieldAppearances?: boolean;
151
+ /** What to do when content is taller than a page and cannot break: `"error"` throws (default),
152
+ * `"warn"` logs and clips, `"ignore"` clips silently. It is always clipped either way. */
153
+ onOverflow?: OverflowPolicy;
154
+ /**
155
+ * Called once, after the render, with the characters no font could draw - they were removed rather
156
+ * than emitted as `.notdef`, which PDF/A forbids. Deliberately a callback and not a policy: a
157
+ * missing tick mark must never stop an invoice from being produced, but the application should be
158
+ * able to SAY which character went missing, where a server log would never be read.
159
+ */
160
+ onMissingGlyphs?: (characters: string[]) => void;
161
+ /** Encrypt the PDF with a password (AES-256, the newest standard). NOT compatible with PDF/A
162
+ * (ZUGFeRD invoices) - encrypting one throws, since PDF/A forbids encryption. */
163
+ encrypt?: EncryptOptions;
164
+ /** Emit an accessible, tagged PDF (structure tree for screen readers). */
165
+ accessible?: boolean;
166
+ /** Document language for accessibility, e.g. `"en-US"` / `"de-DE"` (default `"en-US"`). */
167
+ lang?: string;
168
+ /** Document title (used by accessible readers; also good metadata). */
169
+ title?: string;
76
170
  }
77
171
  /** Renders a `Document(...)` tree to the raw PDF string. */
78
172
  export declare function renderPdf(doc: PDFDocumentElement, options?: RenderOptions): Promise<string>;
79
173
  /** Renders a `Document(...)` tree to PDF bytes (e.g. for a download / save dialog). */
80
- export declare function renderToBytes(doc: PDFDocumentElement, options?: RenderOptions): Promise<Uint8Array>;
174
+ export declare function renderToBytes(doc: PDFDocumentElement, options?: RenderOptions): Promise<Uint8Array<ArrayBuffer>>;
@@ -1,125 +1,224 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.Page = Page;
13
- exports.Document = Document;
14
- exports.renderPdf = renderPdf;
15
- exports.renderToBytes = renderToBytes;
16
- const pdf_document_element_1 = require("../elements/pdf-document-element");
17
- const page_element_1 = require("../elements/page-element");
18
- const page_sizes_1 = require("../constants/page-sizes");
19
- const pdf_config_1 = require("../renderer/pdf-config");
20
- const pdf_document_class_1 = require("../renderer/pdf-document-class");
21
- const pdf_object_manager_1 = require("../utils/pdf-object-manager");
22
- const utf8_to_windows1252_encoder_1 = require("../utils/utf8-to-windows1252-encoder");
23
- const layout_1 = require("./layout");
24
- const insets_1 = require("./insets");
25
- const PAGE_SIZE_VALUES = new Set(Object.values(page_sizes_1.PageSize));
1
+ import { readFileBytes } from "../platform/node-fs.js";
2
+ import { PDFDocumentElement } from "../elements/pdf-document-element.js";
3
+ import { PageElement } from "../elements/page-element.js";
4
+ import { DefaultTextStyleElement } from "../elements/layout/default-text-style-element.js";
5
+ import { PageSize, pageFormats } from "../constants/page-sizes.js";
6
+ import { resolveEdges } from "../layout/insets.js";
7
+ import { Orientation } from "../renderer/pdf-config.js";
8
+ import { PDFDocument } from "../renderer/pdf-document-class.js";
9
+ import { FontStyle } from "../utils/pdf-object-manager.js";
10
+ import { getArrayBuffer } from "../utils/utf8-to-windows1252-encoder.js";
11
+ import { createSecurityHandler } from "../crypto/security-handler.js";
12
+ import { uaXmp } from "../utils/ua-xmp.js";
13
+ import { Column } from "./layout.js";
14
+ import { loadFontFromUrl } from "./font-url.js";
15
+ import { toEdges } from "./insets.js";
16
+ import { toTextStyleOverride } from "./text.js";
17
+ const MM_TO_PT = 72 / 25.4; // 1 mm in PDF points
18
+ /** Millimetres → a custom page size, e.g. a 50×65 mm label: `Page({ size: mm(50, 65) }, …)`. */
19
+ export function mm(width, height) {
20
+ return { width, height, unit: "mm" };
21
+ }
22
+ const PAGE_SIZE_VALUES = new Set(Object.values(PageSize));
26
23
  function toPageSize(input) {
27
24
  const v = String(input).toLowerCase();
28
25
  if (!PAGE_SIZE_VALUES.has(v))
29
26
  throw new Error(`Unknown page size: "${input}"`);
30
27
  return v;
31
28
  }
29
+ /** Resolve `size` into config fields: a named `pageSize`, or an explicit [width, height] in points. */
30
+ function resolveSize(input) {
31
+ if (input !== undefined && typeof input === "object") {
32
+ const f = input.unit === "mm" ? MM_TO_PT : 1;
33
+ return { pageSize: PageSize.A4, customSize: [input.width * f, input.height * f] };
34
+ }
35
+ return { pageSize: input !== undefined ? toPageSize(input) : PageSize.A4 };
36
+ }
32
37
  /** Default page margin (all sides, points) when a `Page` doesn't set one. */
33
38
  const DEFAULT_MARGIN = 56;
34
- function Page(a, b) {
35
- var _a;
39
+ export function Page(a, b) {
36
40
  const isOpts = !Array.isArray(a);
37
41
  const opts = (isOpts ? a : {});
38
- const children = (isOpts ? (b !== null && b !== void 0 ? b : []) : a);
39
- const [top, right, bottom, left] = (0, insets_1.toEdges)((_a = opts.margin) !== null && _a !== void 0 ? _a : DEFAULT_MARGIN);
42
+ const children = (isOpts ? (b ?? []) : a);
43
+ const size = resolveSize(opts.size);
44
+ const landscape = opts.orientation === "landscape";
45
+ // A percentage margin resolves against the page WIDTH (see `Insets`), which is known right here -
46
+ // the page is the one box whose geometry does not depend on a parent.
47
+ const [w, h] = size.customSize ?? pageFormats[size.pageSize ?? PageSize.A4];
48
+ const pageWidth = landscape ? h : w;
49
+ const [top, right, bottom, left] = resolveEdges(toEdges(opts.margin ?? DEFAULT_MARGIN), pageWidth);
40
50
  const config = {
41
- pageSize: opts.size !== undefined ? toPageSize(opts.size) : page_sizes_1.PageSize.A4,
42
- orientation: opts.orientation === "landscape" ? pdf_config_1.Orientation.landscape : pdf_config_1.Orientation.portrait,
51
+ ...size,
52
+ orientation: landscape ? Orientation.landscape : Orientation.portrait,
43
53
  margin: { top, right, bottom, left },
44
54
  };
45
- return new page_element_1.PageElement({
55
+ return new PageElement({
46
56
  config,
47
57
  header: opts.header,
48
58
  footer: opts.footer,
49
- children: [(0, layout_1.Column)({ gap: opts.gap, justify: opts.justify, align: opts.align }, children)],
59
+ children: [Column({ gap: opts.gap, justify: opts.justify, align: opts.align }, children)],
50
60
  });
51
61
  }
52
62
  // Document metadata is a document-render concern, not part of the element tree, so it is
53
63
  // kept beside the returned element and picked up by `renderPdf`.
54
64
  const docMeta = new WeakMap();
55
- function Document(a, b) {
65
+ // Fonts registered via doc.addFont(...), kept beside the element (like meta) and registered on the
66
+ // object manager at render time. Path sources are read to bytes in addFont, so the map holds bytes.
67
+ const docFonts = new WeakMap();
68
+ // The document's color-emoji fallback source, kept beside the element and applied at render time.
69
+ const docEmoji = new WeakMap();
70
+ /** Reads any path sources to bytes, leaving bytes / families as-is. */
71
+ function resolveFontSource(source) {
72
+ const read = (s) => (typeof s === "string" ? readFileBytes(s) : s);
73
+ if (typeof source === "string" || source instanceof Uint8Array) {
74
+ return read(source);
75
+ }
76
+ const family = { normal: read(source.normal) };
77
+ if (source.bold)
78
+ family.bold = read(source.bold);
79
+ if (source.italic)
80
+ family.italic = read(source.italic);
81
+ if (source.boldItalic)
82
+ family.boldItalic = read(source.boldItalic);
83
+ return family;
84
+ }
85
+ export function Document(a, b) {
56
86
  const isOpts = !Array.isArray(a);
57
87
  const opts = (isOpts ? a : {});
58
- const pages = (isOpts ? (b !== null && b !== void 0 ? b : []) : a);
59
- const doc = new pdf_document_element_1.PDFDocumentElement({ children: pages });
88
+ const pages = (isOpts ? (b ?? []) : a);
89
+ const doc = new PDFDocumentElement({
90
+ children: pages,
91
+ defaultTextStyle: toTextStyleOverride(opts),
92
+ });
60
93
  if (opts.meta)
61
94
  docMeta.set(doc, opts.meta);
95
+ if (opts.emoji)
96
+ docEmoji.set(doc, opts.emoji);
97
+ // Managed font registry: addFont registers, getFonts/hasFont query, render reads it (below).
98
+ const registry = new Map();
99
+ docFonts.set(doc, registry);
100
+ doc.addFont = (name, source) => {
101
+ registry.set(name, resolveFontSource(source));
102
+ return doc;
103
+ };
104
+ doc.addFontFromUrl = async (name, source) => {
105
+ registry.set(name, await loadFontFromUrl(source));
106
+ return doc;
107
+ };
108
+ doc.getFonts = () => [...registry.keys()];
109
+ doc.hasFont = (name) => registry.has(name);
62
110
  return doc;
63
111
  }
112
+ /**
113
+ * Sets default text properties (font/size/color/lineHeight/align/weight) for a whole subtree -
114
+ * Flutter's `DefaultTextStyle`, the per-section counterpart to the `Document` defaults. Children
115
+ * inherit them unless they set their own, and they layer onto whatever is inherited from above.
116
+ * `DefaultTextStyle(opts, children)`.
117
+ */
118
+ export function DefaultTextStyle(opts, children) {
119
+ return new DefaultTextStyleElement({ style: toTextStyleOverride(opts), child: Column(children) });
120
+ }
64
121
  function isFontBytes(v) {
65
- return Buffer.isBuffer(v) || v instanceof Uint8Array;
122
+ return v instanceof Uint8Array;
66
123
  }
67
124
  /** Renders a `Document(...)` tree to the raw PDF string. */
68
- function renderPdf(doc, options) {
69
- return __awaiter(this, void 0, void 0, function* () {
70
- var _a, _b;
71
- const meta = docMeta.get(doc);
72
- const config = Object.assign(Object.assign({}, (meta ? { metaData: { title: meta.title, author: meta.author, keywords: [] } } : {})), ((options === null || options === void 0 ? void 0 : options.standardFonts) === false ? { registerStandardFonts: false } : {}));
73
- const fonts = (_a = options === null || options === void 0 ? void 0 : options.fonts) !== null && _a !== void 0 ? _a : {};
74
- const attachments = (_b = options === null || options === void 0 ? void 0 : options.attachments) !== null && _b !== void 0 ? _b : [];
75
- // A throwaway PDFDocument whose build() yields this tree, reusing the engine's standard
76
- // font registration + config handling (the constructor does both). Custom fonts are
77
- // registered here, before layout/render, so both the metrics and the backend see them.
78
- const Anon = class extends pdf_document_class_1.PDFDocument {
79
- constructor() {
80
- super(config);
81
- const om = this.objectManager;
82
- om.setCompress((options === null || options === void 0 ? void 0 : options.compress) !== false); // FlateDecode streams by default
83
- for (const [name, value] of Object.entries(fonts)) {
84
- if (isFontBytes(value)) {
85
- om.registerCustomFont(name, Buffer.from(value));
86
- }
87
- else {
88
- om.registerCustomFont(name, Buffer.from(value.normal), pdf_object_manager_1.FontStyle.Normal);
89
- if (value.bold)
90
- om.registerCustomFont(name, Buffer.from(value.bold), pdf_object_manager_1.FontStyle.Bold);
91
- if (value.italic)
92
- om.registerCustomFont(name, Buffer.from(value.italic), pdf_object_manager_1.FontStyle.Italic);
93
- if (value.boldItalic)
94
- om.registerCustomFont(name, Buffer.from(value.boldItalic), pdf_object_manager_1.FontStyle.BoldItalic);
95
- }
125
+ export async function renderPdf(doc, options) {
126
+ const meta = docMeta.get(doc);
127
+ const config = {
128
+ ...(meta ? { metaData: { title: meta.title, author: meta.author, keywords: [] } } : {}),
129
+ ...(options?.standardFonts === false ? { registerStandardFonts: false } : {}),
130
+ };
131
+ // Fonts registered via doc.addFont(...) plus any passed in options (options win on a name clash).
132
+ const registered = docFonts.get(doc);
133
+ const fonts = {
134
+ ...Object.fromEntries(registered ?? []),
135
+ ...options?.fonts,
136
+ };
137
+ const attachments = options?.attachments ?? [];
138
+ // The security handler's key derivation is async (WebCrypto), so build it here, before the sync
139
+ // PDFDocument constructor wires it into the object manager.
140
+ const securityHandler = options?.encrypt
141
+ ? await createSecurityHandler(options.encrypt)
142
+ : undefined;
143
+ // A throwaway PDFDocument whose build() yields this tree, reusing the engine's standard
144
+ // font registration + config handling (the constructor does both). Custom fonts are
145
+ // registered here, before layout/render, so both the metrics and the backend see them.
146
+ // Captured so the caller can be told what had to be dropped: `render()` is static and builds its own
147
+ // instance, so this is the only place the object manager surfaces.
148
+ let manager;
149
+ const Anon = class extends PDFDocument {
150
+ constructor() {
151
+ super(config);
152
+ const om = this.objectManager;
153
+ manager = om;
154
+ om.setCompress(options?.compress !== false); // FlateDecode streams by default
155
+ om.setOverflowPolicy(options?.onOverflow ?? "error");
156
+ om.setKerning(options?.kerning !== false); // on by default (experimental); `kerning:false` opts out
157
+ om.acroform.setBakeAppearances(options?.fieldAppearances !== false); // baked unless opted out
158
+ for (const [name, value] of Object.entries(fonts)) {
159
+ if (isFontBytes(value)) {
160
+ om.registerCustomFont(name, value);
96
161
  }
97
- for (const a of attachments) {
98
- om.attachFile(a.name, Buffer.from(a.data), {
99
- relationship: a.relationship,
100
- mimeType: a.mimeType,
101
- description: a.description,
102
- });
162
+ else {
163
+ om.registerCustomFont(name, value.normal, FontStyle.Normal);
164
+ if (value.bold)
165
+ om.registerCustomFont(name, value.bold, FontStyle.Bold);
166
+ if (value.italic)
167
+ om.registerCustomFont(name, value.italic, FontStyle.Italic);
168
+ if (value.boldItalic)
169
+ om.registerCustomFont(name, value.boldItalic, FontStyle.BoldItalic);
103
170
  }
104
- if (options === null || options === void 0 ? void 0 : options.xmp)
105
- om.setXmpMetadata(options.xmp);
106
- if (options === null || options === void 0 ? void 0 : options.outputIntent)
107
- om.setOutputIntent(Buffer.from(options.outputIntent));
108
- if (options === null || options === void 0 ? void 0 : options.pdfVersion)
109
- om.setPdfVersion(options.pdfVersion);
110
- if (options === null || options === void 0 ? void 0 : options.documentId)
111
- om.enableDocumentId();
112
171
  }
113
- build() {
114
- return doc;
172
+ for (const a of attachments) {
173
+ om.attachFile(a.name, a.data, {
174
+ relationship: a.relationship,
175
+ mimeType: a.mimeType,
176
+ description: a.description,
177
+ });
115
178
  }
116
- };
117
- return Anon.render();
118
- });
179
+ // Color-emoji fallback (Document({ emoji })): a font (native vector) or a CDN/image source.
180
+ const emoji = docEmoji.get(doc);
181
+ if (typeof emoji === "string")
182
+ om.setEmojiFont(emoji);
183
+ else if (emoji && "font" in emoji)
184
+ om.setEmojiFont(emoji.font);
185
+ else if (emoji && "url" in emoji)
186
+ om.setEmojiImageSource(emoji.url, emoji.format ?? "png");
187
+ if (options?.xmp)
188
+ om.setXmpMetadata(options.xmp);
189
+ if (options?.outputIntent)
190
+ om.setOutputIntent(options.outputIntent);
191
+ if (options?.pdfVersion)
192
+ om.setPdfVersion(options.pdfVersion);
193
+ if (options?.documentId)
194
+ om.enableDocumentId();
195
+ if (securityHandler)
196
+ om.setSecurityHandler(securityHandler);
197
+ if (options?.accessible) {
198
+ om.struct.enabled = true;
199
+ if (options.lang)
200
+ om.struct.lang = options.lang;
201
+ // Fall back to the document's own title (Document({ meta })) when no explicit title is given.
202
+ const title = options.title ?? meta?.title;
203
+ if (title)
204
+ om.struct.title = title;
205
+ // Declare PDF/UA-1 in the XMP metadata, unless the caller supplied their own packet.
206
+ if (!options.xmp)
207
+ om.setXmpMetadata(uaXmp({ title }));
208
+ }
209
+ }
210
+ build() {
211
+ return doc;
212
+ }
213
+ };
214
+ const pdf = await Anon.render();
215
+ const missing = manager?.getMissingGlyphs() ?? [];
216
+ if (missing.length > 0)
217
+ options?.onMissingGlyphs?.(missing.map((cp) => String.fromCodePoint(cp)));
218
+ return pdf;
119
219
  }
120
220
  /** Renders a `Document(...)` tree to PDF bytes (e.g. for a download / save dialog). */
121
- function renderToBytes(doc, options) {
122
- return __awaiter(this, void 0, void 0, function* () {
123
- return new Uint8Array((0, utf8_to_windows1252_encoder_1.getArrayBuffer)(yield renderPdf(doc, options)));
124
- });
221
+ export async function renderToBytes(doc, options) {
222
+ // Plain ArrayBuffer-backed, so the bytes drop straight into a Blob/Response (BlobPart needs <ArrayBuffer>).
223
+ return new Uint8Array(getArrayBuffer(await renderPdf(doc, options)));
125
224
  }
@@ -1,6 +1,6 @@
1
- import { PDFElement } from "../elements/pdf-element";
2
- import { Insets } from "./insets";
3
- import { ColorInput } from "./color";
1
+ import { PDFElement } from "../elements/pdf-element.js";
2
+ import { Insets } from "./insets.js";
3
+ import { ColorInput } from "./color.js";
4
4
  /**
5
5
  * A column width: a `number` of points (fixed), an `"Nfr"` fraction (splits the leftover
6
6
  * width), or `"auto"` (as wide as the widest cell in that column - measured at layout time).
@@ -21,11 +21,11 @@ export interface TableOptions {
21
21
  colGap?: number;
22
22
  /** Padding inside every cell (default none). */
23
23
  cellPadding?: Insets;
24
- /** Grid line colour around every cell. Draws the complete grid once - don't add your own
24
+ /** Grid line color around every cell. Draws the complete grid once - don't add your own
25
25
  * frame Box, or the outer edges double up. */
26
26
  cellBorder?: ColorInput;
27
27
  /** A single thin horizontal rule under the header row and along the foot of the table (e.g. a
28
- * light-grey separator). Independent of `cellBorder`, which draws the full grid. */
28
+ * light-gray separator). Independent of `cellBorder`, which draws the full grid. */
29
29
  rule?: ColorInput;
30
30
  }
31
31
  /**
package/dist/api/table.js CHANGED
@@ -1,13 +1,11 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Table = Table;
4
- const box_constraints_1 = require("../layout/box-constraints");
5
- const repeating_header_element_1 = require("../elements/layout/repeating-header-element");
6
- const deferred_element_1 = require("../elements/layout/deferred-element");
7
- const layout_1 = require("./layout");
8
- const text_1 = require("./text");
1
+ import { BoxConstraints } from "../layout/box-constraints.js";
2
+ import { RepeatingHeaderElement } from "../elements/layout/repeating-header-element.js";
3
+ import { DeferredElement } from "../elements/layout/deferred-element.js";
4
+ import { StructGroup } from "../elements/layout/struct-group.js";
5
+ import { Column, Row, Box, Expanded, Padding } from "./layout.js";
6
+ import { Text } from "./text.js";
9
7
  function asElement(c) {
10
- return typeof c === "string" ? (0, text_1.Text)(c) : c;
8
+ return typeof c === "string" ? Text(c) : c;
11
9
  }
12
10
  /** `"2fr"` → 2, `"fr"` → 1; not a fraction → null. */
13
11
  function fractionOf(col) {
@@ -18,8 +16,8 @@ function fractionOf(col) {
18
16
  }
19
17
  /** The cell's unbounded (max-content) width - what an `"auto"` column sizes to. */
20
18
  function naturalWidth(cell, cellPadding, ctx) {
21
- const el = cellPadding !== undefined ? (0, layout_1.Padding)(cellPadding, asElement(cell)) : asElement(cell);
22
- return el.calculateLayout(box_constraints_1.BoxConstraints.loose(Infinity, Infinity), { x: 0, y: 0 }, ctx).width;
19
+ const el = cellPadding !== undefined ? Padding(cellPadding, asElement(cell)) : asElement(cell);
20
+ return el.calculateLayout(BoxConstraints.loose(Infinity, Infinity), { x: 0, y: 0 }, ctx).width;
23
21
  }
24
22
  /** Replaces every `"auto"` column with the widest cell width measured down that column. */
25
23
  function resolveAutoColumns(opts, rows, ctx) {
@@ -40,38 +38,52 @@ function resolveAutoColumns(opts, rows, ctx) {
40
38
  }
41
39
  /** Builds the table tree for already-resolved column widths (no `"auto"`). */
42
40
  function composeTable(opts, rows, columns) {
43
- var _a, _b, _c, _d;
44
41
  const { cellPadding } = opts;
45
- const colGap = (_b = (_a = opts.colGap) !== null && _a !== void 0 ? _a : opts.gap) !== null && _b !== void 0 ? _b : 0;
46
- const rowGap = (_d = (_c = opts.rowGap) !== null && _c !== void 0 ? _c : opts.gap) !== null && _d !== void 0 ? _d : 0;
42
+ const colGap = opts.colGap ?? opts.gap ?? 0;
43
+ const rowGap = opts.rowGap ?? opts.gap ?? 0;
47
44
  const cb = opts.cellBorder;
48
45
  const rule = opts.rule;
49
46
  // The complete grid, once: every cell bottom+right, first row also top, first col also left.
50
47
  const borderFor = (firstRow, firstCol) => cb === undefined
51
48
  ? {}
52
- : Object.assign(Object.assign({ borderBottom: cb, borderRight: cb }, (firstRow ? { borderTop: cb } : {})), (firstCol ? { borderLeft: cb } : {}));
49
+ : {
50
+ borderBottom: cb,
51
+ borderRight: cb,
52
+ ...(firstRow ? { borderTop: cb } : {}),
53
+ ...(firstCol ? { borderLeft: cb } : {}),
54
+ };
53
55
  // `ruled` rows get just a bottom line (the header underline / the table foot), without the full grid.
54
- const wrap = (cell, col, firstRow, firstCol, ruled) => {
55
- const inner = cellPadding !== undefined ? (0, layout_1.Padding)(cellPadding, asElement(cell)) : asElement(cell);
56
+ const wrap = (cell, col, firstRow, firstCol, ruled, isHeader) => {
57
+ const content = cellPadding !== undefined ? Padding(cellPadding, asElement(cell)) : asElement(cell);
58
+ // Tag the cell (TH for a header cell, else TD). StructGroup is layout-transparent - visually nothing
59
+ // changes; it only nests the cell's content under a table-cell element in the accessible structure tree.
60
+ const inner = new StructGroup({ role: isHeader ? "TH" : "TD", child: content });
56
61
  // The border lives on the wrapper, which stretches to the row height (crisp lines).
57
- const border = Object.assign(Object.assign({}, borderFor(firstRow, firstCol)), (ruled && rule !== undefined ? { borderBottom: rule } : {}));
62
+ const border = {
63
+ ...borderFor(firstRow, firstCol),
64
+ ...(ruled && rule !== undefined ? { borderBottom: rule } : {}),
65
+ };
58
66
  const boxed = cb !== undefined || (ruled && rule !== undefined);
59
67
  if (typeof col === "number")
60
- return (0, layout_1.Box)(Object.assign({ width: col }, border), [inner]);
68
+ return Box({ width: col, ...border }, [inner]);
61
69
  const fr = fractionOf(col);
62
70
  if (fr !== null)
63
- return (0, layout_1.Expanded)({ flex: fr }, boxed ? (0, layout_1.Box)(Object.assign({}, border), [inner]) : inner);
71
+ return Expanded({ flex: fr }, boxed ? Box({ ...border }, [inner]) : inner);
64
72
  throw new Error(`Unsupported column width "${col}" - use a number of points or "<n>fr"`);
65
73
  };
66
74
  // align:stretch → equal-height cells, so a wrapping cell keeps the row's bottom rule straight.
67
- const buildRow = (cells, firstRow, ruled = false) => (0, layout_1.Row)({ gap: colGap, align: "stretch" }, cells.map((cell, i) => { var _a; return wrap(cell, (_a = columns[i]) !== null && _a !== void 0 ? _a : "1fr", firstRow, i === 0, ruled); }));
75
+ const buildRow = (cells, firstRow, ruled = false, isHeader = false) => new StructGroup({
76
+ role: "TR",
77
+ child: Row({ gap: colGap, align: "stretch" }, cells.map((cell, i) => wrap(cell, columns[i] ?? "1fr", firstRow, i === 0, ruled, isHeader))),
78
+ });
68
79
  // The first row (which gets the top border) is the header if present, else body row 0.
69
80
  // `rule` underlines the header and the last body row (the table foot).
70
81
  const last = rows.length - 1;
71
- const body = (0, layout_1.Column)({ gap: rowGap }, rows.map((cells, idx) => buildRow(cells, !opts.header && idx === 0, rule !== undefined && idx === last)));
72
- return opts.header
73
- ? new repeating_header_element_1.RepeatingHeaderElement(buildRow(opts.header, true, rule !== undefined), body, rowGap)
82
+ const body = Column({ gap: rowGap }, rows.map((cells, idx) => buildRow(cells, !opts.header && idx === 0, rule !== undefined && idx === last)));
83
+ const table = opts.header
84
+ ? new RepeatingHeaderElement(buildRow(opts.header, true, rule !== undefined, true), body, rowGap)
74
85
  : body;
86
+ return new StructGroup({ role: "Table", child: table });
75
87
  }
76
88
  /**
77
89
  * A `Column` of `Row`s. Fixed-point columns sit in a fixed-width `Box`, `"Nfr"` fractions
@@ -79,9 +91,9 @@ function composeTable(opts, rows, columns) {
79
91
  * paginates at row boundaries for free (a row that doesn't fit moves whole). `"auto"`
80
92
  * columns are resolved at layout time (a `DeferredElement`), once metrics are available.
81
93
  */
82
- function Table(opts, rows) {
94
+ export function Table(opts, rows) {
83
95
  if (opts.columns.some((c) => c === "auto")) {
84
- return new deferred_element_1.DeferredElement((ctx) => composeTable(opts, rows, resolveAutoColumns(opts, rows, ctx)));
96
+ return new DeferredElement((ctx) => composeTable(opts, rows, resolveAutoColumns(opts, rows, ctx)));
85
97
  }
86
98
  return composeTable(opts, rows, opts.columns);
87
99
  }