Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Descriptions of stylistic sets are added to font files (#2664). #2665

Merged
merged 1 commit into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}