Fix compatibility ligature building (#524).
This commit is contained in:
parent
8d7a304b96
commit
6b319855e4
9 changed files with 354 additions and 251 deletions
85
README.md
85
README.md
|
@ -72,12 +72,15 @@ Since version 2.0, Iosevka would no longer support building via `makefile`. To i
|
|||
2. Add a build plan into `private-build-plans.toml`, following this format:
|
||||
|
||||
```toml
|
||||
[buildPlans.iosevka-custom] # <iosevka-custom> is your plan name
|
||||
family = "Iosevka Custom" # Font menu family name
|
||||
design = ["leading-1500", "v-i-hooky", "v-l-hooky"] # Customize styles
|
||||
hintParams = ["-a", "sss"] # Optional custom parameters for ttfautohint
|
||||
|
||||
[buildPlans.iosevka-custom] # <iosevka-custom> is your plan name
|
||||
family = "Iosevka Custom" # Font menu family name
|
||||
design = ["v-i-hooky", "v-l-hooky"] # Customize styles
|
||||
# upright = ["upright-styles"] # Uncomment this line to set styles for upright only
|
||||
# italic = ["italic-styles"] # Uncomment this line to set styles for italic only
|
||||
# oblique = ["oblique-styles"] # Uncomment this line to set styles for oblique only
|
||||
hintParams = ["-a", "sss"] # Optional custom parameters for ttfautohint
|
||||
|
||||
###################################################################################################
|
||||
# Override default building weights
|
||||
# When buildPlans.<plan name>.weights is absent, all weights would built and mapped to
|
||||
# default values.
|
||||
|
@ -99,19 +102,24 @@ Since version 2.0, Iosevka would no longer support building via `makefile`. To i
|
|||
shape = 700
|
||||
menu = 700
|
||||
css = 700
|
||||
|
||||
# End weight section
|
||||
###################################################################################################
|
||||
|
||||
|
||||
###################################################################################################
|
||||
# Override default building slant sets
|
||||
# Format: <upright|italic|oblique> = <"normal"|"italic"|"oblique">
|
||||
# When this section is absent, all slants would be built.
|
||||
|
||||
[buildPlans.iosevka-custom.slants]
|
||||
upright = "normal"
|
||||
italic = "italic"
|
||||
oblique = "oblique"
|
||||
|
||||
# End slant section
|
||||
###################################################################################################
|
||||
|
||||
|
||||
###################################################################################################
|
||||
# Override default building widths
|
||||
# When buildPlans.<plan name>.widths is absent, all widths would built and mapped to
|
||||
# default values.
|
||||
|
@ -119,17 +127,62 @@ Since version 2.0, Iosevka would no longer support building via `makefile`. To i
|
|||
# support 1, 2, 3, 4, 5, 6, 7, 8, 9.
|
||||
# If you decide to use custom weights you have to define all the weights you
|
||||
# plan to use otherwise they will not be built.
|
||||
|
||||
[buildPlans.iosevka-custom.widths.normal]
|
||||
shape = 5 # Width of glyph shapes.
|
||||
menu = 5 # Width for the font's names.
|
||||
css = "normal" # "font-stretch' property of webfont CSS.
|
||||
|
||||
|
||||
[buildPlans.iosevka-custom.widths.extended]
|
||||
shape = 7
|
||||
menu = 7
|
||||
css = "expanded"
|
||||
|
||||
# End width section
|
||||
###################################################################################################
|
||||
|
||||
###################################################################################################
|
||||
# Character Exclusion
|
||||
# Specify character ranges in the section below to exclude certain characters from the font being
|
||||
# built. Remove this section when this feature is not needed.
|
||||
|
||||
[buildPlans.iosevka-custom.exclude-chars]
|
||||
ranges = [[10003, 10008]]
|
||||
|
||||
# End character exclusion
|
||||
###################################################################################################
|
||||
|
||||
###################################################################################################
|
||||
# Compatibility Ligatures
|
||||
# Certain applications like Emacs does not support proper programming liagtures provided by
|
||||
# OpenType, but can support ligatures provided by PUA codepoints. Therefore you can edit the
|
||||
# following section to build PUA characters that are generated from the OpenType ligatures.
|
||||
# Remove this section when compatibility ligatures are not needed.
|
||||
|
||||
[[buildPlans.iosevka-custom.compatibility-ligatures]]
|
||||
unicode = 57600 # 0xE100
|
||||
featureTag = 'calt'
|
||||
sequence = '<*>'
|
||||
|
||||
# End compatibility ligatures section
|
||||
###################################################################################################
|
||||
|
||||
###################################################################################################
|
||||
# Metric overrides
|
||||
# Certain metrics like line height (leading) could be overridden in your build plan file.
|
||||
# Edit the values to change the metrics. Remove this section when overriding is not needed.
|
||||
|
||||
[buildPlans.iosevka-custom.metric-override]
|
||||
leading = 1250
|
||||
winMetricAscenderPad = 0
|
||||
winMetricDescenderPad = 0
|
||||
powerlineScaleY = 1
|
||||
powerlineScaleX = 1
|
||||
powerlineShiftY = 0
|
||||
powerlineShiftX = 0
|
||||
|
||||
# End metric override section
|
||||
###################################################################################################
|
||||
```
|
||||
|
||||
3. Run `npm run build -- contents::<your plan name>` and the built fonts would be avaliable in `dist/`. Aside from `contents::<plan>`, other options are:
|
||||
|
@ -212,22 +265,6 @@ The current available styles for `design`/`upright`/`italic`/`oblique` options a
|
|||
|
||||
<!-- END Section-Cherry-Picking-Ligation-Sets -->
|
||||
|
||||
* Styles for changing the line space (leading):
|
||||
|
||||
* `leading-750`, `leading-1000`, `leading-1250`, `leading-1500`, `leading-1750`, `leading-2000`: Change the line space. Default is `leading-1250`.
|
||||
* `win-metric-pad-0`, `win-metric-pad-50`, `win-metric-pad-100`, `win-metric-pad-150`, `win-metric-pad-200`, `win-metric-pad-250`, `win-metric-pad-300`: Add extra space to [OS/2 table’s Win metrics](https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswinascent) to avoid clipping in certain legacy software.
|
||||
|
||||
* Styles for changing Powerline symbols' position:
|
||||
|
||||
* `powerline-scale-y-750`, `powerline-scale-y-875`, `powerline-scale-y-1000`, `powerline-scale-y-1125`, `powerline-scale-y-1250`, `powerline-scale-y-1375`, `powerline-scale-y-1500`: Resize the Powerline symbols vertically, from 75% to 150%.
|
||||
* `powerline-scale-x-750`, `powerline-scale-x-875`, `powerline-scale-x-1000`, `powerline-scale-x-1125`, `powerline-scale-x-1250`, `powerline-scale-x-1375`, `powerline-scale-x-1500`: Resize the Powerline symbols horizontally, from 75% to 150%.
|
||||
* `powerline-shift-y-n500`, `powerline-shift-y-n450`, `powerline-shift-y-n400`, `powerline-shift-y-n350`, `powerline-shift-y-n300`, `powerline-shift-y-n250`, `powerline-shift-y-n200`, `powerline-shift-y-n150`, `powerline-shift-y-n100`, `powerline-shift-y-n50`, `powerline-shift-y-0`, `powerline-shift-y-p50`, `powerline-shift-y-p100`, `powerline-shift-y-p150`, `powerline-shift-y-p200`, `powerline-shift-y-p250`, `powerline-shift-y-p300`, `powerline-shift-y-p350`, `powerline-shift-y-p400`, `powerline-shift-y-p450`, `powerline-shift-y-p500`: Shift the Powerline symbols vertically, from -0.5em to +0.5em.
|
||||
* `powerline-shift-x-n500`, `powerline-shift-x-n450`, `powerline-shift-x-n400`, `powerline-shift-x-n350`, `powerline-shift-x-n300`, `powerline-shift-x-n250`, `powerline-shift-x-n200`, `powerline-shift-x-n150`, `powerline-shift-x-n100`, `powerline-shift-x-n50`, `powerline-shift-x-0`, `powerline-shift-x-p50`, `powerline-shift-x-p100`, `powerline-shift-x-p150`, `powerline-shift-x-p200`, `powerline-shift-x-p250`, `powerline-shift-x-p300`, `powerline-shift-x-p350`, `powerline-shift-x-p400`, `powerline-shift-x-p450`, `powerline-shift-x-p500`: Shift the Powerline symbols horizontally, from -0.5em to +0.5em.
|
||||
|
||||
* Symbol exclusion:
|
||||
|
||||
* `exclude-check-and-cross-symbol`: Exclude `✓✔✕✖✗✘` (U+2713 – U+2718) from the font.
|
||||
|
||||
<!-- BEGIN Section-Stylistic-Sets -->
|
||||
<!-- THIS SECTION IS AUTOMATICALLY GENERATED. DO NOT EDIT. -->
|
||||
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
* Fix dot removal on various derived glyphs (#513).
|
||||
* Fix styling features for Bulgarian, Macedonian, or Serbian (#514).
|
||||
* Fix seam on certain Cyrillic letters with descender shape (#517).
|
||||
* Fix compatibility ligature building (#524). Also moved metric override configuration, compatibility ligature configuration and character removal configuration into build plans.
|
42
gen/index.js
42
gen/index.js
|
@ -3,16 +3,16 @@
|
|||
const fs = require("fs-extra");
|
||||
const path = require("path");
|
||||
|
||||
const buildFont = require("./build-font.js");
|
||||
const parameters = require("../support/parameters");
|
||||
const formVariantData = require("../support/variant-data");
|
||||
const formLigationData = require("../support/ligation-data");
|
||||
const toml = require("toml");
|
||||
const BuildFont = require("./build-font.js");
|
||||
const Parameters = require("../support/parameters");
|
||||
const FormVariantData = require("../support/variant-data");
|
||||
const FormLigationData = require("../support/ligation-data");
|
||||
const Toml = require("toml");
|
||||
|
||||
module.exports = async function main(argv) {
|
||||
const para = await getParameters(argv);
|
||||
const font = buildFont(para);
|
||||
if (argv.charmap) await saveCharMap(argv, font);
|
||||
const font = BuildFont(para);
|
||||
if (argv.oCharMap) await saveCharMap(argv, font);
|
||||
if (argv.o) await saveOtd(argv, font);
|
||||
};
|
||||
|
||||
|
@ -31,23 +31,27 @@ async function getParameters(argv) {
|
|||
const rawVariantsData = await tryParseToml(VARIANTS_TOML);
|
||||
const rawLigationData = await tryParseToml(LIGATIONS_TOML);
|
||||
|
||||
const para = parameters.build(parametersData, argv.hives, { shapeWeight: argv.shapeWeight });
|
||||
const para = Parameters.build(parametersData, argv.hives, { shapeWeight: argv.shape.weight });
|
||||
|
||||
const variantsData = formVariantData(rawVariantsData, para);
|
||||
const variantsData = FormVariantData(rawVariantsData, para);
|
||||
para.variants = variantsData;
|
||||
para.variantSelector = parameters.build(variantsData, ["default", ...argv.hives]);
|
||||
para.variantSelector = Parameters.build(variantsData, ["default", ...argv.hives]);
|
||||
para.defaultVariant = variantsData.default;
|
||||
|
||||
const ligationData = formLigationData(rawLigationData, para);
|
||||
const ligationData = FormLigationData(rawLigationData, para);
|
||||
para.defaultBuildup = ligationData.defaultBuildup;
|
||||
para.ligation = parameters.build(ligationData.hives, ["default", ...argv.hives]);
|
||||
para.ligation = Parameters.build(ligationData.hives, ["default", ...argv.hives]);
|
||||
|
||||
if (argv.excludedCharRanges) para.excludedCodePointRanges = argv.excludedCharRanges;
|
||||
if (argv.compatibilityLigatures) para.compLig = argv.compatibilityLigatures;
|
||||
if (argv.metricOverride) Parameters.applymetricOverride(para, argv.metricOverride);
|
||||
|
||||
para.naming = {
|
||||
family: argv.family,
|
||||
version: argv.version,
|
||||
weight: argv.menuWeight - 0,
|
||||
width: argv.menuWidth - 0,
|
||||
slant: argv.menuSlant
|
||||
family: argv.menu.family,
|
||||
version: argv.menu.version,
|
||||
weight: argv.menu.weight - 0,
|
||||
width: argv.menu.width - 0,
|
||||
slant: argv.menu.slant
|
||||
};
|
||||
|
||||
return para;
|
||||
|
@ -55,7 +59,7 @@ async function getParameters(argv) {
|
|||
|
||||
async function tryParseToml(str) {
|
||||
try {
|
||||
return toml.parse(await fs.readFile(str, "utf-8"));
|
||||
return Toml.parse(await fs.readFile(str, "utf-8"));
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Failed to parse configuration file ${str}.\nPlease validate whether there's syntax error.\n${e}`
|
||||
|
@ -101,5 +105,5 @@ async function saveCharMap(argv, font) {
|
|||
glyph.featureSelector ? Object.keys(glyph.featureSelector) : []
|
||||
]);
|
||||
}
|
||||
await fs.writeFile(argv.charmap, JSON.stringify(charMap), "utf8");
|
||||
await fs.writeFile(argv.oCharMap, JSON.stringify(charMap), "utf8");
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ define GDEF_MARK 3
|
|||
define [interpretLookups gs lutns lookups] : begin
|
||||
foreach [lutn : items-of lutns] : begin
|
||||
local lut lookups.(lutn)
|
||||
interpretLookup gs lut lookups
|
||||
if lut : interpretLookup gs lut lookups
|
||||
|
||||
define [interpretLookup gs lut lookups] : match lut.type
|
||||
"gsub_chaining" : begin
|
||||
|
@ -59,15 +59,20 @@ export : define [BuildCompatLigatures glyphs glyphList unicodeGlyphs GSUB GDEF c
|
|||
foreach [cldef : items-of config] : do
|
||||
if [not cldef.unicode] : break nothing
|
||||
if [not cldef.featureTag] : break nothing
|
||||
if [not GSUB.features.(cldef.featureTag)] : break nothing
|
||||
if [not cldef.sequence] : break nothing
|
||||
|
||||
local feature null
|
||||
foreach [fn : items-of GSUB.languages.'DFLT_DFLT'.features]
|
||||
if (cldef.featureTag === [fn.slice 0 4]) : set feature GSUB.features.(fn)
|
||||
|
||||
if [not feature] : break nothing
|
||||
|
||||
local gnames {}
|
||||
for [local j 0] [j < cldef.sequence.length] [inc j] : begin
|
||||
if [not unicodeGlyphs.[cldef.sequence.charCodeAt j]] : break nothing
|
||||
gnames.push unicodeGlyphs.[cldef.sequence.charCodeAt j].name
|
||||
|
||||
interpretLookups gnames GSUB.features.(cldef.featureTag) GSUB.lookups
|
||||
interpretLookups gnames feature GSUB.lookups
|
||||
|
||||
local g1 : new Glyph ('$clig.' + cldef.unicode)
|
||||
set g1.advanceWidth 0
|
||||
|
@ -84,4 +89,3 @@ export : define [BuildCompatLigatures glyphs glyphList unicodeGlyphs GSUB GDEF c
|
|||
set unicodeGlyphs.(cldef.unicode) g1
|
||||
glyphList.push g1
|
||||
set GDEF.glyphClassDef.(g1.name) GDEF_LIGATURE
|
||||
|
||||
|
|
|
@ -307,6 +307,11 @@ enableLigation = false
|
|||
[no-cv-ss]
|
||||
enableCvSs = false
|
||||
|
||||
###################################################################################################
|
||||
### Metric-override hives
|
||||
### These hives are now discouraged in favor of 'metric-override' in build plans but they are
|
||||
### still supported in version 3.x.
|
||||
|
||||
###### Leading
|
||||
[leading-750]
|
||||
leading = 750
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
[buildPlans.iosevka-custom] # <iosevka-custom> is your plan name
|
||||
family = "Iosevka Custom" # Font menu family name
|
||||
design = ["leading-1500", "v-i-hooky", "v-l-hooky"] # Customize styles
|
||||
hintParams = ["-a", "sss"] # Optional custom parameters for ttfautohint
|
||||
|
||||
[buildPlans.iosevka-custom] # <iosevka-custom> is your plan name
|
||||
family = "Iosevka Custom" # Font menu family name
|
||||
design = ["v-i-hooky", "v-l-hooky"] # Customize styles
|
||||
# upright = ["upright-styles"] # Uncomment this line to set styles for upright only
|
||||
# italic = ["italic-styles"] # Uncomment this line to set styles for italic only
|
||||
# oblique = ["oblique-styles"] # Uncomment this line to set styles for oblique only
|
||||
hintParams = ["-a", "sss"] # Optional custom parameters for ttfautohint
|
||||
|
||||
###################################################################################################
|
||||
# Override default building weights
|
||||
# When buildPlans.<plan name>.weights is absent, all weights would built and mapped to
|
||||
# default values.
|
||||
|
@ -25,19 +28,24 @@ css = 450
|
|||
shape = 700
|
||||
menu = 700
|
||||
css = 700
|
||||
|
||||
# End weight section
|
||||
###################################################################################################
|
||||
|
||||
|
||||
###################################################################################################
|
||||
# Override default building slant sets
|
||||
# Format: <upright|italic|oblique> = <"normal"|"italic"|"oblique">
|
||||
# When this section is absent, all slants would be built.
|
||||
|
||||
[buildPlans.iosevka-custom.slants]
|
||||
upright = "normal"
|
||||
italic = "italic"
|
||||
oblique = "oblique"
|
||||
|
||||
# End slant section
|
||||
###################################################################################################
|
||||
|
||||
|
||||
###################################################################################################
|
||||
# Override default building widths
|
||||
# When buildPlans.<plan name>.widths is absent, all widths would built and mapped to
|
||||
# default values.
|
||||
|
@ -45,6 +53,7 @@ oblique = "oblique"
|
|||
# support 1, 2, 3, 4, 5, 6, 7, 8, 9.
|
||||
# If you decide to use custom weights you have to define all the weights you
|
||||
# plan to use otherwise they will not be built.
|
||||
|
||||
[buildPlans.iosevka-custom.widths.normal]
|
||||
shape = 5 # Width of glyph shapes.
|
||||
menu = 5 # Width for the font's names.
|
||||
|
@ -54,4 +63,49 @@ css = "normal" # "font-stretch' property of webfont CSS.
|
|||
shape = 7
|
||||
menu = 7
|
||||
css = "expanded"
|
||||
|
||||
# End width section
|
||||
###################################################################################################
|
||||
|
||||
###################################################################################################
|
||||
# Character Exclusion
|
||||
# Specify character ranges in the section below to exclude certain characters from the font being
|
||||
# built. Remove this section when this feature is not needed.
|
||||
|
||||
[buildPlans.iosevka-custom.exclude-chars]
|
||||
ranges = [[10003, 10008]]
|
||||
|
||||
# End character exclusion
|
||||
###################################################################################################
|
||||
|
||||
###################################################################################################
|
||||
# Compatibility Ligatures
|
||||
# Certain applications like Emacs does not support proper programming liagtures provided by
|
||||
# OpenType, but can support ligatures provided by PUA codepoints. Therefore you can edit the
|
||||
# following section to build PUA characters that are generated from the OpenType ligatures.
|
||||
# Remove this section when compatibility ligatures are not needed.
|
||||
|
||||
[[buildPlans.iosevka-custom.compatibility-ligatures]]
|
||||
unicode = 57600 # 0xE100
|
||||
featureTag = 'calt'
|
||||
sequence = '<*>'
|
||||
|
||||
# End compatibility ligatures section
|
||||
###################################################################################################
|
||||
|
||||
###################################################################################################
|
||||
# Metric overrides
|
||||
# Certain metrics like line height (leading) could be overridden in your build plan file.
|
||||
# Edit the values to change the metrics. Remove this section when overriding is not needed.
|
||||
|
||||
[buildPlans.iosevka-custom.metric-override]
|
||||
leading = 1250
|
||||
winMetricAscenderPad = 0
|
||||
winMetricDescenderPad = 0
|
||||
powerlineScaleY = 1
|
||||
powerlineScaleX = 1
|
||||
powerlineShiftY = 0
|
||||
powerlineShiftX = 0
|
||||
|
||||
# End metric override section
|
||||
###################################################################################################
|
||||
|
|
|
@ -22,3 +22,22 @@ export : define [build parametersData styles blendParams] : begin
|
|||
|
||||
foreach [style : items-of styles] : introStyle style
|
||||
return param
|
||||
|
||||
extern isFinite
|
||||
define [numericConfigExists x] : [isFinite x] && (x != null)
|
||||
|
||||
export : define [applymetricOverride para mo] : begin
|
||||
if [numericConfigExists mo.leading]
|
||||
set para.leading mo.leading
|
||||
if [numericConfigExists mo.winMetricAscenderPad]
|
||||
set para.winMetricAscenderPad mo.winMetricAscenderPad
|
||||
if [numericConfigExists mo.winMetricDescenderPad]
|
||||
set para.winMetricDescenderPad mo.winMetricDescenderPad
|
||||
if [numericConfigExists mo.powerlineScaleY]
|
||||
set para.powerlineScaleY mo.powerlineScaleY
|
||||
if [numericConfigExists mo.powerlineScaleX]
|
||||
set para.powerlineScaleX mo.powerlineScaleX
|
||||
if [numericConfigExists mo.powerlineShiftY]
|
||||
set para.powerlineShiftY mo.powerlineShiftY
|
||||
if [numericConfigExists mo.powerlineShiftX]
|
||||
set para.powerlineShiftX mo.powerlineShiftX
|
||||
|
|
|
@ -10,20 +10,20 @@ module.exports = function (output, family, hs, formats) {
|
|||
@font-face {
|
||||
font-family: '${family + " Web"}';
|
||||
font-display: swap;
|
||||
font-weight: ${term.cssWeight};
|
||||
font-stretch: ${term.cssStretch};
|
||||
font-style: ${term.cssStyle};
|
||||
font-weight: ${term.css.weight};
|
||||
font-stretch: ${term.css.stretch};
|
||||
font-style: ${term.css.style};
|
||||
src: ${src};
|
||||
}
|
||||
`;
|
||||
if (term.cssStyle === "oblique") {
|
||||
if (term.css.style === "oblique") {
|
||||
// CHROME hates a family with both Italic and Oblique
|
||||
ans += `
|
||||
@font-face {
|
||||
font-family: '${family + " Web Oblique"}';
|
||||
font-display: swap;
|
||||
font-weight: ${term.cssWeight};
|
||||
font-stretch: ${term.cssStretch};
|
||||
font-weight: ${term.css.weight};
|
||||
font-stretch: ${term.css.stretch};
|
||||
src: ${src};
|
||||
}
|
||||
`;
|
||||
|
|
355
verdafile.js
355
verdafile.js
|
@ -51,7 +51,7 @@ const Version = oracle(`metadata:version`, async () => {
|
|||
|
||||
async function tryParseToml(str) {
|
||||
try {
|
||||
return toml.parse(fs.readFileSync(str, "utf-8"));
|
||||
return JSON.parse(JSON.stringify(toml.parse(fs.readFileSync(str, "utf-8"))));
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Failed to parse configuration file ${str}.\n` +
|
||||
|
@ -69,33 +69,16 @@ const RawPlans = oracle(`metadata:raw-plans`, async target => {
|
|||
const privateBP = await tryParseToml(PRIVATE_BUILD_PLANS);
|
||||
Object.assign(bp.buildPlans, privateBP.buildPlans);
|
||||
}
|
||||
for (const prefix in bp.buildPlans) {
|
||||
const plan = bp.buildPlans[prefix];
|
||||
plan.prefix = prefix;
|
||||
|
||||
// Style groups
|
||||
if (!plan.pre) plan.pre = {};
|
||||
if (!plan.post) plan.post = {};
|
||||
|
||||
if (!plan.pre.design) plan.pre.design = plan.design || [];
|
||||
if (!plan.pre.upright) plan.pre.upright = plan.upright || [];
|
||||
if (!plan.pre.oblique) plan.pre.oblique = plan.oblique || [];
|
||||
if (!plan.pre.italic) plan.pre.italic = plan.italic || [];
|
||||
|
||||
if (!plan.post.design) plan.post.design = [];
|
||||
if (!plan.post.upright) plan.post.upright = [];
|
||||
if (!plan.post.oblique) plan.post.oblique = [];
|
||||
if (!plan.post.italic) plan.post.italic = [];
|
||||
}
|
||||
for (const prefix in bp.collectPlans) {
|
||||
bp.collectPlans[prefix].prefix = prefix;
|
||||
}
|
||||
return bp;
|
||||
});
|
||||
|
||||
const BuildPlans = computed("metadata:build-plans", async target => {
|
||||
const RawCollectPlans = computed("metadata:raw-collect-plans", async target => {
|
||||
const [rp] = await target.need(RawPlans);
|
||||
return rp.buildPlans;
|
||||
return rp.collectPlans;
|
||||
});
|
||||
const CollectConfig = computed("metadata:collect-config", async target => {
|
||||
const [rp] = await target.need(RawPlans);
|
||||
return rp.collectConfig;
|
||||
});
|
||||
const ExportPlans = computed("metadata:export-plans", async target => {
|
||||
const [rp] = await target.need(RawCollectPlans);
|
||||
|
@ -105,27 +88,130 @@ const ExportPlans = computed("metadata:export-plans", async target => {
|
|||
}
|
||||
return result;
|
||||
});
|
||||
const RawCollectPlans = computed("metadata:raw-collect-plans", async target => {
|
||||
|
||||
const BuildPlans = computed("metadata:build-plans", async target => {
|
||||
const [rp] = await target.need(RawPlans);
|
||||
return rp.collectPlans;
|
||||
const rawBuildPlans = rp.buildPlans;
|
||||
|
||||
const returnBuildPlans = {};
|
||||
const fileNameToBpMap = {};
|
||||
for (const prefix in rawBuildPlans) {
|
||||
const bp = { ...rawBuildPlans[prefix] };
|
||||
shimBuildPlans(bp, rp.weights, rp.slants, rp.widths);
|
||||
|
||||
bp.targets = [];
|
||||
const suffixMapping = getSuffixMapping(bp.weights, bp.slants, bp.widths);
|
||||
for (const suffix in suffixMapping) {
|
||||
const sfi = suffixMapping[suffix];
|
||||
if (bp.weights && !bp.weights[sfi.weight]) continue;
|
||||
if (bp.slants && !bp.slants[sfi.slant]) continue;
|
||||
const fileName = [prefix, suffix].join("-");
|
||||
bp.targets.push(fileName);
|
||||
fileNameToBpMap[fileName] = { prefix, suffix };
|
||||
}
|
||||
returnBuildPlans[prefix] = bp;
|
||||
}
|
||||
return { fileNameToBpMap, buildPlans: returnBuildPlans };
|
||||
});
|
||||
const Weights = computed("metadata:global-weights", async target => {
|
||||
const [rp] = await target.need(RawPlans);
|
||||
return rp.weights;
|
||||
|
||||
function shimBuildPlans(bp, dWeights, dSlants, dWidths) {
|
||||
if (!bp.pre) bp.pre = {};
|
||||
if (!bp.post) bp.post = {};
|
||||
|
||||
if (!bp.pre.design) bp.pre.design = bp.design || [];
|
||||
if (!bp.pre.upright) bp.pre.upright = bp.upright || [];
|
||||
if (!bp.pre.oblique) bp.pre.oblique = bp.oblique || [];
|
||||
if (!bp.pre.italic) bp.pre.italic = bp.italic || [];
|
||||
|
||||
if (!bp.post.design) bp.post.design = [];
|
||||
if (!bp.post.upright) bp.post.upright = [];
|
||||
if (!bp.post.oblique) bp.post.oblique = [];
|
||||
if (!bp.post.italic) bp.post.italic = [];
|
||||
|
||||
bp.weights = bp.weights || dWeights;
|
||||
bp.slants = bp.slants || dSlants;
|
||||
bp.widths = bp.widths || dWidths;
|
||||
}
|
||||
|
||||
const BuildPlanOf = computed.group("metadata:build-plan-of", async (target, gid) => {
|
||||
const [{ buildPlans }] = await target.need(BuildPlans);
|
||||
const plan = buildPlans[gid];
|
||||
if (!plan) fail(`Build plan for '${gid}' not found.` + whyBuildPlanIsnNotThere(gid));
|
||||
return plan;
|
||||
});
|
||||
const Slants = computed("metadata:global-slants", async target => {
|
||||
const [rp] = await target.need(RawPlans);
|
||||
return rp.slants;
|
||||
|
||||
const GroupFontsOf = computed.group("metadata:group-fonts-of", async (target, gid) => {
|
||||
const [plan] = await target.need(BuildPlanOf(gid));
|
||||
return plan.targets;
|
||||
});
|
||||
const Widths = computed("metadata:global-widths", async target => {
|
||||
const [rp] = await target.need(RawPlans);
|
||||
return rp.widths;
|
||||
});
|
||||
const CollectConfig = computed("metadata:collect-config", async target => {
|
||||
const [rp] = await target.need(RawPlans);
|
||||
return rp.collectConfig;
|
||||
|
||||
const FontInfoOf = computed.group("metadata:font-info-of", async (target, fileName) => {
|
||||
const [{ fileNameToBpMap, buildPlans }] = await target.need(BuildPlans);
|
||||
const [version] = await target.need(Version);
|
||||
|
||||
const fi0 = fileNameToBpMap[fileName];
|
||||
if (!fi0) fail(`Build plan for '${fileName}' not found.` + whyBuildPlanIsnNotThere(fileName));
|
||||
|
||||
const bp = buildPlans[fi0.prefix];
|
||||
if (!bp) fail(`Build plan for '${fileName}' not found.` + whyBuildPlanIsnNotThere(fileName));
|
||||
|
||||
const sfi = getSuffixMapping(bp.weights, bp.slants, bp.widths)[fi0.suffix];
|
||||
const preHives = [...bp.pre.design, ...bp.pre[sfi.slant]];
|
||||
const postHives = [...bp.post.design, ...bp.post[sfi.slant]];
|
||||
return {
|
||||
name: fileName,
|
||||
hives: ["iosevka", ...preHives, ...sfi.hives, ...postHives],
|
||||
shape: {
|
||||
weight: sfi.shapeWeight,
|
||||
width: sfi.shapeWidth
|
||||
},
|
||||
menu: {
|
||||
family: bp.family,
|
||||
version: version,
|
||||
width: sfi.menuWidth,
|
||||
slant: sfi.menuSlant,
|
||||
weight: sfi.menuWeight
|
||||
},
|
||||
css: {
|
||||
weight: sfi.cssWeight,
|
||||
stretch: sfi.cssStretch,
|
||||
style: sfi.cssStyle
|
||||
},
|
||||
hintParams: bp.hintParams || [],
|
||||
compatibilityLigatures: bp["compatibility-ligatures"] || null,
|
||||
metricOverride: bp["metric-override"] || null,
|
||||
excludedCharRanges: bp["exclude-chars"] ? bp["exclude-chars"].ranges || null : null
|
||||
};
|
||||
});
|
||||
|
||||
function getSuffixMapping(weights, slants, widths) {
|
||||
const mapping = {};
|
||||
for (const w in weights) {
|
||||
validateRecommendedWeight(w, weights[w].menu, "Menu");
|
||||
validateRecommendedWeight(w, weights[w].css, "CSS");
|
||||
for (const s in slants) {
|
||||
for (const wd in widths) {
|
||||
const suffix = makeSuffix(w, wd, s, "regular");
|
||||
mapping[suffix] = {
|
||||
hives: [`shapeWeight`, `s-${s}`, `wd-${widths[wd].shape}`],
|
||||
weight: w,
|
||||
shapeWeight: nValidate("Shape weight of " + w, weights[w].shape, vlShapeWeight),
|
||||
cssWeight: nValidate("CSS weight of " + w, weights[w].css, vlCssWeight),
|
||||
menuWeight: nValidate("Menu weight of " + w, weights[w].menu, vlMenuWeight),
|
||||
width: wd,
|
||||
shapeWidth: nValidate("Shape width of " + wd, widths[wd].shape, vlShapeWidth),
|
||||
cssStretch: widths[wd].css || wd,
|
||||
menuWidth: nValidate("Menu width of " + wd, widths[wd].menu, vlMenuWidth),
|
||||
slant: s,
|
||||
cssStyle: slants[s] || s,
|
||||
menuSlant: slants[s] || s
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
|
||||
function makeSuffix(w, wd, s, fallback) {
|
||||
return (
|
||||
(wd === "normal" ? "" : wd) + (w === "regular" ? "" : w) + (s === "upright" ? "" : s) ||
|
||||
|
@ -133,6 +219,15 @@ function makeSuffix(w, wd, s, fallback) {
|
|||
);
|
||||
}
|
||||
|
||||
function validateRecommendedWeight(w, value, label) {
|
||||
if (recommendedMenuWeights[w] && recommendedMenuWeights[w] !== value) {
|
||||
echo.warn(
|
||||
`${label} weight settings of ${w} ( = ${value}) doesn't match ` +
|
||||
`the recommended value ( = ${recommendedMenuWeights[w]}).`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function nValidate(key, v, f) {
|
||||
if (typeof v !== "number" || !isFinite(v) || (f && !f(v))) {
|
||||
throw new TypeError(`${key} = "${v}" is not a valid number.`);
|
||||
|
@ -166,113 +261,50 @@ const recommendedMenuWeights = {
|
|||
extrabold: 800,
|
||||
heavy: 900
|
||||
};
|
||||
function validateRecommendedWeight(w, value, label) {
|
||||
if (recommendedMenuWeights[w] && recommendedMenuWeights[w] !== value) {
|
||||
echo.warn(
|
||||
`${label} weight settings of ${w} ( = ${value}) doesn't match ` +
|
||||
`the recommended value ( = ${recommendedMenuWeights[w]}).`
|
||||
);
|
||||
}
|
||||
|
||||
function whyBuildPlanIsnNotThere(gid) {
|
||||
if (!fs.existsSync(PRIVATE_BUILD_PLANS))
|
||||
return "\n -- Possible reason: Config file 'private-build-plans.toml' does not exist.";
|
||||
return "";
|
||||
}
|
||||
|
||||
function getSuffixSet(weights, slants, widths) {
|
||||
const mapping = {};
|
||||
for (const w in weights) {
|
||||
validateRecommendedWeight(w, weights[w].menu, "Menu");
|
||||
validateRecommendedWeight(w, weights[w].css, "CSS");
|
||||
for (const s in slants) {
|
||||
for (const wd in widths) {
|
||||
const suffix = makeSuffix(w, wd, s, "regular");
|
||||
mapping[suffix] = {
|
||||
hives: [`shapeWeight`, `s-${s}`, `wd-${widths[wd].shape}`],
|
||||
weight: w,
|
||||
shapeWeight: nValidate("Shape weight of " + w, weights[w].shape, vlShapeWeight),
|
||||
cssWeight: nValidate("CSS weight of " + w, weights[w].css, vlCssWeight),
|
||||
menuWeight: nValidate("Menu weight of " + w, weights[w].menu, vlMenuWeight),
|
||||
width: wd,
|
||||
shapeWidth: nValidate("Shape width of " + wd, widths[wd].shape, vlShapeWidth),
|
||||
cssStretch: widths[wd].css || wd,
|
||||
menuWidth: nValidate("Menu width of " + wd, widths[wd].menu, vlMenuWidth),
|
||||
slant: s,
|
||||
cssStyle: slants[s] || s,
|
||||
menuSlant: slants[s] || s
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
|
||||
const Suffixes = computed(`metadata:suffixes`, async target => {
|
||||
const [weights, slants, widths] = await target.need(Weights, Slants, Widths);
|
||||
return getSuffixSet(weights, slants, widths);
|
||||
const CollectPlans = computed(`metadata:collect-plans`, async target => {
|
||||
const [rawCollectPlans, suffixMapping, collectConfig] = await target.need(
|
||||
RawCollectPlans,
|
||||
StandardSuffixes,
|
||||
CollectConfig
|
||||
);
|
||||
return await getCollectPlans(
|
||||
target,
|
||||
rawCollectPlans,
|
||||
suffixMapping,
|
||||
collectConfig,
|
||||
fnStandardTtc
|
||||
);
|
||||
});
|
||||
|
||||
const FontBuildingParameters = computed(`metadata:font-building-parameters`, async target => {
|
||||
const [buildPlans, defaultWeights, defaultSlants, defaultWidths] = await target.need(
|
||||
BuildPlans,
|
||||
Weights,
|
||||
Slants,
|
||||
Widths
|
||||
);
|
||||
const fontInfos = {};
|
||||
const bp = {};
|
||||
for (const p in buildPlans) {
|
||||
const { pre, post, prefix, family, weights, slants, widths, hintParams } = buildPlans[p];
|
||||
const targets = [];
|
||||
const suffixMapping = getSuffixSet(
|
||||
weights || defaultWeights,
|
||||
slants || defaultSlants,
|
||||
widths || defaultWidths
|
||||
);
|
||||
for (const suffix in suffixMapping) {
|
||||
if (weights && !weights[suffixMapping[suffix].weight]) continue;
|
||||
if (slants && !slants[suffixMapping[suffix].slant]) continue;
|
||||
const fileName = [prefix, suffix].join("-");
|
||||
const preHives = [...pre.design, ...pre[suffixMapping[suffix].slant]];
|
||||
const postHives = [...post.design, ...post[suffixMapping[suffix].slant]];
|
||||
fontInfos[fileName] = {
|
||||
name: fileName,
|
||||
family,
|
||||
hives: ["iosevka", ...preHives, ...suffixMapping[suffix].hives, ...postHives],
|
||||
shapeWeight: suffixMapping[suffix].shapeWeight,
|
||||
shapeWidth: suffixMapping[suffix].shapeWidth,
|
||||
menuWeight: suffixMapping[suffix].menuWeight,
|
||||
menuWidth: suffixMapping[suffix].menuWidth,
|
||||
menuSlant: suffixMapping[suffix].menuSlant,
|
||||
cssWeight: suffixMapping[suffix].cssWeight,
|
||||
cssStretch: suffixMapping[suffix].cssStretch,
|
||||
cssStyle: suffixMapping[suffix].cssStyle,
|
||||
hintParams: hintParams || []
|
||||
};
|
||||
targets.push(fileName);
|
||||
}
|
||||
bp[prefix] = {
|
||||
family,
|
||||
prefix,
|
||||
targets
|
||||
};
|
||||
}
|
||||
return { fontInfos, buildPlans: bp };
|
||||
const StandardSuffixes = computed(`metadata:standard-suffixes`, async target => {
|
||||
const [rp] = await target.need(RawPlans);
|
||||
return getSuffixMapping(rp.weights, rp.slants, rp.widths);
|
||||
});
|
||||
|
||||
async function getCollectPlans(target, rawCollectPlans, suffixMapping, config, fnFileName) {
|
||||
const ttcComposition = {},
|
||||
ttcContents = {},
|
||||
groupDecomposition = {};
|
||||
for (const gid in rawCollectPlans) {
|
||||
for (const collectPrefix in rawCollectPlans) {
|
||||
const groupFileList = new Set();
|
||||
const collect = rawCollectPlans[gid];
|
||||
const collect = rawCollectPlans[collectPrefix];
|
||||
if (!collect || !collect.from || !collect.from.length) continue;
|
||||
|
||||
for (const prefix of collect.from) {
|
||||
const [gri] = await target.need(GroupInfo(prefix));
|
||||
const [gri] = await target.need(BuildPlanOf(prefix));
|
||||
const ttfFileNameSet = new Set(gri.targets);
|
||||
for (const suffix in suffixMapping) {
|
||||
const gr = suffixMapping[suffix];
|
||||
const ttcFileName = fnFileName(
|
||||
config,
|
||||
collect.prefix,
|
||||
collectPrefix,
|
||||
gr.weight,
|
||||
gr.width,
|
||||
gr.slant
|
||||
|
@ -285,8 +317,8 @@ async function getCollectPlans(target, rawCollectPlans, suffixMapping, config, f
|
|||
groupFileList.add(ttcFileName);
|
||||
}
|
||||
}
|
||||
ttcContents[gid] = [...groupFileList];
|
||||
groupDecomposition[gid] = [...collect.from];
|
||||
ttcContents[collectPrefix] = [...groupFileList];
|
||||
groupDecomposition[collectPrefix] = [...collect.from];
|
||||
}
|
||||
return { ttcComposition, ttcContents, groupDecomposition };
|
||||
}
|
||||
|
@ -300,46 +332,6 @@ function fnStandardTtc(collectConfig, prefix, w, wd, s) {
|
|||
return `${prefix}-${ttcSuffix}`;
|
||||
}
|
||||
|
||||
const CollectPlans = computed(`metadata:collect-plans`, async target => {
|
||||
const [rawCollectPlans, suffixMapping, collectConfig] = await target.need(
|
||||
RawCollectPlans,
|
||||
Suffixes,
|
||||
CollectConfig
|
||||
);
|
||||
return await getCollectPlans(
|
||||
target,
|
||||
rawCollectPlans,
|
||||
suffixMapping,
|
||||
collectConfig,
|
||||
fnStandardTtc
|
||||
);
|
||||
});
|
||||
|
||||
const HivesOf = computed.group("metadata:hives-of", async (target, gid) => {
|
||||
const [{ fontInfos }] = await target.need(FontBuildingParameters);
|
||||
const hvs = fontInfos[gid];
|
||||
if (!hvs) fail(`Build plan for '${gid}' not found.` + whyBuildPlanIsnNotThere(gid));
|
||||
return hvs;
|
||||
});
|
||||
|
||||
const GroupInfo = computed.group("metadata:group-info", async (target, gid) => {
|
||||
const [{ buildPlans }] = await target.need(FontBuildingParameters);
|
||||
const plan = buildPlans[gid];
|
||||
if (!plan) fail(`Build plan for '${gid}' not found.` + whyBuildPlanIsnNotThere(gid));
|
||||
return plan;
|
||||
});
|
||||
|
||||
function whyBuildPlanIsnNotThere(gid) {
|
||||
if (!fs.existsSync(PRIVATE_BUILD_PLANS))
|
||||
return "\n -- Possible reason: Config file 'private-build-plans.toml' does not exist.";
|
||||
return "";
|
||||
}
|
||||
|
||||
const GroupFontsOf = computed.group("metadata:group-fonts-of", async (target, gid) => {
|
||||
const [plan] = await target.need(GroupInfo(gid));
|
||||
return plan.targets;
|
||||
});
|
||||
|
||||
const CollectionPartsOf = computed.group("metadata:collection-parts-of", async (target, id) => {
|
||||
const [{ ttcComposition }] = await target.need(CollectPlans);
|
||||
return ttcComposition[id];
|
||||
|
@ -351,24 +343,11 @@ const CollectionPartsOf = computed.group("metadata:collection-parts-of", async (
|
|||
|
||||
const BuildOTD = file.make(
|
||||
(gr, fn) => `${BUILD}/${gr}/${fn}.otd`,
|
||||
async (target, output, _gr, fn) => {
|
||||
const [
|
||||
{ hives, family, shapeWeight, menuWeight, menuSlant, menuWidth },
|
||||
version
|
||||
] = await target.need(HivesOf(fn), Version);
|
||||
async (target, output, gr, fn) => {
|
||||
const [fi] = await target.need(FontInfoOf(fn), Version);
|
||||
const charmap = output.dir + "/" + output.name + ".charmap";
|
||||
await target.need(Scripts, fu`parameters.toml`, de`${output.dir}`);
|
||||
await node("gen/index", {
|
||||
o: output.full,
|
||||
charmap,
|
||||
family,
|
||||
version,
|
||||
shapeWeight,
|
||||
menuWeight,
|
||||
menuSlant,
|
||||
menuWidth,
|
||||
hives
|
||||
});
|
||||
await node("gen/index", { o: output.full, oCharMap: charmap, ...fi });
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -407,7 +386,7 @@ const DistUnhintedTTF = file.make(
|
|||
const DistHintedTTF = file.make(
|
||||
(gr, fn) => `${DIST}/${gr}/ttf/${fn}.ttf`,
|
||||
async (target, path, gr, f) => {
|
||||
const [{ hintParams }] = await target.need(HivesOf(f));
|
||||
const [{ hintParams }] = await target.need(FontInfoOf(f));
|
||||
const [from] = await target.need(BuildTTF(gr, f), de`${path.dir}`);
|
||||
await run("ttfautohint", hintParams, from.full, path.full);
|
||||
}
|
||||
|
@ -453,8 +432,8 @@ const DistWebFontCSS = file.make(
|
|||
gid => `${DIST}/${gid}/${gid}.css`,
|
||||
async (target, out, gid) => {
|
||||
// Note: this target does NOT depend on the font files.
|
||||
const [gr, ts] = await target.need(GroupInfo(gid), GroupFontsOf(gid), de(out.dir));
|
||||
const hs = await target.need(...ts.map(HivesOf));
|
||||
const [gr, ts] = await target.need(BuildPlanOf(gid), GroupFontsOf(gid), de(out.dir));
|
||||
const hs = await target.need(...ts.map(FontInfoOf));
|
||||
await node("utility/make-webfont-css.js", out.full, gr.family, hs, webfontFormats);
|
||||
}
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue