1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-22 21:01:36 +03:00

Allow matching font weight and font width in wezterm.font

refs: https://github.com/wez/wezterm/issues/655
This commit is contained in:
Wez Furlong 2021-04-08 15:41:07 -07:00
parent dee10c2f21
commit 43ea2f192a
13 changed files with 263 additions and 144 deletions

12
Cargo.lock generated
View File

@ -697,6 +697,7 @@ dependencies = [
"bstr 0.2.15",
"chrono",
"dirs-next",
"enum-display-derive",
"filenamegen",
"hostname",
"lazy_static",
@ -1070,6 +1071,17 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "enum-display-derive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f16ef37b2a9b242295d61a154ee91ae884afff6b8b933b486b12481cc58310ca"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "enumflags2"
version = "0.6.4"

View File

@ -17,6 +17,7 @@ bitflags = "1.0"
bstr = "0.2"
chrono = {version="0.4", features=["unstable-locales"]}
dirs-next = "2.0"
enum-display-derive = "0.1"
filenamegen = "0.2"
hostname = "0.3"
lazy_static = "1.4"

View File

@ -1,9 +1,135 @@
use crate::*;
use bitflags::*;
use enum_display_derive::Display;
use luahelper::impl_lua_conversion;
use serde::{Deserialize, Deserializer, Serialize};
use std::fmt::Display;
use termwiz::color::RgbColor;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, Display)]
pub enum FontWidth {
UltraCondensed,
ExtraCondensed,
Condensed,
SemiCondensed,
Normal,
SemiExpanded,
Expanded,
ExtraExpanded,
UltraExpanded,
}
impl FontWidth {
pub fn from_opentype_width(w: u16) -> Self {
match w {
1 => Self::UltraCondensed,
2 => Self::ExtraCondensed,
3 => Self::Condensed,
4 => Self::SemiCondensed,
5 => Self::Normal,
6 => Self::SemiExpanded,
7 => Self::Expanded,
8 => Self::ExtraExpanded,
9 => Self::UltraExpanded,
_ if w < 1 => Self::UltraCondensed,
_ => Self::UltraExpanded,
}
}
pub fn to_opentype_width(self) -> u16 {
match self {
Self::UltraCondensed => 1,
Self::ExtraCondensed => 2,
Self::Condensed => 3,
Self::SemiCondensed => 4,
Self::Normal => 5,
Self::SemiExpanded => 6,
Self::Expanded => 7,
Self::ExtraExpanded => 8,
Self::UltraExpanded => 9,
}
}
}
impl Default for FontWidth {
fn default() -> Self {
Self::Normal
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Hash, Display)]
pub enum FontWeight {
Thin,
ExtraLight,
Light,
DemiLight,
Book,
Regular,
Medium,
DemiBold,
Bold,
ExtraBold,
Black,
ExtraBlack,
}
impl Default for FontWeight {
fn default() -> Self {
Self::Regular
}
}
impl FontWeight {
pub fn from_opentype_weight(w: u16) -> Self {
if w >= 1000 {
Self::ExtraBlack
} else if w >= 900 {
Self::Black
} else if w >= 800 {
Self::ExtraBold
} else if w >= 700 {
Self::Bold
} else if w >= 600 {
Self::DemiBold
} else if w >= 500 {
Self::Medium
} else if w >= 400 {
Self::Regular
} else if w >= 380 {
Self::Book
} else if w >= 350 {
Self::DemiLight
} else if w >= 300 {
Self::Light
} else if w >= 200 {
Self::ExtraLight
} else {
Self::Thin
}
}
pub fn to_opentype_weight(self) -> u16 {
match self {
Self::Thin => 100,
Self::ExtraLight => 200,
Self::Light => 300,
Self::DemiLight => 350,
Self::Book => 380,
Self::Regular => 400,
Self::Medium => 500,
Self::DemiBold => 600,
Self::Bold => 700,
Self::ExtraBold => 800,
Self::Black => 900,
Self::ExtraBlack => 1000,
}
}
pub fn bolder(self) -> Self {
Self::from_opentype_weight(self.to_opentype_weight() + 200)
}
}
#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FreeTypeLoadTarget {
/// This corresponds to the default hinting algorithm, optimized
@ -123,7 +249,9 @@ pub struct FontAttributes {
pub family: String,
/// Whether the font should be a bold variant
#[serde(default)]
pub bold: bool,
pub weight: FontWeight,
#[serde(default)]
pub width: FontWidth,
/// Whether the font should be an italic variant
#[serde(default)]
pub italic: bool,
@ -136,8 +264,8 @@ impl std::fmt::Display for FontAttributes {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(
fmt,
"wezterm.font('{}', {{bold={}, italic={}}})",
self.family, self.bold, self.italic
"wezterm.font('{}', {{weight='{}', width='{}', italic={}}})",
self.family, self.weight, self.width, self.italic
)
}
}
@ -146,7 +274,8 @@ impl FontAttributes {
pub fn new(family: &str) -> Self {
Self {
family: family.into(),
bold: false,
weight: FontWeight::default(),
width: FontWidth::default(),
italic: false,
is_fallback: false,
is_synthetic: false,
@ -156,7 +285,8 @@ impl FontAttributes {
pub fn new_fallback(family: &str) -> Self {
Self {
family: family.into(),
bold: false,
weight: FontWeight::default(),
width: FontWidth::default(),
italic: false,
is_fallback: true,
is_synthetic: false,
@ -168,7 +298,8 @@ impl Default for FontAttributes {
fn default() -> Self {
Self {
family: "JetBrains Mono".into(),
bold: false,
weight: FontWeight::default(),
width: FontWidth::default(),
italic: false,
is_fallback: false,
is_synthetic: false,
@ -269,7 +400,7 @@ impl TextStyle {
.iter()
.map(|attr| {
let mut attr = attr.clone();
attr.bold = true;
attr.weight = attr.weight.bolder();
attr.is_synthetic = true;
attr
})

View File

@ -1,4 +1,4 @@
use crate::{FontAttributes, TextStyle};
use crate::{FontAttributes, FontWeight, FontWidth, TextStyle};
use anyhow::anyhow;
use bstr::BString;
pub use luahelper::*;
@ -334,7 +334,11 @@ fn hostname<'lua>(_: &'lua Lua, _: ()) -> mlua::Result<String> {
struct TextStyleAttributes {
/// Whether the font should be a bold variant
#[serde(default)]
pub bold: bool,
pub bold: Option<bool>,
#[serde(default)]
pub weight: Option<FontWeight>,
#[serde(default)]
pub width: FontWidth,
/// Whether the font should be an italic variant
#[serde(default)]
pub italic: bool,
@ -365,7 +369,12 @@ fn font<'lua>(
text_style.font.clear();
text_style.font.push(FontAttributes {
family,
bold: attrs.bold,
width: attrs.width,
weight: match attrs.bold {
Some(true) => FontWeight::Bold,
Some(false) => FontWeight::Regular,
None => attrs.weight.unwrap_or(FontWeight::Regular),
},
italic: attrs.italic,
is_fallback: false,
is_synthetic: false,
@ -393,7 +402,12 @@ fn font_with_fallback<'lua>(
for (idx, family) in fallback.into_iter().enumerate() {
text_style.font.push(FontAttributes {
family,
bold: attrs.bold,
width: attrs.width,
weight: match attrs.bold {
Some(true) => FontWeight::Bold,
Some(false) => FontWeight::Regular,
None => attrs.weight.unwrap_or(FontWeight::Regular),
},
italic: attrs.italic,
is_fallback: idx != 0,
is_synthetic: false,

View File

@ -52,6 +52,16 @@ pub const FC_SLANT_ROMAN: c_int = 0;
pub const FC_SLANT_ITALIC: c_int = 100;
pub const FC_SLANT_OBLIQUE: c_int = 110;
pub const FC_WIDTH_ULTRACONDENSED: c_int = 50;
pub const FC_WIDTH_EXTRACONDENSED: c_int = 63;
pub const FC_WIDTH_CONDENSED: c_int = 75;
pub const FC_WIDTH_SEMICONDENSED: c_int = 87;
pub const FC_WIDTH_NORMAL: c_int = 100;
pub const FC_WIDTH_SEMIEXPANDED: c_int = 113;
pub const FC_WIDTH_EXPANDED: c_int = 125;
pub const FC_WIDTH_EXTRAEXPANDED: c_int = 150;
pub const FC_WIDTH_ULTRAEXPANDED: c_int = 200;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct struct__FcMatrix {

View File

@ -15,6 +15,7 @@ As features stabilize some brief notes about them will accumulate here.
* Fixed: the selection wouldn't always clear when the intersecting lines change [#644](https://github.com/wez/wezterm/issues/644)
* Fixed: vertical alignment issue with Iosevka on Windows [#661](https://github.com/wez/wezterm/issues/661)
* Fixed: support for "Variable" fonts such as Cascadia Code and Inconsolata on all platforms [#655](https://github.com/wez/wezterm/issues/655)
* New: [wezterm.font](config/lua/wezterm/font.md) and [wezterm.font_with_fallback](config/lua/wezterm.font_with_fallback.md) *attributes* parameter now allows matching more granular font weights and font widths. e.g.: `wezterm.font('Iosevka Term', {width="Expanded", weight="Regular"})`
### 20210405-110924-a5bb5be8

View File

@ -25,4 +25,21 @@ return {
}
```
*Since: nightly builds only*
It is now possible to specify both font weight and font width:
* `width` - specifies the font width to select. The default value is `"Normal"`, and possible values are `"UltraCondensed"`, `"ExtraCondensed"`, `"Condensed"`, `"SemiCondensed"`, `"Normal"`, `"SemiExpanded"`, `"Expanded"`, `"ExtraExpanded"`, `"UltraExpanded"`.
* `weight` - specifies the weight of the font with more precision than `bold`. The default value is `"Regular"`, and possible values are `"Thin"`, `"ExtraLight"`, `"Light"`, `"DemiLight"`, `"Book"`, `"Regular"`, `"Medium"`, `"DemiBold"`, `"Bold"`, `"ExtraBold"`, `"Black"`, and `"ExtraBlack"`.
* `bold` - has been superseded by the new `weight` parameter and will be eventually removed. For compatibility purposes, specifying `bold=true` is equivalent to specifying `weight="Bold"`.
Font weight matching will find the closest matching weight that is equal of
heavier to the specified weight.
```lua
local wezterm = require 'wezterm';
return {
font = wezterm.font('Iosevka Term', {width="Expanded", weight="Regular"}),
}
```

View File

@ -14,6 +14,7 @@ return {
}
```
The second parameter behaves the same as that of `wezterm.font`.
The *attributes* parameter behaves the same as that of [wezterm.font](font.md)
in that it allows you to specify font weight and style attributes that you
want to match.

View File

@ -2,6 +2,7 @@
#![allow(clippy::mutex_atomic)]
use anyhow::{anyhow, ensure, Error};
use config::{FontWeight, FontWidth};
pub use fontconfig::*;
use std::ffi::{CStr, CString};
use std::fmt;
@ -446,3 +447,33 @@ impl fmt::Debug for Pattern {
)
}
}
pub fn to_fc_weight(weight: FontWeight) -> c_int {
match weight {
FontWeight::Thin => FC_WEIGHT_THIN,
FontWeight::ExtraLight => FC_WEIGHT_EXTRALIGHT,
FontWeight::Light => FC_WEIGHT_LIGHT,
FontWeight::DemiLight | FontWeight::Book => FC_WEIGHT_BOOK,
FontWeight::Regular => FC_WEIGHT_REGULAR,
FontWeight::Medium => FC_WEIGHT_MEDIUM,
FontWeight::DemiBold => FC_WEIGHT_DEMIBOLD,
FontWeight::Bold => FC_WEIGHT_BOLD,
FontWeight::ExtraBold => FC_WEIGHT_EXTRABOLD,
FontWeight::Black => FC_WEIGHT_BLACK,
FontWeight::ExtraBlack => FC_WEIGHT_EXTRABLACK,
}
}
pub fn to_fc_width(width: FontWidth) -> c_int {
match width {
FontWidth::UltraCondensed => FC_WIDTH_ULTRACONDENSED,
FontWidth::ExtraCondensed => FC_WIDTH_EXTRACONDENSED,
FontWidth::Condensed => FC_WIDTH_CONDENSED,
FontWidth::SemiCondensed => FC_WIDTH_SEMICONDENSED,
FontWidth::Normal => FC_WIDTH_NORMAL,
FontWidth::SemiExpanded => FC_WIDTH_SEMIEXPANDED,
FontWidth::Expanded => FC_WIDTH_EXPANDED,
FontWidth::ExtraExpanded => FC_WIDTH_EXTRAEXPANDED,
FontWidth::UltraExpanded => FC_WIDTH_ULTRAEXPANDED,
}
}

View File

@ -3,7 +3,9 @@ use crate::locator::{new_locator, FontDataHandle, FontLocator};
use crate::rasterizer::{new_rasterizer, FontRasterizer};
use crate::shaper::{new_shaper, FontShaper};
use anyhow::{Context, Error};
use config::{configuration, ConfigHandle, FontRasterizerSelection, TextStyle};
use config::{
configuration, ConfigHandle, FontRasterizerSelection, FontWeight, FontWidth, TextStyle,
};
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::rc::{Rc, Weak};
@ -281,8 +283,11 @@ impl FontConfigInner {
for attr in &attributes {
if !attr.is_synthetic && !attr.is_fallback && !loaded.contains(attr) {
let styled_extra = if attr.bold || attr.italic {
". A bold or italic variant of the font was requested; \
let styled_extra = if attr.weight != FontWeight::default()
|| attr.italic
|| attr.width != FontWidth::default()
{
". An alternative variant of the font was requested; \
TrueType and OpenType fonts don't have an automatic way to \
produce these font variants, so a separate font file containing \
the bold or italic variant must be installed"

View File

@ -3,7 +3,7 @@ use crate::locator::{FontDataHandle, FontDataSource, FontLocator};
use crate::parser::FontMatch;
use anyhow::Context;
use config::FontAttributes;
use fcwrap::{CharSet, Pattern as FontPattern};
use fcwrap::{to_fc_weight, to_fc_width, CharSet, Pattern as FontPattern};
use std::collections::HashSet;
use std::convert::TryInto;
@ -28,8 +28,16 @@ impl FontLocator for FontConfigFontLocator {
let mut pattern = FontPattern::new()?;
let start = std::time::Instant::now();
pattern.family(&attr.family)?;
pattern.add_integer("weight", if attr.bold { 200 } else { 80 })?;
pattern.add_integer("slant", if attr.italic { 100 } else { 0 })?;
pattern.add_integer("weight", to_fc_weight(attr.weight))?;
pattern.add_integer("width", to_fc_width(attr.width))?;
pattern.add_integer(
"slant",
if attr.italic {
fcwrap::FC_SLANT_ITALIC
} else {
fcwrap::FC_SLANT_ROMAN
},
)?;
pattern.add_integer("spacing", spacing)?;
log::trace!("fc pattern before config subst: {:?}", pattern);
@ -44,10 +52,13 @@ impl FontLocator for FontConfigFontLocator {
);
let file = best.get_file()?;
let index = best.get_integer("index")? as u32;
let variation = index >> 16;
let index = index & 0xffff;
let handle = FontDataHandle {
source: FontDataSource::OnDisk(file.into()),
index: best.get_integer("index")?.try_into()?,
variation: 0,
index,
variation,
};
// fontconfig will give us a boatload of random fallbacks.

View File

@ -2,7 +2,7 @@
use crate::locator::{FontDataHandle, FontDataSource, FontLocator};
use crate::parser::{parse_and_collect_font_info, rank_matching_fonts, FontMatch, ParsedFont};
use config::FontAttributes;
use config::{FontAttributes, FontWeight as WTFontWeight, FontWidth};
use dwrote::{FontDescriptor, FontStretch, FontStyle, FontWeight};
use std::borrow::Cow;
use std::collections::HashSet;
@ -88,7 +88,7 @@ fn load_font(font_attr: &FontAttributes) -> anyhow::Result<FontDataHandle> {
lfWidth: 0,
lfEscapement: 0,
lfOrientation: 0,
lfWeight: if font_attr.bold { 700 } else { 0 },
lfWeight: font_attr.weight.to_opentype_weight() as _,
lfItalic: if font_attr.italic { 1 } else { 0 },
lfUnderline: 0,
lfStrikeOut: 0,
@ -122,11 +122,7 @@ fn load_font(font_attr: &FontAttributes) -> anyhow::Result<FontDataHandle> {
fn attributes_to_descriptor(font_attr: &FontAttributes) -> FontDescriptor {
FontDescriptor {
family_name: font_attr.family.to_string(),
weight: if font_attr.bold {
FontWeight::Bold
} else {
FontWeight::Regular
},
weight: FontWeight::from_u32(font_attr.weight.to_opentype_weight() as u32),
stretch: FontStretch::Normal,
style: if font_attr.italic {
FontStyle::Italic
@ -282,20 +278,8 @@ impl FontLocator for GdiFontLocator {
);
let attr = FontAttributes {
bold: match font.weight() {
FontWeight::Thin
| FontWeight::ExtraLight
| FontWeight::Light
| FontWeight::SemiLight
| FontWeight::Regular
| FontWeight::Medium => false,
FontWeight::SemiBold
| FontWeight::Bold
| FontWeight::ExtraBold
| FontWeight::Black
| FontWeight::ExtraBlack => true,
FontWeight::Unknown(n) => n > 80,
},
weight: WTFontWeight::from_opentype_weight(font.weight().to_u32() as _),
width: FontWidth::from_opentype_width(font.stretch().to_u32() as _),
italic: false,
family: font.family_name(),
is_fallback: true,

View File

@ -1,6 +1,7 @@
use crate::locator::{FontDataHandle, FontDataSource};
use crate::shaper::GlyphInfo;
use config::FontAttributes;
pub use config::{FontWeight, FontWidth};
use std::borrow::Cow;
#[derive(Debug)]
@ -9,100 +10,6 @@ pub enum MaybeShaped {
Unresolved { raw: String, slice_start: usize },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FontWidth {
UltraCondensed,
ExtraCondensed,
Condensed,
SemiCondensed,
Normal,
SemiExpanded,
Expanded,
ExtraExpanded,
UltraExpanded,
}
impl FontWidth {
pub fn from_opentype_width(w: u16) -> Self {
match w {
1 => Self::UltraCondensed,
2 => Self::ExtraCondensed,
3 => Self::Condensed,
4 => Self::SemiCondensed,
5 => Self::Normal,
6 => Self::SemiExpanded,
7 => Self::Expanded,
8 => Self::ExtraExpanded,
9 => Self::UltraExpanded,
_ if w < 1 => Self::UltraCondensed,
_ => Self::UltraExpanded,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FontWeight {
Thin,
ExtraLight,
Light,
DemiLight,
Book,
Regular,
Medium,
DemiBold,
Bold,
ExtraBold,
Black,
ExtraBlack,
}
impl FontWeight {
pub fn from_opentype_weight(w: u16) -> Self {
if w >= 1000 {
Self::ExtraBlack
} else if w >= 900 {
Self::Black
} else if w >= 800 {
Self::ExtraBold
} else if w >= 700 {
Self::Bold
} else if w >= 600 {
Self::DemiBold
} else if w >= 500 {
Self::Medium
} else if w >= 400 {
Self::Regular
} else if w >= 380 {
Self::Book
} else if w >= 350 {
Self::DemiLight
} else if w >= 300 {
Self::Light
} else if w >= 200 {
Self::ExtraLight
} else {
Self::Thin
}
}
pub fn to_opentype_weight(self) -> u16 {
match self {
Self::Thin => 100,
Self::ExtraLight => 200,
Self::Light => 300,
Self::DemiLight => 350,
Self::Book => 380,
Self::Regular => 400,
Self::Medium => 500,
Self::DemiBold => 600,
Self::Bold => 700,
Self::ExtraBold => 800,
Self::Black => 900,
Self::ExtraBlack => 1000,
}
}
}
/// Represents a parsed font
#[derive(Debug)]
pub struct ParsedFont {
@ -183,14 +90,8 @@ impl ParsedFont {
pub fn matches_attributes(&self, attr: &FontAttributes) -> FontMatch {
if let Some(fam) = self.names.family.as_ref() {
if attr.family == *fam {
let wanted_width = FontWidth::Normal;
if wanted_width == self.width {
let wanted_weight = if attr.bold {
FontWeight::Bold
} else {
FontWeight::Regular
}
.to_opentype_weight();
if attr.width == self.width {
let wanted_weight = attr.weight.to_opentype_weight();
let weight = self.weight.to_opentype_weight();
if weight >= wanted_weight {