@stll/folio-core 0.32.2 → 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 +649 -85
- 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 +219 -15
- 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 +39 -8
- package/dist/document-operations.js +169 -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 +3 -3
- 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,593 @@
|
|
|
1
|
+
import { parseSfnt } from "../fonts/sfnt/parse.js";
|
|
2
|
+
import { subsetTrueType } from "../fonts/sfnt/subset.js";
|
|
3
|
+
import { toSfntBytes } from "../fonts/sfnt/woff.js";
|
|
4
|
+
import { needsShaping, placeRun } from "../shaping/placeRun.js";
|
|
5
|
+
import { pdfArray, pdfAsciiString, pdfDict, pdfFlateStream, pdfName, pdfNumber, pdfNumberArray } from "./objects.js";
|
|
6
|
+
import { Result, TaggedError, panic } from "better-result";
|
|
7
|
+
//#region src/pdf/fonts.ts
|
|
8
|
+
/**
|
|
9
|
+
* Font embedding.
|
|
10
|
+
*
|
|
11
|
+
* Every face the document paints becomes a composite (Type 0) font with
|
|
12
|
+
* `Identity-H` encoding, so a run is written as two-byte glyph ids and the
|
|
13
|
+
* file carries no encoding table to disagree with the font. A face the source
|
|
14
|
+
* cannot supply, or whose licence bits forbid embedding, falls back to a
|
|
15
|
+
* base-14 face and is *reported*: a substitution the caller cannot see is a
|
|
16
|
+
* silently different document.
|
|
17
|
+
*
|
|
18
|
+
* ## A face is not a binary
|
|
19
|
+
*
|
|
20
|
+
* A packaged family arrives cut into disjoint script subsets: one binary
|
|
21
|
+
* carries ASCII, another the Central European letters, and Czech, Slovak or
|
|
22
|
+
* Polish text needs both at once. Each binary is its own sfnt with its own
|
|
23
|
+
* glyph space, so one face becomes one PDF font resource *per binary the
|
|
24
|
+
* document actually uses*, and a code point is served by the first binary
|
|
25
|
+
* whose cmap covers it.
|
|
26
|
+
*/
|
|
27
|
+
var PdfFontError = class extends TaggedError("PdfFontError") {};
|
|
28
|
+
/** Text space is 1000 units to the em whatever the font's own head is. */
|
|
29
|
+
const TEXT_SPACE_UNITS_PER_EM = 1e3;
|
|
30
|
+
/** `fsType` bit 1: the vendor forbids embedding this face outright. */
|
|
31
|
+
const FSTYPE_RESTRICTED_LICENSE = 2;
|
|
32
|
+
const FONT_FLAG_FIXED_PITCH = 1;
|
|
33
|
+
const FONT_FLAG_SERIF = 2;
|
|
34
|
+
const FONT_FLAG_SYMBOLIC = 4;
|
|
35
|
+
const FONT_FLAG_ITALIC = 64;
|
|
36
|
+
/**
|
|
37
|
+
* `/StemV` has no counterpart in an sfnt table folio parses. A reader only
|
|
38
|
+
* uses it to synthesize a stand-in when the embedded program is unusable, so
|
|
39
|
+
* an approximation from the weight is honest and a wrong number is harmless.
|
|
40
|
+
*/
|
|
41
|
+
const STEM_V_REGULAR = 80;
|
|
42
|
+
const STEM_V_BOLD = 160;
|
|
43
|
+
const BOLD_WEIGHT_THRESHOLD = 600;
|
|
44
|
+
const NOTDEF_GLYPH = 0;
|
|
45
|
+
const SUBSET_TAG_LENGTH = 6;
|
|
46
|
+
const ALPHABET_LENGTH = 26;
|
|
47
|
+
const UPPERCASE_A = 65;
|
|
48
|
+
const WOFF_SIGNATURES = ["wOFF", "wOF2"];
|
|
49
|
+
const readTag = (bytes) => String.fromCharCode(bytes[0] ?? 0, bytes[1] ?? 0, bytes[2] ?? 0, bytes[3] ?? 0);
|
|
50
|
+
/**
|
|
51
|
+
* Six uppercase letters derived from the face and the exact glyph set, so the
|
|
52
|
+
* tag is stable across runs and two different subsets of one family do not
|
|
53
|
+
* collide under the same name.
|
|
54
|
+
*/
|
|
55
|
+
const subsetTag = (seed) => {
|
|
56
|
+
const FNV_OFFSET = 2166136261;
|
|
57
|
+
const FNV_PRIME = 16777619;
|
|
58
|
+
let hash = FNV_OFFSET;
|
|
59
|
+
for (let index = 0; index < seed.length; index += 1) hash = Math.imul(hash ^ seed.charCodeAt(index), FNV_PRIME) >>> 0;
|
|
60
|
+
let tag = "";
|
|
61
|
+
for (let index = 0; index < SUBSET_TAG_LENGTH; index += 1) {
|
|
62
|
+
tag += String.fromCharCode(UPPERCASE_A + hash % ALPHABET_LENGTH);
|
|
63
|
+
hash = Math.floor(hash / ALPHABET_LENGTH);
|
|
64
|
+
}
|
|
65
|
+
return tag;
|
|
66
|
+
};
|
|
67
|
+
const BASE_14_ROMAN = {
|
|
68
|
+
helvetica: "Helvetica",
|
|
69
|
+
times: "Times-Roman",
|
|
70
|
+
courier: "Courier"
|
|
71
|
+
};
|
|
72
|
+
const BASE_14_BOLD = {
|
|
73
|
+
helvetica: "Helvetica-Bold",
|
|
74
|
+
times: "Times-Bold",
|
|
75
|
+
courier: "Courier-Bold"
|
|
76
|
+
};
|
|
77
|
+
const BASE_14_ITALIC = {
|
|
78
|
+
helvetica: "Helvetica-Oblique",
|
|
79
|
+
times: "Times-Italic",
|
|
80
|
+
courier: "Courier-Oblique"
|
|
81
|
+
};
|
|
82
|
+
const BASE_14_BOLD_ITALIC = {
|
|
83
|
+
helvetica: "Helvetica-BoldOblique",
|
|
84
|
+
times: "Times-BoldItalic",
|
|
85
|
+
courier: "Courier-BoldOblique"
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Total over the display list's generic categories: a new category is a
|
|
89
|
+
* compile error here rather than a face that silently lands on Helvetica.
|
|
90
|
+
*/
|
|
91
|
+
const GENERIC_TO_BASE_14 = {
|
|
92
|
+
serif: "times",
|
|
93
|
+
"sans-serif": "helvetica",
|
|
94
|
+
monospace: "courier",
|
|
95
|
+
cursive: "times",
|
|
96
|
+
fantasy: "helvetica"
|
|
97
|
+
};
|
|
98
|
+
const base14Name = (face) => {
|
|
99
|
+
const family = GENERIC_TO_BASE_14[face.generic];
|
|
100
|
+
const bold = face.weight >= BOLD_WEIGHT_THRESHOLD;
|
|
101
|
+
if (bold && face.italic) return BASE_14_BOLD_ITALIC[family];
|
|
102
|
+
if (bold) return BASE_14_BOLD[family];
|
|
103
|
+
return face.italic ? BASE_14_ITALIC[family] : BASE_14_ROMAN[family];
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* The code points WinAnsiEncoding places in 0x80..0x9F, where it departs from
|
|
107
|
+
* Latin-1. Outside that block WinAnsi and Latin-1 agree.
|
|
108
|
+
*/
|
|
109
|
+
const WIN_ANSI_HIGH_RANGE = [
|
|
110
|
+
[128, 8364],
|
|
111
|
+
[130, 8218],
|
|
112
|
+
[131, 402],
|
|
113
|
+
[132, 8222],
|
|
114
|
+
[133, 8230],
|
|
115
|
+
[134, 8224],
|
|
116
|
+
[135, 8225],
|
|
117
|
+
[136, 710],
|
|
118
|
+
[137, 8240],
|
|
119
|
+
[138, 352],
|
|
120
|
+
[139, 8249],
|
|
121
|
+
[140, 338],
|
|
122
|
+
[142, 381],
|
|
123
|
+
[145, 8216],
|
|
124
|
+
[146, 8217],
|
|
125
|
+
[147, 8220],
|
|
126
|
+
[148, 8221],
|
|
127
|
+
[149, 8226],
|
|
128
|
+
[150, 8211],
|
|
129
|
+
[151, 8212],
|
|
130
|
+
[152, 732],
|
|
131
|
+
[153, 8482],
|
|
132
|
+
[154, 353],
|
|
133
|
+
[155, 8250],
|
|
134
|
+
[156, 339],
|
|
135
|
+
[158, 382],
|
|
136
|
+
[159, 376]
|
|
137
|
+
];
|
|
138
|
+
const WIN_ANSI_QUESTION_MARK = 63;
|
|
139
|
+
const buildWinAnsiTable = () => {
|
|
140
|
+
const table = /* @__PURE__ */ new Map();
|
|
141
|
+
const PRINTABLE_ASCII_START = 32;
|
|
142
|
+
const PRINTABLE_ASCII_END = 126;
|
|
143
|
+
const LATIN1_HIGH_START = 160;
|
|
144
|
+
const LATIN1_HIGH_END = 255;
|
|
145
|
+
for (let code = PRINTABLE_ASCII_START; code <= PRINTABLE_ASCII_END; code += 1) table.set(code, code);
|
|
146
|
+
for (let code = LATIN1_HIGH_START; code <= LATIN1_HIGH_END; code += 1) table.set(code, code);
|
|
147
|
+
for (const [byte, codePoint] of WIN_ANSI_HIGH_RANGE) table.set(codePoint, byte);
|
|
148
|
+
return table;
|
|
149
|
+
};
|
|
150
|
+
const WIN_ANSI_BY_CODE_POINT = buildWinAnsiTable();
|
|
151
|
+
const utf16BeHexOf = (text) => [...text].map((character) => utf16BeHex(character.codePointAt(0) ?? 0)).join("");
|
|
152
|
+
const utf16BeHex = (codePoint) => {
|
|
153
|
+
const SUPPLEMENTARY_START = 65536;
|
|
154
|
+
const SURROGATE_HALF_BITS = 10;
|
|
155
|
+
const LOW_SURROGATE_MASK = 1023;
|
|
156
|
+
const HIGH_SURROGATE_BASE = 55296;
|
|
157
|
+
const LOW_SURROGATE_BASE = 56320;
|
|
158
|
+
const hex = (unit) => unit.toString(16).padStart(4, "0").toUpperCase();
|
|
159
|
+
if (codePoint < SUPPLEMENTARY_START) return hex(codePoint);
|
|
160
|
+
const offset = codePoint - SUPPLEMENTARY_START;
|
|
161
|
+
return hex(HIGH_SURROGATE_BASE + (offset >> SURROGATE_HALF_BITS)) + hex(LOW_SURROGATE_BASE + (offset & LOW_SURROGATE_MASK));
|
|
162
|
+
};
|
|
163
|
+
/** Entries per `beginbfchar` block; the CMap syntax caps this at 100. */
|
|
164
|
+
const BFCHAR_BLOCK_SIZE = 100;
|
|
165
|
+
const toUnicodeCMap = (glyphToText) => {
|
|
166
|
+
const entries = [...glyphToText.entries()].sort(([left], [right]) => left - right);
|
|
167
|
+
let body = "";
|
|
168
|
+
for (let start = 0; start < entries.length; start += BFCHAR_BLOCK_SIZE) {
|
|
169
|
+
const block = entries.slice(start, start + BFCHAR_BLOCK_SIZE);
|
|
170
|
+
body += `${String(block.length)} beginbfchar\n`;
|
|
171
|
+
for (const [glyphId, text] of block) body += `<${utf16BeHex(glyphId)}> <${utf16BeHexOf(text)}>\n`;
|
|
172
|
+
body += "endbfchar\n";
|
|
173
|
+
}
|
|
174
|
+
return `/CIDInit /ProcSet findresource begin
|
|
175
|
+
12 dict begin
|
|
176
|
+
begincmap
|
|
177
|
+
/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def
|
|
178
|
+
/CMapName /Adobe-Identity-UCS def
|
|
179
|
+
/CMapType 2 def
|
|
180
|
+
1 begincodespacerange
|
|
181
|
+
<0000> <FFFF>
|
|
182
|
+
endcodespacerange
|
|
183
|
+
${body}endcmap
|
|
184
|
+
CMapName currentdict /CMap defineresource pop
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
`;
|
|
188
|
+
};
|
|
189
|
+
/**
|
|
190
|
+
* `/W` as consecutive runs: `[ firstGid [w w w] nextGid [w] ]`. A subset's
|
|
191
|
+
* glyph ids are dense, so this is normally one run.
|
|
192
|
+
*/
|
|
193
|
+
const widthsArray = (widths) => {
|
|
194
|
+
const items = [];
|
|
195
|
+
let runStart = null;
|
|
196
|
+
let expected = 0;
|
|
197
|
+
let run = [];
|
|
198
|
+
const flush = () => {
|
|
199
|
+
if (runStart !== null && run.length > 0) items.push(pdfNumber(runStart), pdfNumberArray(run));
|
|
200
|
+
};
|
|
201
|
+
for (const glyphId of [...widths.keys()].sort((left, right) => left - right)) {
|
|
202
|
+
if (runStart === null || glyphId !== expected) {
|
|
203
|
+
flush();
|
|
204
|
+
runStart = glyphId;
|
|
205
|
+
run = [];
|
|
206
|
+
}
|
|
207
|
+
run.push(widths.get(glyphId) ?? panic("width run lost a glyph"));
|
|
208
|
+
expected = glyphId + 1;
|
|
209
|
+
}
|
|
210
|
+
flush();
|
|
211
|
+
return pdfArray(items);
|
|
212
|
+
};
|
|
213
|
+
const asciiOnly = (value) => value.replace(/[^\x20-\x7e]/gu, "");
|
|
214
|
+
const fontDescriptor = ({ font, face, fontName, fontFileKey, fontFileRef }) => {
|
|
215
|
+
const scale = TEXT_SPACE_UNITS_PER_EM / font.unitsPerEm;
|
|
216
|
+
const italic = face.italic || font.italicAngle !== 0;
|
|
217
|
+
const flags = FONT_FLAG_SYMBOLIC | (face.generic === "serif" ? FONT_FLAG_SERIF : 0) | (face.generic === "monospace" ? FONT_FLAG_FIXED_PITCH : 0) | (italic ? FONT_FLAG_ITALIC : 0);
|
|
218
|
+
return pdfDict([
|
|
219
|
+
["Type", pdfName("FontDescriptor")],
|
|
220
|
+
["FontName", pdfName(fontName)],
|
|
221
|
+
["Flags", pdfNumber(flags)],
|
|
222
|
+
["FontBBox", pdfNumberArray(font.bbox.map((value) => value * scale))],
|
|
223
|
+
["ItalicAngle", pdfNumber(font.italicAngle)],
|
|
224
|
+
["Ascent", pdfNumber(font.ascender * scale)],
|
|
225
|
+
["Descent", pdfNumber(font.descender * scale)],
|
|
226
|
+
["CapHeight", pdfNumber(font.capHeight * scale)],
|
|
227
|
+
["StemV", pdfNumber(face.weight >= BOLD_WEIGHT_THRESHOLD ? STEM_V_BOLD : STEM_V_REGULAR)],
|
|
228
|
+
[fontFileKey, fontFileRef]
|
|
229
|
+
]);
|
|
230
|
+
};
|
|
231
|
+
const planFont = ({ font, face, codePoints, shapedGlyphIds, textByShapedGlyphId }) => {
|
|
232
|
+
const sourceGlyphByCodePoint = /* @__PURE__ */ new Map();
|
|
233
|
+
const sourceGlyphIds = /* @__PURE__ */ new Set([NOTDEF_GLYPH]);
|
|
234
|
+
for (const codePoint of codePoints) {
|
|
235
|
+
const glyphId = font.glyphIdFor(codePoint);
|
|
236
|
+
sourceGlyphByCodePoint.set(codePoint, glyphId);
|
|
237
|
+
sourceGlyphIds.add(glyphId);
|
|
238
|
+
}
|
|
239
|
+
for (const glyphId of shapedGlyphIds ?? []) sourceGlyphIds.add(glyphId);
|
|
240
|
+
let glyphIdMap;
|
|
241
|
+
let programBytes;
|
|
242
|
+
if (font.isCff) {
|
|
243
|
+
glyphIdMap = new Map([...sourceGlyphIds].map((glyphId) => [glyphId, glyphId]));
|
|
244
|
+
programBytes = font.bytes;
|
|
245
|
+
} else {
|
|
246
|
+
const subset = subsetTrueType(font, sourceGlyphIds);
|
|
247
|
+
if (subset.isErr()) return Result.err(new PdfFontError({ message: `subsetting failed: ${subset.error.message}` }));
|
|
248
|
+
glyphIdMap = subset.value.glyphIdMap;
|
|
249
|
+
programBytes = subset.value.bytes;
|
|
250
|
+
}
|
|
251
|
+
const scale = TEXT_SPACE_UNITS_PER_EM / font.unitsPerEm;
|
|
252
|
+
const glyphIdByCodePoint = /* @__PURE__ */ new Map();
|
|
253
|
+
const widthByGlyphId = /* @__PURE__ */ new Map();
|
|
254
|
+
const glyphToText = /* @__PURE__ */ new Map();
|
|
255
|
+
for (const sourceGlyphId of [...sourceGlyphIds].sort((left, right) => left - right)) {
|
|
256
|
+
const targetGlyphId = glyphIdMap.get(sourceGlyphId);
|
|
257
|
+
if (targetGlyphId === void 0) return Result.err(new PdfFontError({ message: `subset dropped glyph ${String(sourceGlyphId)}` }));
|
|
258
|
+
widthByGlyphId.set(targetGlyphId, Math.round(font.advanceWidthFor(sourceGlyphId) * scale));
|
|
259
|
+
}
|
|
260
|
+
for (const codePoint of [...sourceGlyphByCodePoint.keys()].sort((left, right) => left - right)) {
|
|
261
|
+
const sourceGlyphId = sourceGlyphByCodePoint.get(codePoint) ?? NOTDEF_GLYPH;
|
|
262
|
+
const targetGlyphId = glyphIdMap.get(sourceGlyphId) ?? NOTDEF_GLYPH;
|
|
263
|
+
glyphIdByCodePoint.set(codePoint, targetGlyphId);
|
|
264
|
+
if (!glyphToText.has(targetGlyphId)) glyphToText.set(targetGlyphId, String.fromCodePoint(codePoint));
|
|
265
|
+
}
|
|
266
|
+
for (const [sourceGlyphId, text] of textByShapedGlyphId ?? []) {
|
|
267
|
+
const targetGlyphId = glyphIdMap.get(sourceGlyphId);
|
|
268
|
+
if (targetGlyphId !== void 0 && !glyphToText.has(targetGlyphId)) glyphToText.set(targetGlyphId, text);
|
|
269
|
+
}
|
|
270
|
+
const baseName = asciiOnly(font.postScriptName) || asciiOnly(face.family) || "Unknown";
|
|
271
|
+
const fontName = `${subsetTag(`${baseName}:${[...sourceGlyphIds].sort((left, right) => left - right).join(",")}`)}+${baseName}`;
|
|
272
|
+
return Result.ok({
|
|
273
|
+
font,
|
|
274
|
+
face,
|
|
275
|
+
fontName,
|
|
276
|
+
programBytes,
|
|
277
|
+
glyphIdByCodePoint,
|
|
278
|
+
widthByGlyphId,
|
|
279
|
+
glyphToText,
|
|
280
|
+
glyphIdMap
|
|
281
|
+
});
|
|
282
|
+
};
|
|
283
|
+
const emitFont = (document, plan) => {
|
|
284
|
+
const { font, face, fontName, programBytes } = plan;
|
|
285
|
+
const fontFileRef = document.add(font.isCff ? pdfFlateStream([["Subtype", pdfName("OpenType")]], programBytes) : pdfFlateStream([["Length1", pdfNumber(programBytes.length)]], programBytes));
|
|
286
|
+
const descriptorRef = document.add(fontDescriptor({
|
|
287
|
+
font,
|
|
288
|
+
face,
|
|
289
|
+
fontName,
|
|
290
|
+
fontFileKey: font.isCff ? "FontFile3" : "FontFile2",
|
|
291
|
+
fontFileRef
|
|
292
|
+
}));
|
|
293
|
+
const toUnicodeRef = document.add(pdfFlateStream([], new TextEncoder().encode(toUnicodeCMap(plan.glyphToText))));
|
|
294
|
+
const descendantRef = document.add(pdfDict([
|
|
295
|
+
["Type", pdfName("Font")],
|
|
296
|
+
["Subtype", pdfName(font.isCff ? "CIDFontType0" : "CIDFontType2")],
|
|
297
|
+
["BaseFont", pdfName(fontName)],
|
|
298
|
+
["CIDSystemInfo", pdfDict([
|
|
299
|
+
["Registry", pdfAsciiString("Adobe")],
|
|
300
|
+
["Ordering", pdfAsciiString("Identity")],
|
|
301
|
+
["Supplement", pdfNumber(0)]
|
|
302
|
+
])],
|
|
303
|
+
["FontDescriptor", descriptorRef],
|
|
304
|
+
["DW", pdfNumber(TEXT_SPACE_UNITS_PER_EM)],
|
|
305
|
+
["W", widthsArray(plan.widthByGlyphId)],
|
|
306
|
+
["CIDToGIDMap", font.isCff ? void 0 : pdfName("Identity")]
|
|
307
|
+
]));
|
|
308
|
+
return document.add(pdfDict([
|
|
309
|
+
["Type", pdfName("Font")],
|
|
310
|
+
["Subtype", pdfName("Type0")],
|
|
311
|
+
["BaseFont", pdfName(fontName)],
|
|
312
|
+
["Encoding", pdfName("Identity-H")],
|
|
313
|
+
["DescendantFonts", pdfArray([descendantRef])],
|
|
314
|
+
["ToUnicode", toUnicodeRef]
|
|
315
|
+
]));
|
|
316
|
+
};
|
|
317
|
+
const standardFont = (document, face) => document.add(pdfDict([
|
|
318
|
+
["Type", pdfName("Font")],
|
|
319
|
+
["Subtype", pdfName("Type1")],
|
|
320
|
+
["BaseFont", pdfName(base14Name(face))],
|
|
321
|
+
["Encoding", pdfName("WinAnsiEncoding")]
|
|
322
|
+
]));
|
|
323
|
+
const parseFace = (binaries) => {
|
|
324
|
+
if (binaries.length === 0) return {
|
|
325
|
+
kind: "unusable",
|
|
326
|
+
reason: "the font source supplied no bytes for this face"
|
|
327
|
+
};
|
|
328
|
+
const fonts = [];
|
|
329
|
+
let reason = "";
|
|
330
|
+
for (const bytes of binaries) {
|
|
331
|
+
let sfntBytes = bytes;
|
|
332
|
+
if (WOFF_SIGNATURES.includes(readTag(bytes))) {
|
|
333
|
+
const decoded = toSfntBytes(bytes);
|
|
334
|
+
if (decoded.isErr()) {
|
|
335
|
+
reason = `WOFF decoding failed: ${decoded.error.message}`;
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
sfntBytes = decoded.value;
|
|
339
|
+
}
|
|
340
|
+
const parsed = parseSfnt(sfntBytes);
|
|
341
|
+
if (parsed.isErr()) {
|
|
342
|
+
reason = `font parsing failed: ${parsed.error.message}`;
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
if ((parsed.value.fsType & FSTYPE_RESTRICTED_LICENSE) !== 0) {
|
|
346
|
+
reason = "the face's fsType bits forbid embedding";
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
fonts.push(parsed.value);
|
|
350
|
+
}
|
|
351
|
+
return fonts.length === 0 ? {
|
|
352
|
+
kind: "unusable",
|
|
353
|
+
reason
|
|
354
|
+
} : {
|
|
355
|
+
kind: "usable",
|
|
356
|
+
fonts
|
|
357
|
+
};
|
|
358
|
+
};
|
|
359
|
+
/** Where a code point no binary covers is painted from: `.notdef` is there. */
|
|
360
|
+
const FALLBACK_BINARY = 0;
|
|
361
|
+
/**
|
|
362
|
+
* Which binary of the face serves a code point, or -1 when none does. One
|
|
363
|
+
* answer for the whole file: coverage and shaping must agree about which
|
|
364
|
+
* binary a character belongs to, or a run is shaped against one face and
|
|
365
|
+
* painted from another's glyph space.
|
|
366
|
+
*/
|
|
367
|
+
const binaryIndexFor = (fonts, codePoint) => fonts.findIndex((font) => font.glyphIdFor(codePoint) !== NOTDEF_GLYPH);
|
|
368
|
+
const resolveCoverage = (fonts, codePoints) => {
|
|
369
|
+
const codePointsByBinary = /* @__PURE__ */ new Map();
|
|
370
|
+
const uncovered = [];
|
|
371
|
+
for (const codePoint of codePoints) {
|
|
372
|
+
const found = binaryIndexFor(fonts, codePoint);
|
|
373
|
+
if (found === -1) uncovered.push(codePoint);
|
|
374
|
+
const binary = found === -1 ? FALLBACK_BINARY : found;
|
|
375
|
+
const bucket = codePointsByBinary.get(binary) ?? [];
|
|
376
|
+
bucket.push(codePoint);
|
|
377
|
+
codePointsByBinary.set(binary, bucket);
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
codePointsByBinary,
|
|
381
|
+
uncovered
|
|
382
|
+
};
|
|
383
|
+
};
|
|
384
|
+
const segmentRun = (fonts, text) => {
|
|
385
|
+
const segments = [];
|
|
386
|
+
let open = null;
|
|
387
|
+
let index = 0;
|
|
388
|
+
for (const character of text) {
|
|
389
|
+
const found = binaryIndexFor(fonts, character.codePointAt(0) ?? 0);
|
|
390
|
+
const binaryIndex = found === -1 ? FALLBACK_BINARY : found;
|
|
391
|
+
if (open === null || open.binaryIndex !== binaryIndex) {
|
|
392
|
+
open = {
|
|
393
|
+
binaryIndex,
|
|
394
|
+
startIndex: index,
|
|
395
|
+
text: character
|
|
396
|
+
};
|
|
397
|
+
segments.push(open);
|
|
398
|
+
} else open.text += character;
|
|
399
|
+
index += 1;
|
|
400
|
+
}
|
|
401
|
+
return segments;
|
|
402
|
+
};
|
|
403
|
+
const EMPTY_SHAPING = {
|
|
404
|
+
glyphIdsByBinary: /* @__PURE__ */ new Map(),
|
|
405
|
+
textByGlyphByBinary: /* @__PURE__ */ new Map(),
|
|
406
|
+
placementsByRun: /* @__PURE__ */ new Map()
|
|
407
|
+
};
|
|
408
|
+
const runKeyOf = ({ text, direction, fontSizePx }) => `${direction}\u0000${String(fontSizePx)}\u0000${text}`;
|
|
409
|
+
/**
|
|
410
|
+
* The characters behind one glyph: the code points of its cluster, carried by
|
|
411
|
+
* the first glyph of that cluster only. Giving every mark of a cluster the same
|
|
412
|
+
* characters would repeat them on extraction.
|
|
413
|
+
*/
|
|
414
|
+
const clusterText = (characters, glyphs, position) => {
|
|
415
|
+
const glyph = glyphs[position];
|
|
416
|
+
if (glyph === void 0) return "";
|
|
417
|
+
if (glyphs.find((other) => other.clusterIndex === glyph.clusterIndex) !== glyph) return "";
|
|
418
|
+
const next = [...new Set(glyphs.map((other) => other.clusterIndex))].sort((left, right) => left - right).find((start) => start > glyph.clusterIndex) ?? characters.length;
|
|
419
|
+
return characters.slice(glyph.clusterIndex, next).join("");
|
|
420
|
+
};
|
|
421
|
+
const shapeFace = (fonts, runs, shaper) => {
|
|
422
|
+
const glyphIdsByBinary = /* @__PURE__ */ new Map();
|
|
423
|
+
const textByGlyphByBinary = /* @__PURE__ */ new Map();
|
|
424
|
+
const placementsByRun = /* @__PURE__ */ new Map();
|
|
425
|
+
for (const run of runs) {
|
|
426
|
+
const key = runKeyOf(run);
|
|
427
|
+
if (placementsByRun.has(key)) continue;
|
|
428
|
+
const placed = [];
|
|
429
|
+
for (const segment of segmentRun(fonts, run.text)) {
|
|
430
|
+
const font = fonts[segment.binaryIndex];
|
|
431
|
+
if (font === void 0 || !needsShaping(segment.text)) continue;
|
|
432
|
+
const glyphs = placeRun({
|
|
433
|
+
shaper,
|
|
434
|
+
font: font.bytes,
|
|
435
|
+
text: segment.text,
|
|
436
|
+
fontSizePx: run.fontSizePx,
|
|
437
|
+
direction: run.direction
|
|
438
|
+
});
|
|
439
|
+
placed.push({
|
|
440
|
+
binaryIndex: segment.binaryIndex,
|
|
441
|
+
startIndex: segment.startIndex,
|
|
442
|
+
glyphs
|
|
443
|
+
});
|
|
444
|
+
const ids = glyphIdsByBinary.get(segment.binaryIndex) ?? /* @__PURE__ */ new Set();
|
|
445
|
+
const texts = textByGlyphByBinary.get(segment.binaryIndex) ?? /* @__PURE__ */ new Map();
|
|
446
|
+
const characters = [...segment.text];
|
|
447
|
+
for (const [position, glyph] of glyphs.entries()) {
|
|
448
|
+
ids.add(glyph.glyphId);
|
|
449
|
+
const text = clusterText(characters, glyphs, position);
|
|
450
|
+
if (text !== "" && !texts.has(glyph.glyphId)) texts.set(glyph.glyphId, text);
|
|
451
|
+
}
|
|
452
|
+
glyphIdsByBinary.set(segment.binaryIndex, ids);
|
|
453
|
+
textByGlyphByBinary.set(segment.binaryIndex, texts);
|
|
454
|
+
}
|
|
455
|
+
placementsByRun.set(key, placed);
|
|
456
|
+
}
|
|
457
|
+
return {
|
|
458
|
+
glyphIdsByBinary,
|
|
459
|
+
textByGlyphByBinary,
|
|
460
|
+
placementsByRun
|
|
461
|
+
};
|
|
462
|
+
};
|
|
463
|
+
/**
|
|
464
|
+
* Turns the display list's font table into PDF font objects. A face nothing
|
|
465
|
+
* paints is skipped entirely: an unused face is not a substitution, and
|
|
466
|
+
* embedding it would put bytes in the file no page reads.
|
|
467
|
+
*/
|
|
468
|
+
const prepareFonts = ({ document, faces, usedCodePoints, shapedRuns, shaper = null, source }) => {
|
|
469
|
+
const byFontIndex = /* @__PURE__ */ new Map();
|
|
470
|
+
const fontRefByResourceIndex = /* @__PURE__ */ new Map();
|
|
471
|
+
const substitutions = [];
|
|
472
|
+
const unencodable = [];
|
|
473
|
+
let nextResourceIndex = 0;
|
|
474
|
+
const claimResource = (ref) => {
|
|
475
|
+
const resourceIndex = nextResourceIndex;
|
|
476
|
+
nextResourceIndex += 1;
|
|
477
|
+
fontRefByResourceIndex.set(resourceIndex, ref);
|
|
478
|
+
return resourceIndex;
|
|
479
|
+
};
|
|
480
|
+
const reported = /* @__PURE__ */ new Set();
|
|
481
|
+
const report = (face, codePoints) => {
|
|
482
|
+
for (const codePoint of codePoints) {
|
|
483
|
+
const key = `${face.family}${String(face.weight)}${String(face.italic)}${String(codePoint)}`;
|
|
484
|
+
if (reported.has(key)) continue;
|
|
485
|
+
reported.add(key);
|
|
486
|
+
unencodable.push({
|
|
487
|
+
codePoint,
|
|
488
|
+
family: face.family,
|
|
489
|
+
weight: face.weight,
|
|
490
|
+
italic: face.italic
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
const substitute = (fontIndex, face, codePoints, reason) => {
|
|
495
|
+
substitutions.push({
|
|
496
|
+
family: face.family,
|
|
497
|
+
weight: face.weight,
|
|
498
|
+
italic: face.italic,
|
|
499
|
+
reason
|
|
500
|
+
});
|
|
501
|
+
report(face, codePoints.filter((codePoint) => !WIN_ANSI_BY_CODE_POINT.has(codePoint)));
|
|
502
|
+
byFontIndex.set(fontIndex, {
|
|
503
|
+
kind: "standard",
|
|
504
|
+
resourceIndex: claimResource(standardFont(document, face)),
|
|
505
|
+
byteFor: (codePoint) => WIN_ANSI_BY_CODE_POINT.get(codePoint) ?? WIN_ANSI_QUESTION_MARK
|
|
506
|
+
});
|
|
507
|
+
};
|
|
508
|
+
for (const [fontIndex, face] of faces.entries()) {
|
|
509
|
+
const used = usedCodePoints.get(fontIndex);
|
|
510
|
+
if (used === void 0 || used.size === 0) continue;
|
|
511
|
+
const codePoints = [...used].sort((left, right) => left - right);
|
|
512
|
+
const binaries = face.embedded === void 0 ? source.load(face) : [face.embedded.bytes];
|
|
513
|
+
const parsed = parseFace(binaries);
|
|
514
|
+
if (parsed.kind === "unusable") {
|
|
515
|
+
substitute(fontIndex, face, codePoints, parsed.reason);
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
const shaping = shaper === null ? EMPTY_SHAPING : shapeFace(parsed.fonts, shapedRuns?.get(fontIndex) ?? [], shaper);
|
|
519
|
+
const coverage = resolveCoverage(parsed.fonts, codePoints);
|
|
520
|
+
const binaryIndices = [.../* @__PURE__ */ new Set([...coverage.codePointsByBinary.keys(), ...shaping.glyphIdsByBinary.keys()])].sort((left, right) => left - right);
|
|
521
|
+
const plans = [];
|
|
522
|
+
let failure = null;
|
|
523
|
+
for (const binaryIndex of binaryIndices) {
|
|
524
|
+
const plan = planFont({
|
|
525
|
+
font: parsed.fonts[binaryIndex] ?? panic(`face lost binary ${String(binaryIndex)}`),
|
|
526
|
+
face,
|
|
527
|
+
codePoints: coverage.codePointsByBinary.get(binaryIndex) ?? [],
|
|
528
|
+
shapedGlyphIds: shaping.glyphIdsByBinary.get(binaryIndex),
|
|
529
|
+
textByShapedGlyphId: shaping.textByGlyphByBinary.get(binaryIndex)
|
|
530
|
+
});
|
|
531
|
+
if (plan.isErr()) {
|
|
532
|
+
failure = plan.error;
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
plans.push(plan.value);
|
|
536
|
+
}
|
|
537
|
+
if (failure !== null) {
|
|
538
|
+
substitute(fontIndex, face, codePoints, failure.message);
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
const glyphByCodePoint = /* @__PURE__ */ new Map();
|
|
542
|
+
const emitted = /* @__PURE__ */ new Map();
|
|
543
|
+
for (const [position, plan] of plans.entries()) {
|
|
544
|
+
const resourceIndex = claimResource(emitFont(document, plan));
|
|
545
|
+
emitted.set(binaryIndices[position] ?? FALLBACK_BINARY, {
|
|
546
|
+
plan,
|
|
547
|
+
resourceIndex
|
|
548
|
+
});
|
|
549
|
+
for (const [codePoint, glyphId] of plan.glyphIdByCodePoint) glyphByCodePoint.set(codePoint, {
|
|
550
|
+
resourceIndex,
|
|
551
|
+
glyphId,
|
|
552
|
+
widthUnits: plan.widthByGlyphId.get(glyphId) ?? panic(`no width for glyph ${String(glyphId)}`)
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
report(face, coverage.uncovered);
|
|
556
|
+
byFontIndex.set(fontIndex, {
|
|
557
|
+
kind: "embedded",
|
|
558
|
+
glyphFor: (codePoint) => glyphByCodePoint.get(codePoint) ?? panic(`code point ${String(codePoint)} was painted but never collected`),
|
|
559
|
+
placeShapedRun: (request) => {
|
|
560
|
+
const segments = shaping.placementsByRun.get(runKeyOf(request));
|
|
561
|
+
if (segments === void 0 || segments.length === 0) return null;
|
|
562
|
+
return segments.flatMap(({ binaryIndex, startIndex, glyphs }) => {
|
|
563
|
+
const target = emitted.get(binaryIndex) ?? panic(`shaped run used binary ${String(binaryIndex)}, which was never emitted`);
|
|
564
|
+
return glyphs.map(({ glyphId, clusterIndex, xAdvancePx, xOffsetPx, yOffsetPx }) => {
|
|
565
|
+
const subsetGlyphId = target.plan.glyphIdMap.get(glyphId) ?? panic(`subset dropped shaped glyph ${String(glyphId)}`);
|
|
566
|
+
return {
|
|
567
|
+
resourceIndex: target.resourceIndex,
|
|
568
|
+
glyphId: subsetGlyphId,
|
|
569
|
+
widthUnits: target.plan.widthByGlyphId.get(subsetGlyphId) ?? panic(`no width for glyph ${String(subsetGlyphId)}`),
|
|
570
|
+
clusterIndex: startIndex + clusterIndex,
|
|
571
|
+
xAdvancePx,
|
|
572
|
+
xOffsetPx,
|
|
573
|
+
yOffsetPx
|
|
574
|
+
};
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
const byCodeUnit = (left, right) => {
|
|
581
|
+
if (left === right) return 0;
|
|
582
|
+
return left < right ? -1 : 1;
|
|
583
|
+
};
|
|
584
|
+
unencodable.sort((left, right) => byCodeUnit(left.family, right.family) || left.weight - right.weight || Number(left.italic) - Number(right.italic) || left.codePoint - right.codePoint);
|
|
585
|
+
return {
|
|
586
|
+
byFontIndex,
|
|
587
|
+
fontRefByResourceIndex,
|
|
588
|
+
substitutions,
|
|
589
|
+
unencodable
|
|
590
|
+
};
|
|
591
|
+
};
|
|
592
|
+
//#endregion
|
|
593
|
+
export { prepareFonts };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { DisplayImageSource } from "../display-list/types.js";
|
|
2
|
+
import { PdfValue } from "./objects.js";
|
|
3
|
+
import { Result } from "better-result";
|
|
4
|
+
//#region src/pdf/images.d.ts
|
|
5
|
+
declare const PdfImageError_base: import("better-result").TaggedErrorClass<"PdfImageError">;
|
|
6
|
+
declare class PdfImageError extends PdfImageError_base<{
|
|
7
|
+
message: string;
|
|
8
|
+
}> {}
|
|
9
|
+
/** A soft mask: one alpha sample per pixel, matching the image's dimensions. */
|
|
10
|
+
type PdfImageAlpha = {
|
|
11
|
+
readonly data: Uint8Array;
|
|
12
|
+
readonly bitsPerComponent: number;
|
|
13
|
+
};
|
|
14
|
+
type PdfImageSamples = {
|
|
15
|
+
readonly widthPx: number;
|
|
16
|
+
readonly heightPx: number;
|
|
17
|
+
readonly bitsPerComponent: number;
|
|
18
|
+
readonly colorSpace: PdfValue;
|
|
19
|
+
/** `jpeg` is written as `/DCTDecode`; `raw` is deflated by the writer. */
|
|
20
|
+
readonly encoding: "raw" | "jpeg";
|
|
21
|
+
readonly data: Uint8Array;
|
|
22
|
+
/** Component inversion, for Adobe CMYK JPEGs. */
|
|
23
|
+
readonly decode?: readonly number[];
|
|
24
|
+
readonly alpha?: PdfImageAlpha;
|
|
25
|
+
};
|
|
26
|
+
declare const decodeImage: (source: DisplayImageSource) => Result<PdfImageSamples, PdfImageError>;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { PdfImageAlpha, PdfImageError, PdfImageSamples, decodeImage };
|