Complexity cleanup

This commit is contained in:
be5invis 2020-11-01 10:22:19 -08:00
parent f38901ba7a
commit 97920d0f4d
4 changed files with 212 additions and 192 deletions

View file

@ -18,6 +18,7 @@
"no-console": 0,
"no-constant-condition": ["error", { "checkLoops": false }],
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
"no-unused-vars": ["off"]
"no-unused-vars": ["off"],
"complexity": ["warn", 16]
}
}

View file

@ -3,78 +3,22 @@
const { Radical } = require("../../support/gr");
module.exports = function gcFont(glyphStore, excludedChars, restFont, cfg) {
markSweepOtl(restFont.GSUB);
markSweepOtl(restFont.GPOS);
const sink = mark(glyphStore, excludedChars, restFont, cfg);
markSweepOtlLookups(restFont.GSUB);
markSweepOtlLookups(restFont.GPOS);
const sink = markGlyphs(glyphStore, excludedChars, restFont, cfg);
return sweep(glyphStore, sink);
};
function markSweepOtl(table) {
function markSweepOtlLookups(table) {
if (!table || !table.features || !table.lookups) return;
const accessibleLookupsIds = new Set();
markLookups(table, accessibleLookupsIds);
let lookups1 = {};
for (const l in table.lookups) {
if (accessibleLookupsIds.has(l)) lookups1[l] = table.lookups[l];
}
table.lookups = lookups1;
let features1 = {};
for (let f in table.features) {
const feature = table.features[f];
if (!feature) continue;
const featureFiltered = [];
for (const l of feature) if (accessibleLookupsIds.has(l)) featureFiltered.push(l);
if (!featureFiltered.length) continue;
features1[f] = featureFiltered;
}
table.features = features1;
sweepLookups(table, accessibleLookupsIds);
sweepFeatures(table, accessibleLookupsIds);
}
function mark(glyphStore, excludedChars, restFont, cfg) {
const sink = markInitial(glyphStore, excludedChars);
while (markStep(glyphStore, sink, restFont, cfg));
return sink;
}
function markInitial(glyphStore, excludedChars) {
let sink = new Set();
for (const [gName, g] of glyphStore.namedEntries()) {
if (!g) continue;
if (g.glyphRank > 0) sink.add(gName);
if (Radical.get(g)) sink.add(gName);
const unicodeSet = glyphStore.queryUnicodeOf(g);
if (unicodeSet) {
for (const u of unicodeSet) {
if (!excludedChars.has(u)) sink.add(gName);
}
}
}
return sink;
}
function markStep(glyphStore, sink, restFont, cfg) {
const glyphCount = sink.size;
if (restFont.GSUB) {
for (const l in restFont.GSUB.lookups) {
const lookup = restFont.GSUB.lookups[l];
if (!lookup || !lookup.subtables) continue;
for (let st of lookup.subtables) {
markSubtable(sink, lookup.type, st, cfg);
}
}
}
const glyphCount1 = sink.size;
return glyphCount1 > glyphCount;
}
function markLookups(table, sink) {
if (!table || !table.features) return;
for (let f in table.features) {
const feature = table.features[f];
if (!feature) continue;
for (const l of feature) sink.add(l);
}
markLookupsStart(table, sink);
let loop = 0,
lookupSetChanged = false;
do {
@ -94,38 +38,111 @@ function markLookups(table, sink) {
lookupSetChanged = sizeBefore !== sink.size;
} while (loop < 0xff && lookupSetChanged);
}
function markLookupsStart(table, sink) {
for (let f in table.features) {
const feature = table.features[f];
if (!feature) continue;
for (const l of feature) sink.add(l);
}
}
function sweepLookups(table, accessibleLookupsIds) {
let lookups1 = {};
for (const l in table.lookups) {
if (accessibleLookupsIds.has(l)) lookups1[l] = table.lookups[l];
}
table.lookups = lookups1;
return accessibleLookupsIds;
}
function sweepFeatures(table, accessibleLookupsIds) {
let features1 = {};
for (let f in table.features) {
const feature = table.features[f];
if (!feature) continue;
const featureFiltered = [];
for (const l of feature) if (accessibleLookupsIds.has(l)) featureFiltered.push(l);
if (!featureFiltered.length) continue;
features1[f] = featureFiltered;
}
table.features = features1;
}
function markSubtable(sink, type, st, cfg) {
function markGlyphs(glyphStore, excludedChars, restFont, cfg) {
const sink = markGlyphsInitial(glyphStore, excludedChars);
while (markGlyphsStep(glyphStore, sink, restFont, cfg));
return sink;
}
function markGlyphsInitial(glyphStore, excludedChars) {
let sink = new Set();
for (const [gName, g] of glyphStore.namedEntries()) {
if (!g) continue;
if (g.glyphRank > 0) sink.add(gName);
if (Radical.get(g)) sink.add(gName);
const unicodeSet = glyphStore.queryUnicodeOf(g);
if (unicodeSet) {
for (const u of unicodeSet) {
if (!excludedChars.has(u)) sink.add(gName);
}
}
}
return sink;
}
function markGlyphsStep(glyphStore, sink, restFont, cfg) {
const glyphCount = sink.size;
if (restFont.GSUB) {
for (const l in restFont.GSUB.lookups) {
const lookup = restFont.GSUB.lookups[l];
if (!lookup || !lookup.subtables) continue;
for (let st of lookup.subtables) {
markGlyphsSubtable(sink, lookup.type, st, cfg);
}
}
}
const glyphCount1 = sink.size;
return glyphCount1 > glyphCount;
}
function markGlyphsSubtable(sink, type, st, cfg) {
switch (type) {
case "gsub_single":
for (const k in st) if (sink.has(k) && st[k]) sink.add(st[k]);
break;
return markGlyphsGsubSingle(sink, st, cfg);
case "gsub_multiple":
for (const k in st) if (sink.has(k) && st[k]) for (const g of st[k]) sink.add(g);
break;
return markGlyphsGsubMultiple(sink, st, cfg);
case "gsub_alternate":
return markGlyphsGsubAlternate(sink, st, cfg);
case "gsub_ligature":
return markGlyphsGsubLigature(sink, st, cfg);
case "gsub_chaining":
break;
case "gsub_reverse":
return markGlyphsGsubReverse(sink, st, cfg);
}
}
function markGlyphsGsubSingle(sink, st, cfg) {
for (const k in st) if (sink.has(k) && st[k]) sink.add(st[k]);
}
function markGlyphsGsubMultiple(sink, st, cfg) {
for (const k in st) if (sink.has(k) && st[k]) for (const g of st[k]) sink.add(g);
}
function markGlyphsGsubAlternate(sink, st, cfg) {
if (!cfg || !cfg.ignoreAltSub) {
for (const k in st) if (sink.has(k) && st[k]) for (const g of st[k]) sink.add(g);
}
break;
case "gsub_ligature":
}
function markGlyphsGsubLigature(sink, st, cfg) {
for (const sub of st.substitutions) {
let check = true;
for (const g of sub.from) if (!sink.has(g)) check = false;
if (check && sub.to) sink.add(sub.to);
}
break;
case "gsub_chaining":
break;
case "gsub_reverse":
}
function markGlyphsGsubReverse(sink, st, cfg) {
if (st.match && st.to) {
const matchCoverage = st.match[st.inputIndex];
for (let j = 0; j < matchCoverage.length; j++) {
if (sink.has(matchCoverage[j]) && st.to[j]) sink.add(st.to[j]);
}
}
break;
}
}
function sweep(glyphStore, gnSet) {

View file

@ -25,6 +25,11 @@ module.exports = async function (charMapPath, charMapItalicPath, charMapObliqueP
};
function getSupportedLanguageSet(rawCoverage) {
const supportLocaleSet = getSupportLocaleSet(rawCoverage);
addSimilarLocales(supportLocaleSet);
return getSupportedLangs(supportLocaleSet);
}
function getSupportLocaleSet(rawCoverage) {
const supportLocaleSet = new Set();
for (const locale of cldr.localeIds) {
@ -52,6 +57,9 @@ function getSupportedLanguageSet(rawCoverage) {
supportLocaleSet.add(locale);
}
}
return supportLocaleSet;
}
function addSimilarLocales(supportLocaleSet) {
for (const loc of supportLocaleSet) {
const seg = loc.split("_");
if (seg.length < 2) continue;
@ -62,6 +70,8 @@ function getSupportedLanguageSet(rawCoverage) {
}
}
}
}
function getSupportedLangs(supportLocaleSet) {
const supportLangSet = new Set(overrideSupportedLanguages);
for (const loc of supportLocaleSet) {
const seg = loc.split("_");
@ -74,7 +84,6 @@ function getSupportedLanguageSet(rawCoverage) {
}
if (displayName) supportLangSet.add(displayName);
}
return supportLangSet;
}

View file

@ -23,11 +23,7 @@ const ARCHIVE_DIR = "release-archives";
const TTX = "ttx";
const PATEL_C = ["node", "./node_modules/patel/bin/patel-c"];
const TTCIZE = [
"node",
"--max-old-space-size=8192",
"node_modules/otb-ttc-bundle/bin/otb-ttc-bundle"
];
const TTCIZE = ["node", "node_modules/otb-ttc-bundle/bin/otb-ttc-bundle"];
const webfontFormats = [
["woff2", "woff2"],
["ttf", "truetype"]
@ -147,29 +143,6 @@ const BuildPlans = computed("metadata:build-plans", async target => {
return { fileNameToBpMap, buildPlans: returnBuildPlans };
});
function validateAndShimBuildPlans(prefix, bp, dWeights, dSlopes, dWidths) {
if (!bp.family) {
fail(`Build plan for ${prefix} does not have a family name. Exit.`);
}
if (!bp.slopes && bp.slants) {
echo.warn(
`Build plan for ${prefix} uses legacy "slants" to define slopes. ` +
`Use "slopes" instead.`
);
}
if (!bp.pre) bp.pre = {};
if (!bp.pre.design) bp.pre.design = bp.design || [];
if (!bp.pre.upright) bp.pre.upright = bp.upright || [];
if (!bp.pre.oblique) bp.pre.oblique = bp.oblique || [];
if (!bp.pre.italic) bp.pre.italic = bp.italic || [];
bp.weights = bp.weights || dWeights;
bp.slopes = bp.slopes || bp.slants || dSlopes;
bp.widths = bp.widths || dWidths;
}
const BuildPlanOf = computed.group("metadata:build-plan-of", async (target, gid) => {
const [{ buildPlans }] = await target.need(BuildPlans);
const plan = buildPlans[gid];
@ -241,29 +214,32 @@ function getSuffixMapping(weights, slopes, widths) {
for (const s in slopes) {
for (const wd in widths) {
const suffix = makeSuffix(w, wd, s, DEFAULT_SUBFAMILY);
mapping[suffix] = {
weight: w,
shapeWeight: nValidate("Shape weight of " + w, weights[w].shape, vlShapeWeight),
cssWeight: nValidate("CSS weight of " + w, weights[w].css, vlCssWeight),
menuWeight: nValidate("Menu weight of " + w, weights[w].menu, vlMenuWeight),
width: wd,
shapeWidth: nValidate(
"Shape width of " + wd,
widths[wd].shape,
vlShapeWidth,
fixShapeWidth
),
cssStretch: widths[wd].css || wd,
menuWidth: nValidate("Menu width of " + wd, widths[wd].menu, vlMenuWidth),
slope: s,
cssStyle: slopes[s] || s,
menuSlope: slopes[s] || s
};
mapping[suffix] = getSuffixMappingItem(weights, w, slopes, s, widths, wd);
}
}
}
return mapping;
}
function getSuffixMappingItem(weights, w, slopes, s, widths, wd) {
return {
// Weights
weight: w,
shapeWeight: nValidate("Shape weight of " + w, weights[w].shape, VlShapeWeight),
cssWeight: nValidate("CSS weight of " + w, weights[w].css, VlCssWeight),
menuWeight: nValidate("Menu weight of " + w, weights[w].menu, VlMenuWeight),
// Widths
width: wd,
shapeWidth: nValidate("Shape width of " + wd, widths[wd].shape, VlShapeWidth),
cssStretch: widths[wd].css || wd,
menuWidth: nValidate("Menu width of " + wd, widths[wd].menu, VlMenuWidth),
// Slopes
slope: s,
cssStyle: slopes[s] || s,
menuSlope: slopes[s] || s
};
}
function makeFileName(prefix, suffix) {
return prefix + "-" + suffix;
@ -276,65 +252,6 @@ function makeSuffix(w, wd, s, fallback) {
);
}
function validateRecommendedWeight(w, value, label) {
if (recommendedMenuWeights[w] && recommendedMenuWeights[w] !== value) {
echo.warn(
`${label} weight settings of ${w} ( = ${value}) doesn't match ` +
`the recommended value ( = ${recommendedMenuWeights[w]}).`
);
}
}
function nValidate(key, v, f, ft) {
if (ft) v = ft(v);
if (typeof v !== "number" || !isFinite(v) || (f && !f(v))) {
throw new TypeError(`${key} = ${v} is not a valid number.`);
}
return v;
}
function vlShapeWeight(x) {
return x >= 100 && x <= 900;
}
function vlCssWeight(x) {
return x > 0 && x < 1000;
}
function vlMenuWeight(x) {
return vlCssWeight(x);
}
const g_widthFixupMemory = new Map();
function fixShapeWidth(x) {
if (x >= 3 && x <= 9) {
if (g_widthFixupMemory.has(x)) return g_widthFixupMemory.get(x);
const xCorrected = Math.round(500 * Math.pow(Math.sqrt(576 / 500), x - 5));
echo.warn(
`The build plan is using legacy width grade ${x}. ` +
`Converting to unit width ${xCorrected}.`
);
g_widthFixupMemory.set(x, xCorrected);
return xCorrected;
} else {
return x;
}
}
function vlShapeWidth(x) {
return x >= 433 && x <= 665;
}
function vlMenuWidth(x) {
return x >= 1 && x <= 9 && x % 1 === 0;
}
const recommendedMenuWeights = {
thin: 100,
extralight: 200,
light: 300,
regular: 400,
book: 450,
medium: 500,
semibold: 600,
bold: 700,
extrabold: 800,
heavy: 900
};
function whyBuildPlanIsnNotThere(gid) {
if (!fs.existsSync(PRIVATE_BUILD_PLANS))
return "\n -- Possible reason: Config file 'private-build-plans.toml' does not exist.";
@ -939,3 +856,79 @@ const Parameters = task(`meta:parameters`, async target => {
sfu`params/ligation-set.toml`
);
});
///////////////////////////////////////////////////////////
////// Config Validation //////
///////////////////////////////////////////////////////////
// Build plan validation
function validateAndShimBuildPlans(prefix, bp, dWeights, dSlopes, dWidths) {
if (!bp.family) {
fail(`Build plan for ${prefix} does not have a family name. Exit.`);
}
if (!bp.slopes && bp.slants) {
echo.warn(
`Build plan for ${prefix} uses legacy "slants" to define slopes. ` +
`Use "slopes" instead.`
);
}
bp.weights = bp.weights || dWeights;
bp.slopes = bp.slopes || bp.slants || dSlopes;
bp.widths = bp.widths || dWidths;
}
// Recommended weight validation
function validateRecommendedWeight(w, value, label) {
const RecommendedMenuWeights = {
thin: 100,
extralight: 200,
light: 300,
regular: 400,
book: 450,
medium: 500,
semibold: 600,
bold: 700,
extrabold: 800,
heavy: 900
};
if (RecommendedMenuWeights[w] && RecommendedMenuWeights[w] !== value) {
echo.warn(
`${label} weight settings of ${w} ( = ${value}) doesn't match ` +
`the recommended value ( = ${RecommendedMenuWeights[w]}).`
);
}
}
// Value validation
function nValidate(key, v, validator) {
if (validator.fixup) v = validator.fix(v);
if (typeof v !== "number" || !isFinite(v) || !validator.validate(v)) {
throw new TypeError(`${key} = ${v} is not a valid number.`);
}
return v;
}
const VlShapeWeight = { validate: x => x >= 100 && x <= 900 };
const VlCssWeight = { validate: x => x > 0 && x < 1000 };
const VlMenuWeight = VlCssWeight;
const g_widthFixupMemory = new Map();
const VlShapeWidth = {
validate: x => x >= 433 && x <= 665,
fix(x) {
if (x >= 3 && x <= 9) {
if (g_widthFixupMemory.has(x)) return g_widthFixupMemory.get(x);
const xCorrected = Math.round(500 * Math.pow(Math.sqrt(576 / 500), x - 5));
echo.warn(
`The build plan is using legacy width grade ${x}. ` +
`Converting to unit width ${xCorrected}.`
);
g_widthFixupMemory.set(x, xCorrected);
return xCorrected;
} else {
return x;
}
}
};
const VlMenuWidth = { validate: x => x >= 1 && x <= 9 && x % 1 === 0 };