From f702ea5b62410b56066d0d9ec92881d681cf8339 Mon Sep 17 00:00:00 2001 From: be5invis Date: Wed, 29 Jan 2025 05:18:31 -0800 Subject: [PATCH] Descriptions of stylistic sets are added to font files (#2664). --- changes/32.5.0.md | 1 + packages/font/src/build-font/index.mjs | 3 + packages/font/src/derive-spacing.mjs | 12 ++++ packages/font/src/index.mjs | 63 +------------------ packages/font/src/naming/index.mjs | 2 +- packages/font/src/otd-conv/index.mjs | 2 +- packages/font/src/param/index.mjs | 62 ++++++++++++++++++ .../src/post-processing/feature-params.mjs | 46 ++++++++++++++ packages/font/src/post-processing/index.mjs | 5 ++ 9 files changed, 132 insertions(+), 64 deletions(-) create mode 100644 packages/font/src/param/index.mjs create mode 100644 packages/font/src/post-processing/feature-params.mjs create mode 100644 packages/font/src/post-processing/index.mjs diff --git a/changes/32.5.0.md b/changes/32.5.0.md index b77cdbe943..0869f30887 100644 --- a/changes/32.5.0.md +++ b/changes/32.5.0.md @@ -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). diff --git a/packages/font/src/build-font/index.mjs b/packages/font/src/build-font/index.mjs index a6caf3b7fb..a5c57e5eae 100644 --- a/packages/font/src/build-font/index.mjs +++ b/packages/font/src/build-font/index.mjs @@ -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"; @@ -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); diff --git a/packages/font/src/derive-spacing.mjs b/packages/font/src/derive-spacing.mjs index f55178668a..03c5bd7344 100644 --- a/packages/font/src/derive-spacing.mjs +++ b/packages/font/src/derive-spacing.mjs @@ -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); @@ -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; diff --git a/packages/font/src/index.mjs b/packages/font/src/index.mjs index 50c718450e..8be6d21906 100644 --- a/packages/font/src/index.mjs +++ b/packages/font/src/index.mjs @@ -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) { @@ -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 = []; diff --git a/packages/font/src/naming/index.mjs b/packages/font/src/naming/index.mjs index 278ba12969..711be75980 100644 --- a/packages/font/src/naming/index.mjs +++ b/packages/font/src/naming/index.mjs @@ -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 } diff --git a/packages/font/src/otd-conv/index.mjs b/packages/font/src/otd-conv/index.mjs index 5893407b0c..afffda8a55 100644 --- a/packages/font/src/otd-conv/index.mjs +++ b/packages/font/src/otd-conv/index.mjs @@ -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); diff --git a/packages/font/src/param/index.mjs b/packages/font/src/param/index.mjs new file mode 100644 index 0000000000..dd02ed5c8b --- /dev/null +++ b/packages/font/src/param/index.mjs @@ -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)); +} diff --git a/packages/font/src/post-processing/feature-params.mjs b/packages/font/src/post-processing/feature-params.mjs new file mode 100644 index 0000000000..37813f6381 --- /dev/null +++ b/packages/font/src/post-processing/feature-params.mjs @@ -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); + } + } +} diff --git a/packages/font/src/post-processing/index.mjs b/packages/font/src/post-processing/index.mjs new file mode 100644 index 0000000000..f1ab0d7678 --- /dev/null +++ b/packages/font/src/post-processing/index.mjs @@ -0,0 +1,5 @@ +import { postProcessingFeatureParams } from "./feature-params.mjs"; + +export function postProcessFont(para, font) { + postProcessingFeatureParams(para, font); +}