-
Notifications
You must be signed in to change notification settings - Fork 237
Expand file tree
/
Copy pathGlyph.js
More file actions
202 lines (170 loc) · 5.07 KB
/
Glyph.js
File metadata and controls
202 lines (170 loc) · 5.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
import { cache } from '../decorators';
import Path from './Path';
import {isMark} from 'unicode-properties';
import StandardNames from './StandardNames';
/**
* Glyph objects represent a glyph in the font. They have various properties for accessing metrics and
* the actual vector path the glyph represents, and methods for rendering the glyph to a graphics context.
*
* You do not create glyph objects directly. They are created by various methods on the font object.
* There are several subclasses of the base Glyph class internally that may be returned depending
* on the font format, but they all inherit from this class.
*/
export default class Glyph {
constructor(id, codePoints, font) {
/**
* The glyph id in the font
* @type {number}
*/
this.id = id;
/**
* An array of unicode code points that are represented by this glyph.
* There can be multiple code points in the case of ligatures and other glyphs
* that represent multiple visual characters.
* @type {number[]}
*/
this.codePoints = codePoints;
this._font = font;
// TODO: get this info from GDEF if available
this.isMark = this.codePoints.length > 0 && this.codePoints.every(isMark);
this.isLigature = this.codePoints.length > 1;
}
_getPath() {
return new Path();
}
_getCBox() {
return this.path.cbox;
}
_getBBox() {
return this.path.bbox;
}
_getTableMetrics(table) {
if (this.id < table.metrics.length) {
return table.metrics.get(this.id);
}
let metric = table.metrics.get(table.metrics.length - 1);
let res = {
advance: metric ? metric.advance : 0,
bearing: table.bearings.get(this.id - table.metrics.length) || 0
};
return res;
}
_getMetrics(cbox) {
if (this._metrics) { return this._metrics; }
let {advance:advanceWidth, bearing:leftBearing} = this._getTableMetrics(this._font.hmtx);
// For vertical metrics, use vmtx if available, or fall back to global data
if (this._font.vmtx) {
var {advance:advanceHeight, bearing:topBearing} = this._getTableMetrics(this._font.vmtx);
} else {
if (typeof cbox === 'undefined' || cbox === null) { ({ cbox } = this); }
var advanceHeight = Math.abs(this._font.ascent - this._font.descent);
var topBearing = this._font.ascent - cbox.maxY;
}
if (this._font._variationProcessor && this._font.HVAR) {
advanceWidth += this._font._variationProcessor.getAdvanceAdjustment(this.id, this._font.HVAR);
}
return this._metrics = { advanceWidth, advanceHeight, leftBearing, topBearing };
}
/**
* The glyph’s control box.
* This is often the same as the bounding box, but is faster to compute.
* Because of the way bezier curves are defined, some of the control points
* can be outside of the bounding box. Where `bbox` takes this into account,
* `cbox` does not. Thus, cbox is less accurate, but faster to compute.
* See [here](http://www.freetype.org/freetype2/docs/glyphs/glyphs-6.html#section-2)
* for a more detailed description.
*
* @type {BBox}
*/
@cache
get cbox() {
return this._getCBox();
}
/**
* The glyph’s bounding box, i.e. the rectangle that encloses the
* glyph outline as tightly as possible.
* @type {BBox}
*/
@cache
get bbox() {
return this._getBBox();
}
/**
* A vector Path object representing the glyph outline.
* @type {Path}
*/
@cache
get path() {
// Cache the path so we only decode it once
// Decoding is actually performed by subclasses
return this._getPath();
}
/**
* Returns a path scaled to the given font size.
* @param {number} size
* @return {Path}
*/
getScaledPath(size) {
let scale = 1 / this._font.unitsPerEm * size;
return this.path.scale(scale);
}
/**
* The glyph's advance width.
* @type {number}
*/
@cache
get advanceWidth() {
return this._getMetrics().advanceWidth;
}
/**
* The glyph's advance height.
* @type {number}
*/
@cache
get advanceHeight() {
return this._getMetrics().advanceHeight;
}
get ligatureCaretPositions() {}
_getName() {
let { post } = this._font;
if (!post) {
return null;
}
switch (post.version) {
case 1:
return StandardNames[this.id];
case 2:
let id = post.glyphNameIndex[this.id];
if (id < StandardNames.length) {
return StandardNames[id];
}
return post.names[id - StandardNames.length];
case 2.5:
return StandardNames[this.id + post.offsets[this.id]];
case 4:
return String.fromCharCode(post.map[this.id]);
}
}
/**
* The glyph's name
* @type {string}
*/
@cache
get name() {
return this._getName();
}
/**
* Renders the glyph to the given graphics context, at the specified font size.
* @param {CanvasRenderingContext2d} ctx
* @param {number} size
*/
render(ctx, size) {
ctx.save();
let scale = 1 / this._font.head.unitsPerEm * size;
ctx.scale(scale, scale);
let fn = this.path.toFunction();
fn(ctx);
ctx.fill();
ctx.restore();
}
}