Skip to content

Commit 84cb5aa

Browse files
committed
feat: variable fonts
1 parent f1c7efd commit 84cb5aa

File tree

5 files changed

+106
-25
lines changed

5 files changed

+106
-25
lines changed

Cargo.lock

Lines changed: 21 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,9 @@ pdf = "0.7"
3535

3636
[workspace]
3737
members = ["crates/*"]
38+
39+
[patch.crates-io.fontdue]
40+
# variable font support
41+
git = 'https://github.com/xiphoseer/fontdue.git'
42+
rev = "7eb87c059487ff8a42e2488e40aaa6e1f1ac4480"
43+
#path = "../repos/fontdue"

crates/signum/src/raster/page.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,7 @@ impl Page {
525525
pub fn from_image(g: &GrayImage, threshold: u8, hpad: (u8, u8)) -> Self {
526526
let width = g.width() + hpad.0 as u32 + hpad.1 as u32;
527527
let height = g.height();
528-
let bytes_per_line = (width - 1) / 8 + 1;
528+
let bytes_per_line = if width > 0 { (width - 1) / 8 + 1 } else { 0 };
529529
let mut bit_writer = BitWriter::new();
530530
let mut c = 0;
531531
assert!(hpad.0 < 32);

crates/ttf2sig/src/bin/ttf-info.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ fn main() -> color_eyre::Result<()> {
3535
let l = LigatureInfo::new(&face);
3636
let gpos = KerningInfo::new(&face);
3737

38+
for ax in face.variation_axes() {
39+
println!("var: {:?}", ax);
40+
}
41+
3842
debug_kern(&face);
3943

4044
let lig = opt.ligature.chars().collect::<Vec<_>>();
@@ -58,14 +62,12 @@ fn main() -> color_eyre::Result<()> {
5862
let fm = FontMetrics::new(pk, font_size);
5963
let px = fm.em_square_pixels() as f32;
6064

61-
let font = fontdue::Font::from_bytes(
62-
&data[..],
63-
fontdue::FontSettings {
64-
collection_index: opt.index,
65-
scale: 40.0,
66-
load_substitutions: true,
67-
},
68-
)
65+
let font = fontdue::Font::from_bytes(&data[..], {
66+
let mut settings = fontdue::FontSettings::default();
67+
settings.collection_index = opt.index;
68+
settings.load_substitutions = true;
69+
settings
70+
})
6971
.expect("already ttf parsed");
7072

7173
for pair in lig.windows(2) {

crates/ttf2sig/src/main.rs

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
use std::{
22
convert::TryInto,
3+
fmt,
34
io::BufWriter,
4-
num::TryFromIntError,
5+
num::{ParseFloatError, TryFromIntError},
56
path::{Path, PathBuf},
7+
str::FromStr,
68
};
79

810
use clap::Parser;
911
use color_eyre::eyre::{self, eyre, Context, ContextCompat};
12+
use fontdue::VariationAxis;
1013
use signum::{
1114
chsets::{
1215
editor::{EChar, ESet, ECHAR_NULL},
@@ -48,6 +51,63 @@ pub struct Opts {
4851
/// Threshold at which to treat coverage as "on"
4952
#[clap(short, long, default_value = "170")]
5053
threshold: u8,
54+
55+
#[clap(short, long)]
56+
variation: Vec<Variation>,
57+
}
58+
59+
#[derive(Debug, Copy, Clone)]
60+
struct Variation {
61+
axis: VariationAxis,
62+
value: f32,
63+
}
64+
65+
fn variation_tag(s: &str) -> Option<VariationAxis> {
66+
if s.is_ascii() && s.len() == 4 {
67+
let mut chars = s.chars();
68+
let a = chars.next()?;
69+
let b = chars.next()?;
70+
let c = chars.next()?;
71+
let d = chars.next()?;
72+
Some(VariationAxis::from_bytes([
73+
a as u8, b as u8, c as u8, d as u8,
74+
]))
75+
} else {
76+
None
77+
}
78+
}
79+
80+
#[derive(Debug, Clone)]
81+
enum VariationError {
82+
NoEquals,
83+
MalformedTag,
84+
#[allow(dead_code)]
85+
Value(ParseFloatError),
86+
}
87+
88+
impl From<ParseFloatError> for VariationError {
89+
fn from(value: ParseFloatError) -> Self {
90+
Self::Value(value)
91+
}
92+
}
93+
94+
impl std::error::Error for VariationError {}
95+
96+
impl fmt::Display for VariationError {
97+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98+
<Self as fmt::Debug>::fmt(self, f)
99+
}
100+
}
101+
102+
impl FromStr for Variation {
103+
type Err = VariationError;
104+
105+
fn from_str(s: &str) -> Result<Self, Self::Err> {
106+
let (first, rest) = s.split_once("=").ok_or(VariationError::NoEquals)?;
107+
let axis = variation_tag(first).ok_or(VariationError::MalformedTag)?;
108+
let value = f32::from_str(rest)?;
109+
Ok(Variation { axis, value })
110+
}
51111
}
52112

53113
fn main() -> eyre::Result<()> {
@@ -62,11 +122,13 @@ fn main() -> eyre::Result<()> {
62122
let ligatures = LigatureInfo::new(&face);
63123

64124
// Parse it into the font type.
65-
let font_settings = fontdue::FontSettings {
66-
collection_index: opt.index,
67-
load_substitutions: true,
68-
..Default::default()
69-
};
125+
let mut font_settings = fontdue::FontSettings::default();
126+
font_settings.collection_index = opt.index;
127+
font_settings.load_substitutions = true;
128+
129+
for var in &opt.variation {
130+
font_settings.variations.insert(var.axis, var.value);
131+
}
70132
let font = fontdue::Font::from_bytes(&font[..], font_settings)
71133
.map_err(|e| eyre!("Failed to load font: {}", e))?;
72134

0 commit comments

Comments
 (0)