Skip to content

Commit 02ad2a4

Browse files
authored
Add support for variable fonts (#13)
1 parent 63de755 commit 02ad2a4

21 files changed

+4514
-103
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ jobs:
2525
- uses: dtolnay/rust-toolchain@stable
2626
- run: cargo build
2727
name: Build
28+
- run: cargo build --no-default-features
29+
name: Build without default features
2830
- run: cargo test
2931
name: Run tests
3032

Cargo.lock

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

Cargo.toml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,16 @@ repository = { workspace = true }
2323
readme = { workspace = true }
2424
license = { workspace = true }
2525

26+
[features]
27+
default = ["variable-fonts"]
28+
variable-fonts = ["dep:skrifa", "dep:write-fonts", "dep:kurbo"]
29+
2630
[dependencies]
2731
rustc-hash = "2.1"
32+
skrifa = { optional = true, version = "0.33" }
33+
kurbo = { optional = true, version = "0.11" }
34+
write-fonts = { optional = true, version = "0.39.1"}
2835

2936
[dev-dependencies]
30-
skrifa = "0.29.0"
37+
skrifa = "0.33"
3138
ttf-parser = "0.25.1"

NOTICE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ SOFTWARE.
6666
The SIL OPEN FONT LICENSE Version 1.1 applies to the following fonts:
6767
- fonts/ClickerScript-Regular.ttf
6868
- fonts/MPLUS1p-Regular.ttf
69+
- fonts/Cantarell-VF.otf
6970
- fonts/NotoSans-Regular.ttf
71+
- fonts/NotoSans-Regular_var.ttf
7072
- fonts/NotoSansCJKsc-Bold-subset1.otf
7173
- fonts/NotoSansCJKsc-Regular.otf
7274
- fonts/Syne-Regular_subset.oft (Copyright 2017 The Syne Project Authors (https://gitlab.com/bonjour-monde/fonderie/syne-typeface))

fonts/Cantarell-VF.otf

167 KB
Binary file not shown.

fonts/NotoSans-Regular_var.ttf

2.38 MB
Binary file not shown.

src/cff2.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use crate::interjector::Interjector;
2+
use crate::Error::MalformedFont;
3+
use crate::{glyf, Context, MaxpData};
4+
use std::borrow::Cow;
5+
6+
/// CFF2 fonts will currently be converted into TTF fonts.
7+
pub fn subset(ctx: &mut Context) -> crate::Result<()> {
8+
let mut maxp_data = MaxpData::default();
9+
10+
let result = glyf::subset_with(ctx, |old_gid, ctx| {
11+
let data = match &ctx.interjector {
12+
// We reject CFF2 fonts earlier if `variable-fonts` feature is not enabled.
13+
Interjector::Dummy(_) => unreachable!(),
14+
#[cfg(feature = "variable-fonts")]
15+
Interjector::Skrifa(s) => {
16+
Cow::Owned(s.glyph_data(&mut maxp_data, old_gid).ok_or(MalformedFont)?)
17+
}
18+
};
19+
20+
Ok(data)
21+
});
22+
23+
ctx.custom_maxp_data = Some(maxp_data);
24+
result
25+
}

src/glyf.rs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,29 @@ pub fn closure(face: &Face, glyph_remapper: &mut GlyphRemapper) -> Result<()> {
4646
}
4747

4848
pub fn subset(ctx: &mut Context) -> Result<()> {
49-
let subsetted_entries = subset_glyf_entries(ctx)?;
49+
let table = Table::new(&ctx.face).ok_or(MalformedFont)?;
50+
let mut _maxp = MaxpData::default();
51+
52+
subset_with(ctx, |old_gid, ctx| {
53+
let data = match &ctx.interjector {
54+
Interjector::Dummy(_) => {
55+
Cow::Borrowed(table.glyph_data(old_gid).ok_or(MalformedFont)?)
56+
}
57+
#[cfg(feature = "variable-fonts")]
58+
Interjector::Skrifa(s) => {
59+
Cow::Owned(s.glyph_data(&mut _maxp, old_gid).ok_or(MalformedFont)?)
60+
}
61+
};
62+
63+
Ok(data)
64+
})
65+
}
66+
67+
pub(crate) fn subset_with<'a>(
68+
ctx: &mut Context<'a>,
69+
glyph_data_fn: impl FnMut(u16, &Context<'a>) -> Result<Cow<'a, [u8]>>,
70+
) -> Result<()> {
71+
let subsetted_entries = subset_glyf_entries(ctx, glyph_data_fn)?;
5072

5173
let mut sub_glyf = Writer::new();
5274
let mut sub_loca = Writer::new();
@@ -112,29 +134,30 @@ impl<'a> Table<'a> {
112134
}
113135
}
114136

115-
fn subset_glyf_entries<'a>(ctx: &mut Context<'a>) -> Result<Vec<Cow<'a, [u8]>>> {
116-
let table = Table::new(&ctx.face).ok_or(MalformedFont)?;
117-
137+
fn subset_glyf_entries<'a>(
138+
ctx: &mut Context<'a>,
139+
mut glyph_data_fn: impl FnMut(u16, &Context<'a>) -> Result<Cow<'a, [u8]>>,
140+
) -> Result<Vec<Cow<'a, [u8]>>> {
118141
let mut size = 0;
119142
let mut glyf_entries = vec![];
120143

121144
for old_gid in ctx.mapper.remapped_gids() {
122-
let glyph_data = table.glyph_data(old_gid).ok_or(MalformedFont)?;
145+
let glyph_data = glyph_data_fn(old_gid, ctx)?;
123146

124147
// Empty glyph.
125148
if glyph_data.is_empty() {
126-
glyf_entries.push(Cow::Borrowed(glyph_data));
149+
glyf_entries.push(glyph_data);
127150
continue;
128151
}
129152

130-
let mut r = Reader::new(glyph_data);
153+
let mut r = Reader::new(&glyph_data);
131154
let num_contours = r.read::<i16>().ok_or(MalformedFont)?;
132155

133156
let glyph_data = if num_contours < 0 {
134-
Cow::Owned(remap_component_glyph(&ctx.mapper, glyph_data)?)
157+
Cow::Owned(remap_component_glyph(&ctx.mapper, &glyph_data)?)
135158
} else {
136159
// Simple glyphs don't need any subsetting.
137-
Cow::Borrowed(glyph_data)
160+
glyph_data
138161
};
139162

140163
let mut len = glyph_data.len();

0 commit comments

Comments
 (0)