Show More
Commit Description:
prevent multiple place login using uuid cookie
Commit Description:
prevent multiple place login using uuid cookie
References:
File last commit:
Show/Diff file:
Action:
node_modules/fontkit/src/TTFFont.js
| 551 lines
| 13.8 KiB
| application/javascript
| JavascriptGenshiLexer
|
r789 | import r from 'restructure'; | |||
import { cache } from './decorators'; | ||||
import fontkit from './base'; | ||||
import Directory from './tables/directory'; | ||||
import tables from './tables'; | ||||
import CmapProcessor from './CmapProcessor'; | ||||
import LayoutEngine from './layout/LayoutEngine'; | ||||
import TTFGlyph from './glyph/TTFGlyph'; | ||||
import CFFGlyph from './glyph/CFFGlyph'; | ||||
import SBIXGlyph from './glyph/SBIXGlyph'; | ||||
import COLRGlyph from './glyph/COLRGlyph'; | ||||
import GlyphVariationProcessor from './glyph/GlyphVariationProcessor'; | ||||
import TTFSubset from './subset/TTFSubset'; | ||||
import CFFSubset from './subset/CFFSubset'; | ||||
import BBox from './glyph/BBox'; | ||||
/** | ||||
* This is the base class for all SFNT-based font formats in fontkit. | ||||
* It supports TrueType, and PostScript glyphs, and several color glyph formats. | ||||
*/ | ||||
export default class TTFFont { | ||||
static probe(buffer) { | ||||
let format = buffer.toString('ascii', 0, 4); | ||||
return format === 'true' || format === 'OTTO' || format === String.fromCharCode(0, 1, 0, 0); | ||||
} | ||||
constructor(stream, variationCoords = null) { | ||||
this.defaultLanguage = null; | ||||
this.stream = stream; | ||||
this.variationCoords = variationCoords; | ||||
this._directoryPos = this.stream.pos; | ||||
this._tables = {}; | ||||
this._glyphs = {}; | ||||
this._decodeDirectory(); | ||||
// define properties for each table to lazily parse | ||||
for (let tag in this.directory.tables) { | ||||
let table = this.directory.tables[tag]; | ||||
if (tables[tag] && table.length > 0) { | ||||
Object.defineProperty(this, tag, { | ||||
get: this._getTable.bind(this, table) | ||||
}); | ||||
} | ||||
} | ||||
} | ||||
setDefaultLanguage(lang = null) { | ||||
this.defaultLanguage = lang; | ||||
} | ||||
_getTable(table) { | ||||
if (!(table.tag in this._tables)) { | ||||
try { | ||||
this._tables[table.tag] = this._decodeTable(table); | ||||
} catch (e) { | ||||
if (fontkit.logErrors) { | ||||
console.error(`Error decoding table ${table.tag}`); | ||||
console.error(e.stack); | ||||
} | ||||
} | ||||
} | ||||
return this._tables[table.tag]; | ||||
} | ||||
_getTableStream(tag) { | ||||
let table = this.directory.tables[tag]; | ||||
if (table) { | ||||
this.stream.pos = table.offset; | ||||
return this.stream; | ||||
} | ||||
return null; | ||||
} | ||||
_decodeDirectory() { | ||||
return this.directory = Directory.decode(this.stream, {_startOffset: 0}); | ||||
} | ||||
_decodeTable(table) { | ||||
let pos = this.stream.pos; | ||||
let stream = this._getTableStream(table.tag); | ||||
let result = tables[table.tag].decode(stream, this, table.length); | ||||
this.stream.pos = pos; | ||||
return result; | ||||
} | ||||
/** | ||||
* Gets a string from the font's `name` table | ||||
* `lang` is a BCP-47 language code. | ||||
* @return {string} | ||||
*/ | ||||
getName(key, lang = this.defaultLanguage || fontkit.defaultLanguage) { | ||||
let record = this.name && this.name.records[key]; | ||||
if (record) { | ||||
// Attempt to retrieve the entry, depending on which translation is available: | ||||
return ( | ||||
record[lang] | ||||
|| record[this.defaultLanguage] | ||||
|| record[fontkit.defaultLanguage] | ||||
|| record['en'] | ||||
|| record[Object.keys(record)[0]] // Seriously, ANY language would be fine | ||||
|| null | ||||
); | ||||
} | ||||
return null; | ||||
} | ||||
/** | ||||
* The unique PostScript name for this font, e.g. "Helvetica-Bold" | ||||
* @type {string} | ||||
*/ | ||||
get postscriptName() { | ||||
return this.getName('postscriptName'); | ||||
} | ||||
/** | ||||
* The font's full name, e.g. "Helvetica Bold" | ||||
* @type {string} | ||||
*/ | ||||
get fullName() { | ||||
return this.getName('fullName'); | ||||
} | ||||
/** | ||||
* The font's family name, e.g. "Helvetica" | ||||
* @type {string} | ||||
*/ | ||||
get familyName() { | ||||
return this.getName('fontFamily'); | ||||
} | ||||
/** | ||||
* The font's sub-family, e.g. "Bold". | ||||
* @type {string} | ||||
*/ | ||||
get subfamilyName() { | ||||
return this.getName('fontSubfamily'); | ||||
} | ||||
/** | ||||
* The font's copyright information | ||||
* @type {string} | ||||
*/ | ||||
get copyright() { | ||||
return this.getName('copyright'); | ||||
} | ||||
/** | ||||
* The font's version number | ||||
* @type {string} | ||||
*/ | ||||
get version() { | ||||
return this.getName('version'); | ||||
} | ||||
/** | ||||
* The font’s [ascender](https://en.wikipedia.org/wiki/Ascender_(typography)) | ||||
* @type {number} | ||||
*/ | ||||
get ascent() { | ||||
return this.hhea.ascent; | ||||
} | ||||
/** | ||||
* The font’s [descender](https://en.wikipedia.org/wiki/Descender) | ||||
* @type {number} | ||||
*/ | ||||
get descent() { | ||||
return this.hhea.descent; | ||||
} | ||||
/** | ||||
* The amount of space that should be included between lines | ||||
* @type {number} | ||||
*/ | ||||
get lineGap() { | ||||
return this.hhea.lineGap; | ||||
} | ||||
/** | ||||
* The offset from the normal underline position that should be used | ||||
* @type {number} | ||||
*/ | ||||
get underlinePosition() { | ||||
return this.post.underlinePosition; | ||||
} | ||||
/** | ||||
* The weight of the underline that should be used | ||||
* @type {number} | ||||
*/ | ||||
get underlineThickness() { | ||||
return this.post.underlineThickness; | ||||
} | ||||
/** | ||||
* If this is an italic font, the angle the cursor should be drawn at to match the font design | ||||
* @type {number} | ||||
*/ | ||||
get italicAngle() { | ||||
return this.post.italicAngle; | ||||
} | ||||
/** | ||||
* The height of capital letters above the baseline. | ||||
* See [here](https://en.wikipedia.org/wiki/Cap_height) for more details. | ||||
* @type {number} | ||||
*/ | ||||
get capHeight() { | ||||
let os2 = this['OS/2']; | ||||
return os2 ? os2.capHeight : this.ascent; | ||||
} | ||||
/** | ||||
* The height of lower case letters in the font. | ||||
* See [here](https://en.wikipedia.org/wiki/X-height) for more details. | ||||
* @type {number} | ||||
*/ | ||||
get xHeight() { | ||||
let os2 = this['OS/2']; | ||||
return os2 ? os2.xHeight : 0; | ||||
} | ||||
/** | ||||
* The number of glyphs in the font. | ||||
* @type {number} | ||||
*/ | ||||
get numGlyphs() { | ||||
return this.maxp.numGlyphs; | ||||
} | ||||
/** | ||||
* The size of the font’s internal coordinate grid | ||||
* @type {number} | ||||
*/ | ||||
get unitsPerEm() { | ||||
return this.head.unitsPerEm; | ||||
} | ||||
/** | ||||
* The font’s bounding box, i.e. the box that encloses all glyphs in the font. | ||||
* @type {BBox} | ||||
*/ | ||||
@cache | ||||
get bbox() { | ||||
return Object.freeze(new BBox(this.head.xMin, this.head.yMin, this.head.xMax, this.head.yMax)); | ||||
} | ||||
@cache | ||||
get _cmapProcessor() { | ||||
return new CmapProcessor(this.cmap); | ||||
} | ||||
/** | ||||
* An array of all of the unicode code points supported by the font. | ||||
* @type {number[]} | ||||
*/ | ||||
@cache | ||||
get characterSet() { | ||||
return this._cmapProcessor.getCharacterSet(); | ||||
} | ||||
/** | ||||
* Returns whether there is glyph in the font for the given unicode code point. | ||||
* | ||||
* @param {number} codePoint | ||||
* @return {boolean} | ||||
*/ | ||||
hasGlyphForCodePoint(codePoint) { | ||||
return !!this._cmapProcessor.lookup(codePoint); | ||||
} | ||||
/** | ||||
* Maps a single unicode code point to a Glyph object. | ||||
* Does not perform any advanced substitutions (there is no context to do so). | ||||
* | ||||
* @param {number} codePoint | ||||
* @return {Glyph} | ||||
*/ | ||||
glyphForCodePoint(codePoint) { | ||||
return this.getGlyph(this._cmapProcessor.lookup(codePoint), [codePoint]); | ||||
} | ||||
/** | ||||
* Returns an array of Glyph objects for the given string. | ||||
* This is only a one-to-one mapping from characters to glyphs. | ||||
* For most uses, you should use font.layout (described below), which | ||||
* provides a much more advanced mapping supporting AAT and OpenType shaping. | ||||
* | ||||
* @param {string} string | ||||
* @return {Glyph[]} | ||||
*/ | ||||
glyphsForString(string) { | ||||
let glyphs = []; | ||||
let len = string.length; | ||||
let idx = 0; | ||||
let last = -1; | ||||
let state = -1; | ||||
while (idx <= len) { | ||||
let code = 0; | ||||
let nextState = 0; | ||||
if (idx < len) { | ||||
// Decode the next codepoint from UTF 16 | ||||
code = string.charCodeAt(idx++); | ||||
if (0xd800 <= code && code <= 0xdbff && idx < len) { | ||||
let next = string.charCodeAt(idx); | ||||
if (0xdc00 <= next && next <= 0xdfff) { | ||||
idx++; | ||||
code = ((code & 0x3ff) << 10) + (next & 0x3ff) + 0x10000; | ||||
} | ||||
} | ||||
// Compute the next state: 1 if the next codepoint is a variation selector, 0 otherwise. | ||||
nextState = ((0xfe00 <= code && code <= 0xfe0f) || (0xe0100 <= code && code <= 0xe01ef)) ? 1 : 0; | ||||
} else { | ||||
idx++; | ||||
} | ||||
if (state === 0 && nextState === 1) { | ||||
// Variation selector following normal codepoint. | ||||
glyphs.push(this.getGlyph(this._cmapProcessor.lookup(last, code), [last, code])); | ||||
} else if (state === 0 && nextState === 0) { | ||||
// Normal codepoint following normal codepoint. | ||||
glyphs.push(this.glyphForCodePoint(last)); | ||||
} | ||||
last = code; | ||||
state = nextState; | ||||
} | ||||
return glyphs; | ||||
} | ||||
@cache | ||||
get _layoutEngine() { | ||||
return new LayoutEngine(this); | ||||
} | ||||
/** | ||||
* Returns a GlyphRun object, which includes an array of Glyphs and GlyphPositions for the given string. | ||||
* | ||||
* @param {string} string | ||||
* @param {string[]} [userFeatures] | ||||
* @param {string} [script] | ||||
* @param {string} [language] | ||||
* @param {string} [direction] | ||||
* @return {GlyphRun} | ||||
*/ | ||||
layout(string, userFeatures, script, language, direction) { | ||||
return this._layoutEngine.layout(string, userFeatures, script, language, direction); | ||||
} | ||||
/** | ||||
* Returns an array of strings that map to the given glyph id. | ||||
* @param {number} gid - glyph id | ||||
*/ | ||||
stringsForGlyph(gid) { | ||||
return this._layoutEngine.stringsForGlyph(gid); | ||||
} | ||||
/** | ||||
* An array of all [OpenType feature tags](https://www.microsoft.com/typography/otspec/featuretags.htm) | ||||
* (or mapped AAT tags) supported by the font. | ||||
* The features parameter is an array of OpenType feature tags to be applied in addition to the default set. | ||||
* If this is an AAT font, the OpenType feature tags are mapped to AAT features. | ||||
* | ||||
* @type {string[]} | ||||
*/ | ||||
get availableFeatures() { | ||||
return this._layoutEngine.getAvailableFeatures(); | ||||
} | ||||
getAvailableFeatures(script, language) { | ||||
return this._layoutEngine.getAvailableFeatures(script, language); | ||||
} | ||||
_getBaseGlyph(glyph, characters = []) { | ||||
if (!this._glyphs[glyph]) { | ||||
if (this.directory.tables.glyf) { | ||||
this._glyphs[glyph] = new TTFGlyph(glyph, characters, this); | ||||
} else if (this.directory.tables['CFF '] || this.directory.tables.CFF2) { | ||||
this._glyphs[glyph] = new CFFGlyph(glyph, characters, this); | ||||
} | ||||
} | ||||
return this._glyphs[glyph] || null; | ||||
} | ||||
/** | ||||
* Returns a glyph object for the given glyph id. | ||||
* You can pass the array of code points this glyph represents for | ||||
* your use later, and it will be stored in the glyph object. | ||||
* | ||||
* @param {number} glyph | ||||
* @param {number[]} characters | ||||
* @return {Glyph} | ||||
*/ | ||||
getGlyph(glyph, characters = []) { | ||||
if (!this._glyphs[glyph]) { | ||||
if (this.directory.tables.sbix) { | ||||
this._glyphs[glyph] = new SBIXGlyph(glyph, characters, this); | ||||
} else if ((this.directory.tables.COLR) && (this.directory.tables.CPAL)) { | ||||
this._glyphs[glyph] = new COLRGlyph(glyph, characters, this); | ||||
} else { | ||||
this._getBaseGlyph(glyph, characters); | ||||
} | ||||
} | ||||
return this._glyphs[glyph] || null; | ||||
} | ||||
/** | ||||
* Returns a Subset for this font. | ||||
* @return {Subset} | ||||
*/ | ||||
createSubset() { | ||||
if (this.directory.tables['CFF ']) { | ||||
return new CFFSubset(this); | ||||
} | ||||
return new TTFSubset(this); | ||||
} | ||||
/** | ||||
* Returns an object describing the available variation axes | ||||
* that this font supports. Keys are setting tags, and values | ||||
* contain the axis name, range, and default value. | ||||
* | ||||
* @type {object} | ||||
*/ | ||||
@cache | ||||
get variationAxes() { | ||||
let res = {}; | ||||
if (!this.fvar) { | ||||
return res; | ||||
} | ||||
for (let axis of this.fvar.axis) { | ||||
res[axis.axisTag.trim()] = { | ||||
name: axis.name.en, | ||||
min: axis.minValue, | ||||
default: axis.defaultValue, | ||||
max: axis.maxValue | ||||
}; | ||||
} | ||||
return res; | ||||
} | ||||
/** | ||||
* Returns an object describing the named variation instances | ||||
* that the font designer has specified. Keys are variation names | ||||
* and values are the variation settings for this instance. | ||||
* | ||||
* @type {object} | ||||
*/ | ||||
@cache | ||||
get namedVariations() { | ||||
let res = {}; | ||||
if (!this.fvar) { | ||||
return res; | ||||
} | ||||
for (let instance of this.fvar.instance) { | ||||
let settings = {}; | ||||
for (let i = 0; i < this.fvar.axis.length; i++) { | ||||
let axis = this.fvar.axis[i]; | ||||
settings[axis.axisTag.trim()] = instance.coord[i]; | ||||
} | ||||
res[instance.name.en] = settings; | ||||
} | ||||
return res; | ||||
} | ||||
/** | ||||
* Returns a new font with the given variation settings applied. | ||||
* Settings can either be an instance name, or an object containing | ||||
* variation tags as specified by the `variationAxes` property. | ||||
* | ||||
* @param {object} settings | ||||
* @return {TTFFont} | ||||
*/ | ||||
getVariation(settings) { | ||||
if (!(this.directory.tables.fvar && ((this.directory.tables.gvar && this.directory.tables.glyf) || this.directory.tables.CFF2))) { | ||||
throw new Error('Variations require a font with the fvar, gvar and glyf, or CFF2 tables.'); | ||||
} | ||||
if (typeof settings === 'string') { | ||||
settings = this.namedVariations[settings]; | ||||
} | ||||
if (typeof settings !== 'object') { | ||||
throw new Error('Variation settings must be either a variation name or settings object.'); | ||||
} | ||||
// normalize the coordinates | ||||
let coords = this.fvar.axis.map((axis, i) => { | ||||
let axisTag = axis.axisTag.trim(); | ||||
if (axisTag in settings) { | ||||
return Math.max(axis.minValue, Math.min(axis.maxValue, settings[axisTag])); | ||||
} else { | ||||
return axis.defaultValue; | ||||
} | ||||
}); | ||||
let stream = new r.DecodeStream(this.stream.buffer); | ||||
stream.pos = this._directoryPos; | ||||
let font = new TTFFont(stream, coords); | ||||
font._tables = this._tables; | ||||
return font; | ||||
} | ||||
@cache | ||||
get _variationProcessor() { | ||||
if (!this.fvar) { | ||||
return null; | ||||
} | ||||
let variationCoords = this.variationCoords; | ||||
// Ignore if no variation coords and not CFF2 | ||||
if (!variationCoords && !this.CFF2) { | ||||
return null; | ||||
} | ||||
if (!variationCoords) { | ||||
variationCoords = this.fvar.axis.map(axis => axis.defaultValue); | ||||
} | ||||
return new GlyphVariationProcessor(this, variationCoords); | ||||
} | ||||
// Standardized format plugin API | ||||
getFont(name) { | ||||
return this.getVariation(name); | ||||
} | ||||
} | ||||