Skip to content

Commit f702ea5

Browse files
committed
Descriptions of stylistic sets are added to font files (#2664).
1 parent f74cbae commit f702ea5

File tree

9 files changed

+132
-64
lines changed

9 files changed

+132
-64
lines changed

changes/32.5.0.md

+1
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@
2525
- ENTER SYMBOL (`U+2386`).
2626
- ALTERNATIVE KEY SYMBOL (`U+2387`).
2727
- SQUARE POSITION INDICATOR (`U+2BD0`).
28+
* Descriptions of stylistic sets are added to font files (#2664).

packages/font/src/build-font/index.mjs

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { CreateEmptyFont } from "../font-io/index.mjs";
77
import { buildCompatLigatures } from "../hb-compat-ligature/index.mjs";
88
import { assignFontNames } from "../naming/index.mjs";
99
import { convertOtd } from "../otd-conv/index.mjs";
10+
import { postProcessFont } from "../post-processing/index.mjs";
1011
import { generateTtfaControls } from "../ttfa-controls/index.mjs";
1112
import { validateFontConfigMono } from "../validate/metrics.mjs";
1213

@@ -34,6 +35,8 @@ export async function buildFont(para, cache) {
3435
const font = await convertOtd(baseFont, otl, cleanGs);
3536
// Build compatibility ligatures
3637
if (para.compatibilityLigatures) await buildCompatLigatures(para, font);
38+
// Apply post processing
39+
postProcessFont(para, font);
3740
// Generate ttfaControls
3841
const ttfaControls = await generateTtfaControls(cleanGs, font.glyphs);
3942

packages/font/src/derive-spacing.mjs

+12
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,24 @@ import { CliProc, Ot } from "ot-builder";
66

77
import { readTTF, saveTTF } from "./font-io/index.mjs";
88
import { assignFontNames, createNamingDictFromArgv } from "./naming/index.mjs";
9+
import { getParametersT } from "./param/index.mjs";
10+
import { postProcessFont } from "./post-processing/index.mjs";
911
import { validateFontConfigMono } from "./validate/metrics.mjs";
1012

1113
export default main;
1214
async function main(argv) {
15+
// Set up parameters
16+
const paraT = await getParametersT(argv);
17+
const para = paraT(argv);
18+
19+
// Read in font
1320
const font = await readTTF(argv.i);
1421

22+
// Assign font names
1523
const naming = createNamingDictFromArgv(argv);
1624
assignFontNames(font, naming, false);
1725

26+
// Derive spacing
1827
switch (argv.shape.spacing) {
1928
case "term":
2029
await deriveTerm(font);
@@ -31,12 +40,15 @@ async function main(argv) {
3140
break;
3241
}
3342

43+
// Save no-GC result
3444
await saveTTF(argv.oNoGc, font);
3545

46+
// GC and save
3647
switch (argv.shape.spacing) {
3748
case "fontconfig-mono":
3849
case "fixed":
3950
CliProc.gcFont(font, Ot.ListGlyphStoreFactory);
51+
postProcessFont(para, font);
4052
validateFontConfigMono(font);
4153
await saveTTF(argv.o, font);
4254
break;

packages/font/src/index.mjs

+1-62
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
11
import fs from "fs";
2-
import path from "path";
32
import zlib from "zlib";
43

5-
import * as Toml from "@iarna/toml";
64
import * as Caching from "@iosevka/geometry-cache";
75
import { createGrDisplaySheet } from "@iosevka/glyph/relation";
8-
import * as Parameters from "@iosevka/param";
9-
import { applyLigationData } from "@iosevka/param/ligation";
10-
import { applyMetricOverride } from "@iosevka/param/metric-override";
11-
import * as VariantData from "@iosevka/param/variant";
126
import { encode } from "@msgpack/msgpack";
137

148
import { buildFont } from "./build-font/index.mjs";
159
import { saveTTF } from "./font-io/index.mjs";
16-
import { createNamingDictFromArgv } from "./naming/index.mjs";
10+
import { getParametersT } from "./param/index.mjs";
1711

1812
export default main;
1913
async function main(argv) {
@@ -42,61 +36,6 @@ async function main(argv) {
4236
return { cacheUpdated };
4337
}
4438

45-
///////////////////////////////////////////////////////////////////////////////////////////////////
46-
47-
// Parameter preparation
48-
async function getParametersT(argv) {
49-
const PARAMETERS_TOML = path.resolve(argv.paramsDir, "./parameters.toml");
50-
const WEIGHTS_TOML = path.resolve(argv.paramsDir, "./shape-weight.toml");
51-
const WIDTHS_TOML = path.resolve(argv.paramsDir, "./shape-width.toml");
52-
const SLOPES_TOML = path.resolve(argv.paramsDir, "./shape-slope.toml");
53-
const PRIVATE_TOML = path.resolve(argv.paramsDir, "./private-parameters.toml");
54-
const VARIANTS_TOML = path.resolve(argv.paramsDir, "./variants.toml");
55-
const LIGATIONS_TOML = path.resolve(argv.paramsDir, "./ligation-set.toml");
56-
const parametersData = Object.assign(
57-
{},
58-
await tryParseToml(PARAMETERS_TOML),
59-
await tryParseToml(WEIGHTS_TOML),
60-
await tryParseToml(WIDTHS_TOML),
61-
await tryParseToml(SLOPES_TOML),
62-
fs.existsSync(PRIVATE_TOML) ? await tryParseToml(PRIVATE_TOML) : {},
63-
);
64-
const rawVariantsData = await tryParseToml(VARIANTS_TOML);
65-
const rawLigationData = await tryParseToml(LIGATIONS_TOML);
66-
function createParaImpl(argv) {
67-
let para = Parameters.init(deepClone(parametersData), argv);
68-
VariantData.apply(deepClone(rawVariantsData), para, argv);
69-
applyLigationData(deepClone(rawLigationData), para, argv);
70-
if (argv.excludedCharRanges) para.excludedCharRanges = argv.excludedCharRanges;
71-
if (argv.compatibilityLigatures) para.compatibilityLigatures = argv.compatibilityLigatures;
72-
if (argv.metricOverride) applyMetricOverride(para, argv.metricOverride, argv);
73-
para.naming = { ...para.naming, ...createNamingDictFromArgv(argv) };
74-
return para;
75-
}
76-
function paraT(argv) {
77-
const para = createParaImpl(argv);
78-
para.createFork = function (tf) {
79-
const argv1 = deepClone(argv);
80-
tf(argv1, argv);
81-
return paraT(argv1);
82-
};
83-
return para;
84-
}
85-
return paraT;
86-
}
87-
async function tryParseToml(str) {
88-
try {
89-
return Toml.parse(await fs.promises.readFile(str, "utf-8"));
90-
} catch (e) {
91-
throw new Error(
92-
`Failed to parse configuration file ${str}.\nPlease validate whether there's syntax error.\n${e}`,
93-
);
94-
}
95-
}
96-
function deepClone(pod) {
97-
return JSON.parse(JSON.stringify(pod));
98-
}
99-
10039
// Save character map file
10140
async function saveCharMap(argv, glyphStore) {
10241
let charMap = [];

packages/font/src/naming/index.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ function getStyleLinkedStyles(menuNameMap, weight, width, slope) {
212212
};
213213
}
214214

215-
function nameFont(font, nameID, str) {
215+
export function nameFont(font, nameID, str) {
216216
nameFontImpl(font.name.records, 1, 0, 0, nameID, Buffer.from(str, "utf-8")); // Mac Roman
217217
nameFontImpl(font.name.records, 3, 1, 1033, nameID, str); // Windows Unicode English
218218
}

packages/font/src/otd-conv/index.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { CliProc } from "ot-builder";
22

33
import { convertGlyphs } from "./glyphs.mjs";
4-
import { convertGsub, convertGpos, convertGdef } from "./layout.mjs";
4+
import { convertGdef, convertGpos, convertGsub } from "./layout.mjs";
55

66
export function convertOtd(baseFont, otl, gs) {
77
const { glyphs, cmap } = convertGlyphs(gs);

packages/font/src/param/index.mjs

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import fs from "fs";
2+
import path from "path";
3+
4+
import * as Toml from "@iarna/toml";
5+
import * as Parameters from "@iosevka/param";
6+
import { applyLigationData } from "@iosevka/param/ligation";
7+
import { applyMetricOverride } from "@iosevka/param/metric-override";
8+
import * as VariantData from "@iosevka/param/variant";
9+
10+
import { createNamingDictFromArgv } from "../naming/index.mjs";
11+
12+
export async function getParametersT(argv) {
13+
const PARAMETERS_TOML = path.resolve(argv.paramsDir, "./parameters.toml");
14+
const WEIGHTS_TOML = path.resolve(argv.paramsDir, "./shape-weight.toml");
15+
const WIDTHS_TOML = path.resolve(argv.paramsDir, "./shape-width.toml");
16+
const SLOPES_TOML = path.resolve(argv.paramsDir, "./shape-slope.toml");
17+
const PRIVATE_TOML = path.resolve(argv.paramsDir, "./private-parameters.toml");
18+
const VARIANTS_TOML = path.resolve(argv.paramsDir, "./variants.toml");
19+
const LIGATIONS_TOML = path.resolve(argv.paramsDir, "./ligation-set.toml");
20+
const parametersData = Object.assign(
21+
{},
22+
await tryParseToml(PARAMETERS_TOML),
23+
await tryParseToml(WEIGHTS_TOML),
24+
await tryParseToml(WIDTHS_TOML),
25+
await tryParseToml(SLOPES_TOML),
26+
fs.existsSync(PRIVATE_TOML) ? await tryParseToml(PRIVATE_TOML) : {},
27+
);
28+
const rawVariantsData = await tryParseToml(VARIANTS_TOML);
29+
const rawLigationData = await tryParseToml(LIGATIONS_TOML);
30+
function createParaImpl(argv) {
31+
let para = Parameters.init(deepClone(parametersData), argv);
32+
VariantData.apply(deepClone(rawVariantsData), para, argv);
33+
applyLigationData(deepClone(rawLigationData), para, argv);
34+
if (argv.excludedCharRanges) para.excludedCharRanges = argv.excludedCharRanges;
35+
if (argv.compatibilityLigatures) para.compatibilityLigatures = argv.compatibilityLigatures;
36+
if (argv.metricOverride) applyMetricOverride(para, argv.metricOverride, argv);
37+
para.naming = { ...para.naming, ...createNamingDictFromArgv(argv) };
38+
return para;
39+
}
40+
function paraT(argv) {
41+
const para = createParaImpl(argv);
42+
para.createFork = function (tf) {
43+
const argv1 = deepClone(argv);
44+
tf(argv1, argv);
45+
return paraT(argv1);
46+
};
47+
return para;
48+
}
49+
return paraT;
50+
}
51+
async function tryParseToml(str) {
52+
try {
53+
return Toml.parse(await fs.promises.readFile(str, "utf-8"));
54+
} catch (e) {
55+
throw new Error(
56+
`Failed to parse configuration file ${str}.\nPlease validate whether there's syntax error.\n${e}`,
57+
);
58+
}
59+
}
60+
function deepClone(pod) {
61+
return JSON.parse(JSON.stringify(pod));
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { Ot, Sigma } from "ot-builder";
2+
3+
import { nameFont } from "../naming/index.mjs";
4+
5+
export function postProcessingFeatureParams(para, font) {
6+
if (!para.variants.composites) return;
7+
if (!font.gsub) return;
8+
if (!font.name) return;
9+
10+
const nm = new NameManager();
11+
12+
for (const [name, ss] of para.variants.composites) {
13+
if (!ss.description) continue;
14+
for (const feat of font.gsub.features) {
15+
if (feat.tag !== ss.tag) continue;
16+
const nameId = nm.getNameId(ss.description);
17+
feat.params = Sigma.DependentPair.create(Ot.GsubGpos.FeatureParams.TID_StylisticSet, {
18+
uiNameID: nameId,
19+
});
20+
}
21+
}
22+
23+
nm.apply(font);
24+
}
25+
26+
class NameManager {
27+
constructor() {
28+
this.nameId = 257;
29+
this.nameMap = new Map();
30+
}
31+
32+
getNameId(name) {
33+
let nameId = this.nameMap.get(name);
34+
if (!nameId) {
35+
nameId = this.nameId++;
36+
this.nameMap.set(name, nameId);
37+
}
38+
return nameId;
39+
}
40+
41+
apply(font) {
42+
for (const [name, id] of this.nameMap) {
43+
nameFont(font, id, name);
44+
}
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { postProcessingFeatureParams } from "./feature-params.mjs";
2+
3+
export function postProcessFont(para, font) {
4+
postProcessingFeatureParams(para, font);
5+
}

0 commit comments

Comments
 (0)