mirror of
https://github.com/kiliman/operator-mono-lig.git
synced 2026-08-12 02:10:52 +02:00
⚡️ Add support for mapping non-ligature glyphs
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user