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 3 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
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
79 changes: 69 additions & 10 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 @@ -477,10 +488,12 @@ 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 {
/// Returns an iterator that yields the glyph identifier and flags of each
/// component in the composite glyph.
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 @@ -581,19 +594,19 @@ impl Iterator for ComponentIter<'_> {
}
}

/// Iterator that only returns glyph identifiers for each component.
/// Iterator that only returns glyph identifiers and flags for each component.
///
/// 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 overlap bit on the first flag
assert_eq!(glyph.has_overlaps, gid == 2 || gid == 3);
}
}
}
22 changes: 20 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,24 @@ 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();
for gid in 0..glyph_count {
let metrics = scaler.outline(GlyphId::new(gid), &mut path).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!(metrics.has_overlaps, gid == 2 || gid == 3);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same preference for how to assert this as #582

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aye

}

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