Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
be5invis committed May 9, 2020
2 parents 4f49292 + e2afedb commit f5a1bf7
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 86 deletions.
1 change: 0 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"rules": {
"indent": ["error", "tab", { "SwitchCase": 1 }],
"linebreak-style": ["error", "windows"],
"quotes": ["error", "double", { "allowTemplateLiterals": true }],
"semi": ["error", "always"],
"no-var": "error",
"no-console": 0,
Expand Down
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
166 changes: 138 additions & 28 deletions make/common/gc.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,141 @@
"use strict";

module.exports = function gcFont(font, cfg) {
const sink = mark(font, cfg);
sweep(font, sink);
markSweepOtl(font.GSUB);
markSweepOtl(font.GPOS);
const glyphSink = markGlyphs(font, cfg);
sweepGlyphs(font, glyphSink);
return [...glyphSink].sort((a, b) => a[1] - b[1]).map(x => x[0]);
};

function mark(font, cfg) {
let sink = new Set();
let glyphCount = 0;
///////////////////////////////////////////////////////////////////////////////////////////////////

function markSweepOtl(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;
}
function markLookups(gsub, lookupSet) {
if (!gsub || !gsub.features) return;
for (let f in gsub.features) {
const feature = gsub.features[f];
if (!feature) continue;
for (const l of feature) lookupSet.add(l);
}
let loop = 0,
lookupSetChanged = false;
do {
lookupSetChanged = false;
let sizeBefore = lookupSet.size;
for (const l of Array.from(lookupSet)) {
const lookup = gsub.lookups[l];
if (!lookup || !lookup.subtables) continue;
if (lookup.type === "gsub_chaining" || lookup.type === "gpos_chaining") {
for (let st of lookup.subtables) {
if (!st || !st.apply) continue;
for (const app of st.apply) lookupSet.add(app.lookup);
}
}
}
loop++;
lookupSetChanged = sizeBefore !== lookupSet.size;
} while (loop < 0xff && lookupSetChanged);
}

///////////////////////////////////////////////////////////////////////////////////////////////////

const RANK_MOST = 0;
const RANK_UNICODE_PREFERRED = 0x1000000;
const RANK_UNICODE_ALIASED = 0x2000000;
const RANK_LEAST = 0xf000000;

function simplyAdd(sink, gn) {
if (!sink.has(gn)) sink.set(gn, RANK_LEAST);
}

function rankFromUnicode(c) {
if (!c) return RANK_LEAST;
// CJK aliased
if (
(c >= 0x2e80 && c <= 0x2fff) || // CJK radicals
(c >= 0x3192 && c <= 0x319f) || // Ideographic annotation
(c >= 0x31c0 && c <= 0x31ef) || // CJK strokes
(c >= 0xf900 && c <= 0xfa6f) // CJK compatibility ideographs
) {
return RANK_UNICODE_ALIASED | c;
}

// Letter-like Symbols
if (c >= 0x2100 && c <= 0x214f) return RANK_UNICODE_ALIASED | c;

return RANK_UNICODE_PREFERRED | c;
}

function rankedAdd(sink, gn, rank) {
if (!rank) simplyAdd(sink, gn);
if (sink.has(gn)) {
const existing = sink.get(gn);
if (rank < existing) sink.set(gn, rank);
} else {
sink.set(gn, rank);
}
}

function markGlyphs(font, cfg) {
let glyphSink = new Map();

if (font.glyf[".notdef"]) glyphSink.set(".notdef", RANK_MOST);

sink.add(".notdef");
if (font.glyph_order) {
for (let idx = 0; idx < font.glyph_order.length; idx++) {
const g = font.glyph_order[idx];
if (idx === 0 || /\.notdef$/.test(g)) sink.add(g);
if (idx === 0 || /\.notdef$/.test(g)) glyphSink.set(g, RANK_MOST);
}
}

if (cfg && cfg.rankMap) {
for (const [gn, rank] of cfg.rankMap) rankedAdd(glyphSink, gn, rank);
}

if (font.cmap) {
for (const k in font.cmap) {
if (font.cmap[k]) sink.add(font.cmap[k]);
if (font.cmap[k]) rankedAdd(glyphSink, font.cmap[k], rankFromUnicode(parseInt(k)));
}
}
if (font.cmap_uvs) {
for (const k in font.cmap_uvs) {
if (font.cmap_uvs[k]) sink.add(font.cmap_uvs[k]);
if (font.cmap_uvs[k]) simplyAdd(glyphSink, font.cmap_uvs[k]);
}
}

let glyphCount;
do {
glyphCount = sink.size;
glyphCount = glyphSink.size;

if (font.GSUB) {
for (const l in font.GSUB.lookups) {
const lookup = font.GSUB.lookups[l];
if (!lookup || !lookup.subtables) continue;
if (lookup && lookup.subtables) {
for (let st of lookup.subtables) {
markSubtable(sink, lookup.type, st, cfg);
markSubtable(glyphSink, lookup.type, st, cfg);
}
}
}
Expand All @@ -47,32 +145,42 @@ function mark(font, cfg) {
for (const g in font.glyf) {
const glyph = font.glyf[g];
if (!glyph || !glyph.references) continue;
for (const ref of glyph.references) if (ref && ref.glyph) sink.add(ref.glyph);
for (const ref of glyph.references) {
if (ref && ref.glyph) simplyAdd(glyphSink, ref.glyph);
}
}
}

let glyphCount1 = sink.size;
let glyphCount1 = glyphSink.size;
if (glyphCount1 === glyphCount) break;
} while (true);
return sink;
return glyphSink;
}

function markSubtable(sink, type, st, cfg) {
function markSubtable(glyphSink, type, st, cfg) {
switch (type) {
case "gsub_single":
for (const k in st) if (glyphSink.has(k) && st[k]) simplyAdd(glyphSink, st[k]);
break;
case "gsub_multi":
for (const k in st) if (sink.has(k) && st[k]) sink.add(st[k]);
for (const k in st)
if (glyphSink.has(k) && st[k]) {
for (const gTo of st[k]) simplyAdd(glyphSink, gTo);
}
break;
case "gsub_alternate":
if (!cfg || !cfg.ignoreAltSub) {
for (const k in st) if (sink.has(k) && st[k]) sink.add(st[k]);
for (const k in st)
if (glyphSink.has(k) && st[k]) {
for (const gTo of st[k]) simplyAdd(glyphSink, gTo);
}
}
break;
case "gsub_ligature":
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);
for (const g of sub.from) if (!glyphSink.has(g)) check = false;
if (check && sub.to) simplyAdd(glyphSink, sub.to);
}
break;
case "gsub_chaining":
Expand All @@ -81,52 +189,54 @@ function markSubtable(sink, type, 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]);
if (glyphSink.has(matchCoverage[j]) && st.to[j]) simplyAdd(glyphSink, st.to[j]);
}
}
break;
}
}

function sweep(font, sink) {
///////////////////////////////////////////////////////////////////////////////////////////////////

function sweepGlyphs(font, glyphSink) {
// glyf
if (font.glyf) {
const filteredGlyf = {};
for (const key in font.glyf) {
if (sink.has(key)) filteredGlyf[key] = font.glyf[key];
if (glyphSink.has(key)) filteredGlyf[key] = font.glyf[key];
}
font.glyf = filteredGlyf;
} else {
font.glyf = {};
}

// GSUB
sweepOtl(font.GSUB, sink);
sweepOtl(font.GSUB, glyphSink);
}

function sweepOtl(gsub, sink) {
function sweepOtl(gsub, glyphSink) {
if (!gsub || !gsub.lookups) return;
for (const lid in gsub.lookups) {
const lookup = gsub.lookups[lid];
if (!lookup.subtables) continue;
const newSubtables = [];
for (const st of lookup.subtables) {
const keep = sweepSubtable(st, lookup.type, sink);
const keep = sweepSubtable(st, lookup.type, glyphSink);
if (keep) newSubtables.push(st);
}
lookup.subtables = newSubtables;
}
}

function sweepSubtable(st, type, sink) {
function sweepSubtable(st, type, glyphSink) {
switch (type) {
case "gsub_ligature": {
if (!st.substitutions) return false;
let newSubst = [];
for (const rule of st.substitutions) {
let include = true;
if (!sink.has(rule.to)) include = false;
for (const from of rule.from) if (!sink.has(from)) include = false;
if (!glyphSink.has(rule.to)) include = false;
for (const from of rule.from) if (!glyphSink.has(from)) include = false;
if (include) newSubst.push(rule);
}
st.substitutions = newSubst;
Expand Down
9 changes: 5 additions & 4 deletions make/common/unicode-kind.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"use strict";

exports.isIdeograph = function(c) {
exports.isIdeograph = function (c) {
return (
(c >= 0x2e80 && c <= 0x2fff) || // CJK radicals
(c >= 0x3192 && c <= 0x319f) || // Ideographic annotation
(c >= 0x31c0 && c <= 0x31ef) || // CJK strokes
(c >= 0x3300 && c <= 0x9fff) || // BMP ideographs
(c >= 0x3400 && c <= 0x4dbf) || // ExtA
(c >= 0x4e00 && c <= 0x9fff) || // URO
(c >= 0xf900 && c <= 0xfa6f) || // CJK compatibility ideographs
(c >= 0x20000 && c <= 0x3ffff) // SIP, TIP
);
Expand All @@ -23,11 +24,11 @@ exports.isKorean = c =>
(c >= 0xa960 && c <= 0xa97f) ||
(c >= 0xd7b0 && c <= 0xd7ff);

exports.isWS = function(c, _isType = false, isTerm = false) {
exports.isWS = function (c, _isType = false, isTerm = false) {
return c >= (isTerm ? 0x2000 : 0x20a0) && c < 0x3000 && !(c >= 0x2e3a && c <= 0x2e3b);
};

exports.filterUnicodeRange = function(a, fn) {
exports.filterUnicodeRange = function (a, fn) {
for (let c in a.cmap) {
if (!fn(c - 0)) a.cmap[c] = null;
}
Expand Down
4 changes: 2 additions & 2 deletions make/pass1/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ async function pass(ctx, config, argv) {
);

if (argv.italize) italize(a, +10);
gc(ctx.items.a);
await ctx.run(build, "a", { to: config.o, optimize: true });
ctx.items.a.glyph_order = gc(ctx.items.a);
await ctx.run(build, "a", { to: config.o });
}

module.exports = async function makeFont(ctx, config, argv) {
Expand Down
8 changes: 6 additions & 2 deletions make/pass2/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ module.exports = async function makeFont(ctx, config, argv) {
await ctx.run(merge.below, "a", "a", "c", { mergeOTL: true });
shareFeatures(ctx.items.a.GSUB);
shareFeatures(ctx.items.a.GPOS);
gc(ctx.items.a);
await ctx.run(build, "a", { to: argv.o, optimize: true });

// This will make the order of glyphs in TTC less mangled
ctx.items.a.glyf.__glyf_pad__ = { advanceWidth: 0, contours: [[{ x: 0, y: 0, on: true }]] };
ctx.items.a.glyph_order = gc(ctx.items.a, { rankMap: [["__glyf_pad__", 1]] });

await ctx.run(build, "a", { to: argv.o });
};
2 changes: 2 additions & 0 deletions make/punct/as.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const {
removeDashCcmp,
aliasFeatMap
} = require("./common");
const gc = require("../common/gc");

module.exports = async function makeFont(ctx, config, argv) {
const a = await ctx.run(introduce, "a", {
Expand All @@ -41,6 +42,7 @@ module.exports = async function makeFont(ctx, config, argv) {
}
removeUnusedFeatures(ctx.items.a, argv.mono);
aliasFeatMap(ctx.items.a, "vert", [[0x2014, 0x2015]]);
gc(ctx.items.a);

await ctx.run(build, "a", { to: argv.o, optimize: true });
ctx.remove("a");
Expand Down
2 changes: 1 addition & 1 deletion make/punct/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,14 @@ function removeUnusedFeature(table, tag) {
}

exports.removeUnusedFeatures = function (a, mono) {
removeUnusedFeature(a.GSUB, "aalt");
removeUnusedFeature(a.GSUB, "pwid");
removeUnusedFeature(a.GSUB, "fwid");
removeUnusedFeature(a.GSUB, "hwid");
removeUnusedFeature(a.GSUB, "twid");
removeUnusedFeature(a.GSUB, "qwid");

if (mono) {
removeUnusedFeature(a.GSUB, "aalt");
removeUnusedFeature(a.GSUB, "locl");
removeUnusedFeature(a.GPOS, "kern");
removeUnusedFeature(a.GPOS, "vkrn");
Expand Down
2 changes: 2 additions & 0 deletions make/punct/ws.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
filterUnicodeRange
} = require("../common/unicode-kind");
const { sanitizeSymbols, removeUnusedFeatures, toPWID } = require("./common");
const gc = require("../common/gc");

module.exports = async function makeFont(ctx, config, argv) {
const a = await ctx.run(introduce, "a", {
Expand All @@ -33,6 +34,7 @@ module.exports = async function makeFont(ctx, config, argv) {
await ctx.run(manip.glyph, "a", sanitizeSymbols, argv.type);
}
removeUnusedFeatures(ctx.items.a);
gc(ctx.items.a);

await ctx.run(build, "a", { to: argv.o, optimize: true });
ctx.remove("a");
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sarasa-gothic",
"version": "0.12.5",
"version": "0.12.6",
"main": "./run",
"dependencies": {
"@chlorophytum/cli": "^0.7.0",
Expand All @@ -11,7 +11,7 @@
"colors": "^1.4.0",
"fs-extra": "^8.1.0",
"megaminx": "^0.9.0",
"otfcc-ttcize": "^0.9.6",
"otfcc-ttcize": "^0.10.0",
"verda": "^1.0.0-10",
"which": "^2.0.2",
"yargs": "^15.3.0"
Expand Down
Loading

0 comments on commit f5a1bf7

Please sign in to comment.