@flighthq/font-formats 0.3.1-next.1041.35b950c
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/cffCharstring.d.ts +13 -0
- package/dist/cffCharstring.d.ts.map +1 -0
- package/dist/cffCharstring.js +336 -0
- package/dist/cffCharstring.js.map +1 -0
- package/dist/cffDict.d.ts +8 -0
- package/dist/cffDict.d.ts.map +1 -0
- package/dist/cffDict.js +118 -0
- package/dist/cffDict.js.map +1 -0
- package/dist/cffFdSelect.d.ts +2 -0
- package/dist/cffFdSelect.d.ts.map +1 -0
- package/dist/cffFdSelect.js +60 -0
- package/dist/cffFdSelect.js.map +1 -0
- package/dist/cffIndex.d.ts +3 -0
- package/dist/cffIndex.d.ts.map +1 -0
- package/dist/cffIndex.js +52 -0
- package/dist/cffIndex.js.map +1 -0
- package/dist/cffTable.d.ts +3 -0
- package/dist/cffTable.d.ts.map +1 -0
- package/dist/cffTable.js +134 -0
- package/dist/cffTable.js.map +1 -0
- package/dist/contract.d.ts +15 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +15 -0
- package/dist/contract.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/openTypeCmap.d.ts +5 -0
- package/dist/openTypeCmap.d.ts.map +1 -0
- package/dist/openTypeCmap.js +161 -0
- package/dist/openTypeCmap.js.map +1 -0
- package/dist/openTypeGlyf.d.ts +4 -0
- package/dist/openTypeGlyf.d.ts.map +1 -0
- package/dist/openTypeGlyf.js +229 -0
- package/dist/openTypeGlyf.js.map +1 -0
- package/dist/openTypeGlyphOutlineSource.d.ts +4 -0
- package/dist/openTypeGlyphOutlineSource.d.ts.map +1 -0
- package/dist/openTypeGlyphOutlineSource.js +168 -0
- package/dist/openTypeGlyphOutlineSource.js.map +1 -0
- package/dist/openTypeMetrics.d.ts +6 -0
- package/dist/openTypeMetrics.d.ts.map +1 -0
- package/dist/openTypeMetrics.js +74 -0
- package/dist/openTypeMetrics.js.map +1 -0
- package/dist/openTypeTestHelper.d.ts +27 -0
- package/dist/openTypeTestHelper.d.ts.map +1 -0
- package/dist/openTypeTestHelper.js +520 -0
- package/dist/openTypeTestHelper.js.map +1 -0
- package/dist/sfntAssembly.d.ts +20 -0
- package/dist/sfntAssembly.d.ts.map +1 -0
- package/dist/sfntAssembly.js +237 -0
- package/dist/sfntAssembly.js.map +1 -0
- package/dist/sfntTableDirectory.d.ts +4 -0
- package/dist/sfntTableDirectory.d.ts.map +1 -0
- package/dist/sfntTableDirectory.js +39 -0
- package/dist/sfntTableDirectory.js.map +1 -0
- package/dist/woff2Font.d.ts +6 -0
- package/dist/woff2Font.d.ts.map +1 -0
- package/dist/woff2Font.js +210 -0
- package/dist/woff2Font.js.map +1 -0
- package/dist/woff2GlyfTransform.d.ts +23 -0
- package/dist/woff2GlyfTransform.d.ts.map +1 -0
- package/dist/woff2GlyfTransform.js +437 -0
- package/dist/woff2GlyfTransform.js.map +1 -0
- package/dist/woffFont.d.ts +6 -0
- package/dist/woffFont.d.ts.map +1 -0
- package/dist/woffFont.js +122 -0
- package/dist/woffFont.js.map +1 -0
- package/package.json +51 -0
- package/src/cffCharstring.test.ts +177 -0
- package/src/cffDict.test.ts +67 -0
- package/src/cffFdSelect.test.ts +84 -0
- package/src/cffIndex.test.ts +97 -0
- package/src/cffTable.test.ts +84 -0
- package/src/openTypeCmap.test.ts +124 -0
- package/src/openTypeGlyf.test.ts +168 -0
- package/src/openTypeGlyfWinding.test.ts +80 -0
- package/src/openTypeGlyphOutlineSource.test.ts +298 -0
- package/src/openTypeMetrics.test.ts +103 -0
- package/src/sfntAssembly.test.ts +228 -0
- package/src/sfntTableDirectory.test.ts +64 -0
- package/src/woff2Font.test.ts +129 -0
- package/src/woff2GlyfTransform.test.ts +315 -0
- package/src/woffFont.test.ts +207 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { PathCommand } from '@flighthq/types/contract';
|
|
2
|
+
// TrueType outlines: `loca` gives each glyph's byte range inside `glyf`, and a glyph is a set of closed
|
|
3
|
+
// contours of points, each point flagged on-curve or off-curve.
|
|
4
|
+
//
|
|
5
|
+
// THE ONE THING THAT MAKES THIS FORMAT DIFFERENT FROM EVERY OTHER PATH FORMAT: consecutive off-curve
|
|
6
|
+
// points imply an on-curve point exactly halfway between them. The format omits those midpoints because
|
|
7
|
+
// they are recoverable, so a reader that treats the point list literally produces contours that visibly
|
|
8
|
+
// cut corners. They are reconstructed below.
|
|
9
|
+
//
|
|
10
|
+
// Coordinates are design units, y-up in the font's own convention. `Path` is y-down, so y is negated at
|
|
11
|
+
// this seam — once, here — which is what makes the produced outlines match `GlyphOutlineMetrics`'
|
|
12
|
+
// documented "ink above the baseline has negative y".
|
|
13
|
+
const ON_CURVE = 0x01;
|
|
14
|
+
const X_SHORT = 0x02;
|
|
15
|
+
const Y_SHORT = 0x04;
|
|
16
|
+
const REPEAT = 0x08;
|
|
17
|
+
const X_SAME_OR_POSITIVE = 0x10;
|
|
18
|
+
const Y_SAME_OR_POSITIVE = 0x20;
|
|
19
|
+
// Replaces `out` with one glyph's contours. Returns false for a glyph whose data is unreadable; returns
|
|
20
|
+
// true with an empty path for a glyph that legitimately has no outline.
|
|
21
|
+
//
|
|
22
|
+
// Composite glyphs — an accented letter assembled from a base and a mark — are NOT expanded. They are
|
|
23
|
+
// reported as an empty outline rather than as a failure, because the glyph exists and its advance is
|
|
24
|
+
// still correct; only its ink is missing. Expanding them needs component transforms and is a deliberate
|
|
25
|
+
// follow-up rather than something to half-do inside the simple-glyph reader.
|
|
26
|
+
export function readOpenTypeGlyphOutline(out, bytes, directory, ranges, glyphIndex) {
|
|
27
|
+
const glyf = directory.tables.get('glyf');
|
|
28
|
+
if (glyf === undefined || glyphIndex < 0 || glyphIndex + 1 >= ranges.length)
|
|
29
|
+
return false;
|
|
30
|
+
out.commands.length = 0;
|
|
31
|
+
out.data.length = 0;
|
|
32
|
+
// `out` is REPLACED, so every field it owns is written — including this one. Both outline
|
|
33
|
+
// flavors fill by the nonzero rule, and a caller reusing a scratch path would otherwise keep
|
|
34
|
+
// whatever winding the previous glyph left, which turns a counter into a solid blob.
|
|
35
|
+
out.winding = 'nonZero';
|
|
36
|
+
const start = glyf.offset + ranges[glyphIndex];
|
|
37
|
+
const end = glyf.offset + ranges[glyphIndex + 1];
|
|
38
|
+
// An empty range is a glyph with no ink. True, not false: the caller still needs its advance.
|
|
39
|
+
if (end <= start)
|
|
40
|
+
return true;
|
|
41
|
+
if (end > bytes.byteLength || start + 10 > bytes.byteLength)
|
|
42
|
+
return false;
|
|
43
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
44
|
+
const contourCount = view.getInt16(start);
|
|
45
|
+
// Negative means composite. Empty outline, advance preserved — see above.
|
|
46
|
+
if (contourCount <= 0)
|
|
47
|
+
return true;
|
|
48
|
+
const endPoints = new Uint16Array(contourCount);
|
|
49
|
+
let cursor = start + 10;
|
|
50
|
+
if (cursor + contourCount * 2 + 2 > bytes.byteLength)
|
|
51
|
+
return false;
|
|
52
|
+
for (let contour = 0; contour < contourCount; contour += 1) {
|
|
53
|
+
endPoints[contour] = view.getUint16(cursor);
|
|
54
|
+
cursor += 2;
|
|
55
|
+
}
|
|
56
|
+
const pointCount = endPoints[contourCount - 1] + 1;
|
|
57
|
+
// Skip the hinting program: this package produces unhinted design-unit outlines by charter.
|
|
58
|
+
cursor += 2 + view.getUint16(cursor);
|
|
59
|
+
const flags = readGlyfFlags(view, cursor, pointCount, bytes.byteLength);
|
|
60
|
+
if (flags === null)
|
|
61
|
+
return false;
|
|
62
|
+
cursor = flags.cursor;
|
|
63
|
+
const xs = readGlyfCoordinates(view, cursor, flags.flags, X_SHORT, X_SAME_OR_POSITIVE, bytes.byteLength);
|
|
64
|
+
if (xs === null)
|
|
65
|
+
return false;
|
|
66
|
+
const ys = readGlyfCoordinates(view, xs.cursor, flags.flags, Y_SHORT, Y_SAME_OR_POSITIVE, bytes.byteLength);
|
|
67
|
+
if (ys === null)
|
|
68
|
+
return false;
|
|
69
|
+
let first = 0;
|
|
70
|
+
for (let contour = 0; contour < contourCount; contour += 1) {
|
|
71
|
+
const last = endPoints[contour];
|
|
72
|
+
if (last >= pointCount)
|
|
73
|
+
return false;
|
|
74
|
+
appendGlyfContour(out, flags.flags, xs.values, ys.values, first, last);
|
|
75
|
+
first = last + 1;
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
// Byte ranges of every glyph, indexed by glyph id, with one extra trailing entry so glyph `n` spans
|
|
80
|
+
// `[offsets[n], offsets[n + 1])`. An empty span is a real and common answer — a space has no outline —
|
|
81
|
+
// and is preserved rather than dropped, because the glyph still exists and still advances.
|
|
82
|
+
export function readOpenTypeGlyphRanges(bytes, directory, glyphCount, locaFormat) {
|
|
83
|
+
const loca = directory.tables.get('loca');
|
|
84
|
+
if (loca === undefined)
|
|
85
|
+
return null;
|
|
86
|
+
const entryCount = glyphCount + 1;
|
|
87
|
+
const entryBytes = locaFormat === 0 ? 2 : 4;
|
|
88
|
+
if (entryCount * entryBytes > loca.length)
|
|
89
|
+
return null;
|
|
90
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
91
|
+
const offsets = new Uint32Array(entryCount);
|
|
92
|
+
for (let index = 0; index < entryCount; index += 1) {
|
|
93
|
+
// The short form stores HALVED offsets, which is the whole reason the format flag exists.
|
|
94
|
+
offsets[index] =
|
|
95
|
+
locaFormat === 0 ? view.getUint16(loca.offset + index * 2) * 2 : view.getUint32(loca.offset + index * 4);
|
|
96
|
+
}
|
|
97
|
+
return offsets;
|
|
98
|
+
}
|
|
99
|
+
// One closed contour, with the implied on-curve midpoints reconstructed.
|
|
100
|
+
//
|
|
101
|
+
// The contour may begin on an off-curve point, in which case the true start is either the last point
|
|
102
|
+
// (if that one is on-curve) or the midpoint between the two. Getting this wrong rotates the whole
|
|
103
|
+
// contour by one point and is invisible on a circle while obvious on a letter.
|
|
104
|
+
function appendGlyfContour(out, flags, xs, ys, first, last) {
|
|
105
|
+
const count = last - first + 1;
|
|
106
|
+
if (count <= 0)
|
|
107
|
+
return;
|
|
108
|
+
const onCurve = (index) => (flags[first + (((index % count) + count) % count)] & ON_CURVE) !== 0;
|
|
109
|
+
const pointX = (index) => xs[first + (((index % count) + count) % count)];
|
|
110
|
+
// y is negated here — the single place the font's y-up convention becomes `Path`'s y-down.
|
|
111
|
+
const pointY = (index) => -ys[first + (((index % count) + count) % count)];
|
|
112
|
+
let startX;
|
|
113
|
+
let startY;
|
|
114
|
+
let startIndex;
|
|
115
|
+
// How many points remain to be traversed after the start. When the start IS one of the contour's
|
|
116
|
+
// points, that point must not be walked again — traversing all `count` would emit a zero-length
|
|
117
|
+
// segment back onto the start that `CLOSE` already implies. Only the synthesized-midpoint start,
|
|
118
|
+
// which is not a point of the contour, leaves all `count` still to visit.
|
|
119
|
+
let stepCount;
|
|
120
|
+
if (onCurve(0)) {
|
|
121
|
+
startX = pointX(0);
|
|
122
|
+
startY = pointY(0);
|
|
123
|
+
startIndex = 1;
|
|
124
|
+
stepCount = count - 1;
|
|
125
|
+
}
|
|
126
|
+
else if (onCurve(count - 1)) {
|
|
127
|
+
startX = pointX(count - 1);
|
|
128
|
+
startY = pointY(count - 1);
|
|
129
|
+
startIndex = 0;
|
|
130
|
+
stepCount = count - 1;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
startX = (pointX(0) + pointX(count - 1)) / 2;
|
|
134
|
+
startY = (pointY(0) + pointY(count - 1)) / 2;
|
|
135
|
+
startIndex = 0;
|
|
136
|
+
stepCount = count;
|
|
137
|
+
}
|
|
138
|
+
out.commands.push(PathCommand.MOVE_TO);
|
|
139
|
+
out.data.push(startX, startY);
|
|
140
|
+
let controlX = 0;
|
|
141
|
+
let controlY = 0;
|
|
142
|
+
let hasControl = false;
|
|
143
|
+
for (let step = 0; step < stepCount; step += 1) {
|
|
144
|
+
const index = startIndex + step;
|
|
145
|
+
const x = pointX(index);
|
|
146
|
+
const y = pointY(index);
|
|
147
|
+
if (onCurve(index)) {
|
|
148
|
+
if (hasControl) {
|
|
149
|
+
out.commands.push(PathCommand.CURVE_TO);
|
|
150
|
+
out.data.push(controlX, controlY, x, y);
|
|
151
|
+
hasControl = false;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
out.commands.push(PathCommand.LINE_TO);
|
|
155
|
+
out.data.push(x, y);
|
|
156
|
+
}
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
// Two off-curve points in a row: the on-curve point between them was omitted because it is exactly
|
|
160
|
+
// their midpoint. Emit it, then carry this point as the next control.
|
|
161
|
+
if (hasControl) {
|
|
162
|
+
out.commands.push(PathCommand.CURVE_TO);
|
|
163
|
+
out.data.push(controlX, controlY, (controlX + x) / 2, (controlY + y) / 2);
|
|
164
|
+
}
|
|
165
|
+
controlX = x;
|
|
166
|
+
controlY = y;
|
|
167
|
+
hasControl = true;
|
|
168
|
+
}
|
|
169
|
+
// Close back onto the start, through a trailing control point when the contour ended off-curve.
|
|
170
|
+
if (hasControl) {
|
|
171
|
+
out.commands.push(PathCommand.CURVE_TO);
|
|
172
|
+
out.data.push(controlX, controlY, startX, startY);
|
|
173
|
+
}
|
|
174
|
+
out.commands.push(PathCommand.CLOSE);
|
|
175
|
+
}
|
|
176
|
+
// Flags are run-length encoded: a flag with REPEAT set is followed by a count of additional points
|
|
177
|
+
// carrying the same flag. Reading them as one byte per point desynchronizes every coordinate after the
|
|
178
|
+
// first repeat, which is why this is expanded before any coordinate is touched.
|
|
179
|
+
function readGlyfFlags(view, start, pointCount, byteLength) {
|
|
180
|
+
const flags = new Uint8Array(pointCount);
|
|
181
|
+
let cursor = start;
|
|
182
|
+
for (let point = 0; point < pointCount;) {
|
|
183
|
+
if (cursor >= byteLength)
|
|
184
|
+
return null;
|
|
185
|
+
const flag = view.getUint8(cursor);
|
|
186
|
+
cursor += 1;
|
|
187
|
+
flags[point] = flag;
|
|
188
|
+
point += 1;
|
|
189
|
+
if ((flag & REPEAT) === 0)
|
|
190
|
+
continue;
|
|
191
|
+
if (cursor >= byteLength)
|
|
192
|
+
return null;
|
|
193
|
+
let repeats = view.getUint8(cursor);
|
|
194
|
+
cursor += 1;
|
|
195
|
+
while (repeats > 0 && point < pointCount) {
|
|
196
|
+
flags[point] = flag;
|
|
197
|
+
point += 1;
|
|
198
|
+
repeats -= 1;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return { cursor, flags };
|
|
202
|
+
}
|
|
203
|
+
// Coordinates are stored as DELTAS, in one of three widths chosen per point by two flag bits: one byte
|
|
204
|
+
// with the sign carried by the second bit, two bytes signed, or omitted entirely to mean "unchanged
|
|
205
|
+
// from the previous point". Accumulated into absolute values here.
|
|
206
|
+
function readGlyfCoordinates(view, start, flags, shortBit, sameOrPositiveBit, byteLength) {
|
|
207
|
+
const values = new Int32Array(flags.length);
|
|
208
|
+
let cursor = start;
|
|
209
|
+
let value = 0;
|
|
210
|
+
for (let point = 0; point < flags.length; point += 1) {
|
|
211
|
+
const flag = flags[point];
|
|
212
|
+
if ((flag & shortBit) !== 0) {
|
|
213
|
+
if (cursor >= byteLength)
|
|
214
|
+
return null;
|
|
215
|
+
const magnitude = view.getUint8(cursor);
|
|
216
|
+
cursor += 1;
|
|
217
|
+
value += (flag & sameOrPositiveBit) !== 0 ? magnitude : -magnitude;
|
|
218
|
+
}
|
|
219
|
+
else if ((flag & sameOrPositiveBit) === 0) {
|
|
220
|
+
if (cursor + 2 > byteLength)
|
|
221
|
+
return null;
|
|
222
|
+
value += view.getInt16(cursor);
|
|
223
|
+
cursor += 2;
|
|
224
|
+
}
|
|
225
|
+
values[point] = value;
|
|
226
|
+
}
|
|
227
|
+
return { cursor, values };
|
|
228
|
+
}
|
|
229
|
+
//# sourceMappingURL=openTypeGlyf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openTypeGlyf.js","sourceRoot":"","sources":["../src/openTypeGlyf.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,wGAAwG;AACxG,gEAAgE;AAChE,EAAE;AACF,qGAAqG;AACrG,wGAAwG;AACxG,wGAAwG;AACxG,6CAA6C;AAC7C,EAAE;AACF,wGAAwG;AACxG,kGAAkG;AAClG,sDAAsD;AAEtD,MAAM,QAAQ,GAAG,IAAI,CAAC;AACtB,MAAM,OAAO,GAAG,IAAI,CAAC;AACrB,MAAM,OAAO,GAAG,IAAI,CAAC;AACrB,MAAM,MAAM,GAAG,IAAI,CAAC;AACpB,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAChC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC,wGAAwG;AACxG,wEAAwE;AACxE,EAAE;AACF,sGAAsG;AACtG,qGAAqG;AACrG,wGAAwG;AACxG,6EAA6E;AAC7E,MAAM,UAAU,wBAAwB,CACtC,GAAS,EACT,KAA2B,EAC3B,SAAuC,EACvC,MAA6B,EAC7B,UAAkB;IAElB,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,IAAI,KAAK,SAAS,IAAI,UAAU,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAE1F,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IACxB,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IACpB,0FAA0F;IAC1F,6FAA6F;IAC7F,qFAAqF;IACrF,GAAG,CAAC,OAAO,GAAG,SAAS,CAAC;IAExB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,UAAU,CAAE,CAAC;IAChD,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,UAAU,GAAG,CAAC,CAAE,CAAC;IAClD,8FAA8F;IAC9F,IAAI,GAAG,IAAI,KAAK;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,GAAG,GAAG,KAAK,CAAC,UAAU,IAAI,KAAK,GAAG,EAAE,GAAG,KAAK,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IAE1E,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5E,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1C,0EAA0E;IAC1E,IAAI,YAAY,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnC,MAAM,SAAS,GAAG,IAAI,WAAW,CAAC,YAAY,CAAC,CAAC;IAChD,IAAI,MAAM,GAAG,KAAK,GAAG,EAAE,CAAC;IACxB,IAAI,MAAM,GAAG,YAAY,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC;IACnE,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,YAAY,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;QAC3D,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QAC5C,MAAM,IAAI,CAAC,CAAC;IACd,CAAC;IAED,MAAM,UAAU,GAAG,SAAS,CAAC,YAAY,GAAG,CAAC,CAAE,GAAG,CAAC,CAAC;IACpD,4FAA4F;IAC5F,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAErC,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACxE,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACjC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAEtB,MAAM,EAAE,GAAG,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACzG,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9B,MAAM,EAAE,GAAG,mBAAmB,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5G,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAE9B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,YAAY,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;QAC3D,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAE,CAAC;QACjC,IAAI,IAAI,IAAI,UAAU;YAAE,OAAO,KAAK,CAAC;QACrC,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QACvE,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,oGAAoG;AACpG,uGAAuG;AACvG,2FAA2F;AAC3F,MAAM,UAAU,uBAAuB,CACrC,KAA2B,EAC3B,SAAuC,EACvC,UAAkB,EAClB,UAAkB;IAElB,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAEpC,MAAM,UAAU,GAAG,UAAU,GAAG,CAAC,CAAC;IAClC,MAAM,UAAU,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,IAAI,UAAU,GAAG,UAAU,GAAG,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEvD,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC5E,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,UAAU,CAAC,CAAC;IAC5C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACnD,0FAA0F;QAC1F,OAAO,CAAC,KAAK,CAAC;YACZ,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,yEAAyE;AACzE,EAAE;AACF,qGAAqG;AACrG,kGAAkG;AAClG,+EAA+E;AAC/E,SAAS,iBAAiB,CACxB,GAAS,EACT,KAA2B,EAC3B,EAAwB,EACxB,EAAwB,EACxB,KAAa,EACb,IAAY;IAEZ,MAAM,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO;IAEvB,MAAM,OAAO,GAAG,CAAC,KAAa,EAAW,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,CAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnH,MAAM,MAAM,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,CAAE,CAAC;IAC3F,2FAA2F;IAC3F,MAAM,MAAM,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,CAAE,CAAC;IAE5F,IAAI,MAAc,CAAC;IACnB,IAAI,MAAc,CAAC;IACnB,IAAI,UAAkB,CAAC;IACvB,iGAAiG;IACjG,gGAAgG;IAChG,iGAAiG;IACjG,0EAA0E;IAC1E,IAAI,SAAiB,CAAC;IACtB,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACf,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,UAAU,GAAG,CAAC,CAAC;QACf,SAAS,GAAG,KAAK,GAAG,CAAC,CAAC;IACxB,CAAC;SAAM,IAAI,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;QAC9B,MAAM,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAC3B,MAAM,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAC3B,UAAU,GAAG,CAAC,CAAC;QACf,SAAS,GAAG,KAAK,GAAG,CAAC,CAAC;IACxB,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC7C,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC7C,UAAU,GAAG,CAAC,CAAC;QACf,SAAS,GAAG,KAAK,CAAC;IACpB,CAAC;IAED,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACvC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE9B,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,UAAU,GAAG,KAAK,CAAC;IAEvB,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,SAAS,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,UAAU,GAAG,IAAI,CAAC;QAChC,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QACxB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAExB,IAAI,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACnB,IAAI,UAAU,EAAE,CAAC;gBACf,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;gBACxC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;gBACxC,UAAU,GAAG,KAAK,CAAC;YACrB,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;gBACvC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACtB,CAAC;YACD,SAAS;QACX,CAAC;QAED,mGAAmG;QACnG,sEAAsE;QACtE,IAAI,UAAU,EAAE,CAAC;YACf,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;YACxC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5E,CAAC;QACD,QAAQ,GAAG,CAAC,CAAC;QACb,QAAQ,GAAG,CAAC,CAAC;QACb,UAAU,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,gGAAgG;IAChG,IAAI,UAAU,EAAE,CAAC;QACf,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QACxC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACpD,CAAC;IACD,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC;AAED,mGAAmG;AACnG,uGAAuG;AACvG,gFAAgF;AAChF,SAAS,aAAa,CACpB,IAAwB,EACxB,KAAa,EACb,UAAkB,EAClB,UAAkB;IAElB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;IACzC,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU,GAAI,CAAC;QACzC,IAAI,MAAM,IAAI,UAAU;YAAE,OAAO,IAAI,CAAC;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACnC,MAAM,IAAI,CAAC,CAAC;QACZ,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;QACpB,KAAK,IAAI,CAAC,CAAC;QAEX,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC;YAAE,SAAS;QACpC,IAAI,MAAM,IAAI,UAAU;YAAE,OAAO,IAAI,CAAC;QACtC,IAAI,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,IAAI,CAAC,CAAC;QACZ,OAAO,OAAO,GAAG,CAAC,IAAI,KAAK,GAAG,UAAU,EAAE,CAAC;YACzC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;YACpB,KAAK,IAAI,CAAC,CAAC;YACX,OAAO,IAAI,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAC3B,CAAC;AAED,uGAAuG;AACvG,oGAAoG;AACpG,mEAAmE;AACnE,SAAS,mBAAmB,CAC1B,IAAwB,EACxB,KAAa,EACb,KAA2B,EAC3B,QAAgB,EAChB,iBAAyB,EACzB,UAAkB;IAElB,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAE,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,IAAI,MAAM,IAAI,UAAU;gBAAE,OAAO,IAAI,CAAC;YACtC,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YACxC,MAAM,IAAI,CAAC,CAAC;YACZ,KAAK,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACrE,CAAC;aAAM,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5C,IAAI,MAAM,GAAG,CAAC,GAAG,UAAU;gBAAE,OAAO,IAAI,CAAC;YACzC,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC/B,MAAM,IAAI,CAAC,CAAC;QACd,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;IACxB,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { GlyphOutlineSource, ImportDiagnostic, OpenTypeFontExplanation } from '@flighthq/types/contract';
|
|
2
|
+
export declare function createGlyphOutlineSourceFromOpenTypeFont(source: Readonly<Uint8Array>, diagnostics?: ImportDiagnostic[]): GlyphOutlineSource | null;
|
|
3
|
+
export declare function explainOpenTypeFont(bytes: Readonly<Uint8Array>): OpenTypeFontExplanation;
|
|
4
|
+
//# sourceMappingURL=openTypeGlyphOutlineSource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openTypeGlyphOutlineSource.d.ts","sourceRoot":"","sources":["../src/openTypeGlyphOutlineSource.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAGV,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EAGxB,MAAM,0BAA0B,CAAC;AAmDlC,wBAAgB,wCAAwC,CACtD,MAAM,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC5B,WAAW,CAAC,EAAE,gBAAgB,EAAE,GAC/B,kBAAkB,GAAG,IAAI,CAgC3B;AAOD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,uBAAuB,CAsDxF"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { getDecompressor } from '@flighthq/compression/contract';
|
|
2
|
+
import { detectFontFormat } from '@flighthq/font/contract';
|
|
3
|
+
import { runCffCharstring } from './cffCharstring';
|
|
4
|
+
import { readCffTable } from './cffTable';
|
|
5
|
+
import { readOpenTypeCodepointMap } from './openTypeCmap';
|
|
6
|
+
import { readOpenTypeGlyphOutline, readOpenTypeGlyphRanges } from './openTypeGlyf';
|
|
7
|
+
import { readOpenTypeAdvances, readOpenTypeGlyphCount, readOpenTypeLocaFormat, readOpenTypeMetrics, } from './openTypeMetrics';
|
|
8
|
+
import { readSfntTableDirectory } from './sfntTableDirectory';
|
|
9
|
+
import { readWoffFont, WOFF_COMPRESSION } from './woffFont';
|
|
10
|
+
/**
|
|
11
|
+
* OPENTYPE/TRUETYPE BYTES → THE EXISTING `GlyphOutlineSource` SEAM.
|
|
12
|
+
*
|
|
13
|
+
* This package produces an interface it does not own. `GlyphOutlineSource` is declared in
|
|
14
|
+
* `@flighthq/types`, `@flighthq/font` already turns one into a rasterizer backend, and `@flighthq/swf`
|
|
15
|
+
* already produces one from its own edge-record encoding. What was missing was only a producer that
|
|
16
|
+
* reads a font FILE, so the output shape here is determined rather than designed.
|
|
17
|
+
*
|
|
18
|
+
* WHAT THIS PACKAGE IS NOT. Shaping is `@flighthq/textshaper`, layout is `@flighthq/textlayout`, and
|
|
19
|
+
* static bitmap strikes are `@flighthq/bitmapfont`. This produces outlines, advances, and a codepoint
|
|
20
|
+
* mapping, in unhinted design units, and nothing else.
|
|
21
|
+
*
|
|
22
|
+
* ON PROVENANCE, BECAUSE THIS IS THE MOST ENCUMBERED DOMAIN IN THE TREE. Table tags, field offsets, and
|
|
23
|
+
* flag bits are interface facts — what a published format exists to state — and carry no obligation.
|
|
24
|
+
* The outline reconstruction is an ALGORITHM, and it is built from the specification in Flight's own
|
|
25
|
+
* architecture, never transcribed from a reference implementation. Anything tested against is fetched
|
|
26
|
+
* on demand and committed nowhere; synthetic fonts assembled byte by byte in a test are preferred.
|
|
27
|
+
*/
|
|
28
|
+
// WOFF is a wrapper around the same tables, so it is unwrapped once here and everything downstream — the
|
|
29
|
+
// directory, both outline flavors, CID — reads the rebuilt sfnt without knowing it arrived compressed.
|
|
30
|
+
// Returns the source unchanged for a plain sfnt, and the null sentinel for a container this package does
|
|
31
|
+
// not open or one whose tables need a decompressor nobody registered.
|
|
32
|
+
function unwrapFontContainer(source, diagnostics) {
|
|
33
|
+
const format = detectFontFormat(source);
|
|
34
|
+
if (format === 'truetype' || format === 'opentype')
|
|
35
|
+
return source;
|
|
36
|
+
if (format !== 'woff')
|
|
37
|
+
return null;
|
|
38
|
+
return readWoffFont(source, getDecompressor(WOFF_COMPRESSION), diagnostics);
|
|
39
|
+
}
|
|
40
|
+
// The producer. Returns the null sentinel for any font this package cannot read, which covers cases
|
|
41
|
+
// with completely different remedies — so `explainOpenTypeFont` is the shakeable query that separates
|
|
42
|
+
// them, and a caller diagnosing a rejection calls it rather than reading a message string.
|
|
43
|
+
export function createGlyphOutlineSourceFromOpenTypeFont(source, diagnostics) {
|
|
44
|
+
const parsed = readOpenTypeFontTables(source, diagnostics);
|
|
45
|
+
if (parsed === null)
|
|
46
|
+
return null;
|
|
47
|
+
const { advances, bytes, cff, codepoints, directory, glyphCount, metrics, ranges } = parsed;
|
|
48
|
+
// A bound method object rather than a plain record, because the source owns tables the methods close
|
|
49
|
+
// over. The scratch-free contract is the caller's: `getGlyphOutline` writes into their `out`.
|
|
50
|
+
return {
|
|
51
|
+
getGlyphOutline(out, glyphIndex) {
|
|
52
|
+
if (glyphIndex < 0 || glyphIndex >= glyphCount)
|
|
53
|
+
return false;
|
|
54
|
+
if (cff !== null) {
|
|
55
|
+
const charstring = cff.charstrings[glyphIndex];
|
|
56
|
+
if (charstring === undefined)
|
|
57
|
+
return false;
|
|
58
|
+
// A CID font selects the pool per glyph; every other font shares one. Reading the wrong pool
|
|
59
|
+
// draws plausible geometry rather than failing, so the choice is made here from the table's own
|
|
60
|
+
// structure rather than defaulted anywhere.
|
|
61
|
+
const localSubrs = cff.localSubrsByGlyph?.[glyphIndex] ?? cff.localSubrs;
|
|
62
|
+
return runCffCharstring(out, bytes, charstring, localSubrs, cff.globalSubrs);
|
|
63
|
+
}
|
|
64
|
+
return ranges === null ? false : readOpenTypeGlyphOutline(out, bytes, directory, ranges, glyphIndex);
|
|
65
|
+
},
|
|
66
|
+
getGlyphOutlineAdvance(glyphIndex) {
|
|
67
|
+
return glyphIndex < 0 || glyphIndex >= glyphCount ? 0 : advances[glyphIndex];
|
|
68
|
+
},
|
|
69
|
+
getGlyphOutlineIndexForCodePoint(codePoint) {
|
|
70
|
+
return codepoints.get(codePoint) ?? -1;
|
|
71
|
+
},
|
|
72
|
+
getGlyphOutlineMetrics() {
|
|
73
|
+
return metrics;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// Why `createGlyphOutlineSourceFromOpenTypeFont` returned null, as plain data.
|
|
78
|
+
//
|
|
79
|
+
// Kept separate from the producer, and re-reading the bytes rather than being handed its intermediate
|
|
80
|
+
// state, so a caller that never diagnoses anything does not link it. That costs a second parse on the
|
|
81
|
+
// failure path only, which is the path where nobody is counting microseconds.
|
|
82
|
+
export function explainOpenTypeFont(bytes) {
|
|
83
|
+
const format = detectFontFormat(bytes) ?? '';
|
|
84
|
+
const empty = { format, readableTableCount: 0, table: '', tableCount: 0 };
|
|
85
|
+
if (bytes.byteLength < 12)
|
|
86
|
+
return { ...empty, accepted: false, reason: 'too-short' };
|
|
87
|
+
if (format === '')
|
|
88
|
+
return { ...empty, accepted: false, reason: 'unrecognized' };
|
|
89
|
+
// WOFF is opened now: it wraps these same tables, so it is unwrapped and read like any other font. It
|
|
90
|
+
// needs a DEFLATE decompressor, which `@flighthq/compression` provides on request — an unregistered one
|
|
91
|
+
// is its own reason, because the remedy is one line of registration rather than a different producer.
|
|
92
|
+
// WOFF2 and a collection are still not opened: WOFF2 needs Brotli and a table-transform reversal, and a
|
|
93
|
+
// collection holds several fonts with no rule here for which is meant.
|
|
94
|
+
if (format === 'woff' && getDecompressor(WOFF_COMPRESSION) === null) {
|
|
95
|
+
return { ...empty, accepted: false, reason: 'missing-decompressor' };
|
|
96
|
+
}
|
|
97
|
+
if (format !== 'truetype' && format !== 'opentype' && format !== 'woff') {
|
|
98
|
+
return { ...empty, accepted: false, reason: 'unsupported-container' };
|
|
99
|
+
}
|
|
100
|
+
const unwrapped = format === 'woff' ? readWoffFont(bytes, getDecompressor(WOFF_COMPRESSION)) : bytes;
|
|
101
|
+
if (unwrapped === null)
|
|
102
|
+
return { ...empty, accepted: false, reason: 'malformed-table', table: '' };
|
|
103
|
+
const directory = readSfntTableDirectory(unwrapped);
|
|
104
|
+
if (directory === null)
|
|
105
|
+
return { ...empty, accepted: false, reason: 'too-short' };
|
|
106
|
+
const counted = {
|
|
107
|
+
format,
|
|
108
|
+
readableTableCount: directory.tables.size,
|
|
109
|
+
tableCount: directory.declaredTableCount,
|
|
110
|
+
};
|
|
111
|
+
void unwrapped;
|
|
112
|
+
for (const tag of ['cmap', 'head', 'hhea', 'hmtx', 'maxp']) {
|
|
113
|
+
if (!directory.tables.has(tag))
|
|
114
|
+
return { ...counted, accepted: false, reason: 'missing-required-table', table: tag };
|
|
115
|
+
}
|
|
116
|
+
// `glyf` and `CFF ` are the two outline flavors, and both are read now. `CFF2` is not: it is a
|
|
117
|
+
// different charstring dialect with variation support, so it keeps the stated-absence treatment that
|
|
118
|
+
// `CFF ` had until this landed — a caller holding one needs a different producer, not a repaired file.
|
|
119
|
+
if (!directory.tables.has('glyf')) {
|
|
120
|
+
if (directory.tables.has('CFF2') && !directory.tables.has('CFF ')) {
|
|
121
|
+
return { ...counted, accepted: false, reason: 'unsupported-outlines', table: 'CFF2' };
|
|
122
|
+
}
|
|
123
|
+
if (!directory.tables.has('CFF ')) {
|
|
124
|
+
return { ...counted, accepted: false, reason: 'missing-required-table', table: 'glyf' };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else if (!directory.tables.has('loca')) {
|
|
128
|
+
return { ...counted, accepted: false, reason: 'missing-required-table', table: 'loca' };
|
|
129
|
+
}
|
|
130
|
+
// Every table is present, so anything still wrong is a table that disagrees with its own extent.
|
|
131
|
+
const parsed = readOpenTypeFontTables(bytes);
|
|
132
|
+
if (parsed === null)
|
|
133
|
+
return { ...counted, accepted: false, reason: 'malformed-table', table: '' };
|
|
134
|
+
return { ...counted, accepted: true, reason: 'ok', table: '' };
|
|
135
|
+
}
|
|
136
|
+
// The single parse both entry points run, so the producer and the explanation can never disagree about
|
|
137
|
+
// whether a font is readable — a disagreement there is the defect where a caller is told the font is
|
|
138
|
+
// fine and still gets null.
|
|
139
|
+
function readOpenTypeFontTables(source, diagnostics) {
|
|
140
|
+
const bytes = unwrapFontContainer(source, diagnostics);
|
|
141
|
+
if (bytes === null)
|
|
142
|
+
return null;
|
|
143
|
+
const directory = readSfntTableDirectory(bytes);
|
|
144
|
+
if (directory === null)
|
|
145
|
+
return null;
|
|
146
|
+
const glyphCount = readOpenTypeGlyphCount(bytes, directory);
|
|
147
|
+
if (glyphCount <= 0)
|
|
148
|
+
return null;
|
|
149
|
+
const metrics = readOpenTypeMetrics(bytes, directory);
|
|
150
|
+
const advances = readOpenTypeAdvances(bytes, directory, glyphCount);
|
|
151
|
+
const codepoints = readOpenTypeCodepointMap(bytes, directory);
|
|
152
|
+
if (metrics === null || advances === null || codepoints === null)
|
|
153
|
+
return null;
|
|
154
|
+
// The flavor-independent tables are read first because they are needed either way; only the outline
|
|
155
|
+
// source differs. `glyf` is preferred when both are somehow present, since a font shipping both is
|
|
156
|
+
// malformed and the quadratic path is the one with the longer history here.
|
|
157
|
+
const shared = { advances, bytes, codepoints, directory, glyphCount, metrics };
|
|
158
|
+
if (directory.tables.has('glyf')) {
|
|
159
|
+
const locaFormat = readOpenTypeLocaFormat(bytes, directory);
|
|
160
|
+
if (locaFormat === -1)
|
|
161
|
+
return null;
|
|
162
|
+
const ranges = readOpenTypeGlyphRanges(bytes, directory, glyphCount, locaFormat);
|
|
163
|
+
return ranges === null ? null : { ...shared, cff: null, ranges };
|
|
164
|
+
}
|
|
165
|
+
const cff = readCffTable(bytes, directory);
|
|
166
|
+
return cff === null ? null : { ...shared, cff, ranges: null };
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=openTypeGlyphOutlineSource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openTypeGlyphOutlineSource.js","sourceRoot":"","sources":["../src/openTypeGlyphOutlineSource.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { GlyphOutlineMetrics, SfntTableDirectory } from '@flighthq/types/contract';
|
|
2
|
+
export declare function readOpenTypeAdvances(bytes: Readonly<Uint8Array>, directory: Readonly<SfntTableDirectory>, glyphCount: number): Int32Array | null;
|
|
3
|
+
export declare function readOpenTypeGlyphCount(bytes: Readonly<Uint8Array>, directory: Readonly<SfntTableDirectory>): number;
|
|
4
|
+
export declare function readOpenTypeLocaFormat(bytes: Readonly<Uint8Array>, directory: Readonly<SfntTableDirectory>): number;
|
|
5
|
+
export declare function readOpenTypeMetrics(bytes: Readonly<Uint8Array>, directory: Readonly<SfntTableDirectory>): GlyphOutlineMetrics | null;
|
|
6
|
+
//# sourceMappingURL=openTypeMetrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openTypeMetrics.d.ts","sourceRoot":"","sources":["../src/openTypeMetrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAexF,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC3B,SAAS,EAAE,QAAQ,CAAC,kBAAkB,CAAC,EACvC,UAAU,EAAE,MAAM,GACjB,UAAU,GAAG,IAAI,CAgBnB;AAKD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,kBAAkB,CAAC,GAAG,MAAM,CAInH;AAKD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,kBAAkB,CAAC,GAAG,MAAM,CAKnH;AAID,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC3B,SAAS,EAAE,QAAQ,CAAC,kBAAkB,CAAC,GACtC,mBAAmB,GAAG,IAAI,CAqB5B"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// The tables every outline source needs regardless of which flavor carries the outlines: `head` for the
|
|
2
|
+
// design-unit scale and the `loca` index width, `hhea` for the vertical metrics and the count of long
|
|
3
|
+
// horizontal metrics, `maxp` for the glyph count, and `hmtx` for per-glyph advances.
|
|
4
|
+
//
|
|
5
|
+
// All four are read into plain numbers here rather than kept as byte ranges, because they are small,
|
|
6
|
+
// fixed-layout, and read on every glyph — the alternative is re-reading a header inside a hot loop.
|
|
7
|
+
// Per-glyph horizontal advances, in design units, indexed by glyph id.
|
|
8
|
+
//
|
|
9
|
+
// `hmtx` is run-length shaped rather than flat: it holds `numberOfHMetrics` paired advance/bearing
|
|
10
|
+
// records followed by bearings alone, so every glyph past that count repeats the LAST stated advance.
|
|
11
|
+
// That is how monospaced and CJK fonts stay small, and reading it as a flat array silently gives every
|
|
12
|
+
// such glyph the wrong width. Expanded once here into a dense array so lookup is a single index.
|
|
13
|
+
export function readOpenTypeAdvances(bytes, directory, glyphCount) {
|
|
14
|
+
const hhea = directory.tables.get('hhea');
|
|
15
|
+
const hmtx = directory.tables.get('hmtx');
|
|
16
|
+
if (hhea === undefined || hmtx === undefined || hhea.length < 36)
|
|
17
|
+
return null;
|
|
18
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
19
|
+
const longMetricCount = view.getUint16(hhea.offset + 34);
|
|
20
|
+
if (longMetricCount === 0 || longMetricCount * 4 > hmtx.length)
|
|
21
|
+
return null;
|
|
22
|
+
const advances = new Int32Array(glyphCount);
|
|
23
|
+
let lastAdvance = 0;
|
|
24
|
+
for (let glyph = 0; glyph < glyphCount; glyph += 1) {
|
|
25
|
+
if (glyph < longMetricCount)
|
|
26
|
+
lastAdvance = view.getUint16(hmtx.offset + glyph * 4);
|
|
27
|
+
advances[glyph] = lastAdvance;
|
|
28
|
+
}
|
|
29
|
+
return advances;
|
|
30
|
+
}
|
|
31
|
+
// The number of glyphs the font declares, which bounds every glyph-indexed lookup. Returns -1 rather
|
|
32
|
+
// than null so a caller can compare against it without unwrapping; a font with no `maxp` has no
|
|
33
|
+
// defensible glyph count and every index is out of range.
|
|
34
|
+
export function readOpenTypeGlyphCount(bytes, directory) {
|
|
35
|
+
const maxp = directory.tables.get('maxp');
|
|
36
|
+
if (maxp === undefined || maxp.length < 6)
|
|
37
|
+
return -1;
|
|
38
|
+
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint16(maxp.offset + 4);
|
|
39
|
+
}
|
|
40
|
+
// `head.indexToLocFormat`: 0 means `loca` holds 16-bit halved offsets, 1 means 32-bit byte offsets.
|
|
41
|
+
// Returns -1 when the table is absent or the value is neither, which is a malformed table rather than a
|
|
42
|
+
// value to guess at — guessing produces plausible garbage outlines instead of a clean rejection.
|
|
43
|
+
export function readOpenTypeLocaFormat(bytes, directory) {
|
|
44
|
+
const head = directory.tables.get('head');
|
|
45
|
+
if (head === undefined || head.length < 54)
|
|
46
|
+
return -1;
|
|
47
|
+
const format = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getInt16(head.offset + 50);
|
|
48
|
+
return format === 0 || format === 1 ? format : -1;
|
|
49
|
+
}
|
|
50
|
+
// Font-wide vertical metrics, in the design units `unitsPerEm` denominates. Returns the null sentinel
|
|
51
|
+
// when either table is absent or too short to hold the fields being read.
|
|
52
|
+
export function readOpenTypeMetrics(bytes, directory) {
|
|
53
|
+
const head = directory.tables.get('head');
|
|
54
|
+
const hhea = directory.tables.get('hhea');
|
|
55
|
+
if (head === undefined || hhea === undefined || head.length < 54 || hhea.length < 36)
|
|
56
|
+
return null;
|
|
57
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
58
|
+
const unitsPerEm = view.getUint16(head.offset + 18);
|
|
59
|
+
// A zero would make every scaled coordinate a division by zero downstream, and it is the one value in
|
|
60
|
+
// this table that cannot be defaulted sensibly, so it is treated as a malformed table rather than
|
|
61
|
+
// silently replaced.
|
|
62
|
+
if (unitsPerEm === 0)
|
|
63
|
+
return null;
|
|
64
|
+
return {
|
|
65
|
+
// `hhea` states ascender as a positive distance above the baseline and descender as a negative one.
|
|
66
|
+
// `GlyphOutlineMetrics` documents both as POSITIVE distances from the baseline, so the descender's
|
|
67
|
+
// sign is flipped here — at the seam, once, rather than in every consumer.
|
|
68
|
+
ascent: view.getInt16(hhea.offset + 4),
|
|
69
|
+
descent: -view.getInt16(hhea.offset + 6),
|
|
70
|
+
lineGap: view.getInt16(hhea.offset + 8),
|
|
71
|
+
unitsPerEm,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=openTypeMetrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openTypeMetrics.js","sourceRoot":"","sources":["../src/openTypeMetrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface SyntheticGlyph {
|
|
2
|
+
endPoints: readonly number[];
|
|
3
|
+
points: readonly (readonly [number, number, boolean])[];
|
|
4
|
+
}
|
|
5
|
+
export interface SyntheticFontOptions {
|
|
6
|
+
advances?: readonly number[];
|
|
7
|
+
codepoints?: ReadonlyMap<number, number>;
|
|
8
|
+
charstrings?: readonly Uint8Array[];
|
|
9
|
+
cid?: Readonly<SyntheticCidOptions>;
|
|
10
|
+
glyphs?: readonly SyntheticGlyph[];
|
|
11
|
+
flavor?: 'opentype' | 'truetype';
|
|
12
|
+
omitTable?: string;
|
|
13
|
+
unitsPerEm?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function cffOperand(value: number): number;
|
|
16
|
+
export declare function createSyntheticFont(options?: Readonly<SyntheticFontOptions>): Uint8Array;
|
|
17
|
+
export declare function emptySyntheticGlyph(): SyntheticGlyph;
|
|
18
|
+
export declare function encodeSyntheticCff(charstrings: readonly Uint8Array[], cid?: Readonly<SyntheticCidOptions>): Uint8Array;
|
|
19
|
+
export interface SyntheticCidOptions {
|
|
20
|
+
fdSelect: readonly number[];
|
|
21
|
+
pools: readonly (readonly Uint8Array[])[];
|
|
22
|
+
}
|
|
23
|
+
export declare function encodeSyntheticWoff(sfnt: Readonly<Uint8Array>, reverseTableOrder?: boolean): Uint8Array;
|
|
24
|
+
export declare function encodeSyntheticWoff2(sfnt: Readonly<Uint8Array>, transformTags?: readonly string[]): Uint8Array;
|
|
25
|
+
export declare function ringSyntheticGlyph(reverseCounter?: boolean, flipBoth?: boolean): SyntheticGlyph;
|
|
26
|
+
export declare function squareSyntheticGlyph(size: number): SyntheticGlyph;
|
|
27
|
+
//# sourceMappingURL=openTypeTestHelper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openTypeTestHelper.d.ts","sourceRoot":"","sources":["../src/openTypeTestHelper.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,cAAc;IAE7B,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAE7B,MAAM,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;CACzD;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAE7B,UAAU,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAGzC,WAAW,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;IAEpC,GAAG,CAAC,EAAE,QAAQ,CAAC,mBAAmB,CAAC,CAAC;IACpC,MAAM,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IAGnC,MAAM,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAGD,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,QAAQ,CAAC,oBAAoB,CAAM,GAAG,UAAU,CAkC5F;AAED,wBAAgB,mBAAmB,IAAI,cAAc,CAEpD;AAKD,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,SAAS,UAAU,EAAE,EAClC,GAAG,CAAC,EAAE,QAAQ,CAAC,mBAAmB,CAAC,GAClC,UAAU,CAuEZ;AAED,MAAM,WAAW,mBAAmB;IAElC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAE5B,KAAK,EAAE,SAAS,CAAC,SAAS,UAAU,EAAE,CAAC,EAAE,CAAC;CAC3C;AAwCD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,UAAQ,GAAG,UAAU,CAyCrG;AA2KD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,aAAa,GAAE,SAAS,MAAM,EAAO,GAAG,UAAU,CA2ClH;AASD,wBAAgB,kBAAkB,CAAC,cAAc,UAAQ,EAAE,QAAQ,UAAQ,GAAG,cAAc,CAkB3F;AA8FD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAUjE"}
|