Skip to content

Commit

Permalink
Capture overlap bits from TrueType glyphs
Browse files Browse the repository at this point in the history
First step in addressing #581. Captures simple and composite overlap bits and plumbs them through internally.

Still needs to be exposed in skrifa public API. This will be done in a follow up.
  • Loading branch information
dfrg committed Aug 24, 2023
1 parent 830b113 commit 211ba5d
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 10 deletions.
Binary file modified font-test-data/test_data/ttf/vazirmatn_var_trimmed.ttf
Binary file not shown.
4 changes: 2 additions & 2 deletions font-test-data/test_data/ttx/vazirmatn_var_trimmed.ttx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ and is intended to contain examples of many variable font tables -->

<TTGlyph name="Agrave" xMin="29" yMin="0" xMax="1310" yMax="1847">
<component glyphName="A" x="0" y="0" flags="0x204"/>
<component glyphName="grave" x="303" y="311" flags="0x4"/>
<component glyphName="grave" x="303" y="311" flags="0x404"/>
<instructions>
<assembly>
PUSHB[ ] /* 2 values pushed */
Expand All @@ -142,7 +142,7 @@ and is intended to contain examples of many variable font tables -->

<TTGlyph name="grave" xMin="57" yMin="1242" xMax="474" yMax="1536">
<contour>
<pt x="281" y="1536" on="1"/>
<pt x="281" y="1536" on="1" overlap="1"/>
<pt x="474" y="1242" on="1"/>
<pt x="315" y="1242" on="1"/>
<pt x="57" y="1536" on="1"/>
Expand Down
73 changes: 66 additions & 7 deletions read-fonts/src/tables/glyf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,17 @@ impl<'a> SimpleGlyph<'a> {
.unwrap_or(0)
}

/// Returns true if the contours in the simple glyph may overlap.
pub fn has_overlapping_contours(&self) -> bool {
// Checks the first flag for the OVERLAP_SIMPLE bit.
// Spec says: "When used, it must be set on the first flag byte for
// the glyph."
FontData::new(self.glyph_data())
.read_at::<SimpleGlyphFlags>(0)
.map(|flag| flag.contains(SimpleGlyphFlags::OVERLAP_SIMPLE))
.unwrap_or_default()
}

/// Reads points and flags into the provided buffers.
///
/// Drops all flag bits except on-curve. The lengths of the buffers must be
Expand Down Expand Up @@ -479,8 +490,10 @@ impl<'a> CompositeGlyph<'a> {

/// Returns an iterator that yields the glyph identifier of each component
/// in the composite glyph.
pub fn component_glyphs(&self) -> impl Iterator<Item = GlyphId> + 'a + Clone {
ComponentGlyphIdIter {
pub fn component_glyphs_and_flags(
&self,
) -> impl Iterator<Item = (GlyphId, CompositeGlyphFlags)> + 'a + Clone {
ComponentGlyphIdFlagsIter {
cur_flags: CompositeGlyphFlags::empty(),
done: false,
cursor: FontData::new(self.component_data()).cursor(),
Expand All @@ -490,7 +503,7 @@ impl<'a> CompositeGlyph<'a> {
/// Returns the component count and TrueType interpreter instructions
/// in a single pass.
pub fn count_and_instructions(&self) -> (usize, Option<&'a [u8]>) {
let mut iter = ComponentGlyphIdIter {
let mut iter = ComponentGlyphIdFlagsIter {
cur_flags: CompositeGlyphFlags::empty(),
done: false,
cursor: FontData::new(self.component_data()).cursor(),
Expand Down Expand Up @@ -586,14 +599,14 @@ impl Iterator for ComponentIter<'_> {
/// Significantly faster in cases where we're just processing the glyph
/// tree, counting components or accessing instructions.
#[derive(Clone)]
struct ComponentGlyphIdIter<'a> {
struct ComponentGlyphIdFlagsIter<'a> {
cur_flags: CompositeGlyphFlags,
done: bool,
cursor: Cursor<'a>,
}

impl Iterator for ComponentGlyphIdIter<'_> {
type Item = GlyphId;
impl Iterator for ComponentGlyphIdFlagsIter<'_> {
type Item = (GlyphId, CompositeGlyphFlags);

fn next(&mut self) -> Option<Self::Item> {
if self.done {
Expand All @@ -616,7 +629,7 @@ impl Iterator for ComponentGlyphIdIter<'_> {
self.cursor.advance_by(8);
}
self.done = !flags.contains(CompositeGlyphFlags::MORE_COMPONENTS);
Some(glyph)
Some((glyph, flags))
}
}

Expand Down Expand Up @@ -945,6 +958,52 @@ mod tests {
);
}

#[test]
fn simple_glyph_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::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())
}
}
}

#[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))
}
}
}
}

#[test]
fn compute_anchor_flags() {
let anchor = Anchor::Offset { x: -128, y: 127 };
Expand Down
2 changes: 2 additions & 0 deletions skrifa/src/scale/glyf/glyph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pub struct ScalerGlyph<'a> {
pub has_hinting: bool,
/// True if the glyph requires variation delta processing.
pub has_variations: bool,
/// True if the glyph contains any simple or compound overlap flags.
pub has_overlaps: bool,
}

impl<'a> ScalerGlyph<'a> {
Expand Down
2 changes: 2 additions & 0 deletions skrifa/src/scale/glyf/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ mod tests {
max_component_delta_stack: 4,
has_hinting: false,
has_variations: true,
has_overlaps: false,
};
let required_size = outline_info.required_buffer_size();
let mut buf = vec![0u8; required_size];
Expand Down Expand Up @@ -185,6 +186,7 @@ mod tests {
max_component_delta_stack: 4,
has_hinting: false,
has_variations: true,
has_overlaps: false,
};
// Required size adds 4 bytes slop to account for internal alignment
// requirements. So subtract 5 to force a failure.
Expand Down
23 changes: 22 additions & 1 deletion skrifa/src/scale/glyf/scaler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,14 @@ impl<'a> Scaler<'a> {
info.contours += simple.end_pts_of_contours().len();
info.has_hinting = info.has_hinting || simple.instruction_length() != 0;
info.max_other_points = info.max_other_points.max(num_points_with_phantom);
info.has_overlaps |= simple.has_overlapping_contours();
}
Glyph::Composite(composite) => {
let (mut count, instructions) = composite.count_and_instructions();
count += PHANTOM_POINT_COUNT;
let point_base = info.points;
for component in composite.component_glyphs() {
for (component, flags) in composite.component_glyphs_and_flags() {
info.has_overlaps |= flags.contains(CompositeGlyphFlags::OVERLAP_COMPOUND);
let component_glyph = self.loca.get_glyf(component, &self.glyf)?;
let Some(component_glyph) = component_glyph else {
continue;
Expand Down Expand Up @@ -733,3 +735,22 @@ impl<'a, H> ScalerInstance<'a, H> {
self.phantom[3].y = self.phantom[2].y - F26Dot6::from_bits(vadvance);
}
}

#[cfg(test)]
mod tests {
use super::*;
use read_fonts::{FontRef, TableProvider};

#[test]
fn overlap_flags() {
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 overflag bit on the first flag
assert_eq!(glyph.has_overlaps, gid == 2 || gid == 3);
}
}
}

0 comments on commit 211ba5d

Please sign in to comment.