@cantoo/fontkit 2.0.4 → 2.0.6
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.mjs +4448 -1142
- package/dist/browser-module.mjs.map +1 -1
- package/dist/browser.cjs +4449 -1142
- package/dist/browser.cjs.map +1 -1
- package/dist/main.cjs +4449 -1142
- package/dist/main.cjs.map +1 -1
- package/dist/module.mjs +4448 -1142
- package/dist/module.mjs.map +1 -1
- package/package.json +4 -1
- package/src/CmapProcessor.js +60 -21
- package/src/DFont.js +114 -14
- package/src/TTFFont.js +260 -62
- package/src/TrueTypeCollection.js +59 -15
- package/src/WOFF2Font.js +128 -22
- package/src/WOFFFont.js +45 -8
- package/src/aat/AATFeatureMap.js +83 -40
- package/src/aat/AATLayoutEngine.js +34 -2
- package/src/aat/AATLookupTable.js +24 -6
- package/src/aat/AATMorxProcessor.js +202 -65
- package/src/aat/AATStateMachine.js +35 -3
- package/src/base.js +20 -1
- package/src/cff/CFFCharsets.js +3 -0
- package/src/cff/CFFDict.js +81 -11
- package/src/cff/CFFEncodings.js +2 -0
- package/src/cff/CFFFont.js +120 -24
- package/src/cff/CFFIndex.js +66 -9
- package/src/cff/CFFOperand.js +53 -28
- package/src/cff/CFFPointer.js +41 -2
- package/src/cff/CFFPrivateDict.js +34 -24
- package/src/cff/CFFStandardStrings.js +68 -67
- package/src/cff/CFFTop.js +161 -68
- package/src/decorators.js +34 -15
- package/src/encodings.js +133 -116
- package/src/fs.js +10 -0
- package/src/glyph/BBox.js +14 -0
- package/src/glyph/CFFGlyph.js +293 -152
- package/src/glyph/COLRGlyph.js +40 -5
- package/src/glyph/Glyph.js +72 -13
- package/src/glyph/GlyphVariationProcessor.js +158 -62
- package/src/glyph/Path.js +119 -32
- package/src/glyph/SBIXGlyph.js +57 -8
- package/src/glyph/StandardNames.js +1 -0
- package/src/glyph/TTFGlyph.js +203 -63
- package/src/glyph/TTFGlyphEncoder.js +58 -26
- package/src/glyph/WOFF2Glyph.js +25 -6
- package/src/layout/GlyphPosition.js +6 -0
- package/src/layout/GlyphRun.js +28 -10
- package/src/layout/KernProcessor.js +63 -4
- package/src/layout/LayoutEngine.js +99 -26
- package/src/layout/Script.js +52 -26
- package/src/layout/UnicodeLayoutEngine.js +34 -10
- package/src/opentype/GPOSProcessor.js +156 -45
- package/src/opentype/GSUBProcessor.js +92 -33
- package/src/opentype/GlyphInfo.js +53 -5
- package/src/opentype/GlyphIterator.js +51 -2
- package/src/opentype/OTLayoutEngine.js +66 -8
- package/src/opentype/OTProcessor.js +284 -71
- package/src/opentype/ShapingPlan.js +52 -5
- package/src/opentype/shapers/ArabicShaper.js +27 -8
- package/src/opentype/shapers/DefaultShaper.js +54 -10
- package/src/opentype/shapers/HangulShaper.js +120 -42
- package/src/opentype/shapers/HebrewShaper.js +50 -16
- package/src/opentype/shapers/IndicShaper.js +159 -44
- package/src/opentype/shapers/ThaiShaper.js +50 -19
- package/src/opentype/shapers/UniversalShaper.js +83 -17
- package/src/opentype/shapers/index.js +48 -35
- package/src/opentype/shapers/indic-data.js +2 -2
- package/src/packages/unicode-properties/data-trie.js +2 -1
- package/src/packages/unicode-properties/data.js +13 -1
- package/src/packages/unicode-properties/dist/index.cjs +23 -10
- package/src/packages/unicode-properties/dist/index.cjs.map +2 -2
- package/src/packages/unicode-properties/dist/index.mjs +22 -10
- package/src/packages/unicode-properties/dist/index.mjs.map +2 -2
- package/src/packages/unicode-properties/generate.js +3 -4
- package/src/packages/unicode-properties/index.js +95 -30
- package/src/packages/unicode-properties/package.json +1 -1
- package/src/packages/unicode-trie/builder.js +146 -29
- package/src/packages/unicode-trie/dist/builder.cjs +135 -25
- package/src/packages/unicode-trie/dist/builder.cjs.map +2 -2
- package/src/packages/unicode-trie/dist/builder.mjs +134 -25
- 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 +63 -12
- package/src/packages/unicode-trie/package.json +1 -1
- package/src/packages/unicode-trie/swap.js +15 -2
- package/src/subset/CFFSubset.js +112 -22
- package/src/subset/Subset.js +23 -3
- package/src/subset/TTFSubset.js +84 -41
- package/src/tables/BASE.js +28 -27
- package/src/tables/COLR.js +10 -9
- package/src/tables/CPAL.js +2 -1
- package/src/tables/DSIG.js +8 -7
- package/src/tables/EBDT.js +10 -4
- package/src/tables/EBLC.js +70 -10
- package/src/tables/GDEF.js +18 -17
- package/src/tables/GPOS.js +153 -90
- package/src/tables/GSUB.js +50 -39
- package/src/tables/HVAR.js +51 -5
- package/src/tables/JSTF.js +17 -16
- package/src/tables/LTSH.js +4 -3
- package/src/tables/OS2.js +50 -49
- package/src/tables/PCLT.js +16 -15
- package/src/tables/STAT.js +46 -6
- package/src/tables/VDMX.js +18 -17
- package/src/tables/VORG.js +8 -7
- package/src/tables/WOFF2Directory.js +57 -9
- package/src/tables/WOFFDirectory.js +32 -20
- 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 +83 -63
- package/src/tables/cvt.js +1 -0
- package/src/tables/directory.js +56 -28
- 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 +6 -5
- package/src/tables/glyf.js +2 -1
- package/src/tables/gvar.js +29 -2
- package/src/tables/hdmx.js +18 -7
- package/src/tables/head.js +17 -16
- package/src/tables/hhea.js +15 -14
- package/src/tables/hmtx.js +18 -2
- package/src/tables/index.js +1 -5
- package/src/tables/just.js +15 -4
- package/src/tables/kern.js +84 -47
- package/src/tables/loca.js +23 -10
- package/src/tables/maxp.js +16 -15
- package/src/tables/morx.js +27 -20
- package/src/tables/name.js +120 -39
- package/src/tables/opbd.js +2 -1
- package/src/tables/opentype.js +126 -91
- package/src/tables/post.js +18 -11
- package/src/tables/prep.js +1 -0
- package/src/tables/sbix.js +12 -1
- package/src/tables/variations.js +29 -9
- package/src/tables/vhea.js +15 -14
- package/src/tables/vmtx.js +20 -4
- package/src/utils.js +15 -0
package/src/glyph/TTFGlyph.js
CHANGED
|
@@ -3,46 +3,105 @@ 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
|
|
9
|
-
xMin:
|
|
10
|
-
yMin:
|
|
11
|
-
xMax:
|
|
12
|
-
yMax:
|
|
16
|
+
xMin: r.int16,
|
|
17
|
+
yMin: r.int16,
|
|
18
|
+
xMax: r.int16,
|
|
19
|
+
yMax: r.int16
|
|
13
20
|
});
|
|
14
21
|
|
|
15
22
|
// Flags for simple glyphs
|
|
16
|
-
const ON_CURVE
|
|
17
|
-
const X_SHORT_VECTOR
|
|
18
|
-
const Y_SHORT_VECTOR
|
|
19
|
-
const REPEAT
|
|
20
|
-
const SAME_X
|
|
21
|
-
const SAME_Y
|
|
23
|
+
const ON_CURVE = 1 << 0;
|
|
24
|
+
const X_SHORT_VECTOR = 1 << 1;
|
|
25
|
+
const Y_SHORT_VECTOR = 1 << 2;
|
|
26
|
+
const REPEAT = 1 << 3;
|
|
27
|
+
const SAME_X = 1 << 4;
|
|
28
|
+
const SAME_Y = 1 << 5;
|
|
22
29
|
|
|
23
30
|
// Flags for composite glyphs
|
|
24
|
-
const ARG_1_AND_2_ARE_WORDS
|
|
25
|
-
const _ARGS_ARE_XY_VALUES
|
|
26
|
-
const _ROUND_XY_TO_GRID
|
|
27
|
-
const WE_HAVE_A_SCALE
|
|
28
|
-
const MORE_COMPONENTS
|
|
29
|
-
const WE_HAVE_AN_X_AND_Y_SCALE
|
|
30
|
-
const WE_HAVE_A_TWO_BY_TWO
|
|
31
|
-
const WE_HAVE_INSTRUCTIONS
|
|
32
|
-
const _USE_MY_METRICS
|
|
33
|
-
const _OVERLAP_COMPOUND
|
|
34
|
-
const _SCALED_COMPONENT_OFFSET
|
|
31
|
+
const ARG_1_AND_2_ARE_WORDS = 1 << 0;
|
|
32
|
+
const _ARGS_ARE_XY_VALUES = 1 << 1;
|
|
33
|
+
const _ROUND_XY_TO_GRID = 1 << 2;
|
|
34
|
+
const WE_HAVE_A_SCALE = 1 << 3;
|
|
35
|
+
const MORE_COMPONENTS = 1 << 5;
|
|
36
|
+
const WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
|
|
37
|
+
const WE_HAVE_A_TWO_BY_TWO = 1 << 7;
|
|
38
|
+
const WE_HAVE_INSTRUCTIONS = 1 << 8;
|
|
39
|
+
const _USE_MY_METRICS = 1 << 9;
|
|
40
|
+
const _OVERLAP_COMPOUND = 1 << 10;
|
|
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,24 +200,34 @@ 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);
|
|
127
|
-
|
|
128
231
|
} else if (glyph.numberOfContours < 0) {
|
|
129
232
|
this._decodeComposite(glyph, stream, startPos);
|
|
130
233
|
}
|
|
@@ -132,13 +235,19 @@ export default class TTFGlyph extends Glyph {
|
|
|
132
235
|
return glyph;
|
|
133
236
|
}
|
|
134
237
|
|
|
238
|
+
/**
|
|
239
|
+
* @param {DecodedGlyf} glyph
|
|
240
|
+
* @param {DecodeStream} stream
|
|
241
|
+
* @returns {void}
|
|
242
|
+
*/
|
|
135
243
|
_decodeSimple(glyph, stream) {
|
|
136
244
|
// this is a simple glyph
|
|
137
245
|
glyph.points = [];
|
|
138
246
|
|
|
139
|
-
let endPtsOfContours = new r.Array(r.uint16, glyph.numberOfContours).decode(stream);
|
|
140
|
-
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));
|
|
141
249
|
|
|
250
|
+
/** @type {number[]} */
|
|
142
251
|
let flags = [];
|
|
143
252
|
let numCoords = endPtsOfContours[endPtsOfContours.length - 1] + 1;
|
|
144
253
|
|
|
@@ -164,13 +273,15 @@ export default class TTFGlyph extends Glyph {
|
|
|
164
273
|
let px = 0;
|
|
165
274
|
for (let i = 0; i < flags.length; i++) {
|
|
166
275
|
let flag = flags[i];
|
|
167
|
-
glyph.points[i]
|
|
276
|
+
let point = glyph.points[i];
|
|
277
|
+
point.x = px = this._parseGlyphCoord(stream, px, flag & X_SHORT_VECTOR, flag & SAME_X);
|
|
168
278
|
}
|
|
169
279
|
|
|
170
280
|
let py = 0;
|
|
171
281
|
for (let i = 0; i < flags.length; i++) {
|
|
172
282
|
let flag = flags[i];
|
|
173
|
-
glyph.points[i]
|
|
283
|
+
let point = glyph.points[i];
|
|
284
|
+
point.y = py = this._parseGlyphCoord(stream, py, flag & Y_SHORT_VECTOR, flag & SAME_Y);
|
|
174
285
|
}
|
|
175
286
|
|
|
176
287
|
if (this._font._variationProcessor) {
|
|
@@ -184,6 +295,12 @@ export default class TTFGlyph extends Glyph {
|
|
|
184
295
|
return;
|
|
185
296
|
}
|
|
186
297
|
|
|
298
|
+
/**
|
|
299
|
+
* @param {DecodedGlyf} glyph
|
|
300
|
+
* @param {DecodeStream} stream
|
|
301
|
+
* @param {number} [offset]
|
|
302
|
+
* @returns {boolean}
|
|
303
|
+
*/
|
|
187
304
|
_decodeComposite(glyph, stream, offset = 0) {
|
|
188
305
|
// this is a composite glyph
|
|
189
306
|
glyph.components = [];
|
|
@@ -212,24 +329,23 @@ export default class TTFGlyph extends Glyph {
|
|
|
212
329
|
|
|
213
330
|
if (flags & WE_HAVE_A_SCALE) {
|
|
214
331
|
// fixed number with 14 bits of fraction
|
|
215
|
-
component.scaleX
|
|
216
|
-
|
|
217
|
-
|
|
332
|
+
component.scaleX
|
|
333
|
+
= component.scaleY = ((stream.readUInt8() << 24) | (stream.readUInt8() << 16)) / 1073741824;
|
|
218
334
|
} else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
|
|
219
335
|
component.scaleX = ((stream.readUInt8() << 24) | (stream.readUInt8() << 16)) / 1073741824;
|
|
220
336
|
component.scaleY = ((stream.readUInt8() << 24) | (stream.readUInt8() << 16)) / 1073741824;
|
|
221
|
-
|
|
222
337
|
} else if (flags & WE_HAVE_A_TWO_BY_TWO) {
|
|
223
|
-
component.scaleX
|
|
338
|
+
component.scaleX = ((stream.readUInt8() << 24) | (stream.readUInt8() << 16)) / 1073741824;
|
|
224
339
|
component.scale01 = ((stream.readUInt8() << 24) | (stream.readUInt8() << 16)) / 1073741824;
|
|
225
340
|
component.scale10 = ((stream.readUInt8() << 24) | (stream.readUInt8() << 16)) / 1073741824;
|
|
226
|
-
component.scaleY
|
|
341
|
+
component.scaleY = ((stream.readUInt8() << 24) | (stream.readUInt8() << 16)) / 1073741824;
|
|
227
342
|
}
|
|
228
343
|
|
|
229
344
|
glyph.components.push(component);
|
|
230
345
|
}
|
|
231
346
|
|
|
232
347
|
if (this._font._variationProcessor) {
|
|
348
|
+
/** @type {Point[]} */
|
|
233
349
|
let points = [];
|
|
234
350
|
for (let j = 0; j < glyph.components.length; j++) {
|
|
235
351
|
let component = glyph.components[j];
|
|
@@ -251,6 +367,10 @@ export default class TTFGlyph extends Glyph {
|
|
|
251
367
|
return haveInstructions;
|
|
252
368
|
}
|
|
253
369
|
|
|
370
|
+
/**
|
|
371
|
+
* @param {DecodedGlyf} glyph
|
|
372
|
+
* @returns {Point[]}
|
|
373
|
+
*/
|
|
254
374
|
_getPhantomPoints(glyph) {
|
|
255
375
|
let cbox = this._getCBox(true);
|
|
256
376
|
if (this._metrics == null) {
|
|
@@ -267,20 +387,25 @@ export default class TTFGlyph extends Glyph {
|
|
|
267
387
|
];
|
|
268
388
|
}
|
|
269
389
|
|
|
270
|
-
|
|
390
|
+
/**
|
|
391
|
+
* Decodes font data, resolves composite glyphs, and returns an array of contours
|
|
392
|
+
* @returns {Point[][]}
|
|
393
|
+
*/
|
|
271
394
|
_getContours() {
|
|
272
395
|
let glyph = this._decode();
|
|
273
396
|
if (!glyph) {
|
|
274
397
|
return [];
|
|
275
398
|
}
|
|
276
399
|
|
|
400
|
+
/** @type {Point[]} */
|
|
277
401
|
let points = [];
|
|
278
402
|
|
|
279
403
|
if (glyph.numberOfContours < 0) {
|
|
280
404
|
// resolve composite glyphs
|
|
281
|
-
|
|
405
|
+
let components = glyph.components || [];
|
|
406
|
+
for (let component of components) {
|
|
282
407
|
let componentGlyph = this._font._getBaseGlyph(component.glyphID);
|
|
283
|
-
if (!componentGlyph) {
|
|
408
|
+
if (!componentGlyph || typeof componentGlyph._getContours !== 'function') {
|
|
284
409
|
continue;
|
|
285
410
|
}
|
|
286
411
|
|
|
@@ -302,18 +427,22 @@ export default class TTFGlyph extends Glyph {
|
|
|
302
427
|
// Recompute and cache metrics if we performed variation processing, and don't have an HVAR table
|
|
303
428
|
if (glyph.phantomPoints && !this._font.directory.tables.HVAR) {
|
|
304
429
|
let m = this._metrics;
|
|
305
|
-
m
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
+
}
|
|
311
438
|
}
|
|
312
439
|
|
|
440
|
+
/** @type {Point[][]} */
|
|
313
441
|
let contours = [];
|
|
442
|
+
/** @type {Point[]} */
|
|
314
443
|
let cur = [];
|
|
315
444
|
for (let k = 0; k < points.length; k++) {
|
|
316
|
-
|
|
445
|
+
let point = points[k];
|
|
317
446
|
cur.push(point);
|
|
318
447
|
if (point.endContour) {
|
|
319
448
|
contours.push(cur);
|
|
@@ -324,6 +453,9 @@ export default class TTFGlyph extends Glyph {
|
|
|
324
453
|
return contours;
|
|
325
454
|
}
|
|
326
455
|
|
|
456
|
+
/**
|
|
457
|
+
* @returns {GlyphMetrics}
|
|
458
|
+
*/
|
|
327
459
|
_getMetrics() {
|
|
328
460
|
if (this._metrics) {
|
|
329
461
|
return this._metrics;
|
|
@@ -337,20 +469,29 @@ export default class TTFGlyph extends Glyph {
|
|
|
337
469
|
this.path;
|
|
338
470
|
}
|
|
339
471
|
|
|
340
|
-
|
|
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;
|
|
341
478
|
}
|
|
342
479
|
|
|
343
|
-
|
|
480
|
+
/**
|
|
481
|
+
* Converts contours to a Path object that can be rendered
|
|
482
|
+
* @returns {Path}
|
|
483
|
+
*/
|
|
344
484
|
_getPath() {
|
|
345
485
|
let contours = this._getContours();
|
|
346
|
-
let path = new Path;
|
|
486
|
+
let path = new Path();
|
|
347
487
|
|
|
348
488
|
for (let i = 0; i < contours.length; i++) {
|
|
349
489
|
let contour = contours[i];
|
|
350
490
|
let firstPt = contour[0];
|
|
351
491
|
let lastPt = contour[contour.length - 1];
|
|
352
492
|
let start = 0;
|
|
353
|
-
|
|
493
|
+
/** @type {Point | null} */
|
|
494
|
+
let curvePt = null;
|
|
354
495
|
|
|
355
496
|
if (firstPt.onCurve) {
|
|
356
497
|
// The first point will be consumed by the moveTo command, so skip in the loop
|
|
@@ -376,22 +517,21 @@ export default class TTFGlyph extends Glyph {
|
|
|
376
517
|
|
|
377
518
|
if (prevPt.onCurve && pt.onCurve) {
|
|
378
519
|
path.lineTo(pt.x, pt.y);
|
|
379
|
-
|
|
380
520
|
} else if (prevPt.onCurve && !pt.onCurve) {
|
|
381
521
|
curvePt = pt;
|
|
382
|
-
|
|
383
522
|
} else if (!prevPt.onCurve && !pt.onCurve) {
|
|
384
523
|
let midX = (prevPt.x + pt.x) / 2;
|
|
385
524
|
let midY = (prevPt.y + pt.y) / 2;
|
|
386
525
|
path.quadraticCurveTo(prevPt.x, prevPt.y, midX, midY);
|
|
387
526
|
curvePt = pt;
|
|
388
|
-
|
|
389
527
|
} else if (!prevPt.onCurve && pt.onCurve) {
|
|
528
|
+
if (!curvePt) {
|
|
529
|
+
throw new Error('Unknown TTF path state');
|
|
530
|
+
}
|
|
390
531
|
path.quadraticCurveTo(curvePt.x, curvePt.y, pt.x, pt.y);
|
|
391
532
|
curvePt = null;
|
|
392
|
-
|
|
393
533
|
} else {
|
|
394
|
-
throw new Error(
|
|
534
|
+
throw new Error('Unknown TTF path state');
|
|
395
535
|
}
|
|
396
536
|
}
|
|
397
537
|
|
|
@@ -1,18 +1,31 @@
|
|
|
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
|
-
const ON_CURVE
|
|
5
|
-
const X_SHORT_VECTOR
|
|
6
|
-
const Y_SHORT_VECTOR
|
|
7
|
-
const REPEAT
|
|
8
|
-
const SAME_X
|
|
9
|
-
const SAME_Y
|
|
8
|
+
const ON_CURVE = 1 << 0;
|
|
9
|
+
const X_SHORT_VECTOR = 1 << 1;
|
|
10
|
+
const Y_SHORT_VECTOR = 1 << 2;
|
|
11
|
+
const REPEAT = 1 << 3;
|
|
12
|
+
const SAME_X = 1 << 4;
|
|
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,23 +52,32 @@ 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;
|
|
49
71
|
let pointCount = 0;
|
|
50
|
-
|
|
72
|
+
|
|
51
73
|
for (let i = 0; i < path.commands.length; i++) {
|
|
52
74
|
let c = path.commands[i];
|
|
53
|
-
|
|
75
|
+
|
|
54
76
|
for (let j = 0; j < c.args.length; j += 2) {
|
|
55
77
|
let x = c.args[j];
|
|
56
78
|
let y = c.args[j + 1];
|
|
57
79
|
let flag = 0;
|
|
58
|
-
|
|
80
|
+
|
|
59
81
|
// If the ending point of a quadratic curve is the midpoint
|
|
60
82
|
// between the control point and the control point of the next
|
|
61
83
|
// quadratic curve, we can omit the ending point.
|
|
@@ -64,21 +86,21 @@ export default class TTFGlyphEncoder {
|
|
|
64
86
|
if (next && next.command === 'quadraticCurveTo') {
|
|
65
87
|
let midX = (lastX + next.args[0]) / 2;
|
|
66
88
|
let midY = (lastY + next.args[1]) / 2;
|
|
67
|
-
|
|
89
|
+
|
|
68
90
|
if (x === midX && y === midY) {
|
|
69
91
|
continue;
|
|
70
92
|
}
|
|
71
93
|
}
|
|
72
94
|
}
|
|
73
|
-
|
|
95
|
+
|
|
74
96
|
// All points except control points are on curve.
|
|
75
97
|
if (!(c.command === 'quadraticCurveTo' && j === 0)) {
|
|
76
98
|
flag |= ON_CURVE;
|
|
77
99
|
}
|
|
78
|
-
|
|
100
|
+
|
|
79
101
|
flag = this._encodePoint(x, lastX, xPoints, flag, X_SHORT_VECTOR, SAME_X);
|
|
80
102
|
flag = this._encodePoint(y, lastY, yPoints, flag, Y_SHORT_VECTOR, SAME_Y);
|
|
81
|
-
|
|
103
|
+
|
|
82
104
|
if (flag === lastFlag && same < 255) {
|
|
83
105
|
flags[flags.length - 1] |= REPEAT;
|
|
84
106
|
same++;
|
|
@@ -87,16 +109,16 @@ export default class TTFGlyphEncoder {
|
|
|
87
109
|
flags.push(same);
|
|
88
110
|
same = 0;
|
|
89
111
|
}
|
|
90
|
-
|
|
112
|
+
|
|
91
113
|
flags.push(flag);
|
|
92
114
|
lastFlag = flag;
|
|
93
115
|
}
|
|
94
|
-
|
|
116
|
+
|
|
95
117
|
lastX = x;
|
|
96
118
|
lastY = y;
|
|
97
119
|
pointCount++;
|
|
98
120
|
}
|
|
99
|
-
|
|
121
|
+
|
|
100
122
|
if (c.command === 'closePath') {
|
|
101
123
|
endPtsOfContours.push(pointCount - 1);
|
|
102
124
|
}
|
|
@@ -106,8 +128,9 @@ export default class TTFGlyphEncoder {
|
|
|
106
128
|
if (path.commands.length > 1 && path.commands[path.commands.length - 1].command !== 'closePath') {
|
|
107
129
|
endPtsOfContours.push(pointCount - 1);
|
|
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,
|
|
@@ -120,24 +143,33 @@ export default class TTFGlyphEncoder {
|
|
|
120
143
|
xPoints: xPoints,
|
|
121
144
|
yPoints: yPoints
|
|
122
145
|
};
|
|
123
|
-
|
|
146
|
+
|
|
124
147
|
let size = Glyf.size(glyf);
|
|
125
148
|
let tail = 4 - (size % 4);
|
|
126
|
-
|
|
149
|
+
|
|
127
150
|
let stream = new r.EncodeStream(size + tail);
|
|
128
151
|
Glyf.encode(stream, glyf);
|
|
129
|
-
|
|
152
|
+
|
|
130
153
|
// Align to 4-byte length
|
|
131
154
|
if (tail !== 0) {
|
|
132
155
|
stream.fill(0, tail);
|
|
133
156
|
}
|
|
134
|
-
|
|
157
|
+
|
|
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
|
+
|
|
141
173
|
if (value === last) {
|
|
142
174
|
flag |= sameFlag;
|
|
143
175
|
} else {
|
|
@@ -149,10 +181,10 @@ export default class TTFGlyphEncoder {
|
|
|
149
181
|
flag |= sameFlag;
|
|
150
182
|
}
|
|
151
183
|
}
|
|
152
|
-
|
|
184
|
+
|
|
153
185
|
points.push(diff);
|
|
154
186
|
}
|
|
155
|
-
|
|
187
|
+
|
|
156
188
|
return flag;
|
|
157
189
|
}
|
|
158
190
|
}
|