Generate TTFAutohint control files for better glyph display for variant glyphs (#1963).
This commit is contained in:
parent
6fed1572c1
commit
25ee0bcc50
9 changed files with 932 additions and 31 deletions
|
@ -5,3 +5,4 @@
|
|||
- COMBINING DOUBLE VERTICAL STROKE OVERLAY (`U+20E6`).
|
||||
- COMBINING LONG DOUBLE SOLIDUS OVERLAY (`U+20EB`).
|
||||
* Improve glyphs for COLON SIGN (`U+20A1`), GUARANI SIGN (`U+20B2`), and CEDI SIGN (`U+20B5`).
|
||||
* Generate TTFAutohint control files for better glyph display for variant glyphs (#1963).
|
||||
|
|
|
@ -7,6 +7,7 @@ import { finalizeFont } from "./finalize/index.mjs";
|
|||
import { CreateEmptyFont } from "./meta/empty-font.mjs";
|
||||
import { assignFontNames } from "./meta/naming.mjs";
|
||||
import { convertOtd } from "./otd-conv/index.mjs";
|
||||
import { generateTtfaControls } from "./ttfa-controls/index.mjs";
|
||||
|
||||
export async function buildFont(argv, para) {
|
||||
const baseFont = CreateEmptyFont(argv);
|
||||
|
@ -30,6 +31,7 @@ export async function buildFont(argv, para) {
|
|||
if (cache.isUpdated()) {
|
||||
await Caching.save(argv.oCache, argv.menu.version, cache, true);
|
||||
}
|
||||
const font = convertOtd(baseFont, otl, finalGs);
|
||||
return { font, glyphStore: finalGs, cacheUpdated: cache.isUpdated() };
|
||||
const font = await convertOtd(baseFont, otl, finalGs);
|
||||
const ttfaControls = await generateTtfaControls(finalGs, font.glyphs);
|
||||
return { font, glyphStore: finalGs, cacheUpdated: cache.isUpdated(), ttfaControls };
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Ot } from "ot-builder";
|
|||
import { Point } from "../../support/geometry/point.mjs";
|
||||
import * as Gr from "../../support/gr.mjs";
|
||||
|
||||
import { byCode, bySpacing, byGr, byBuildOrder } from "./glyph-name.mjs";
|
||||
import { byBuildOrder, byCode, byGr, bySpacing } from "./glyph-name.mjs";
|
||||
|
||||
function byRank([gna, a], [gnb, b]) {
|
||||
return (
|
||||
|
@ -28,9 +28,11 @@ class MappedGlyphStore {
|
|||
this.m_primaryUnicodeMapping.set(u, source);
|
||||
}
|
||||
queryBySourceGlyph(source) {
|
||||
if (!source) return undefined;
|
||||
return this.m_mapping.get(source);
|
||||
}
|
||||
queryByName(name) {
|
||||
if (!name) return undefined;
|
||||
return this.m_nameMapping.get(name);
|
||||
}
|
||||
decideOrder() {
|
||||
|
|
56
font-src/gen/ttfa-controls/index.mjs
Normal file
56
font-src/gen/ttfa-controls/index.mjs
Normal file
|
@ -0,0 +1,56 @@
|
|||
import ttfaRanges from "../../generated/ttfa-ranges.mjs";
|
||||
import * as Gr from "../../support/gr.mjs";
|
||||
import { ArrayUtil } from "../../support/utils.mjs";
|
||||
|
||||
export async function generateTtfaControls(gsOrig, gs) {
|
||||
let ttfaControls = [];
|
||||
|
||||
for (const alignment of ttfaRanges) {
|
||||
ttfaControls.push(generateTTFAAlignments(alignment, gsOrig, gs));
|
||||
}
|
||||
|
||||
return ttfaControls;
|
||||
}
|
||||
|
||||
function generateTTFAAlignments(alignment, gsOrig, gsTtf) {
|
||||
let collectedGlyphs = new Map();
|
||||
for (const [lo, hi] of alignment.ranges) {
|
||||
for (let lch = lo; lch <= hi; lch++) {
|
||||
const go = gsOrig.queryByUnicode(lch);
|
||||
if (!go) continue;
|
||||
const gd = gsTtf.queryBySourceGlyph(go);
|
||||
if (!gd) continue;
|
||||
collectedGlyphs.set(go, gd);
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
let sizeBefore = collectedGlyphs.size;
|
||||
|
||||
for (const [go, gd] of collectedGlyphs) {
|
||||
const cvs = Gr.AnyCv.query(go);
|
||||
for (const gr of cvs) {
|
||||
const gnLinked = gr.get(go);
|
||||
if (!gnLinked) continue;
|
||||
const goLinked = gsOrig.queryByName(gnLinked);
|
||||
if (!goLinked) continue;
|
||||
const gdLinked = gsTtf.queryBySourceGlyph(goLinked);
|
||||
if (!gdLinked) continue;
|
||||
collectedGlyphs.set(goLinked, gdLinked);
|
||||
}
|
||||
}
|
||||
|
||||
let sizeAfter = collectedGlyphs.size;
|
||||
if (sizeAfter <= sizeBefore) break;
|
||||
}
|
||||
|
||||
const gOrd = gsTtf.decideOrder();
|
||||
const glyphIndices = Array.from(collectedGlyphs.values()).map(gd => gOrd.reverse(gd));
|
||||
const glyphIndicesRangesStr = ArrayUtil.toRanges(glyphIndices)
|
||||
.map(([lo, hi]) => (lo === hi ? `${lo}` : `${lo}-${hi}`))
|
||||
.join(", ");
|
||||
|
||||
const styleAdjustLine = `${alignment.scriptTag} ${alignment.featureTag} @ ${glyphIndicesRangesStr}`;
|
||||
|
||||
return styleAdjustLine;
|
||||
}
|
514
font-src/generated/ttfa-ranges.mjs
Normal file
514
font-src/generated/ttfa-ranges.mjs
Normal file
|
@ -0,0 +1,514 @@
|
|||
/* eslint-disable */
|
||||
// Machine generated. Do not modify。
|
||||
export default [
|
||||
{
|
||||
"scriptTag": "latn",
|
||||
"featureTag": "dflt",
|
||||
"ranges": [
|
||||
[
|
||||
0,
|
||||
177
|
||||
],
|
||||
[
|
||||
180,
|
||||
184
|
||||
],
|
||||
[
|
||||
186,
|
||||
442
|
||||
],
|
||||
[
|
||||
444,
|
||||
447
|
||||
],
|
||||
[
|
||||
452,
|
||||
452
|
||||
],
|
||||
[
|
||||
454,
|
||||
455
|
||||
],
|
||||
[
|
||||
457,
|
||||
458
|
||||
],
|
||||
[
|
||||
460,
|
||||
497
|
||||
],
|
||||
[
|
||||
499,
|
||||
659
|
||||
],
|
||||
[
|
||||
661,
|
||||
687
|
||||
],
|
||||
[
|
||||
7424,
|
||||
7461
|
||||
],
|
||||
[
|
||||
7531,
|
||||
7543
|
||||
],
|
||||
[
|
||||
7545,
|
||||
7578
|
||||
],
|
||||
[
|
||||
7680,
|
||||
7935
|
||||
],
|
||||
[
|
||||
8192,
|
||||
8303
|
||||
],
|
||||
[
|
||||
8352,
|
||||
8399
|
||||
],
|
||||
[
|
||||
8450,
|
||||
8479
|
||||
],
|
||||
[
|
||||
8483,
|
||||
8506
|
||||
],
|
||||
[
|
||||
8508,
|
||||
8524
|
||||
],
|
||||
[
|
||||
8526,
|
||||
8527
|
||||
],
|
||||
[
|
||||
8579,
|
||||
8580
|
||||
],
|
||||
[
|
||||
11360,
|
||||
11387
|
||||
],
|
||||
[
|
||||
11390,
|
||||
11391
|
||||
],
|
||||
[
|
||||
11776,
|
||||
11903
|
||||
],
|
||||
[
|
||||
42786,
|
||||
42863
|
||||
],
|
||||
[
|
||||
42865,
|
||||
42887
|
||||
],
|
||||
[
|
||||
42891,
|
||||
42894
|
||||
],
|
||||
[
|
||||
42896,
|
||||
42954
|
||||
],
|
||||
[
|
||||
42960,
|
||||
42961
|
||||
],
|
||||
[
|
||||
42963,
|
||||
42963
|
||||
],
|
||||
[
|
||||
42965,
|
||||
42969
|
||||
],
|
||||
[
|
||||
42997,
|
||||
42998
|
||||
],
|
||||
[
|
||||
43002,
|
||||
43002
|
||||
],
|
||||
[
|
||||
43824,
|
||||
43866
|
||||
],
|
||||
[
|
||||
43872,
|
||||
43876
|
||||
],
|
||||
[
|
||||
43878,
|
||||
43880
|
||||
],
|
||||
[
|
||||
64256,
|
||||
64262
|
||||
],
|
||||
[
|
||||
122624,
|
||||
122633
|
||||
],
|
||||
[
|
||||
122635,
|
||||
122654
|
||||
],
|
||||
[
|
||||
122661,
|
||||
122666
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"scriptTag": "latb",
|
||||
"featureTag": "dflt",
|
||||
"ranges": [
|
||||
[
|
||||
178,
|
||||
179
|
||||
],
|
||||
[
|
||||
185,
|
||||
185
|
||||
],
|
||||
[
|
||||
7522,
|
||||
7525
|
||||
],
|
||||
[
|
||||
8304,
|
||||
8304
|
||||
],
|
||||
[
|
||||
8308,
|
||||
8318
|
||||
],
|
||||
[
|
||||
8320,
|
||||
8334
|
||||
],
|
||||
[
|
||||
8336,
|
||||
8348
|
||||
],
|
||||
[
|
||||
11388,
|
||||
11388
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"scriptTag": "latp",
|
||||
"featureTag": "dflt",
|
||||
"ranges": [
|
||||
[
|
||||
688,
|
||||
696
|
||||
],
|
||||
[
|
||||
736,
|
||||
740
|
||||
],
|
||||
[
|
||||
7468,
|
||||
7516
|
||||
],
|
||||
[
|
||||
7579,
|
||||
7614
|
||||
],
|
||||
[
|
||||
8305,
|
||||
8305
|
||||
],
|
||||
[
|
||||
8319,
|
||||
8319
|
||||
],
|
||||
[
|
||||
11389,
|
||||
11389
|
||||
],
|
||||
[
|
||||
42864,
|
||||
42864
|
||||
],
|
||||
[
|
||||
42994,
|
||||
42996
|
||||
],
|
||||
[
|
||||
43000,
|
||||
43001
|
||||
],
|
||||
[
|
||||
43868,
|
||||
43871
|
||||
],
|
||||
[
|
||||
43881,
|
||||
43881
|
||||
],
|
||||
[
|
||||
67456,
|
||||
67461
|
||||
],
|
||||
[
|
||||
67463,
|
||||
67504
|
||||
],
|
||||
[
|
||||
67506,
|
||||
67514
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"scriptTag": "grek",
|
||||
"featureTag": "dflt",
|
||||
"ranges": [
|
||||
[
|
||||
880,
|
||||
883
|
||||
],
|
||||
[
|
||||
886,
|
||||
887
|
||||
],
|
||||
[
|
||||
891,
|
||||
893
|
||||
],
|
||||
[
|
||||
895,
|
||||
895
|
||||
],
|
||||
[
|
||||
902,
|
||||
902
|
||||
],
|
||||
[
|
||||
904,
|
||||
906
|
||||
],
|
||||
[
|
||||
908,
|
||||
908
|
||||
],
|
||||
[
|
||||
910,
|
||||
929
|
||||
],
|
||||
[
|
||||
931,
|
||||
993
|
||||
],
|
||||
[
|
||||
1008,
|
||||
1013
|
||||
],
|
||||
[
|
||||
1015,
|
||||
1023
|
||||
],
|
||||
[
|
||||
7462,
|
||||
7466
|
||||
],
|
||||
[
|
||||
7936,
|
||||
7957
|
||||
],
|
||||
[
|
||||
7960,
|
||||
7965
|
||||
],
|
||||
[
|
||||
7968,
|
||||
8005
|
||||
],
|
||||
[
|
||||
8008,
|
||||
8013
|
||||
],
|
||||
[
|
||||
8016,
|
||||
8023
|
||||
],
|
||||
[
|
||||
8025,
|
||||
8025
|
||||
],
|
||||
[
|
||||
8027,
|
||||
8027
|
||||
],
|
||||
[
|
||||
8029,
|
||||
8029
|
||||
],
|
||||
[
|
||||
8031,
|
||||
8061
|
||||
],
|
||||
[
|
||||
8064,
|
||||
8071
|
||||
],
|
||||
[
|
||||
8080,
|
||||
8087
|
||||
],
|
||||
[
|
||||
8096,
|
||||
8103
|
||||
],
|
||||
[
|
||||
8112,
|
||||
8116
|
||||
],
|
||||
[
|
||||
8118,
|
||||
8123
|
||||
],
|
||||
[
|
||||
8126,
|
||||
8126
|
||||
],
|
||||
[
|
||||
8130,
|
||||
8132
|
||||
],
|
||||
[
|
||||
8134,
|
||||
8139
|
||||
],
|
||||
[
|
||||
8144,
|
||||
8147
|
||||
],
|
||||
[
|
||||
8150,
|
||||
8155
|
||||
],
|
||||
[
|
||||
8160,
|
||||
8172
|
||||
],
|
||||
[
|
||||
8178,
|
||||
8180
|
||||
],
|
||||
[
|
||||
8182,
|
||||
8187
|
||||
],
|
||||
[
|
||||
43877,
|
||||
43877
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"scriptTag": "grek",
|
||||
"featureTag": "sups",
|
||||
"ranges": [
|
||||
[
|
||||
890,
|
||||
890
|
||||
],
|
||||
[
|
||||
7517,
|
||||
7521
|
||||
],
|
||||
[
|
||||
7615,
|
||||
7615
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"scriptTag": "cyrl",
|
||||
"featureTag": "dflt",
|
||||
"ranges": [
|
||||
[
|
||||
1024,
|
||||
1153
|
||||
],
|
||||
[
|
||||
1162,
|
||||
1327
|
||||
],
|
||||
[
|
||||
7296,
|
||||
7304
|
||||
],
|
||||
[
|
||||
7467,
|
||||
7467
|
||||
],
|
||||
[
|
||||
42560,
|
||||
42605
|
||||
],
|
||||
[
|
||||
42624,
|
||||
42651
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"scriptTag": "grek",
|
||||
"featureTag": "subs",
|
||||
"ranges": [
|
||||
[
|
||||
7526,
|
||||
7530
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"scriptTag": "cyrl",
|
||||
"featureTag": "sups",
|
||||
"ranges": [
|
||||
[
|
||||
7544,
|
||||
7544
|
||||
],
|
||||
[
|
||||
42623,
|
||||
42623
|
||||
],
|
||||
[
|
||||
42652,
|
||||
42653
|
||||
],
|
||||
[
|
||||
122928,
|
||||
122960
|
||||
],
|
||||
[
|
||||
122987,
|
||||
122989
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"scriptTag": "cyrl",
|
||||
"featureTag": "subs",
|
||||
"ranges": [
|
||||
[
|
||||
122961,
|
||||
122986
|
||||
]
|
||||
]
|
||||
}
|
||||
];
|
|
@ -22,10 +22,13 @@ export default main;
|
|||
async function main(argv) {
|
||||
const paraT = await getParameters();
|
||||
const para = paraT(argv);
|
||||
const { font, glyphStore, cacheUpdated } = await buildFont(argv, para);
|
||||
const { font, glyphStore, cacheUpdated, ttfaControls } = await buildFont(argv, para);
|
||||
if (argv.oCharMap) {
|
||||
await saveCharMap(argv, glyphStore);
|
||||
}
|
||||
if (argv.oTtfaControls) {
|
||||
await fs.promises.writeFile(argv.oTtfaControls, ttfaControls.join("\n") + "\n");
|
||||
}
|
||||
if (argv.o) {
|
||||
if (para.compatibilityLigatures) await buildCompatLigatures(para, font);
|
||||
await saveTTF(argv.o, font);
|
||||
|
|
|
@ -114,5 +114,27 @@ export const ArrayUtil = {
|
|||
},
|
||||
insertSliceAt(a, i, b) {
|
||||
a.splice(i, 0, ...b);
|
||||
},
|
||||
// Convert character array to array of ranges. Input may be unsorted.
|
||||
// The output ranges has both ends inclusive.
|
||||
toRanges(chars) {
|
||||
chars.sort((a, b) => a - b);
|
||||
|
||||
const ranges = [];
|
||||
let range = null;
|
||||
|
||||
for (const ch of chars) {
|
||||
if (!range) {
|
||||
range = [ch, ch];
|
||||
ranges.push(range);
|
||||
} else if (ch === range[1] + 1) {
|
||||
range[1] = ch;
|
||||
} else {
|
||||
range = [ch, ch];
|
||||
ranges.push(range);
|
||||
}
|
||||
}
|
||||
|
||||
return ranges;
|
||||
}
|
||||
};
|
||||
|
|
264
utility/generate-ttfa-ranges.mjs
Normal file
264
utility/generate-ttfa-ranges.mjs
Normal file
|
@ -0,0 +1,264 @@
|
|||
import fs from "fs";
|
||||
|
||||
import { ArrayUtil } from "../font-src/support/utils.mjs";
|
||||
|
||||
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(
|
||||
"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 LatinSubscript = {
|
||||
scriptTag: "latb",
|
||||
featureTag: "dflt",
|
||||
filter: Either(In("ₐₑₔₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓ"))
|
||||
};
|
||||
const GreekSubscript = {
|
||||
scriptTag: "grek",
|
||||
featureTag: "subs",
|
||||
filter: Either(In("ᵦᵧᵨᵩᵪ"))
|
||||
};
|
||||
const CyrillicSubscript = {
|
||||
scriptTag: "cyrl",
|
||||
featureTag: "subs",
|
||||
filter: Either(In("𞁑𞁒𞁓𞁔𞁧𞁕𞁖𞁗𞁘𞁩𞁙𞁨𞁚𞁛𞁜𞁝𞁞𞁟𞁠𞁡𞁢𞁣𞁪𞁤𞁥𞁦"))
|
||||
};
|
||||
|
||||
const LatinSuperscript = {
|
||||
scriptTag: "latp",
|
||||
featureTag: "dflt",
|
||||
filter: Either(All(Script("Latin"), Either(GeneralCategory("Modifier_Letter"))))
|
||||
};
|
||||
const GreekSuperscript = {
|
||||
scriptTag: "grek",
|
||||
featureTag: "sups",
|
||||
filter: All(Script("Greek"), Either(GeneralCategory("Modifier_Letter")))
|
||||
};
|
||||
const CyrillicSuperscript = {
|
||||
scriptTag: "cyrl",
|
||||
featureTag: "sups",
|
||||
filter: All(Script("Cyrillic"), Either(GeneralCategory("Modifier_Letter")))
|
||||
};
|
||||
|
||||
const DigitBase = {
|
||||
scriptTag: "latn",
|
||||
featureTag: "dflt",
|
||||
filter: Either(In("0123456789"))
|
||||
};
|
||||
const DigitSubscript = {
|
||||
scriptTag: "latb",
|
||||
featureTag: "dflt",
|
||||
filter: Either(In("₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎"))
|
||||
};
|
||||
const DigitSuperscript = {
|
||||
scriptTag: "latb",
|
||||
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
|
||||
];
|
|
@ -379,22 +379,25 @@ const DistUnhintedTTF = file.make(
|
|||
|
||||
const charMapDir = `${BUILD}/ttf/${gr}`;
|
||||
const charMapPath = `${charMapDir}/${fn}.charmap.mpz`;
|
||||
const noGcTtfPath = `${charMapDir}/${fn}.no-gc.ttf`;
|
||||
const ttfaControlsPath = `${charMapDir}/${fn}.ttfa.txt`;
|
||||
|
||||
if (fi.spacingDerive) {
|
||||
// The font is a spacing variant, and is derivable form an existing
|
||||
// normally-spaced variant.
|
||||
|
||||
const noGcTtfPath = `${charMapDir}/${fn}.no-gc.unhinted.ttf`;
|
||||
|
||||
const spD = fi.spacingDerive;
|
||||
const [deriveFrom] = await target.need(
|
||||
DistUnhintedTTF(spD.prefix, spD.fileName),
|
||||
de(charMapDir)
|
||||
);
|
||||
|
||||
echo.action(echo.hl.command(`Create TTF`), out.full);
|
||||
echo.action(echo.hl.command(`Hint TTF`), out.full);
|
||||
await silently.node(`font-src/derive-spacing.mjs`, {
|
||||
i: deriveFrom.full,
|
||||
oNoGc: noGcTtfPath,
|
||||
o: out.full,
|
||||
oNoGc: noGcTtfPath,
|
||||
...fi
|
||||
});
|
||||
} else {
|
||||
|
@ -415,6 +418,7 @@ const DistUnhintedTTF = file.make(
|
|||
const { cacheUpdated } = await silently.node("font-src/index.mjs", {
|
||||
o: out.full,
|
||||
oCharMap: charMapPath,
|
||||
oTtfaControls: ttfaControlsPath,
|
||||
cacheFreshAgeKey: ageKey,
|
||||
iCache: cachePath,
|
||||
oCache: cacheDiffPath,
|
||||
|
@ -437,13 +441,66 @@ const DistUnhintedTTF = file.make(
|
|||
}
|
||||
);
|
||||
|
||||
const BuildNoGcTtfImpl = file.make(
|
||||
(gr, f) => `${BUILD}/ttf/${gr}/${f}.no-gc.ttf`,
|
||||
const BuildCM = file.make(
|
||||
(gr, f) => `${BUILD}/ttf/${gr}/${f}.charmap.mpz`,
|
||||
async (target, output, gr, f) => {
|
||||
await target.need(DistUnhintedTTF(gr, f));
|
||||
}
|
||||
);
|
||||
|
||||
const BuildTtfaControls = file.make(
|
||||
(gr, f) => `${BUILD}/ttf/${gr}/${f}.ttfa.txt`,
|
||||
async (target, output, gr, f) => {
|
||||
await target.need(DistUnhintedTTF(gr, f));
|
||||
}
|
||||
);
|
||||
|
||||
const DistHintedTTF = file.make(
|
||||
(gr, fn) => `${DIST}/${gr}/ttf/${fn}.ttf`,
|
||||
async (target, out, gr, fn) => {
|
||||
const [fi, hint] = await target.need(
|
||||
FontInfoOf(fn),
|
||||
CheckTtfAutoHintExists,
|
||||
de`${out.dir}`
|
||||
);
|
||||
if (fi.spacingDerive) {
|
||||
// The font is a spacing variant, and is derivable form an existing
|
||||
// normally-spaced variant.
|
||||
|
||||
const spD = fi.spacingDerive;
|
||||
const charMapDir = `${BUILD}/ttf/${gr}`;
|
||||
const noGcTtfPath = `${charMapDir}/${fn}.no-gc.hinted.ttf`;
|
||||
|
||||
const [deriveFrom] = await target.need(
|
||||
DistHintedTTF(spD.prefix, spD.fileName),
|
||||
de(charMapDir)
|
||||
);
|
||||
|
||||
echo.action(echo.hl.command(`Create TTF`), out.full);
|
||||
await silently.node(`font-src/derive-spacing.mjs`, {
|
||||
i: deriveFrom.full,
|
||||
oNoGc: noGcTtfPath,
|
||||
o: out.full,
|
||||
...fi
|
||||
});
|
||||
} else {
|
||||
const [from, ttfaControls] = await target.need(
|
||||
DistUnhintedTTF(gr, fn),
|
||||
BuildTtfaControls(gr, fn)
|
||||
);
|
||||
echo.action(echo.hl.command(`Hint TTF`), out.full, echo.hl.operator("<-"), from.full);
|
||||
await silently.run(hint, fi.hintParams, "-m", ttfaControls.full, from.full, out.full);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const BuildNoGcTtfImpl = file.make(
|
||||
(gr, f) => `${BUILD}/ttf/${gr}/${f}.no-gc.hinted.ttf`,
|
||||
async (target, output, gr, f) => {
|
||||
await target.need(DistHintedTTF(gr, f));
|
||||
}
|
||||
);
|
||||
|
||||
const BuildNoGcTtf = task.make(
|
||||
(gr, fn) => `BuildNoGcTtf::${gr}/${fn}`,
|
||||
async (target, gr, fn) => {
|
||||
|
@ -452,29 +509,12 @@ const BuildNoGcTtf = task.make(
|
|||
const [noGc] = await target.need(BuildNoGcTtfImpl(gr, fn));
|
||||
return noGc;
|
||||
} else {
|
||||
const [distUnhinted] = await target.need(DistUnhintedTTF(gr, fn));
|
||||
const [distUnhinted] = await target.need(DistHintedTTF(gr, fn));
|
||||
return distUnhinted;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const BuildCM = file.make(
|
||||
(gr, f) => `${BUILD}/ttf/${gr}/${f}.charmap.mpz`,
|
||||
async (target, output, gr, f) => {
|
||||
await target.need(DistUnhintedTTF(gr, f));
|
||||
}
|
||||
);
|
||||
|
||||
const DistHintedTTF = file.make(
|
||||
(gr, fn) => `${DIST}/${gr}/ttf/${fn}.ttf`,
|
||||
async (target, out, gr, fn) => {
|
||||
const [fi, hint] = await target.need(FontInfoOf(fn), CheckTtfAutoHintExists);
|
||||
const [from] = await target.need(DistUnhintedTTF(gr, fn), de`${out.dir}`);
|
||||
echo.action(echo.hl.command(`Hint TTF`), out.full, echo.hl.operator("<-"), from.full);
|
||||
await silently.run(hint, fi.hintParams, from.full, out.full);
|
||||
}
|
||||
);
|
||||
|
||||
function formatSuffix(fmt, unhinted) {
|
||||
return fmt + (unhinted ? "-unhinted" : "");
|
||||
}
|
||||
|
@ -712,12 +752,9 @@ async function buildCompositeTtc(out, inputs) {
|
|||
async function buildGlyphSharingTtc(target, parts, out) {
|
||||
await target.need(de`${out.dir}`);
|
||||
const [ttfInputs] = await target.need(parts.map(part => BuildNoGcTtf(part.dir, part.file)));
|
||||
const tmpTtc = `${out.dir}/${out.name}.unhinted.ttc`;
|
||||
const ttfInputPaths = ttfInputs.map(p => p.full);
|
||||
echo.action(echo.hl.command(`Create TTC`), out.full, echo.hl.operator("<-"), ttfInputPaths);
|
||||
await silently.run(TTCIZE, "-u", ["-o", tmpTtc], ttfInputPaths);
|
||||
await silently.run("ttfautohint", tmpTtc, out.full);
|
||||
await rm(tmpTtc);
|
||||
await silently.run(TTCIZE, "-u", ["-o", out.full], ttfInputPaths);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue