Skip to content

Commit

Permalink
[write-fonts] Add Glyph::Empty
Browse files Browse the repository at this point in the history
This is a cleaner way of representing the idea of a glyph with no
outlines, but which should still be added to loca.

I played around with also adding this in read-fonts, but it doesn't
really play well with codegen or parsing, there, because we expect a
format enum to have a table for each variant, and in the empty case we
have no data from which to parse a table.
  • Loading branch information
cmyr committed Jan 18, 2024
1 parent 5143ffa commit e8a91f0
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 37 deletions.
19 changes: 10 additions & 9 deletions write-fonts/src/tables/glyf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ impl TopLevelTable for Glyf {
/// A simple or composite glyph
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Glyph {
/// An empty glyph gets an entry in `loca`, but no data is written to `glyf`
Empty,
Simple(SimpleGlyph),
Composite(CompositeGlyph),
}
Expand All @@ -57,18 +59,11 @@ impl Glyph {
/// The bounding box for the glyph
pub fn bbox(&self) -> Bbox {
match self {
Glyph::Empty => todo!(),
Glyph::Simple(glyph) => glyph.bbox,
Glyph::Composite(glyph) => glyph.bbox,
}
}

/// 'true' if the glyph contains no contours or components
pub fn is_empty(&self) -> bool {
match self {
Glyph::Simple(glyph) => glyph.contours().is_empty(),
Glyph::Composite(glyph) => glyph.components().is_empty(),
}
}
}

impl Bbox {
Expand Down Expand Up @@ -132,7 +127,11 @@ impl<'a> FontRead<'a> for Glyph {

impl From<SimpleGlyph> for Glyph {
fn from(value: SimpleGlyph) -> Self {
Glyph::Simple(value)
if value.contours().is_empty() {
Glyph::Empty
} else {
Glyph::Simple(value)
}
}
}

Expand All @@ -145,6 +144,7 @@ impl From<CompositeGlyph> for Glyph {
impl Validate for Glyph {
fn validate_impl(&self, ctx: &mut ValidationCtx) {
match self {
Glyph::Empty => (),
Glyph::Simple(glyph) => glyph.validate_impl(ctx),
Glyph::Composite(glyph) => glyph.validate_impl(ctx),
}
Expand All @@ -154,6 +154,7 @@ impl Validate for Glyph {
impl FontWrite for Glyph {
fn write_into(&self, writer: &mut crate::TableWriter) {
match self {
Glyph::Empty => (),
Glyph::Simple(glyph) => glyph.write_into(writer),
Glyph::Composite(glyph) => glyph.write_into(writer),
}
Expand Down
35 changes: 7 additions & 28 deletions write-fonts/src/tables/glyf/glyf_loca_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,7 @@ pub struct GlyfLocaBuilder {
/// A trait encompassing [`Glyph`], [`SimpleGlyph`] and [`CompositeGlyph`]
///
/// This is a marker trait to ensure that only glyphs are passed to [`GlyfLocaBuilder`].
pub trait SomeGlyph: Validate + FontWrite {
/// Returns `true` if the glyph contains no contours or components.
///
/// If a glyph is empty, we do not need to write any data at all for the glyph,
/// and we insert a duplicate value in the loca table.
fn is_empty(&self) -> bool;
}
pub trait SomeGlyph: Validate + FontWrite {}

impl GlyfLocaBuilder {
/// Construct a new builder for the 'glyf' and 'loca' tables.
Expand All @@ -65,10 +59,8 @@ impl GlyfLocaBuilder {
/// The glyph is validated and compiled immediately, so that the caller can
/// associate any errors with a particular glyph.
pub fn add_glyph(&mut self, glyph: &impl SomeGlyph) -> Result<&mut Self, Error> {
if !glyph.is_empty() {
glyph.validate()?;
glyph.write_into(&mut self.glyph_writer);
}
glyph.validate()?;
glyph.write_into(&mut self.glyph_writer);
let pos = self.glyph_writer.current_data().bytes.len();
self.raw_loca.push(pos as u32);
Ok(self)
Expand All @@ -89,23 +81,11 @@ impl GlyfLocaBuilder {
}
}

impl SomeGlyph for SimpleGlyph {
fn is_empty(&self) -> bool {
self.contours().is_empty()
}
}
impl SomeGlyph for SimpleGlyph {}

impl SomeGlyph for CompositeGlyph {
fn is_empty(&self) -> bool {
self.components().is_empty()
}
}
impl SomeGlyph for CompositeGlyph {}

impl SomeGlyph for Glyph {
fn is_empty(&self) -> bool {
self.is_empty()
}
}
impl SomeGlyph for Glyph {}

impl Default for GlyfLocaBuilder {
fn default() -> Self {
Expand Down Expand Up @@ -133,11 +113,10 @@ mod tests {
path.line_to((0., 0.));
path
}
let notdef = BezPath::new();
let square = kurbo::Rect::from_points((5., 5.), (100., 100.)).into_path(0.1);
let triangle = make_triangle();

let glyph0 = SimpleGlyph::from_bezpath(&notdef).unwrap();
let glyph0 = Glyph::Empty;
let glyph1 = SimpleGlyph::from_bezpath(&square).unwrap();
let glyph2 = SimpleGlyph::from_bezpath(&triangle).unwrap();
let gid1 = GlyphId::new(1);
Expand Down

0 comments on commit e8a91f0

Please sign in to comment.