@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,29 +1,34 @@
1
- import { PDFElement, LayoutContext } from "../elements/pdf-element";
2
- import { BoxConstraints, Offset, Size } from "../layout/box-constraints";
1
+ import { PDFElement, LayoutContext } from "../elements/pdf-element.js";
2
+ import { BoxConstraints, Offset, Size } from "../layout/box-constraints.js";
3
3
  /** Distribution of the children ALONG the stacking (main) axis when there is leftover
4
4
  * space and no flex child to absorb it. */
5
- export type MainAlign = "start" | "center" | "end" | "between" | "around";
6
- /** Position/size of each child ACROSS the cross axis. `stretch` fills the cross extent;
7
- * the others place the child at its natural cross size. */
8
- export type CrossAlign = "start" | "center" | "end" | "stretch";
5
+ export type { MainAlign, CrossAlign } from "../layout/alignment.js";
6
+ import type { CrossAlign, MainAlign } from "../layout/alignment.js";
9
7
  /**
10
8
  * Maps the abstract MAIN/CROSS axes onto concrete width/height + x/y, so one flex
11
9
  * algorithm drives both a vertical Column (main = height) and a horizontal Row
12
10
  * (main = width). "main" is the stacking direction; "cross" is perpendicular.
13
11
  */
14
12
  export interface FlexAxis {
13
+ /** Whether the MAIN (stacking) axis is horizontal - i.e. this is a Row. Used to ask a child for the
14
+ * right relative-size factor (width vs height) when resolving a percentage child. */
15
+ mainHorizontal: boolean;
15
16
  mainOf(size: Size): number;
16
17
  crossOf(size: Size): number;
17
18
  /**
18
- * Constraints for a fixed child: the main extent is unbounded (it takes its natural
19
- * size); the cross extent is ALWAYS capped to what the line offers. The cap is a hard
19
+ * Constraints for a fixed child: the main extent is unbounded by default (the child takes its
20
+ * natural size); the cross extent is ALWAYS capped to what the line offers. The cap is a hard
20
21
  * ceiling regardless of `cross` alignment - it is what guarantees nothing ever overflows
21
22
  * (a paragraph wraps at the column width instead of running one line off the page). A
22
23
  * child smaller than the cap keeps its size and is positioned by `crossOffset`; a child
23
24
  * that wants to fill (Container, Text) fills the cap. So `stretch` vs `start/center/end`
24
25
  * differ only in where a smaller child sits, never in the ceiling.
26
+ *
27
+ * `mainMax` bounds the main axis too (default unbounded): passed only for a PERCENTAGE child, so its
28
+ * `width: "50%"` can resolve against the space the line offers its items. A plain child keeps the
29
+ * unbounded main axis and shrink-wraps as before.
25
30
  */
26
- measureConstraints(crossAvail: number): BoxConstraints;
31
+ measureConstraints(crossAvail: number, mainMax?: number): BoxConstraints;
27
32
  /** Constraints for a flex child (fills the cross axis like a stretched child). */
28
33
  flexConstraints(mainExtent: number, crossAvail: number): BoxConstraints;
29
34
  /** Absolute offset for a child at main position `mainPos`, cross position `crossPos`. */
@@ -35,6 +40,12 @@ export interface FlexOptions {
35
40
  gap?: number;
36
41
  main?: MainAlign;
37
42
  cross?: CrossAlign;
43
+ /** Lay the children out along the main axis backwards (CSS `row-reverse` / `column-reverse`). */
44
+ reverse?: boolean;
45
+ /** Let the children flow onto further lines when they do not fit (CSS `flex-wrap: wrap`). */
46
+ wrap?: boolean;
47
+ /** How the BLOCK of wrapped lines sits across the axis (CSS `align-content`). Default `start`. */
48
+ alignContent?: MainAlign;
38
49
  }
39
50
  export declare class FlexLayoutHelper {
40
51
  /**
@@ -46,8 +57,24 @@ export declare class FlexLayoutHelper {
46
57
  * extent occupied. Vertical with `gap 0`, `main start`, `cross stretch` reproduces the
47
58
  * previous Column layout exactly.
48
59
  */
60
+ /**
61
+ * Lays out the children along `axis`, on one line or several.
62
+ *
63
+ * Without `wrap` this is the single-line engine and nothing about it changed - the call goes
64
+ * straight through, which is what keeps every existing document byte-identical. With `wrap` the
65
+ * children are split into lines first and the SAME engine runs once per line.
66
+ */
49
67
  static layout(children: PDFElement[], axis: FlexAxis, mainAvail: number, crossAvail: number, mainStart: number, crossOrigin: number, options: FlexOptions, ctx: LayoutContext): {
50
68
  mainUsed: number;
51
69
  crossUsed: number;
52
70
  };
71
+ /**
72
+ * The wrapping path: split into lines, lay each one out, then place the lines across the axis.
73
+ *
74
+ * Lines are laid out TWICE - once to learn how tall each is, once at its final cross position. That
75
+ * is the same measure-then-place shape the single-line engine already uses, and it is what
76
+ * `alignContent` needs: the block of lines can only be centred once its total is known.
77
+ */
78
+ private static layoutWrapped;
79
+ private static layoutLine;
53
80
  }
@@ -1,20 +1,19 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FlexLayoutHelper = exports.HORIZONTAL_AXIS = exports.VERTICAL_AXIS = void 0;
4
- const pdf_element_1 = require("../elements/pdf-element");
5
- const box_constraints_1 = require("../layout/box-constraints");
6
- exports.VERTICAL_AXIS = {
1
+ import { FlexiblePDFElement } from "../elements/pdf-element.js";
2
+ import { BoxConstraints } from "../layout/box-constraints.js";
3
+ export const VERTICAL_AXIS = {
4
+ mainHorizontal: false,
7
5
  mainOf: (s) => s.height,
8
6
  crossOf: (s) => s.width,
9
- measureConstraints: (crossAvail) => box_constraints_1.BoxConstraints.loose(crossAvail, Infinity),
10
- flexConstraints: (mainExtent, crossAvail) => box_constraints_1.BoxConstraints.loose(crossAvail, mainExtent),
7
+ measureConstraints: (crossAvail, mainMax = Infinity) => BoxConstraints.loose(crossAvail, mainMax),
8
+ flexConstraints: (mainExtent, crossAvail) => BoxConstraints.loose(crossAvail, mainExtent),
11
9
  offsetAt: (mainPos, crossPos) => ({ x: crossPos, y: mainPos }),
12
10
  };
13
- exports.HORIZONTAL_AXIS = {
11
+ export const HORIZONTAL_AXIS = {
12
+ mainHorizontal: true,
14
13
  mainOf: (s) => s.width,
15
14
  crossOf: (s) => s.height,
16
- measureConstraints: (crossAvail) => box_constraints_1.BoxConstraints.loose(Infinity, crossAvail),
17
- flexConstraints: (mainExtent, crossAvail) => box_constraints_1.BoxConstraints.loose(mainExtent, crossAvail),
15
+ measureConstraints: (crossAvail, mainMax = Infinity) => BoxConstraints.loose(mainMax, crossAvail),
16
+ flexConstraints: (mainExtent, crossAvail) => BoxConstraints.loose(mainExtent, crossAvail),
18
17
  offsetAt: (mainPos, crossPos) => ({ x: mainPos, y: crossPos }),
19
18
  };
20
19
  /** Cross-axis offset of a child of size `childCross` within `crossExtent`. */
@@ -25,7 +24,21 @@ function crossOffset(align, crossExtent, childCross) {
25
24
  return Math.max(0, crossExtent - childCross);
26
25
  return 0; // start, stretch (stretch fills, so no offset)
27
26
  }
28
- class FlexLayoutHelper {
27
+ /**
28
+ * The order children are LAID OUT in: by `order` (lowest first, ties keeping source order), then
29
+ * reversed if the container asks for it. The element tree itself is never touched, so the reading order
30
+ * a tagged PDF exposes stays the source order - which is what CSS says too.
31
+ *
32
+ * `sort` is stable in every engine we target, so equal `order` values keep their source positions and a
33
+ * container where nobody sets one comes back with the identical array.
34
+ */
35
+ function inLayoutOrder(children, reverse) {
36
+ const ordered = children.some((c) => c.order !== 0)
37
+ ? [...children].sort((a, b) => a.order - b.order)
38
+ : children;
39
+ return reverse ? [...ordered].reverse() : ordered;
40
+ }
41
+ export class FlexLayoutHelper {
29
42
  /**
30
43
  * Lays out a flex line along `axis`, IN SOURCE ORDER, and places every child.
31
44
  * Fixed children take their natural main extent; flex (`ExpandedElement`) children
@@ -35,31 +48,188 @@ class FlexLayoutHelper {
35
48
  * extent occupied. Vertical with `gap 0`, `main start`, `cross stretch` reproduces the
36
49
  * previous Column layout exactly.
37
50
  */
51
+ /**
52
+ * Lays out the children along `axis`, on one line or several.
53
+ *
54
+ * Without `wrap` this is the single-line engine and nothing about it changed - the call goes
55
+ * straight through, which is what keeps every existing document byte-identical. With `wrap` the
56
+ * children are split into lines first and the SAME engine runs once per line.
57
+ */
38
58
  static layout(children, axis, mainAvail, crossAvail, mainStart, crossOrigin, options, ctx) {
39
- var _a, _b, _c;
40
- const gap = (_a = options.gap) !== null && _a !== void 0 ? _a : 0;
41
- const main = (_b = options.main) !== null && _b !== void 0 ? _b : "start";
42
- const cross = (_c = options.cross) !== null && _c !== void 0 ? _c : "stretch";
59
+ if (!options.wrap || children.length === 0 || !Number.isFinite(mainAvail)) {
60
+ // An unbounded main axis has no edge to wrap at, so wrapping there is meaningless, not an error.
61
+ return FlexLayoutHelper.layoutLine(children, axis, mainAvail, crossAvail, mainStart, crossOrigin, options, ctx);
62
+ }
63
+ return FlexLayoutHelper.layoutWrapped(children, axis, mainAvail, crossAvail, mainStart, crossOrigin, options, ctx);
64
+ }
65
+ /**
66
+ * The wrapping path: split into lines, lay each one out, then place the lines across the axis.
67
+ *
68
+ * Lines are laid out TWICE - once to learn how tall each is, once at its final cross position. That
69
+ * is the same measure-then-place shape the single-line engine already uses, and it is what
70
+ * `alignContent` needs: the block of lines can only be centred once its total is known.
71
+ */
72
+ static layoutWrapped(children, axis, mainAvail, crossAvail, mainStart, crossOrigin, options, ctx) {
73
+ const gap = options.gap ?? 0;
74
+ const ordered = inLayoutOrder(children, options.reverse ?? false);
75
+ // Which children share a line. A child is measured at its natural main extent; a flex child
76
+ // contributes its BASIS, which is 0 unless it asked for one - so it never forces a break by itself.
77
+ // A child's natural main extent. A PERCENTAGE child has no fixed answer: its size depends on how
78
+ // many gaps the line ends up with, which depends on who is on it. So it is asked again for every
79
+ // candidate membership below rather than measured once.
80
+ // A plain child's extent does not depend on the line, so it is laid out ONCE and remembered.
81
+ // Without this each membership probe re-lays every child already on the line - O(n^2) subtree
82
+ // layouts, which is expensive the moment those children hold a Table or a wrapping paragraph.
83
+ const measured = new Map();
84
+ const naturalMain = (child, base) => {
85
+ if (child instanceof FlexiblePDFElement)
86
+ return child.getBasis(base);
87
+ const factor = child.relativeSizeFactor(axis.mainHorizontal);
88
+ if (factor !== undefined)
89
+ return base * factor;
90
+ const cached = measured.get(child);
91
+ if (cached !== undefined)
92
+ return cached;
93
+ // Measured UNCAPPED, the same way the single-line engine measures a plain child. Handing the
94
+ // line width in as a cap would silently squash a child wider than the line - CSS lets such a
95
+ // child overflow, and so does our own non-wrapping path.
96
+ const needsBound = child.needsBoundedMain(axis.mainHorizontal);
97
+ const main = axis.mainOf(child.calculateLayout(axis.measureConstraints(crossAvail, needsBound ? mainAvail : Infinity), axis.offsetAt(mainStart, crossOrigin), ctx));
98
+ measured.set(child, main);
99
+ return main;
100
+ };
101
+ /** What a line of these children would occupy, resolving every `%` against ITS gap count. */
102
+ const lineExtent = (members) => {
103
+ const gaps = Math.max(0, members.length - 1) * gap;
104
+ const base = Math.max(0, mainAvail - gaps);
105
+ return members.reduce((n, c) => n + naturalMain(c, base), 0) + gaps;
106
+ };
107
+ const lines = [[]];
108
+ for (const child of ordered) {
109
+ const current = lines[lines.length - 1];
110
+ // A child wider than a whole line still gets one of its own rather than an empty line before it.
111
+ if (current.length > 0 && lineExtent([...current, child]) > mainAvail) {
112
+ lines.push([child]);
113
+ }
114
+ else {
115
+ current.push(child);
116
+ }
117
+ }
118
+ // Each line keeps SOURCE order internally: the ordering was applied above, and applying it again
119
+ // per line would reverse twice.
120
+ const lineOptions = { ...options, wrap: false, reverse: false };
121
+ const measure = lines.map((line) => FlexLayoutHelper.layoutLine(line, axis, mainAvail, Infinity, // let each line report the cross extent it actually needs
122
+ mainStart, crossOrigin, lineOptions, ctx));
123
+ const heights = measure.map((m) => m.crossUsed);
124
+ const totalCross = heights.reduce((n, h) => n + h, 0) + Math.max(0, lines.length - 1) * gap;
125
+ // `alignContent` distributes the BLOCK of lines across the axis, using the same vocabulary the main
126
+ // axis uses. Only meaningful in a bounded cross axis with room left over.
127
+ const slack = Number.isFinite(crossAvail) ? crossAvail - totalCross : 0;
128
+ const align = options.alignContent ?? "start";
129
+ let cursor = crossOrigin;
130
+ let between = gap;
131
+ if (slack > 0) {
132
+ if (align === "center")
133
+ cursor += slack / 2;
134
+ else if (align === "end")
135
+ cursor += slack;
136
+ else if (align === "between" && lines.length > 1)
137
+ between = gap + slack / (lines.length - 1);
138
+ else if (align === "around") {
139
+ const unit = slack / lines.length;
140
+ cursor += unit / 2;
141
+ between = gap + unit;
142
+ }
143
+ }
144
+ lines.forEach((line, i) => {
145
+ FlexLayoutHelper.layoutLine(line, axis, mainAvail, heights[i], // the line's own extent, so `stretch` fills the line and not the container
146
+ mainStart, cursor, lineOptions, ctx);
147
+ cursor += heights[i];
148
+ if (i < lines.length - 1)
149
+ cursor += between;
150
+ });
151
+ return { mainUsed: mainAvail, crossUsed: Math.max(totalCross, cursor - crossOrigin) };
152
+ }
153
+ static layoutLine(children, axis, mainAvail, crossAvail, mainStart, crossOrigin, options, ctx) {
154
+ const gap = options.gap ?? 0;
155
+ const main = options.main ?? "start";
156
+ const cross = options.cross ?? "stretch";
157
+ children = inLayoutOrder(children, options.reverse ?? false);
43
158
  const count = children.length;
159
+ const totalGap = Math.max(0, count - 1) * gap;
160
+ // The main extent a percentage child resolves against: the line's own extent minus the gaps
161
+ // between the items. So N children at (100/N)% + gaps fit exactly, instead of overflowing by the
162
+ // gaps (the flexbox `width: 33%` + gap trap). Only finite when the line's main axis is bounded -
163
+ // a fraction of an unbounded main axis has no meaning, so `%` children fall back to shrink-wrap.
164
+ const percentBase = mainAvail !== Infinity ? Math.max(0, mainAvail - totalGap) : Infinity;
165
+ // The main cap to hand a child: `percentBase` for a percentage child (so its fraction resolves) and
166
+ // for a child that cannot lay itself out without a bound (a nested stack holding an `Expanded`/
167
+ // `Spacer`); unbounded for everyone else, who keep their natural size and never fill the line.
168
+ // Filled by the shrink pass below; read by `mainCapFor` in pass 2. Declared here so the closure
169
+ // can see it - it is empty for every document that does not ask for shrinking.
170
+ const shrunkCap = new Map();
171
+ const mainCapFor = (child) => {
172
+ const target = shrunkCap.get(child);
173
+ if (target !== undefined)
174
+ return target;
175
+ if (percentBase === Infinity)
176
+ return Infinity;
177
+ const needsBound = child.relativeSizeFactor(axis.mainHorizontal) !== undefined ||
178
+ child.needsBoundedMain(axis.mainHorizontal);
179
+ return needsBound ? percentBase : Infinity;
180
+ };
44
181
  // Pass 1: measure the fixed children (main extent + cross size) and total the flex.
182
+ // A flex child's BASIS is reserved here too: it is main extent the leftover no longer contains,
183
+ // exactly like a fixed child's size. With the default basis of 0 this line adds nothing.
45
184
  let fixedMain = 0;
46
185
  let totalFlex = 0;
186
+ let totalBasis = 0;
47
187
  let crossUsed = 0;
48
188
  const fixedSize = new Map();
49
189
  for (const child of children) {
50
- if (child instanceof pdf_element_1.FlexiblePDFElement) {
190
+ if (child instanceof FlexiblePDFElement) {
51
191
  totalFlex += child.getFlex();
192
+ totalBasis += child.getBasis(percentBase);
52
193
  }
53
194
  else {
54
- const size = child.calculateLayout(axis.measureConstraints(crossAvail), axis.offsetAt(mainStart, crossOrigin), ctx);
195
+ const size = child.calculateLayout(axis.measureConstraints(crossAvail, mainCapFor(child)), axis.offsetAt(mainStart, crossOrigin), ctx);
55
196
  fixedMain += axis.mainOf(size);
56
197
  crossUsed = Math.max(crossUsed, axis.crossOf(size));
57
198
  fixedSize.set(child, size);
58
199
  }
59
200
  }
60
- const totalGap = Math.max(0, count - 1) * gap;
61
- const leftover = mainAvail - fixedMain - totalGap;
62
- const remaining = Math.max(leftover, 0); // for flex children
201
+ // Shrinking: the line overflows and some children said they would give space back. CSS weights a
202
+ // shrinker's share by `shrink x its own size`, so a big item yields more than a small one - which is
203
+ // why this cannot reuse the grow maths above. Nobody shrinks by default, so a document that never
204
+ // asks for it never enters this block.
205
+ const shrinkers = children.filter((c) => c.flexShrink > 0 && !(c instanceof FlexiblePDFElement));
206
+ const overflow = fixedMain + totalBasis + totalGap - mainAvail;
207
+ if (shrinkers.length > 0 && Number.isFinite(mainAvail) && overflow > 0) {
208
+ const weight = (c) => c.flexShrink * axis.mainOf(fixedSize.get(c));
209
+ const totalWeight = shrinkers.reduce((n, c) => n + weight(c), 0);
210
+ if (totalWeight > 0) {
211
+ for (const child of shrinkers) {
212
+ const natural = axis.mainOf(fixedSize.get(child));
213
+ // Never below zero: when the GAPS alone outgrow the line, a proportional share asks for more
214
+ // than a child has. No test can tell this clamp apart from its absence - `constrainWidth`
215
+ // floors at 0 further down either way - but handing out a negative constraint is nonsense,
216
+ // and the next reader should not have to work out that it happens to be harmless.
217
+ const target = Math.max(0, natural - (weight(child) / totalWeight) * overflow);
218
+ shrunkCap.set(child, target);
219
+ // Re-measure NOW, not in pass 2: a narrower child may wrap to more lines, and the line's
220
+ // cross extent is settled just below.
221
+ const size = child.calculateLayout(axis.measureConstraints(crossAvail, target), axis.offsetAt(mainStart, crossOrigin), ctx);
222
+ fixedMain += axis.mainOf(size) - natural;
223
+ crossUsed = Math.max(crossUsed, axis.crossOf(size));
224
+ fixedSize.set(child, size);
225
+ }
226
+ }
227
+ }
228
+ const leftover = mainAvail - fixedMain - totalBasis - totalGap;
229
+ // A flex child on an UNBOUNDED main axis has no leftover space to claim. It must collapse to zero,
230
+ // never to `Infinity`: an infinite extent would become the offset of every following sibling and get
231
+ // written into the content stream verbatim, silently corrupting the page from that point on.
232
+ const remaining = Number.isFinite(leftover) ? Math.max(leftover, 0) : 0;
63
233
  // Main-axis distribution only kicks in with no flex child and bounded, positive space.
64
234
  let leadingSpace = 0;
65
235
  let betweenSpace = gap;
@@ -80,8 +250,8 @@ class FlexLayoutHelper {
80
250
  // toward the line's cross extent - they're placed in pass 2, but crossExtent needs them now.
81
251
  if (totalFlex > 0) {
82
252
  for (const child of children) {
83
- if (child instanceof pdf_element_1.FlexiblePDFElement) {
84
- const mainExtent = (child.getFlex() / totalFlex) * remaining;
253
+ if (child instanceof FlexiblePDFElement) {
254
+ const mainExtent = child.getBasis(percentBase) + (child.getFlex() / totalFlex) * remaining;
85
255
  const size = child.calculateLayout(axis.flexConstraints(mainExtent, crossAvail), axis.offsetAt(mainStart, crossOrigin), ctx);
86
256
  crossUsed = Math.max(crossUsed, axis.crossOf(size));
87
257
  }
@@ -92,20 +262,28 @@ class FlexLayoutHelper {
92
262
  // `stretch` caps a child's cross to `crossExtent` (not `crossAvail`) so siblings end up
93
263
  // equal across the axis. Bounded lines have crossExtent == crossAvail (byte-identical);
94
264
  // only an unbounded line (a shrink-wrap Row) now equalises instead of staying natural.
95
- const stretch = cross === "stretch";
96
- // Pass 2: place each child at the running main position, offset across by `cross`.
265
+ // A child may override the container with its own `alignSelf` (CSS), so the decision is per child.
266
+ const alignFor = (child) => child.alignSelf ?? cross;
267
+ // Pass 2: place each child at the running main position, offset across by its alignment.
97
268
  let mainPos = mainStart + leadingSpace;
98
269
  let placedCross = 0;
99
270
  children.forEach((child, index) => {
271
+ const align = alignFor(child);
272
+ const stretch = align === "stretch";
100
273
  let mainExtent;
101
- if (child instanceof pdf_element_1.FlexiblePDFElement) {
102
- mainExtent = (child.getFlex() / totalFlex) * remaining;
103
- const size = child.calculateLayout(axis.flexConstraints(mainExtent, stretch ? crossExtent : crossAvail), axis.offsetAt(mainPos, crossOrigin), ctx);
274
+ if (child instanceof FlexiblePDFElement) {
275
+ mainExtent = child.getBasis(percentBase) + (child.getFlex() / totalFlex) * remaining;
276
+ // A flex child fills the MAIN axis, and its cross size is only known after layout - there is
277
+ // nothing to align against. So alignSelf is a no-op here, and that has to hold for the CROSS
278
+ // CONSTRAINT too: reading the per-child alignment would hand an `alignSelf: "start"` flex child
279
+ // an unbounded cross axis on a shrink-wrapping line, where the container's own rule gives it
280
+ // the line's extent. A no-op that changes the constraints is not a no-op.
281
+ const size = child.calculateLayout(axis.flexConstraints(mainExtent, cross === "stretch" ? crossExtent : crossAvail), axis.offsetAt(mainPos, crossOrigin), ctx);
104
282
  placedCross = Math.max(placedCross, axis.crossOf(size));
105
283
  }
106
284
  else {
107
285
  const childCross = axis.crossOf(fixedSize.get(child));
108
- const size = child.calculateLayout(axis.measureConstraints(stretch ? crossExtent : crossAvail), axis.offsetAt(mainPos, crossOrigin + crossOffset(cross, crossExtent, childCross)), ctx);
286
+ const size = child.calculateLayout(axis.measureConstraints(stretch ? crossExtent : crossAvail, mainCapFor(child)), axis.offsetAt(mainPos, crossOrigin + crossOffset(align, crossExtent, childCross)), ctx);
109
287
  mainExtent = axis.mainOf(size);
110
288
  placedCross = Math.max(placedCross, axis.crossOf(size));
111
289
  }
@@ -116,4 +294,3 @@ class FlexLayoutHelper {
116
294
  return { mainUsed: mainPos - mainStart, crossUsed: placedCross };
117
295
  }
118
296
  }
119
- exports.FlexLayoutHelper = FlexLayoutHelper;
@@ -1,4 +1,6 @@
1
- import type { FontStyle } from "./pdf-object-manager";
1
+ import type { FontStyle } from "./pdf-object-manager.js";
2
+ import type { FontVerticals } from "../text/line-metrics.js";
3
+ import type { FontDecoration } from "../text/text-decoration.js";
2
4
  /**
3
5
  * Read-only font measurement - the slice of the object manager that the layout pass
4
6
  * needs. Keeping layout (and, later, fragmentation) behind this interface means those
@@ -9,4 +11,27 @@ import type { FontStyle } from "./pdf-object-manager";
9
11
  export interface FontMetrics {
10
12
  getStringWidth(text: string, fontFamily: string, fontSize: number, fontStyle: FontStyle): number;
11
13
  getCharWidth(char: string, fontSize: number, fullFontName?: string, fontName?: string, fontStyle?: FontStyle): number;
14
+ /** Ascent / descent / lineGap of a face, in em fractions - the vertical counterpart of the widths
15
+ * above. Drives the line box and the baseline (see `text/line-metrics.ts`). */
16
+ getFontVerticals(fontFamily: string, fontStyle: FontStyle): FontVerticals;
17
+ /** Underline / strikethrough geometry of a face, in em fractions. GLYPH metrics, deliberately kept
18
+ * apart from the LINE metrics above (see `text/text-decoration.ts`). */
19
+ getFontDecoration(fontFamily: string, fontStyle: FontStyle): FontDecoration;
20
+ /** Whether kerning is on for this document. `runAdvance` reads it so a measured advance matches the
21
+ * drawn one (the backend emits a `TJ` under the same flag). */
22
+ readonly kerningEnabled: boolean;
23
+ /** Per-adjacent-pair kerning of `text`, in em/1000 (negative tightens); length `codePoints - 1`,
24
+ * zero next to a space. Only meaningful when `kerningEnabled`. */
25
+ getKernPairs(text: string, fontFamily: string, fontStyle: FontStyle): number[];
26
+ /** Whether this family can draw the code point at all - what picks a face out of a fallback stack. */
27
+ hasGlyph(codePoint: number, fontFamily: string, fontStyle: FontStyle): boolean;
28
+ /** Whether the document draws this code point as colour emoji rather than from the text font - it
29
+ * is missing from that font on purpose, so glyph coverage must not touch it. */
30
+ rendersAsEmoji?(codePoint: number, fontFamily: string, fontStyle: FontStyle): boolean;
31
+ /** Told when a code point had to be removed because no font could draw it. Optional: a metrics
32
+ * object used only for measuring (a test double) has nothing to report to. */
33
+ reportMissingGlyph?(codePoints: number[]): void;
34
+ /** How many glyphs the run will DRAW, when a ligature made that differ from its code-point count.
35
+ * Absent or `undefined` means "the same", which is every Latin run. */
36
+ shapedGlyphCount?(text: string, fontFamily?: string, fontStyle?: FontStyle): number | undefined;
12
37
  }
@@ -1,2 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ export {};
@@ -1,9 +1,6 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getFontPath = getFontPath;
4
- const os = require("os");
5
- const path = require("path");
6
- function getFontPath(fontName) {
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ export function getFontPath(fontName) {
7
4
  const platform = os.platform();
8
5
  let fontPath = "";
9
6
  if (platform === "win32") {
@@ -0,0 +1,36 @@
1
+ export declare class GsubTable {
2
+ private data;
3
+ private base;
4
+ private scriptList;
5
+ private featureList;
6
+ private lookupList;
7
+ private lookupCache;
8
+ private coverageCache;
9
+ constructor(data: Uint8Array, base: number);
10
+ /** Whether the font carries this script at all - `arab` is the question the shaper asks. */
11
+ hasScript(script: string): boolean;
12
+ /** A feature's lookup indices, under the script's DEFAULT language system - Arabic shaping features
13
+ * are script-wide, so a language-specific override would only add typographic niceties. */
14
+ lookups(script: string, feature: string): number[];
15
+ /** Type 1: substitute one glyph. `null` when no subtable covers it - the feature does not apply. */
16
+ substituteSingle(lookupIndex: number, glyph: number): number | null;
17
+ /**
18
+ * Type 4: collapse a run of glyphs into one, starting at `at`. Longest match wins, as the spec
19
+ * requires - a three-glyph ligature beats the two-glyph one that starts the same way.
20
+ */
21
+ substituteLigature(lookupIndex: number, glyphs: readonly number[], at: number): {
22
+ glyph: number;
23
+ consumed: number;
24
+ } | null;
25
+ /** The type of a lookup, for reporting what a font needs that we do not do. */
26
+ lookupType(lookupIndex: number): number | undefined;
27
+ private scriptOffset;
28
+ /**
29
+ * Resolve a lookup, unwrapping type 7 (extension) - which exists only because the 16-bit offsets
30
+ * elsewhere cannot reach far enough in a big font. Callers never learn it was wrapped.
31
+ */
32
+ private lookup;
33
+ /** Coverage table -> glyph id to its INDEX in the table, which is how subtables address glyphs. */
34
+ private coverage;
35
+ private cacheCoverage;
36
+ }