@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
@@ -0,0 +1,76 @@
1
+ import type { Radii } from "../ir/display-list.js";
2
+ import type { CrossAlign } from "../layout/alignment.js";
3
+ /**
4
+ * Size input for a Box's `width` / `height` (relative sizing). Either a number of PDF points (a
5
+ * fixed size) or a percentage string like `"50%"` - a fraction of the space the parent offers on
6
+ * that axis. Every form normalizes to a `Dimension` via `toDimension`, so the engine only ever sees
7
+ * points and factors, never a string. A percentage resolves against the parent's offered extent, so
8
+ * it only has meaning inside a bounded region (a fraction of an unbounded axis is a no-op).
9
+ */
10
+ export type SizeInput = number | `${number}%`;
11
+ /** A resolved `SizeInput`: exactly one of an absolute point size or a 0..1 fraction of the parent. */
12
+ export interface Dimension {
13
+ /** Absolute size in PDF points. */
14
+ points?: number;
15
+ /** Fraction of the parent's offered extent on this axis; a `"50%"` input is `0.5`. */
16
+ factor?: number;
17
+ }
18
+ /**
19
+ * Normalizes a `SizeInput` to a `Dimension` (a point size or a fraction). Rejects negatives at this
20
+ * boundary so `resolveExtent` and the layout never see a negative size.
21
+ */
22
+ export declare function toDimension(value: SizeInput): Dimension;
23
+ /** The bound and ratio options every sized factory accepts, in the public `SizeInput` form. */
24
+ export interface BoundsInput {
25
+ /**
26
+ * This element's own cross-axis alignment inside its Column / Row (CSS `align-self`), overriding the
27
+ * container's `align`. On a `Spacer` / `Expanded` it is a no-op - a flex child fills the main axis and
28
+ * has no natural cross size to align.
29
+ */
30
+ alignSelf?: CrossAlign;
31
+ /**
32
+ * Where this child sits among its siblings (CSS `order`), lowest first, source order deciding ties.
33
+ * It moves the child in the LAYOUT only - the element tree is untouched, so a tagged PDF still
34
+ * exposes the source reading order. Default 0.
35
+ */
36
+ order?: number;
37
+ /**
38
+ * How willingly this child gives up main-axis space when the line overflows (CSS `flex-shrink`),
39
+ * weighted by its own size. **Default 0, deliberately unlike CSS's 1** - shrinking changes what a
40
+ * document looks like, so it is opted into per child rather than applied to everything at once.
41
+ */
42
+ flexShrink?: number;
43
+ minWidth?: SizeInput;
44
+ maxWidth?: SizeInput;
45
+ minHeight?: SizeInput;
46
+ maxHeight?: SizeInput;
47
+ /**
48
+ * width / height (CSS `aspect-ratio`). Fills in whichever axis you leave open, and sizes the box
49
+ * from the offered width when you leave both open. An explicit `min`/`max` still wins over it.
50
+ */
51
+ aspectRatio?: number;
52
+ }
53
+ /** Split `BoundsInput` into the points-and-factors form the elements take. */
54
+ export declare function toBounds(o: BoundsInput): {
55
+ minWidth: number | undefined;
56
+ maxWidth: number | undefined;
57
+ minHeight: number | undefined;
58
+ maxHeight: number | undefined;
59
+ minWidthFactor: number | undefined;
60
+ maxWidthFactor: number | undefined;
61
+ minHeightFactor: number | undefined;
62
+ maxHeightFactor: number | undefined;
63
+ aspectRatio: number | undefined;
64
+ };
65
+ /**
66
+ * Corner radius input. A single number rounds all four corners; the object names them; the tuple is
67
+ * CSS order - `[topLeft, topRight, bottomRight, bottomLeft]`, clockwise from the top left.
68
+ */
69
+ export type RadiusInput = number | {
70
+ topLeft?: number;
71
+ topRight?: number;
72
+ bottomRight?: number;
73
+ bottomLeft?: number;
74
+ } | [number, number, number, number];
75
+ /** Normalizes a `RadiusInput` to the engine's short corner names. */
76
+ export declare function toRadius(r: RadiusInput): number | Radii;
@@ -0,0 +1,43 @@
1
+ const PERCENT = /^(\d+(?:\.\d+)?)%$/; // no leading "-": a size cannot be negative
2
+ /**
3
+ * Normalizes a `SizeInput` to a `Dimension` (a point size or a fraction). Rejects negatives at this
4
+ * boundary so `resolveExtent` and the layout never see a negative size.
5
+ */
6
+ export function toDimension(value) {
7
+ if (typeof value === "number") {
8
+ if (value < 0)
9
+ throw new Error(`Invalid size ${value}: a size cannot be negative.`);
10
+ return { points: value };
11
+ }
12
+ const m = PERCENT.exec(value.trim());
13
+ if (m)
14
+ return { factor: parseFloat(m[1]) / 100 };
15
+ throw new Error(`Invalid size "${value}": use a number of points or a percentage like "50%".`);
16
+ }
17
+ /** Split `BoundsInput` into the points-and-factors form the elements take. */
18
+ export function toBounds(o) {
19
+ const d = (v) => (v !== undefined ? toDimension(v) : undefined);
20
+ const [minW, maxW, minH, maxH] = [d(o.minWidth), d(o.maxWidth), d(o.minHeight), d(o.maxHeight)];
21
+ if (o.aspectRatio !== undefined && !(o.aspectRatio > 0)) {
22
+ throw new Error(`Invalid aspectRatio ${o.aspectRatio}: it is width / height and must be above 0.`);
23
+ }
24
+ return {
25
+ minWidth: minW?.points,
26
+ maxWidth: maxW?.points,
27
+ minHeight: minH?.points,
28
+ maxHeight: maxH?.points,
29
+ minWidthFactor: minW?.factor,
30
+ maxWidthFactor: maxW?.factor,
31
+ minHeightFactor: minH?.factor,
32
+ maxHeightFactor: maxH?.factor,
33
+ aspectRatio: o.aspectRatio,
34
+ };
35
+ }
36
+ /** Normalizes a `RadiusInput` to the engine's short corner names. */
37
+ export function toRadius(r) {
38
+ if (typeof r === "number")
39
+ return r;
40
+ if (Array.isArray(r))
41
+ return { tl: r[0], tr: r[1], br: r[2], bl: r[3] };
42
+ return { tl: r.topLeft, tr: r.topRight, br: r.bottomRight, bl: r.bottomLeft };
43
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Loading a font over the network for `doc.addFontFromUrl(...)`.
3
+ *
4
+ * `addFont` resolves its source AT REGISTRATION - a path is read there and then - and this keeps the
5
+ * same contract, differing only in being async. So the failure lands where you asked for the font,
6
+ * not somewhere inside a later render.
7
+ *
8
+ * `fetch` is used directly: it is in every browser and in Node since 18, so this needs no platform
9
+ * port, unlike the filesystem read beside it.
10
+ */
11
+ /** One font file by URL, or a styled family of them. Mirrors `FontSource`, with strings meaning URLs. */
12
+ export type UrlFontSource = string | {
13
+ normal: string;
14
+ bold?: string;
15
+ italic?: string;
16
+ boldItalic?: string;
17
+ };
18
+ /** Thrown when a font could not be fetched or is not one we can parse. Always says which URL. */
19
+ export declare class FontUrlError extends Error {
20
+ constructor(message: string);
21
+ }
22
+ /** Fetch a whole `UrlFontSource`, in parallel for a family. */
23
+ export declare function loadFontFromUrl(source: UrlFontSource): Promise<Uint8Array | {
24
+ normal: Uint8Array;
25
+ bold?: Uint8Array;
26
+ italic?: Uint8Array;
27
+ boldItalic?: Uint8Array;
28
+ }>;
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Loading a font over the network for `doc.addFontFromUrl(...)`.
3
+ *
4
+ * `addFont` resolves its source AT REGISTRATION - a path is read there and then - and this keeps the
5
+ * same contract, differing only in being async. So the failure lands where you asked for the font,
6
+ * not somewhere inside a later render.
7
+ *
8
+ * `fetch` is used directly: it is in every browser and in Node since 18, so this needs no platform
9
+ * port, unlike the filesystem read beside it.
10
+ */
11
+ /** Thrown when a font could not be fetched or is not one we can parse. Always says which URL. */
12
+ export class FontUrlError extends Error {
13
+ constructor(message) {
14
+ super(`@jasy/pdf: ${message}`);
15
+ }
16
+ }
17
+ /**
18
+ * What the first four bytes of an sfnt file say it is. Checked here because `TTFParser` does not look
19
+ * at them: it goes straight for the table directory, so an HTML error page or a WOFF would fail deep
20
+ * inside with `missing required table "head"` instead of saying what the file actually is.
21
+ */
22
+ function describeSignature(bytes) {
23
+ const tag = String.fromCharCode(...bytes.subarray(0, 4));
24
+ const v = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
25
+ if (v === 0x00010000 || tag === "true")
26
+ return { ok: true, what: "TrueType" };
27
+ if (tag === "OTTO")
28
+ return { ok: false, what: "an OpenType/CFF font, which jasy does not parse yet" };
29
+ if (tag === "wOFF")
30
+ return { ok: true, what: "WOFF" };
31
+ if (tag === "wOF2")
32
+ return { ok: false, what: "a WOFF2 font, which jasy does not parse yet" };
33
+ if (tag === "ttcf")
34
+ return { ok: false, what: "a TrueType Collection, which jasy does not parse yet" };
35
+ if (tag.startsWith("<") || tag === "%PDF")
36
+ return { ok: false, what: "not a font at all" };
37
+ return { ok: false, what: "not a font we recognise" };
38
+ }
39
+ /** How long one font may take to arrive before we give up. A hung server must not hang a render. */
40
+ const TIMEOUT_MS = 15000;
41
+ /**
42
+ * How large a font file may be. Generous on purpose - a full CJK face is tens of megabytes and is a
43
+ * legitimate thing to embed - but not unbounded: a URL pointing at the wrong file (a video, a disk
44
+ * image) would otherwise be read into memory in full before anyone notices it is not a font.
45
+ */
46
+ const MAX_BYTES = 32 * 1024 * 1024;
47
+ /** Read the body with a running ceiling, so an oversized response is dropped WHILE it arrives. */
48
+ async function readBounded(response, url) {
49
+ // `Content-Length` is the cheap early exit; a server may omit or misstate it, so it is a hint only
50
+ // and the real ceiling is enforced below while reading.
51
+ const declared = Number(response.headers?.get?.("content-length") ?? NaN);
52
+ if (Number.isFinite(declared) && declared > MAX_BYTES) {
53
+ throw new FontUrlError(`the font at ${url} is ${declared} bytes; the limit is ${MAX_BYTES}`);
54
+ }
55
+ const body = response.body;
56
+ if (!body?.getReader) {
57
+ // No streaming body (an older runtime, or a stubbed response): fall back to reading it whole and
58
+ // checking afterwards. Weaker, but never worse than not checking at all.
59
+ const whole = new Uint8Array(await response.arrayBuffer());
60
+ if (whole.length > MAX_BYTES) {
61
+ throw new FontUrlError(`the font at ${url} is larger than the ${MAX_BYTES} byte limit`);
62
+ }
63
+ return whole;
64
+ }
65
+ const reader = body.getReader();
66
+ const chunks = [];
67
+ let total = 0;
68
+ for (;;) {
69
+ const { done, value } = await reader.read();
70
+ if (done)
71
+ break;
72
+ total += value.length;
73
+ if (total > MAX_BYTES) {
74
+ await reader.cancel();
75
+ throw new FontUrlError(`the font at ${url} is larger than the ${MAX_BYTES} byte limit`);
76
+ }
77
+ chunks.push(value);
78
+ }
79
+ if (chunks.length === 1)
80
+ return chunks[0];
81
+ const out = new Uint8Array(total);
82
+ let at = 0;
83
+ for (const c of chunks) {
84
+ out.set(c, at);
85
+ at += c.length;
86
+ }
87
+ return out;
88
+ }
89
+ /** Fetch one font file and hand back its bytes, refusing anything we could not embed. */
90
+ async function fetchFont(url) {
91
+ let response;
92
+ try {
93
+ response = await fetch(url, { signal: AbortSignal.timeout(TIMEOUT_MS) });
94
+ }
95
+ catch (e) {
96
+ const why = e?.name === "TimeoutError" || e?.name === "AbortError"
97
+ ? `it did not respond within ${TIMEOUT_MS} ms`
98
+ : String(e?.message ?? e);
99
+ throw new FontUrlError(`could not fetch the font at ${url}: ${why}`);
100
+ }
101
+ if (!response.ok) {
102
+ throw new FontUrlError(`the font at ${url} could not be fetched: HTTP ${response.status}`);
103
+ }
104
+ // Everything past here still talks to the network, so a failure mid-body must not escape as a raw
105
+ // TypeError - a caller catching FontUrlError would miss it.
106
+ let bytes;
107
+ try {
108
+ bytes = await readBounded(response, url);
109
+ }
110
+ catch (e) {
111
+ if (e instanceof FontUrlError)
112
+ throw e;
113
+ throw new FontUrlError(`the font at ${url} could not be read: ${String(e?.message ?? e)}`);
114
+ }
115
+ if (bytes.length < 4) {
116
+ throw new FontUrlError(`the font at ${url} is empty`);
117
+ }
118
+ const { ok, what } = describeSignature(bytes);
119
+ if (!ok) {
120
+ throw new FontUrlError(`the file at ${url} is ${what}. jasy embeds TrueType-flavoured fonts (.ttf and .woff)`);
121
+ }
122
+ return bytes;
123
+ }
124
+ /** Fetch a whole `UrlFontSource`, in parallel for a family. */
125
+ export async function loadFontFromUrl(source) {
126
+ if (typeof source === "string")
127
+ return fetchFont(source);
128
+ const styles = ["normal", "bold", "italic", "boldItalic"];
129
+ const wanted = styles.filter((s) => source[s] !== undefined);
130
+ const loaded = await Promise.all(wanted.map((s) => fetchFont(source[s])));
131
+ const family = { normal: new Uint8Array() };
132
+ wanted.forEach((s, i) => (family[s] = loaded[i]));
133
+ return family;
134
+ }
@@ -0,0 +1,347 @@
1
+ import { PDFElement } from "../elements/pdf-element.js";
2
+ import { TextFieldElement } from "../elements/forms/text-field-element.js";
3
+ import { RadioElement } from "../elements/forms/radio-element.js";
4
+ import { ChoiceElement } from "../elements/forms/choice-element.js";
5
+ import { PushButtonElement } from "../elements/forms/push-button-element.js";
6
+ import { SignatureFieldElement } from "../elements/forms/signature-element.js";
7
+ import type { FieldAlign } from "../forms/field.js";
8
+ import { ColorInput } from "./color.js";
9
+ /** A choice option: a plain string (value = label) or `{ value, label }`. */
10
+ export type ChoiceOptionInput = string | {
11
+ value: string;
12
+ label?: string;
13
+ };
14
+ /** Options for a `TextField` - an interactive text input in the generated PDF. */
15
+ export interface TextFieldOptions {
16
+ /** The field name - unique in the document; you read the value back by it. */
17
+ name: string;
18
+ /** Initial value. */
19
+ value?: string;
20
+ /** Tooltip / accessible name shown on hover. */
21
+ tooltip?: string;
22
+ /** Accept multiple lines instead of one (give the box a `height` to match). */
23
+ multiline?: boolean;
24
+ /** Mask the typed characters (a password field). */
25
+ password?: boolean;
26
+ /** How the value sits in the box: left (default), center or right. */
27
+ align?: FieldAlign;
28
+ /** Maximum number of characters. */
29
+ maxLength?: number;
30
+ /** Show the value but do not let the user edit it. */
31
+ readOnly?: boolean;
32
+ /** Mark it as a field that must be filled in before the form is submitted. */
33
+ required?: boolean;
34
+ /** Hide the widget entirely - neither on screen nor in print. */
35
+ hidden?: boolean;
36
+ /** Include the widget when printing (default true). `false` keeps it on screen only. */
37
+ print?: boolean;
38
+ /** Box width in points; omit to fill the offered width. */
39
+ width?: number;
40
+ /** Box height in points; omit for a single-line default. */
41
+ height?: number;
42
+ /** Value font size in points; `0` lets the viewer auto-size. */
43
+ fontSize?: number;
44
+ /** Value text colour (named / hex / rgb). */
45
+ color?: ColorInput;
46
+ /** Box border colour; omit for no border. */
47
+ border?: ColorInput;
48
+ /** Box background fill; omit for transparent. */
49
+ background?: ColorInput;
50
+ /** Border thickness in points (default 1 when a border is set). */
51
+ borderWidth?: number;
52
+ }
53
+ /**
54
+ * An interactive text field the reader of the PDF can type into (AcroForm /Tx). Place it like any element -
55
+ * in a `Column`, `Row`, `Box` or a table cell; it reserves its box and becomes a fillable widget.
56
+ *
57
+ * ```ts
58
+ * TextField({ name: "email", border: "gray", height: 22 })
59
+ * ```
60
+ */
61
+ export declare function TextField(opts: TextFieldOptions): TextFieldElement;
62
+ /** Options for a `Checkbox` - a toggle the reader of the PDF can tick. */
63
+ export interface CheckboxOptions {
64
+ /** The field name - unique in the document. */
65
+ name: string;
66
+ /** Text placed beside the box. Given one, the box and the text become a row - the same shape
67
+ * `RadioGroup` builds for its choices, so a lone check box does not have to be assembled by hand. */
68
+ label?: string;
69
+ /** Size of the label text (default 12). */
70
+ labelSize?: number;
71
+ /** Colour of the label text. */
72
+ labelColor?: ColorInput;
73
+ /** Space between the box and its label, in points (default 8). */
74
+ labelGap?: number;
75
+ /** Whether it starts checked. */
76
+ checked?: boolean;
77
+ /** The "on" export value stored when checked (default `"Yes"`). */
78
+ onValue?: string;
79
+ /** Tooltip / accessible name shown on hover. */
80
+ tooltip?: string;
81
+ /** Show the state but do not let the user toggle it. */
82
+ readOnly?: boolean;
83
+ /** Mark it as a field that must be filled in before the form is submitted. */
84
+ required?: boolean;
85
+ /** Hide the widget entirely - neither on screen nor in print. */
86
+ hidden?: boolean;
87
+ /** Include the widget when printing (default true). `false` keeps it on screen only. */
88
+ print?: boolean;
89
+ /** Box side length in points (default 14). */
90
+ size?: number;
91
+ /** Checkmark colour (default black). */
92
+ color?: ColorInput;
93
+ /** Box border colour (default a mid grey). */
94
+ border?: ColorInput;
95
+ /** Box background fill. */
96
+ background?: ColorInput;
97
+ /** Border thickness in points (default 1). */
98
+ borderWidth?: number;
99
+ }
100
+ /**
101
+ * An interactive checkbox (AcroForm /Btn). It bakes its own checkmark appearance, so the tick shows in
102
+ * any viewer, in print, and under PDF/A - not only in ones that regenerate appearances.
103
+ *
104
+ * ```ts
105
+ * Row({ gap: 6, align: "center" }, [Checkbox({ name: "agree" }), Text("I agree")])
106
+ * ```
107
+ */
108
+ export declare function Checkbox(opts: CheckboxOptions): PDFElement;
109
+ /** Options for a single `Radio` button. Several sharing a `group` are one mutually-exclusive field. */
110
+ export interface RadioOptions {
111
+ /** The shared group name - radios with the same `group` are one field. */
112
+ group: string;
113
+ /** This button's export value - unique within the group. */
114
+ value: string;
115
+ /** Whether it starts selected. */
116
+ selected?: boolean;
117
+ /** Tooltip / accessible name. */
118
+ tooltip?: string;
119
+ /** Show but do not allow changing. */
120
+ readOnly?: boolean;
121
+ /** Mark it as a field that must be filled in before the form is submitted. */
122
+ required?: boolean;
123
+ /** Hide the widget entirely - neither on screen nor in print. */
124
+ hidden?: boolean;
125
+ /** Include the widget when printing (default true). `false` keeps it on screen only. */
126
+ print?: boolean;
127
+ /** Button diameter in points (default 14). */
128
+ size?: number;
129
+ /** Dot colour. */
130
+ color?: ColorInput;
131
+ /** Ring colour. */
132
+ border?: ColorInput;
133
+ /** Fill behind the ring. */
134
+ background?: ColorInput;
135
+ /** Ring thickness. */
136
+ borderWidth?: number;
137
+ }
138
+ /** A single radio button. Use `RadioGroup` for the common labelled-list layout, or this for custom ones. */
139
+ export declare function Radio(opts: RadioOptions): RadioElement;
140
+ /** One option in a `RadioGroup`. */
141
+ export interface RadioChoice {
142
+ /** The export value stored when this option is picked. */
143
+ value: string;
144
+ /** The text shown next to the button. */
145
+ label: string;
146
+ }
147
+ /** Options for a `RadioGroup` - the labelled list of mutually-exclusive buttons. */
148
+ export interface RadioGroupOptions {
149
+ /** The field name (shared by every button). */
150
+ name: string;
151
+ /** Mark the group as required. */
152
+ required?: boolean;
153
+ /** Hide every button - neither on screen nor in print. */
154
+ hidden?: boolean;
155
+ /** Include the buttons when printing (default true). */
156
+ print?: boolean;
157
+ /** The value of the option that starts selected (matches one option's `value`). */
158
+ value?: string;
159
+ /** Vertical space between options (default 8). */
160
+ gap?: number;
161
+ /** Button diameter (default 14). */
162
+ size?: number;
163
+ /** Button ring / dot / background colours, applied to every option. */
164
+ color?: ColorInput;
165
+ border?: ColorInput;
166
+ background?: ColorInput;
167
+ /** Label font size (default 12) and colour. */
168
+ labelSize?: number;
169
+ labelColor?: ColorInput;
170
+ }
171
+ /**
172
+ * A labelled group of mutually-exclusive radio buttons. Sugar over `Radio` + `Text`: it lays each option
173
+ * out as a button-and-label row in a `Column`, and marks the one whose `value` matches the group `value`.
174
+ *
175
+ * ```ts
176
+ * RadioGroup({ name: "size", value: "m" }, [
177
+ * { value: "s", label: "Small" },
178
+ * { value: "m", label: "Medium" },
179
+ * { value: "l", label: "Large" },
180
+ * ])
181
+ * ```
182
+ */
183
+ export declare function RadioGroup(opts: RadioGroupOptions, options: RadioChoice[]): PDFElement;
184
+ /** Style + geometry shared by `Dropdown` and `ListBox`. */
185
+ interface ChoiceStyleOptions {
186
+ /** How the value sits in the box: left (default), center or right. */
187
+ align?: FieldAlign;
188
+ tooltip?: string;
189
+ readOnly?: boolean;
190
+ /** Mark it as a field that must be filled in before the form is submitted. */
191
+ required?: boolean;
192
+ /** Hide the widget entirely - neither on screen nor in print. */
193
+ hidden?: boolean;
194
+ /** Include the widget when printing (default true). `false` keeps it on screen only. */
195
+ print?: boolean;
196
+ width?: number;
197
+ height?: number;
198
+ fontSize?: number;
199
+ color?: ColorInput;
200
+ border?: ColorInput;
201
+ background?: ColorInput;
202
+ borderWidth?: number;
203
+ }
204
+ /** Options for a `Dropdown` (combo box). */
205
+ export interface DropdownOptions extends ChoiceStyleOptions {
206
+ /** The field name. */
207
+ name: string;
208
+ /** The selected value. */
209
+ value?: string;
210
+ /** Let the reader type a value that is not in the list. */
211
+ editable?: boolean;
212
+ }
213
+ /**
214
+ * A dropdown / combo box (AcroForm /Ch). Options are plain strings or `{ value, label }`.
215
+ *
216
+ * ```ts
217
+ * Dropdown({ name: "country", value: "de" }, [{ value: "de", label: "Germany" }, "France"])
218
+ * ```
219
+ */
220
+ export declare function Dropdown(opts: DropdownOptions, options: ChoiceOptionInput[]): ChoiceElement;
221
+ /** `Select` is an alias for `Dropdown`. */
222
+ export declare const Select: typeof Dropdown;
223
+ /** Options for a `ListBox`. */
224
+ export interface ListBoxOptions extends ChoiceStyleOptions {
225
+ /** The field name. */
226
+ name: string;
227
+ /** The selected value (single-select). */
228
+ value?: string;
229
+ /** The selected values (when `multiSelect`). */
230
+ values?: string[];
231
+ /** Allow more than one selection. */
232
+ multiSelect?: boolean;
233
+ }
234
+ /**
235
+ * A scrollable list box (AcroForm /Ch). Give it a `height` to show more rows. Set `multiSelect` for
236
+ * several selections at once.
237
+ */
238
+ export declare function ListBox(opts: ListBoxOptions, options: ChoiceOptionInput[]): ChoiceElement;
239
+ /**
240
+ * What a `PushButton` does when clicked: `"reset"` clears the form, `{ submit: url }` posts the field
241
+ * values, `{ open: url }` opens a link. Omit it for a button that does nothing. Scripted (JavaScript)
242
+ * actions are deliberately not offered - a jasy form is data, not a program.
243
+ *
244
+ * VIEWER SUPPORT. All three actions we emit are spec-correct; what differs is what a viewer implements.
245
+ * PROVEN, not assumed - the same cases built with pdf-lib (an independent producer) behave identically,
246
+ * so none of this is a jasy bug. Measured 2026-07 in pdf.js (VS Code) and poppler/Evince:
247
+ * - `"reset"` - works in both. The safe one.
248
+ * - `{ open: url }` - works in pdf.js, dead in Evince, and NOT fixable from here: poppler's glib
249
+ * layer surfaces actions only through `poppler_page_get_link_mapping` (i.e.
250
+ * /Link annotations); its form-field API has no action getter at all, so a
251
+ * WIDGET's /A can never be reached. **For a plain hyperlink use `Link({ href })`**
252
+ * - the identical URI action on a real /Link annotation works in both.
253
+ * - `{ submit: url }` - Acrobat only. `POPPLER_ACTION_SUBMIT_FORM` does not exist in poppler-glib (no
254
+ * such enum value), and pdf.js does not implement /SubmitForm either.
255
+ * Ruled out as causes: pdf-lib splits field and widget, sets /P and a /D appearance - and still fails
256
+ * the same way, so none of those structural differences matter.
257
+ */
258
+ export type ButtonActionInput = "reset" | {
259
+ submit: string;
260
+ } | {
261
+ open: string;
262
+ };
263
+ /** Options for a `PushButton`. */
264
+ export interface PushButtonOptions {
265
+ /** The field name - unique in the document. */
266
+ name: string;
267
+ /** The caption drawn on the button. */
268
+ label: string;
269
+ /** What clicking it does. */
270
+ action?: ButtonActionInput;
271
+ /** Tooltip / accessible name shown on hover. */
272
+ tooltip?: string;
273
+ /** Draw it but do not let it be clicked. */
274
+ readOnly?: boolean;
275
+ /** Mark it as a field that must be filled in before the form is submitted. */
276
+ required?: boolean;
277
+ /** Hide the widget entirely - neither on screen nor in print. */
278
+ hidden?: boolean;
279
+ /** Include the widget when printing (default true). `false` keeps it on screen only. */
280
+ print?: boolean;
281
+ /** Width in points; omit to fill the offered width. */
282
+ width?: number;
283
+ /** Height in points; omit for a comfortable default. */
284
+ height?: number;
285
+ /** Caption font size (default 12). */
286
+ fontSize?: number;
287
+ /** Caption colour. */
288
+ color?: ColorInput;
289
+ /** Border colour. */
290
+ border?: ColorInput;
291
+ /** Button face fill. */
292
+ background?: ColorInput;
293
+ /** Border thickness in points. */
294
+ borderWidth?: number;
295
+ }
296
+ /**
297
+ * A clickable push button (AcroForm /Btn). It holds no value - it fires an action. Its caption is baked
298
+ * into the appearance, so the button looks the same in every viewer and in print.
299
+ *
300
+ * ```ts
301
+ * PushButton({ name: "send", label: "Submit", action: { submit: "https://example.com/f" }, width: 120 })
302
+ * PushButton({ name: "clear", label: "Reset", action: "reset", width: 90 })
303
+ * ```
304
+ */
305
+ export declare function PushButton(opts: PushButtonOptions): PushButtonElement;
306
+ /** Options for a `SignatureField`. */
307
+ export interface SignatureFieldOptions {
308
+ /** The field name - unique in the document. */
309
+ name: string;
310
+ /** A hint drawn inside the empty box, e.g. "Signature" or "Approved by". */
311
+ label?: string;
312
+ /** Tooltip / accessible name shown on hover. */
313
+ tooltip?: string;
314
+ /** Draw it but do not let it be signed. */
315
+ readOnly?: boolean;
316
+ /** Mark it as a field that must be filled in before the form is submitted. */
317
+ required?: boolean;
318
+ /** Hide the widget entirely - neither on screen nor in print. */
319
+ hidden?: boolean;
320
+ /** Include the widget when printing (default true). `false` keeps it on screen only. */
321
+ print?: boolean;
322
+ /** Width in points; omit to fill the offered width. */
323
+ width?: number;
324
+ /** Height in points (default 48). */
325
+ height?: number;
326
+ /** Hint font size (default 9). */
327
+ fontSize?: number;
328
+ /** Hint colour. */
329
+ color?: ColorInput;
330
+ /** Box border + signing-rule colour. */
331
+ border?: ColorInput;
332
+ /** Box background fill. */
333
+ background?: ColorInput;
334
+ /** Border thickness in points. */
335
+ borderWidth?: number;
336
+ }
337
+ /**
338
+ * A signature field (AcroForm /Sig) - a placeholder someone can sign later in a PDF tool. jasy creates
339
+ * the field and draws its empty "sign here" box; it does not apply signatures itself (real signing needs
340
+ * a certificate and a byte-range digest).
341
+ *
342
+ * ```ts
343
+ * SignatureField({ name: "approver", label: "Signature", width: 240 })
344
+ * ```
345
+ */
346
+ export declare function SignatureField(opts: SignatureFieldOptions): SignatureFieldElement;
347
+ export {};