️ Add support for mapping non-ligature glyphs

This commit is contained in:
Kiliman
2019-07-24 09:19:43 -04:00
committed by unknown
parent f7cafe55b3
commit 360402a197
2 changed files with 60 additions and 35 deletions
+39 -15
View File
@@ -1,6 +1,4 @@
const xpath = require('xpath');
//const { XMLSerializer } = require('xmldom');
//const format = require('xml-formatter');
const NodeType = {};
NodeType.TEXT_NODE = 3;
@@ -15,9 +13,23 @@ let chainIndex = 0;
const substLookupMap = {};
const buildGsubTables = (_dom, ligature) => {
const glyphs = ligature.name.replace(/\.liga$/, '').split('_');
dom = _dom;
if (/_/.test(ligature.glyph) === false) {
// not a ligature, so see if glyph exists in font (as substitute)
const glyphNode = xpath.select(
`ttFont/GlyphOrder/GlyphID[@name="${ligature.name}"]`,
dom,
true
);
if (!glyphNode) {
return;
}
}
const glyphs = ligature.name.replace(/\.liga$/, '').split('_');
const isSingleGlyph = glyphs.length == 1;
gsubDom = xpath.select('ttFont/GSUB', dom, true);
// look for 'calt' feature
@@ -62,12 +74,10 @@ const buildGsubTables = (_dom, ligature) => {
});
appendChildren(
lookupDom,
createElementWithAttributes('LookupType', { value: 6 }),
createElementWithAttributes('LookupType', { value: isSingleGlyph ? 1 : 6 }),
createElementWithAttributes('LookupFlag', { value: 0 })
);
lookupListDom.appendChild(lookupDom);
// add contextual lookup to feature
const featureLookupCount = xpath.select(
'count(LookupListIndex)',
@@ -81,6 +91,11 @@ const buildGsubTables = (_dom, ligature) => {
})
);
if (isSingleGlyph) {
lookupDom.appendChild(
getSubstLookup(ligature.name, ligature.glyph, lookupDom)
);
} else {
lookupDom.appendChild(buildBacktrackFirst(glyphs));
lookupDom.appendChild(buildLookAheadLast(glyphs));
lookupDom.appendChild(buildLigatureSubst(glyphs, ligature.glyph));
@@ -90,6 +105,8 @@ const buildGsubTables = (_dom, ligature) => {
buildChainContext(getLIGs(n), [glyphs[n]], glyphs.slice(n + 1), 'LIG')
);
}
}
lookupListDom.appendChild(lookupDom);
};
const finalizeGsubTables = () => {
@@ -97,9 +114,8 @@ const finalizeGsubTables = () => {
let newIndex = xpath.select('count(Lookup)', lookupListDom, true);
// append added lookups
Object.entries(substLookupMap).forEach(lookupEntry => {
Object.entries(substLookupMap).forEach(([key, lookup]) => {
// fixup existing lookups with new index
const lookup = lookupEntry[1];
const oldIndex = lookup.getAttribute('index');
const substLookups = xpath.select(
`//ChainContextSubst/SubstLookupRecord/LookupListIndex[@value="${oldIndex}"]`,
@@ -212,8 +228,10 @@ const buildCoverage = (chainDom, tagName, coverage) => {
}
};
const getSubstLookup = (input, output) => {
let lookupDom = substLookupMap[output];
const getSubstLookup = (input, output, lookupDom) => {
if (!lookupDom) {
lookupDom = substLookupMap[output];
}
if (!lookupDom) {
lookupDom = createElementWithAttributes('Lookup', {
@@ -225,12 +243,18 @@ const getSubstLookup = (input, output) => {
lookupDom.appendChild(
createElementWithAttributes('LookupFlag', { value: 0 })
);
lookupDom.appendChild(
createElementWithAttributes('SingleSubst', { index: 0, Format: 2 })
);
substLookupMap[output] = lookupDom;
}
const singleSubstDom = xpath.select('SingleSubst', lookupDom, true);
let singleSubstDom = xpath.select('SingleSubst', lookupDom, true);
if (!singleSubstDom) {
singleSubstDom = createElementWithAttributes('SingleSubst', {
index: 0,
Format: 2
});
lookupDom.appendChild(singleSubstDom);
}
// look for input subst
const substitionDom = xpath.select(
+15 -14
View File
@@ -58,9 +58,9 @@ const buildFont = profile => {
//[...ligatures, { name: 'LIG', glyph: 'LIG' }];
processPatch('names', patchNames, dom, ligatures, profile);
processPatch('glyphs', patchGlyphs, dom, ligaturesWithLIG);
processPatch('gsub', patchGsub, dom, ligatures);
processPatch('charstrings', patchCharStrings, dom, ligaturesWithLIG);
processPatch('glyphs', patchGlyphs, dom, ligaturesWithLIG);
processPatch('hmtx', patchHmtx, dom);
console.log(`Writing ligature font file ${dstFileName}`);
@@ -103,7 +103,6 @@ const getProfiles = () => {
profiles.push(profile);
} else {
if (!profile) {
console.log(`Found ${ch} instead of [`);
throw new Error('You must profile a profile name in []');
}
profile.ligatures.push(line);
@@ -113,22 +112,24 @@ const getProfiles = () => {
return profiles;
};
const filterLigatures = (name, ligatures) => {
// loop through ligatures and return if name applies or not
const filterLigatures = (name, mappings) => {
// loop through mappings and return if name applies or not
// skip if setting is !name
return ligatures.filter(ligature => ligature === '!' + name).length === 0;
return mappings.filter(entry => entry === '!' + name).length === 0;
};
const mapLigatures = (name, ligatures) => {
// return { ligature, glyph }
let entry = { name, glyph: name };
ligatures.forEach(ligature => {
const n = ligature.indexOf('=');
if (n > 0 && ligature.substr(0, n) === name) {
entry.glyph = ligature.substr(n + 1);
const mapLigatures = (name, mappings) => {
let mapping = { name, glyph: name };
mappings.forEach(entry => {
const [val1, val2] = entry.split('=');
// handle lig=altliga
if (val1 === name) {
mapping.glyph = val2;
} else if (val2 === name) {
mapping.name = val1;
}
});
return entry;
return mapping;
};
const filterGlyphsExists = ({ glyph }) => {
@@ -266,7 +267,7 @@ const patchGsub = (dom, ligatures) => {
if (ligatures.length <= 1) return;
ligatures
.filter(ligature => /_/.test(ligature.glyph))
//.filter(ligature => /_/.test(ligature.glyph))
.forEach(ligature => {
// build gsub tables
gsub.buildGsubTables(dom, ligature);