Add support for slope customization (#599, #1165).

This commit is contained in:
be5invis 2021-07-23 21:23:08 -07:00
parent 89bbcd4a17
commit 5d38569238
11 changed files with 232 additions and 177 deletions

146
README.md
View file

@ -53,156 +53,137 @@ Monospace Iosevka contains various stylistic sets to change the shape of certain
<table>
<tr>
<td><code>ss01</code></td>
<td colspan="3">Andale Mono Style</td>
<td colspan="2"><code>ss01</code> — Andale Mono Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss01-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss01-1.png"/></td>
<td><img src="images/stylistic-set-u-ss01-1.png"/></td>
<td><img src="images/stylistic-set-i-ss01-1.png"/></td>
</tr>
<tr>
<td><code>ss02</code></td>
<td colspan="3">Anonymous Pro Style</td>
<td colspan="2"><code>ss02</code> — Anonymous Pro Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss02-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss02-1.png"/></td>
<td><img src="images/stylistic-set-u-ss02-1.png"/></td>
<td><img src="images/stylistic-set-i-ss02-1.png"/></td>
</tr>
<tr>
<td><code>ss03</code></td>
<td colspan="3">Consolas Style</td>
<td colspan="2"><code>ss03</code> — Consolas Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss03-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss03-1.png"/></td>
<td><img src="images/stylistic-set-u-ss03-1.png"/></td>
<td><img src="images/stylistic-set-i-ss03-1.png"/></td>
</tr>
<tr>
<td><code>ss04</code></td>
<td colspan="3">Menlo Style</td>
<td colspan="2"><code>ss04</code> — Menlo Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss04-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss04-1.png"/></td>
<td><img src="images/stylistic-set-u-ss04-1.png"/></td>
<td><img src="images/stylistic-set-i-ss04-1.png"/></td>
</tr>
<tr>
<td><code>ss05</code></td>
<td colspan="3">Fira Mono Style</td>
<td colspan="2"><code>ss05</code> — Fira Mono Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss05-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss05-1.png"/></td>
<td><img src="images/stylistic-set-u-ss05-1.png"/></td>
<td><img src="images/stylistic-set-i-ss05-1.png"/></td>
</tr>
<tr>
<td><code>ss06</code></td>
<td colspan="3">Liberation Mono Style</td>
<td colspan="2"><code>ss06</code> — Liberation Mono Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss06-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss06-1.png"/></td>
<td><img src="images/stylistic-set-u-ss06-1.png"/></td>
<td><img src="images/stylistic-set-i-ss06-1.png"/></td>
</tr>
<tr>
<td><code>ss07</code></td>
<td colspan="3">Monaco Style</td>
<td colspan="2"><code>ss07</code> — Monaco Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss07-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss07-1.png"/></td>
<td><img src="images/stylistic-set-u-ss07-1.png"/></td>
<td><img src="images/stylistic-set-i-ss07-1.png"/></td>
</tr>
<tr>
<td><code>ss08</code></td>
<td colspan="3">Pragmata Pro Style</td>
<td colspan="2"><code>ss08</code> — Pragmata Pro Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss08-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss08-1.png"/></td>
<td><img src="images/stylistic-set-u-ss08-1.png"/></td>
<td><img src="images/stylistic-set-i-ss08-1.png"/></td>
</tr>
<tr>
<td><code>ss09</code></td>
<td colspan="3">Source Code Pro Style</td>
<td colspan="2"><code>ss09</code> — Source Code Pro Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss09-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss09-1.png"/></td>
<td><img src="images/stylistic-set-u-ss09-1.png"/></td>
<td><img src="images/stylistic-set-i-ss09-1.png"/></td>
</tr>
<tr>
<td><code>ss10</code></td>
<td colspan="3">Envy Code R Style</td>
<td colspan="2"><code>ss10</code> — Envy Code R Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss10-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss10-1.png"/></td>
<td><img src="images/stylistic-set-u-ss10-1.png"/></td>
<td><img src="images/stylistic-set-i-ss10-1.png"/></td>
</tr>
<tr>
<td><code>ss11</code></td>
<td colspan="3">X Window Style</td>
<td colspan="2"><code>ss11</code> — X Window Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss11-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss11-1.png"/></td>
<td><img src="images/stylistic-set-u-ss11-1.png"/></td>
<td><img src="images/stylistic-set-i-ss11-1.png"/></td>
</tr>
<tr>
<td><code>ss12</code></td>
<td colspan="3">Ubuntu Mono Style</td>
<td colspan="2"><code>ss12</code> — Ubuntu Mono Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss12-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss12-1.png"/></td>
<td><img src="images/stylistic-set-u-ss12-1.png"/></td>
<td><img src="images/stylistic-set-i-ss12-1.png"/></td>
</tr>
<tr>
<td><code>ss13</code></td>
<td colspan="3">Lucida Style</td>
<td colspan="2"><code>ss13</code> — Lucida Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss13-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss13-1.png"/></td>
<td><img src="images/stylistic-set-u-ss13-1.png"/></td>
<td><img src="images/stylistic-set-i-ss13-1.png"/></td>
</tr>
<tr>
<td><code>ss14</code></td>
<td colspan="3">JetBrains Mono Style</td>
<td colspan="2"><code>ss14</code> — JetBrains Mono Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss14-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss14-1.png"/></td>
<td><img src="images/stylistic-set-u-ss14-1.png"/></td>
<td><img src="images/stylistic-set-i-ss14-1.png"/></td>
</tr>
<tr>
<td><code>ss15</code></td>
<td colspan="3">IBM Plex Mono Style</td>
<td colspan="2"><code>ss15</code> — IBM Plex Mono Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss15-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss15-1.png"/></td>
<td><img src="images/stylistic-set-u-ss15-1.png"/></td>
<td><img src="images/stylistic-set-i-ss15-1.png"/></td>
</tr>
<tr>
<td><code>ss16</code></td>
<td colspan="3">PT Mono Style</td>
<td colspan="2"><code>ss16</code> — PT Mono Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss16-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss16-1.png"/></td>
<td><img src="images/stylistic-set-u-ss16-1.png"/></td>
<td><img src="images/stylistic-set-i-ss16-1.png"/></td>
</tr>
<tr>
<td><code>ss17</code></td>
<td colspan="3">Recursive Mono Style</td>
<td colspan="2"><code>ss17</code> — Recursive Mono Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss17-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss17-1.png"/></td>
<td><img src="images/stylistic-set-u-ss17-1.png"/></td>
<td><img src="images/stylistic-set-i-ss17-1.png"/></td>
</tr>
<tr>
<td><code>ss18</code></td>
<td colspan="3">Input Mono Style</td>
<td colspan="2"><code>ss18</code> — Input Mono Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss18-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss18-1.png"/></td>
<td><img src="images/stylistic-set-u-ss18-1.png"/></td>
<td><img src="images/stylistic-set-i-ss18-1.png"/></td>
</tr>
<tr>
<td><code>ss20</code></td>
<td colspan="3">Curly Style</td>
<td colspan="2"><code>ss20</code> — Curly Style</td>
</tr>
<tr>
<td colspan="2"><img src="images/stylistic-set-u-ss20-1.png"/></td>
<td colspan="2"><img src="images/stylistic-set-i-ss20-1.png"/></td>
<td><img src="images/stylistic-set-u-ss20-1.png"/></td>
<td><img src="images/stylistic-set-i-ss20-1.png"/></td>
</tr>
</table>
@ -2971,16 +2952,23 @@ Subsection `variants` is used to configure character variants in the font. Prope
Subsection `weights` is used to change the weight grades that the custom family needs. It is a dictionary of sub-objects with properties:
* `shape`: Integer, configures the weight grade of the glyphs' shapes.
* `shape`: Number, configures the weight grade of the glyphs' shapes.
* `menu`: Integer, configures the weight grade used when naming fonts.
* `css`: Integer, configures the weight grade used in web font CSS.
Subsection `widths` is used to change the weight grades that the custom family needs. It is a dictionary of sub-objects with properties:
Subsection `widths` is used to change the width grades that the custom family needs. It is a dictionary of sub-objects with properties:
* `shape`: Integer, configures the width of the glyphs' shapes, measured in 1/1000 em.
* `shape`: Number, configures the width of the glyphs' shapes, measured in 1/1000 em.
* `menu`: Integer, configures the width grade used when naming fonts. The valid values are `1` to `9`, inclusive.
* `css`: String, configures the [font-stretch](https://developer.mozilla.org/en-US/docs/Web/CSS/font-stretch) value used in web font CSS.
Subsection `slopes` is used to change the slope angles and grades that the custom family needs. It is a dictionary of sub-objects with properties:
* `angle`: Number, configures the slope angle in degrees. The valid vales are `0` to `15`, inclusive.
* `shape`: String from `upright`, `italic` or `oblique`. Configures the slope used for variant selection.
* `menu`: String from `upright`, `italic` or `oblique`. Configures the slope grade used when naming fonts.
* `css`: String from `normal`, `italic` or `oblique`. Configures the [CSS font-style](https://developer.mozilla.org/zh-CN/docs/Web/CSS/font-style) value.
Subsection `slopes` is a simple string-to-string dictionary maps slopes (`upright`, `italic` or `oblique`) to [CSS font-style](https://developer.mozilla.org/zh-CN/docs/Web/CSS/font-style) values, represented in string.
#### Compatibility Ligatures

View file

@ -990,11 +990,6 @@ from = ["iosevka-aile"]
release = true
from = ["iosevka-etoile"]
[collectConfig]
distinguishWeights = true
distinguishWidths = false
distinguishSlope = false
###################################################################################################
# Weight mappings (style => shape weight, menu weight, CSS weight)
# Shape weight : affects the shape of the glyphs
@ -1048,13 +1043,24 @@ shape = 900
menu = 900
css = 900
# slope mappings (style => CSS)
# NOTE: This mapping does NOT affect the font's metadata, only affects
# the webfont CSS. Change `params/parameters.toml` instead.
[slopes]
upright = "normal"
oblique = "oblique"
italic = "italic"
# slope mappings (style => slope angle, shape slope grade, menu slope, CSS slope)
[slopes.upright]
angle = 0
shape = "upright"
menu = "upright"
css = "normal"
[slopes.oblique]
angle = 9.4
shape = "oblique"
menu = "oblique"
css = "oblique"
[slopes.italic]
angle = 9.4
shape = "italic"
menu = "italic"
css = "italic"
# Width mappings (style => shape width, menu width, CSS stretch)
# Shape width : affects the shape of the glyphs

22
changes/8.0.0.md Normal file
View file

@ -0,0 +1,22 @@
* \[**Breaking**\] Add support for slope customization (#599, #1165).
- Slope customization format has a major change, giving ability to customize slope grade used for variant selection, as well as slope angle.
- The format will look like this:
``` toml
[buildPlans.iosevka-custom.slopes.upright]
angle = 0 # Angle in degrees. Valid range [0, 15]
shape = "upright" # Slope grade used for shape selection. `upright` | `oblique` | `italic`
menu = "upright" # Slope grade used for naming. `upright` | `oblique` | `italic`
css = "normal" # Slope grade used for webfont CSS. `normal` | `oblique` | `italic`
[buildPlans.iosevka-custom.slopes.oblique]
angle = 9.4
shape = "oblique"
menu = "oblique"
css = "oblique"
[buildPlans.iosevka-custom.slopes.italic]
angle = 9.4
shape = "italic"
menu = "italic"
css = "italic"
```

View file

@ -26,6 +26,7 @@ async function getParameters() {
const PARAMETERS_TOML = path.resolve(__dirname, "../params/parameters.toml");
const WEIGHTS_TOML = path.resolve(__dirname, "../params/shape-weight.toml");
const WIDTHS_TOML = path.resolve(__dirname, "../params/shape-width.toml");
const SLOPES_TOML = path.resolve(__dirname, "../params/shape-slope.toml");
const PRIVATE_TOML = path.resolve(__dirname, "../params/private-parameters.toml");
const VARIANTS_TOML = path.resolve(__dirname, "../params/variants.toml");
const LIGATIONS_TOML = path.resolve(__dirname, "../params/ligation-set.toml");
@ -35,6 +36,7 @@ async function getParameters() {
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);

View file

@ -11,6 +11,7 @@ function initPara(data, argv) {
applyBlendingParam(argv, para, data, "shapeWeight", "weight");
applyBlendingParam(argv, para, data, "shapeWidth", "width");
applyBlendingParam(argv, para, data, "shapeSlopeAngle", "slopeAngle");
applyAlternatesParam(argv, para, data, "slope", "slope");
if (argv.featureControl.noCvSs) para.enableCvSs = false;

View file

@ -1,6 +1,6 @@
{
"name": "iosevka",
"version": "7.3.3",
"version": "8.0.0",
"main": "./font-src/index.js",
"scripts": {
"build": "node utility/ensure-verda-exists && verda -f verdafile.js",

View file

@ -127,8 +127,6 @@ diversityII = 0.50
###### Slopes
[slope-italic]
isItalic = true
slopeAngle = 9.4
[slope-oblique]
isOblique = true
slopeAngle = 9.4

8
params/shape-slope.toml Normal file
View file

@ -0,0 +1,8 @@
[shapeSlopeAngle.blend.0]
slopeAngle = 0
[shapeSlopeAngle.blend.-15]
slopeAngle = -15
[shapeSlopeAngle.blend.15]
slopeAngle = 15

View file

@ -67,14 +67,25 @@ css = 700
###################################################################################################
# Override default building slope sets
# Format: <upright|italic|oblique> = <"normal"|"italic"|"oblique">
# When this section is absent, all slopes would be built.
[buildPlans.iosevka-custom.slopes]
upright = "normal"
italic = "italic"
oblique = "oblique"
[buildPlans.iosevka-custom.slopes.upright]
angle = 0 # Angle in degrees. Valid range [0, 15]
shape = "upright" # Slope grade used for shape selection. `upright` | `oblique` | `italic`
menu = "upright" # Slope grade used for naming. `upright` | `oblique` | `italic`
css = "normal" # Slope grade used for webfont CSS. `normal` | `oblique` | `italic`
[buildPlans.iosevka-custom.slopes.oblique]
angle = 9.4
shape = "oblique"
menu = "oblique"
css = "oblique"
[buildPlans.iosevka-custom.slopes.italic]
angle = 9.4
shape = "italic"
menu = "italic"
css = "italic"
# End slope section
###################################################################################################

View file

@ -39,18 +39,13 @@ async function processSsOt() {
if (!ss.rank) continue;
{
md.log(`<tr>`);
md.log(`<td><code>${ss.tag}</code></td>`);
md.log(`<td colspan="3">${ss.description}</td>`);
md.log(`<td colspan="2"><code>${ss.tag}</code> — ${ss.description}</td>`);
md.log(`</tr>`);
}
{
md.log(`<tr>`);
md.log(
`<td colspan="2"><img src="images/stylistic-set-u-${ss.tag}-${ss.rank}.png"/></td>`
);
md.log(
`<td colspan="2"><img src="images/stylistic-set-i-${ss.tag}-${ss.rank}.png"/></td>`
);
md.log(`<td><img src="images/stylistic-set-u-${ss.tag}-${ss.rank}.png"/></td>`);
md.log(`<td><img src="images/stylistic-set-i-${ss.tag}-${ss.rank}.png"/></td>`);
md.log(`</tr>`);
}
}

View file

@ -36,7 +36,6 @@ const webfontFormatsPages = [["woff2", "woff2"]];
const WIDTH_NORMAL = "normal";
const WEIGHT_NORMAL = "regular";
const SLOPE_NORMAL = "upright";
const SLOPE_OBLIQUE = "oblique";
const DEFAULT_SUBFAMILY = "regular";
const BUILD_PLANS = "build-plans.toml";
@ -189,8 +188,9 @@ const FontInfoOf = computed.group("metadata:font-info-of", async (target, fileNa
serifs: bp.serifs || null,
spacing: bp.spacing || null,
weight: sfi.shapeWeight,
slope: sfi.slope,
width: sfi.shapeWidth
width: sfi.shapeWidth,
slope: sfi.shapeSlope,
slopeAngle: sfi.shapeSlopeAngle
},
// Menu
menu: {
@ -228,23 +228,28 @@ function getSuffixMapping(weights, slopes, widths) {
return mapping;
}
function getSuffixMappingItem(weights, w, slopes, s, widths, wd) {
const weightDef = wwsDefValidate("Weight definition of " + s, weights[w]);
const widthDef = wwsDefValidate("Width definition of " + s, widths[wd]);
const slopeDef = wwsDefValidate("Slope definition of " + s, slopes[s]);
return {
// Weights
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),
shapeWeight: nValidate("Shape weight of " + w, weightDef.shape, VlShapeWeight),
cssWeight: nValidate("CSS weight of " + w, weightDef.css, VlCssWeight),
menuWeight: nValidate("Menu weight of " + w, weightDef.menu, VlMenuWeight),
// Widths
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),
shapeWidth: nValidate("Shape width of " + wd, widthDef.shape, VlShapeWidth),
cssStretch: sValidate("CSS stretch of " + wd, widthDef.css, VlCssFontStretch),
menuWidth: nValidate("Menu width of " + wd, widthDef.menu, VlMenuWidth),
// Slopes
slope: s,
cssStyle: slopes[s] || s,
menuSlope: slopes[s] || s
shapeSlope: sValidate("Shape slope of " + s, slopeDef.shape, VlShapeSlope),
shapeSlopeAngle: nValidate("Angle of " + s, slopeDef.angle, VlSlopeAngle),
cssStyle: sValidate("CSS style of " + s, slopeDef.css, VlCssStyle),
menuSlope: sValidate("Menu slope of " + s, slopeDef.menu, VlShapeSlope)
};
}
@ -366,18 +371,11 @@ const DistWoff2 = file.make(
const CollectPlans = computed(`metadata:collect-plans`, async target => {
const [rawPlans] = await target.need(RawPlans);
return await getCollectPlans(
target,
rawPlans.collectPlans,
rawPlans.collectConfig,
fnStandardTtc
);
return await getCollectPlans(target, rawPlans.collectPlans);
});
async function getCollectPlans(target, rawCollectPlans, config, fnFileName) {
const glyfTtcComposition = {},
ttcComposition = {},
plans = {};
async function getCollectPlans(target, rawCollectPlans) {
const plans = {};
let allCollectableGroups = new Set();
for (const collectPrefix in rawCollectPlans) {
@ -392,65 +390,60 @@ async function getCollectPlans(target, rawCollectPlans, config, fnFileName) {
}
for (const collectPrefix in amendedRawCollectPlans) {
const groupFileList = new Set();
const glyfTtcComposition = {};
const ttcComposition = {};
const collect = amendedRawCollectPlans[collectPrefix];
if (!collect || !collect.from || !collect.from.length) continue;
for (const prefix of collect.from) {
const [gri] = await target.need(BuildPlanOf(prefix));
const ttfFileNameSet = new Set(gri.targets);
const suffixMapping = getSuffixMapping(gri.weights, gri.slopes, gri.widths);
for (const suffix in suffixMapping) {
const sfi = suffixMapping[suffix];
const ttcFileName = fnFileName(
config,
collectPrefix,
sfi.weight,
sfi.width,
sfi.slope
);
const glyfTtcFileName = fnFileName(
{ ...config, distinguishWidths: true, distinguishWhetherUpright: true },
collectPrefix,
sfi.weight,
sfi.width,
sfi.slope
);
const suffixMap = getSuffixMapping(gri.weights, gri.slopes, gri.widths);
for (const suffix in suffixMap) {
const sfi = suffixMap[suffix];
const ttfTargetName = makeFileName(prefix, suffix);
if (!ttfFileNameSet.has(ttfTargetName)) continue;
const glyfTtcFileName = fnStandardTtc(true, collectPrefix, suffixMap, sfi);
if (!glyfTtcComposition[glyfTtcFileName]) glyfTtcComposition[glyfTtcFileName] = [];
glyfTtcComposition[glyfTtcFileName].push({ dir: prefix, file: ttfTargetName });
const ttcFileName = fnStandardTtc(false, collectPrefix, suffixMap, sfi);
if (!ttcComposition[ttcFileName]) ttcComposition[ttcFileName] = [];
ttcComposition[ttcFileName].push(glyfTtcFileName);
groupFileList.add(ttcFileName);
}
}
plans[collectPrefix] = {
ttcContents: [...groupFileList],
glyfTtcComposition,
ttcComposition,
groupDecomposition: [...collect.from],
inRelease: !!collect.release,
isAmended: !!collect.isAmended
};
}
return { glyfTtcComposition, ttcComposition, plans };
return plans;
}
function fnStandardTtc(collectConfig, prefix, w, wd, s) {
const ttcSuffix = makeSuffix(
collectConfig.distinguishWeights ? w : WEIGHT_NORMAL,
collectConfig.distinguishWidths ? wd : WIDTH_NORMAL,
collectConfig.distinguishSlope
? s
: collectConfig.distinguishWhetherUpright
? s === SLOPE_NORMAL
? SLOPE_NORMAL
: SLOPE_OBLIQUE
: SLOPE_NORMAL,
DEFAULT_SUBFAMILY
);
return `${prefix}-${ttcSuffix}`;
function fnStandardTtc(fIsGlyfTtc, prefix, suffixMapping, sfi) {
let optimalSfi = null,
maxScore = 0;
for (const ttcSuffix in suffixMapping) {
const sfiT = suffixMapping[ttcSuffix];
if (sfi.shapeWeight !== sfiT.shapeWeight) continue;
if (sfi.shapeWidth !== sfiT.shapeWidth) continue;
if (fIsGlyfTtc && sfi.shapeSlopeAngle !== sfiT.shapeSlopeAngle) continue;
const score =
(sfiT.weight === WEIGHT_NORMAL ? 1 : 0) +
(sfiT.width === WIDTH_NORMAL ? 1 : 0) +
(sfiT.slope === SLOPE_NORMAL ? 1 : 0);
if (!optimalSfi || score > maxScore) {
maxScore = score;
optimalSfi = sfiT;
}
}
if (!optimalSfi) throw new Error("Unreachable: TTC name decision");
return `${prefix}-${makeSuffix(optimalSfi.weight, optimalSfi.width, optimalSfi.slope)}`;
}
///////////////////////////////////////////////////////////
@ -464,25 +457,25 @@ const CollectedSuperTtcFile = file.make(
cgr => `${DIST_SUPER_TTC}/${cgr}.ttc`,
async (target, out, cgr) => {
const [cp] = await target.need(CollectPlans, de(out.dir));
const parts = Array.from(new Set(cp.plans[cgr].ttcContents));
const [inputs] = await target.need(parts.map(pt => CollectedTtcFile(cgr, pt)));
const parts = Array.from(Object.keys(cp[cgr].glyfTtcComposition));
const [inputs] = await target.need(parts.map(pt => GlyfTtc(cgr, pt)));
await buildCompositeTtc(out, inputs);
}
);
const CollectedTtcFile = file.make(
(cgr, f) => `${BUILD}/ttc-collect/${cgr}/ttc/${f}.ttc`,
async (target, out, gr, f) => {
(cgr, f) => `${BUILD}/ttc-collect/${cgr}/${f}.ttc`,
async (target, out, cgr, f) => {
const [cp] = await target.need(CollectPlans, de`${out.dir}`);
const parts = Array.from(new Set(cp.ttcComposition[f]));
const [inputs] = await target.need(parts.map(pt => GlyfTtc(gr, pt)));
const parts = Array.from(new Set(cp[cgr].ttcComposition[f]));
const [inputs] = await target.need(parts.map(pt => GlyfTtc(cgr, pt)));
await buildCompositeTtc(out, inputs);
}
);
const GlyfTtc = file.make(
(cgr, f) => `${BUILD}/glyf-ttc/${cgr}/${f}.ttc`,
async (target, out, gr, f) => {
async (target, out, cgr, f) => {
const [cp] = await target.need(CollectPlans);
const parts = cp.glyfTtcComposition[f];
const parts = cp[cgr].glyfTtcComposition[f];
await buildGlyphSharingTtc(target, parts, out);
}
);
@ -508,13 +501,13 @@ async function buildGlyphSharingTtc(target, parts, out) {
const TtcArchiveFile = file.make(
(cgr, version) => `${ARCHIVE_DIR}/ttc-${cgr}-${version}.zip`,
async (target, out, cgr) => {
const [collectPlans] = await target.need(CollectPlans, de`${out.dir}`);
const ttcFiles = Array.from(new Set(collectPlans.plans[cgr].ttcContents));
const [cp] = await target.need(CollectPlans, de`${out.dir}`);
const ttcFiles = Array.from(Object.keys(cp[cgr].ttcComposition));
await target.need(ttcFiles.map(pt => CollectedTtcFile(cgr, pt)));
// Packaging
await rm(out.full);
await cd(`${BUILD}/ttc-collect/${cgr}/ttc`).run(
await cd(`${BUILD}/ttc-collect/${cgr}`).run(
["7z", "a"],
["-tzip", "-r", "-mx=9"],
`../../../../${out.full}`,
@ -795,7 +788,7 @@ phony(`clean`, async () => {
phony(`release`, async target => {
const [collectPlans] = await target.need(CollectPlans);
let goals = [];
for (const [cgr, plan] of Object.entries(collectPlans.plans)) {
for (const [cgr, plan] of Object.entries(collectPlans)) {
if (!plan.inRelease) continue;
goals.push(ReleaseGroup(cgr));
}
@ -804,7 +797,7 @@ phony(`release`, async target => {
});
const ReleaseGroup = phony.group("release-group", async (target, cgr) => {
const [version, collectPlans] = await target.need(Version, CollectPlans);
const subGroups = collectPlans.plans[cgr].groupDecomposition;
const subGroups = collectPlans[cgr].groupDecomposition;
let goals = [TtcArchiveFile(cgr, version), SuperTtcArchiveFile(cgr, version)];
for (const gr of subGroups) {
@ -887,6 +880,7 @@ const Parameters = task(`meta:parameters`, async target => {
sfu`params/parameters.toml`,
sfu`params/shape-weight.toml`,
sfu`params/shape-width.toml`,
sfu`params/shape-slope.toml`,
ofu`params/private-parameters.toml`,
sfu`params/variants.toml`,
sfu`params/ligation-set.toml`
@ -937,6 +931,13 @@ function validateRecommendedWeight(w, value, label) {
}
// Value validation
function wwsDefValidate(key, obj) {
if (!obj || typeof obj === "string") {
throw new TypeError(`${key} is invalid.`);
}
return obj;
}
function nValidate(key, v, validator) {
if (validator.fixup) v = validator.fix(v);
if (typeof v !== "number" || !isFinite(v) || !validator.validate(v)) {
@ -968,3 +969,26 @@ const VlShapeWidth = {
}
};
const VlMenuWidth = { validate: x => x >= 1 && x <= 9 && x % 1 === 0 };
const VlSlopeAngle = { validate: x => x >= 0 && x <= 15 };
function sValidate(key, v, validator) {
if (validator.fixup) v = validator.fix(v);
if (typeof v !== "string" || !validator.validate(v)) {
throw new TypeError(`${key} = ${v} is not a valid string.`);
}
return v;
}
const VlShapeSlope = { validate: x => x === "upright" || x === "oblique" || x === "italic" };
const VlCssStyle = { validate: x => x === "normal" || x === "oblique" || x === "italic" };
const VlCssFontStretch = {
validate: x =>
x == "ultra-condensed" ||
x == "extra-condensed" ||
x == "condensed" ||
x == "semi-condensed" ||
x == "normal" ||
x == "semi-expanded" ||
x == "expanded" ||
x == "extra-expanded" ||
x == "ultra-expanded"
};