Iosevka/font-src/support/variant-data.js
be5invis 4b6dd7376d * Variant tags are reordered.
* Variant names of certain letters are renamed, including:
   - Upper Gamma (`grek-upper-gamma`).
   - Variants for `K`, `k`, Cyrillic Ka (`К` and `к`).
 * Add more serifed variants for `K`, `k`, `n`, Cyrillic Ka (`К` and `к`) and Cyrillic Ef (`ф`) to better reproduce PT Mono (#986).
2021-05-10 19:48:49 -07:00

191 lines
5.4 KiB
JavaScript

"use strict";
exports.apply = applyVariantData;
function applyVariantData(data, para, argv) {
const parsed = parseVariantsData(data);
let tagSet = new Set();
for (const prime of parsed.primes.values()) {
if (!prime.tag) continue;
if (!tagSet.has(prime.tag)) tagSet.add(prime.tag);
else throw new Error(`CV tag conflict: ${prime.tag}`);
}
const variantSelector = {};
parsed.defaultComposite.resolve(para, parsed.selectorTree, parsed.composites, variantSelector);
if (argv.shape.serifs === "slab") {
const slabComp = parsed.composites.get("slab");
slabComp.resolve(para, parsed.selectorTree, parsed.composites, variantSelector);
}
if (argv.variants) {
const userComposite = new Composite("{user}", argv.variants);
userComposite.resolve(para, parsed.selectorTree, parsed.composites, variantSelector);
}
para.variants = {
selectorTree: parsed.selectorTree,
primes: parsed.primes,
composites: parsed.composites
};
para.variantSelector = variantSelector;
}
exports.parse = parseVariantsData;
function parseVariantsData(data) {
const primes = new Map();
const selectorTree = new SelectorTree();
for (const k in data.prime) {
const p = new Prime(k, data.prime[k]);
p.register(selectorTree);
primes.set(k, p);
}
const defaultComposite = new Composite("{default}", data.default);
const composites = new Map();
for (const k in data.composite) {
const comp = new Composite(k, data.composite[k]);
composites.set(k, comp);
}
return { selectorTree: selectorTree, primes, composites, defaultComposite };
}
class SelectorTree {
constructor() {
this.m_mapping = new Map();
}
get(kPrime, kVariant) {
if (!this.m_mapping.has(kPrime)) return undefined;
return this.m_mapping.get(kPrime).get(kVariant);
}
set(kPrime, kVariant, prime, variant) {
if (!this.m_mapping.has(kPrime)) this.m_mapping.set(kPrime, new Map());
this.m_mapping.get(kPrime).set(kVariant, [prime, variant]);
}
*[Symbol.iterator]() {
for (const m of this.m_mapping.values()) yield* m.values();
}
}
class Prime {
constructor(key, cfg) {
if (!cfg.variants) throw new Error(`Missing variants in ${key}`);
this.key = key;
this.sampler = cfg.sampler;
this.ligatureSampler = / /.test(cfg.sampler || "");
this.descSampleText = this.ligatureSampler
? cfg.sampler.split(" ")
: [...(cfg.sampler || "")];
this.samplerExplain = cfg.samplerExplain;
this.tag = cfg.tag;
this.slopeDependent = !!cfg.slopeDependent;
this.variants = new Map();
for (const varKey in cfg.variants) {
const variant = cfg.variants[varKey];
this.variants.set(varKey, new PrimeVariant(varKey, cfg.tag, variant));
}
}
register(tree) {
for (const [k, v] of this.variants) tree.set(this.key, k, this, v);
if (this.tag) {
for (const v of this.variants.values()) if (v.rank) tree.set(this.tag, v.rank, this, v);
}
}
toJson() {
const gr = {
key: this.key,
sampler: this.sampler,
samplerExplain: this.samplerExplain,
tag: this.tag,
slopeDependent: this.slopeDependent,
ligatureSampler: this.ligatureSampler,
descSampleText: this.descSampleText,
variants: []
};
for (const variant of this.variants.values()) {
gr.variants.push({
key: variant.key,
rank: variant.rank,
rankGroup: variant.rankGroup,
description: variant.description
});
}
gr.variants.sort((a, b) => (a.rank || 0x7fffffff) - (b.rank || 0x7fffffff));
return gr;
}
}
class PrimeVariant {
constructor(key, tag, cfg) {
this.key = key;
this.tag = tag;
this.description = cfg.description;
this.rank = cfg.rank;
this.rankGroup = cfg.rankGroup || 0;
this.selector = cfg.selector;
this.nonDeriving = cfg.nonDeriving;
}
resolveFor(para, gn) {
let vs = {};
this.resolve(para, vs);
return vs[gn];
}
resolve(para, vs) {
Object.assign(vs, this.selector);
}
}
class Composite {
constructor(key, cfg) {
this.key = key;
this.tag = cfg.tag;
this.description = cfg.description;
this.inherits = cfg.inherits;
this.design = cfg.design;
this.upright = cfg.upright || cfg["upright-oblique"];
this.oblique = cfg.oblique || cfg["upright-oblique"];
this.italic = cfg.italic;
const slabOverrideCfg = cfg["slab-override"] || {};
this.slabOverride = {
design: slabOverrideCfg.design,
override: slabOverrideCfg.upright || slabOverrideCfg["upright-oblique"],
oblique: slabOverrideCfg.oblique || slabOverrideCfg["upright-oblique"],
italic: slabOverrideCfg.italic
};
}
decompose(para, selTree) {
const ans = [];
const cfg = Object.assign(
{},
this.decomposeSlabOverride(this.design, this.slabOverride.design, para),
para.isItalic
? this.decomposeSlabOverride(this.italic, this.slabOverride.italic, para)
: para.isOblique
? this.decomposeSlabOverride(this.oblique, this.slabOverride.oblique, para)
: this.decomposeSlabOverride(this.upright, this.slabOverride.upright, para)
);
for (const [k, v] of Object.entries(cfg)) {
const pv = selTree.get(k, v);
if (!pv) throw new Error(`Composite ${this.key} cannot be resolved: ${[k, v]}.`);
ans.push(pv);
}
return ans;
}
decomposeSlabOverride(d, sd, para) {
if (para.slab) return Object.assign({}, d, sd);
else return d;
}
resolve(para, selTree, catalog, vs) {
if (this.inherits) {
if (!catalog.has(this.inherits)) {
throw new Error(`Cannot find composite variant: ${this.inherits}`);
}
catalog.get(this.inherits).resolve(para, selTree, catalog, vs);
}
for (const [prime, variant] of this.decompose(para, selTree)) {
variant.resolve(para, vs);
}
}
}