Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[skrifa] return overlap status from scaler #583

Merged
merged 6 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 42 additions & 38 deletions read-fonts/src/tables/glyf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -958,50 +958,54 @@ mod tests {
);
}

#[test]
fn simple_glyph_overlapping_contour_flag() {
let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
// Test helper to enumerate all TrueType glyphs in the given font
fn all_glyphs(font_data: &[u8]) -> impl Iterator<Item = Option<Glyph>> {
let font = FontRef::new(font_data).unwrap();
let loca = font.loca(None).unwrap();
let glyf = font.glyf().unwrap();
let glyph_count = font.maxp().unwrap().num_glyphs();
for gid in 0..glyph_count {
let glyph = match loca.get_glyf(GlyphId::new(gid), &glyf) {
Ok(Some(Glyph::Simple(glyph))) => glyph,
_ => continue,
};
if gid == 3 {
// Only GID 3 has the overlap bit set
assert!(glyph.has_overlapping_contours())
} else {
assert!(!glyph.has_overlapping_contours())
}
}
(0..glyph_count).map(move |gid| loca.get_glyf(GlyphId::new(gid), &glyf).unwrap())
}

#[test]
fn composite_overlapping_contour_flag() {
let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
let loca = font.loca(None).unwrap();
let glyf = font.glyf().unwrap();
let glyph_count = font.maxp().unwrap().num_glyphs();
for gid in 0..glyph_count {
let glyph = match loca.get_glyf(GlyphId::new(gid), &glyf) {
Ok(Some(Glyph::Composite(glyph))) => glyph,
_ => continue,
};
// Only GID 2, component 1 has the overlap bit set
for (component_ix, component) in glyph.components().enumerate() {
if gid == 2 && component_ix == 1 {
assert!(component
.flags
.contains(CompositeGlyphFlags::OVERLAP_COMPOUND))
} else {
assert!(!component
.flags
.contains(CompositeGlyphFlags::OVERLAP_COMPOUND))
}
}
}
fn simple_glyph_overlapping_contour_flag() {
let gids_with_overlap: Vec<_> = all_glyphs(font_test_data::VAZIRMATN_VAR)
.enumerate()
.filter_map(|(gid, glyph)| match glyph {
Some(Glyph::Simple(glyph)) if glyph.has_overlapping_contours() => Some(gid),
_ => None,
})
.collect();
// Only GID 3 has the overlap bit set
let expected_gids_with_overlap = vec![3];
assert_eq!(expected_gids_with_overlap, gids_with_overlap);
}

#[test]
fn composite_glyph_overlapping_contour_flag() {
let gids_components_with_overlap: Vec<_> = all_glyphs(font_test_data::VAZIRMATN_VAR)
.enumerate()
.filter_map(|(gid, glyph)| match glyph {
Some(Glyph::Composite(glyph)) => Some((gid, glyph)),
_ => None,
})
.flat_map(|(gid, glyph)| {
glyph
.components()
.enumerate()
.filter_map(move |(comp_ix, comp)| {
comp.flags
.contains(CompositeGlyphFlags::OVERLAP_COMPOUND)
.then_some((gid, comp_ix))
})
})
.collect();
// Only GID 2, component 1 has the overlap bit set
let expected_gids_components_with_overlap = vec![(2, 1)];
assert_eq!(
expected_gids_components_with_overlap,
gids_components_with_overlap
);
}

#[test]
Expand Down
19 changes: 13 additions & 6 deletions skrifa/src/scale/glyf/scaler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,11 +746,18 @@ mod tests {
let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
let scaler = Scaler::new(&font).unwrap();
let glyph_count = font.maxp().unwrap().num_glyphs();
for gid in 0..glyph_count {
let glyph = scaler.glyph(GlyphId::new(gid), false).unwrap();
// GID 2 is a composite glyph with the overlap bit on a component
// GID 3 is a simple glyph with the overlap bit on the first flag
assert_eq!(glyph.has_overlaps, gid == 2 || gid == 3);
}
// GID 2 is a composite glyph with the overlap bit on a component
// GID 3 is a simple glyph with the overlap bit on the first flag
let expected_gids_with_overlap = vec![2, 3];
assert_eq!(
expected_gids_with_overlap,
(0..glyph_count)
.filter_map(|gid| scaler
.glyph(GlyphId::new(gid), false)
.unwrap()
.has_overlaps
.then_some(gid))
.collect::<Vec<_>>()
);
}
}
29 changes: 27 additions & 2 deletions skrifa/src/scale/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ mod scaler;
pub use read_fonts::types::Pen;

pub use error::{Error, Result};
pub use scaler::{Scaler, ScalerBuilder};
pub use scaler::{Scaler, ScalerBuilder, ScalerMetrics};

use super::{
font::UniqueId,
Expand Down Expand Up @@ -217,7 +217,7 @@ impl Context {
#[cfg(test)]
mod tests {
use super::{Context, Size};
use read_fonts::{scaler_test, FontRef};
use read_fonts::{scaler_test, types::GlyphId, FontRef, TableProvider};

#[test]
fn vazirmatin_var() {
Expand Down Expand Up @@ -246,6 +246,31 @@ mod tests {
);
}

#[test]
fn overlap_flags() {
let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
let mut cx = Context::new();
let mut path = scaler_test::Path {
elements: vec![],
is_cff: false,
};
let mut scaler = cx.new_scaler().build(&font);
let glyph_count = font.maxp().unwrap().num_glyphs();
// GID 2 is a composite glyph with the overlap bit on a component
// GID 3 is a simple glyph with the overlap bit on the first flag
let expected_gids_with_overlap = vec![2, 3];
assert_eq!(
expected_gids_with_overlap,
(0..glyph_count)
.filter_map(|gid| scaler
.outline(GlyphId::new(gid), &mut path)
.unwrap()
.has_overlaps
.then_some(gid))
.collect::<Vec<_>>()
);
}

fn compare_glyphs(font_data: &[u8], expected_outlines: &str, is_cff: bool) {
let font = FontRef::new(font_data).unwrap();
let outlines = scaler_test::parse_glyph_outlines(expected_outlines);
Expand Down
29 changes: 25 additions & 4 deletions skrifa/src/scale/scaler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@ use read_fonts::{
TableProvider,
};

/// Information and adjusted metrics generated while scaling a glyph.
#[derive(Copy, Clone, Default, Debug)]
pub struct ScalerMetrics {
/// True if the underlying glyph contains flags indicating the
/// presence of overlapping contours or components.
pub has_overlaps: bool,
/// If present, an adjusted left side bearing value generated by the
/// scaler.
pub adjusted_lsb: Option<f32>,
/// If present, an adjusted advance width value generated by the
/// scaler.
pub adjusted_advance_width: Option<f32>,
}

/// Builder for configuring a glyph scaler.
///
/// See the [module level documentation](crate::scale#building-a-scaler)
Expand Down Expand Up @@ -206,7 +220,7 @@ impl<'a> Scaler<'a> {

/// Loads a simple outline for the specified glyph identifier and invokes the functions
/// in the given pen for the sequence of path commands that define the outline.
pub fn outline(&mut self, glyph_id: GlyphId, pen: &mut impl Pen) -> Result<()> {
pub fn outline(&mut self, glyph_id: GlyphId, pen: &mut impl Pen) -> Result<ScalerMetrics> {
if let Some(outlines) = &mut self.outlines {
outlines.outline(glyph_id, self.size, self.coords, pen)
} else {
Expand All @@ -230,7 +244,7 @@ impl<'a> Outlines<'a> {
size: f32,
coords: &'a [NormalizedCoord],
pen: &mut impl Pen,
) -> Result<()> {
) -> Result<ScalerMetrics> {
match self {
Self::TrueType(scaler, buf) => {
let glyph = scaler.glyph(glyph_id, false)?;
Expand All @@ -242,14 +256,21 @@ impl<'a> Outlines<'a> {
.memory_from_buffer(&mut buf[..])
.ok_or(Error::InsufficientMemory)?;
let outline = scaler.outline(memory, &glyph, size, coords)?;
Ok(outline.to_path(pen)?)
outline.to_path(pen)?;
Ok(ScalerMetrics {
has_overlaps: glyph.has_overlaps,
..Default::default()
})
}
Self::PostScript(scaler, subfont) => {
let subfont_index = scaler.subfont_index(glyph_id);
if subfont_index != subfont.index() {
*subfont = scaler.subfont(subfont_index, size, coords)?;
}
Ok(scaler.outline(subfont, glyph_id, coords, false, pen)?)
scaler.outline(subfont, glyph_id, coords, false, pen)?;
// CFF does not have overlap flags and hinting never adjusts
// horizontal metrics
Ok(ScalerMetrics::default())
}
}
}
Expand Down