Implement feature dropping in spacing derivation.

This commit is contained in:
be5invis 2022-09-18 21:21:59 -07:00
parent 3431234604
commit 40a0f310c0
11 changed files with 107 additions and 27 deletions

View file

@ -1,11 +1,15 @@
import fs from "fs";
import path from "path";
import url from "url";
import { FontIo, Ot } from "ot-builder";
import * as Toml from "@iarna/toml";
import { FontIo, Ot, CliProc } from "ot-builder";
import { assignFontNames, createNamingDictFromArgv } from "./gen/meta/naming.mjs";
export default main;
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
export default main;
async function main(argv) {
const font = await readTTF(argv);
@ -14,11 +18,19 @@ async function main(argv) {
switch (argv.shape.spacing) {
case "term":
deriveTerm(font);
await deriveTerm(font);
break;
case "fontconfig-mono":
await deriveTerm(font);
await deriveFixed_DropWideChars(font);
await deriveFixed_DropFeatures(font, false);
CliProc.gcFont(font, Ot.ListGlyphStoreFactory);
break;
case "fixed":
deriveTerm(font);
deriveFixed(font);
await deriveTerm(font);
await deriveFixed_DropWideChars(font);
await deriveFixed_DropFeatures(font, true);
CliProc.gcFont(font, Ot.ListGlyphStoreFactory);
break;
}
@ -26,7 +38,7 @@ async function main(argv) {
}
// To derive -Term variants, simply apply NWID
function deriveTerm(font) {
async function deriveTerm(font) {
const gsub = font.gsub;
let nwidMap = new Map();
for (const feature of gsub.features) {
@ -52,7 +64,7 @@ function deriveTerm(font) {
// In FontConfig, a font is considered "monospace" if and only if all encoded non-combining
// characters (AW > 0) have the same width. We use this method to validate whether our
// "Fixed" subfamilies are properly built.
function deriveFixed(font) {
async function deriveFixed_DropWideChars(font) {
const unitWidth = font.os2.xAvgCharWidth;
for (const [ch, g] of [...font.cmap.unicode.entries()]) {
const advanceWidth = g.horizontal.end - g.horizontal.start;
@ -64,6 +76,62 @@ function deriveFixed(font) {
}
}
async function deriveFixed_DropFeatures(font, fFixed) {
if (!font.gsub) return;
const dropFeatureTagSet = new Set();
dropFeatureTagSet.add("NWID");
dropFeatureTagSet.add("WWID");
if (fFixed) {
const LIGATIONS_TOML = path.resolve(__dirname, "../params/ligation-set.toml");
const rawLigationData = Toml.parse(await fs.promises.readFile(LIGATIONS_TOML, "utf-8"));
for (const [_, comp] of Object.entries(rawLigationData.composite)) {
dropFeatureTagSet.add(comp.tag);
}
}
for (const feature of font.gsub.features) {
if (dropFeatureTagSet.has(feature.tag)) {
feature.lookups.length = 0;
feature.params = null;
}
}
markSweepLookups(font.gsub);
}
function markSweepLookups(table) {
let lookupSet = new Set();
for (const feature of table.features) {
for (const lookup of feature.lookups) {
lookupSet.add(lookup);
}
}
do {
let sizeBefore = lookupSet.size;
for (const lookup of table.lookups) {
if (lookup instanceof Ot.Gsub.Chaining || lookup instanceof Ot.Gpos.Chaining) {
for (const rule of lookup.rules) {
for (const app of rule.applications) lookupSet.add(app.apply);
}
}
}
let sizeAfter = lookupSet.size;
if (sizeBefore >= sizeAfter) break;
} while (true);
let front = 0;
for (let rear = 0; rear < table.lookups.length; rear++) {
if (lookupSet.has(table.lookups[rear])) {
table.lookups[front++] = table.lookups[rear];
}
}
table.lookups.length = front;
return lookupSet;
}
async function readTTF(argv) {
const buf = await fs.promises.readFile(argv.i);
const sfnt = FontIo.readSfntOtf(buf);