Add unhinted webfont targets (#1363)

This commit is contained in:
be5invis 2022-06-04 17:00:40 -07:00
parent dd627b4556
commit 5d747b9a66
3 changed files with 148 additions and 97 deletions

View file

@ -38,13 +38,15 @@ To create a custom build, you need:
3. Run `npm run build -- contents::<your plan name>` and the built fonts would be available in `dist/`. Aside from `contents::<plan>`, other options are:
1. `contents::<plan>` : TTF (Hinted and Unhinted), WOFF(2) and Web font CSS;
2. `ttf::<plan>` : TTF;
1. `contents::<plan>` : Everything (TTF + webfont, hinted + unhinted);
2. `ttf::<plan>` : TTF only;
3. `ttf-unhinted::<plan>` : Unhinted TTF only;
4. `webfont::<plan>` : Web fonts only (CSS + WOFF2);
4. `webfont-unhinted::<plan>` : Unhinted web fonts only (CSS + WOFF2);
5. `woff2::<plan>` : WOFF2 only.
5. `woff2-unhinted::<plan>` : Unhinted WOFF2 only.
⚠️ **Important:** By default, the build system will schedule a number of concurrently running jobs equal to the number of threads available on the CPU, which *will* push CPU usage and also likely RAM usage, if you do not have very much to work with, to the ceiling (each job consumes more than 1 GB of RAM at its peak). If this is an issue for you, pass an additional argument `--jCmd=<number of concurrent jobs>`.
⚠️ **Important**: By default, the build system will schedule a number of concurrently running jobs equal to the number of threads available on the CPU, which *will* push CPU usage and also likely RAM usage, if you do not have very much to work with, to the ceiling (each job consumes more than 1 GB of RAM at its peak). If this is an issue for you, pass an additional argument `--jCmd=<number of concurrent jobs>`.
### Configuring Custom Build

View file

@ -6,7 +6,7 @@ const WebfontFormatMap = new Map([
["woff2", "woff2"],
["ttf", "truetype"]
]);
module.exports = function (output, family, hs, formats) {
module.exports = function (output, family, hs, formats, unhinted) {
if (!formats) {
fs.writeFileSync(output, "");
return;
@ -17,8 +17,13 @@ module.exports = function (output, family, hs, formats) {
if (!WebfontFormatMap.get(ext)) throw new TypeError("Invalid webfont file format " + ext);
}
for (const term of hs) {
const dirSuffix = unhinted ? "-unhinted" : "";
const src = formats
.map(ext => `url('${ext}/${term.name}.${ext}') format('${WebfontFormatMap.get(ext)}')`)
.map(
ext =>
`url('${ext}${dirSuffix}/${term.name}.${ext}') ` +
`format('${WebfontFormatMap.get(ext)}')`
)
.join(", ");
ans += `
@font-face {

View file

@ -352,7 +352,6 @@ const DistUnhintedTTF = file.make(
}
}
);
const BuildCM = file.make(
(gr, f) => `${BUILD}/ttf/${gr}/${f}.charmap.mpz`,
async (target, output, gr, f) => {
@ -360,65 +359,10 @@ const BuildCM = file.make(
}
);
///////////////////////////////////////////////////////////
////// Font Distribution //////
///////////////////////////////////////////////////////////
// Group-level
const GroupContents = task.group("contents", async (target, gr) => {
await target.need(GroupFonts(gr), DistWebFontCSS(gr));
return gr;
});
// Webfont CSS
const DistWebFontCSS = file.make(
gr => `${DIST}/${gr}/${gr}.css`,
async (target, out, gr) => {
const [plan] = await target.need(BuildPlanOf(gr));
await target.need(de(out.dir));
await createWebFontCssImpl(target, out.full, gr, plan.webfontFormats);
}
);
async function createWebFontCssImpl(target, output, gr, formats) {
const [bp, ts] = await target.need(BuildPlanOf(gr), GroupFontsOf(gr));
const hs = await target.need(...ts.map(FontInfoOf));
echo.action(echo.hl.command(`Create WebFont CSS`), gr, echo.hl.operator("->"), output);
await silently.node("utility/make-webfont-css.js", output, bp.family, hs, formats);
function formatSuffix(fmt, unhinted) {
return fmt + (unhinted ? "-unhinted" : "");
}
// Content files
const GroupTTFs = task.group("ttf", async (target, gr) => {
const [ts] = await target.need(GroupFontsOf(gr));
await target.need(ts.map(tn => DistHintedTTF(gr, tn)));
});
const GroupUnhintedTTFs = task.group("ttf-unhinted", async (target, gr) => {
const [ts] = await target.need(GroupFontsOf(gr));
await target.need(ts.map(tn => DistUnhintedTTF(gr, tn)));
});
const GroupWebFonts = task.group("webfont", async (target, gr) => {
const [bp] = await target.need(BuildPlanOf(gr));
const groupsNeeded = [];
for (const ext of bp.webfontFormats) {
switch (ext) {
case "ttf":
groupsNeeded.push(GroupTTFs(gr));
break;
case "woff2":
groupsNeeded.push(GroupWoff2s(gr));
break;
}
}
await target.need(groupsNeeded, DistWebFontCSS(gr));
});
const GroupWoff2s = task.group("woff2", async (target, gr) => {
const [ts] = await target.need(GroupFontsOf(gr));
await target.need(ts.map(tn => DistWoff2(gr, tn)));
});
const GroupFonts = task.group("fonts", async (target, gr) => {
await target.need(GroupTTFs(gr), GroupUnhintedTTFs(gr), GroupWoff2s(gr));
});
// Per group file
const DistHintedTTF = file.make(
(gr, fn) => `${DIST}/${gr}/ttf/${fn}.ttf`,
async (target, out, gr, fn) => {
@ -428,15 +372,103 @@ const DistHintedTTF = file.make(
await silently.run(hint, hintParams, from.full, out.full);
}
);
const DistWoff2 = file.make(
(gr, fn) => `${DIST}/${gr}/woff2/${fn}.woff2`,
async (target, out, group, f) => {
const [from] = await target.need(DistHintedTTF(group, f), de`${out.dir}`);
(gr, fn, unhinted) => `${DIST}/${gr}/${formatSuffix("woff2", unhinted)}/${fn}.woff2`,
async (target, out, group, f, unhinted) => {
const Ctor = unhinted ? DistUnhintedTTF : DistHintedTTF;
const [from] = await target.need(Ctor(group, f), de`${out.dir}`);
echo.action(echo.hl.command("Create WOFF2"), from.full, echo.hl.operator("->"), out.full);
await silently.node(`utility/ttf-to-woff2.js`, from.full, out.full);
}
);
///////////////////////////////////////////////////////////
////// Font Distribution //////
///////////////////////////////////////////////////////////
// Group-level entry points
const Entry_GroupContents = task.group("contents", async (target, gr) => {
await target.need(Entry_GroupFonts(gr), Entry_GroupUnhintedFonts(gr));
return gr;
});
const Entry_GroupTTFs = task.group("ttf", async (target, gr) => {
await target.need(GroupTtfsImpl(gr, false));
});
const Entry_GroupUnhintedTTFs = task.group("ttf-unhinted", async (target, gr) => {
await target.need(GroupTtfsImpl(gr, true));
});
const Entry_GroupWoff2s = task.group("woff2", async (target, gr) => {
await target.need(GroupWoff2Impl(gr, false));
});
const Entry_GroupUnhintedWoff2s = task.group("woff2-unhinted", async (target, gr) => {
await target.need(GroupWoff2Impl(gr, true));
});
const Entry_GroupWebFonts = task.group("webfont", async (target, gr) => {
await target.need(GroupWebFontsImpl(gr, false));
});
const Entry_GroupUnhintedWebFonts = task.group("webfont-unhinted", async (target, gr) => {
await target.need(GroupWebFontsImpl(gr, true));
});
const Entry_GroupFonts = task.group("fonts", async (target, gr) => {
await target.need(GroupTtfsImpl(gr, false), GroupWebFontsImpl(gr, false));
});
const Entry_GroupUnhintedFonts = task.group("fonts-unhinted", async (target, gr) => {
await target.need(GroupTtfsImpl(gr, true), GroupWebFontsImpl(gr, true));
});
// Webfont CSS
const DistWebFontCSS = file.make(
(gr, unhinted) => `${DIST}/${gr}/${formatSuffix(gr, unhinted)}.css`,
async (target, out, gr, unhinted) => {
const [plan] = await target.need(BuildPlanOf(gr));
await target.need(de(out.dir));
await createWebFontCssImpl(target, out.full, gr, plan.webfontFormats, unhinted);
}
);
async function createWebFontCssImpl(target, output, gr, formats, unhinted) {
const [bp, ts] = await target.need(BuildPlanOf(gr), GroupFontsOf(gr));
const hs = await target.need(...ts.map(FontInfoOf));
echo.action(echo.hl.command(`Create WebFont CSS`), gr, echo.hl.operator("->"), output);
await silently.node("utility/make-webfont-css.js", output, bp.family, hs, formats, unhinted);
}
// Content files
const GroupTtfsImpl = task.make(
(gr, unhinted) => `group-${formatSuffix("ttf-impl", unhinted)}::${gr}`,
async (target, gr, unhinted) => {
const Ctor = unhinted ? DistUnhintedTTF : DistHintedTTF;
const [ts] = await target.need(GroupFontsOf(gr));
await target.need(ts.map(tn => Ctor(gr, tn)));
}
);
const GroupWoff2Impl = task.make(
(gr, unhinted) => `group-${formatSuffix("woff2-impl", unhinted)}::${gr}`,
async (target, gr, unhinted) => {
const [ts] = await target.need(GroupFontsOf(gr));
await target.need(ts.map(tn => DistWoff2(gr, tn, unhinted)));
}
);
const GroupWebFontsImpl = task.make(
(gr, unhinted) => `group-${formatSuffix("webfont-impl", unhinted)}::${gr}`,
async (target, gr, unhinted) => {
const [bp] = await target.need(BuildPlanOf(gr));
const groupsNeeded = [];
for (const ext of bp.webfontFormats) {
switch (ext) {
case "ttf":
groupsNeeded.push(GroupTtfsImpl(gr, unhinted));
break;
case "woff2":
groupsNeeded.push(GroupWoff2Impl(gr, unhinted));
break;
}
}
await target.need(groupsNeeded, DistWebFontCSS(gr, unhinted));
}
);
///////////////////////////////////////////////////////////
////// Font Collection Plans //////
///////////////////////////////////////////////////////////
@ -582,7 +614,7 @@ async function buildGlyphSharingTtc(target, parts, out) {
///////////////////////////////////////////////////////////
// Collection Archives
const TtcArchiveFile = file.make(
const TtcZip = file.make(
(cgr, version) => `${ARCHIVE_DIR}/ttc-${cgr}-${version}.zip`,
async (target, out, cgr) => {
const [cPlan] = await target.need(CollectPlans, de`${out.dir}`);
@ -591,7 +623,7 @@ const TtcArchiveFile = file.make(
await CreateGroupArchiveFile(`${BUILD}/ttc-collect/${cgr}`, out, `*.ttc`);
}
);
const SuperTtcArchiveFile = file.make(
const SuperTtcZip = file.make(
(cgr, version) => `${ARCHIVE_DIR}/super-ttc-${cgr}-${version}.zip`,
async (target, out, cgr) => {
await target.need(de`${out.dir}`, CollectedSuperTtcFile(cgr));
@ -600,29 +632,32 @@ const SuperTtcArchiveFile = file.make(
);
// Single-group Archives
const GroupTtfArchiveFile = file.make(
(gr, version) => `${ARCHIVE_DIR}/ttf-${gr}-${version}.zip`,
async (target, out, gr) => {
const GroupTtfZip = file.make(
(gr, version, unhinted) =>
`${ARCHIVE_DIR}/${formatSuffix("ttf", unhinted)}-${gr}-${version}.zip`,
async (target, out, gr, _version_, unhinted) => {
await target.need(de`${out.dir}`);
await target.need(GroupContents(gr));
await CreateGroupArchiveFile(`${DIST}/${gr}/ttf`, out, "*.ttf");
await target.need(GroupTtfsImpl(gr, unhinted));
await CreateGroupArchiveFile(
`${DIST}/${gr}/${formatSuffix("ttf", unhinted)}`,
out,
"*.ttf"
);
}
);
const GroupTtfUnhintedArchiveFile = file.make(
(gr, version) => `${ARCHIVE_DIR}/ttf-unhinted-${gr}-${version}.zip`,
async (target, out, gr) => {
await target.need(de`${out.dir}`);
await target.need(GroupContents(gr));
await CreateGroupArchiveFile(`${DIST}/${gr}/ttf-unhinted`, out, "*.ttf");
}
);
const GroupWebArchiveFile = file.make(
(gr, version) => `${ARCHIVE_DIR}/webfont-${gr}-${version}.zip`,
async (target, out, gr) => {
const GroupWebZip = file.make(
(gr, version, unhinted) =>
`${ARCHIVE_DIR}/${formatSuffix("webfont", unhinted)}-${gr}-${version}.zip`,
async (target, out, gr, _version_, unhinted) => {
const [plan] = await target.need(BuildPlanOf(gr));
await target.need(de`${out.dir}`);
await target.need(GroupContents(gr));
await CreateGroupArchiveFile(`${DIST}/${gr}`, out, "*.css", ...plan.webfontFormats);
await target.need(GroupWebFontsImpl(gr, unhinted));
await CreateGroupArchiveFile(
`${DIST}/${gr}`,
out,
`${formatSuffix(gr, unhinted)}.css`,
...plan.webfontFormats.map(format => formatSuffix(format, unhinted))
);
}
);
@ -681,7 +716,7 @@ const PagesFontExport = task.group(`pages:font-export`, async (target, gr) => {
const [pagesDir] = await target.need(PagesDir);
if (!pagesDir) return;
const outDir = Path.resolve(pagesDir, "shared/fonts", gr);
await target.need(GroupWebFonts(gr), de(outDir));
await target.need(GroupWebFontsImpl(gr, false), de(outDir));
await cp(`${DIST}/${gr}/woff2`, Path.resolve(outDir, "woff2"));
await createWebFontCssImpl(target, Path.resolve(outDir, `${gr}.css`), gr, webfontFormatsPages);
await rm(Path.resolve(outDir, "ttf"));
@ -692,7 +727,7 @@ const PagesFastFontExport = task.group(`pages:fast-font-export`, async (target,
const [pagesDir] = await target.need(PagesDir);
if (!pagesDir) return;
const outDir = Path.resolve(pagesDir, "shared/fonts", gr);
await target.need(GroupUnhintedTTFs(gr), de(outDir));
await target.need(GroupTtfsImpl(gr, true), de(outDir));
// Next.js 12 has some problem about refreshing fonts, so write an empty CSS first
await createWebFontCssImpl(target, Path.resolve(outDir, `${gr}.css`), gr, null);
@ -723,10 +758,10 @@ const SampleImages = task(`sample-images`, async target => {
const SampleImagesPre = task(`sample-images:pre`, async target => {
const [sans, slab, aile, etoile] = await target.need(
GroupContents`iosevka`,
GroupContents`iosevka-slab`,
GroupContents`iosevka-aile`,
GroupContents`iosevka-etoile`,
GroupWebFontsImpl(`iosevka`, false),
GroupWebFontsImpl(`iosevka-slab`, false),
GroupWebFontsImpl(`iosevka-aile`, false),
GroupWebFontsImpl(`iosevka-etoile`, false),
SnapShotStatic("index.js"),
SnapShotStatic("get-snap.js"),
SnapShotJson,
@ -928,12 +963,12 @@ phony(`release`, async target => {
for (const [cgr, plan] of Object.entries(collectPlans)) {
if (!plan.inRelease) continue;
const subGroups = collectPlans[cgr].groupDecomposition;
goals.push(TtcArchiveFile(cgr, version));
goals.push(SuperTtcArchiveFile(cgr, version));
goals.push(TtcZip(cgr, version));
goals.push(SuperTtcZip(cgr, version));
for (const gr of subGroups) {
goals.push(GroupTtfArchiveFile(gr, version));
goals.push(GroupTtfUnhintedArchiveFile(gr, version));
goals.push(GroupWebArchiveFile(gr, version));
goals.push(GroupTtfZip(gr, version, false));
goals.push(GroupTtfZip(gr, version, true));
goals.push(GroupWebZip(gr, version, false));
}
}
const [archiveFiles] = await target.need(goals);
@ -943,6 +978,15 @@ phony(`release`, async target => {
// Images and release notes
await target.need(SampleImages, Pages, AmendReadme, ReleaseNotes, ChangeLog);
});
phony.group(`release-test`, async (target, gr) => {
const [version] = await target.need(Version);
await target.need(
GroupTtfZip(gr, version, false),
GroupTtfZip(gr, version, true),
GroupWebZip(gr, version, false),
GroupWebZip(gr, version, true)
);
});
///////////////////////////////////////////////////////////
////// Script Building //////