@cantoo/fontkit 2.0.5 → 2.0.7
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/browser-module.d.ts +239 -0
- package/dist/browser-module.mjs +4423 -1117
- package/dist/browser-module.mjs.map +1 -1
- package/dist/browser.cjs +4424 -1117
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.ts +239 -0
- package/dist/fontkit.umd.d.ts +239 -0
- package/dist/fontkit.umd.js +22076 -0
- package/dist/fontkit.umd.js.map +7 -0
- package/dist/index.d.ts +239 -0
- package/dist/main.cjs +4424 -1117
- package/dist/main.cjs.map +1 -1
- package/dist/main.d.ts +239 -0
- package/dist/module.d.ts +239 -0
- package/dist/module.mjs +4423 -1117
- package/dist/module.mjs.map +1 -1
- package/package.json +22 -5
- package/src/CmapProcessor.js +59 -20
- package/src/DFont.js +114 -14
- package/src/TTFFont.js +259 -57
- package/src/TrueTypeCollection.js +54 -10
- package/src/WOFF2Font.js +128 -16
- package/src/WOFFFont.js +45 -8
- package/src/aat/AATFeatureMap.js +62 -18
- package/src/aat/AATLayoutEngine.js +33 -1
- package/src/aat/AATLookupTable.js +22 -4
- package/src/aat/AATMorxProcessor.js +189 -52
- package/src/aat/AATStateMachine.js +32 -0
- package/src/base.js +19 -0
- package/src/cff/CFFCharsets.js +3 -0
- package/src/cff/CFFDict.js +79 -9
- package/src/cff/CFFEncodings.js +2 -0
- package/src/cff/CFFFont.js +119 -23
- package/src/cff/CFFIndex.js +62 -5
- package/src/cff/CFFOperand.js +52 -17
- package/src/cff/CFFPointer.js +41 -2
- package/src/cff/CFFPrivateDict.js +14 -4
- package/src/cff/CFFStandardStrings.js +1 -0
- package/src/cff/CFFTop.js +103 -10
- package/src/decorators.js +32 -13
- package/src/encodings.js +22 -5
- package/src/fs.js +10 -0
- package/src/glyph/BBox.js +14 -0
- package/src/glyph/CFFGlyph.js +290 -148
- package/src/glyph/COLRGlyph.js +38 -3
- package/src/glyph/Glyph.js +68 -9
- package/src/glyph/GlyphVariationProcessor.js +135 -34
- package/src/glyph/Path.js +107 -20
- package/src/glyph/SBIXGlyph.js +57 -8
- package/src/glyph/StandardNames.js +1 -0
- package/src/glyph/TTFGlyph.js +176 -29
- package/src/glyph/TTFGlyphEncoder.js +32 -0
- package/src/glyph/WOFF2Glyph.js +23 -4
- package/src/layout/GlyphPosition.js +6 -0
- package/src/layout/GlyphRun.js +25 -7
- package/src/layout/KernProcessor.js +62 -3
- package/src/layout/LayoutEngine.js +86 -12
- package/src/layout/Script.js +27 -1
- package/src/layout/UnicodeLayoutEngine.js +29 -4
- package/src/opentype/GPOSProcessor.js +156 -45
- package/src/opentype/GSUBProcessor.js +92 -33
- package/src/opentype/GlyphInfo.js +52 -4
- package/src/opentype/GlyphIterator.js +51 -2
- package/src/opentype/OTLayoutEngine.js +66 -8
- package/src/opentype/OTProcessor.js +283 -70
- package/src/opentype/ShapingPlan.js +51 -3
- package/src/opentype/shapers/ArabicShaper.js +19 -0
- package/src/opentype/shapers/DefaultShaper.js +51 -7
- package/src/opentype/shapers/HangulShaper.js +85 -7
- package/src/opentype/shapers/HebrewShaper.js +45 -11
- package/src/opentype/shapers/IndicShaper.js +141 -26
- package/src/opentype/shapers/ThaiShaper.js +35 -4
- package/src/opentype/shapers/UniversalShaper.js +83 -17
- package/src/opentype/shapers/index.js +15 -2
- package/src/packages/unicode-properties/data-trie.js +1 -0
- package/src/packages/unicode-properties/data.js +12 -0
- package/src/packages/unicode-properties/dist/index.cjs +22 -9
- package/src/packages/unicode-properties/dist/index.cjs.map +2 -2
- package/src/packages/unicode-properties/dist/index.mjs +21 -9
- package/src/packages/unicode-properties/dist/index.mjs.map +2 -2
- package/src/packages/unicode-properties/index.js +65 -0
- package/src/packages/unicode-properties/package.json +1 -1
- package/src/packages/unicode-trie/builder.js +128 -7
- package/src/packages/unicode-trie/dist/builder.cjs +129 -16
- package/src/packages/unicode-trie/dist/builder.cjs.map +2 -2
- package/src/packages/unicode-trie/dist/builder.mjs +128 -16
- package/src/packages/unicode-trie/dist/builder.mjs.map +2 -2
- package/src/packages/unicode-trie/dist/index.cjs +22 -9
- package/src/packages/unicode-trie/dist/index.cjs.map +2 -2
- package/src/packages/unicode-trie/dist/index.mjs +21 -9
- package/src/packages/unicode-trie/dist/index.mjs.map +2 -2
- package/src/packages/unicode-trie/index.js +62 -10
- package/src/packages/unicode-trie/package.json +1 -1
- package/src/packages/unicode-trie/swap.js +13 -0
- package/src/subset/CFFSubset.js +112 -22
- package/src/subset/Subset.js +21 -1
- package/src/subset/TTFSubset.js +79 -36
- package/src/tables/BASE.js +1 -0
- package/src/tables/COLR.js +1 -0
- package/src/tables/CPAL.js +1 -0
- package/src/tables/DSIG.js +1 -0
- package/src/tables/EBDT.js +10 -3
- package/src/tables/EBLC.js +67 -7
- package/src/tables/GDEF.js +1 -0
- package/src/tables/GPOS.js +79 -16
- package/src/tables/GSUB.js +14 -3
- package/src/tables/HVAR.js +51 -5
- package/src/tables/JSTF.js +3 -2
- package/src/tables/LTSH.js +1 -0
- package/src/tables/OS2.js +1 -0
- package/src/tables/PCLT.js +1 -0
- package/src/tables/STAT.js +46 -6
- package/src/tables/VDMX.js +1 -0
- package/src/tables/VORG.js +1 -0
- package/src/tables/WOFF2Directory.js +56 -8
- package/src/tables/WOFFDirectory.js +14 -2
- package/src/tables/aat.js +160 -35
- package/src/tables/avar.js +1 -0
- package/src/tables/bsln.js +3 -2
- package/src/tables/cmap.js +26 -6
- package/src/tables/cvt.js +1 -0
- package/src/tables/directory.js +44 -16
- package/src/tables/feat.js +24 -2
- package/src/tables/fpgm.js +1 -0
- package/src/tables/fvar.js +32 -4
- package/src/tables/gasp.js +1 -0
- package/src/tables/glyf.js +1 -0
- package/src/tables/gvar.js +29 -2
- package/src/tables/hdmx.js +12 -1
- package/src/tables/head.js +1 -0
- package/src/tables/hhea.js +1 -0
- package/src/tables/hmtx.js +18 -2
- package/src/tables/index.js +1 -0
- package/src/tables/just.js +15 -4
- package/src/tables/kern.js +43 -6
- package/src/tables/loca.js +21 -8
- package/src/tables/maxp.js +1 -0
- package/src/tables/morx.js +14 -7
- package/src/tables/name.js +104 -23
- package/src/tables/opbd.js +2 -1
- package/src/tables/opentype.js +39 -4
- package/src/tables/post.js +8 -1
- package/src/tables/prep.js +1 -0
- package/src/tables/sbix.js +12 -1
- package/src/tables/variations.js +23 -3
- package/src/tables/vhea.js +1 -0
- package/src/tables/vmtx.js +18 -2
- package/src/utils.js +15 -0
- package/types/assets.d.ts +13 -0
- package/types/deps.d.ts +83 -0
- package/types/fontkit.d.ts +1324 -0
- package/types/index.d.ts +239 -0
- package/types/restructure.d.ts +354 -0
package/src/glyph/TTFGlyph.js
CHANGED
|
@@ -3,6 +3,13 @@ import Path from './Path';
|
|
|
3
3
|
import BBox from './BBox';
|
|
4
4
|
import * as r from 'restructure';
|
|
5
5
|
|
|
6
|
+
/** @typedef {import('restructure').DecodeStream} DecodeStream */
|
|
7
|
+
/** @typedef {import('restructure').StructValue} StructValue */
|
|
8
|
+
/** @typedef {import('../../types/fontkit').DecodedGlyf} DecodedGlyf */
|
|
9
|
+
/** @typedef {import('../../types/fontkit').GlyphComponent} GlyphComponent */
|
|
10
|
+
/** @typedef {import('../../types/fontkit').GlyphMetrics} GlyphMetrics */
|
|
11
|
+
/** @typedef {import('../../types/fontkit').GlyphPoint} GlyphPoint */
|
|
12
|
+
|
|
6
13
|
// The header for both simple and composite glyphs
|
|
7
14
|
let GlyfHeader = new r.Struct({
|
|
8
15
|
numberOfContours: r.int16, // if negative, this is a composite glyph
|
|
@@ -34,15 +41,67 @@ const _OVERLAP_COMPOUND = 1 << 10;
|
|
|
34
41
|
const _SCALED_COMPONENT_OFFSET = 1 << 11;
|
|
35
42
|
const _UNSCALED_COMPONENT_OFFSET = 1 << 12;
|
|
36
43
|
|
|
44
|
+
/**
|
|
45
|
+
* @param {StructValue} raw
|
|
46
|
+
* @returns {DecodedGlyf}
|
|
47
|
+
*/
|
|
48
|
+
function decodeGlyfHeader(raw) {
|
|
49
|
+
let numberOfContours = raw.numberOfContours;
|
|
50
|
+
let xMin = raw.xMin;
|
|
51
|
+
let yMin = raw.yMin;
|
|
52
|
+
let xMax = raw.xMax;
|
|
53
|
+
let yMax = raw.yMax;
|
|
54
|
+
if (
|
|
55
|
+
typeof numberOfContours !== 'number'
|
|
56
|
+
|| typeof xMin !== 'number'
|
|
57
|
+
|| typeof yMin !== 'number'
|
|
58
|
+
|| typeof xMax !== 'number'
|
|
59
|
+
|| typeof yMax !== 'number'
|
|
60
|
+
) {
|
|
61
|
+
throw new Error('Invalid glyf header');
|
|
62
|
+
}
|
|
63
|
+
return { numberOfContours, xMin, yMin, xMax, yMax };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {unknown[]} values
|
|
68
|
+
* @returns {number[]}
|
|
69
|
+
*/
|
|
70
|
+
function asNumberArray(values) {
|
|
71
|
+
/** @type {number[]} */
|
|
72
|
+
let out = [];
|
|
73
|
+
for (let i = 0; i < values.length; i++) {
|
|
74
|
+
let v = values[i];
|
|
75
|
+
if (typeof v !== 'number') {
|
|
76
|
+
throw new Error('Expected number array from glyf decode');
|
|
77
|
+
}
|
|
78
|
+
out.push(v);
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
|
|
37
83
|
// Represents a point in a simple glyph
|
|
38
84
|
export class Point {
|
|
85
|
+
/**
|
|
86
|
+
* @param {boolean} onCurve
|
|
87
|
+
* @param {boolean} endContour
|
|
88
|
+
* @param {number} [x]
|
|
89
|
+
* @param {number} [y]
|
|
90
|
+
*/
|
|
39
91
|
constructor(onCurve, endContour, x = 0, y = 0) {
|
|
92
|
+
/** @type {boolean} */
|
|
40
93
|
this.onCurve = onCurve;
|
|
94
|
+
/** @type {boolean} */
|
|
41
95
|
this.endContour = endContour;
|
|
96
|
+
/** @type {number} */
|
|
42
97
|
this.x = x;
|
|
98
|
+
/** @type {number} */
|
|
43
99
|
this.y = y;
|
|
44
100
|
}
|
|
45
101
|
|
|
102
|
+
/**
|
|
103
|
+
* @returns {Point}
|
|
104
|
+
*/
|
|
46
105
|
copy() {
|
|
47
106
|
return new Point(this.onCurve, this.endContour, this.x, this.y);
|
|
48
107
|
}
|
|
@@ -50,13 +109,28 @@ export class Point {
|
|
|
50
109
|
|
|
51
110
|
// Represents a component in a composite glyph
|
|
52
111
|
class Component {
|
|
112
|
+
/**
|
|
113
|
+
* @param {number} glyphID
|
|
114
|
+
* @param {number} dx
|
|
115
|
+
* @param {number} dy
|
|
116
|
+
*/
|
|
53
117
|
constructor(glyphID, dx, dy) {
|
|
118
|
+
/** @type {number} */
|
|
54
119
|
this.glyphID = glyphID;
|
|
120
|
+
/** @type {number} */
|
|
55
121
|
this.dx = dx;
|
|
122
|
+
/** @type {number} */
|
|
56
123
|
this.dy = dy;
|
|
124
|
+
/** @type {number} */
|
|
57
125
|
this.pos = 0;
|
|
58
|
-
|
|
59
|
-
this.
|
|
126
|
+
/** @type {number} */
|
|
127
|
+
this.scaleX = 1;
|
|
128
|
+
/** @type {number} */
|
|
129
|
+
this.scaleY = 1;
|
|
130
|
+
/** @type {number} */
|
|
131
|
+
this.scale01 = 0;
|
|
132
|
+
/** @type {number} */
|
|
133
|
+
this.scale10 = 0;
|
|
60
134
|
}
|
|
61
135
|
}
|
|
62
136
|
|
|
@@ -66,7 +140,11 @@ class Component {
|
|
|
66
140
|
export default class TTFGlyph extends Glyph {
|
|
67
141
|
type = 'TTF';
|
|
68
142
|
|
|
69
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Parses just the glyph header and returns the bounding box
|
|
145
|
+
* @param {boolean} [internal]
|
|
146
|
+
* @returns {BBox}
|
|
147
|
+
*/
|
|
70
148
|
_getCBox(internal) {
|
|
71
149
|
// We need to decode the glyph if variation processing is requested,
|
|
72
150
|
// so it's easier just to recompute the path's cbox after decoding.
|
|
@@ -74,19 +152,34 @@ export default class TTFGlyph extends Glyph {
|
|
|
74
152
|
return this.path.cbox;
|
|
75
153
|
}
|
|
76
154
|
|
|
77
|
-
let
|
|
155
|
+
let loca = this._font.loca;
|
|
156
|
+
if (!loca) {
|
|
157
|
+
return Object.freeze(new BBox(0, 0, 0, 0));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let glyfPos = loca.offsets[this.id];
|
|
78
161
|
// No outline data (e.g. space): avoid decoding the next glyph's header
|
|
79
|
-
if (glyfPos ===
|
|
162
|
+
if (glyfPos === loca.offsets[this.id + 1]) {
|
|
80
163
|
return Object.freeze(new BBox(0, 0, 0, 0));
|
|
81
164
|
}
|
|
82
165
|
|
|
83
166
|
let stream = this._font._getTableStream('glyf');
|
|
167
|
+
if (!stream) {
|
|
168
|
+
return Object.freeze(new BBox(0, 0, 0, 0));
|
|
169
|
+
}
|
|
84
170
|
stream.pos += glyfPos;
|
|
85
|
-
let glyph = GlyfHeader.decode(stream);
|
|
171
|
+
let glyph = decodeGlyfHeader(GlyfHeader.decode(stream));
|
|
86
172
|
return Object.freeze(new BBox(glyph.xMin, glyph.yMin, glyph.xMax, glyph.yMax));
|
|
87
173
|
}
|
|
88
174
|
|
|
89
|
-
|
|
175
|
+
/**
|
|
176
|
+
* Parses a single glyph coordinate
|
|
177
|
+
* @param {DecodeStream} stream
|
|
178
|
+
* @param {number} prev
|
|
179
|
+
* @param {number} short
|
|
180
|
+
* @param {number} same
|
|
181
|
+
* @returns {number}
|
|
182
|
+
*/
|
|
90
183
|
_parseGlyphCoord(stream, prev, short, same) {
|
|
91
184
|
let val;
|
|
92
185
|
if (short) {
|
|
@@ -107,20 +200,31 @@ export default class TTFGlyph extends Glyph {
|
|
|
107
200
|
return val;
|
|
108
201
|
}
|
|
109
202
|
|
|
110
|
-
|
|
111
|
-
|
|
203
|
+
/**
|
|
204
|
+
* Decodes the glyph data into points for simple glyphs,
|
|
205
|
+
* or components for composite glyphs
|
|
206
|
+
* @returns {DecodedGlyf | null}
|
|
207
|
+
*/
|
|
112
208
|
_decode() {
|
|
113
|
-
let
|
|
114
|
-
|
|
209
|
+
let loca = this._font.loca;
|
|
210
|
+
if (!loca) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
let glyfPos = loca.offsets[this.id];
|
|
215
|
+
let nextPos = loca.offsets[this.id + 1];
|
|
115
216
|
|
|
116
217
|
// Nothing to do if there is no data for this glyph
|
|
117
218
|
if (glyfPos === nextPos) { return null; }
|
|
118
219
|
|
|
119
220
|
let stream = this._font._getTableStream('glyf');
|
|
221
|
+
if (!stream) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
120
224
|
stream.pos += glyfPos;
|
|
121
225
|
let startPos = stream.pos;
|
|
122
226
|
|
|
123
|
-
let glyph = GlyfHeader.decode(stream);
|
|
227
|
+
let glyph = decodeGlyfHeader(GlyfHeader.decode(stream));
|
|
124
228
|
|
|
125
229
|
if (glyph.numberOfContours > 0) {
|
|
126
230
|
this._decodeSimple(glyph, stream);
|
|
@@ -131,13 +235,19 @@ export default class TTFGlyph extends Glyph {
|
|
|
131
235
|
return glyph;
|
|
132
236
|
}
|
|
133
237
|
|
|
238
|
+
/**
|
|
239
|
+
* @param {DecodedGlyf} glyph
|
|
240
|
+
* @param {DecodeStream} stream
|
|
241
|
+
* @returns {void}
|
|
242
|
+
*/
|
|
134
243
|
_decodeSimple(glyph, stream) {
|
|
135
244
|
// this is a simple glyph
|
|
136
245
|
glyph.points = [];
|
|
137
246
|
|
|
138
|
-
let endPtsOfContours = new r.Array(r.uint16, glyph.numberOfContours).decode(stream);
|
|
139
|
-
glyph.instructions = new r.Array(r.uint8, r.uint16).decode(stream);
|
|
247
|
+
let endPtsOfContours = asNumberArray(new r.Array(r.uint16, glyph.numberOfContours).decode(stream));
|
|
248
|
+
glyph.instructions = asNumberArray(new r.Array(r.uint8, r.uint16).decode(stream));
|
|
140
249
|
|
|
250
|
+
/** @type {number[]} */
|
|
141
251
|
let flags = [];
|
|
142
252
|
let numCoords = endPtsOfContours[endPtsOfContours.length - 1] + 1;
|
|
143
253
|
|
|
@@ -163,13 +273,15 @@ export default class TTFGlyph extends Glyph {
|
|
|
163
273
|
let px = 0;
|
|
164
274
|
for (let i = 0; i < flags.length; i++) {
|
|
165
275
|
let flag = flags[i];
|
|
166
|
-
glyph.points[i]
|
|
276
|
+
let point = glyph.points[i];
|
|
277
|
+
point.x = px = this._parseGlyphCoord(stream, px, flag & X_SHORT_VECTOR, flag & SAME_X);
|
|
167
278
|
}
|
|
168
279
|
|
|
169
280
|
let py = 0;
|
|
170
281
|
for (let i = 0; i < flags.length; i++) {
|
|
171
282
|
let flag = flags[i];
|
|
172
|
-
glyph.points[i]
|
|
283
|
+
let point = glyph.points[i];
|
|
284
|
+
point.y = py = this._parseGlyphCoord(stream, py, flag & Y_SHORT_VECTOR, flag & SAME_Y);
|
|
173
285
|
}
|
|
174
286
|
|
|
175
287
|
if (this._font._variationProcessor) {
|
|
@@ -183,6 +295,12 @@ export default class TTFGlyph extends Glyph {
|
|
|
183
295
|
return;
|
|
184
296
|
}
|
|
185
297
|
|
|
298
|
+
/**
|
|
299
|
+
* @param {DecodedGlyf} glyph
|
|
300
|
+
* @param {DecodeStream} stream
|
|
301
|
+
* @param {number} [offset]
|
|
302
|
+
* @returns {boolean}
|
|
303
|
+
*/
|
|
186
304
|
_decodeComposite(glyph, stream, offset = 0) {
|
|
187
305
|
// this is a composite glyph
|
|
188
306
|
glyph.components = [];
|
|
@@ -227,6 +345,7 @@ export default class TTFGlyph extends Glyph {
|
|
|
227
345
|
}
|
|
228
346
|
|
|
229
347
|
if (this._font._variationProcessor) {
|
|
348
|
+
/** @type {Point[]} */
|
|
230
349
|
let points = [];
|
|
231
350
|
for (let j = 0; j < glyph.components.length; j++) {
|
|
232
351
|
let component = glyph.components[j];
|
|
@@ -248,6 +367,10 @@ export default class TTFGlyph extends Glyph {
|
|
|
248
367
|
return haveInstructions;
|
|
249
368
|
}
|
|
250
369
|
|
|
370
|
+
/**
|
|
371
|
+
* @param {DecodedGlyf} glyph
|
|
372
|
+
* @returns {Point[]}
|
|
373
|
+
*/
|
|
251
374
|
_getPhantomPoints(glyph) {
|
|
252
375
|
let cbox = this._getCBox(true);
|
|
253
376
|
if (this._metrics == null) {
|
|
@@ -264,20 +387,25 @@ export default class TTFGlyph extends Glyph {
|
|
|
264
387
|
];
|
|
265
388
|
}
|
|
266
389
|
|
|
267
|
-
|
|
390
|
+
/**
|
|
391
|
+
* Decodes font data, resolves composite glyphs, and returns an array of contours
|
|
392
|
+
* @returns {Point[][]}
|
|
393
|
+
*/
|
|
268
394
|
_getContours() {
|
|
269
395
|
let glyph = this._decode();
|
|
270
396
|
if (!glyph) {
|
|
271
397
|
return [];
|
|
272
398
|
}
|
|
273
399
|
|
|
400
|
+
/** @type {Point[]} */
|
|
274
401
|
let points = [];
|
|
275
402
|
|
|
276
403
|
if (glyph.numberOfContours < 0) {
|
|
277
404
|
// resolve composite glyphs
|
|
278
|
-
|
|
405
|
+
let components = glyph.components || [];
|
|
406
|
+
for (let component of components) {
|
|
279
407
|
let componentGlyph = this._font._getBaseGlyph(component.glyphID);
|
|
280
|
-
if (!componentGlyph) {
|
|
408
|
+
if (!componentGlyph || typeof componentGlyph._getContours !== 'function') {
|
|
281
409
|
continue;
|
|
282
410
|
}
|
|
283
411
|
|
|
@@ -299,18 +427,22 @@ export default class TTFGlyph extends Glyph {
|
|
|
299
427
|
// Recompute and cache metrics if we performed variation processing, and don't have an HVAR table
|
|
300
428
|
if (glyph.phantomPoints && !this._font.directory.tables.HVAR) {
|
|
301
429
|
let m = this._metrics;
|
|
302
|
-
m
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
430
|
+
if (m) {
|
|
431
|
+
m.advanceWidth = glyph.phantomPoints[1].x - glyph.phantomPoints[0].x;
|
|
432
|
+
m.advanceHeight = glyph.phantomPoints[3].y - glyph.phantomPoints[2].y;
|
|
433
|
+
m.leftBearing = glyph.xMin - glyph.phantomPoints[0].x;
|
|
434
|
+
m.topBearing = glyph.phantomPoints[2].y - glyph.yMax;
|
|
435
|
+
m.rightBearing = m.advanceWidth - m.leftBearing - m.width;
|
|
436
|
+
m.bottomBearing = m.advanceHeight - m.topBearing - m.height;
|
|
437
|
+
}
|
|
308
438
|
}
|
|
309
439
|
|
|
440
|
+
/** @type {Point[][]} */
|
|
310
441
|
let contours = [];
|
|
442
|
+
/** @type {Point[]} */
|
|
311
443
|
let cur = [];
|
|
312
444
|
for (let k = 0; k < points.length; k++) {
|
|
313
|
-
|
|
445
|
+
let point = points[k];
|
|
314
446
|
cur.push(point);
|
|
315
447
|
if (point.endContour) {
|
|
316
448
|
contours.push(cur);
|
|
@@ -321,6 +453,9 @@ export default class TTFGlyph extends Glyph {
|
|
|
321
453
|
return contours;
|
|
322
454
|
}
|
|
323
455
|
|
|
456
|
+
/**
|
|
457
|
+
* @returns {GlyphMetrics}
|
|
458
|
+
*/
|
|
324
459
|
_getMetrics() {
|
|
325
460
|
if (this._metrics) {
|
|
326
461
|
return this._metrics;
|
|
@@ -334,10 +469,18 @@ export default class TTFGlyph extends Glyph {
|
|
|
334
469
|
this.path;
|
|
335
470
|
}
|
|
336
471
|
|
|
337
|
-
|
|
472
|
+
// super._getMetrics always assigns this._metrics
|
|
473
|
+
let metrics = this._metrics;
|
|
474
|
+
if (!metrics) {
|
|
475
|
+
throw new Error('Failed to compute glyph metrics');
|
|
476
|
+
}
|
|
477
|
+
return metrics;
|
|
338
478
|
}
|
|
339
479
|
|
|
340
|
-
|
|
480
|
+
/**
|
|
481
|
+
* Converts contours to a Path object that can be rendered
|
|
482
|
+
* @returns {Path}
|
|
483
|
+
*/
|
|
341
484
|
_getPath() {
|
|
342
485
|
let contours = this._getContours();
|
|
343
486
|
let path = new Path();
|
|
@@ -347,7 +490,8 @@ export default class TTFGlyph extends Glyph {
|
|
|
347
490
|
let firstPt = contour[0];
|
|
348
491
|
let lastPt = contour[contour.length - 1];
|
|
349
492
|
let start = 0;
|
|
350
|
-
|
|
493
|
+
/** @type {Point | null} */
|
|
494
|
+
let curvePt = null;
|
|
351
495
|
|
|
352
496
|
if (firstPt.onCurve) {
|
|
353
497
|
// The first point will be consumed by the moveTo command, so skip in the loop
|
|
@@ -381,6 +525,9 @@ export default class TTFGlyph extends Glyph {
|
|
|
381
525
|
path.quadraticCurveTo(prevPt.x, prevPt.y, midX, midY);
|
|
382
526
|
curvePt = pt;
|
|
383
527
|
} else if (!prevPt.onCurve && pt.onCurve) {
|
|
528
|
+
if (!curvePt) {
|
|
529
|
+
throw new Error('Unknown TTF path state');
|
|
530
|
+
}
|
|
384
531
|
path.quadraticCurveTo(curvePt.x, curvePt.y, pt.x, pt.y);
|
|
385
532
|
curvePt = null;
|
|
386
533
|
} else {
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import * as r from 'restructure';
|
|
2
2
|
|
|
3
|
+
/** @typedef {import('restructure').EncodeStream} EncodeStream */
|
|
4
|
+
/** @typedef {import('restructure').StructValue} StructValue */
|
|
5
|
+
/** @typedef {import('./Path').default} Path */
|
|
6
|
+
|
|
3
7
|
// Flags for simple glyphs
|
|
4
8
|
const ON_CURVE = 1 << 0;
|
|
5
9
|
const X_SHORT_VECTOR = 1 << 1;
|
|
@@ -9,10 +13,19 @@ const SAME_X = 1 << 4;
|
|
|
9
13
|
const SAME_Y = 1 << 5;
|
|
10
14
|
|
|
11
15
|
class Point {
|
|
16
|
+
/**
|
|
17
|
+
* @param {number} val
|
|
18
|
+
* @returns {number}
|
|
19
|
+
*/
|
|
12
20
|
static size(val) {
|
|
13
21
|
return val >= 0 && val <= 255 ? 1 : 2;
|
|
14
22
|
}
|
|
15
23
|
|
|
24
|
+
/**
|
|
25
|
+
* @param {EncodeStream} stream
|
|
26
|
+
* @param {number} value
|
|
27
|
+
* @returns {void}
|
|
28
|
+
*/
|
|
16
29
|
static encode(stream, value) {
|
|
17
30
|
if (value >= 0 && value <= 255) {
|
|
18
31
|
stream.writeUInt8(value);
|
|
@@ -39,10 +52,19 @@ let Glyf = new r.Struct({
|
|
|
39
52
|
* Encodes TrueType glyph outlines
|
|
40
53
|
*/
|
|
41
54
|
export default class TTFGlyphEncoder {
|
|
55
|
+
/**
|
|
56
|
+
* @param {Path} path
|
|
57
|
+
* @param {number[]} [instructions]
|
|
58
|
+
* @returns {import('restructure').BinaryBuffer}
|
|
59
|
+
*/
|
|
42
60
|
encodeSimple(path, instructions = []) {
|
|
61
|
+
/** @type {number[]} */
|
|
43
62
|
let endPtsOfContours = [];
|
|
63
|
+
/** @type {number[]} */
|
|
44
64
|
let xPoints = [];
|
|
65
|
+
/** @type {number[]} */
|
|
45
66
|
let yPoints = [];
|
|
67
|
+
/** @type {number[]} */
|
|
46
68
|
let flags = [];
|
|
47
69
|
let same = 0;
|
|
48
70
|
let lastX = 0, lastY = 0, lastFlag = 0;
|
|
@@ -108,6 +130,7 @@ export default class TTFGlyphEncoder {
|
|
|
108
130
|
}
|
|
109
131
|
|
|
110
132
|
let bbox = path.bbox;
|
|
133
|
+
/** @type {StructValue} */
|
|
111
134
|
let glyf = {
|
|
112
135
|
numberOfContours: endPtsOfContours.length,
|
|
113
136
|
xMin: bbox.minX,
|
|
@@ -135,6 +158,15 @@ export default class TTFGlyphEncoder {
|
|
|
135
158
|
return stream.buffer;
|
|
136
159
|
}
|
|
137
160
|
|
|
161
|
+
/**
|
|
162
|
+
* @param {number} value
|
|
163
|
+
* @param {number} last
|
|
164
|
+
* @param {number[]} points
|
|
165
|
+
* @param {number} flag
|
|
166
|
+
* @param {number} shortFlag
|
|
167
|
+
* @param {number} sameFlag
|
|
168
|
+
* @returns {number}
|
|
169
|
+
*/
|
|
138
170
|
_encodePoint(value, last, points, flag, shortFlag, sameFlag) {
|
|
139
171
|
let diff = value - last;
|
|
140
172
|
|
package/src/glyph/WOFF2Glyph.js
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
import TTFGlyph, { Point } from './TTFGlyph';
|
|
2
2
|
import BBox from './BBox';
|
|
3
3
|
|
|
4
|
+
/** @typedef {import('../../types/fontkit').DecodedGlyf} DecodedGlyf */
|
|
5
|
+
/** @typedef {import('../../types/fontkit').GlyphComponent} GlyphComponent */
|
|
6
|
+
|
|
4
7
|
/**
|
|
5
8
|
* Represents a TrueType glyph in the WOFF2 format, which compresses glyphs differently.
|
|
6
9
|
*/
|
|
7
10
|
export default class WOFF2Glyph extends TTFGlyph {
|
|
8
11
|
type = 'WOFF2';
|
|
9
12
|
|
|
13
|
+
/**
|
|
14
|
+
* @returns {DecodedGlyf | null}
|
|
15
|
+
*/
|
|
10
16
|
_decode() {
|
|
11
|
-
let cached = this._font._transformedGlyphs[this.id];
|
|
17
|
+
let cached = this._font._transformedGlyphs?.[this.id];
|
|
12
18
|
if (!cached) {
|
|
13
19
|
return null;
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
if (!this._font._variationProcessor) {
|
|
17
23
|
// Non-variable path: return the pre-decoded glyph without copying.
|
|
18
|
-
return cached;
|
|
24
|
+
return /** @type {DecodedGlyf} */ (cached);
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
// Clone so per-instance deltas do not mutate the shared transformed cache.
|
|
22
28
|
// Transformed glyf has no per-glyph bbox; zeros keep phantom-point math
|
|
23
29
|
// aligned with hmtx (same approach as a missing bbox bitmap entry).
|
|
30
|
+
/** @type {DecodedGlyf} */
|
|
24
31
|
let glyph = {
|
|
25
32
|
numberOfContours: cached.numberOfContours,
|
|
26
33
|
xMin: 0,
|
|
@@ -30,7 +37,12 @@ export default class WOFF2Glyph extends TTFGlyph {
|
|
|
30
37
|
};
|
|
31
38
|
|
|
32
39
|
if (cached.points) {
|
|
33
|
-
glyph.points = cached.points.map(p =>
|
|
40
|
+
glyph.points = cached.points.map((p) => {
|
|
41
|
+
if (typeof p.copy === 'function') {
|
|
42
|
+
return p.copy();
|
|
43
|
+
}
|
|
44
|
+
return new Point(p.onCurve, p.endContour, p.x, p.y);
|
|
45
|
+
});
|
|
34
46
|
let points = glyph.points.concat(this._getPhantomPoints(glyph));
|
|
35
47
|
this._font._variationProcessor.transformPoints(this.id, points);
|
|
36
48
|
glyph.phantomPoints = points.slice(-4);
|
|
@@ -38,9 +50,12 @@ export default class WOFF2Glyph extends TTFGlyph {
|
|
|
38
50
|
|
|
39
51
|
if (cached.components) {
|
|
40
52
|
glyph.components = cached.components.map((c) => {
|
|
41
|
-
|
|
53
|
+
/** @type {GlyphComponent} */
|
|
54
|
+
let clone = Object.assign(Object.create(Object.getPrototypeOf(c)), c);
|
|
55
|
+
return clone;
|
|
42
56
|
});
|
|
43
57
|
|
|
58
|
+
/** @type {Point[]} */
|
|
44
59
|
let points = [];
|
|
45
60
|
for (let component of glyph.components) {
|
|
46
61
|
points.push(new Point(true, true, component.dx, component.dy));
|
|
@@ -59,6 +74,10 @@ export default class WOFF2Glyph extends TTFGlyph {
|
|
|
59
74
|
return glyph;
|
|
60
75
|
}
|
|
61
76
|
|
|
77
|
+
/**
|
|
78
|
+
* @param {boolean} [internal]
|
|
79
|
+
* @returns {BBox}
|
|
80
|
+
*/
|
|
62
81
|
_getCBox(internal) {
|
|
63
82
|
// Avoid path recursion while building phantom points during variation decode.
|
|
64
83
|
if (internal) {
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
* Represents positioning information for a glyph in a GlyphRun.
|
|
3
3
|
*/
|
|
4
4
|
export default class GlyphPosition {
|
|
5
|
+
/**
|
|
6
|
+
* @param {number} [xAdvance]
|
|
7
|
+
* @param {number} [yAdvance]
|
|
8
|
+
* @param {number} [xOffset]
|
|
9
|
+
* @param {number} [yOffset]
|
|
10
|
+
*/
|
|
5
11
|
constructor(xAdvance = 0, yAdvance = 0, xOffset = 0, yOffset = 0) {
|
|
6
12
|
/**
|
|
7
13
|
* The amount to move the virtual pen in the X direction after rendering this glyph.
|
package/src/layout/GlyphRun.js
CHANGED
|
@@ -6,43 +6,50 @@ import * as Script from '../layout/Script';
|
|
|
6
6
|
* Returned by the font layout method.
|
|
7
7
|
*/
|
|
8
8
|
export default class GlyphRun {
|
|
9
|
+
/**
|
|
10
|
+
* @param {import('../glyph/Glyph').default[]} glyphs
|
|
11
|
+
* @param {import('../../types/fontkit').FeatureInput | null | undefined} features
|
|
12
|
+
* @param {import('../../types/fontkit').ScriptTag | string[] | null | undefined} script
|
|
13
|
+
* @param {import('../../types/fontkit').LanguageTag | null | undefined} language
|
|
14
|
+
* @param {import('../../types/fontkit').TextDirection | null | undefined} direction
|
|
15
|
+
*/
|
|
9
16
|
constructor(glyphs, features, script, language, direction) {
|
|
10
17
|
/**
|
|
11
18
|
* An array of Glyph objects in the run
|
|
12
|
-
* @type {Glyph[]}
|
|
19
|
+
* @type {import('../glyph/Glyph').default[]}
|
|
13
20
|
*/
|
|
14
21
|
this.glyphs = glyphs;
|
|
15
22
|
|
|
16
23
|
/**
|
|
17
24
|
* An array of GlyphPosition objects for each glyph in the run
|
|
18
|
-
* @type {GlyphPosition[]}
|
|
25
|
+
* @type {import('./GlyphPosition').default[] | null}
|
|
19
26
|
*/
|
|
20
27
|
this.positions = null;
|
|
21
28
|
|
|
22
29
|
/**
|
|
23
30
|
* The script that was requested for shaping. This was either passed in or detected automatically.
|
|
24
|
-
* @type {string}
|
|
31
|
+
* @type {import('../../types/fontkit').ScriptTag | string[] | null | undefined}
|
|
25
32
|
*/
|
|
26
33
|
this.script = script;
|
|
27
34
|
|
|
28
35
|
/**
|
|
29
36
|
* The language requested for shaping, as passed in. If `null`, the default language for the
|
|
30
37
|
* script was used.
|
|
31
|
-
* @type {
|
|
38
|
+
* @type {import('../../types/fontkit').LanguageTag | null}
|
|
32
39
|
*/
|
|
33
40
|
this.language = language || null;
|
|
34
41
|
|
|
35
42
|
/**
|
|
36
43
|
* The direction requested for shaping, as passed in (either ltr or rtl).
|
|
37
44
|
* If `null`, the default direction of the script is used.
|
|
38
|
-
* @type {
|
|
45
|
+
* @type {import('../../types/fontkit').TextDirection}
|
|
39
46
|
*/
|
|
40
47
|
this.direction = direction || Script.direction(script);
|
|
41
48
|
|
|
42
49
|
/**
|
|
43
50
|
* The features requested during shaping. This is a combination of user
|
|
44
51
|
* specified features and features chosen by the shaper.
|
|
45
|
-
* @type {
|
|
52
|
+
* @type {import('../../types/fontkit').FeatureMap}
|
|
46
53
|
*/
|
|
47
54
|
this.features = {};
|
|
48
55
|
|
|
@@ -51,7 +58,7 @@ export default class GlyphRun {
|
|
|
51
58
|
for (let tag of features) {
|
|
52
59
|
this.features[tag] = true;
|
|
53
60
|
}
|
|
54
|
-
} else if (typeof features === 'object') {
|
|
61
|
+
} else if (typeof features === 'object' && features !== null) {
|
|
55
62
|
this.features = features;
|
|
56
63
|
}
|
|
57
64
|
}
|
|
@@ -62,6 +69,10 @@ export default class GlyphRun {
|
|
|
62
69
|
*/
|
|
63
70
|
get advanceWidth() {
|
|
64
71
|
let width = 0;
|
|
72
|
+
if (!this.positions) {
|
|
73
|
+
return width;
|
|
74
|
+
}
|
|
75
|
+
|
|
65
76
|
for (let position of this.positions) {
|
|
66
77
|
width += position.xAdvance;
|
|
67
78
|
}
|
|
@@ -75,6 +86,10 @@ export default class GlyphRun {
|
|
|
75
86
|
*/
|
|
76
87
|
get advanceHeight() {
|
|
77
88
|
let height = 0;
|
|
89
|
+
if (!this.positions) {
|
|
90
|
+
return height;
|
|
91
|
+
}
|
|
92
|
+
|
|
78
93
|
for (let position of this.positions) {
|
|
79
94
|
height += position.yAdvance;
|
|
80
95
|
}
|
|
@@ -88,6 +103,9 @@ export default class GlyphRun {
|
|
|
88
103
|
*/
|
|
89
104
|
get bbox() {
|
|
90
105
|
let bbox = new BBox();
|
|
106
|
+
if (!this.positions) {
|
|
107
|
+
return bbox;
|
|
108
|
+
}
|
|
91
109
|
|
|
92
110
|
let x = 0;
|
|
93
111
|
let y = 0;
|