@stll/folio-core 0.32.1 → 0.33.0

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 (222) hide show
  1. package/dist/ai-edits/__fixtures__/paragraphs.d.ts +17 -0
  2. package/dist/ai-edits/__fixtures__/paragraphs.js +33 -0
  3. package/dist/ai-edits/apply.d.ts +49 -3
  4. package/dist/ai-edits/apply.js +692 -75
  5. package/dist/ai-edits/blockRange.d.ts +7 -3
  6. package/dist/ai-edits/blockRange.js +14 -6
  7. package/dist/ai-edits/headless.d.ts +39 -5
  8. package/dist/ai-edits/headless.js +76 -17
  9. package/dist/ai-edits/index.d.ts +6 -6
  10. package/dist/ai-edits/index.js +3 -3
  11. package/dist/ai-edits/read.d.ts +4 -0
  12. package/dist/ai-edits/read.js +30 -6
  13. package/dist/ai-edits/scoped-reading.js +4 -3
  14. package/dist/ai-edits/snapshot.d.ts +42 -2
  15. package/dist/ai-edits/snapshot.js +127 -62
  16. package/dist/ai-edits/table-row-column-mutations.d.ts +24 -1
  17. package/dist/ai-edits/table-row-column-mutations.js +98 -6
  18. package/dist/ai-edits/table-targets.d.ts +12 -3
  19. package/dist/ai-edits/table-targets.js +15 -5
  20. package/dist/ai-edits/types.d.ts +235 -7
  21. package/dist/ai-edits/word-diff.d.ts +54 -11
  22. package/dist/ai-edits/word-diff.js +221 -61
  23. package/dist/compare/__fixtures__/body-sequence.d.ts +33 -0
  24. package/dist/compare/__fixtures__/body-sequence.js +67 -0
  25. package/dist/compare/__fixtures__/nested-table.d.ts +16 -0
  26. package/dist/compare/__fixtures__/nested-table.js +50 -0
  27. package/dist/compare/__fixtures__/numbered-list.d.ts +34 -0
  28. package/dist/compare/__fixtures__/numbered-list.js +85 -0
  29. package/dist/compare/column-alignment.d.ts +27 -0
  30. package/dist/compare/column-alignment.js +162 -0
  31. package/dist/compare/compare.d.ts +107 -0
  32. package/dist/compare/compare.js +406 -0
  33. package/dist/compare/formatting.d.ts +29 -0
  34. package/dist/compare/formatting.js +84 -0
  35. package/dist/compare/plan.d.ts +22 -0
  36. package/dist/compare/plan.js +1059 -0
  37. package/dist/compare/reproducible-package.d.ts +17 -0
  38. package/dist/compare/reproducible-package.js +30 -0
  39. package/dist/compare/scenario.d.ts +77 -0
  40. package/dist/compare/scenario.js +263 -0
  41. package/dist/compare/types.d.ts +282 -0
  42. package/dist/compare/types.js +32 -0
  43. package/dist/compare/verification.d.ts +55 -0
  44. package/dist/compare/verification.js +146 -0
  45. package/dist/compat/eigenpal.d.ts +9 -6
  46. package/dist/compat/eigenpal.js +6 -3
  47. package/dist/controller/layoutPipeline.d.ts +13 -0
  48. package/dist/controller/layoutPipeline.js +15 -1
  49. package/dist/display-list/build/buildContext.d.ts +39 -0
  50. package/dist/display-list/build/buildContext.js +31 -0
  51. package/dist/display-list/build/buildDisplayList.d.ts +79 -0
  52. package/dist/display-list/build/buildDisplayList.js +283 -0
  53. package/dist/display-list/build/colors.d.ts +20 -0
  54. package/dist/display-list/build/colors.js +136 -0
  55. package/dist/display-list/build/floatingImages.d.ts +24 -0
  56. package/dist/display-list/build/floatingImages.js +41 -0
  57. package/dist/display-list/build/fontTable.d.ts +31 -0
  58. package/dist/display-list/build/fontTable.js +97 -0
  59. package/dist/display-list/build/furniture.d.ts +86 -0
  60. package/dist/display-list/build/furniture.js +98 -0
  61. package/dist/display-list/build/glyphs.d.ts +55 -0
  62. package/dist/display-list/build/glyphs.js +103 -0
  63. package/dist/display-list/build/headerFooterPrimitives.d.ts +17 -0
  64. package/dist/display-list/build/headerFooterPrimitives.js +40 -0
  65. package/dist/display-list/build/imagePrimitives.d.ts +35 -0
  66. package/dist/display-list/build/imagePrimitives.js +264 -0
  67. package/dist/display-list/build/pageBorderPrimitives.d.ts +23 -0
  68. package/dist/display-list/build/pageBorderPrimitives.js +111 -0
  69. package/dist/display-list/build/pageFurniture.d.ts +35 -0
  70. package/dist/display-list/build/pageFurniture.js +141 -0
  71. package/dist/display-list/build/paragraphPrimitives.d.ts +19 -0
  72. package/dist/display-list/build/paragraphPrimitives.js +819 -0
  73. package/dist/display-list/build/regions.d.ts +46 -0
  74. package/dist/display-list/build/regions.js +56 -0
  75. package/dist/display-list/build/storyPrimitives.d.ts +29 -0
  76. package/dist/display-list/build/storyPrimitives.js +279 -0
  77. package/dist/display-list/build/strokes.d.ts +19 -0
  78. package/dist/display-list/build/strokes.js +78 -0
  79. package/dist/display-list/build/tablePrimitives.d.ts +38 -0
  80. package/dist/display-list/build/tablePrimitives.js +409 -0
  81. package/dist/display-list/build/textBoxPrimitives.d.ts +14 -0
  82. package/dist/display-list/build/textBoxPrimitives.js +129 -0
  83. package/dist/display-list/build/textDecorations.d.ts +29 -0
  84. package/dist/display-list/build/textDecorations.js +29 -0
  85. package/dist/display-list/build/unsupported.d.ts +57 -0
  86. package/dist/display-list/build/unsupported.js +0 -0
  87. package/dist/display-list/build/watermarkPrimitives.d.ts +15 -0
  88. package/dist/display-list/build/watermarkPrimitives.js +128 -0
  89. package/dist/display-list/dom/renderDisplayListToDom.d.ts +26 -0
  90. package/dist/display-list/dom/renderDisplayListToDom.js +620 -0
  91. package/dist/display-list/editor/displayListPagePainter.d.ts +22 -0
  92. package/dist/display-list/editor/displayListPagePainter.js +63 -0
  93. package/dist/display-list/editor/pageRenderer.d.ts +24 -0
  94. package/dist/display-list/editor/pageRenderer.js +36 -0
  95. package/dist/display-list/primitives.d.ts +95 -0
  96. package/dist/display-list/primitives.js +127 -0
  97. package/dist/display-list/types.d.ts +472 -0
  98. package/dist/display-list/types.js +0 -0
  99. package/dist/document-operations.d.ts +42 -9
  100. package/dist/document-operations.js +171 -16
  101. package/dist/document-stories.d.ts +16 -0
  102. package/dist/document-stories.js +48 -4
  103. package/dist/docx/packageParts.d.ts +35 -0
  104. package/dist/docx/packageParts.js +120 -0
  105. package/dist/docx/paragraphParser.js +22 -22
  106. package/dist/docx/paragraphTraversal.js +1 -1
  107. package/dist/docx/renderedPageBreakNormalization.js +3 -1
  108. package/dist/docx/revisionIdNormalization.d.ts +13 -0
  109. package/dist/docx/revisionIdNormalization.js +103 -0
  110. package/dist/docx/rezip.js +26 -17
  111. package/dist/docx/selectiveSave.js +2 -2
  112. package/dist/docx/serializer/commentSerializer.js +6 -1
  113. package/dist/docx/serializer/paragraphSerializer.js +33 -4
  114. package/dist/docx/serializer/runSerializer.js +11 -10
  115. package/dist/docx/serializer/tableSerializer.js +2 -1
  116. package/dist/docx/server/applyDocxXmlPatchProposal.js +1 -1
  117. package/dist/docx/server/createBilingualDocx.js +3 -2
  118. package/dist/docx/streamingXmlParser.d.ts +13 -1
  119. package/dist/docx/streamingXmlParser.js +49 -11
  120. package/dist/docx/unzip.d.ts +1 -2
  121. package/dist/docx/unzip.js +25 -6
  122. package/dist/docx/xmlParser.d.ts +3 -1
  123. package/dist/docx/xmlParser.js +33 -27
  124. package/dist/export-pdf.d.ts +43 -0
  125. package/dist/export-pdf.js +98 -0
  126. package/dist/fonts/headlessMeasure.d.ts +76 -0
  127. package/dist/fonts/headlessMeasure.js +0 -0
  128. package/dist/fonts/sfnt/parse.d.ts +51 -0
  129. package/dist/fonts/sfnt/parse.js +525 -0
  130. package/dist/fonts/sfnt/subset.d.ts +20 -0
  131. package/dist/fonts/sfnt/subset.js +350 -0
  132. package/dist/fonts/sfnt/tables.d.ts +88 -0
  133. package/dist/fonts/sfnt/tables.js +131 -0
  134. package/dist/fonts/sfnt/woff.d.ts +14 -0
  135. package/dist/fonts/sfnt/woff.js +163 -0
  136. package/dist/generated/text_shaper.js +324 -0
  137. package/dist/generated/text_shaper_bg.wasm +0 -0
  138. package/dist/headless-layout.d.ts +75 -0
  139. package/dist/headless-layout.js +350 -0
  140. package/dist/index.d.ts +9 -6
  141. package/dist/index.js +6 -3
  142. package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
  143. package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
  144. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
  145. package/dist/layout-bridge/convert/headerFooterLayout.js +2 -0
  146. package/dist/layout-bridge/convert/toFlowBlocks.d.ts +3 -1
  147. package/dist/layout-bridge/convert/toFlowBlocks.js +2 -0
  148. package/dist/layout-engine/measure/advanceComposition.d.ts +62 -0
  149. package/dist/layout-engine/measure/advanceComposition.js +148 -0
  150. package/dist/layout-engine/measure/cache.d.ts +2 -0
  151. package/dist/layout-engine/measure/measureContainer.js +13 -0
  152. package/dist/layout-engine/measure/measureParagraph.js +1 -2
  153. package/dist/layout-engine/measure/measureTypes.d.ts +12 -0
  154. package/dist/layout-engine/measure/tableInlinePlacement.d.ts +3 -2
  155. package/dist/layout-engine/measure/tableInlinePlacement.js +14 -1
  156. package/dist/layout-engine/tableIndentCompatibility.d.ts +5 -0
  157. package/dist/layout-engine/tableIndentCompatibility.js +15 -0
  158. package/dist/layout-engine/types.d.ts +8 -0
  159. package/dist/layout-painter/renderPage.d.ts +29 -1
  160. package/dist/layout-painter/renderPage.js +47 -19
  161. package/dist/layout-painter/renderParagraph.d.ts +21 -2
  162. package/dist/layout-painter/renderParagraph.js +1 -1
  163. package/dist/layout-painter/renderTable.js +37 -4
  164. package/dist/managers/editorShortcuts.d.ts +33 -1
  165. package/dist/managers/editorShortcuts.js +20 -1
  166. package/dist/markdown/renderRuns.js +7 -2
  167. package/dist/markdown/renderTable.js +5 -1
  168. package/dist/model.d.ts +3 -3
  169. package/dist/model.js +2 -2
  170. package/dist/pdf/contentStream.d.ts +73 -0
  171. package/dist/pdf/contentStream.js +143 -0
  172. package/dist/pdf/fonts.d.ts +96 -0
  173. package/dist/pdf/fonts.js +593 -0
  174. package/dist/pdf/images.d.ts +28 -0
  175. package/dist/pdf/images.js +439 -0
  176. package/dist/pdf/objects.d.ts +93 -0
  177. package/dist/pdf/objects.js +237 -0
  178. package/dist/pdf/pageSpace.d.ts +43 -0
  179. package/dist/pdf/pageSpace.js +57 -0
  180. package/dist/pdf/paint.d.ts +32 -0
  181. package/dist/pdf/paint.js +442 -0
  182. package/dist/pdf/writePdf.d.ts +56 -0
  183. package/dist/pdf/writePdf.js +345 -0
  184. package/dist/prosemirror/commands/comments.js +91 -6
  185. package/dist/prosemirror/conversion/fromProseDoc.js +41 -7
  186. package/dist/prosemirror/conversion/toProseDoc.js +41 -4
  187. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +15 -2
  188. package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +35 -6
  189. package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +2 -2
  190. package/dist/prosemirror/extensions/features/pasteCleanup.js +4 -3
  191. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.d.ts +3 -1
  192. package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +5 -3
  193. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -1
  194. package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +9 -3
  195. package/dist/prosemirror/extensions/nodes/TableExtension.js +56 -39
  196. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +3 -1
  197. package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +5 -3
  198. package/dist/prosemirror/plugins/suggestionMode.js +1 -0
  199. package/dist/prosemirror/revisionCarriers.js +19 -2
  200. package/dist/prosemirror/utils/extractTrackedChanges.d.ts +2 -2
  201. package/dist/prosemirror/utils/extractTrackedChanges.js +59 -23
  202. package/dist/prosemirror/validation.js +55 -27
  203. package/dist/prosemirror/zeroWidthAnchors.d.ts +10 -0
  204. package/dist/prosemirror/zeroWidthAnchors.js +23 -0
  205. package/dist/redline.js +12 -79
  206. package/dist/server.d.ts +4 -4
  207. package/dist/server.js +2 -2
  208. package/dist/shaping/placeRun.d.ts +51 -0
  209. package/dist/shaping/placeRun.js +84 -0
  210. package/dist/shaping/shaper.d.ts +92 -0
  211. package/dist/shaping/shaper.js +102 -0
  212. package/dist/types/block-id.d.ts +18 -2
  213. package/dist/types/block-id.js +49 -5
  214. package/dist/types/content.d.ts +2 -2
  215. package/dist/utils/clipboard.js +4 -3
  216. package/dist/utils/fontResolver.js +183 -0
  217. package/dist/utils/htmlComments.d.ts +17 -0
  218. package/dist/utils/htmlComments.js +22 -0
  219. package/dist/utils/scriptSegments.d.ts +12 -1
  220. package/dist/utils/scriptSegments.js +17 -1
  221. package/dist/version-comparison.d.ts +1 -1
  222. package/package.json +7 -3
@@ -0,0 +1,237 @@
1
+ import { panic } from "better-result";
2
+ import { deflateSync } from "node:zlib";
3
+ //#region src/pdf/objects.ts
4
+ /**
5
+ * The PDF object model: values, serialization, the cross-reference table and
6
+ * the trailer.
7
+ *
8
+ * Everything a backend writes into a PDF file passes through here, which is
9
+ * what makes byte-for-byte determinism enforceable rather than aspirational:
10
+ * numbers go through one formatter, dictionaries carry their entries as an
11
+ * ordered list (never an object or a `Map` whose iteration order depends on
12
+ * how it was built), and object numbers come from one allocator in call
13
+ * order.
14
+ */
15
+ /**
16
+ * PDF reals carry roughly five significant digits; four decimals on a
17
+ * coordinate in points is a third of a micron, far below anything a
18
+ * rasterizer resolves. Fixing the precision here is what makes two runs that
19
+ * computed the same coordinate by different arithmetic routes emit the same
20
+ * bytes.
21
+ */
22
+ const NUMBER_DECIMALS = 4;
23
+ /**
24
+ * No page coordinate, font metric or matrix entry folio produces comes near
25
+ * this. Past it `toFixed` switches to exponent notation, which PDF readers do
26
+ * not accept, so a value this large is a producer bug and not something to
27
+ * paint approximately.
28
+ */
29
+ const MAX_ABS_NUMBER = 1e10;
30
+ /** The one number formatter. Every number in the file is emitted through it. */
31
+ const formatNumber = (value) => {
32
+ if (!Number.isFinite(value)) panic(`PDF numbers must be finite, got ${String(value)}`);
33
+ if (Math.abs(value) > MAX_ABS_NUMBER) panic(`PDF number out of range: ${String(value)}`);
34
+ const trimmed = value.toFixed(NUMBER_DECIMALS).replace(/0+$/u, "").replace(/\.$/u, "");
35
+ return trimmed === "" || trimmed === "-" || trimmed === "-0" ? "0" : trimmed;
36
+ };
37
+ const pdfNull = { kind: "null" };
38
+ const pdfBool = (value) => ({
39
+ kind: "bool",
40
+ value
41
+ });
42
+ const pdfNumber = (value) => ({
43
+ kind: "number",
44
+ value
45
+ });
46
+ const pdfName = (value) => ({
47
+ kind: "name",
48
+ value
49
+ });
50
+ const pdfAsciiString = (value) => ({
51
+ kind: "asciiString",
52
+ value
53
+ });
54
+ const pdfTextString = (value) => ({
55
+ kind: "textString",
56
+ value
57
+ });
58
+ const pdfHexString = (value) => ({
59
+ kind: "hexString",
60
+ value
61
+ });
62
+ const pdfArray = (items) => ({
63
+ kind: "array",
64
+ items
65
+ });
66
+ const pdfNumberArray = (values) => pdfArray(values.map(pdfNumber));
67
+ /**
68
+ * Entries keep the order they are written in, and an `undefined` value drops
69
+ * the key entirely: an optional entry is either present with a value or
70
+ * absent, never `/Key null`.
71
+ */
72
+ const pdfDict = (entries) => ({
73
+ kind: "dict",
74
+ entries: entries.filter((entry) => entry[1] !== void 0)
75
+ });
76
+ const pdfStream = (dict, data) => ({
77
+ kind: "stream",
78
+ dict,
79
+ data
80
+ });
81
+ /**
82
+ * zlib's output depends on the compression level, so it is fixed rather than
83
+ * left to the default: two runs of the writer must agree byte for byte, and
84
+ * "whatever zlib felt like" is not an agreement.
85
+ */
86
+ const DEFLATE_LEVEL = 9;
87
+ const deflateForPdf = (data) => {
88
+ const deflated = deflateSync(data, { level: DEFLATE_LEVEL });
89
+ return new Uint8Array(deflated.buffer, deflated.byteOffset, deflated.byteLength);
90
+ };
91
+ /** A `/FlateDecode` stream: the one place raw bytes become a compressed one. */
92
+ const pdfFlateStream = (entries, data) => pdfStream(pdfDict([...entries, ["Filter", pdfName("FlateDecode")]]), deflateForPdf(data));
93
+ /** Regular characters may appear in a name unescaped; everything else is `#xx`. */
94
+ const NAME_REGULAR = /^[A-Za-z0-9._\-+]$/u;
95
+ const encodeName = (value) => {
96
+ let out = "/";
97
+ for (const char of value) out += NAME_REGULAR.test(char) ? char : `#${char.charCodeAt(0).toString(16).padStart(2, "0").toUpperCase()}`;
98
+ return out;
99
+ };
100
+ const encodeAsciiString = (value) => {
101
+ let out = "(";
102
+ for (let index = 0; index < value.length; index += 1) {
103
+ const code = value.charCodeAt(index);
104
+ if (code > 255) panic(`byte string carries a code point above 0xFF: ${value}`);
105
+ if (code === 40 || code === 41 || code === 92) out += `\\${String.fromCharCode(code)}`;
106
+ else if (code < 32 || code > 126) out += `\\${code.toString(8).padStart(3, "0")}`;
107
+ else out += String.fromCharCode(code);
108
+ }
109
+ return `${out})`;
110
+ };
111
+ const encodeTextString = (value) => {
112
+ let hex = "FEFF";
113
+ for (let index = 0; index < value.length; index += 1) hex += value.charCodeAt(index).toString(16).padStart(4, "0").toUpperCase();
114
+ return `<${hex}>`;
115
+ };
116
+ const createByteSink = () => {
117
+ const chunks = [];
118
+ let length = 0;
119
+ const writeBytes = (bytes) => {
120
+ chunks.push(bytes);
121
+ length += bytes.length;
122
+ };
123
+ return {
124
+ writeAscii: (text) => {
125
+ const bytes = new Uint8Array(text.length);
126
+ for (let index = 0; index < text.length; index += 1) bytes[index] = text.charCodeAt(index) & 255;
127
+ writeBytes(bytes);
128
+ },
129
+ writeBytes,
130
+ offset: () => length,
131
+ toBytes: () => {
132
+ const out = new Uint8Array(length);
133
+ let cursor = 0;
134
+ for (const chunk of chunks) {
135
+ out.set(chunk, cursor);
136
+ cursor += chunk.length;
137
+ }
138
+ return out;
139
+ }
140
+ };
141
+ };
142
+ /** Serializes everything except a stream's payload, which is raw bytes. */
143
+ const serializeValue = (value) => {
144
+ switch (value.kind) {
145
+ case "null": return "null";
146
+ case "bool": return value.value ? "true" : "false";
147
+ case "number": return formatNumber(value.value);
148
+ case "name": return encodeName(value.value);
149
+ case "asciiString": return encodeAsciiString(value.value);
150
+ case "textString": return encodeTextString(value.value);
151
+ case "hexString": return `<${value.value}>`;
152
+ case "array": return `[${value.items.map(serializeValue).join(" ")}]`;
153
+ case "dict": return `<<${value.entries.map(([key, entry]) => `${encodeName(key)} ${serializeValue(entry)}`).join(" ")}>>`;
154
+ case "stream": return panic("a stream must be an indirect object, not a nested value");
155
+ case "ref": return `${String(value.id)} 0 R`;
156
+ default: return panic(`unhandled PDF value: ${JSON.stringify(value)}`);
157
+ }
158
+ };
159
+ const PDF_HEADER = "%PDF-1.7\n";
160
+ /**
161
+ * A comment whose bytes are all above 127 tells any tool that transfers the
162
+ * file that it is binary. The four bytes are the conventional ones.
163
+ */
164
+ const BINARY_MARKER = new Uint8Array([
165
+ 37,
166
+ 226,
167
+ 227,
168
+ 207,
169
+ 211,
170
+ 10
171
+ ]);
172
+ const XREF_FREE_ENTRY = "0000000000 65535 f \n";
173
+ const createPdfDocument = () => {
174
+ const values = /* @__PURE__ */ new Map();
175
+ let nextId = 1;
176
+ const allocate = () => {
177
+ const ref = {
178
+ kind: "ref",
179
+ id: nextId
180
+ };
181
+ nextId += 1;
182
+ return ref;
183
+ };
184
+ const define = (ref, value) => {
185
+ if (values.has(ref.id)) panic(`PDF object ${String(ref.id)} defined twice`);
186
+ values.set(ref.id, value);
187
+ };
188
+ const writeObject = (sink, id, value) => {
189
+ sink.writeAscii(`${String(id)} 0 obj\n`);
190
+ if (value.kind === "stream") {
191
+ const withLength = pdfDict([...value.dict.entries, ["Length", pdfNumber(value.data.length)]]);
192
+ sink.writeAscii(`${serializeValue(withLength)}\nstream\n`);
193
+ sink.writeBytes(value.data);
194
+ sink.writeAscii("\nendstream");
195
+ } else sink.writeAscii(serializeValue(value));
196
+ sink.writeAscii("\nendobj\n");
197
+ };
198
+ return {
199
+ allocate,
200
+ define,
201
+ add: (value) => {
202
+ const ref = allocate();
203
+ define(ref, value);
204
+ return ref;
205
+ },
206
+ serialize: ({ rootRef, infoRef, idHex }) => {
207
+ const sink = createByteSink();
208
+ sink.writeAscii(PDF_HEADER);
209
+ sink.writeBytes(BINARY_MARKER);
210
+ const offsets = /* @__PURE__ */ new Map();
211
+ for (let id = 1; id < nextId; id += 1) {
212
+ const value = values.get(id);
213
+ if (value === void 0) panic(`PDF object ${String(id)} was allocated but never defined`);
214
+ offsets.set(id, sink.offset());
215
+ writeObject(sink, id, value);
216
+ }
217
+ const xrefOffset = sink.offset();
218
+ const digest = idHex(sink.toBytes());
219
+ sink.writeAscii(`xref\n0 ${String(nextId)}\n`);
220
+ sink.writeAscii(XREF_FREE_ENTRY);
221
+ for (let id = 1; id < nextId; id += 1) {
222
+ const offset = offsets.get(id) ?? panic(`missing offset for PDF object ${String(id)}`);
223
+ sink.writeAscii(`${String(offset).padStart(10, "0")} 00000 n \n`);
224
+ }
225
+ const trailer = pdfDict([
226
+ ["Size", pdfNumber(nextId)],
227
+ ["Root", rootRef],
228
+ ["Info", infoRef],
229
+ ["ID", pdfArray([pdfHexString(digest), pdfHexString(digest)])]
230
+ ]);
231
+ sink.writeAscii(`trailer\n${serializeValue(trailer)}\nstartxref\n${String(xrefOffset)}\n%%EOF\n`);
232
+ return sink.toBytes();
233
+ }
234
+ };
235
+ };
236
+ //#endregion
237
+ export { createPdfDocument, deflateForPdf, formatNumber, pdfArray, pdfAsciiString, pdfBool, pdfDict, pdfFlateStream, pdfHexString, pdfName, pdfNull, pdfNumber, pdfNumberArray, pdfStream, pdfTextString };
@@ -0,0 +1,43 @@
1
+ //#region src/pdf/pageSpace.d.ts
2
+ /**
3
+ * The single conversion between display-list space and PDF space.
4
+ *
5
+ * The display list is CSS px at 96 dpi with the origin at the page's top-left
6
+ * and y growing down. PDF user space is points (72/inch) with the origin at
7
+ * the bottom-left and y growing up. Both the scale and the flip happen here
8
+ * and nowhere else: a second place that flips y is a second opinion about
9
+ * where the top of the page is, and the two will eventually disagree.
10
+ *
11
+ * Everything downstream, content streams included, is written in
12
+ * display-list coordinates. The page's base CTM ({@link basePageMatrix}) does
13
+ * the conversion for painted geometry; annotations and destinations, which
14
+ * are not affected by a content stream's CTM, run the same matrix themselves
15
+ * through {@link displayPointToPdf}.
16
+ */
17
+ /** 72 points per inch over 96 CSS px per inch. */
18
+ declare const POINTS_PER_PIXEL = 0.75;
19
+ /** `[a b c d e f]` as PDF writes a matrix. */
20
+ type PdfMatrix = readonly [number, number, number, number, number, number];
21
+ declare const pxToPt: (px: number) => number;
22
+ /**
23
+ * Scales px to points and flips y about the page's height, so a primitive
24
+ * emitted at display-list y 0 paints at the top of the page.
25
+ */
26
+ declare const basePageMatrix: (pageHeightPx: number) => PdfMatrix;
27
+ declare const applyMatrix: (matrix: PdfMatrix, x: number, y: number) => {
28
+ readonly x: number;
29
+ readonly y: number;
30
+ };
31
+ /** A display-list page point in PDF default user space. */
32
+ declare const displayPointToPdf: (pageHeightPx: number, xPx: number, yPx: number) => {
33
+ readonly x: number;
34
+ readonly y: number;
35
+ };
36
+ /**
37
+ * Rotation about a point, expressed in display-list space. The space is
38
+ * y-down, so the usual `[cos sin -sin cos]` reads as a clockwise rotation on
39
+ * screen, which is what a positive CSS/OOXML angle means.
40
+ */
41
+ declare const rotationMatrix: (degrees: number, originXPx: number, originYPx: number) => PdfMatrix;
42
+ //#endregion
43
+ export { POINTS_PER_PIXEL, PdfMatrix, applyMatrix, basePageMatrix, displayPointToPdf, pxToPt, rotationMatrix };
@@ -0,0 +1,57 @@
1
+ //#region src/pdf/pageSpace.ts
2
+ /**
3
+ * The single conversion between display-list space and PDF space.
4
+ *
5
+ * The display list is CSS px at 96 dpi with the origin at the page's top-left
6
+ * and y growing down. PDF user space is points (72/inch) with the origin at
7
+ * the bottom-left and y growing up. Both the scale and the flip happen here
8
+ * and nowhere else: a second place that flips y is a second opinion about
9
+ * where the top of the page is, and the two will eventually disagree.
10
+ *
11
+ * Everything downstream, content streams included, is written in
12
+ * display-list coordinates. The page's base CTM ({@link basePageMatrix}) does
13
+ * the conversion for painted geometry; annotations and destinations, which
14
+ * are not affected by a content stream's CTM, run the same matrix themselves
15
+ * through {@link displayPointToPdf}.
16
+ */
17
+ /** 72 points per inch over 96 CSS px per inch. */
18
+ const POINTS_PER_PIXEL = .75;
19
+ const pxToPt = (px) => px * POINTS_PER_PIXEL;
20
+ /**
21
+ * Scales px to points and flips y about the page's height, so a primitive
22
+ * emitted at display-list y 0 paints at the top of the page.
23
+ */
24
+ const basePageMatrix = (pageHeightPx) => [
25
+ POINTS_PER_PIXEL,
26
+ 0,
27
+ 0,
28
+ -.75,
29
+ 0,
30
+ pxToPt(pageHeightPx)
31
+ ];
32
+ const applyMatrix = (matrix, x, y) => ({
33
+ x: matrix[0] * x + matrix[2] * y + matrix[4],
34
+ y: matrix[1] * x + matrix[3] * y + matrix[5]
35
+ });
36
+ /** A display-list page point in PDF default user space. */
37
+ const displayPointToPdf = (pageHeightPx, xPx, yPx) => applyMatrix(basePageMatrix(pageHeightPx), xPx, yPx);
38
+ /**
39
+ * Rotation about a point, expressed in display-list space. The space is
40
+ * y-down, so the usual `[cos sin -sin cos]` reads as a clockwise rotation on
41
+ * screen, which is what a positive CSS/OOXML angle means.
42
+ */
43
+ const rotationMatrix = (degrees, originXPx, originYPx) => {
44
+ const radians = degrees * Math.PI / 180;
45
+ const cos = Math.cos(radians);
46
+ const sin = Math.sin(radians);
47
+ return [
48
+ cos,
49
+ sin,
50
+ -sin,
51
+ cos,
52
+ originXPx - originXPx * cos + originYPx * sin,
53
+ originYPx - originXPx * sin - originYPx * cos
54
+ ];
55
+ };
56
+ //#endregion
57
+ export { POINTS_PER_PIXEL, applyMatrix, basePageMatrix, displayPointToPdf, pxToPt, rotationMatrix };
@@ -0,0 +1,32 @@
1
+ import { DisplayPage } from "../display-list/types.js";
2
+ import { PreparedFont, ShapedRunRequest } from "./fonts.js";
3
+ import { ContentPart } from "./contentStream.js";
4
+ import { Result } from "better-result";
5
+ //#region src/pdf/paint.d.ts
6
+ declare const PaintError_base: import("better-result").TaggedErrorClass<"PaintError">;
7
+ declare class PaintError extends PaintError_base<{
8
+ message: string;
9
+ }> {}
10
+ type PaintPageOptions = {
11
+ readonly page: DisplayPage;
12
+ readonly fonts: ReadonlyMap<number, PreparedFont>;
13
+ };
14
+ declare const paintPage: ({ page, fonts }: PaintPageOptions) => readonly ContentPart[];
15
+ /** What the file must carry, gathered before anything is written. */
16
+ type DisplayUsage = {
17
+ readonly codePointsByFont: ReadonlyMap<number, ReadonlySet<number>>;
18
+ /**
19
+ * Runs whose glyphs shaping has to choose, by font index. Collected here
20
+ * because the subset has to carry those glyphs before the first page is
21
+ * written, and they are not reachable from any code point.
22
+ */
23
+ readonly shapedRunsByFont: ReadonlyMap<number, readonly ShapedRunRequest[]>;
24
+ readonly imageIndices: ReadonlySet<number>;
25
+ };
26
+ /**
27
+ * Walks the pages once before anything is written, because subsetting needs
28
+ * every glyph the document uses before the first one is emitted.
29
+ */
30
+ declare const collectUsage: (pages: readonly DisplayPage[], fontCount: number, imageCount: number) => Result<DisplayUsage, PaintError>;
31
+ //#endregion
32
+ export { DisplayUsage, PaintError, PaintPageOptions, collectUsage, paintPage };