@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.
- package/dist/ai-edits/__fixtures__/paragraphs.d.ts +17 -0
- package/dist/ai-edits/__fixtures__/paragraphs.js +33 -0
- package/dist/ai-edits/apply.d.ts +49 -3
- package/dist/ai-edits/apply.js +692 -75
- package/dist/ai-edits/blockRange.d.ts +7 -3
- package/dist/ai-edits/blockRange.js +14 -6
- package/dist/ai-edits/headless.d.ts +39 -5
- package/dist/ai-edits/headless.js +76 -17
- package/dist/ai-edits/index.d.ts +6 -6
- package/dist/ai-edits/index.js +3 -3
- package/dist/ai-edits/read.d.ts +4 -0
- package/dist/ai-edits/read.js +30 -6
- package/dist/ai-edits/scoped-reading.js +4 -3
- package/dist/ai-edits/snapshot.d.ts +42 -2
- package/dist/ai-edits/snapshot.js +127 -62
- package/dist/ai-edits/table-row-column-mutations.d.ts +24 -1
- package/dist/ai-edits/table-row-column-mutations.js +98 -6
- package/dist/ai-edits/table-targets.d.ts +12 -3
- package/dist/ai-edits/table-targets.js +15 -5
- package/dist/ai-edits/types.d.ts +235 -7
- package/dist/ai-edits/word-diff.d.ts +54 -11
- package/dist/ai-edits/word-diff.js +221 -61
- package/dist/compare/__fixtures__/body-sequence.d.ts +33 -0
- package/dist/compare/__fixtures__/body-sequence.js +67 -0
- package/dist/compare/__fixtures__/nested-table.d.ts +16 -0
- package/dist/compare/__fixtures__/nested-table.js +50 -0
- package/dist/compare/__fixtures__/numbered-list.d.ts +34 -0
- package/dist/compare/__fixtures__/numbered-list.js +85 -0
- package/dist/compare/column-alignment.d.ts +27 -0
- package/dist/compare/column-alignment.js +162 -0
- package/dist/compare/compare.d.ts +107 -0
- package/dist/compare/compare.js +406 -0
- package/dist/compare/formatting.d.ts +29 -0
- package/dist/compare/formatting.js +84 -0
- package/dist/compare/plan.d.ts +22 -0
- package/dist/compare/plan.js +1059 -0
- package/dist/compare/reproducible-package.d.ts +17 -0
- package/dist/compare/reproducible-package.js +30 -0
- package/dist/compare/scenario.d.ts +77 -0
- package/dist/compare/scenario.js +263 -0
- package/dist/compare/types.d.ts +282 -0
- package/dist/compare/types.js +32 -0
- package/dist/compare/verification.d.ts +55 -0
- package/dist/compare/verification.js +146 -0
- package/dist/compat/eigenpal.d.ts +9 -6
- package/dist/compat/eigenpal.js +6 -3
- package/dist/controller/layoutPipeline.d.ts +13 -0
- package/dist/controller/layoutPipeline.js +15 -1
- package/dist/display-list/build/buildContext.d.ts +39 -0
- package/dist/display-list/build/buildContext.js +31 -0
- package/dist/display-list/build/buildDisplayList.d.ts +79 -0
- package/dist/display-list/build/buildDisplayList.js +283 -0
- package/dist/display-list/build/colors.d.ts +20 -0
- package/dist/display-list/build/colors.js +136 -0
- package/dist/display-list/build/floatingImages.d.ts +24 -0
- package/dist/display-list/build/floatingImages.js +41 -0
- package/dist/display-list/build/fontTable.d.ts +31 -0
- package/dist/display-list/build/fontTable.js +97 -0
- package/dist/display-list/build/furniture.d.ts +86 -0
- package/dist/display-list/build/furniture.js +98 -0
- package/dist/display-list/build/glyphs.d.ts +55 -0
- package/dist/display-list/build/glyphs.js +103 -0
- package/dist/display-list/build/headerFooterPrimitives.d.ts +17 -0
- package/dist/display-list/build/headerFooterPrimitives.js +40 -0
- package/dist/display-list/build/imagePrimitives.d.ts +35 -0
- package/dist/display-list/build/imagePrimitives.js +264 -0
- package/dist/display-list/build/pageBorderPrimitives.d.ts +23 -0
- package/dist/display-list/build/pageBorderPrimitives.js +111 -0
- package/dist/display-list/build/pageFurniture.d.ts +35 -0
- package/dist/display-list/build/pageFurniture.js +141 -0
- package/dist/display-list/build/paragraphPrimitives.d.ts +19 -0
- package/dist/display-list/build/paragraphPrimitives.js +819 -0
- package/dist/display-list/build/regions.d.ts +46 -0
- package/dist/display-list/build/regions.js +56 -0
- package/dist/display-list/build/storyPrimitives.d.ts +29 -0
- package/dist/display-list/build/storyPrimitives.js +279 -0
- package/dist/display-list/build/strokes.d.ts +19 -0
- package/dist/display-list/build/strokes.js +78 -0
- package/dist/display-list/build/tablePrimitives.d.ts +38 -0
- package/dist/display-list/build/tablePrimitives.js +409 -0
- package/dist/display-list/build/textBoxPrimitives.d.ts +14 -0
- package/dist/display-list/build/textBoxPrimitives.js +129 -0
- package/dist/display-list/build/textDecorations.d.ts +29 -0
- package/dist/display-list/build/textDecorations.js +29 -0
- package/dist/display-list/build/unsupported.d.ts +57 -0
- package/dist/display-list/build/unsupported.js +0 -0
- package/dist/display-list/build/watermarkPrimitives.d.ts +15 -0
- package/dist/display-list/build/watermarkPrimitives.js +128 -0
- package/dist/display-list/dom/renderDisplayListToDom.d.ts +26 -0
- package/dist/display-list/dom/renderDisplayListToDom.js +620 -0
- package/dist/display-list/editor/displayListPagePainter.d.ts +22 -0
- package/dist/display-list/editor/displayListPagePainter.js +63 -0
- package/dist/display-list/editor/pageRenderer.d.ts +24 -0
- package/dist/display-list/editor/pageRenderer.js +36 -0
- package/dist/display-list/primitives.d.ts +95 -0
- package/dist/display-list/primitives.js +127 -0
- package/dist/display-list/types.d.ts +472 -0
- package/dist/display-list/types.js +0 -0
- package/dist/document-operations.d.ts +42 -9
- package/dist/document-operations.js +171 -16
- package/dist/document-stories.d.ts +16 -0
- package/dist/document-stories.js +48 -4
- package/dist/docx/packageParts.d.ts +35 -0
- package/dist/docx/packageParts.js +120 -0
- package/dist/docx/paragraphParser.js +22 -22
- package/dist/docx/paragraphTraversal.js +1 -1
- package/dist/docx/renderedPageBreakNormalization.js +3 -1
- package/dist/docx/revisionIdNormalization.d.ts +13 -0
- package/dist/docx/revisionIdNormalization.js +103 -0
- package/dist/docx/rezip.js +26 -17
- package/dist/docx/selectiveSave.js +2 -2
- package/dist/docx/serializer/commentSerializer.js +6 -1
- package/dist/docx/serializer/paragraphSerializer.js +33 -4
- package/dist/docx/serializer/runSerializer.js +11 -10
- package/dist/docx/serializer/tableSerializer.js +2 -1
- package/dist/docx/server/applyDocxXmlPatchProposal.js +1 -1
- package/dist/docx/server/createBilingualDocx.js +3 -2
- package/dist/docx/streamingXmlParser.d.ts +13 -1
- package/dist/docx/streamingXmlParser.js +49 -11
- package/dist/docx/unzip.d.ts +1 -2
- package/dist/docx/unzip.js +25 -6
- package/dist/docx/xmlParser.d.ts +3 -1
- package/dist/docx/xmlParser.js +33 -27
- package/dist/export-pdf.d.ts +43 -0
- package/dist/export-pdf.js +98 -0
- package/dist/fonts/headlessMeasure.d.ts +76 -0
- package/dist/fonts/headlessMeasure.js +0 -0
- package/dist/fonts/sfnt/parse.d.ts +51 -0
- package/dist/fonts/sfnt/parse.js +525 -0
- package/dist/fonts/sfnt/subset.d.ts +20 -0
- package/dist/fonts/sfnt/subset.js +350 -0
- package/dist/fonts/sfnt/tables.d.ts +88 -0
- package/dist/fonts/sfnt/tables.js +131 -0
- package/dist/fonts/sfnt/woff.d.ts +14 -0
- package/dist/fonts/sfnt/woff.js +163 -0
- package/dist/generated/text_shaper.js +324 -0
- package/dist/generated/text_shaper_bg.wasm +0 -0
- package/dist/headless-layout.d.ts +75 -0
- package/dist/headless-layout.js +350 -0
- package/dist/index.d.ts +9 -6
- package/dist/index.js +6 -3
- package/dist/layout-bridge/convert/footnoteLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/footnoteLayout.js +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.d.ts +1 -0
- package/dist/layout-bridge/convert/headerFooterLayout.js +2 -0
- package/dist/layout-bridge/convert/toFlowBlocks.d.ts +3 -1
- package/dist/layout-bridge/convert/toFlowBlocks.js +2 -0
- package/dist/layout-engine/measure/advanceComposition.d.ts +62 -0
- package/dist/layout-engine/measure/advanceComposition.js +148 -0
- package/dist/layout-engine/measure/cache.d.ts +2 -0
- package/dist/layout-engine/measure/measureContainer.js +13 -0
- package/dist/layout-engine/measure/measureParagraph.js +1 -2
- package/dist/layout-engine/measure/measureTypes.d.ts +12 -0
- package/dist/layout-engine/measure/tableInlinePlacement.d.ts +3 -2
- package/dist/layout-engine/measure/tableInlinePlacement.js +14 -1
- package/dist/layout-engine/tableIndentCompatibility.d.ts +5 -0
- package/dist/layout-engine/tableIndentCompatibility.js +15 -0
- package/dist/layout-engine/types.d.ts +8 -0
- package/dist/layout-painter/renderPage.d.ts +29 -1
- package/dist/layout-painter/renderPage.js +47 -19
- package/dist/layout-painter/renderParagraph.d.ts +21 -2
- package/dist/layout-painter/renderParagraph.js +1 -1
- package/dist/layout-painter/renderTable.js +37 -4
- package/dist/managers/editorShortcuts.d.ts +33 -1
- package/dist/managers/editorShortcuts.js +20 -1
- package/dist/markdown/renderRuns.js +7 -2
- package/dist/markdown/renderTable.js +5 -1
- package/dist/model.d.ts +3 -3
- package/dist/model.js +2 -2
- package/dist/pdf/contentStream.d.ts +73 -0
- package/dist/pdf/contentStream.js +143 -0
- package/dist/pdf/fonts.d.ts +96 -0
- package/dist/pdf/fonts.js +593 -0
- package/dist/pdf/images.d.ts +28 -0
- package/dist/pdf/images.js +439 -0
- package/dist/pdf/objects.d.ts +93 -0
- package/dist/pdf/objects.js +237 -0
- package/dist/pdf/pageSpace.d.ts +43 -0
- package/dist/pdf/pageSpace.js +57 -0
- package/dist/pdf/paint.d.ts +32 -0
- package/dist/pdf/paint.js +442 -0
- package/dist/pdf/writePdf.d.ts +56 -0
- package/dist/pdf/writePdf.js +345 -0
- package/dist/prosemirror/commands/comments.js +91 -6
- package/dist/prosemirror/conversion/fromProseDoc.js +41 -7
- package/dist/prosemirror/conversion/toProseDoc.js +41 -4
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +15 -2
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +35 -6
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +2 -2
- package/dist/prosemirror/extensions/features/pasteCleanup.js +4 -3
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.d.ts +3 -1
- package/dist/prosemirror/extensions/nodes/BookmarkBoundaryExtension.js +5 -3
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.d.ts +7 -1
- package/dist/prosemirror/extensions/nodes/RenderedPageBreakExtension.js +9 -3
- package/dist/prosemirror/extensions/nodes/TableExtension.js +56 -39
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.d.ts +3 -1
- package/dist/prosemirror/extensions/nodes/TextBoxAnchorExtension.js +5 -3
- package/dist/prosemirror/plugins/suggestionMode.js +1 -0
- package/dist/prosemirror/revisionCarriers.js +19 -2
- package/dist/prosemirror/utils/extractTrackedChanges.d.ts +2 -2
- package/dist/prosemirror/utils/extractTrackedChanges.js +59 -23
- package/dist/prosemirror/validation.js +55 -27
- package/dist/prosemirror/zeroWidthAnchors.d.ts +10 -0
- package/dist/prosemirror/zeroWidthAnchors.js +23 -0
- package/dist/redline.js +12 -79
- package/dist/server.d.ts +4 -4
- package/dist/server.js +2 -2
- package/dist/shaping/placeRun.d.ts +51 -0
- package/dist/shaping/placeRun.js +84 -0
- package/dist/shaping/shaper.d.ts +92 -0
- package/dist/shaping/shaper.js +102 -0
- package/dist/types/block-id.d.ts +18 -2
- package/dist/types/block-id.js +49 -5
- package/dist/types/content.d.ts +2 -2
- package/dist/utils/clipboard.js +4 -3
- package/dist/utils/fontResolver.js +183 -0
- package/dist/utils/htmlComments.d.ts +17 -0
- package/dist/utils/htmlComments.js +22 -0
- package/dist/utils/scriptSegments.d.ts +12 -1
- package/dist/utils/scriptSegments.js +17 -1
- package/dist/version-comparison.d.ts +1 -1
- 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 };
|