Skip to content

Commit

Permalink
Descriptions of stylistic sets are added to font files (#2664).
Browse files Browse the repository at this point in the history
  • Loading branch information
be5invis committed Jan 29, 2025
1 parent f74cbae commit f702ea5
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 64 deletions.
1 change: 1 addition & 0 deletions changes/32.5.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
- ENTER SYMBOL (`U+2386`).
- ALTERNATIVE KEY SYMBOL (`U+2387`).
- SQUARE POSITION INDICATOR (`U+2BD0`).
* Descriptions of stylistic sets are added to font files (#2664).
3 changes: 3 additions & 0 deletions packages/font/src/build-font/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { CreateEmptyFont } from "../font-io/index.mjs";
import { buildCompatLigatures } from "../hb-compat-ligature/index.mjs";
import { assignFontNames } from "../naming/index.mjs";
import { convertOtd } from "../otd-conv/index.mjs";
import { postProcessFont } from "../post-processing/index.mjs";
import { generateTtfaControls } from "../ttfa-controls/index.mjs";
import { validateFontConfigMono } from "../validate/metrics.mjs";

Expand Down Expand Up @@ -34,6 +35,8 @@ export async function buildFont(para, cache) {
const font = await convertOtd(baseFont, otl, cleanGs);
// Build compatibility ligatures
if (para.compatibilityLigatures) await buildCompatLigatures(para, font);
// Apply post processing
postProcessFont(para, font);
// Generate ttfaControls
const ttfaControls = await generateTtfaControls(cleanGs, font.glyphs);

Expand Down
12 changes: 12 additions & 0 deletions packages/font/src/derive-spacing.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,24 @@ import { CliProc, Ot } from "ot-builder";

import { readTTF, saveTTF } from "./font-io/index.mjs";
import { assignFontNames, createNamingDictFromArgv } from "./naming/index.mjs";
import { getParametersT } from "./param/index.mjs";
import { postProcessFont } from "./post-processing/index.mjs";
import { validateFontConfigMono } from "./validate/metrics.mjs";

export default main;
async function main(argv) {
// Set up parameters
const paraT = await getParametersT(argv);
const para = paraT(argv);

// Read in font
const font = await readTTF(argv.i);

// Assign font names
const naming = createNamingDictFromArgv(argv);
assignFontNames(font, naming, false);

// Derive spacing
switch (argv.shape.spacing) {
case "term":
await deriveTerm(font);
Expand All @@ -31,12 +40,15 @@ async function main(argv) {
break;
}

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

// GC and save
switch (argv.shape.spacing) {
case "fontconfig-mono":
case "fixed":
CliProc.gcFont(font, Ot.ListGlyphStoreFactory);
postProcessFont(para, font);
validateFontConfigMono(font);
await saveTTF(argv.o, font);
break;
Expand Down
63 changes: 1 addition & 62 deletions packages/font/src/index.mjs
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import fs from "fs";
import path from "path";
import zlib from "zlib";

import * as Toml from "@iarna/toml";
import * as Caching from "@iosevka/geometry-cache";
import { createGrDisplaySheet } from "@iosevka/glyph/relation";
import * as Parameters from "@iosevka/param";
import { applyLigationData } from "@iosevka/param/ligation";
import { applyMetricOverride } from "@iosevka/param/metric-override";
import * as VariantData from "@iosevka/param/variant";
import { encode } from "@msgpack/msgpack";

import { buildFont } from "./build-font/index.mjs";
import { saveTTF } from "./font-io/index.mjs";
import { createNamingDictFromArgv } from "./naming/index.mjs";
import { getParametersT } from "./param/index.mjs";

export default main;
async function main(argv) {
Expand Down Expand Up @@ -42,61 +36,6 @@ async function main(argv) {
return { cacheUpdated };
}

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

// Parameter preparation
async function getParametersT(argv) {
const PARAMETERS_TOML = path.resolve(argv.paramsDir, "./parameters.toml");
const WEIGHTS_TOML = path.resolve(argv.paramsDir, "./shape-weight.toml");
const WIDTHS_TOML = path.resolve(argv.paramsDir, "./shape-width.toml");
const SLOPES_TOML = path.resolve(argv.paramsDir, "./shape-slope.toml");
const PRIVATE_TOML = path.resolve(argv.paramsDir, "./private-parameters.toml");
const VARIANTS_TOML = path.resolve(argv.paramsDir, "./variants.toml");
const LIGATIONS_TOML = path.resolve(argv.paramsDir, "./ligation-set.toml");
const parametersData = Object.assign(
{},
await tryParseToml(PARAMETERS_TOML),
await tryParseToml(WEIGHTS_TOML),
await tryParseToml(WIDTHS_TOML),
await tryParseToml(SLOPES_TOML),
fs.existsSync(PRIVATE_TOML) ? await tryParseToml(PRIVATE_TOML) : {},
);
const rawVariantsData = await tryParseToml(VARIANTS_TOML);
const rawLigationData = await tryParseToml(LIGATIONS_TOML);
function createParaImpl(argv) {
let para = Parameters.init(deepClone(parametersData), argv);
VariantData.apply(deepClone(rawVariantsData), para, argv);
applyLigationData(deepClone(rawLigationData), para, argv);
if (argv.excludedCharRanges) para.excludedCharRanges = argv.excludedCharRanges;
if (argv.compatibilityLigatures) para.compatibilityLigatures = argv.compatibilityLigatures;
if (argv.metricOverride) applyMetricOverride(para, argv.metricOverride, argv);
para.naming = { ...para.naming, ...createNamingDictFromArgv(argv) };
return para;
}
function paraT(argv) {
const para = createParaImpl(argv);
para.createFork = function (tf) {
const argv1 = deepClone(argv);
tf(argv1, argv);
return paraT(argv1);
};
return para;
}
return paraT;
}
async function tryParseToml(str) {
try {
return Toml.parse(await fs.promises.readFile(str, "utf-8"));
} catch (e) {
throw new Error(
`Failed to parse configuration file ${str}.\nPlease validate whether there's syntax error.\n${e}`,
);
}
}
function deepClone(pod) {
return JSON.parse(JSON.stringify(pod));
}

// Save character map file
async function saveCharMap(argv, glyphStore) {
let charMap = [];
Expand Down
2 changes: 1 addition & 1 deletion packages/font/src/naming/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ function getStyleLinkedStyles(menuNameMap, weight, width, slope) {
};
}

function nameFont(font, nameID, str) {
export function nameFont(font, nameID, str) {
nameFontImpl(font.name.records, 1, 0, 0, nameID, Buffer.from(str, "utf-8")); // Mac Roman
nameFontImpl(font.name.records, 3, 1, 1033, nameID, str); // Windows Unicode English
}
Expand Down
2 changes: 1 addition & 1 deletion packages/font/src/otd-conv/index.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CliProc } from "ot-builder";

import { convertGlyphs } from "./glyphs.mjs";
import { convertGsub, convertGpos, convertGdef } from "./layout.mjs";
import { convertGdef, convertGpos, convertGsub } from "./layout.mjs";

export function convertOtd(baseFont, otl, gs) {
const { glyphs, cmap } = convertGlyphs(gs);
Expand Down
62 changes: 62 additions & 0 deletions packages/font/src/param/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import fs from "fs";
import path from "path";

import * as Toml from "@iarna/toml";
import * as Parameters from "@iosevka/param";
import { applyLigationData } from "@iosevka/param/ligation";
import { applyMetricOverride } from "@iosevka/param/metric-override";
import * as VariantData from "@iosevka/param/variant";

import { createNamingDictFromArgv } from "../naming/index.mjs";

export async function getParametersT(argv) {
const PARAMETERS_TOML = path.resolve(argv.paramsDir, "./parameters.toml");
const WEIGHTS_TOML = path.resolve(argv.paramsDir, "./shape-weight.toml");
const WIDTHS_TOML = path.resolve(argv.paramsDir, "./shape-width.toml");
const SLOPES_TOML = path.resolve(argv.paramsDir, "./shape-slope.toml");
const PRIVATE_TOML = path.resolve(argv.paramsDir, "./private-parameters.toml");
const VARIANTS_TOML = path.resolve(argv.paramsDir, "./variants.toml");
const LIGATIONS_TOML = path.resolve(argv.paramsDir, "./ligation-set.toml");
const parametersData = Object.assign(
{},
await tryParseToml(PARAMETERS_TOML),
await tryParseToml(WEIGHTS_TOML),
await tryParseToml(WIDTHS_TOML),
await tryParseToml(SLOPES_TOML),
fs.existsSync(PRIVATE_TOML) ? await tryParseToml(PRIVATE_TOML) : {},
);
const rawVariantsData = await tryParseToml(VARIANTS_TOML);
const rawLigationData = await tryParseToml(LIGATIONS_TOML);
function createParaImpl(argv) {
let para = Parameters.init(deepClone(parametersData), argv);
VariantData.apply(deepClone(rawVariantsData), para, argv);
applyLigationData(deepClone(rawLigationData), para, argv);
if (argv.excludedCharRanges) para.excludedCharRanges = argv.excludedCharRanges;
if (argv.compatibilityLigatures) para.compatibilityLigatures = argv.compatibilityLigatures;
if (argv.metricOverride) applyMetricOverride(para, argv.metricOverride, argv);
para.naming = { ...para.naming, ...createNamingDictFromArgv(argv) };
return para;
}
function paraT(argv) {
const para = createParaImpl(argv);
para.createFork = function (tf) {
const argv1 = deepClone(argv);
tf(argv1, argv);
return paraT(argv1);
};
return para;
}
return paraT;
}
async function tryParseToml(str) {
try {
return Toml.parse(await fs.promises.readFile(str, "utf-8"));
} catch (e) {
throw new Error(
`Failed to parse configuration file ${str}.\nPlease validate whether there's syntax error.\n${e}`,
);
}
}
function deepClone(pod) {
return JSON.parse(JSON.stringify(pod));
}
46 changes: 46 additions & 0 deletions packages/font/src/post-processing/feature-params.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Ot, Sigma } from "ot-builder";

import { nameFont } from "../naming/index.mjs";

export function postProcessingFeatureParams(para, font) {
if (!para.variants.composites) return;
if (!font.gsub) return;
if (!font.name) return;

const nm = new NameManager();

for (const [name, ss] of para.variants.composites) {
if (!ss.description) continue;
for (const feat of font.gsub.features) {
if (feat.tag !== ss.tag) continue;
const nameId = nm.getNameId(ss.description);
feat.params = Sigma.DependentPair.create(Ot.GsubGpos.FeatureParams.TID_StylisticSet, {
uiNameID: nameId,
});
}
}

nm.apply(font);
}

class NameManager {
constructor() {
this.nameId = 257;
this.nameMap = new Map();
}

getNameId(name) {
let nameId = this.nameMap.get(name);
if (!nameId) {
nameId = this.nameId++;
this.nameMap.set(name, nameId);
}
return nameId;
}

apply(font) {
for (const [name, id] of this.nameMap) {
nameFont(font, id, name);
}
}
}
5 changes: 5 additions & 0 deletions packages/font/src/post-processing/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { postProcessingFeatureParams } from "./feature-params.mjs";

export function postProcessFont(para, font) {
postProcessingFeatureParams(para, font);
}

0 comments on commit f702ea5

Please sign in to comment.