@stll/folio-core 0.32.2 → 0.33.1
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 +652 -86
- 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 +56 -0
- package/dist/compare/__fixtures__/body-sequence.js +136 -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 +412 -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 +1141 -0
- package/dist/compare/reproducible-package.d.ts +21 -0
- package/dist/compare/reproducible-package.js +47 -0
- package/dist/compare/scenario.d.ts +77 -0
- package/dist/compare/scenario.js +263 -0
- package/dist/compare/types.d.ts +297 -0
- package/dist/compare/types.js +42 -0
- package/dist/compare/verification.d.ts +83 -0
- package/dist/compare/verification.js +189 -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/hyperlinkParser.d.ts +9 -1
- package/dist/docx/hyperlinkParser.js +19 -13
- package/dist/docx/packageParts.d.ts +35 -0
- package/dist/docx/packageParts.js +120 -0
- package/dist/docx/paraIdRangeNormalization.d.ts +40 -0
- package/dist/docx/paraIdRangeNormalization.js +64 -0
- package/dist/docx/paragraphParser.js +101 -26
- package/dist/docx/paragraphTraversal.js +1 -1
- package/dist/docx/renderedPageBreakNormalization.js +3 -1
- package/dist/docx/revisionIdNormalization.d.ts +27 -0
- package/dist/docx/revisionIdNormalization.js +121 -0
- package/dist/docx/rezip.d.ts +6 -0
- package/dist/docx/rezip.js +51 -19
- package/dist/docx/selectiveSave.js +2 -2
- package/dist/docx/serializer/commentSerializer.js +6 -1
- package/dist/docx/serializer/paragraphSerializer.js +73 -20
- package/dist/docx/serializer/runSerializer.js +11 -10
- package/dist/docx/serializer/tableSerializer.js +26 -10
- 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 +96 -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,525 @@
|
|
|
1
|
+
import { SFNT_TABLE, SFNT_VERSION, fitsInView, readLocaOffsets, readSfntDirectory, readSfntVersion } from "./tables.js";
|
|
2
|
+
import { Result, TaggedError } from "better-result";
|
|
3
|
+
//#region src/fonts/sfnt/parse.ts
|
|
4
|
+
/**
|
|
5
|
+
* A self-contained TrueType/OpenType (sfnt) reader.
|
|
6
|
+
*
|
|
7
|
+
* It reads exactly what text measurement and PDF embedding need: the metrics
|
|
8
|
+
* head/hhea/OS-2 carry, a character-to-glyph lookup, and advance widths. It
|
|
9
|
+
* does not decode outlines (the subsetter copies `glyf` bytes verbatim) and it
|
|
10
|
+
* does not hint, shape or rasterize.
|
|
11
|
+
*
|
|
12
|
+
* Robustness contract: every offset and length is checked against the buffer
|
|
13
|
+
* before it is read, so a truncated or lying font yields an `Err` rather than
|
|
14
|
+
* a throw, and no field is ever `NaN`.
|
|
15
|
+
*/
|
|
16
|
+
var SfntParseError = class extends TaggedError("SfntParseError") {};
|
|
17
|
+
const fail = (message) => Result.err(new SfntParseError({ message }));
|
|
18
|
+
/** Byte offsets inside `head`. */
|
|
19
|
+
const HEAD = {
|
|
20
|
+
checkSumAdjustment: 8,
|
|
21
|
+
magicNumber: 12,
|
|
22
|
+
unitsPerEm: 18,
|
|
23
|
+
xMin: 36,
|
|
24
|
+
yMin: 38,
|
|
25
|
+
xMax: 40,
|
|
26
|
+
yMax: 42,
|
|
27
|
+
indexToLocFormat: 50,
|
|
28
|
+
size: 54
|
|
29
|
+
};
|
|
30
|
+
/** `head.magicNumber`, the one field that proves the table is really `head`. */
|
|
31
|
+
const HEAD_MAGIC = 1594834165;
|
|
32
|
+
const MIN_UNITS_PER_EM = 16;
|
|
33
|
+
const MAX_UNITS_PER_EM = 16384;
|
|
34
|
+
/** Byte offsets inside `hhea`. */
|
|
35
|
+
const HHEA = {
|
|
36
|
+
ascender: 4,
|
|
37
|
+
descender: 6,
|
|
38
|
+
lineGap: 8,
|
|
39
|
+
numberOfHMetrics: 34,
|
|
40
|
+
size: 36
|
|
41
|
+
};
|
|
42
|
+
/** Byte offsets inside `maxp` (only the half both versions share). */
|
|
43
|
+
const MAXP = {
|
|
44
|
+
numGlyphs: 4,
|
|
45
|
+
size: 6
|
|
46
|
+
};
|
|
47
|
+
/** Byte offsets inside `OS/2`. */
|
|
48
|
+
const OS2 = {
|
|
49
|
+
version: 0,
|
|
50
|
+
fsType: 8,
|
|
51
|
+
sxHeight: 86,
|
|
52
|
+
sCapHeight: 88,
|
|
53
|
+
/** Version 2 is the first that has sxHeight/sCapHeight at all. */
|
|
54
|
+
minimumVersionWithHeights: 2,
|
|
55
|
+
sizeWithHeights: 96
|
|
56
|
+
};
|
|
57
|
+
/** Byte offsets inside `post`. */
|
|
58
|
+
const POST = {
|
|
59
|
+
italicAngle: 4,
|
|
60
|
+
size: 32
|
|
61
|
+
};
|
|
62
|
+
/** 16.16 fixed-point, as `post.italicAngle` and table versions are stored. */
|
|
63
|
+
const FIXED_POINT_DIVISOR = 65536;
|
|
64
|
+
/** One hmtx `longHorMetric`: advanceWidth (uint16) then lsb (int16). */
|
|
65
|
+
const HMTX_METRIC_SIZE = 4;
|
|
66
|
+
/** Byte offsets inside a glyph description header. */
|
|
67
|
+
const GLYPH_HEADER = {
|
|
68
|
+
numberOfContours: 0,
|
|
69
|
+
xMin: 2,
|
|
70
|
+
yMin: 4,
|
|
71
|
+
xMax: 6,
|
|
72
|
+
yMax: 8,
|
|
73
|
+
size: 10
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Cap and x-height fallbacks for fonts without OS/2 version 2 whose 'H' and
|
|
77
|
+
* 'x' cannot be measured (no cmap, no outline, or CFF outlines this reader
|
|
78
|
+
* does not decode). Ratios of the em, rounded, so measurement stays sane.
|
|
79
|
+
*/
|
|
80
|
+
const CAP_HEIGHT_EM_RATIO = .7;
|
|
81
|
+
const X_HEIGHT_EM_RATIO = .5;
|
|
82
|
+
const CAP_HEIGHT_SAMPLE = 72;
|
|
83
|
+
const X_HEIGHT_SAMPLE = 120;
|
|
84
|
+
const NAME = {
|
|
85
|
+
count: 2,
|
|
86
|
+
stringOffset: 4,
|
|
87
|
+
recordsStart: 6,
|
|
88
|
+
recordSize: 12,
|
|
89
|
+
size: 6
|
|
90
|
+
};
|
|
91
|
+
const NAME_RECORD = {
|
|
92
|
+
platformId: 0,
|
|
93
|
+
encodingId: 2,
|
|
94
|
+
languageId: 4,
|
|
95
|
+
nameId: 6,
|
|
96
|
+
length: 8,
|
|
97
|
+
offset: 10
|
|
98
|
+
};
|
|
99
|
+
/** name id 6 is the PostScript name. */
|
|
100
|
+
const POSTSCRIPT_NAME_ID = 6;
|
|
101
|
+
const PLATFORM = {
|
|
102
|
+
unicode: 0,
|
|
103
|
+
macintosh: 1,
|
|
104
|
+
windows: 3
|
|
105
|
+
};
|
|
106
|
+
const WINDOWS_ENCODING = {
|
|
107
|
+
symbol: 0,
|
|
108
|
+
bmp: 1,
|
|
109
|
+
full: 10
|
|
110
|
+
};
|
|
111
|
+
const CMAP = {
|
|
112
|
+
numTables: 2,
|
|
113
|
+
recordsStart: 4,
|
|
114
|
+
recordSize: 8
|
|
115
|
+
};
|
|
116
|
+
const CMAP_RECORD = {
|
|
117
|
+
platformId: 0,
|
|
118
|
+
encodingId: 2,
|
|
119
|
+
offset: 4
|
|
120
|
+
};
|
|
121
|
+
const CMAP_FORMAT = {
|
|
122
|
+
/** Segment mapping to delta values: the BMP workhorse. */
|
|
123
|
+
segmentMapping: 4,
|
|
124
|
+
/** Trimmed table mapping: a single contiguous run. */
|
|
125
|
+
trimmedTable: 6,
|
|
126
|
+
/** Segmented coverage: the full Unicode range, uint32 code points. */
|
|
127
|
+
segmentedCoverage: 12
|
|
128
|
+
};
|
|
129
|
+
/** Byte offsets inside a format 4 subtable. */
|
|
130
|
+
const FORMAT_4 = {
|
|
131
|
+
segCountX2: 6,
|
|
132
|
+
endCodes: 14,
|
|
133
|
+
headerSize: 14
|
|
134
|
+
};
|
|
135
|
+
/** Byte offsets inside a format 6 subtable. */
|
|
136
|
+
const FORMAT_6 = {
|
|
137
|
+
firstCode: 6,
|
|
138
|
+
entryCount: 8,
|
|
139
|
+
glyphIds: 10,
|
|
140
|
+
headerSize: 10
|
|
141
|
+
};
|
|
142
|
+
/** Byte offsets inside a format 12 subtable. */
|
|
143
|
+
const FORMAT_12 = {
|
|
144
|
+
numGroups: 12,
|
|
145
|
+
groups: 16,
|
|
146
|
+
headerSize: 16,
|
|
147
|
+
groupSize: 12
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* Symbol fonts (platform 3, encoding 0) map their glyphs into the private use
|
|
151
|
+
* area, so a lookup for 'A' has to be retried as `0xF000 | 'A'`.
|
|
152
|
+
*/
|
|
153
|
+
const SYMBOL_CODEPOINT_BASE = 61440;
|
|
154
|
+
/**
|
|
155
|
+
* Ceiling on entries materialized from a format 6/12 subtable. Glyph ids are
|
|
156
|
+
* uint16, so an honest font cannot map more code points than this; a font that
|
|
157
|
+
* claims to is lying about its group ranges and must not be allowed to
|
|
158
|
+
* allocate against it.
|
|
159
|
+
*/
|
|
160
|
+
const MAX_CMAP_MAPPINGS = 1 << 18;
|
|
161
|
+
const MAX_UNICODE_CODE_POINT = 1114111;
|
|
162
|
+
/** Reads `count` big-endian uint16s into a typed array. Bounds pre-checked. */
|
|
163
|
+
const readUint16Array = (view, offset, count) => {
|
|
164
|
+
const values = new Uint16Array(count);
|
|
165
|
+
for (let index = 0; index < count; index++) values[index] = view.getUint16(offset + index * 2);
|
|
166
|
+
return values;
|
|
167
|
+
};
|
|
168
|
+
const readInt16Array = (view, offset, count) => {
|
|
169
|
+
const values = new Int16Array(count);
|
|
170
|
+
for (let index = 0; index < count; index++) values[index] = view.getInt16(offset + index * 2);
|
|
171
|
+
return values;
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Format 4: four parallel segment arrays plus a trailing glyph id array that
|
|
175
|
+
* segments address by a byte offset from their own `idRangeOffset` slot. The
|
|
176
|
+
* arrays are read once here; lookup is a binary search over `endCodes`.
|
|
177
|
+
*/
|
|
178
|
+
const buildFormat4Lookup = ({ view, start, end }) => {
|
|
179
|
+
if (!fitsInView(view, start, FORMAT_4.headerSize) || start + FORMAT_4.headerSize > end) return;
|
|
180
|
+
const segCountX2 = view.getUint16(start + FORMAT_4.segCountX2);
|
|
181
|
+
const segCount = segCountX2 >> 1;
|
|
182
|
+
if (segCount === 0) return;
|
|
183
|
+
const endCodesBase = start + FORMAT_4.endCodes;
|
|
184
|
+
const startCodesBase = endCodesBase + segCountX2 + 2;
|
|
185
|
+
const idDeltasBase = startCodesBase + segCountX2;
|
|
186
|
+
const idRangeOffsetsBase = idDeltasBase + segCountX2;
|
|
187
|
+
const arraysSize = segCountX2 * 4 + 2;
|
|
188
|
+
if (!fitsInView(view, endCodesBase, arraysSize) || endCodesBase + arraysSize > end) return;
|
|
189
|
+
const endCodes = readUint16Array(view, endCodesBase, segCount);
|
|
190
|
+
const startCodes = readUint16Array(view, startCodesBase, segCount);
|
|
191
|
+
const idDeltas = readInt16Array(view, idDeltasBase, segCount);
|
|
192
|
+
const idRangeOffsets = readUint16Array(view, idRangeOffsetsBase, segCount);
|
|
193
|
+
return (codePoint) => {
|
|
194
|
+
if (codePoint < 0 || codePoint > 65535) return 0;
|
|
195
|
+
let low = 0;
|
|
196
|
+
let high = segCount - 1;
|
|
197
|
+
let segment = -1;
|
|
198
|
+
while (low <= high) {
|
|
199
|
+
const middle = low + high >> 1;
|
|
200
|
+
if ((endCodes[middle] ?? 0) < codePoint) low = middle + 1;
|
|
201
|
+
else {
|
|
202
|
+
segment = middle;
|
|
203
|
+
high = middle - 1;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (segment < 0) return 0;
|
|
207
|
+
const segmentStart = startCodes[segment] ?? 0;
|
|
208
|
+
if (segmentStart > codePoint) return 0;
|
|
209
|
+
const delta = idDeltas[segment] ?? 0;
|
|
210
|
+
const rangeOffset = idRangeOffsets[segment] ?? 0;
|
|
211
|
+
if (rangeOffset === 0) return codePoint + delta & 65535;
|
|
212
|
+
const address = idRangeOffsetsBase + segment * 2 + rangeOffset + (codePoint - segmentStart) * 2;
|
|
213
|
+
if (!fitsInView(view, address, 2) || address + 2 > end) return 0;
|
|
214
|
+
const glyphId = view.getUint16(address);
|
|
215
|
+
return glyphId === 0 ? 0 : glyphId + delta & 65535;
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
/** Format 6: one contiguous run of glyph ids, materialized eagerly. */
|
|
219
|
+
const buildFormat6Lookup = ({ view, start, end }) => {
|
|
220
|
+
if (!fitsInView(view, start, FORMAT_6.headerSize) || start + FORMAT_6.headerSize > end) return;
|
|
221
|
+
const firstCode = view.getUint16(start + FORMAT_6.firstCode);
|
|
222
|
+
const entryCount = view.getUint16(start + FORMAT_6.entryCount);
|
|
223
|
+
const glyphIdsBase = start + FORMAT_6.glyphIds;
|
|
224
|
+
if (!fitsInView(view, glyphIdsBase, entryCount * 2) || glyphIdsBase + entryCount * 2 > end) return;
|
|
225
|
+
const mappings = /* @__PURE__ */ new Map();
|
|
226
|
+
for (let index = 0; index < entryCount; index++) {
|
|
227
|
+
const glyphId = view.getUint16(glyphIdsBase + index * 2);
|
|
228
|
+
if (glyphId !== 0) mappings.set(firstCode + index, glyphId);
|
|
229
|
+
}
|
|
230
|
+
return (codePoint) => mappings.get(codePoint) ?? 0;
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* Format 12: sorted `(startCharCode, endCharCode, startGlyphID)` groups. Every
|
|
234
|
+
* group is expanded into the map up to {@link MAX_CMAP_MAPPINGS}, so lookup is
|
|
235
|
+
* a hash hit rather than a per-character binary search.
|
|
236
|
+
*/
|
|
237
|
+
const buildFormat12Lookup = ({ view, start, end }) => {
|
|
238
|
+
if (!fitsInView(view, start, FORMAT_12.headerSize) || start + FORMAT_12.headerSize > end) return;
|
|
239
|
+
const numGroups = view.getUint32(start + FORMAT_12.numGroups);
|
|
240
|
+
const groupsBase = start + FORMAT_12.groups;
|
|
241
|
+
const groupsSize = numGroups * FORMAT_12.groupSize;
|
|
242
|
+
if (!fitsInView(view, groupsBase, groupsSize) || groupsBase + groupsSize > end) return;
|
|
243
|
+
const mappings = /* @__PURE__ */ new Map();
|
|
244
|
+
for (let index = 0; index < numGroups; index++) {
|
|
245
|
+
const group = groupsBase + index * FORMAT_12.groupSize;
|
|
246
|
+
const startCharCode = view.getUint32(group);
|
|
247
|
+
const endCharCode = view.getUint32(group + 4);
|
|
248
|
+
const startGlyphId = view.getUint32(group + 8);
|
|
249
|
+
if (endCharCode < startCharCode || endCharCode > MAX_UNICODE_CODE_POINT) return;
|
|
250
|
+
if (mappings.size + (endCharCode - startCharCode + 1) > MAX_CMAP_MAPPINGS) return;
|
|
251
|
+
for (let codePoint = startCharCode; codePoint <= endCharCode; codePoint++) {
|
|
252
|
+
const glyphId = startGlyphId + (codePoint - startCharCode);
|
|
253
|
+
if (glyphId !== 0) mappings.set(codePoint, glyphId);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return (codePoint) => mappings.get(codePoint) ?? 0;
|
|
257
|
+
};
|
|
258
|
+
const buildLookup = (subtable) => {
|
|
259
|
+
switch (subtable.format) {
|
|
260
|
+
case CMAP_FORMAT.segmentMapping: return buildFormat4Lookup(subtable);
|
|
261
|
+
case CMAP_FORMAT.trimmedTable: return buildFormat6Lookup(subtable);
|
|
262
|
+
case CMAP_FORMAT.segmentedCoverage: return buildFormat12Lookup(subtable);
|
|
263
|
+
default: return;
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
/**
|
|
267
|
+
* Subtable preference, most specific first. Order is the whole contract here:
|
|
268
|
+
* a full-coverage Windows table beats the BMP one, a Unicode-platform table
|
|
269
|
+
* beats the symbol encoding, and the symbol encoding is the only one whose
|
|
270
|
+
* lookup retries in the private use area.
|
|
271
|
+
*/
|
|
272
|
+
const CMAP_PREFERENCES = [
|
|
273
|
+
{
|
|
274
|
+
matches: (subtable) => subtable.platformId === PLATFORM.windows && subtable.encodingId === WINDOWS_ENCODING.full && subtable.format === CMAP_FORMAT.segmentedCoverage,
|
|
275
|
+
symbol: false
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
matches: (subtable) => subtable.platformId === PLATFORM.windows && subtable.encodingId === WINDOWS_ENCODING.bmp && subtable.format === CMAP_FORMAT.segmentMapping,
|
|
279
|
+
symbol: false
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
matches: (subtable) => subtable.platformId === PLATFORM.unicode && (subtable.format === CMAP_FORMAT.segmentedCoverage || subtable.format === CMAP_FORMAT.segmentMapping),
|
|
283
|
+
symbol: false
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
matches: (subtable) => subtable.platformId === PLATFORM.windows && subtable.encodingId === WINDOWS_ENCODING.symbol && subtable.format === CMAP_FORMAT.segmentMapping,
|
|
287
|
+
symbol: true
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
matches: (subtable) => subtable.platformId === PLATFORM.macintosh || subtable.platformId === PLATFORM.unicode,
|
|
291
|
+
symbol: false
|
|
292
|
+
}
|
|
293
|
+
];
|
|
294
|
+
const listCmapSubtables = (view, cmap) => {
|
|
295
|
+
const end = cmap.offset + cmap.length;
|
|
296
|
+
if (!fitsInView(view, cmap.offset, CMAP.recordsStart)) return [];
|
|
297
|
+
const numTables = view.getUint16(cmap.offset + CMAP.numTables);
|
|
298
|
+
const subtables = [];
|
|
299
|
+
for (let index = 0; index < numTables; index++) {
|
|
300
|
+
const record = cmap.offset + CMAP.recordsStart + index * CMAP.recordSize;
|
|
301
|
+
if (record + CMAP.recordSize > end || !fitsInView(view, record, CMAP.recordSize)) break;
|
|
302
|
+
const start = cmap.offset + view.getUint32(record + CMAP_RECORD.offset);
|
|
303
|
+
if (!fitsInView(view, start, 2) || start + 2 > end) continue;
|
|
304
|
+
subtables.push({
|
|
305
|
+
platformId: view.getUint16(record + CMAP_RECORD.platformId),
|
|
306
|
+
encodingId: view.getUint16(record + CMAP_RECORD.encodingId),
|
|
307
|
+
format: view.getUint16(start),
|
|
308
|
+
start,
|
|
309
|
+
end,
|
|
310
|
+
view
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
return subtables;
|
|
314
|
+
};
|
|
315
|
+
/**
|
|
316
|
+
* Picks the best subtable the preference order admits and builds its lookup
|
|
317
|
+
* once. A `cmap` that exists but yields no usable subtable is an error: the
|
|
318
|
+
* alternative is a font that silently maps every character to .notdef.
|
|
319
|
+
*/
|
|
320
|
+
const buildCmapLookup = (view, cmap) => {
|
|
321
|
+
const subtables = listCmapSubtables(view, cmap);
|
|
322
|
+
for (const preference of CMAP_PREFERENCES) for (const subtable of subtables) {
|
|
323
|
+
if (!preference.matches(subtable)) continue;
|
|
324
|
+
const lookup = buildLookup(subtable);
|
|
325
|
+
if (!lookup) continue;
|
|
326
|
+
if (!preference.symbol) return Result.ok(lookup);
|
|
327
|
+
return Result.ok((codePoint) => {
|
|
328
|
+
const glyphId = lookup(codePoint);
|
|
329
|
+
return glyphId === 0 ? lookup(SYMBOL_CODEPOINT_BASE | codePoint) : glyphId;
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
const formats = subtables.map((subtable) => subtable.format).join(", ");
|
|
333
|
+
return fail(`cmap has no subtable this reader can use (found ${subtables.length} subtable(s), format(s): ${formats || "none"})`);
|
|
334
|
+
};
|
|
335
|
+
/** Strips the delimiters a PDF name cannot carry and caps the spec's length. */
|
|
336
|
+
const MAX_POSTSCRIPT_NAME_LENGTH = 63;
|
|
337
|
+
const POSTSCRIPT_NAME_DELIMITERS = /* @__PURE__ */ new Set("()<>[]{}/% ");
|
|
338
|
+
const sanitizePostScriptName = (raw) => {
|
|
339
|
+
let name = "";
|
|
340
|
+
for (const character of raw) {
|
|
341
|
+
const code = character.codePointAt(0) ?? 0;
|
|
342
|
+
if (code > 32 && code < 127 && !POSTSCRIPT_NAME_DELIMITERS.has(character)) name += character;
|
|
343
|
+
}
|
|
344
|
+
return name.slice(0, MAX_POSTSCRIPT_NAME_LENGTH);
|
|
345
|
+
};
|
|
346
|
+
const decodeUtf16Be = (view, offset, length) => {
|
|
347
|
+
let text = "";
|
|
348
|
+
for (let index = 0; index + 1 < length; index += 2) text += String.fromCharCode(view.getUint16(offset + index));
|
|
349
|
+
return text;
|
|
350
|
+
};
|
|
351
|
+
const decodeMacRomanAscii = (view, offset, length) => {
|
|
352
|
+
let text = "";
|
|
353
|
+
for (let index = 0; index < length; index++) {
|
|
354
|
+
const code = view.getUint8(offset + index);
|
|
355
|
+
text += code < 128 ? String.fromCharCode(code) : "";
|
|
356
|
+
}
|
|
357
|
+
return text;
|
|
358
|
+
};
|
|
359
|
+
/**
|
|
360
|
+
* name id 6, preferring the Windows UTF-16BE record over the Macintosh one.
|
|
361
|
+
* Returns `""` when the table is absent, unreadable, or carries no usable
|
|
362
|
+
* PostScript name; the caller substitutes a synthetic name.
|
|
363
|
+
*/
|
|
364
|
+
const readPostScriptName = (view, name) => {
|
|
365
|
+
const end = name.offset + name.length;
|
|
366
|
+
if (!fitsInView(view, name.offset, NAME.size)) return "";
|
|
367
|
+
const count = view.getUint16(name.offset + NAME.count);
|
|
368
|
+
const stringsBase = name.offset + view.getUint16(name.offset + NAME.stringOffset);
|
|
369
|
+
let windows = "";
|
|
370
|
+
let macintosh = "";
|
|
371
|
+
for (let index = 0; index < count; index++) {
|
|
372
|
+
const record = name.offset + NAME.recordsStart + index * NAME.recordSize;
|
|
373
|
+
if (record + NAME.recordSize > end || !fitsInView(view, record, NAME.recordSize)) break;
|
|
374
|
+
if (view.getUint16(record + NAME_RECORD.nameId) !== POSTSCRIPT_NAME_ID) continue;
|
|
375
|
+
const platformId = view.getUint16(record + NAME_RECORD.platformId);
|
|
376
|
+
const length = view.getUint16(record + NAME_RECORD.length);
|
|
377
|
+
const offset = stringsBase + view.getUint16(record + NAME_RECORD.offset);
|
|
378
|
+
if (!fitsInView(view, offset, length) || offset + length > end) continue;
|
|
379
|
+
if (platformId === PLATFORM.windows && windows === "") windows = decodeUtf16Be(view, offset, length);
|
|
380
|
+
else if (platformId === PLATFORM.macintosh && macintosh === "") macintosh = decodeMacRomanAscii(view, offset, length);
|
|
381
|
+
}
|
|
382
|
+
return sanitizePostScriptName(windows) || sanitizePostScriptName(macintosh);
|
|
383
|
+
};
|
|
384
|
+
/**
|
|
385
|
+
* A stable stand-in when the font carries no PostScript name. Derived from
|
|
386
|
+
* `head.checkSumAdjustment`, which is a function of the whole file, so the
|
|
387
|
+
* same bytes always produce the same name and two different faces do not
|
|
388
|
+
* collide in a PDF resource dictionary.
|
|
389
|
+
*/
|
|
390
|
+
const syntheticPostScriptName = (checkSumAdjustment) => `SfntFont-${checkSumAdjustment.toString(16).toUpperCase().padStart(8, "0")}`;
|
|
391
|
+
/**
|
|
392
|
+
* Builds the `glyphBoundsFor` accessor.
|
|
393
|
+
*
|
|
394
|
+
* A composite glyph carries its own bounds in the same header fields as a
|
|
395
|
+
* simple one, so no component recursion is needed. A CFF font returns null
|
|
396
|
+
* for every glyph: charstring interpretation is out of scope, and the caller
|
|
397
|
+
* has to fall back on the hhea metrics.
|
|
398
|
+
*/
|
|
399
|
+
const buildGlyphBoundsReader = ({ directory, numGlyphs, longLoca }) => {
|
|
400
|
+
const loca = directory.tables.get(SFNT_TABLE.loca);
|
|
401
|
+
const glyf = directory.tables.get(SFNT_TABLE.glyf);
|
|
402
|
+
const offsets = loca && glyf ? readLocaOffsets({
|
|
403
|
+
view: directory.view,
|
|
404
|
+
loca,
|
|
405
|
+
numGlyphs,
|
|
406
|
+
longFormat: longLoca
|
|
407
|
+
}) : void 0;
|
|
408
|
+
if (!glyf || !offsets) return () => null;
|
|
409
|
+
const { view } = directory;
|
|
410
|
+
return (glyphId) => {
|
|
411
|
+
if (!Number.isInteger(glyphId) || glyphId < 0 || glyphId >= numGlyphs) return null;
|
|
412
|
+
const start = offsets[glyphId];
|
|
413
|
+
const end = offsets[glyphId + 1];
|
|
414
|
+
if (start === void 0 || end === void 0 || end - start < GLYPH_HEADER.size) return null;
|
|
415
|
+
const at = glyf.offset + start;
|
|
416
|
+
if (!fitsInView(view, at, GLYPH_HEADER.size) || start + GLYPH_HEADER.size > glyf.length) return null;
|
|
417
|
+
return {
|
|
418
|
+
xMin: view.getInt16(at + GLYPH_HEADER.xMin),
|
|
419
|
+
yMin: view.getInt16(at + GLYPH_HEADER.yMin),
|
|
420
|
+
xMax: view.getInt16(at + GLYPH_HEADER.xMax),
|
|
421
|
+
yMax: view.getInt16(at + GLYPH_HEADER.yMax)
|
|
422
|
+
};
|
|
423
|
+
};
|
|
424
|
+
};
|
|
425
|
+
/** Requires a table to exist and to be at least `size` bytes long. */
|
|
426
|
+
const requireTable = (directory, tag, size) => {
|
|
427
|
+
const record = directory.tables.get(tag);
|
|
428
|
+
if (!record) return fail(`font has no '${tag}' table`);
|
|
429
|
+
if (record.length < size || !fitsInView(directory.view, record.offset, size)) return fail(`'${tag}' table is ${record.length} bytes, needs at least ${size}`);
|
|
430
|
+
return Result.ok(record);
|
|
431
|
+
};
|
|
432
|
+
const rejectContainer = (version) => {
|
|
433
|
+
switch (version) {
|
|
434
|
+
case SFNT_VERSION.collection: return fail("font collections ('ttcf') are not supported; extract a single face first");
|
|
435
|
+
case SFNT_VERSION.woff: return fail("this is a WOFF container; decode it with decodeWoff before parsing");
|
|
436
|
+
case SFNT_VERSION.woff2: return fail("this is a WOFF2 container; it must be decoded before parsing");
|
|
437
|
+
case SFNT_VERSION.trueType:
|
|
438
|
+
case SFNT_VERSION.trueMac:
|
|
439
|
+
case SFNT_VERSION.openTypeCff: return Result.ok();
|
|
440
|
+
default: return fail(`unrecognized sfnt version 0x${(version >>> 0).toString(16).padStart(8, "0")}`);
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
const parseSfnt = (bytes) => {
|
|
444
|
+
const version = readSfntVersion(bytes);
|
|
445
|
+
if (version === void 0) return fail(`buffer is ${bytes.byteLength} bytes, too short to be a font`);
|
|
446
|
+
const container = rejectContainer(version);
|
|
447
|
+
if (container.isErr()) return Result.err(container.error);
|
|
448
|
+
const directoryResult = readSfntDirectory(bytes).mapError((error) => new SfntParseError({ message: error.message }));
|
|
449
|
+
if (directoryResult.isErr()) return Result.err(directoryResult.error);
|
|
450
|
+
const directory = directoryResult.value;
|
|
451
|
+
const { view } = directory;
|
|
452
|
+
const headResult = requireTable(directory, SFNT_TABLE.head, HEAD.size);
|
|
453
|
+
if (headResult.isErr()) return Result.err(headResult.error);
|
|
454
|
+
const head = headResult.value;
|
|
455
|
+
if (view.getUint32(head.offset + HEAD.magicNumber) !== HEAD_MAGIC) return fail("'head' table has the wrong magic number");
|
|
456
|
+
const unitsPerEm = view.getUint16(head.offset + HEAD.unitsPerEm);
|
|
457
|
+
if (unitsPerEm < MIN_UNITS_PER_EM || unitsPerEm > MAX_UNITS_PER_EM) return fail(`head.unitsPerEm is ${unitsPerEm}, outside the permitted 16..16384`);
|
|
458
|
+
const hheaResult = requireTable(directory, SFNT_TABLE.hhea, HHEA.size);
|
|
459
|
+
if (hheaResult.isErr()) return Result.err(hheaResult.error);
|
|
460
|
+
const hhea = hheaResult.value;
|
|
461
|
+
const maxpResult = requireTable(directory, SFNT_TABLE.maxp, MAXP.size);
|
|
462
|
+
if (maxpResult.isErr()) return Result.err(maxpResult.error);
|
|
463
|
+
const numGlyphs = view.getUint16(maxpResult.value.offset + MAXP.numGlyphs);
|
|
464
|
+
if (numGlyphs === 0) return fail("maxp.numGlyphs is 0");
|
|
465
|
+
const numberOfHMetrics = view.getUint16(hhea.offset + HHEA.numberOfHMetrics);
|
|
466
|
+
if (numberOfHMetrics === 0 || numberOfHMetrics > numGlyphs) return fail(`hhea.numberOfHMetrics is ${numberOfHMetrics}, which is not in 1..${numGlyphs} (maxp.numGlyphs)`);
|
|
467
|
+
const hmtxResult = requireTable(directory, SFNT_TABLE.hmtx, numberOfHMetrics * HMTX_METRIC_SIZE);
|
|
468
|
+
if (hmtxResult.isErr()) return Result.err(hmtxResult.error);
|
|
469
|
+
const hmtx = hmtxResult.value;
|
|
470
|
+
const cmap = directory.tables.get(SFNT_TABLE.cmap);
|
|
471
|
+
const unmappedLookup = () => 0;
|
|
472
|
+
const lookupResult = cmap ? buildCmapLookup(view, cmap) : Result.ok(unmappedLookup);
|
|
473
|
+
if (lookupResult.isErr()) return Result.err(lookupResult.error);
|
|
474
|
+
const lookup = lookupResult.value;
|
|
475
|
+
const glyphIdFor = (codePoint) => {
|
|
476
|
+
if (!Number.isInteger(codePoint) || codePoint < 0 || codePoint > MAX_UNICODE_CODE_POINT) return 0;
|
|
477
|
+
const glyphId = lookup(codePoint);
|
|
478
|
+
return glyphId < numGlyphs ? glyphId : 0;
|
|
479
|
+
};
|
|
480
|
+
const advanceWidthFor = (glyphId) => {
|
|
481
|
+
if (!Number.isInteger(glyphId) || glyphId < 0) return 0;
|
|
482
|
+
const index = Math.min(glyphId, numberOfHMetrics - 1);
|
|
483
|
+
return view.getUint16(hmtx.offset + index * HMTX_METRIC_SIZE);
|
|
484
|
+
};
|
|
485
|
+
const longLoca = view.getInt16(head.offset + HEAD.indexToLocFormat) === 1;
|
|
486
|
+
const glyphBoundsFor = buildGlyphBoundsReader({
|
|
487
|
+
directory,
|
|
488
|
+
numGlyphs,
|
|
489
|
+
longLoca
|
|
490
|
+
});
|
|
491
|
+
const os2 = directory.tables.get(SFNT_TABLE.os2);
|
|
492
|
+
const os2HasHeights = os2 !== void 0 && os2.length >= OS2.sizeWithHeights && fitsInView(view, os2.offset, OS2.sizeWithHeights) && view.getUint16(os2.offset + OS2.version) >= OS2.minimumVersionWithHeights;
|
|
493
|
+
const capHeight = os2HasHeights ? view.getInt16(os2.offset + OS2.sCapHeight) : glyphBoundsFor(glyphIdFor(CAP_HEIGHT_SAMPLE))?.yMax ?? Math.round(unitsPerEm * CAP_HEIGHT_EM_RATIO);
|
|
494
|
+
const xHeight = os2HasHeights ? view.getInt16(os2.offset + OS2.sxHeight) : glyphBoundsFor(glyphIdFor(X_HEIGHT_SAMPLE))?.yMax ?? Math.round(unitsPerEm * X_HEIGHT_EM_RATIO);
|
|
495
|
+
const fsType = os2 !== void 0 && os2.length >= OS2.fsType + 2 && fitsInView(view, os2.offset, OS2.fsType + 2) ? view.getUint16(os2.offset + OS2.fsType) : 0;
|
|
496
|
+
const post = directory.tables.get(SFNT_TABLE.post);
|
|
497
|
+
const italicAngle = post !== void 0 && post.length >= POST.size && fitsInView(view, post.offset, POST.size) ? view.getInt32(post.offset + POST.italicAngle) / FIXED_POINT_DIVISOR : 0;
|
|
498
|
+
const name = directory.tables.get(SFNT_TABLE.name);
|
|
499
|
+
const postScriptName = (name ? readPostScriptName(view, name) : "") || syntheticPostScriptName(view.getUint32(head.offset + HEAD.checkSumAdjustment));
|
|
500
|
+
return Result.ok({
|
|
501
|
+
unitsPerEm,
|
|
502
|
+
ascender: view.getInt16(hhea.offset + HHEA.ascender),
|
|
503
|
+
descender: view.getInt16(hhea.offset + HHEA.descender),
|
|
504
|
+
lineGap: view.getInt16(hhea.offset + HHEA.lineGap),
|
|
505
|
+
capHeight,
|
|
506
|
+
xHeight,
|
|
507
|
+
italicAngle,
|
|
508
|
+
bbox: [
|
|
509
|
+
view.getInt16(head.offset + HEAD.xMin),
|
|
510
|
+
view.getInt16(head.offset + HEAD.yMin),
|
|
511
|
+
view.getInt16(head.offset + HEAD.xMax),
|
|
512
|
+
view.getInt16(head.offset + HEAD.yMax)
|
|
513
|
+
],
|
|
514
|
+
postScriptName,
|
|
515
|
+
isCff: directory.tables.has(SFNT_TABLE.cff),
|
|
516
|
+
numGlyphs,
|
|
517
|
+
fsType,
|
|
518
|
+
bytes,
|
|
519
|
+
glyphIdFor,
|
|
520
|
+
advanceWidthFor,
|
|
521
|
+
glyphBoundsFor
|
|
522
|
+
});
|
|
523
|
+
};
|
|
524
|
+
//#endregion
|
|
525
|
+
export { SfntParseError, parseSfnt };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SfntFont } from "./parse.js";
|
|
2
|
+
import { Result } from "better-result";
|
|
3
|
+
//#region src/fonts/sfnt/subset.d.ts
|
|
4
|
+
declare const SubsetError_base: import("better-result").TaggedErrorClass<"SubsetError">;
|
|
5
|
+
declare class SubsetError extends SubsetError_base<{
|
|
6
|
+
message: string;
|
|
7
|
+
}> {}
|
|
8
|
+
type TrueTypeSubset = {
|
|
9
|
+
/** Subset sfnt bytes. */
|
|
10
|
+
readonly bytes: Uint8Array;
|
|
11
|
+
/** Old glyph id -> new glyph id. Always maps 0 -> 0. */
|
|
12
|
+
readonly glyphIdMap: ReadonlyMap<number, number>;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Build a `glyf`-flavoured subset containing `glyphIds` plus the components
|
|
16
|
+
* they reference transitively, plus .notdef.
|
|
17
|
+
*/
|
|
18
|
+
declare const subsetTrueType: (font: SfntFont, glyphIds: ReadonlySet<number>) => Result<TrueTypeSubset, SubsetError>;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { SubsetError, TrueTypeSubset, subsetTrueType };
|