Allow the user to customize the characters' width freely (#554).
This commit is contained in:
parent
275fc23ba3
commit
966838af7b
9 changed files with 163 additions and 115 deletions
|
@ -360,7 +360,7 @@ snapshotFamily = 'iosevka-aile'
|
|||
design = ["diversity-2"]
|
||||
|
||||
[buildPlans.iosevka-aile.widths.normal]
|
||||
shape = 7
|
||||
shape = 576
|
||||
menu = 5
|
||||
css = "normal"
|
||||
|
||||
|
@ -379,7 +379,7 @@ snapshotFamily = 'iosevka-etoile'
|
|||
design = ["diversity-1"]
|
||||
|
||||
[buildPlans.iosevka-etoile.widths.normal]
|
||||
shape = 7
|
||||
shape = 576
|
||||
menu = 5
|
||||
css = "normal"
|
||||
|
||||
|
@ -398,7 +398,7 @@ snapshotFamily = 'iosevka-sparkle'
|
|||
design = ["diversity-1"]
|
||||
|
||||
[buildPlans.iosevka-sparkle.widths.normal]
|
||||
shape = 7
|
||||
shape = 576
|
||||
menu = 5
|
||||
css = "normal"
|
||||
|
||||
|
@ -614,12 +614,12 @@ italic = "italic"
|
|||
# and "menu" only support 1 ... 9
|
||||
|
||||
[widths.normal]
|
||||
shape = 5
|
||||
shape = 500
|
||||
menu = 5
|
||||
css = "normal"
|
||||
|
||||
[widths.extended]
|
||||
shape = 7
|
||||
shape = 576
|
||||
menu = 7
|
||||
css = "expanded"
|
||||
|
||||
|
|
|
@ -9,3 +9,4 @@
|
|||
* Add parenthesis variant with larger contour (#570).
|
||||
* Fix placement of U+0315 COMBINING COMMA ABOVE RIGHT (#583).
|
||||
* Fix shape of U+1D24 LATIN LETTER VOICED LARYNGEAL SPIRANT (#584).
|
||||
* Allow the user to customize the characters' width freely (#554).
|
|
@ -32,7 +32,10 @@ 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.shape.weight });
|
||||
const para = Parameters.build(parametersData, argv.hives, {
|
||||
shapeWeight: argv.shape.weight,
|
||||
shapeWidth: argv.shape.width
|
||||
});
|
||||
|
||||
const variantsData = FormVariantData(rawVariantsData, para);
|
||||
para.variants = variantsData;
|
||||
|
|
|
@ -271,7 +271,18 @@ spacing = 0
|
|||
# NOTE: this section is highly experimental
|
||||
# HANDLE WITH EXTREME CARE
|
||||
# Expanded : I heard someone want it being wider...
|
||||
[wd-9.multiplies]
|
||||
[shapeWidth.multiplies.blend.500]
|
||||
width = 1
|
||||
stroke = 1
|
||||
sb = 1
|
||||
jut = 1
|
||||
longjut = 1
|
||||
rhook = 1
|
||||
rbalance = 1
|
||||
tbalance = 1
|
||||
smallsmooth = 1
|
||||
|
||||
[shapeWidth.multiplies.blend.664]
|
||||
width = 1.328 # 664 for normal char
|
||||
stroke = 1.103 # Make strokes a little thicker
|
||||
sb = 1.777
|
||||
|
@ -282,10 +293,7 @@ rbalance = 1.236
|
|||
tbalance = 1.210
|
||||
smallsmooth = 1.103
|
||||
|
||||
[ultra-extended]
|
||||
inherits = ['wd-9']
|
||||
|
||||
[wd-8.multiplies]
|
||||
[shapeWidth.multiplies.blend.618]
|
||||
width = 1.236 # 618 for normal char
|
||||
stroke = 1.075 # Make strokes a little thicker
|
||||
sb = 1.539
|
||||
|
@ -296,10 +304,7 @@ rbalance = 1.236
|
|||
tbalance = 1.154
|
||||
smallsmooth = 1.075
|
||||
|
||||
[extra-extended]
|
||||
inherits = ['wd-8']
|
||||
|
||||
[wd-7.multiplies]
|
||||
[shapeWidth.multiplies.blend.576]
|
||||
width = 1.152 # 576mem for normal char
|
||||
stroke = 1.050 # Make strokes a little thicker
|
||||
sb = 1.333
|
||||
|
@ -310,10 +315,7 @@ rbalance = 1.152
|
|||
tbalance = 1.100
|
||||
smallsmooth = 1.050
|
||||
|
||||
[extended]
|
||||
inherits = ['wd-7']
|
||||
|
||||
[wd-6.multiplies]
|
||||
[shapeWidth.multiplies.blend.537]
|
||||
width = 1.074 # 537mem for normal char
|
||||
stroke = 1.023 # Make strokes a little thicker
|
||||
sb = 1.154
|
||||
|
@ -324,10 +326,7 @@ rbalance = 1.074
|
|||
tbalance = 1.049
|
||||
smallsmooth = 1.023
|
||||
|
||||
[semi-extended]
|
||||
inherits = ['wd-6']
|
||||
|
||||
[wd-4.multiplies]
|
||||
[shapeWidth.multiplies.blend.466]
|
||||
width = 0.932 # 466mem for normal char
|
||||
stroke = 0.975
|
||||
sb = 0.866
|
||||
|
@ -338,10 +337,7 @@ rbalance = 0.931
|
|||
tbalance = 0.953
|
||||
smallsmooth = 0.975
|
||||
|
||||
[semi-condensed]
|
||||
inherits = ['wd-4']
|
||||
|
||||
[wd-3.multiplies]
|
||||
[shapeWidth.multiplies.blend.434]
|
||||
width = 0.868 # 434mem for normal char
|
||||
stroke = 0.952
|
||||
sb = 0.750
|
||||
|
@ -352,8 +348,7 @@ rbalance = 0.868
|
|||
tbalance = 0.909
|
||||
smallsmooth = 0.952
|
||||
|
||||
[condensed]
|
||||
inherits = ['wd-3']
|
||||
###### Diversity
|
||||
|
||||
[diversity-1]
|
||||
diversityM = 1.25
|
||||
|
|
|
@ -49,18 +49,20 @@ oblique = "oblique"
|
|||
# Override default building widths
|
||||
# When buildPlans.<plan name>.widths is absent, all widths would built and mapped to
|
||||
# default values.
|
||||
# IMPORTANT : Currently "shape" property only supports integers between 3 and 9 (inclusive), while
|
||||
# "menu" only supports integers between 1 and 9 (inclusive).
|
||||
# If you decide to use custom widths you have to define all the widths you
|
||||
# plan to use otherwise they will not be built.
|
||||
# IMPORTANT : Currently "shape" property only supports numbers between 434 and 664 (inclusive),
|
||||
# while "menu" only supports integers between 1 and 9 (inclusive).
|
||||
# The "shape" parameter specifies the unit width, measured in 1/1000 em. The glyphs'
|
||||
# width are equal to, or a simple multiple of the unit width.
|
||||
# If you decide to use custom widths you have to define all the widths you plan to use,
|
||||
# otherwise they will not be built.
|
||||
|
||||
[buildPlans.iosevka-custom.widths.normal]
|
||||
shape = 5 # Width grade of glyph shapes. NOT actual character width.
|
||||
menu = 5 # Width grade for the font's names. NOT actual character width.
|
||||
shape = 500 # Unit Width, measured in 1/1000 em.
|
||||
menu = 5 # Width grade for the font's names.
|
||||
css = "normal" # "font-stretch' property of webfont CSS.
|
||||
|
||||
[buildPlans.iosevka-custom.widths.extended]
|
||||
shape = 7
|
||||
shape = 576
|
||||
menu = 7
|
||||
css = "expanded"
|
||||
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const blend = require("./monotonic-interpolate");
|
||||
|
||||
module.exports = function (aspect, hive, params, sink) {
|
||||
if (!hive || !hive.blend || !params) return;
|
||||
|
||||
const block = hive.blend;
|
||||
let keys = new Set();
|
||||
for (const grade in block) {
|
||||
sink[grade] = block[grade];
|
||||
if (!isFinite(parseFloat(grade))) continue;
|
||||
for (const key in block[grade]) {
|
||||
if (block[grade][key] == null) continue;
|
||||
keys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of keys) {
|
||||
let xs = [],
|
||||
ys = [];
|
||||
for (const grade in block) {
|
||||
if (!isFinite(parseFloat(grade))) continue;
|
||||
if (block[grade][key] == null) continue;
|
||||
xs.push(grade);
|
||||
ys.push(block[grade][key]);
|
||||
}
|
||||
const fn = blend(xs, ys);
|
||||
sink[key] = fn(params[aspect]);
|
||||
}
|
||||
};
|
100
support/parameters.js
Normal file
100
support/parameters.js
Normal file
|
@ -0,0 +1,100 @@
|
|||
"use strict";
|
||||
|
||||
const monotonicInterpolate = require("./monotonic-interpolate");
|
||||
|
||||
function build(parametersData, styles, blendArgs) {
|
||||
const sink = {};
|
||||
for (const item of styles) intro(parametersData, item, blendArgs, sink);
|
||||
return sink;
|
||||
}
|
||||
exports.build = build;
|
||||
|
||||
function intro(source, style, blendArgs, sink) {
|
||||
let hive = source[style];
|
||||
if (!hive) return;
|
||||
hive = { ...hive };
|
||||
|
||||
if (hive.inherits) {
|
||||
for (const hn of hive.inherits) intro(source, hn, blendArgs, sink);
|
||||
delete hive.inherits;
|
||||
}
|
||||
if (hive.multiplies) {
|
||||
const mu = hiveBlend(hive.multiplies, getBlendArg(blendArgs, style), sink);
|
||||
for (const k in mu) sink[k] = (sink[k] || 0) * mu[k];
|
||||
delete hive.multiplies;
|
||||
}
|
||||
if (hive.adds) {
|
||||
const mu = hiveBlend(hive.adds, getBlendArg(blendArgs, style), sink);
|
||||
for (const k in mu) sink[k] = (sink[k] || 0) + mu[k];
|
||||
delete hive.adds;
|
||||
}
|
||||
if (hive.appends) {
|
||||
const mu = hive.appends;
|
||||
for (const k in mu) sink[k] = [...(sink[k] || []), ...mu[k]];
|
||||
delete hive.appends;
|
||||
}
|
||||
hive = hiveBlend(hive, getBlendArg(blendArgs, style), sink);
|
||||
for (const k in hive) sink[k] = hive[k];
|
||||
}
|
||||
|
||||
function getBlendArg(blendArgs, style) {
|
||||
if (!blendArgs) return undefined;
|
||||
return blendArgs[style];
|
||||
}
|
||||
|
||||
function hiveBlend(hive, value, sink) {
|
||||
if (!hive || !hive.blend || value == null) return hive;
|
||||
|
||||
const generatedHive = {};
|
||||
const block = hive.blend;
|
||||
let keys = new Set();
|
||||
for (const grade in block) {
|
||||
sink[grade] = block[grade];
|
||||
if (!isFinite(parseFloat(grade))) continue;
|
||||
for (const key in block[grade]) {
|
||||
if (block[grade][key] == null) continue;
|
||||
keys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of keys) {
|
||||
let xs = [],
|
||||
ys = [];
|
||||
for (const grade in block) {
|
||||
if (!isFinite(parseFloat(grade))) continue;
|
||||
if (block[grade][key] == null) continue;
|
||||
xs.push(grade);
|
||||
ys.push(block[grade][key]);
|
||||
}
|
||||
generatedHive[key] = monotonicInterpolate(xs, ys)(value);
|
||||
}
|
||||
return generatedHive;
|
||||
}
|
||||
|
||||
function numericConfigExists(x) {
|
||||
return x != null && isFinite(x);
|
||||
}
|
||||
function applyMetricOverride(para, mo) {
|
||||
if (numericConfigExists(mo.leading)) {
|
||||
para.leading = mo.leading;
|
||||
}
|
||||
if (numericConfigExists(mo.winMetricAscenderPad)) {
|
||||
para.winMetricAscenderPad = mo.winMetricAscenderPad;
|
||||
}
|
||||
if (numericConfigExists(mo.winMetricDescenderPad)) {
|
||||
para.winMetricDescenderPad = mo.winMetricDescenderPad;
|
||||
}
|
||||
if (numericConfigExists(mo.powerlineScaleY)) {
|
||||
para.powerlineScaleY = mo.powerlineScaleY;
|
||||
}
|
||||
if (numericConfigExists(mo.powerlineScaleX)) {
|
||||
para.powerlineScaleX = mo.powerlineScaleX;
|
||||
}
|
||||
if (numericConfigExists(mo.powerlineShiftY)) {
|
||||
para.powerlineShiftY = mo.powerlineShiftY;
|
||||
}
|
||||
if (numericConfigExists(mo.powerlineShiftX)) {
|
||||
para.powerlineShiftX = mo.powerlineShiftX;
|
||||
}
|
||||
}
|
||||
exports.applyMetricOverride = applyMetricOverride;
|
|
@ -1,43 +0,0 @@
|
|||
import "./param-blend" as paramBlend
|
||||
|
||||
export : define [build parametersData styles blendParams] : begin
|
||||
local param {.}
|
||||
|
||||
define [introStyle style] : begin
|
||||
local hive parametersData.(style)
|
||||
if (!hive) : return nothing
|
||||
|
||||
if hive.inherits : foreach [h : items-of hive.inherits] : introStyle h
|
||||
|
||||
foreach [k : items-of : Object.keys hive] : piecewise
|
||||
(k === "multiplies") : foreach [k : items-of : Object.keys hive.multiplies] : begin
|
||||
set param.(k) : param.(k) * hive.multiplies.(k)
|
||||
(k === "adds") : foreach [k : items-of : Object.keys hive.adds] : begin
|
||||
set param.(k) : param.(k) + hive.adds.(k)
|
||||
(k === "appends") : foreach [k : items-of : Object.keys hive.appends] : begin
|
||||
set param.(k) : (param.(k) || {}).concat(hive.appends.(k))
|
||||
true : set param.(k) hive.(k)
|
||||
|
||||
paramBlend style hive blendParams param
|
||||
|
||||
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
|
31
verdafile.js
31
verdafile.js
|
@ -213,13 +213,18 @@ function getSuffixMapping(weights, slants, widths) {
|
|||
for (const wd in widths) {
|
||||
const suffix = makeSuffix(w, wd, s, "regular");
|
||||
mapping[suffix] = {
|
||||
hives: [`shapeWeight`, `s-${s}`, `wd-${widths[wd].shape}`],
|
||||
hives: [`shapeWeight`, `s-${s}`, `shapeWidth`],
|
||||
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),
|
||||
shapeWidth: nValidate(
|
||||
"Shape width of " + wd,
|
||||
widths[wd].shape,
|
||||
vlShapeWidth,
|
||||
fixShapeWidth
|
||||
),
|
||||
cssStretch: widths[wd].css || wd,
|
||||
menuWidth: nValidate("Menu width of " + wd, widths[wd].menu, vlMenuWidth),
|
||||
slant: s,
|
||||
|
@ -248,9 +253,10 @@ function validateRecommendedWeight(w, value, label) {
|
|||
}
|
||||
}
|
||||
|
||||
function nValidate(key, v, f) {
|
||||
function nValidate(key, v, f, ft) {
|
||||
if (ft) v = ft(v);
|
||||
if (typeof v !== "number" || !isFinite(v) || (f && !f(v))) {
|
||||
throw new TypeError(`${key} = "${v}" is not a valid number.`);
|
||||
throw new TypeError(`${key} = ${v} is not a valid number.`);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
@ -263,8 +269,23 @@ function vlCssWeight(x) {
|
|||
function vlMenuWeight(x) {
|
||||
return vlCssWeight(x);
|
||||
}
|
||||
const g_widthFixupMemory = new Map();
|
||||
function fixShapeWidth(x) {
|
||||
if (x >= 3 && x <= 9) {
|
||||
if (g_widthFixupMemory.has(x)) return g_widthFixupMemory.get(x);
|
||||
const xCorrected = Math.round(500 * Math.pow(Math.sqrt(576 / 500), x - 5));
|
||||
echo.warn(
|
||||
`The build plan is using legacy width grade ${x}. ` +
|
||||
`Converting to unit width ${xCorrected}.`
|
||||
);
|
||||
g_widthFixupMemory.set(x, xCorrected);
|
||||
return xCorrected;
|
||||
} else {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
function vlShapeWidth(x) {
|
||||
return x >= 3 && x <= 9 && x % 1 === 0;
|
||||
return x >= 433 && x <= 665;
|
||||
}
|
||||
function vlMenuWidth(x) {
|
||||
return x >= 1 && x <= 9 && x % 1 === 0;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue