pdf-codec 4.1.0 → 4.3.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/README.md +3 -3
- package/dist/content-write.d.cts +1 -1
- package/dist/content-write.d.ts +1 -1
- package/dist/{embedded-font-fREdbxnr.d.ts → embedded-font-B4p9a3O3.d.ts} +3 -1
- package/dist/{embedded-font-BU6Jgcof.d.cts → embedded-font-oc65VGlr.d.cts} +3 -1
- package/dist/embedded-font-write.d.cts +2 -2
- package/dist/embedded-font-write.d.ts +2 -2
- package/dist/embedded-font.cjs +46 -15
- package/dist/embedded-font.d.cts +1 -1
- package/dist/embedded-font.d.ts +1 -1
- package/dist/embedded-font.js +46 -15
- package/dist/font-registry.d.cts +1 -1
- package/dist/font-registry.d.ts +1 -1
- package/dist/gsub-table-BRTQ8EUW.d.ts +10 -0
- package/dist/gsub-table-URczLd85.d.cts +10 -0
- package/dist/gsub-table.cjs +225 -0
- package/dist/gsub-table.d.cts +2 -0
- package/dist/gsub-table.d.ts +2 -0
- package/dist/gsub-table.js +224 -0
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/math-font-write.cjs +1 -1
- package/dist/math-font-write.js +1 -1
- package/dist/sfnt-subset.cjs +10 -3
- package/dist/sfnt-subset.d.cts +1 -1
- package/dist/sfnt-subset.d.ts +1 -1
- package/dist/sfnt-subset.js +10 -3
- package/dist/tounicode.cjs +8 -3
- package/dist/tounicode.d.cts +1 -1
- package/dist/tounicode.d.ts +1 -1
- package/dist/tounicode.js +8 -3
- package/dist/write.cjs +97 -11
- package/dist/write.d.cts +1 -1
- package/dist/write.d.ts +1 -1
- package/dist/write.js +98 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -272,9 +272,9 @@ Dependency direction is strictly downward and checkable: `layout` imports only `
|
|
|
272
272
|
- **Real per-glyph ink bounds are measured from the outline (`inkAscentPt`/`inkDescentPt`), computed by walking each glyph's Type 2 charstring.** `ascentPerEm`/`descentPerEm` remain alongside them as the uniform face-wide figure, still the right measure for anything sized against the font rather than particular characters, and the fallback for a glyph with no outline to measure. An ink box is genuinely tight, which for a math font is often _larger_ than the nominal metrics (over a tenth of STIX Two Math's repertoire draws above its nominal ascent). `inkDescentPt` is negative where the glyph's lowest ink sits above the baseline.
|
|
273
273
|
- **A `LayoutLine`/`LayoutPath` `style` of `dashed`/`dotted` becomes a real dash-array (`d`) operator scaled to the stroke's own width; `double` becomes two genuinely separate offset strokes.** Dash lengths are stroke-width multiples so a hairline and a thick rule both read as recognisably dashed: `dashed` emits `[3w 3w] 0 d`, `dotted` emits `[0 2w] 0 d` with a `1 J` round cap (the zero on-length under a round cap paints a filled circle — exactly a dot; under PDF's default butt cap it paints nothing). Both are reset immediately after the paint operator (`[] 0 d`, `0 J`) since the graphics state persists for the whole content stream. `double` has no PDF operator and is drawn as geometry: width `w` splits into three equal bands, each rule `w/3` wide with centreline `w/3` offset, outer edges matching the single stroke's. The read side tracks the `d` operator's own dash array as graphics state and recovers `dashed`/`dotted` back onto a stroked `LayoutLine`/`LayoutPath` from it — a zero on-length reads as `dotted`, any other non-empty array as `dashed`, an empty array (or none at all) as `solid`. `double` has no operator of its own to recover from, so it reads back as whatever geometry its two offset strokes actually are (typically two plain solid lines or paths), not as `double`.
|
|
274
274
|
- **An embedded `CIDFontType2` program needs no `cmap` table of its own**, and `sfnt-subset.ts`'s output doesn't carry one — character code → CID goes through the `Type0` font's `/Encoding` (Identity-H, so CID == character code), and CID → GID through `/CIDToGIDMap /Identity` (matching the GID-preserving design). Both happen inside the PDF's object graph, before the embedded font program is consulted (ISO 32000-1 9.7.4.2).
|
|
275
|
-
- **`GPOS` pair kerning
|
|
275
|
+
- **`GPOS` pair kerning and `GSUB`'s default ligature features are read and applied for an embedded face; the contextual and opt-in layout features are not.** An `fi` in a face whose `liga` feature declares the ligature draws as that real ligature glyph (advances, kerning, subsetting, and the ToUnicode CMap all describing the one substituted sequence — a ligature's ToUnicode entry maps back to its whole character run, so copy/paste recovers `fi`, not one glyph's worth of it). Still refused by name: the contextual features (`calt`, `clig`), which reach through Contextual/Chaining Contextual lookup machinery out of proportion to apply; the opt-in features (`smcp`, `dlig` and friends), which no real shaper turns on until a caller asks; and lookups with a nonzero `lookupFlag`, whose behaviour depends on GDEF glyph classes this package does not read. The legacy `kern` table is not read either (neither Carlito nor Caladea ships one). A kerned run is shown with `TJ`, and the sign of a `TJ` number is the opposite of the adjustment it expresses (ISO 32000-1 9.4.3: a positive number moves the next glyph closer). A run with no kerning pairs stays as one unsplit hex string with `Tj`.
|
|
276
276
|
- **Kerning applies to whole shown strings, so a wrap decision does not see a pair straddling the boundary between two separately-measured words.** The width a line reports is the width the page draws; making the wrap decision itself exact would mean widening the `TextMeasurer` port for a sub-point difference that only ever errs towards breaking a line early.
|
|
277
|
-
- **`font-substitutes.ts` maps both `Calibri` and `Calibri Light` onto the same ordinary-weight Carlito face** — Carlito ships only one weight per style axis, so `Calibri Light` substitutes to standard Carlito rather than a genuinely lighter face. An honest, documented approximation: width metrics match, visibly thinner strokes do not.
|
|
277
|
+
- **`font-substitutes.ts` maps both `Calibri` and `Calibri Light` onto the same ordinary-weight Carlito face** — Carlito ships only one weight per style axis (no Light face exists upstream to vendor), so `Calibri Light` substitutes to standard Carlito rather than a genuinely lighter face. An honest, documented approximation: width metrics match, visibly thinner strokes do not — and a REPORTED one, never silent: the registry's `onSubstitution` channel fires for it like every vendored substitution, naming `Calibri Light` → `carlito` so a caller can act on the weight mismatch (pinned by test in write-embedded-font.test.ts).
|
|
278
278
|
- **`cff-probe.ts`'s CID-keyed CFF guard exists for a source-embedded-font phase this package hasn't built yet** — it is not wired into any write path today. Every face currently embedded is `glyf`-flavoured TrueType, and `sfnt-subset.ts` already refuses anything else before this guard would run.
|
|
279
279
|
|
|
280
280
|
## Text extraction and font encodings
|
|
@@ -315,7 +315,7 @@ A differential test pins the _set_ of template positions and offsets but not the
|
|
|
315
315
|
|
|
316
316
|
**Supply a `FontRegistry` and Calibri/Cambria stop being an approximation at all** — the vendored Carlito/Caladea faces are the real metric-compatible TrueType families, resolved automatically. Aptos still has no vendored substitute. A resolved face is measured at its own real `hmtx` advances (never the width-correction table — applying both would silently draw text narrower than measured), subsetted to used glyphs, and embedded as a real `/Type0` + `/CIDFontType2` + `/FontFile2` group. Remaining limits: only TrueType (`glyf`) outlines can be embedded; a character with no glyph is drawn as `.notdef` and reported through `onMissingGlyph`; and vertical-metric policy is caller-chosen.
|
|
317
317
|
|
|
318
|
-
**The acceptance bar for embedded-font fidelity is "no page-count drift on a real corpus", not "line-identical".** An embedded run is placed at real `hmtx` advances adjusted by real `GPOS` pair kerning — measured and drawn from one shared computation. What separates this from line-identical:
|
|
318
|
+
**The acceptance bar for embedded-font fidelity is "no page-count drift on a real corpus", not "line-identical".** An embedded run is placed at real `hmtx` advances adjusted by real `GPOS` pair kerning, shaped through real `GSUB` default ligatures — measured and drawn from one shared computation. What separates this from line-identical: contextual alternates and the opt-in layout features are never applied, and kerning is within each shown string rather than across whitespace boundaries a wrap decision measures separately.
|
|
319
319
|
|
|
320
320
|
**The one exception is math-formula rendering (`WritePdfOptions.formulas`): this genuinely embeds a real, hand-parsed font.** Real box-model glyph runs through the embedded STIX Two Math font with genuine per-glyph metrics and font-wide layout constants parsed directly from the `MATH` table. Stretchy constructions are real: a `MathVariants` variant or assembly resolved, measured against actual outlines, and drawn by glyph ID.
|
|
321
321
|
|
package/dist/content-write.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as StandardFontName } from "./afm-widths-CneRyu5l.cjs";
|
|
2
2
|
import { LayoutItem } from "./layout.cjs";
|
|
3
|
-
import { r as EmbeddedFaceSubstitution, t as EmbeddedFace } from "./embedded-font-
|
|
3
|
+
import { r as EmbeddedFaceSubstitution, t as EmbeddedFace } from "./embedded-font-oc65VGlr.cjs";
|
|
4
4
|
import { WinAnsiSubstitution } from "./winansi.cjs";
|
|
5
5
|
import { LayoutFont, TextMeasurer } from "document-schema.js";
|
|
6
6
|
//#region src/content-write.d.ts
|
package/dist/content-write.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { r as StandardFontName } from "./afm-widths-CneRyu5l.js";
|
|
2
2
|
import { LayoutItem } from "./layout.js";
|
|
3
|
-
import { r as EmbeddedFaceSubstitution, t as EmbeddedFace } from "./embedded-font-
|
|
3
|
+
import { r as EmbeddedFaceSubstitution, t as EmbeddedFace } from "./embedded-font-B4p9a3O3.js";
|
|
4
4
|
import { WinAnsiSubstitution } from "./winansi.js";
|
|
5
5
|
import { LayoutFont, TextMeasurer } from "document-schema.js";
|
|
6
6
|
//#region src/content-write.d.ts
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { t as SfntFont } from "./sfnt-V08bb5ut.js";
|
|
2
|
+
import { t as GsubShaper } from "./gsub-table-BRTQ8EUW.js";
|
|
2
3
|
//#region src/embedded-font.d.ts
|
|
3
4
|
declare const NOTDEF_GLYPH_ID = 0;
|
|
4
5
|
interface EmbeddedFaceMetrics {
|
|
@@ -27,6 +28,7 @@ interface EmbeddedFace {
|
|
|
27
28
|
glyphId(codePoint: number): number | undefined;
|
|
28
29
|
glyphSpaceWidth(glyphId: number): number;
|
|
29
30
|
kernGlyphSpace(leftGlyphId: number, rightGlyphId: number): number;
|
|
31
|
+
readonly gsubShaper: GsubShaper | undefined;
|
|
30
32
|
}
|
|
31
33
|
declare function loadEmbeddedFace(font: SfntFont): EmbeddedFace | undefined;
|
|
32
34
|
interface EmbeddedFaceSubstitution {
|
|
@@ -43,6 +45,6 @@ interface EmbeddedShow {
|
|
|
43
45
|
readonly kerns: readonly EmbeddedKern[];
|
|
44
46
|
}
|
|
45
47
|
declare function encodeForShowEmbedded(text: string, face: EmbeddedFace): EmbeddedShow;
|
|
46
|
-
declare function collectEmbeddedGlyphs(texts: Iterable<string>, face: EmbeddedFace): ReadonlyMap<number, number>;
|
|
48
|
+
declare function collectEmbeddedGlyphs(texts: Iterable<string>, face: EmbeddedFace): ReadonlyMap<number, readonly number[]>;
|
|
47
49
|
//#endregion
|
|
48
50
|
export { EmbeddedShow as a, encodeForShowEmbedded as c, EmbeddedKern as i, loadEmbeddedFace as l, EmbeddedFaceMetrics as n, NOTDEF_GLYPH_ID as o, EmbeddedFaceSubstitution as r, collectEmbeddedGlyphs as s, EmbeddedFace as t };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { t as SfntFont } from "./sfnt-V08bb5ut.cjs";
|
|
2
|
+
import { t as GsubShaper } from "./gsub-table-URczLd85.cjs";
|
|
2
3
|
//#region src/embedded-font.d.ts
|
|
3
4
|
declare const NOTDEF_GLYPH_ID = 0;
|
|
4
5
|
interface EmbeddedFaceMetrics {
|
|
@@ -27,6 +28,7 @@ interface EmbeddedFace {
|
|
|
27
28
|
glyphId(codePoint: number): number | undefined;
|
|
28
29
|
glyphSpaceWidth(glyphId: number): number;
|
|
29
30
|
kernGlyphSpace(leftGlyphId: number, rightGlyphId: number): number;
|
|
31
|
+
readonly gsubShaper: GsubShaper | undefined;
|
|
30
32
|
}
|
|
31
33
|
declare function loadEmbeddedFace(font: SfntFont): EmbeddedFace | undefined;
|
|
32
34
|
interface EmbeddedFaceSubstitution {
|
|
@@ -43,6 +45,6 @@ interface EmbeddedShow {
|
|
|
43
45
|
readonly kerns: readonly EmbeddedKern[];
|
|
44
46
|
}
|
|
45
47
|
declare function encodeForShowEmbedded(text: string, face: EmbeddedFace): EmbeddedShow;
|
|
46
|
-
declare function collectEmbeddedGlyphs(texts: Iterable<string>, face: EmbeddedFace): ReadonlyMap<number, number>;
|
|
48
|
+
declare function collectEmbeddedGlyphs(texts: Iterable<string>, face: EmbeddedFace): ReadonlyMap<number, readonly number[]>;
|
|
47
49
|
//#endregion
|
|
48
50
|
export { EmbeddedShow as a, encodeForShowEmbedded as c, EmbeddedKern as i, loadEmbeddedFace as l, EmbeddedFaceMetrics as n, NOTDEF_GLYPH_ID as o, EmbeddedFaceSubstitution as r, collectEmbeddedGlyphs as s, EmbeddedFace as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as PdfDict, r as PdfObject } from "./objects-B7HdMeWS.cjs";
|
|
2
|
-
import { t as EmbeddedFace } from "./embedded-font-
|
|
2
|
+
import { t as EmbeddedFace } from "./embedded-font-oc65VGlr.cjs";
|
|
3
3
|
import { SfntSubsetResult } from "./sfnt-subset.cjs";
|
|
4
4
|
//#region src/embedded-font-write.d.ts
|
|
5
5
|
declare function embeddedSubsetTag(postScriptName: string, glyphIds: readonly number[]): string;
|
|
@@ -17,6 +17,6 @@ interface EmbeddedFontObjects {
|
|
|
17
17
|
readonly fontFile: PdfObject;
|
|
18
18
|
readonly toUnicode: PdfObject;
|
|
19
19
|
}
|
|
20
|
-
declare function buildEmbeddedFontObjects(face: EmbeddedFace, subset: SfntSubsetResult, usedGlyphs: ReadonlyMap<number, number>, refs: EmbeddedFontObjectRefs, compress: boolean): EmbeddedFontObjects;
|
|
20
|
+
declare function buildEmbeddedFontObjects(face: EmbeddedFace, subset: SfntSubsetResult, usedGlyphs: ReadonlyMap<number, readonly number[]>, refs: EmbeddedFontObjectRefs, compress: boolean): EmbeddedFontObjects;
|
|
21
21
|
//#endregion
|
|
22
22
|
export { EmbeddedFontObjectRefs, EmbeddedFontObjects, buildEmbeddedFontObjects, embeddedSubsetTag };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as PdfDict, r as PdfObject } from "./objects-B7HdMeWS.js";
|
|
2
|
-
import { t as EmbeddedFace } from "./embedded-font-
|
|
2
|
+
import { t as EmbeddedFace } from "./embedded-font-B4p9a3O3.js";
|
|
3
3
|
import { SfntSubsetResult } from "./sfnt-subset.js";
|
|
4
4
|
//#region src/embedded-font-write.d.ts
|
|
5
5
|
declare function embeddedSubsetTag(postScriptName: string, glyphIds: readonly number[]): string;
|
|
@@ -17,6 +17,6 @@ interface EmbeddedFontObjects {
|
|
|
17
17
|
readonly fontFile: PdfObject;
|
|
18
18
|
readonly toUnicode: PdfObject;
|
|
19
19
|
}
|
|
20
|
-
declare function buildEmbeddedFontObjects(face: EmbeddedFace, subset: SfntSubsetResult, usedGlyphs: ReadonlyMap<number, number>, refs: EmbeddedFontObjectRefs, compress: boolean): EmbeddedFontObjects;
|
|
20
|
+
declare function buildEmbeddedFontObjects(face: EmbeddedFace, subset: SfntSubsetResult, usedGlyphs: ReadonlyMap<number, readonly number[]>, refs: EmbeddedFontObjectRefs, compress: boolean): EmbeddedFontObjects;
|
|
21
21
|
//#endregion
|
|
22
22
|
export { EmbeddedFontObjectRefs, EmbeddedFontObjects, buildEmbeddedFontObjects, embeddedSubsetTag };
|
package/dist/embedded-font.cjs
CHANGED
|
@@ -4,6 +4,7 @@ const require_cmap_table = require("./cmap-table.cjs");
|
|
|
4
4
|
const require_font_tables = require("./font-tables.cjs");
|
|
5
5
|
const require_glyf = require("./glyf.cjs");
|
|
6
6
|
const require_gpos_table = require("./gpos-table.cjs");
|
|
7
|
+
const require_gsub_table = require("./gsub-table.cjs");
|
|
7
8
|
const require_hmtx_table = require("./hmtx-table.cjs");
|
|
8
9
|
//#region src/embedded-font.ts
|
|
9
10
|
const GLYPH_SPACE_UNITS_PER_EM = 1e3;
|
|
@@ -57,10 +58,12 @@ function readEmbeddedFace(font) {
|
|
|
57
58
|
const scale = (designUnits) => scaleToGlyphSpace(designUnits, unitsPerEm);
|
|
58
59
|
const capHeight = resolveCapHeight(font, cmap, os2?.sCapHeight, hhea.ascent);
|
|
59
60
|
const kern = require_gpos_table.buildGposKernLookup(font);
|
|
61
|
+
const gsubShaper = require_gsub_table.buildGsubShaper(font);
|
|
60
62
|
return {
|
|
61
63
|
font,
|
|
62
64
|
postScriptName: name.postScriptName,
|
|
63
65
|
numGlyphs: maxp.numGlyphs,
|
|
66
|
+
gsubShaper,
|
|
64
67
|
metrics: {
|
|
65
68
|
unitsPerEm,
|
|
66
69
|
ascentGlyphSpace: scale(hhea.ascent),
|
|
@@ -109,30 +112,31 @@ function parseHhea(font) {
|
|
|
109
112
|
}
|
|
110
113
|
const CID_BYTE_LENGTH = 2;
|
|
111
114
|
function encodeForShowEmbedded(text, face) {
|
|
112
|
-
const
|
|
115
|
+
const resolvedGlyphIds = [];
|
|
113
116
|
const substitutions = [];
|
|
114
|
-
const kerns = [];
|
|
115
|
-
let width1000 = 0;
|
|
116
117
|
for (const character of text) {
|
|
117
118
|
const glyphId = face.glyphId(character.codePointAt(0));
|
|
118
119
|
if (glyphId === void 0) substitutions.push({ from: character });
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
resolvedGlyphIds.push(glyphId ?? 0);
|
|
121
|
+
}
|
|
122
|
+
const shownGlyphIds = face.gsubShaper === void 0 ? resolvedGlyphIds : face.gsubShaper(resolvedGlyphIds).glyphIds;
|
|
123
|
+
const kerns = [];
|
|
124
|
+
let width1000 = 0;
|
|
125
|
+
for (const [index, shown] of shownGlyphIds.entries()) {
|
|
126
|
+
if (index > 0) {
|
|
127
|
+
const adjustment1000 = face.kernGlyphSpace(shownGlyphIds[index - 1], shown);
|
|
123
128
|
if (adjustment1000 !== 0) {
|
|
124
129
|
kerns.push({
|
|
125
|
-
codeOffset:
|
|
130
|
+
codeOffset: index * CID_BYTE_LENGTH,
|
|
126
131
|
adjustment1000
|
|
127
132
|
});
|
|
128
133
|
width1000 += adjustment1000;
|
|
129
134
|
}
|
|
130
135
|
}
|
|
131
|
-
glyphIds.push(shown);
|
|
132
136
|
width1000 += face.glyphSpaceWidth(shown);
|
|
133
137
|
}
|
|
134
|
-
const codes = new Uint8Array(
|
|
135
|
-
|
|
138
|
+
const codes = new Uint8Array(shownGlyphIds.length * CID_BYTE_LENGTH);
|
|
139
|
+
shownGlyphIds.forEach((glyphId, index) => {
|
|
136
140
|
codes[index * CID_BYTE_LENGTH] = glyphId >> 8 & 255;
|
|
137
141
|
codes[index * CID_BYTE_LENGTH + 1] = glyphId & 255;
|
|
138
142
|
});
|
|
@@ -145,10 +149,37 @@ function encodeForShowEmbedded(text, face) {
|
|
|
145
149
|
}
|
|
146
150
|
function collectEmbeddedGlyphs(texts, face) {
|
|
147
151
|
const used = /* @__PURE__ */ new Map();
|
|
148
|
-
for (const text of texts)
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
+
for (const text of texts) {
|
|
153
|
+
const resolvedGlyphIds = [];
|
|
154
|
+
const codePoints = [];
|
|
155
|
+
for (const character of text) {
|
|
156
|
+
const codePoint = character.codePointAt(0);
|
|
157
|
+
const glyphId = face.glyphId(codePoint);
|
|
158
|
+
if (glyphId === void 0) {
|
|
159
|
+
resolvedGlyphIds.push(0);
|
|
160
|
+
codePoints.push(void 0);
|
|
161
|
+
} else {
|
|
162
|
+
resolvedGlyphIds.push(glyphId);
|
|
163
|
+
codePoints.push(codePoint);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const shaping = face.gsubShaper === void 0 ? {
|
|
167
|
+
glyphIds: resolvedGlyphIds,
|
|
168
|
+
spans: resolvedGlyphIds.map(() => 1)
|
|
169
|
+
} : face.gsubShaper(resolvedGlyphIds);
|
|
170
|
+
let cursor = 0;
|
|
171
|
+
for (const [index, glyphId] of shaping.glyphIds.entries()) {
|
|
172
|
+
const span = shaping.spans[index];
|
|
173
|
+
const sequence = [];
|
|
174
|
+
for (let s = 0; s < span; s++) {
|
|
175
|
+
const codePoint = codePoints[cursor + s];
|
|
176
|
+
if (codePoint !== void 0) sequence.push(codePoint);
|
|
177
|
+
}
|
|
178
|
+
cursor += span;
|
|
179
|
+
if (glyphId !== 0 && sequence.length > 0) {
|
|
180
|
+
if (used.get(glyphId) === void 0) used.set(glyphId, sequence);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
152
183
|
}
|
|
153
184
|
return used;
|
|
154
185
|
}
|
package/dist/embedded-font.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as EmbeddedShow, c as encodeForShowEmbedded, i as EmbeddedKern, l as loadEmbeddedFace, n as EmbeddedFaceMetrics, o as NOTDEF_GLYPH_ID, r as EmbeddedFaceSubstitution, s as collectEmbeddedGlyphs, t as EmbeddedFace } from "./embedded-font-
|
|
1
|
+
import { a as EmbeddedShow, c as encodeForShowEmbedded, i as EmbeddedKern, l as loadEmbeddedFace, n as EmbeddedFaceMetrics, o as NOTDEF_GLYPH_ID, r as EmbeddedFaceSubstitution, s as collectEmbeddedGlyphs, t as EmbeddedFace } from "./embedded-font-oc65VGlr.cjs";
|
|
2
2
|
export { EmbeddedFace, EmbeddedFaceMetrics, EmbeddedFaceSubstitution, EmbeddedKern, EmbeddedShow, NOTDEF_GLYPH_ID, collectEmbeddedGlyphs, encodeForShowEmbedded, loadEmbeddedFace };
|
package/dist/embedded-font.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as EmbeddedShow, c as encodeForShowEmbedded, i as EmbeddedKern, l as loadEmbeddedFace, n as EmbeddedFaceMetrics, o as NOTDEF_GLYPH_ID, r as EmbeddedFaceSubstitution, s as collectEmbeddedGlyphs, t as EmbeddedFace } from "./embedded-font-
|
|
1
|
+
import { a as EmbeddedShow, c as encodeForShowEmbedded, i as EmbeddedKern, l as loadEmbeddedFace, n as EmbeddedFaceMetrics, o as NOTDEF_GLYPH_ID, r as EmbeddedFaceSubstitution, s as collectEmbeddedGlyphs, t as EmbeddedFace } from "./embedded-font-B4p9a3O3.js";
|
|
2
2
|
export { EmbeddedFace, EmbeddedFaceMetrics, EmbeddedFaceSubstitution, EmbeddedKern, EmbeddedShow, NOTDEF_GLYPH_ID, collectEmbeddedGlyphs, encodeForShowEmbedded, loadEmbeddedFace };
|
package/dist/embedded-font.js
CHANGED
|
@@ -3,6 +3,7 @@ import { buildCmapLookup } from "./cmap-table.js";
|
|
|
3
3
|
import { parseHead, parseMaxp, parseName, parseOs2, parsePost } from "./font-tables.js";
|
|
4
4
|
import { parseGlyf } from "./glyf.js";
|
|
5
5
|
import { buildGposKernLookup } from "./gpos-table.js";
|
|
6
|
+
import { buildGsubShaper } from "./gsub-table.js";
|
|
6
7
|
import { parseHmtx } from "./hmtx-table.js";
|
|
7
8
|
//#region src/embedded-font.ts
|
|
8
9
|
const GLYPH_SPACE_UNITS_PER_EM = 1e3;
|
|
@@ -56,10 +57,12 @@ function readEmbeddedFace(font) {
|
|
|
56
57
|
const scale = (designUnits) => scaleToGlyphSpace(designUnits, unitsPerEm);
|
|
57
58
|
const capHeight = resolveCapHeight(font, cmap, os2?.sCapHeight, hhea.ascent);
|
|
58
59
|
const kern = buildGposKernLookup(font);
|
|
60
|
+
const gsubShaper = buildGsubShaper(font);
|
|
59
61
|
return {
|
|
60
62
|
font,
|
|
61
63
|
postScriptName: name.postScriptName,
|
|
62
64
|
numGlyphs: maxp.numGlyphs,
|
|
65
|
+
gsubShaper,
|
|
63
66
|
metrics: {
|
|
64
67
|
unitsPerEm,
|
|
65
68
|
ascentGlyphSpace: scale(hhea.ascent),
|
|
@@ -108,30 +111,31 @@ function parseHhea(font) {
|
|
|
108
111
|
}
|
|
109
112
|
const CID_BYTE_LENGTH = 2;
|
|
110
113
|
function encodeForShowEmbedded(text, face) {
|
|
111
|
-
const
|
|
114
|
+
const resolvedGlyphIds = [];
|
|
112
115
|
const substitutions = [];
|
|
113
|
-
const kerns = [];
|
|
114
|
-
let width1000 = 0;
|
|
115
116
|
for (const character of text) {
|
|
116
117
|
const glyphId = face.glyphId(character.codePointAt(0));
|
|
117
118
|
if (glyphId === void 0) substitutions.push({ from: character });
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
resolvedGlyphIds.push(glyphId ?? 0);
|
|
120
|
+
}
|
|
121
|
+
const shownGlyphIds = face.gsubShaper === void 0 ? resolvedGlyphIds : face.gsubShaper(resolvedGlyphIds).glyphIds;
|
|
122
|
+
const kerns = [];
|
|
123
|
+
let width1000 = 0;
|
|
124
|
+
for (const [index, shown] of shownGlyphIds.entries()) {
|
|
125
|
+
if (index > 0) {
|
|
126
|
+
const adjustment1000 = face.kernGlyphSpace(shownGlyphIds[index - 1], shown);
|
|
122
127
|
if (adjustment1000 !== 0) {
|
|
123
128
|
kerns.push({
|
|
124
|
-
codeOffset:
|
|
129
|
+
codeOffset: index * CID_BYTE_LENGTH,
|
|
125
130
|
adjustment1000
|
|
126
131
|
});
|
|
127
132
|
width1000 += adjustment1000;
|
|
128
133
|
}
|
|
129
134
|
}
|
|
130
|
-
glyphIds.push(shown);
|
|
131
135
|
width1000 += face.glyphSpaceWidth(shown);
|
|
132
136
|
}
|
|
133
|
-
const codes = new Uint8Array(
|
|
134
|
-
|
|
137
|
+
const codes = new Uint8Array(shownGlyphIds.length * CID_BYTE_LENGTH);
|
|
138
|
+
shownGlyphIds.forEach((glyphId, index) => {
|
|
135
139
|
codes[index * CID_BYTE_LENGTH] = glyphId >> 8 & 255;
|
|
136
140
|
codes[index * CID_BYTE_LENGTH + 1] = glyphId & 255;
|
|
137
141
|
});
|
|
@@ -144,10 +148,37 @@ function encodeForShowEmbedded(text, face) {
|
|
|
144
148
|
}
|
|
145
149
|
function collectEmbeddedGlyphs(texts, face) {
|
|
146
150
|
const used = /* @__PURE__ */ new Map();
|
|
147
|
-
for (const text of texts)
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
+
for (const text of texts) {
|
|
152
|
+
const resolvedGlyphIds = [];
|
|
153
|
+
const codePoints = [];
|
|
154
|
+
for (const character of text) {
|
|
155
|
+
const codePoint = character.codePointAt(0);
|
|
156
|
+
const glyphId = face.glyphId(codePoint);
|
|
157
|
+
if (glyphId === void 0) {
|
|
158
|
+
resolvedGlyphIds.push(0);
|
|
159
|
+
codePoints.push(void 0);
|
|
160
|
+
} else {
|
|
161
|
+
resolvedGlyphIds.push(glyphId);
|
|
162
|
+
codePoints.push(codePoint);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const shaping = face.gsubShaper === void 0 ? {
|
|
166
|
+
glyphIds: resolvedGlyphIds,
|
|
167
|
+
spans: resolvedGlyphIds.map(() => 1)
|
|
168
|
+
} : face.gsubShaper(resolvedGlyphIds);
|
|
169
|
+
let cursor = 0;
|
|
170
|
+
for (const [index, glyphId] of shaping.glyphIds.entries()) {
|
|
171
|
+
const span = shaping.spans[index];
|
|
172
|
+
const sequence = [];
|
|
173
|
+
for (let s = 0; s < span; s++) {
|
|
174
|
+
const codePoint = codePoints[cursor + s];
|
|
175
|
+
if (codePoint !== void 0) sequence.push(codePoint);
|
|
176
|
+
}
|
|
177
|
+
cursor += span;
|
|
178
|
+
if (glyphId !== 0 && sequence.length > 0) {
|
|
179
|
+
if (used.get(glyphId) === void 0) used.set(glyphId, sequence);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
151
182
|
}
|
|
152
183
|
return used;
|
|
153
184
|
}
|
package/dist/font-registry.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as StandardFontName } from "./afm-widths-CneRyu5l.cjs";
|
|
2
|
-
import { t as EmbeddedFace } from "./embedded-font-
|
|
2
|
+
import { t as EmbeddedFace } from "./embedded-font-oc65VGlr.cjs";
|
|
3
3
|
import { FontRegistryOptions, LayoutFont } from "document-schema.js";
|
|
4
4
|
//#region src/font-registry.d.ts
|
|
5
5
|
type ResolvedFace = {
|
package/dist/font-registry.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as StandardFontName } from "./afm-widths-CneRyu5l.js";
|
|
2
|
-
import { t as EmbeddedFace } from "./embedded-font-
|
|
2
|
+
import { t as EmbeddedFace } from "./embedded-font-B4p9a3O3.js";
|
|
3
3
|
import { FontRegistryOptions, LayoutFont } from "document-schema.js";
|
|
4
4
|
//#region src/font-registry.d.ts
|
|
5
5
|
type ResolvedFace = {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { t as SfntFont } from "./sfnt-V08bb5ut.js";
|
|
2
|
+
//#region src/gsub-table.d.ts
|
|
3
|
+
interface GsubShaping {
|
|
4
|
+
readonly glyphIds: number[];
|
|
5
|
+
readonly spans: number[];
|
|
6
|
+
}
|
|
7
|
+
type GsubShaper = (glyphIds: readonly number[]) => GsubShaping;
|
|
8
|
+
declare function buildGsubShaper(font: SfntFont): GsubShaper | undefined;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { GsubShaping as n, buildGsubShaper as r, GsubShaper as t };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { t as SfntFont } from "./sfnt-V08bb5ut.cjs";
|
|
2
|
+
//#region src/gsub-table.d.ts
|
|
3
|
+
interface GsubShaping {
|
|
4
|
+
readonly glyphIds: number[];
|
|
5
|
+
readonly spans: number[];
|
|
6
|
+
}
|
|
7
|
+
type GsubShaper = (glyphIds: readonly number[]) => GsubShaping;
|
|
8
|
+
declare function buildGsubShaper(font: SfntFont): GsubShaper | undefined;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { GsubShaping as n, buildGsubShaper as r, GsubShaper as t };
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_sfnt = require("./sfnt.cjs");
|
|
3
|
+
const require_ot_layout_common = require("./ot-layout-common.cjs");
|
|
4
|
+
//#region src/gsub-table.ts
|
|
5
|
+
const LIGATURE_FEATURE_TAGS = ["liga", "rlig"];
|
|
6
|
+
const GSUB_HEADER_SIZE = 10;
|
|
7
|
+
const SCRIPT_RECORD_SIZE = 6;
|
|
8
|
+
const FEATURE_RECORD_SIZE = 6;
|
|
9
|
+
const LANG_SYS_HEADER_SIZE = 6;
|
|
10
|
+
const FEATURE_HEADER_SIZE = 4;
|
|
11
|
+
const LOOKUP_HEADER_SIZE = 6;
|
|
12
|
+
const LOOKUP_TYPE_SINGLE_SUBST = 1;
|
|
13
|
+
const LOOKUP_TYPE_LIGATURE_SUBST = 4;
|
|
14
|
+
const LOOKUP_TYPE_EXTENSION_SUBST = 7;
|
|
15
|
+
const EXTENSION_SUBST_HEADER_SIZE = 8;
|
|
16
|
+
const PREFERRED_SCRIPT_TAGS = ["latn", "DFLT"];
|
|
17
|
+
function readTag(bytes, offset) {
|
|
18
|
+
return String.fromCharCode(require_sfnt.u16(bytes, offset) >> 8, require_sfnt.u16(bytes, offset) & 255, require_sfnt.u16(bytes, offset + 2) >> 8, require_sfnt.u16(bytes, offset + 2) & 255);
|
|
19
|
+
}
|
|
20
|
+
const SINGLE_SUBST_FORMAT_1_SIZE = 6;
|
|
21
|
+
function parseSingleSubstFormat1(bytes, subtableOffset) {
|
|
22
|
+
if (!require_sfnt.hasBytes(bytes, subtableOffset, SINGLE_SUBST_FORMAT_1_SIZE)) return;
|
|
23
|
+
const coverage = require_ot_layout_common.parseCoverage(bytes, subtableOffset + require_sfnt.u16(bytes, subtableOffset + 2));
|
|
24
|
+
if (coverage === void 0) return;
|
|
25
|
+
const delta = require_sfnt.i16(bytes, subtableOffset + 4);
|
|
26
|
+
return {
|
|
27
|
+
kind: "single",
|
|
28
|
+
lookup: (glyphId) => {
|
|
29
|
+
if (coverage.coverageIndex(glyphId) === void 0) return;
|
|
30
|
+
return glyphId + delta;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const SINGLE_SUBST_FORMAT_2_HEADER_SIZE = 6;
|
|
35
|
+
function parseSingleSubstFormat2(bytes, subtableOffset) {
|
|
36
|
+
if (!require_sfnt.hasBytes(bytes, subtableOffset, SINGLE_SUBST_FORMAT_2_HEADER_SIZE)) return;
|
|
37
|
+
const coverage = require_ot_layout_common.parseCoverage(bytes, subtableOffset + require_sfnt.u16(bytes, subtableOffset + 2));
|
|
38
|
+
if (coverage === void 0) return;
|
|
39
|
+
const glyphCount = require_sfnt.u16(bytes, subtableOffset + 4);
|
|
40
|
+
const substitutesOffset = subtableOffset + SINGLE_SUBST_FORMAT_2_HEADER_SIZE;
|
|
41
|
+
if (!require_sfnt.hasBytes(bytes, substitutesOffset, glyphCount * 2)) return;
|
|
42
|
+
return {
|
|
43
|
+
kind: "single",
|
|
44
|
+
lookup: (glyphId) => {
|
|
45
|
+
const coverageIndex = coverage.coverageIndex(glyphId);
|
|
46
|
+
if (coverageIndex === void 0 || coverageIndex >= glyphCount) return;
|
|
47
|
+
return require_sfnt.u16(bytes, substitutesOffset + coverageIndex * 2);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const LIGATURE_SUBST_FORMAT_1_HEADER_SIZE = 6;
|
|
52
|
+
const LIGATURE_SET_HEADER_SIZE = 2;
|
|
53
|
+
const LIGATURE_HEADER_PREFIX_SIZE = 4;
|
|
54
|
+
function parseLigatureSubstFormat1(bytes, subtableOffset) {
|
|
55
|
+
if (!require_sfnt.hasBytes(bytes, subtableOffset, LIGATURE_SUBST_FORMAT_1_HEADER_SIZE)) return;
|
|
56
|
+
const coverage = require_ot_layout_common.parseCoverage(bytes, subtableOffset + require_sfnt.u16(bytes, subtableOffset + 2));
|
|
57
|
+
if (coverage === void 0) return;
|
|
58
|
+
const ligSetCount = require_sfnt.u16(bytes, subtableOffset + 4);
|
|
59
|
+
const ligSetOffsetsOffset = subtableOffset + LIGATURE_SUBST_FORMAT_1_HEADER_SIZE;
|
|
60
|
+
if (!require_sfnt.hasBytes(bytes, ligSetOffsetsOffset, ligSetCount * 2)) return;
|
|
61
|
+
return {
|
|
62
|
+
kind: "ligature",
|
|
63
|
+
match: (glyphIds, index) => {
|
|
64
|
+
const coverageIndex = coverage.coverageIndex(glyphIds[index]);
|
|
65
|
+
if (coverageIndex === void 0 || coverageIndex >= ligSetCount) return;
|
|
66
|
+
const ligSetOffset = subtableOffset + require_sfnt.u16(bytes, ligSetOffsetsOffset + coverageIndex * 2);
|
|
67
|
+
if (!require_sfnt.hasBytes(bytes, ligSetOffset, LIGATURE_SET_HEADER_SIZE)) return;
|
|
68
|
+
const ligatureCount = require_sfnt.u16(bytes, ligSetOffset);
|
|
69
|
+
const ligatureOffsetsOffset = ligSetOffset + LIGATURE_SET_HEADER_SIZE;
|
|
70
|
+
if (!require_sfnt.hasBytes(bytes, ligatureOffsetsOffset, ligatureCount * 2)) return;
|
|
71
|
+
for (let i = 0; i < ligatureCount; i++) {
|
|
72
|
+
const ligatureOffset = ligSetOffset + require_sfnt.u16(bytes, ligatureOffsetsOffset + i * 2);
|
|
73
|
+
if (!require_sfnt.hasBytes(bytes, ligatureOffset, LIGATURE_HEADER_PREFIX_SIZE)) continue;
|
|
74
|
+
const ligatureGlyph = require_sfnt.u16(bytes, ligatureOffset);
|
|
75
|
+
const componentCount = require_sfnt.u16(bytes, ligatureOffset + 2);
|
|
76
|
+
if (componentCount === 0) continue;
|
|
77
|
+
const componentsOffset = ligatureOffset + LIGATURE_HEADER_PREFIX_SIZE;
|
|
78
|
+
if (!require_sfnt.hasBytes(bytes, componentsOffset, (componentCount - 1) * 2)) continue;
|
|
79
|
+
if (index + componentCount > glyphIds.length) continue;
|
|
80
|
+
let matches = true;
|
|
81
|
+
for (let c = 1; c < componentCount; c++) if (glyphIds[index + c] !== require_sfnt.u16(bytes, componentsOffset + (c - 1) * 2)) {
|
|
82
|
+
matches = false;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
if (matches) return {
|
|
86
|
+
ligatureGlyph,
|
|
87
|
+
componentCount
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function parseSubtable(bytes, lookupType, subtableOffset) {
|
|
94
|
+
if (lookupType === LOOKUP_TYPE_EXTENSION_SUBST) {
|
|
95
|
+
if (!require_sfnt.hasBytes(bytes, subtableOffset, EXTENSION_SUBST_HEADER_SIZE) || require_sfnt.u16(bytes, subtableOffset) !== 1) return;
|
|
96
|
+
const extensionLookupType = require_sfnt.u16(bytes, subtableOffset + 2);
|
|
97
|
+
if (extensionLookupType === LOOKUP_TYPE_EXTENSION_SUBST) return;
|
|
98
|
+
return parseSubtable(bytes, extensionLookupType, subtableOffset + require_sfnt.u32(bytes, subtableOffset + 4));
|
|
99
|
+
}
|
|
100
|
+
if (lookupType === LOOKUP_TYPE_SINGLE_SUBST) {
|
|
101
|
+
if (!require_sfnt.hasBytes(bytes, subtableOffset, 2)) return;
|
|
102
|
+
const substFormat = require_sfnt.u16(bytes, subtableOffset);
|
|
103
|
+
if (substFormat === 1) return parseSingleSubstFormat1(bytes, subtableOffset);
|
|
104
|
+
if (substFormat === 2) return parseSingleSubstFormat2(bytes, subtableOffset);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (lookupType === LOOKUP_TYPE_LIGATURE_SUBST) {
|
|
108
|
+
if (!require_sfnt.hasBytes(bytes, subtableOffset, 2)) return;
|
|
109
|
+
if (require_sfnt.u16(bytes, subtableOffset) !== 1) return;
|
|
110
|
+
return parseLigatureSubstFormat1(bytes, subtableOffset);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function findScriptOffset(bytes, scriptListOffset) {
|
|
114
|
+
if (!require_sfnt.hasBytes(bytes, scriptListOffset, 2)) return;
|
|
115
|
+
const scriptCount = require_sfnt.u16(bytes, scriptListOffset);
|
|
116
|
+
const recordsOffset = scriptListOffset + 2;
|
|
117
|
+
if (!require_sfnt.hasBytes(bytes, recordsOffset, scriptCount * SCRIPT_RECORD_SIZE) || scriptCount === 0) return;
|
|
118
|
+
for (const wanted of PREFERRED_SCRIPT_TAGS) for (let i = 0; i < scriptCount; i++) {
|
|
119
|
+
const recordOffset = recordsOffset + i * SCRIPT_RECORD_SIZE;
|
|
120
|
+
if (readTag(bytes, recordOffset) === wanted) return scriptListOffset + require_sfnt.u16(bytes, recordOffset + 4);
|
|
121
|
+
}
|
|
122
|
+
return scriptListOffset + require_sfnt.u16(bytes, recordsOffset + 4);
|
|
123
|
+
}
|
|
124
|
+
function parseDefaultLangSysFeatureIndices(bytes, scriptOffset) {
|
|
125
|
+
if (!require_sfnt.hasBytes(bytes, scriptOffset, 2)) return [];
|
|
126
|
+
const defaultLangSysOffset = require_sfnt.u16(bytes, scriptOffset);
|
|
127
|
+
if (defaultLangSysOffset === 0) return [];
|
|
128
|
+
const langSysOffset = scriptOffset + defaultLangSysOffset;
|
|
129
|
+
if (!require_sfnt.hasBytes(bytes, langSysOffset, LANG_SYS_HEADER_SIZE)) return [];
|
|
130
|
+
const featureIndexCount = require_sfnt.u16(bytes, langSysOffset + 4);
|
|
131
|
+
const indicesOffset = langSysOffset + LANG_SYS_HEADER_SIZE;
|
|
132
|
+
if (!require_sfnt.hasBytes(bytes, indicesOffset, featureIndexCount * 2)) return [];
|
|
133
|
+
const indices = [];
|
|
134
|
+
for (let i = 0; i < featureIndexCount; i++) indices.push(require_sfnt.u16(bytes, indicesOffset + i * 2));
|
|
135
|
+
return indices;
|
|
136
|
+
}
|
|
137
|
+
function collectLigatureLookupIndices(bytes, featureListOffset, featureIndices) {
|
|
138
|
+
if (!require_sfnt.hasBytes(bytes, featureListOffset, 2)) return [];
|
|
139
|
+
const featureCount = require_sfnt.u16(bytes, featureListOffset);
|
|
140
|
+
const recordsOffset = featureListOffset + 2;
|
|
141
|
+
if (!require_sfnt.hasBytes(bytes, recordsOffset, featureCount * FEATURE_RECORD_SIZE)) return [];
|
|
142
|
+
const lookupIndices = [];
|
|
143
|
+
const seen = /* @__PURE__ */ new Set();
|
|
144
|
+
for (const featureIndex of featureIndices) {
|
|
145
|
+
if (featureIndex >= featureCount) continue;
|
|
146
|
+
const recordOffset = recordsOffset + featureIndex * FEATURE_RECORD_SIZE;
|
|
147
|
+
const tag = readTag(bytes, recordOffset);
|
|
148
|
+
if (!LIGATURE_FEATURE_TAGS.includes(tag)) continue;
|
|
149
|
+
const featureOffset = featureListOffset + require_sfnt.u16(bytes, recordOffset + 4);
|
|
150
|
+
if (!require_sfnt.hasBytes(bytes, featureOffset, FEATURE_HEADER_SIZE)) continue;
|
|
151
|
+
const lookupIndexCount = require_sfnt.u16(bytes, featureOffset + 2);
|
|
152
|
+
const indicesOffset = featureOffset + FEATURE_HEADER_SIZE;
|
|
153
|
+
if (!require_sfnt.hasBytes(bytes, indicesOffset, lookupIndexCount * 2)) continue;
|
|
154
|
+
for (let i = 0; i < lookupIndexCount; i++) {
|
|
155
|
+
const lookupIndex = require_sfnt.u16(bytes, indicesOffset + i * 2);
|
|
156
|
+
if (!seen.has(lookupIndex)) {
|
|
157
|
+
seen.add(lookupIndex);
|
|
158
|
+
lookupIndices.push(lookupIndex);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return lookupIndices;
|
|
163
|
+
}
|
|
164
|
+
function buildGsubShaper(font) {
|
|
165
|
+
const bytes = require_sfnt.sfntTableBytes(font, "GSUB");
|
|
166
|
+
if (bytes === void 0 || !require_sfnt.hasBytes(bytes, 0, GSUB_HEADER_SIZE) || require_sfnt.u16(bytes, 0) !== 1) return;
|
|
167
|
+
const scriptOffset = findScriptOffset(bytes, require_sfnt.u16(bytes, 4));
|
|
168
|
+
if (scriptOffset === void 0) return;
|
|
169
|
+
const lookupIndices = collectLigatureLookupIndices(bytes, require_sfnt.u16(bytes, 6), parseDefaultLangSysFeatureIndices(bytes, scriptOffset));
|
|
170
|
+
if (lookupIndices.length === 0) return;
|
|
171
|
+
const lookupListOffset = require_sfnt.u16(bytes, 8);
|
|
172
|
+
if (!require_sfnt.hasBytes(bytes, lookupListOffset, 2)) return;
|
|
173
|
+
const lookupCount = require_sfnt.u16(bytes, lookupListOffset);
|
|
174
|
+
const lookupOffsetsOffset = lookupListOffset + 2;
|
|
175
|
+
if (!require_sfnt.hasBytes(bytes, lookupOffsetsOffset, lookupCount * 2)) return;
|
|
176
|
+
const subtables = [];
|
|
177
|
+
for (const lookupIndex of lookupIndices) {
|
|
178
|
+
if (lookupIndex >= lookupCount) continue;
|
|
179
|
+
const lookupOffset = lookupListOffset + require_sfnt.u16(bytes, lookupOffsetsOffset + lookupIndex * 2);
|
|
180
|
+
if (!require_sfnt.hasBytes(bytes, lookupOffset, LOOKUP_HEADER_SIZE)) continue;
|
|
181
|
+
if (require_sfnt.u16(bytes, lookupOffset + 2) !== 0) continue;
|
|
182
|
+
const lookupType = require_sfnt.u16(bytes, lookupOffset);
|
|
183
|
+
const subTableCount = require_sfnt.u16(bytes, lookupOffset + 4);
|
|
184
|
+
const subtableOffsetsOffset = lookupOffset + LOOKUP_HEADER_SIZE;
|
|
185
|
+
if (!require_sfnt.hasBytes(bytes, subtableOffsetsOffset, subTableCount * 2)) continue;
|
|
186
|
+
for (let i = 0; i < subTableCount; i++) {
|
|
187
|
+
const subtable = parseSubtable(bytes, lookupType, lookupOffset + require_sfnt.u16(bytes, subtableOffsetsOffset + i * 2));
|
|
188
|
+
if (subtable !== void 0) subtables.push(subtable);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (subtables.length === 0) return;
|
|
192
|
+
return (glyphIds) => {
|
|
193
|
+
const shaped = [];
|
|
194
|
+
const spans = [];
|
|
195
|
+
let index = 0;
|
|
196
|
+
outer: while (index < glyphIds.length) {
|
|
197
|
+
for (const subtable of subtables) if (subtable.kind === "single") {
|
|
198
|
+
const substitute = subtable.lookup(glyphIds[index]);
|
|
199
|
+
if (substitute !== void 0) {
|
|
200
|
+
shaped.push(substitute);
|
|
201
|
+
spans.push(1);
|
|
202
|
+
index += 1;
|
|
203
|
+
continue outer;
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
const match = subtable.match(glyphIds, index);
|
|
207
|
+
if (match !== void 0) {
|
|
208
|
+
shaped.push(match.ligatureGlyph);
|
|
209
|
+
spans.push(match.componentCount);
|
|
210
|
+
index += match.componentCount;
|
|
211
|
+
continue outer;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
shaped.push(glyphIds[index]);
|
|
215
|
+
spans.push(1);
|
|
216
|
+
index += 1;
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
glyphIds: shaped,
|
|
220
|
+
spans
|
|
221
|
+
};
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
exports.buildGsubShaper = buildGsubShaper;
|