Comparing version 1.6.1 to 1.7.1
{ | ||
"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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1742719
128
24327
13