Comparing version
{ | ||
"name": "fontkit", | ||
"version": "1.6.1", | ||
"version": "1.7.1", | ||
"description": "An advanced font engine for Node and the browser", | ||
@@ -28,3 +28,4 @@ "keywords": [ | ||
"data.trie", | ||
"use.trie" | ||
"use.trie", | ||
"indic.trie" | ||
], | ||
@@ -31,0 +32,0 @@ "author": "Devon Govett <devongovett@gmail.com>", |
@@ -481,5 +481,5 @@ // see https://developer.apple.com/fonts/TrueType-Reference-Manual/RM09/AppendixF.html | ||
let res = {}; | ||
for (let k = 0; k < features.length; k++) { | ||
for (let k in features) { | ||
let r; | ||
if (r = OTMapping[features[k]]) { | ||
if (r = OTMapping[k]) { | ||
if (res[r[0]] == null) { | ||
@@ -489,3 +489,3 @@ res[r[0]] = {}; | ||
res[r[0]][r[1]] = true; | ||
res[r[0]][r[1]] = features[k]; | ||
} | ||
@@ -492,0 +492,0 @@ } |
@@ -12,12 +12,10 @@ import * as AATFeatureMap from './AATFeatureMap'; | ||
substitute(glyphs, features, script, language) { | ||
substitute(glyphRun) { | ||
// AAT expects the glyphs to be in visual order prior to morx processing, | ||
// so reverse the glyphs if the script is right-to-left. | ||
let isRTL = Script.direction(script) === 'rtl'; | ||
if (isRTL) { | ||
if (glyphRun.direction === 'rtl') { | ||
glyphs.reverse(); | ||
} | ||
this.morxProcessor.process(glyphs, AATFeatureMap.mapOTToAAT(features)); | ||
return glyphs; | ||
this.morxProcessor.process(glyphRun.glyphs, AATFeatureMap.mapOTToAAT(glyphRun.features)); | ||
} | ||
@@ -24,0 +22,0 @@ |
import BBox from '../glyph/BBox'; | ||
import * as Script from '../layout/Script'; | ||
@@ -8,3 +9,3 @@ /** | ||
export default class GlyphRun { | ||
constructor(glyphs, positions) { | ||
constructor(glyphs, features, script, language) { | ||
/** | ||
@@ -20,3 +21,38 @@ * An array of Glyph objects in the run | ||
*/ | ||
this.positions = positions; | ||
this.positions = null; | ||
/** | ||
* The script that was requested for shaping. This was either passed in or detected automatically. | ||
* @type {string} | ||
*/ | ||
this.script = script; | ||
/** | ||
* The language requested for shaping, as passed in. If `null`, the default language for the | ||
* script was used. | ||
* @type {string} | ||
*/ | ||
this.language = language || null; | ||
/** | ||
* The directionality of the requested script (either ltr or rtl). | ||
* @type {string} | ||
*/ | ||
this.direction = Script.direction(script); | ||
/** | ||
* The features requested during shaping. This is a combination of user | ||
* specified features and features chosen by the shaper. | ||
* @type {object} | ||
*/ | ||
this.features = {}; | ||
// Convert features to an object | ||
if (Array.isArray(features)) { | ||
for (let tag of features) { | ||
this.features[tag] = true; | ||
} | ||
} else if (typeof features === 'object') { | ||
this.features = features; | ||
} | ||
} | ||
@@ -23,0 +59,0 @@ |
@@ -26,3 +26,3 @@ import KernProcessor from './KernProcessor'; | ||
layout(string, features = [], script, language) { | ||
layout(string, features, script, language) { | ||
// Make the features parameter optional | ||
@@ -57,5 +57,8 @@ if (typeof features === 'string') { | ||
let glyphRun = new GlyphRun(glyphs, features, script, language); | ||
// Return early if there are no glyphs | ||
if (glyphs.length === 0) { | ||
return new GlyphRun(glyphs, []); | ||
glyphRun.positions = []; | ||
return glyphRun; | ||
} | ||
@@ -65,9 +68,11 @@ | ||
if (this.engine && this.engine.setup) { | ||
this.engine.setup(glyphs, features, script, language); | ||
this.engine.setup(glyphRun); | ||
} | ||
// Substitute and position the glyphs | ||
glyphs = this.substitute(glyphs, features, script, language); | ||
let positions = this.position(glyphs, features, script, language); | ||
this.substitute(glyphRun); | ||
this.position(glyphRun); | ||
this.hideDefaultIgnorables(glyphRun.glyphs, glyphRun.positions); | ||
// Let the layout engine clean up any state it might have | ||
@@ -78,17 +83,15 @@ if (this.engine && this.engine.cleanup) { | ||
return new GlyphRun(glyphs, positions); | ||
return glyphRun; | ||
} | ||
substitute(glyphs, features, script, language) { | ||
substitute(glyphRun) { | ||
// Call the advanced layout engine to make substitutions | ||
if (this.engine && this.engine.substitute) { | ||
glyphs = this.engine.substitute(glyphs, features, script, language); | ||
this.engine.substitute(glyphRun); | ||
} | ||
return glyphs; | ||
} | ||
position(glyphs, features, script, language) { | ||
position(glyphRun) { | ||
// Get initial glyph positions | ||
let positions = glyphs.map(glyph => new GlyphPosition(glyph.advanceWidth)); | ||
glyphRun.positions = glyphRun.glyphs.map(glyph => new GlyphPosition(glyph.advanceWidth)); | ||
let positioned = null; | ||
@@ -98,3 +101,3 @@ | ||
if (this.engine && this.engine.position) { | ||
positioned = this.engine.position(glyphs, positions, features, script, language); | ||
positioned = this.engine.position(glyphRun); | ||
} | ||
@@ -108,7 +111,7 @@ | ||
this.unicodeLayoutEngine.positionGlyphs(glyphs, positions); | ||
this.unicodeLayoutEngine.positionGlyphs(glyphRun.glyphs, glyphRun.positions); | ||
} | ||
// if kerning is not supported by GPOS, do kerning with the TrueType/AAT kern table | ||
if ((!positioned || !positioned.kern) && this.font.kern) { | ||
if ((!positioned || !positioned.kern) && glyphRun.features.kern !== false && this.font.kern) { | ||
if (!this.kernProcessor) { | ||
@@ -118,8 +121,46 @@ this.kernProcessor = new KernProcessor(this.font); | ||
this.kernProcessor.process(glyphs, positions); | ||
this.kernProcessor.process(glyphRun.glyphs, glyphRun.positions); | ||
glyphRun.features.kern = true; | ||
} | ||
} | ||
return positions; | ||
hideDefaultIgnorables(glyphs, positions) { | ||
let space = this.font.glyphForCodePoint(0x20); | ||
for (let i = 0; i < glyphs.length; i++) { | ||
if (this.isDefaultIgnorable(glyphs[i].codePoints[0])) { | ||
glyphs[i] = space; | ||
positions[i].xAdvance = 0; | ||
positions[i].yAdvance = 0; | ||
} | ||
} | ||
} | ||
isDefaultIgnorable(ch) { | ||
// From DerivedCoreProperties.txt in the Unicode database, | ||
// minus U+115F, U+1160, U+3164 and U+FFA0, which is what | ||
// Harfbuzz and Uniscribe do. | ||
let plane = ch >> 16; | ||
if (plane === 0) { | ||
// BMP | ||
switch (ch >> 8) { | ||
case 0x00: return ch === 0x00AD; | ||
case 0x03: return ch === 0x034F; | ||
case 0x06: return ch === 0x061C; | ||
case 0x17: return 0x17B4 <= ch && ch <= 0x17B5; | ||
case 0x18: return 0x180B <= ch && ch <= 0x180E; | ||
case 0x20: return (0x200B <= ch && ch <= 0x200F) || (0x202A <= ch && ch <= 0x202E) || (0x2060 <= ch && ch <= 0x206F); | ||
case 0xFE: return (0xFE00 <= ch && ch <= 0xFE0F) || ch === 0xFEFF; | ||
case 0xFF: return 0xFFF0 <= ch && ch <= 0xFFF8; | ||
default: return false; | ||
} | ||
} else { | ||
// Other planes | ||
switch (plane) { | ||
case 0x01: return (0x1BCA0 <= ch && ch <= 0x1BCA3) || (0x1D173 <= ch && ch <= 0x1D17A); | ||
case 0x0E: return 0xE0000 <= ch && ch <= 0xE0FFF; | ||
default: return false; | ||
} | ||
} | ||
} | ||
getAvailableFeatures(script, language) { | ||
@@ -126,0 +167,0 @@ let features = []; |
@@ -87,3 +87,3 @@ import unicode from 'unicode-properties'; | ||
Old_Turkic: 'orkh', | ||
Oriya: 'orya', | ||
Oriya: ['ory2', 'orya'], | ||
Osmanya: 'osma', | ||
@@ -117,3 +117,3 @@ Palmyrene: 'palm', | ||
New_Tai_Lue: 'talu', | ||
Tamil: 'taml', | ||
Tamil: ['tml2', 'taml'], | ||
Tai_Viet: 'tavt', | ||
@@ -138,2 +138,14 @@ Telugu: ['tel2', 'telu'], | ||
const OPENTYPE_SCRIPTS = {}; | ||
for (let script in UNICODE_SCRIPTS) { | ||
let tag = UNICODE_SCRIPTS[script]; | ||
if (Array.isArray(tag)) { | ||
for (let t of tag) { | ||
OPENTYPE_SCRIPTS[t] = script; | ||
} | ||
} else { | ||
OPENTYPE_SCRIPTS[tag] = script; | ||
} | ||
} | ||
export function fromUnicode(script) { | ||
@@ -143,2 +155,6 @@ return UNICODE_SCRIPTS[script]; | ||
export function fromOpenType(tag) { | ||
return OPENTYPE_SCRIPTS[tag]; | ||
} | ||
export function forString(string) { | ||
@@ -145,0 +161,0 @@ let len = string.length; |
@@ -5,3 +5,3 @@ import unicode from 'unicode-properties'; | ||
export default class GlyphInfo { | ||
constructor(font, id, codePoints = [], features = []) { | ||
constructor(font, id, codePoints = [], features) { | ||
this._font = font; | ||
@@ -23,3 +23,3 @@ this.codePoints = codePoints; | ||
this.ligatureComponent = null; | ||
this.ligated = false; | ||
this.isLigated = false; | ||
this.cursiveAttachment = null; | ||
@@ -29,2 +29,3 @@ this.markAttachment = null; | ||
this.substituted = false; | ||
this.isMultiplied = false; | ||
} | ||
@@ -40,12 +41,21 @@ | ||
if (this._font.GDEF && this._font.GDEF.glyphClassDef) { | ||
let GDEF = this._font.GDEF; | ||
if (GDEF && GDEF.glyphClassDef) { | ||
// TODO: clean this up | ||
let classID = OTProcessor.prototype.getClassID(id, this._font.GDEF.glyphClassDef); | ||
let classID = OTProcessor.prototype.getClassID(id, GDEF.glyphClassDef); | ||
this.isBase = classID === 1; | ||
this.isLigature = classID === 2; | ||
this.isMark = classID === 3; | ||
this.isLigature = classID === 2; | ||
this.markAttachmentType = GDEF.markAttachClassDef ? OTProcessor.prototype.getClassID(id, GDEF.markAttachClassDef) : 0; | ||
} else { | ||
this.isMark = this.codePoints.every(unicode.isMark); | ||
this.isBase = !this.isMark; | ||
this.isLigature = this.codePoints.length > 1; | ||
this.markAttachmentType = 0; | ||
} | ||
} | ||
copy() { | ||
return new GlyphInfo(this._font, this.id, this.codePoints, this.features); | ||
} | ||
} |
export default class GlyphIterator { | ||
constructor(glyphs, flags) { | ||
constructor(glyphs, options) { | ||
this.glyphs = glyphs; | ||
this.reset(flags); | ||
this.reset(options); | ||
} | ||
reset(flags = {}) { | ||
this.flags = flags; | ||
this.index = 0; | ||
reset(options = {}, index = 0) { | ||
this.options = options; | ||
this.flags = options.flags || {}; | ||
this.markAttachmentType = options.markAttachmentType || 0; | ||
this.index = index; | ||
} | ||
@@ -16,6 +18,7 @@ | ||
shouldIgnore(glyph, flags) { | ||
return ((flags.ignoreMarks && glyph.isMark) || | ||
(flags.ignoreBaseGlyphs && !glyph.isMark) || | ||
(flags.ignoreLigatures && glyph.isLigature)); | ||
shouldIgnore(glyph) { | ||
return (this.flags.ignoreMarks && glyph.isMark) || | ||
(this.flags.ignoreBaseGlyphs && glyph.isBase) || | ||
(this.flags.ignoreLigatures && glyph.isLigature) || | ||
(this.markAttachmentType && glyph.isMark && glyph.markAttachmentType !== this.markAttachmentType); | ||
} | ||
@@ -25,3 +28,3 @@ | ||
this.index += dir; | ||
while (0 <= this.index && this.index < this.glyphs.length && this.shouldIgnore(this.glyphs[this.index], this.flags)) { | ||
while (0 <= this.index && this.index < this.glyphs.length && this.shouldIgnore(this.glyphs[this.index])) { | ||
this.index += dir; | ||
@@ -28,0 +31,0 @@ } |
@@ -42,2 +42,3 @@ import OTProcessor from './OTProcessor'; | ||
glyph.substituted = true; | ||
glyph.isMultiplied = true; | ||
return glyph; | ||
@@ -44,0 +45,0 @@ }); |
@@ -25,15 +25,25 @@ import ShapingPlan from './ShapingPlan'; | ||
setup(glyphs, features, script, language) { | ||
setup(glyphRun) { | ||
// Map glyphs to GlyphInfo objects so data can be passed between | ||
// GSUB and GPOS without mutating the real (shared) Glyph objects. | ||
this.glyphInfos = glyphs.map(glyph => new GlyphInfo(this.font, glyph.id, [...glyph.codePoints])); | ||
this.glyphInfos = glyphRun.glyphs.map(glyph => new GlyphInfo(this.font, glyph.id, [...glyph.codePoints])); | ||
// Select a script based on what is available in GSUB/GPOS. | ||
let script = this.GSUBProcessor | ||
? this.GSUBProcessor.selectScript(glyphRun.script, glyphRun.language) | ||
: this.GPOSProcessor.selectScript(glyphRun.script, glyphRun.language); | ||
// Choose a shaper based on the script, and setup a shaping plan. | ||
// This determines which features to apply to which glyphs. | ||
this.shaper = Shapers.choose(script); | ||
this.plan = new ShapingPlan(this.font, script, language); | ||
return this.shaper.plan(this.plan, this.glyphInfos, features); | ||
this.plan = new ShapingPlan(this.font, script); | ||
this.shaper.plan(this.plan, this.glyphInfos, glyphRun.features); | ||
// Assign chosen features to output glyph run | ||
for (let key in this.plan.allFeatures) { | ||
glyphRun.features[key] = true; | ||
} | ||
} | ||
substitute(glyphs) { | ||
substitute(glyphRun) { | ||
if (this.GSUBProcessor) { | ||
@@ -43,25 +53,23 @@ this.plan.process(this.GSUBProcessor, this.glyphInfos); | ||
// Map glyph infos back to normal Glyph objects | ||
glyphs = this.glyphInfos.map(glyphInfo => this.font.getGlyph(glyphInfo.id, glyphInfo.codePoints)); | ||
glyphRun.glyphs = this.glyphInfos.map(glyphInfo => this.font.getGlyph(glyphInfo.id, glyphInfo.codePoints)); | ||
} | ||
return glyphs; | ||
} | ||
position(glyphs, positions) { | ||
position(glyphRun) { | ||
if (this.shaper.zeroMarkWidths === 'BEFORE_GPOS') { | ||
this.zeroMarkAdvances(positions); | ||
this.zeroMarkAdvances(glyphRun.positions); | ||
} | ||
if (this.GPOSProcessor) { | ||
this.plan.process(this.GPOSProcessor, this.glyphInfos, positions); | ||
this.plan.process(this.GPOSProcessor, this.glyphInfos, glyphRun.positions); | ||
} | ||
if (this.shaper.zeroMarkWidths === 'AFTER_GPOS') { | ||
this.zeroMarkAdvances(positions); | ||
this.zeroMarkAdvances(glyphRun.positions); | ||
} | ||
// Reverse the glyphs and positions if the script is right-to-left | ||
if (this.plan.direction === 'rtl') { | ||
glyphs.reverse(); | ||
positions.reverse(); | ||
if (glyphRun.direction === 'rtl') { | ||
glyphRun.glyphs.reverse(); | ||
glyphRun.positions.reverse(); | ||
} | ||
@@ -68,0 +76,0 @@ |
@@ -32,2 +32,3 @@ import GlyphIterator from './GlyphIterator'; | ||
this.ligatureID = 1; | ||
this.currentFeature = null; | ||
} | ||
@@ -44,4 +45,4 @@ | ||
for (let entry of this.table.scriptList) { | ||
for (let s of script) { | ||
for (let s of script) { | ||
for (let entry of this.table.scriptList) { | ||
if (entry.tag === s) { | ||
@@ -61,6 +62,2 @@ return entry; | ||
entry = this.findScript(script); | ||
if (script) { | ||
entry = this.findScript(script); | ||
} | ||
if (!entry) { | ||
@@ -71,3 +68,3 @@ entry = this.findScript(DEFAULT_SCRIPTS); | ||
if (!entry) { | ||
return; | ||
return this.scriptTag; | ||
} | ||
@@ -79,18 +76,23 @@ | ||
this.language = null; | ||
this.languageTag = null; | ||
changed = true; | ||
} | ||
if (!language && language !== this.langugeTag) { | ||
if (!language || language !== this.languageTag) { | ||
this.language = null; | ||
for (let lang of this.script.langSysRecords) { | ||
if (lang.tag === language) { | ||
this.language = lang.langSys; | ||
this.langugeTag = lang.tag; | ||
changed = true; | ||
this.languageTag = lang.tag; | ||
break; | ||
} | ||
} | ||
} | ||
if (!this.language) { | ||
this.language = this.script.defaultLangSys; | ||
if (!this.language) { | ||
this.language = this.script.defaultLangSys; | ||
this.languageTag = null; | ||
} | ||
changed = true; | ||
} | ||
@@ -109,2 +111,4 @@ | ||
} | ||
return this.scriptTag; | ||
} | ||
@@ -188,2 +192,3 @@ | ||
for (let {feature, lookup} of lookups) { | ||
this.currentFeature = feature; | ||
this.glyphIterator.reset(lookup.flags); | ||
@@ -214,15 +219,23 @@ | ||
applyLookupList(lookupRecords) { | ||
let options = this.glyphIterator.options; | ||
let glyphIndex = this.glyphIterator.index; | ||
for (let lookupRecord of lookupRecords) { | ||
this.glyphIterator.index = glyphIndex; | ||
// Reset flags and find glyph index for this lookup record | ||
this.glyphIterator.reset(options, glyphIndex); | ||
this.glyphIterator.increment(lookupRecord.sequenceIndex); | ||
// Get the lookup and setup flags for subtables | ||
let lookup = this.table.lookupList.get(lookupRecord.lookupListIndex); | ||
this.glyphIterator.reset(lookup.flags, this.glyphIterator.index); | ||
// Apply lookup subtables until one matches | ||
for (let table of lookup.subTables) { | ||
this.applyLookup(lookup.lookupType, table); | ||
if (this.applyLookup(lookup.lookupType, table)) { | ||
break; | ||
} | ||
} | ||
} | ||
this.glyphIterator.index = glyphIndex; | ||
this.glyphIterator.reset(options, glyphIndex); | ||
return true; | ||
@@ -258,3 +271,3 @@ } | ||
while (idx < sequence.length && glyph && fn(sequence[idx], glyph.id)) { | ||
while (idx < sequence.length && glyph && fn(sequence[idx], glyph)) { | ||
if (matched) { | ||
@@ -277,7 +290,14 @@ matched.push(this.glyphIterator.index); | ||
sequenceMatches(sequenceIndex, sequence) { | ||
return this.match(sequenceIndex, sequence, (component, glyph) => component === glyph); | ||
return this.match(sequenceIndex, sequence, (component, glyph) => component === glyph.id); | ||
} | ||
sequenceMatchIndices(sequenceIndex, sequence) { | ||
return this.match(sequenceIndex, sequence, (component, glyph) => component === glyph, []); | ||
return this.match(sequenceIndex, sequence, (component, glyph) => { | ||
// If the current feature doesn't apply to this glyph, | ||
if (!(this.currentFeature in glyph.features)) { | ||
return false; | ||
} | ||
return component === glyph.id; | ||
}, []); | ||
} | ||
@@ -287,3 +307,3 @@ | ||
return this.match(sequenceIndex, sequence, (coverage, glyph) => | ||
this.coverageIndex(coverage, glyph) >= 0 | ||
this.coverageIndex(coverage, glyph.id) >= 0 | ||
); | ||
@@ -317,3 +337,3 @@ } | ||
return this.match(sequenceIndex, sequence, (classID, glyph) => | ||
classID === this.getClassID(glyph, classDef) | ||
classID === this.getClassID(glyph.id, classDef) | ||
); | ||
@@ -320,0 +340,0 @@ } |
@@ -40,3 +40,4 @@ import unicode from 'unicode-properties'; | ||
static planPostprocessing(plan, userFeatures) { | ||
plan.add([...COMMON_FEATURES, ...HORIZONTAL_FEATURES, ...userFeatures]); | ||
plan.add([...COMMON_FEATURES, ...HORIZONTAL_FEATURES]); | ||
plan.setFeatureOverrides(userFeatures); | ||
} | ||
@@ -43,0 +44,0 @@ |
import DefaultShaper from './DefaultShaper'; | ||
import ArabicShaper from './ArabicShaper'; | ||
import HangulShaper from './HangulShaper'; | ||
import IndicShaper from './IndicShaper'; | ||
import UniversalShaper from './UniversalShaper'; | ||
@@ -18,2 +19,22 @@ | ||
bng2: IndicShaper, // Bengali | ||
beng: IndicShaper, // Bengali | ||
dev2: IndicShaper, // Devanagari | ||
deva: IndicShaper, // Devanagari | ||
gjr2: IndicShaper, // Gujarati | ||
gujr: IndicShaper, // Gujarati | ||
guru: IndicShaper, // Gurmukhi | ||
gur2: IndicShaper, // Gurmukhi | ||
knda: IndicShaper, // Kannada | ||
knd2: IndicShaper, // Kannada | ||
mlm2: IndicShaper, // Malayalam | ||
mlym: IndicShaper, // Malayalam | ||
ory2: IndicShaper, // Oriya | ||
orya: IndicShaper, // Oriya | ||
taml: IndicShaper, // Tamil | ||
tml2: IndicShaper, // Tamil | ||
telu: IndicShaper, // Telugu | ||
tel2: IndicShaper, // Telugu | ||
khmr: IndicShaper, // Khmer | ||
bali: UniversalShaper, // Balinese | ||
@@ -70,6 +91,14 @@ batk: UniversalShaper, // Batak | ||
export function choose(script) { | ||
let shaper = SHAPERS[script]; | ||
if (shaper) { return shaper; } | ||
if (!Array.isArray(script)) { | ||
script = [script]; | ||
} | ||
for (let s of script) { | ||
let shaper = SHAPERS[s]; | ||
if (shaper) { | ||
return shaper; | ||
} | ||
} | ||
return DefaultShaper; | ||
} |
@@ -13,6 +13,5 @@ import * as Script from '../layout/Script'; | ||
export default class ShapingPlan { | ||
constructor(font, script, language) { | ||
constructor(font, script) { | ||
this.font = font; | ||
this.script = script; | ||
this.language = language; | ||
this.direction = Script.direction(script); | ||
@@ -28,8 +27,13 @@ this.stages = []; | ||
*/ | ||
_addFeatures(features) { | ||
let stage = this.stages[this.stages.length - 1]; | ||
_addFeatures(features, global) { | ||
let stageIndex = this.stages.length - 1; | ||
let stage = this.stages[stageIndex]; | ||
for (let feature of features) { | ||
if (!this.allFeatures[feature]) { | ||
if (this.allFeatures[feature] == null) { | ||
stage.push(feature); | ||
this.allFeatures[feature] = true; | ||
this.allFeatures[feature] = stageIndex; | ||
if (global) { | ||
this.globalFeatures[feature] = true; | ||
} | ||
} | ||
@@ -40,11 +44,2 @@ } | ||
/** | ||
* Adds the given features to the global list | ||
*/ | ||
_addGlobal(features) { | ||
for (let feature of features) { | ||
this.globalFeatures[feature] = true; | ||
} | ||
} | ||
/** | ||
* Add features to the last stage | ||
@@ -62,12 +57,6 @@ */ | ||
if (Array.isArray(arg)) { | ||
this._addFeatures(arg); | ||
if (global) { | ||
this._addGlobal(arg); | ||
} | ||
this._addFeatures(arg, global); | ||
} else if (typeof arg === 'object') { | ||
let features = (arg.global || []).concat(arg.local || []); | ||
this._addFeatures(features); | ||
if (arg.global) { | ||
this._addGlobal(arg.global); | ||
} | ||
this._addFeatures(arg.global || [], true); | ||
this._addFeatures(arg.local || [], false); | ||
} else { | ||
@@ -90,2 +79,19 @@ throw new Error("Unsupported argument to ShapingPlan#add"); | ||
setFeatureOverrides(features) { | ||
if (Array.isArray(features)) { | ||
this.add(features); | ||
} else if (typeof features === 'object') { | ||
for (let tag in features) { | ||
if (features[tag]) { | ||
this.add(tag); | ||
} else if (this.allFeatures[tag] != null) { | ||
let stage = this.stages[this.allFeatures[tag]]; | ||
stage.splice(stage.indexOf(tag), 1); | ||
delete this.allFeatures[tag]; | ||
delete this.globalFeatures[tag]; | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
@@ -111,3 +117,3 @@ * Assigns the global features to the given glyphs | ||
if (!positions) { | ||
stage(this.font, glyphs, positions); | ||
stage(this.font, glyphs, this); | ||
} | ||
@@ -114,0 +120,0 @@ |
@@ -49,6 +49,9 @@ import r from 'restructure'; | ||
let LookupFlags = new r.Bitfield(r.uint16, [ | ||
'rightToLeft', 'ignoreBaseGlyphs', 'ignoreLigatures', | ||
'ignoreMarks', 'useMarkFilteringSet', null, 'markAttachmentType' | ||
]); | ||
let LookupFlags = new r.Struct({ | ||
markAttachmentType: r.uint8, | ||
flags: new r.Bitfield(r.uint8, [ | ||
'rightToLeft', 'ignoreBaseGlyphs', 'ignoreLigatures', | ||
'ignoreMarks', 'useMarkFilteringSet' | ||
]) | ||
}); | ||
@@ -61,3 +64,3 @@ export function LookupList(SubTable) { | ||
subTables: new r.Array(new r.Pointer(r.uint16, SubTable), 'subTableCount'), | ||
markFilteringSet: r.uint16 // TODO: only present when flags says so... | ||
markFilteringSet: new r.Optional(r.uint16, t => t.flags.flags.useMarkFilteringSet) | ||
}); | ||
@@ -64,0 +67,0 @@ |
@@ -368,2 +368,6 @@ import r from 'restructure'; | ||
getAvailableFeatures(script, language) { | ||
return this._layoutEngine.getAvailableFeatures(script, language); | ||
} | ||
_getBaseGlyph(glyph, characters = []) { | ||
@@ -370,0 +374,0 @@ if (!this._glyphs[glyph]) { |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
1742719
18.34%128
5.79%24327
11.89%13
30%