Iosevka/utility/generate-ttfa-ranges/index.mjs
2023-10-18 19:58:33 -07:00

267 lines
5.9 KiB
JavaScript

import fs from "fs";
import path from "path";
import url from "url";
import { ArrayUtil } from "../../font-src/support/utils.mjs";
const __dirname = url.fileURLToPath(new URL(".", import.meta.url));
setImmediate(() => {
main().catch(e => {
console.error(e);
process.exit(1);
});
});
///////////////////////////////////////////////////////////////////////////////////////////////////
async function main() {
for (const target of Targets) {
await target.filter.load();
}
const results = new Map();
nextChar: for (let lch = 0; lch < 0x20000; lch++) {
for (const target of Targets) {
if (target.filter.has(lch)) {
const resultKey = `${target.scriptTag}-${target.featureTag}`;
let result = results.get(resultKey);
if (!result) {
result = [];
results.set(resultKey, result);
}
result.push(lch);
continue nextChar;
}
}
}
let out = [];
for (const [key, value] of results) {
out.push({
scriptTag: key.split("-")[0],
featureTag: key.split("-")[1],
ranges: ArrayUtil.toRanges(value)
});
}
await fs.promises.writeFile(
path.resolve(__dirname, "../../font-src/generated/ttfa-ranges.mjs"),
`/* eslint-disable */\n` +
`// Machine generated. Do not modify。\n` +
`export default ` +
JSON.stringify(out, null, "\t") +
";\n"
);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
class InUnicodeDataSet {
constructor(subpath) {
this.subpath = subpath;
this.dataset = null;
}
async load() {
if (this.dataset) return;
const d = (await import(`@unicode/unicode-15.0.0/${this.subpath}/code-points.js`)).default;
this.dataset = new Set(d);
}
has(lch) {
return this.dataset.has(lch);
}
}
class InScriptDataSet extends InUnicodeDataSet {
constructor(script) {
super(`Script/${script}`);
}
}
class InBlockDataSet extends InUnicodeDataSet {
constructor(block) {
super(`Block/${block}`);
}
}
class InGeneralCategoryDataSet extends InUnicodeDataSet {
constructor(general_category) {
super(`General_Category/${general_category}`);
}
}
class InString {
constructor(s) {
this.s = s;
this.dataset = null;
}
async load() {
if (this.dataset) return;
this.dataset = new Set(this.s);
}
has(lch) {
return this.dataset.has(String.fromCodePoint(lch));
}
}
class Negation {
constructor(operand) {
this.operand = operand;
}
async load() {
await this.operand.load();
}
has(lch) {
return !this.operand.has(lch);
}
}
class Conjunct {
constructor(operands) {
this.operands = operands;
}
async load() {
for (const operand of this.operands) await operand.load();
}
has(lch) {
for (const operand of this.operands) if (!operand.has(lch)) return false;
return true;
}
}
class Disjunct {
constructor(operands) {
this.operands = operands;
}
async load() {
for (const operand of this.operands) await operand.load();
}
has(lch) {
for (const operand of this.operands) if (operand.has(lch)) return true;
return false;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
const Script = s => new InScriptDataSet(s);
const Block = b => new InBlockDataSet(b);
const GeneralCategory = gc => new InGeneralCategoryDataSet(gc);
const In = s => new InString(s);
const All = (...operands) => new Conjunct(operands);
const Either = (...operands) => new Disjunct(operands);
const Not = operand => new Negation(operand);
///////////////////////////////////////////////////////////////////////////////////////////////////
const LatinBase = {
scriptTag: "latn",
featureTag: "dflt",
filter: Either(
All(
Script("Latin"),
Either(GeneralCategory("Uppercase_Letter"), GeneralCategory("Lowercase_Letter")),
Not(Block("Halfwidth_And_Fullwidth_Forms"))
),
Block("Currency_Symbols"),
All(Block("Letterlike_Symbols"), Not(In("℀℁⅍℠℡™℻")))
)
};
const CyrillicBase = {
scriptTag: "cyrl",
featureTag: "dflt",
filter: All(
Script("Cyrillic"),
Either(GeneralCategory("Uppercase_Letter"), GeneralCategory("Lowercase_Letter"))
)
};
const GreekBase = {
scriptTag: "grek",
featureTag: "dflt",
filter: All(
Script("Greek"),
Either(GeneralCategory("Uppercase_Letter"), GeneralCategory("Lowercase_Letter"))
)
};
const DigitBase = {
scriptTag: "latn",
featureTag: "dflt",
filter: Either(In("0123456789"))
};
const LatinSubscript = {
scriptTag: "latb",
featureTag: "dflt",
filter: Either(In("ₐₑₔₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓ"))
};
const GreekSubscript = {
scriptTag: "latb",
featureTag: "dflt",
filter: Either(In("ᵦᵧᵨᵩᵪ"))
};
const CyrillicSubscript = {
scriptTag: "latb",
featureTag: "dflt",
filter: Either(In("𞁑𞁒𞁓𞁔𞁧𞁕𞁖𞁗𞁘𞁩𞁙𞁨𞁚𞁛𞁜𞁝𞁞𞁟𞁠𞁡𞁢𞁣𞁪𞁤𞁥𞁦"))
};
const DigitSubscript = {
scriptTag: "latb",
featureTag: "dflt",
filter: Either(In("₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎"))
};
const LatinSuperscript = {
scriptTag: "latp",
featureTag: "dflt",
filter: Either(In("ªº"), All(Script("Latin"), Either(GeneralCategory("Modifier_Letter"))))
};
const GreekSuperscript = {
scriptTag: "latp",
featureTag: "dflt",
filter: All(Script("Greek"), Either(GeneralCategory("Modifier_Letter")))
};
const CyrillicSuperscript = {
scriptTag: "latp",
featureTag: "dflt",
filter: All(Script("Cyrillic"), Either(GeneralCategory("Modifier_Letter")))
};
const DigitSuperscript = {
scriptTag: "latp",
featureTag: "dflt",
filter: Either(In("⁰¹²³⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾"))
};
const LatinPunctuation = {
scriptTag: "latn",
featureTag: "dflt",
filter: Either(
Block("Basic_Latin"),
Block("Latin_1_Supplement"),
Block("General_Punctuation"),
Block("Supplemental_Punctuation")
)
};
const Targets = [
LatinBase,
LatinSubscript,
LatinSuperscript,
GreekBase,
GreekSubscript,
GreekSuperscript,
CyrillicBase,
CyrillicSubscript,
CyrillicSuperscript,
DigitBase,
DigitSubscript,
DigitSuperscript,
LatinPunctuation
];