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: 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; 1. `contents::<plan>` : Everything (TTF + webfont, hinted + unhinted);
2. `ttf::<plan>` : TTF; 2. `ttf::<plan>` : TTF only;
3. `ttf-unhinted::<plan>` : Unhinted TTF only; 3. `ttf-unhinted::<plan>` : Unhinted TTF only;
4. `webfont::<plan>` : Web fonts only (CSS + WOFF2); 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::<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 ### Configuring Custom Build

View file

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

View file

@ -352,7 +352,6 @@ const DistUnhintedTTF = file.make(
} }
} }
); );
const BuildCM = file.make( const BuildCM = file.make(
(gr, f) => `${BUILD}/ttf/${gr}/${f}.charmap.mpz`, (gr, f) => `${BUILD}/ttf/${gr}/${f}.charmap.mpz`,
async (target, output, gr, f) => { async (target, output, gr, f) => {
@ -360,65 +359,10 @@ const BuildCM = file.make(
} }
); );
/////////////////////////////////////////////////////////// function formatSuffix(fmt, unhinted) {
////// Font Distribution ////// return fmt + (unhinted ? "-unhinted" : "");
///////////////////////////////////////////////////////////
// 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);
} }
// 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( const DistHintedTTF = file.make(
(gr, fn) => `${DIST}/${gr}/ttf/${fn}.ttf`, (gr, fn) => `${DIST}/${gr}/ttf/${fn}.ttf`,
async (target, out, gr, fn) => { async (target, out, gr, fn) => {
@ -428,15 +372,103 @@ const DistHintedTTF = file.make(
await silently.run(hint, hintParams, from.full, out.full); await silently.run(hint, hintParams, from.full, out.full);
} }
); );
const DistWoff2 = file.make( const DistWoff2 = file.make(
(gr, fn) => `${DIST}/${gr}/woff2/${fn}.woff2`, (gr, fn, unhinted) => `${DIST}/${gr}/${formatSuffix("woff2", unhinted)}/${fn}.woff2`,
async (target, out, group, f) => { async (target, out, group, f, unhinted) => {
const [from] = await target.need(DistHintedTTF(group, f), de`${out.dir}`); 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); 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); 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 ////// ////// Font Collection Plans //////
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
@ -582,7 +614,7 @@ async function buildGlyphSharingTtc(target, parts, out) {
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
// Collection Archives // Collection Archives
const TtcArchiveFile = file.make( const TtcZip = file.make(
(cgr, version) => `${ARCHIVE_DIR}/ttc-${cgr}-${version}.zip`, (cgr, version) => `${ARCHIVE_DIR}/ttc-${cgr}-${version}.zip`,
async (target, out, cgr) => { async (target, out, cgr) => {
const [cPlan] = await target.need(CollectPlans, de`${out.dir}`); 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`); 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`, (cgr, version) => `${ARCHIVE_DIR}/super-ttc-${cgr}-${version}.zip`,
async (target, out, cgr) => { async (target, out, cgr) => {
await target.need(de`${out.dir}`, CollectedSuperTtcFile(cgr)); await target.need(de`${out.dir}`, CollectedSuperTtcFile(cgr));
@ -600,29 +632,32 @@ const SuperTtcArchiveFile = file.make(
); );
// Single-group Archives // Single-group Archives
const GroupTtfArchiveFile = file.make( const GroupTtfZip = file.make(
(gr, version) => `${ARCHIVE_DIR}/ttf-${gr}-${version}.zip`, (gr, version, unhinted) =>
async (target, out, gr) => { `${ARCHIVE_DIR}/${formatSuffix("ttf", unhinted)}-${gr}-${version}.zip`,
async (target, out, gr, _version_, unhinted) => {
await target.need(de`${out.dir}`); await target.need(de`${out.dir}`);
await target.need(GroupContents(gr)); await target.need(GroupTtfsImpl(gr, unhinted));
await CreateGroupArchiveFile(`${DIST}/${gr}/ttf`, out, "*.ttf"); await CreateGroupArchiveFile(
`${DIST}/${gr}/${formatSuffix("ttf", unhinted)}`,
out,
"*.ttf"
);
} }
); );
const GroupTtfUnhintedArchiveFile = file.make( const GroupWebZip = file.make(
(gr, version) => `${ARCHIVE_DIR}/ttf-unhinted-${gr}-${version}.zip`, (gr, version, unhinted) =>
async (target, out, gr) => { `${ARCHIVE_DIR}/${formatSuffix("webfont", unhinted)}-${gr}-${version}.zip`,
await target.need(de`${out.dir}`); async (target, out, gr, _version_, unhinted) => {
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 [plan] = await target.need(BuildPlanOf(gr)); const [plan] = await target.need(BuildPlanOf(gr));
await target.need(de`${out.dir}`); await target.need(de`${out.dir}`);
await target.need(GroupContents(gr)); await target.need(GroupWebFontsImpl(gr, unhinted));
await CreateGroupArchiveFile(`${DIST}/${gr}`, out, "*.css", ...plan.webfontFormats); 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); const [pagesDir] = await target.need(PagesDir);
if (!pagesDir) return; if (!pagesDir) return;
const outDir = Path.resolve(pagesDir, "shared/fonts", gr); 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 cp(`${DIST}/${gr}/woff2`, Path.resolve(outDir, "woff2"));
await createWebFontCssImpl(target, Path.resolve(outDir, `${gr}.css`), gr, webfontFormatsPages); await createWebFontCssImpl(target, Path.resolve(outDir, `${gr}.css`), gr, webfontFormatsPages);
await rm(Path.resolve(outDir, "ttf")); 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); const [pagesDir] = await target.need(PagesDir);
if (!pagesDir) return; if (!pagesDir) return;
const outDir = Path.resolve(pagesDir, "shared/fonts", gr); 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 // 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); 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 SampleImagesPre = task(`sample-images:pre`, async target => {
const [sans, slab, aile, etoile] = await target.need( const [sans, slab, aile, etoile] = await target.need(
GroupContents`iosevka`, GroupWebFontsImpl(`iosevka`, false),
GroupContents`iosevka-slab`, GroupWebFontsImpl(`iosevka-slab`, false),
GroupContents`iosevka-aile`, GroupWebFontsImpl(`iosevka-aile`, false),
GroupContents`iosevka-etoile`, GroupWebFontsImpl(`iosevka-etoile`, false),
SnapShotStatic("index.js"), SnapShotStatic("index.js"),
SnapShotStatic("get-snap.js"), SnapShotStatic("get-snap.js"),
SnapShotJson, SnapShotJson,
@ -928,12 +963,12 @@ phony(`release`, async target => {
for (const [cgr, plan] of Object.entries(collectPlans)) { for (const [cgr, plan] of Object.entries(collectPlans)) {
if (!plan.inRelease) continue; if (!plan.inRelease) continue;
const subGroups = collectPlans[cgr].groupDecomposition; const subGroups = collectPlans[cgr].groupDecomposition;
goals.push(TtcArchiveFile(cgr, version)); goals.push(TtcZip(cgr, version));
goals.push(SuperTtcArchiveFile(cgr, version)); goals.push(SuperTtcZip(cgr, version));
for (const gr of subGroups) { for (const gr of subGroups) {
goals.push(GroupTtfArchiveFile(gr, version)); goals.push(GroupTtfZip(gr, version, false));
goals.push(GroupTtfUnhintedArchiveFile(gr, version)); goals.push(GroupTtfZip(gr, version, true));
goals.push(GroupWebArchiveFile(gr, version)); goals.push(GroupWebZip(gr, version, false));
} }
} }
const [archiveFiles] = await target.need(goals); const [archiveFiles] = await target.need(goals);
@ -943,6 +978,15 @@ phony(`release`, async target => {
// Images and release notes // Images and release notes
await target.need(SampleImages, Pages, AmendReadme, ReleaseNotes, ChangeLog); 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 ////// ////// Script Building //////