Pass pre-normalized coords to the swash scaler for variable fonts
This commit is contained in:
parent
ddad5fb741
commit
d5a972a2b6
1 changed files with 65 additions and 10 deletions
75
src/swash.rs
75
src/swash.rs
|
|
@ -33,11 +33,10 @@ fn swash_image(
|
|||
.size(f32::from_bits(cache_key.font_size_bits))
|
||||
.hint(!cache_key.flags.contains(CacheKeyFlags::DISABLE_HINTING));
|
||||
if let Some(variation) = variable_width {
|
||||
scaler = scaler.variations(std::iter::once(swash::Setting {
|
||||
tag: swash::Tag::from_be_bytes(*b"wght"),
|
||||
value: f32::from(cache_key.font_weight.0)
|
||||
.clamp(variation.min_value(), variation.max_value()),
|
||||
}));
|
||||
scaler = scaler.normalized_coords(font.as_swash().variations().normalized_coords([(
|
||||
swash::Tag::from_be_bytes(*b"wght"),
|
||||
f32::from(cache_key.font_weight.0).clamp(variation.min_value(), variation.max_value()),
|
||||
)]));
|
||||
}
|
||||
let mut scaler = scaler.build();
|
||||
|
||||
|
|
@ -100,11 +99,10 @@ fn swash_outline_commands(
|
|||
.size(f32::from_bits(cache_key.font_size_bits))
|
||||
.hint(!cache_key.flags.contains(CacheKeyFlags::DISABLE_HINTING));
|
||||
if let Some(variation) = variable_width {
|
||||
scaler = scaler.variations(std::iter::once(swash::Setting {
|
||||
tag: swash::Tag::from_be_bytes(*b"wght"),
|
||||
value: f32::from(cache_key.font_weight.0)
|
||||
.clamp(variation.min_value(), variation.max_value()),
|
||||
}));
|
||||
scaler = scaler.normalized_coords(font.as_swash().variations().normalized_coords([(
|
||||
swash::Tag::from_be_bytes(*b"wght"),
|
||||
f32::from(cache_key.font_weight.0).clamp(variation.min_value(), variation.max_value()),
|
||||
)]));
|
||||
}
|
||||
let mut scaler = scaler.build();
|
||||
|
||||
|
|
@ -244,3 +242,60 @@ impl SwashCache {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use swash::{FontRef, Setting, Tag};
|
||||
|
||||
// variations() resizes context.coords in place (stale values persist),
|
||||
// whereas using normalized_coords() clears and replaces them.
|
||||
#[test]
|
||||
fn no_coord_leakage_across_fonts() {
|
||||
let [Ok(sfns), Ok(sfns_italic)] = [
|
||||
"/System/Library/Fonts/SFNS.ttf",
|
||||
"/System/Library/Fonts/SFNSItalic.ttf",
|
||||
]
|
||||
.map(std::fs::read) else {
|
||||
return;
|
||||
};
|
||||
let regular = FontRef::from_index(&sfns, 0).unwrap();
|
||||
let italic = FontRef::from_index(&sfns_italic, 0).unwrap();
|
||||
let wght = Tag::from_be_bytes(*b"wght");
|
||||
|
||||
let render = |ctx: &mut ScaleContext, font: FontRef, weight: f32, use_normalized| {
|
||||
let mut b = ctx.builder(font).size(16.0).hint(true);
|
||||
if use_normalized {
|
||||
b = b.normalized_coords(font.variations().normalized_coords([(wght, weight)]));
|
||||
} else {
|
||||
b = b.variations(std::iter::once(Setting {
|
||||
tag: wght,
|
||||
value: weight,
|
||||
}));
|
||||
}
|
||||
Render::new(&[Source::Outline])
|
||||
.format(Format::Alpha)
|
||||
.render(&mut b.build(), 36)
|
||||
};
|
||||
|
||||
// reference: regular@400 with no prior context
|
||||
let mut ctx = ScaleContext::new();
|
||||
let reference = render(&mut ctx, regular, 400.0, false).map(|i| i.data);
|
||||
|
||||
// variations(): pollute ctx with italic@700, then render regular@400
|
||||
let mut ctx = ScaleContext::new();
|
||||
render(&mut ctx, italic, 700.0, false);
|
||||
let not_normalized = render(&mut ctx, regular, 400.0, false).map(|i| i.data);
|
||||
|
||||
// normalized_coords(): same sequence
|
||||
let mut ctx = ScaleContext::new();
|
||||
render(&mut ctx, italic, 700.0, true);
|
||||
let normalized = render(&mut ctx, regular, 400.0, true).map(|i| i.data);
|
||||
|
||||
assert_ne!(not_normalized, reference, "variations leak across fonts");
|
||||
assert_eq!(
|
||||
normalized, reference,
|
||||
"normalized_coords match clean render"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue