Current File : /home/tradevaly/www/node_modules/fontkit/src/glyph/Glyph.js |
import { cache } from '../decorators';
import Path from './Path';
import unicode 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(unicode.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 from OS/2 or hhea
if (this._font.vmtx) {
var {advance:advanceHeight, bearing:topBearing} = this._getTableMetrics(this._font.vmtx);
} else {
let os2;
if (typeof cbox === 'undefined' || cbox === null) { ({ cbox } = this); }
if ((os2 = this._font['OS/2']) && os2.version > 0) {
var advanceHeight = Math.abs(os2.typoAscender - os2.typoDescender);
var topBearing = os2.typoAscender - cbox.maxY;
} else {
let { hhea } = this._font;
var advanceHeight = Math.abs(hhea.ascent - hhea.descent);
var topBearing = hhea.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();
}
}