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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/dist/api/color.d.ts +1 -24
  2. package/dist/api/color.js +2 -278
  3. package/dist/api/content.d.ts +10 -5
  4. package/dist/api/content.js +29 -13
  5. package/dist/api/descriptor.js +30 -3
  6. package/dist/api/flow.d.ts +55 -0
  7. package/dist/api/flow.js +71 -0
  8. package/dist/api/font-url.d.ts +1 -2
  9. package/dist/api/font-url.js +8 -74
  10. package/dist/api/forms.d.ts +3 -0
  11. package/dist/api/forms.js +3 -0
  12. package/dist/api/gradient.d.ts +1 -53
  13. package/dist/api/gradient.js +2 -97
  14. package/dist/api/index.d.ts +4 -0
  15. package/dist/api/index.js +3 -0
  16. package/dist/api/layout.d.ts +6 -0
  17. package/dist/api/layout.js +9 -3
  18. package/dist/api/page-builder.d.ts +4 -0
  19. package/dist/api/page-builder.js +4 -0
  20. package/dist/api/structure.d.ts +10 -2
  21. package/dist/api/structure.js +9 -6
  22. package/dist/api/table.d.ts +5 -3
  23. package/dist/api/table.js +101 -22
  24. package/dist/api/text.d.ts +8 -8
  25. package/dist/api/text.js +6 -0
  26. package/dist/canvas/painter.d.ts +2 -2
  27. package/dist/canvas/painter.js +2 -2
  28. package/dist/common/color-input.d.ts +24 -0
  29. package/dist/common/color-input.js +278 -0
  30. package/dist/common/diagnostics.d.ts +61 -0
  31. package/dist/common/diagnostics.js +77 -0
  32. package/dist/common/gradient.d.ts +53 -0
  33. package/dist/common/gradient.js +97 -0
  34. package/dist/edit/acroform-reader.js +7 -7
  35. package/dist/edit/appearance.js +3 -3
  36. package/dist/edit/attachments.d.ts +52 -0
  37. package/dist/edit/attachments.js +178 -0
  38. package/dist/edit/document.d.ts +38 -8
  39. package/dist/edit/document.js +208 -51
  40. package/dist/edit/fill.js +8 -23
  41. package/dist/edit/filters.d.ts +15 -0
  42. package/dist/edit/filters.js +157 -0
  43. package/dist/edit/index.d.ts +4 -1
  44. package/dist/edit/index.js +3 -1
  45. package/dist/edit/objects.d.ts +3 -4
  46. package/dist/edit/objects.js +5 -18
  47. package/dist/elements/canvas-element.d.ts +3 -0
  48. package/dist/elements/canvas-element.js +14 -3
  49. package/dist/elements/container-element.d.ts +8 -1
  50. package/dist/elements/container-element.js +49 -17
  51. package/dist/elements/forms/checkbox-element.d.ts +2 -0
  52. package/dist/elements/forms/checkbox-element.js +12 -3
  53. package/dist/elements/forms/choice-element.d.ts +6 -1
  54. package/dist/elements/forms/choice-element.js +17 -6
  55. package/dist/elements/forms/field-font.d.ts +8 -0
  56. package/dist/elements/forms/field-font.js +5 -0
  57. package/dist/elements/forms/push-button-element.d.ts +6 -1
  58. package/dist/elements/forms/push-button-element.js +16 -4
  59. package/dist/elements/forms/radio-element.d.ts +2 -0
  60. package/dist/elements/forms/radio-element.js +12 -3
  61. package/dist/elements/forms/signature-element.d.ts +6 -1
  62. package/dist/elements/forms/signature-element.js +16 -4
  63. package/dist/elements/forms/text-field-element.d.ts +6 -1
  64. package/dist/elements/forms/text-field-element.js +16 -4
  65. package/dist/elements/image-element.d.ts +70 -21
  66. package/dist/elements/image-element.js +173 -52
  67. package/dist/elements/index.d.ts +3 -0
  68. package/dist/elements/index.js +3 -0
  69. package/dist/elements/layout/anchor-element.d.ts +8 -10
  70. package/dist/elements/layout/anchor-element.js +10 -18
  71. package/dist/elements/layout/bookmark-element.d.ts +9 -11
  72. package/dist/elements/layout/bookmark-element.js +11 -20
  73. package/dist/elements/layout/column-break-element.d.ts +17 -0
  74. package/dist/elements/layout/column-break-element.js +26 -0
  75. package/dist/elements/layout/default-text-style-element.d.ts +11 -13
  76. package/dist/elements/layout/default-text-style-element.js +14 -31
  77. package/dist/elements/layout/deferred-element.d.ts +20 -4
  78. package/dist/elements/layout/deferred-element.js +41 -9
  79. package/dist/elements/layout/expanded-element.d.ts +11 -0
  80. package/dist/elements/layout/expanded-element.js +53 -18
  81. package/dist/elements/layout/flow-element.d.ts +109 -0
  82. package/dist/elements/layout/flow-element.js +317 -0
  83. package/dist/elements/layout/keep-together-element.d.ts +8 -18
  84. package/dist/elements/layout/keep-together-element.js +33 -45
  85. package/dist/elements/layout/link-element.d.ts +8 -13
  86. package/dist/elements/layout/link-element.js +9 -19
  87. package/dist/elements/layout/padding-element.d.ts +18 -1
  88. package/dist/elements/layout/padding-element.js +63 -28
  89. package/dist/elements/layout/page-break-element.d.ts +4 -1
  90. package/dist/elements/layout/page-break-element.js +6 -1
  91. package/dist/elements/layout/page-builder-element.d.ts +5 -0
  92. package/dist/elements/layout/page-builder-element.js +13 -2
  93. package/dist/elements/layout/positioned-element.d.ts +23 -4
  94. package/dist/elements/layout/positioned-element.js +42 -12
  95. package/dist/elements/layout/proxy-element.d.ts +47 -0
  96. package/dist/elements/layout/proxy-element.js +106 -0
  97. package/dist/elements/layout/region-element.d.ts +53 -0
  98. package/dist/elements/layout/region-element.js +108 -0
  99. package/dist/elements/layout/repeating-header-element.d.ts +9 -0
  100. package/dist/elements/layout/repeating-header-element.js +55 -13
  101. package/dist/elements/layout/rotated-box-element.d.ts +6 -0
  102. package/dist/elements/layout/rotated-box-element.js +27 -14
  103. package/dist/elements/layout/rotated-element.d.ts +9 -0
  104. package/dist/elements/layout/rotated-element.js +33 -2
  105. package/dist/elements/layout/struct-group.d.ts +9 -17
  106. package/dist/elements/layout/struct-group.js +10 -35
  107. package/dist/elements/line-element.d.ts +1 -0
  108. package/dist/elements/line-element.js +6 -0
  109. package/dist/elements/page-element.d.ts +10 -3
  110. package/dist/elements/page-element.js +39 -31
  111. package/dist/elements/pdf-document-element.d.ts +3 -0
  112. package/dist/elements/pdf-document-element.js +7 -0
  113. package/dist/elements/pdf-element.d.ts +105 -16
  114. package/dist/elements/pdf-element.js +118 -19
  115. package/dist/elements/rectangle-element.d.ts +22 -2
  116. package/dist/elements/rectangle-element.js +119 -55
  117. package/dist/elements/row-element.d.ts +17 -12
  118. package/dist/elements/row-element.js +89 -9
  119. package/dist/elements/svg-element.d.ts +30 -6
  120. package/dist/elements/svg-element.js +91 -26
  121. package/dist/elements/text-element.d.ts +48 -13
  122. package/dist/elements/text-element.js +161 -116
  123. package/dist/forms/acroform.d.ts +9 -1
  124. package/dist/forms/acroform.js +83 -34
  125. package/dist/forms/appearance.d.ts +17 -6
  126. package/dist/forms/appearance.js +18 -16
  127. package/dist/ir/display-list.d.ts +18 -3
  128. package/dist/layout/box-constraints.d.ts +17 -0
  129. package/dist/layout/box-constraints.js +31 -0
  130. package/dist/layout/collect-images.d.ts +6 -4
  131. package/dist/layout/collect-images.js +14 -8
  132. package/dist/layout/flex-item.d.ts +11 -0
  133. package/dist/layout/flex-item.js +51 -0
  134. package/dist/layout/fragmentation.d.ts +23 -9
  135. package/dist/layout/fragmentation.js +62 -19
  136. package/dist/layout/layout-cache.d.ts +53 -0
  137. package/dist/layout/layout-cache.js +110 -0
  138. package/dist/platform/browser-fs.d.ts +1 -2
  139. package/dist/platform/browser-fs.js +3 -6
  140. package/dist/platform/node-fs.d.ts +0 -1
  141. package/dist/platform/node-fs.js +0 -4
  142. package/dist/renderer/anchor-renderer.d.ts +2 -1
  143. package/dist/renderer/anchor-renderer.js +5 -4
  144. package/dist/renderer/bookmark-renderer.d.ts +2 -1
  145. package/dist/renderer/bookmark-renderer.js +5 -4
  146. package/dist/renderer/canvas-renderer.d.ts +2 -1
  147. package/dist/renderer/canvas-renderer.js +5 -2
  148. package/dist/renderer/column-break-renderer.d.ts +8 -0
  149. package/dist/renderer/column-break-renderer.js +15 -0
  150. package/dist/renderer/container-renderer.d.ts +2 -1
  151. package/dist/renderer/container-renderer.js +6 -5
  152. package/dist/renderer/default-text-style-renderer.d.ts +2 -1
  153. package/dist/renderer/default-text-style-renderer.js +2 -3
  154. package/dist/renderer/deferred-renderer.d.ts +2 -1
  155. package/dist/renderer/deferred-renderer.js +2 -3
  156. package/dist/renderer/emoji-image.js +3 -1
  157. package/dist/renderer/expanded-renderer.d.ts +2 -1
  158. package/dist/renderer/expanded-renderer.js +2 -3
  159. package/dist/renderer/flow-renderer.d.ts +16 -0
  160. package/dist/renderer/flow-renderer.js +21 -0
  161. package/dist/renderer/form-field-renderer.d.ts +2 -1
  162. package/dist/renderer/form-field-renderer.js +16 -3
  163. package/dist/renderer/image-renderer.d.ts +2 -1
  164. package/dist/renderer/image-renderer.js +33 -53
  165. package/dist/renderer/index.d.ts +2 -0
  166. package/dist/renderer/index.js +2 -0
  167. package/dist/renderer/keep-together-renderer.d.ts +2 -1
  168. package/dist/renderer/keep-together-renderer.js +2 -3
  169. package/dist/renderer/line-renderer.d.ts +2 -1
  170. package/dist/renderer/line-renderer.js +7 -2
  171. package/dist/renderer/link-renderer.d.ts +2 -1
  172. package/dist/renderer/link-renderer.js +36 -5
  173. package/dist/renderer/padding-renderer.d.ts +2 -1
  174. package/dist/renderer/padding-renderer.js +5 -5
  175. package/dist/renderer/page-break-renderer.d.ts +6 -5
  176. package/dist/renderer/page-break-renderer.js +14 -7
  177. package/dist/renderer/page-builder-renderer.d.ts +2 -1
  178. package/dist/renderer/page-builder-renderer.js +2 -3
  179. package/dist/renderer/page-renderer.js +30 -10
  180. package/dist/renderer/pdf-backend.d.ts +4 -3
  181. package/dist/renderer/pdf-backend.js +14 -7
  182. package/dist/renderer/pdf-document-renderer.d.ts +1 -8
  183. package/dist/renderer/pdf-document-renderer.js +17 -17
  184. package/dist/renderer/pdf-renderer.js +20 -11
  185. package/dist/renderer/positioned-renderer.d.ts +18 -1
  186. package/dist/renderer/positioned-renderer.js +43 -6
  187. package/dist/renderer/rectangle-renderer.d.ts +2 -1
  188. package/dist/renderer/rectangle-renderer.js +18 -8
  189. package/dist/renderer/repeating-header-renderer.d.ts +2 -1
  190. package/dist/renderer/repeating-header-renderer.js +6 -5
  191. package/dist/renderer/rotated-renderer.d.ts +2 -1
  192. package/dist/renderer/rotated-renderer.js +9 -5
  193. package/dist/renderer/row-renderer.d.ts +2 -1
  194. package/dist/renderer/row-renderer.js +6 -5
  195. package/dist/renderer/struct-group-renderer.d.ts +2 -1
  196. package/dist/renderer/struct-group-renderer.js +2 -3
  197. package/dist/renderer/svg-renderer.d.ts +2 -1
  198. package/dist/renderer/svg-renderer.js +8 -2
  199. package/dist/renderer/text-renderer.d.ts +4 -6
  200. package/dist/renderer/text-renderer.js +114 -127
  201. package/dist/svg/gradients.js +5 -0
  202. package/dist/svg/index.d.ts +1 -1
  203. package/dist/svg/index.js +36 -6
  204. package/dist/svg/style.d.ts +2 -0
  205. package/dist/svg/style.js +12 -1
  206. package/dist/text/advance.d.ts +4 -0
  207. package/dist/text/advance.js +17 -4
  208. package/dist/text/bidi.d.ts +0 -2
  209. package/dist/text/bidi.js +0 -10
  210. package/dist/text/glyph-coverage.d.ts +1 -1
  211. package/dist/text/glyph-coverage.js +7 -3
  212. package/dist/text/hyphen-breaks.d.ts +17 -0
  213. package/dist/text/hyphen-breaks.js +54 -0
  214. package/dist/text/line-breaker.d.ts +55 -29
  215. package/dist/text/line-breaker.js +453 -188
  216. package/dist/text/line-metrics.d.ts +8 -3
  217. package/dist/text/line-metrics.js +11 -2
  218. package/dist/text/pdf-text-string.d.ts +21 -0
  219. package/dist/text/pdf-text-string.js +114 -0
  220. package/dist/text/text-decoration.d.ts +5 -5
  221. package/dist/text/text-decoration.js +5 -5
  222. package/dist/text/word-splitting.d.ts +15 -2
  223. package/dist/text/word-splitting.js +45 -13
  224. package/dist/utils/afm-parser.d.ts +2 -0
  225. package/dist/utils/afm-parser.js +10 -1
  226. package/dist/utils/dest-registry.d.ts +1 -1
  227. package/dist/utils/dest-registry.js +3 -2
  228. package/dist/utils/fetch-bytes.d.ts +19 -0
  229. package/dist/utils/fetch-bytes.js +79 -0
  230. package/dist/utils/flex-layout.d.ts +16 -14
  231. package/dist/utils/flex-layout.js +427 -217
  232. package/dist/utils/font-metrics.d.ts +4 -3
  233. package/dist/utils/image-helper.d.ts +59 -11
  234. package/dist/utils/image-helper.js +209 -81
  235. package/dist/utils/pdf-object-manager.d.ts +17 -6
  236. package/dist/utils/pdf-object-manager.js +70 -20
  237. package/dist/utils/renderer-registry.d.ts +49 -2
  238. package/dist/utils/renderer-registry.js +40 -0
  239. package/dist/utils/struct-tree.d.ts +24 -0
  240. package/dist/utils/struct-tree.js +61 -9
  241. package/dist/utils/ttf-parser.d.ts +4 -0
  242. package/dist/utils/ttf-parser.js +30 -6
  243. package/dist/validators/element-validator.js +2 -1
  244. package/package.json +1 -1
  245. package/dist/elements/layout/sized-container-element.d.ts +0 -14
  246. package/dist/elements/layout/sized-container-element.js +0 -32
@@ -2,13 +2,14 @@
2
2
  * The vertical half of text layout: how tall a line box is, and where its baseline sits.
3
3
  *
4
4
  * One canonical answer, like `line-breaker.ts` is the one canonical answer for the horizontal
5
- * half. Measuring (`calculateTextHeight`), fragmenting (`TextElement.fragment`) and drawing
5
+ * half. Measuring (`textHeight`), fragmenting (`TextElement.fragment`) and drawing
6
6
  * (`TextRenderer._buildRuns`) all call in here, so they can never disagree about a line's height
7
7
  * again - the divergence that produced ISSUE-5.
8
8
  */
9
9
  import type { FontStyle } from "../utils/pdf-object-manager.js";
10
10
  import type { FontMetrics } from "../utils/font-metrics.js";
11
- import type { SegmentDefaults, SegmentLine } from "./line-breaker.js";
11
+ import type { TextSegment } from "../elements/text-element.js";
12
+ import { type SegmentDefaults, type SegmentLine, type TextOverflow } from "./line-breaker.js";
12
13
  /** A font's vertical metrics, as fractions of the em (so they scale by multiplying with fontSize). */
13
14
  export interface FontVerticals {
14
15
  /** Distance from the baseline up to the ascender. Positive. */
@@ -42,8 +43,12 @@ export interface LineBox {
42
43
  * exactly like CSS, and the author asked for it.
43
44
  */
44
45
  export declare function lineBoxFor(parts: LinePart[], lineHeight?: number): LineBox;
45
- /** The line box of a single-font line (the plain-string path). */
46
+ /** The line box of a single-font line - what an empty line takes in the paragraph's own font. */
46
47
  export declare function lineBoxForString(metrics: FontMetrics, fontFamily: string, fontStyle: FontStyle, fontSize: number, lineHeight?: number): LineBox;
47
48
  /** The line box of a mixed-font line: the tallest ascent and the deepest descent on it win, so a
48
49
  * big or a deep-descending span pushes the whole line apart rather than colliding with it. */
49
50
  export declare function lineBoxForSegmentLine(line: SegmentLine, defaults: SegmentDefaults, metrics: FontMetrics, lineHeight?: number): LineBox;
51
+ /** How tall a text is at `maxWidth`: its lines, from the one breaker, each in its own line box. */
52
+ export declare function textHeight(spans: TextSegment[], defaults: SegmentDefaults, maxWidth: number, metrics: FontMetrics, maxLines?: number, overflow?: TextOverflow, lineHeight?: number): number;
53
+ /** How tall these lines stand, each in its own line box. */
54
+ export declare function linesHeight(lines: SegmentLine[], defaults: SegmentDefaults, metrics: FontMetrics, lineHeight?: number): number;
@@ -2,10 +2,11 @@
2
2
  * The vertical half of text layout: how tall a line box is, and where its baseline sits.
3
3
  *
4
4
  * One canonical answer, like `line-breaker.ts` is the one canonical answer for the horizontal
5
- * half. Measuring (`calculateTextHeight`), fragmenting (`TextElement.fragment`) and drawing
5
+ * half. Measuring (`textHeight`), fragmenting (`TextElement.fragment`) and drawing
6
6
  * (`TextRenderer._buildRuns`) all call in here, so they can never disagree about a line's height
7
7
  * again - the divergence that produced ISSUE-5.
8
8
  */
9
+ import { breakSegmentsIntoLines, } from "./line-breaker.js";
9
10
  /**
10
11
  * The line box for a set of fonts sharing one line.
11
12
  *
@@ -34,7 +35,7 @@ export function lineBoxFor(parts, lineHeight) {
34
35
  const height = lineHeight === undefined ? content + gap : maxFontSize * lineHeight;
35
36
  return { height, baseline: (height - content) / 2 + ascent };
36
37
  }
37
- /** The line box of a single-font line (the plain-string path). */
38
+ /** The line box of a single-font line - what an empty line takes in the paragraph's own font. */
38
39
  export function lineBoxForString(metrics, fontFamily, fontStyle, fontSize, lineHeight) {
39
40
  return lineBoxFor([{ verticals: metrics.getFontVerticals(fontFamily, fontStyle), fontSize }], lineHeight);
40
41
  }
@@ -55,3 +56,11 @@ export function lineBoxForSegmentLine(line, defaults, metrics, lineHeight) {
55
56
  }
56
57
  return lineBoxFor(parts, lineHeight);
57
58
  }
59
+ /** How tall a text is at `maxWidth`: its lines, from the one breaker, each in its own line box. */
60
+ export function textHeight(spans, defaults, maxWidth, metrics, maxLines, overflow, lineHeight) {
61
+ return linesHeight(breakSegmentsIntoLines(spans, defaults, maxWidth, metrics, maxLines, overflow), defaults, metrics, lineHeight);
62
+ }
63
+ /** How tall these lines stand, each in its own line box. */
64
+ export function linesHeight(lines, defaults, metrics, lineHeight) {
65
+ return lines.reduce((total, line) => total + lineBoxForSegmentLine(line, defaults, metrics, lineHeight).height, 0);
66
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * PDF text strings - a title, a bookmark, alt text, a field value, an attachment's name - in both
3
+ * directions, from ONE table.
4
+ *
5
+ * A text string is PDFDocEncoding or UTF-16BE with a byte-order mark (PDF 2.0 adds UTF-8 with one).
6
+ * PDFDocEncoding is NOT Windows-1252: byte 0x96 is an en dash in Windows-1252 and "Œ" here, 0x80 is "€"
7
+ * there and "•" here. A writer that sends text through the Windows-1252 page encoder therefore shows
8
+ * "Preis – 5 €" as "Preis Œ 5 •".
9
+ */
10
+ /** A text string's bytes: PDFDocEncoding when it can say everything, otherwise UTF-16BE with its BOM. */
11
+ export declare function encodePdfText(text: string): Uint8Array;
12
+ /** Name-tree order: keys sorted by their ENCODED bytes, which is what a reader searching the tree compares -
13
+ * not JS string order, which disagrees as soon as one key is PDFDocEncoding and another UTF-16. */
14
+ export declare function comparePdfText(a: string, b: string): number;
15
+ /** A text string's meaning: UTF-16BE or UTF-8 by their byte-order mark, otherwise PDFDocEncoding. */
16
+ export declare function decodePdfText(bytes: Uint8Array): string;
17
+ /**
18
+ * Whether `text` can be written as a plain `(literal)` that the Windows-1252 page encoder turns into the
19
+ * same bytes PDFDocEncoding reads back: printable ASCII and the Latin-1 letters both encodings share.
20
+ */
21
+ export declare function isPlainPdfText(text: string): boolean;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * PDF text strings - a title, a bookmark, alt text, a field value, an attachment's name - in both
3
+ * directions, from ONE table.
4
+ *
5
+ * A text string is PDFDocEncoding or UTF-16BE with a byte-order mark (PDF 2.0 adds UTF-8 with one).
6
+ * PDFDocEncoding is NOT Windows-1252: byte 0x96 is an en dash in Windows-1252 and "Œ" here, 0x80 is "€"
7
+ * there and "•" here. A writer that sends text through the Windows-1252 page encoder therefore shows
8
+ * "Preis – 5 €" as "Preis Œ 5 •".
9
+ */
10
+ /** PDFDocEncoding where it differs from Latin-1 (ISO 32000-1, Annex D); 0x9F and 0xAD are undefined. */
11
+ const PDF_DOC = {
12
+ 0x18: 0x02d8,
13
+ 0x19: 0x02c7,
14
+ 0x1a: 0x02c6,
15
+ 0x1b: 0x02d9,
16
+ 0x1c: 0x02dd,
17
+ 0x1d: 0x02db,
18
+ 0x1e: 0x02da,
19
+ 0x1f: 0x02dc,
20
+ 0x80: 0x2022,
21
+ 0x81: 0x2020,
22
+ 0x82: 0x2021,
23
+ 0x83: 0x2026,
24
+ 0x84: 0x2014,
25
+ 0x85: 0x2013,
26
+ 0x86: 0x0192,
27
+ 0x87: 0x2044,
28
+ 0x88: 0x2039,
29
+ 0x89: 0x203a,
30
+ 0x8a: 0x2212,
31
+ 0x8b: 0x2030,
32
+ 0x8c: 0x201e,
33
+ 0x8d: 0x201c,
34
+ 0x8e: 0x201d,
35
+ 0x8f: 0x2018,
36
+ 0x90: 0x2019,
37
+ 0x91: 0x201a,
38
+ 0x92: 0x2122,
39
+ 0x93: 0xfb01,
40
+ 0x94: 0xfb02,
41
+ 0x95: 0x0141,
42
+ 0x96: 0x0152,
43
+ 0x97: 0x0160,
44
+ 0x98: 0x0178,
45
+ 0x99: 0x017d,
46
+ 0x9a: 0x0131,
47
+ 0x9b: 0x0142,
48
+ 0x9c: 0x0153,
49
+ 0x9d: 0x0161,
50
+ 0x9e: 0x017e,
51
+ 0xa0: 0x20ac,
52
+ };
53
+ const FROM_UNICODE = new Map(Object.entries(PDF_DOC).map(([byte, cp]) => [cp, Number(byte)]));
54
+ /** Latin-1 code points that mean something else in PDFDocEncoding, or nothing. */
55
+ const NOT_LATIN1 = new Set([...Object.keys(PDF_DOC).map(Number), 0x9f, 0xad]);
56
+ /** A text string's bytes: PDFDocEncoding when it can say everything, otherwise UTF-16BE with its BOM. */
57
+ export function encodePdfText(text) {
58
+ const doc = [];
59
+ for (const ch of text) {
60
+ const cp = ch.codePointAt(0);
61
+ const byte = cp <= 0xff && !NOT_LATIN1.has(cp) ? cp : FROM_UNICODE.get(cp);
62
+ if (byte === undefined)
63
+ return utf16be(text);
64
+ doc.push(byte);
65
+ }
66
+ return Uint8Array.from(doc);
67
+ }
68
+ function utf16be(text) {
69
+ const out = new Uint8Array(2 + text.length * 2);
70
+ out[0] = 0xfe;
71
+ out[1] = 0xff;
72
+ for (let i = 0; i < text.length; i++) {
73
+ const unit = text.charCodeAt(i);
74
+ out[2 + i * 2] = unit >> 8;
75
+ out[3 + i * 2] = unit & 0xff;
76
+ }
77
+ return out;
78
+ }
79
+ /** Name-tree order: keys sorted by their ENCODED bytes, which is what a reader searching the tree compares -
80
+ * not JS string order, which disagrees as soon as one key is PDFDocEncoding and another UTF-16. */
81
+ export function comparePdfText(a, b) {
82
+ const x = encodePdfText(a);
83
+ const y = encodePdfText(b);
84
+ for (let i = 0; i < Math.min(x.length, y.length); i++)
85
+ if (x[i] !== y[i])
86
+ return x[i] - y[i];
87
+ return x.length - y.length;
88
+ }
89
+ /** A text string's meaning: UTF-16BE or UTF-8 by their byte-order mark, otherwise PDFDocEncoding. */
90
+ export function decodePdfText(bytes) {
91
+ if (bytes.length >= 2 && bytes[0] === 0xfe && bytes[1] === 0xff) {
92
+ return new TextDecoder("utf-16be").decode(bytes.subarray(2));
93
+ }
94
+ if (bytes.length >= 3 && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf) {
95
+ return new TextDecoder().decode(bytes.subarray(3));
96
+ }
97
+ let s = "";
98
+ for (const byte of bytes)
99
+ s += String.fromCodePoint(PDF_DOC[byte] ?? byte);
100
+ return s;
101
+ }
102
+ /**
103
+ * Whether `text` can be written as a plain `(literal)` that the Windows-1252 page encoder turns into the
104
+ * same bytes PDFDocEncoding reads back: printable ASCII and the Latin-1 letters both encodings share.
105
+ */
106
+ export function isPlainPdfText(text) {
107
+ for (let i = 0; i < text.length; i++) {
108
+ const c = text.charCodeAt(i);
109
+ const ascii = (c >= 0x20 && c <= 0x7e) || c === 0x09 || c === 0x0a || c === 0x0d;
110
+ if (!ascii && !(c >= 0xa1 && c <= 0xff && c !== 0xad))
111
+ return false;
112
+ }
113
+ return true;
114
+ }
@@ -7,11 +7,11 @@
7
7
  * `FontBBox`. Keep the two kinds of number in two places so they cannot be mixed up again.
8
8
  *
9
9
  * Every number here comes out of the font: the standard-14 AFM header, or a TrueType `post` +
10
- * `OS/2` table. Nothing is invented. Verified against `google-chrome --headless --print-to-pdf` on
11
- * the same font: Chrome's strikethrough centre lands on `xHeight / 2` (measured 0.260 em vs the
12
- * font's 0.262), and its underline stroke with `text-decoration-thickness: from-font` is the font's
13
- * `underlineThickness` (0.048 em vs 0.050). Chrome does NOT use the font's underline POSITION - it
14
- * has its own offset - so we follow the font there and land within 0.02 em of it.
10
+ * `OS/2` table. Nothing is invented. The strikethrough sits at `xHeight / 2` - our rule, not
11
+ * Chrome's: on the font first measured Chrome's centre landed there too (0.260 em vs 0.262), on
12
+ * Liberation Sans and Noto Sans it sat 2-4 pt higher at 40 pt. The underline stroke is the font's
13
+ * `underlineThickness`, as Chrome draws it with `text-decoration-thickness: from-font`; its POSITION
14
+ * follows the font, where Chrome has an offset of its own.
15
15
  */
16
16
  /** A font's decoration metrics, as fractions of the em. */
17
17
  export interface FontDecoration {
@@ -7,11 +7,11 @@
7
7
  * `FontBBox`. Keep the two kinds of number in two places so they cannot be mixed up again.
8
8
  *
9
9
  * Every number here comes out of the font: the standard-14 AFM header, or a TrueType `post` +
10
- * `OS/2` table. Nothing is invented. Verified against `google-chrome --headless --print-to-pdf` on
11
- * the same font: Chrome's strikethrough centre lands on `xHeight / 2` (measured 0.260 em vs the
12
- * font's 0.262), and its underline stroke with `text-decoration-thickness: from-font` is the font's
13
- * `underlineThickness` (0.048 em vs 0.050). Chrome does NOT use the font's underline POSITION - it
14
- * has its own offset - so we follow the font there and land within 0.02 em of it.
10
+ * `OS/2` table. Nothing is invented. The strikethrough sits at `xHeight / 2` - our rule, not
11
+ * Chrome's: on the font first measured Chrome's centre landed there too (0.260 em vs 0.262), on
12
+ * Liberation Sans and Noto Sans it sat 2-4 pt higher at 40 pt. The underline stroke is the font's
13
+ * `underlineThickness`, as Chrome draws it with `text-decoration-thickness: from-font`; its POSITION
14
+ * follows the font, where Chrome has an offset of its own.
15
15
  */
16
16
  /** The underline for a run whose baseline sits at `baselineY` (engine coords, y grows down). */
17
17
  export function underlineStroke(decoration, fontSize, baselineY) {
@@ -27,16 +27,29 @@ export interface WordSplitting {
27
27
  /** Split anywhere as a last resort, without a hyphen. CSS `overflow-wrap: break-word`. */
28
28
  breakWord?: boolean;
29
29
  }
30
+ /** The hyphen actually drawn at a hyphenation break. U+002D, in every font we can encode. */
31
+ export declare const HYPHEN = "-";
32
+ /**
33
+ * One piece of a split word. `hyphen`: a hyphen was added at the break - it is not in the source.
34
+ * `source`: how many characters of the word the piece covers, which is where the next piece starts.
35
+ */
36
+ export interface WordPiece {
37
+ text: string;
38
+ hyphen: boolean;
39
+ source: number;
40
+ }
30
41
  /**
31
42
  * Splits `word` into pieces that each fit within `room`. Returns null when nothing applies - no
32
43
  * splitting configured, or the word fits anyway - and the caller keeps its current behaviour, so a
33
44
  * document that asks for neither is untouched.
34
45
  *
35
46
  * Every piece but the last is a finished line. The last is what carries on, so the caller keeps
36
- * filling it as usual.
47
+ * filling it as usual. A piece says how much of the source it covers: a split paragraph needs that to
48
+ * find where the next line starts, and the drawn text cannot tell (an added hyphen, a soft hyphen the
49
+ * hyphenator dropped).
37
50
  */
38
51
  export declare function splitLongWord(word: string, room: number, font: {
39
52
  fontFamily: string;
40
53
  fontSize: number;
41
54
  fontStyle: FontStyle;
42
- }, metrics: FontMetrics, letterSpacing: number, options: WordSplitting): string[] | null;
55
+ }, metrics: FontMetrics, letterSpacing: number, options: WordSplitting): WordPiece[] | null;
@@ -1,13 +1,15 @@
1
- import { runAdvance } from "./advance.js";
1
+ import { runAdvance, SOFT_HYPHEN } from "./advance.js";
2
2
  /** The hyphen actually drawn at a hyphenation break. U+002D, in every font we can encode. */
3
- const HYPHEN = "-";
3
+ export const HYPHEN = "-";
4
4
  /**
5
5
  * Splits `word` into pieces that each fit within `room`. Returns null when nothing applies - no
6
6
  * splitting configured, or the word fits anyway - and the caller keeps its current behaviour, so a
7
7
  * document that asks for neither is untouched.
8
8
  *
9
9
  * Every piece but the last is a finished line. The last is what carries on, so the caller keeps
10
- * filling it as usual.
10
+ * filling it as usual. A piece says how much of the source it covers: a split paragraph needs that to
11
+ * find where the next line starts, and the drawn text cannot tell (an added hyphen, a soft hyphen the
12
+ * hyphenator dropped).
11
13
  */
12
14
  export function splitLongWord(word, room, font, metrics, letterSpacing, options) {
13
15
  const { hyphenate, breakWord } = options;
@@ -19,23 +21,47 @@ export function splitLongWord(word, room, font, metrics, letterSpacing, options)
19
21
  return null;
20
22
  const width = (s) => runAdvance(metrics, s, font, letterSpacing);
21
23
  const pieces = [];
22
- let rest = word;
24
+ let rest = word; // what is drawn
25
+ let restSource = word; // the same text as it stands in the source
23
26
  // Bounded by construction: every pass either emits a piece and shortens `rest`, or gives up.
24
27
  while (width(rest) > room) {
25
28
  // The layering: a linguistically valid split if one is offered and reaches far enough, otherwise
26
29
  // the blunt one - which is what an e-mail address or an IBAN needs, having no valid point at all.
27
30
  const head = (hyphenate ? longestHyphenatedPrefix(rest, room, width, hyphenate) : null) ??
28
31
  (breakWord ? longestPrefix(rest, room, width) : null);
29
- // Nothing fits - not even one character, or hyphenation found nothing and break-word is off.
30
- // Leave the remainder whole and let it overflow, exactly as before.
32
+ // Hyphenation found nothing and break-word is off, or one character is all that is left: leave the
33
+ // remainder whole and let it overflow.
31
34
  if (head === null)
32
35
  break;
33
- pieces.push(head.text);
36
+ // A hyphenator that returns text other than the word is not splitting it: leave it whole.
37
+ const source = sourceLength(restSource, head.taken);
38
+ if (source === null)
39
+ break;
40
+ pieces.push({ text: head.text, hyphen: head.hyphen, source });
34
41
  rest = head.rest;
42
+ restSource = restSource.slice(source);
35
43
  }
36
44
  if (pieces.length === 0)
37
45
  return null;
38
- return [...pieces, rest];
46
+ return [...pieces, { text: rest, hyphen: false, source: restSource.length }];
47
+ }
48
+ /**
49
+ * How many characters of `source` the drawn `prefix` covers, with a soft hyphen at the break. A
50
+ * hyphenator may drop the soft hyphens already in the text (the `hyphen` package does), so they are
51
+ * skipped; anything else that does not match is null.
52
+ */
53
+ function sourceLength(source, prefix) {
54
+ let i = 0;
55
+ for (const char of prefix) {
56
+ while (source[i] === SOFT_HYPHEN && char !== SOFT_HYPHEN)
57
+ i++;
58
+ if (!source.startsWith(char, i))
59
+ return null;
60
+ i += char.length;
61
+ }
62
+ while (source[i] === SOFT_HYPHEN)
63
+ i++;
64
+ return i;
39
65
  }
40
66
  /** The longest prefix that fits, plus a hyphen - split only where the hyphenator allows. */
41
67
  function longestHyphenatedPrefix(word, room, width, hyphenate) {
@@ -49,7 +75,7 @@ function longestHyphenatedPrefix(word, room, width, hyphenate) {
49
75
  // The hyphen has to fit too, or the line it lands on overflows by exactly that hyphen.
50
76
  if (width(taken + HYPHEN) > room)
51
77
  break;
52
- best = { text: taken + HYPHEN, rest: parts.slice(i + 1).join("") };
78
+ best = { text: taken + HYPHEN, taken, rest: parts.slice(i + 1).join(""), hyphen: true };
53
79
  }
54
80
  return best;
55
81
  }
@@ -64,9 +90,15 @@ function longestPrefix(word, room, width) {
64
90
  taken += char;
65
91
  count += 1;
66
92
  }
67
- // Not even one character fits: the box is narrower than a single glyph. Splitting cannot help, and
68
- // looping on it would never terminate.
69
- if (count === 0 || count === chars.length)
93
+ if (count === chars.length)
70
94
  return null;
71
- return { text: taken, rest: chars.slice(count).join("") };
95
+ // Not even one character fits - the box is narrower than a glyph: one character per line, as CSS
96
+ // `overflow-wrap` sets it. Each pass still takes one, so the loop ends.
97
+ if (count === 0) {
98
+ if (chars.length === 1)
99
+ return null;
100
+ taken = chars[0];
101
+ count = 1;
102
+ }
103
+ return { text: taken, taken, rest: chars.slice(count).join(""), hyphen: false };
72
104
  }
@@ -42,5 +42,7 @@ export declare class AFMParser {
42
42
  */
43
43
  decoration(): FontDecoration;
44
44
  getAdvanceWidth(charName: string): number;
45
+ /** The pair's kerning. The AFM keys its pairs by GLYPH name (`KPX T comma`), like its widths - so the
46
+ * characters are named first, or only letter-letter pairs (where the two coincide) would ever hit. */
45
47
  getKerning(firstChar: string, secondChar: string): number;
46
48
  }
@@ -11,6 +11,13 @@ function glyphList() {
11
11
  if (parts.length >= 2)
12
12
  map[String.fromCharCode(parseInt(parts[0], 16))] = parts[1];
13
13
  }
14
+ // Windows-1252 characters the AGLFN leaves out, by the glyph WinAnsiEncoding draws them with
15
+ // (ISO 32000-1, Annex D). Unnamed, they were measured as "?" while the viewer drew the real glyph.
16
+ map["\u00a0"] = "space";
17
+ map["\u00ad"] = "hyphen";
18
+ map["\u00b2"] = "twosuperior";
19
+ map["\u00b3"] = "threesuperior";
20
+ map["\u00b9"] = "onesuperior";
14
21
  return (sharedGlyphMap = map);
15
22
  }
16
23
  /** One parser per standard-14 face for the whole process: the AFM data is a constant and a parsed
@@ -152,7 +159,9 @@ export class AFMParser {
152
159
  getAdvanceWidth(charName) {
153
160
  return this.advanceWidths[this.getGlyphName(charName)] || 0;
154
161
  }
162
+ /** The pair's kerning. The AFM keys its pairs by GLYPH name (`KPX T comma`), like its widths - so the
163
+ * characters are named first, or only letter-letter pairs (where the two coincide) would ever hit. */
155
164
  getKerning(firstChar, secondChar) {
156
- return this.kerningPairs[firstChar]?.[secondChar] ?? 0;
165
+ return this.kerningPairs[this.getGlyphName(firstChar)]?.[this.getGlyphName(secondChar)] ?? 0;
157
166
  }
158
167
  }
@@ -14,7 +14,7 @@ export declare class DestRegistry {
14
14
  add(name: string, dest: Dest): void;
15
15
  get isEmpty(): boolean;
16
16
  /** The `/Dests << /Names [ ... ] >>` fragment for the catalog `/Names` dictionary (merged with any
17
- * `/EmbeddedFiles`). Returns "" when empty. Names are sorted lexically - a name tree requires it.
17
+ * `/EmbeddedFiles`). Returns "" when empty. Names are sorted by their encoded bytes - a name tree requires it.
18
18
  * Takes the object manager only so the names go through its string choke-point and get encrypted
19
19
  * with everything else. */
20
20
  finalize(om: PDFObjectManager): string;
@@ -1,3 +1,4 @@
1
+ import { comparePdfText } from "../text/pdf-text-string.js";
1
2
  const num = (n) => (Number.isFinite(n) ? Number(n.toFixed(2)) : 0);
2
3
  /**
3
4
  * Collects named destinations - the jump targets an internal `Link({ to })` points at, declared with
@@ -17,13 +18,13 @@ export class DestRegistry {
17
18
  return this.dests.size === 0;
18
19
  }
19
20
  /** The `/Dests << /Names [ ... ] >>` fragment for the catalog `/Names` dictionary (merged with any
20
- * `/EmbeddedFiles`). Returns "" when empty. Names are sorted lexically - a name tree requires it.
21
+ * `/EmbeddedFiles`). Returns "" when empty. Names are sorted by their encoded bytes - a name tree requires it.
21
22
  * Takes the object manager only so the names go through its string choke-point and get encrypted
22
23
  * with everything else. */
23
24
  finalize(om) {
24
25
  if (this.dests.size === 0)
25
26
  return "";
26
- const entries = [...this.dests.keys()].sort().map((name) => {
27
+ const entries = [...this.dests.keys()].sort(comparePdfText).map((name) => {
27
28
  const d = this.dests.get(name);
28
29
  return `${om.pdfString(name)} [${d.pageObjNum} 0 R /XYZ null ${num(d.top)} null]`;
29
30
  });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Bytes from a URL, bounded - what a font and an image from a URL both go through.
3
+ *
4
+ * `fetch` is used directly: it is in every browser and in Node since 18, so this needs no platform
5
+ * port. A hung server must not hang a render, and a URL pointing at the wrong file (a video, a disk
6
+ * image) must not be read into memory in full before anyone notices: a timeout and a size ceiling that
7
+ * is enforced WHILE the body arrives.
8
+ */
9
+ /** The error a caller wants its failures in - `FontUrlError`, `ImageUrlError`. */
10
+ export type UrlErrorClass = new (message: string) => Error;
11
+ export interface FetchLimits {
12
+ timeoutMs: number;
13
+ maxBytes: number;
14
+ }
15
+ /**
16
+ * Fetch `url` and hand back its body. Every failure is a `Fail` naming the URL and what it was meant
17
+ * to be (`what`: "font", "image"), never a raw network error a caller would not expect.
18
+ */
19
+ export declare function fetchBytes(url: string, what: string, Fail: UrlErrorClass, limits: FetchLimits): Promise<Uint8Array>;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Bytes from a URL, bounded - what a font and an image from a URL both go through.
3
+ *
4
+ * `fetch` is used directly: it is in every browser and in Node since 18, so this needs no platform
5
+ * port. A hung server must not hang a render, and a URL pointing at the wrong file (a video, a disk
6
+ * image) must not be read into memory in full before anyone notices: a timeout and a size ceiling that
7
+ * is enforced WHILE the body arrives.
8
+ */
9
+ /**
10
+ * Fetch `url` and hand back its body. Every failure is a `Fail` naming the URL and what it was meant
11
+ * to be (`what`: "font", "image"), never a raw network error a caller would not expect.
12
+ */
13
+ export async function fetchBytes(url, what, Fail, limits) {
14
+ let response;
15
+ try {
16
+ response = await fetch(url, { signal: AbortSignal.timeout(limits.timeoutMs) });
17
+ }
18
+ catch (e) {
19
+ const why = e?.name === "TimeoutError" || e?.name === "AbortError"
20
+ ? `it did not respond within ${limits.timeoutMs} ms`
21
+ : String(e?.message ?? e);
22
+ throw new Fail(`could not fetch the ${what} at ${url}: ${why}`);
23
+ }
24
+ if (!response.ok) {
25
+ throw new Fail(`the ${what} at ${url} could not be fetched: HTTP ${response.status}`);
26
+ }
27
+ // Everything past here still talks to the network, so a failure mid-body must not escape as a raw
28
+ // TypeError - a caller catching its own error class would miss it.
29
+ try {
30
+ return await readBounded(response, url, what, Fail, limits.maxBytes);
31
+ }
32
+ catch (e) {
33
+ if (e instanceof Fail)
34
+ throw e;
35
+ throw new Fail(`the ${what} at ${url} could not be read: ${String(e?.message ?? e)}`);
36
+ }
37
+ }
38
+ /** Read the body with a running ceiling, so an oversized response is dropped WHILE it arrives. */
39
+ async function readBounded(response, url, what, Fail, maxBytes) {
40
+ // `Content-Length` is the cheap early exit; a server may omit or misstate it, so it is a hint only
41
+ // and the real ceiling is enforced below while reading.
42
+ const declared = Number(response.headers?.get?.("content-length") ?? NaN);
43
+ if (Number.isFinite(declared) && declared > maxBytes) {
44
+ throw new Fail(`the ${what} at ${url} is ${declared} bytes; the limit is ${maxBytes}`);
45
+ }
46
+ const body = response.body;
47
+ if (!body?.getReader) {
48
+ // No streaming body (an older runtime, or a stubbed response): fall back to reading it whole and
49
+ // checking afterwards. Weaker, but never worse than not checking at all.
50
+ const whole = new Uint8Array(await response.arrayBuffer());
51
+ if (whole.length > maxBytes) {
52
+ throw new Fail(`the ${what} at ${url} is larger than the ${maxBytes} byte limit`);
53
+ }
54
+ return whole;
55
+ }
56
+ const reader = body.getReader();
57
+ const chunks = [];
58
+ let total = 0;
59
+ for (;;) {
60
+ const { done, value } = await reader.read();
61
+ if (done)
62
+ break;
63
+ total += value.length;
64
+ if (total > maxBytes) {
65
+ await reader.cancel();
66
+ throw new Fail(`the ${what} at ${url} is larger than the ${maxBytes} byte limit`);
67
+ }
68
+ chunks.push(value);
69
+ }
70
+ if (chunks.length === 1)
71
+ return chunks[0];
72
+ const out = new Uint8Array(total);
73
+ let at = 0;
74
+ for (const c of chunks) {
75
+ out.set(c, at);
76
+ at += c.length;
77
+ }
78
+ return out;
79
+ }
@@ -1,7 +1,7 @@
1
1
  import { PDFElement, LayoutContext } from "../elements/pdf-element.js";
2
2
  import { BoxConstraints, Offset, Size } from "../layout/box-constraints.js";
3
- /** Distribution of the children ALONG the stacking (main) axis when there is leftover
4
- * space and no flex child to absorb it. */
3
+ /** `layout` places every child (one `calculateLayout` each); `measure` only sizes, with no side effects. */
4
+ export type FlexMode = "layout" | "measure";
5
5
  export type { MainAlign, CrossAlign } from "../layout/alignment.js";
6
6
  import type { CrossAlign, MainAlign } from "../layout/alignment.js";
7
7
  /**
@@ -29,9 +29,10 @@ export interface FlexAxis {
29
29
  * unbounded main axis and shrink-wraps as before.
30
30
  */
31
31
  measureConstraints(crossAvail: number, mainMax?: number): BoxConstraints;
32
- /** Constraints for a flex child (fills the cross axis like a stretched child). */
32
+ /** Constraints for a flex child: its main extent is SET (it fills its share, whatever size it asked
33
+ * for), across it is an ordinary child. */
33
34
  flexConstraints(mainExtent: number, crossAvail: number): BoxConstraints;
34
- /** Absolute offset for a child at main position `mainPos`, cross position `crossPos`. */
35
+ /** Offset (from the stack's top-left) of a child at main position `mainPos`, cross `crossPos`. */
35
36
  offsetAt(mainPos: number, crossPos: number): Offset;
36
37
  }
37
38
  export declare const VERTICAL_AXIS: FlexAxis;
@@ -48,15 +49,6 @@ export interface FlexOptions {
48
49
  alignContent?: MainAlign;
49
50
  }
50
51
  export declare class FlexLayoutHelper {
51
- /**
52
- * Lays out a flex line along `axis`, IN SOURCE ORDER, and places every child.
53
- * Fixed children take their natural main extent; flex (`ExpandedElement`) children
54
- * split the leftover main space by their `flex`. `gap` is inserted between children.
55
- * `main` distributes any leftover when there is no flex child; `cross` positions/sizes
56
- * each child across the line. Returns the total main extent consumed and the cross
57
- * extent occupied. Vertical with `gap 0`, `main start`, `cross stretch` reproduces the
58
- * previous Column layout exactly.
59
- */
60
52
  /**
61
53
  * Lays out the children along `axis`, on one line or several.
62
54
  *
@@ -64,9 +56,10 @@ export declare class FlexLayoutHelper {
64
56
  * straight through, which is what keeps every existing document byte-identical. With `wrap` the
65
57
  * children are split into lines first and the SAME engine runs once per line.
66
58
  */
67
- static layout(children: PDFElement[], axis: FlexAxis, mainAvail: number, crossAvail: number, mainStart: number, crossOrigin: number, options: FlexOptions, ctx: LayoutContext): {
59
+ static layout(children: PDFElement[], axis: FlexAxis, mainAvail: number, crossAvail: number, options: FlexOptions, ctx: LayoutContext, mode?: FlexMode): {
68
60
  mainUsed: number;
69
61
  crossUsed: number;
62
+ widestLine?: number;
70
63
  };
71
64
  /**
72
65
  * The wrapping path: split into lines, lay each one out, then place the lines across the axis.
@@ -76,5 +69,14 @@ export declare class FlexLayoutHelper {
76
69
  * `alignContent` needs: the block of lines can only be centred once its total is known.
77
70
  */
78
71
  private static layoutWrapped;
72
+ /**
73
+ * Lays out ONE flex line along `axis` and places every child. Fixed children take their natural main
74
+ * extent; flex children split the leftover by their `flex`; `gap` goes between children. `main`
75
+ * distributes leftover when nothing flexes; `cross` positions or stretches each child across the line.
76
+ * Returns the main extent consumed and the cross extent occupied.
77
+ *
78
+ * The steps run in this order, and the order is part of the result: `leftover` is taken after the
79
+ * shrink and before the share, and the main-axis spacing reads exactly that value.
80
+ */
79
81
  private static layoutLine;
80
82
  }