fontcull_write_fonts/tables/
glyf.rs

1//! The [glyf (Glyph Data)](https://docs.microsoft.com/en-us/typography/opentype/spec/glyf) table
2
3use crate::{
4    from_obj::{FromObjRef, FromTableRef},
5    validate::{Validate, ValidationCtx},
6    FontWrite, OtRound, TableWriter,
7};
8
9use fontcull_font_types::Tag;
10use fontcull_read_fonts::{FontRead, TopLevelTable};
11use kurbo::Rect;
12
13mod composite;
14mod glyf_loca_builder;
15mod simple;
16
17pub use composite::{Anchor, Component, ComponentFlags, CompositeGlyph, Transform};
18pub use glyf_loca_builder::{GlyfLocaBuilder, SomeGlyph};
19pub use simple::{Contour, MalformedPath, SimpleGlyph};
20
21/// The [glyf (Glyph Data)](https://docs.microsoft.com/en-us/typography/opentype/spec/glyf) table
22///
23/// This table is the concatenated bytes of all the glyphs in the font, with
24/// the positions of each individual glyph stored in the ['loca' table][super::loca].
25/// As such, these two tables must be constructed together. The [`GlyfLocaBuilder`]
26/// type is provided to simplify this.
27#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub struct Glyf(Vec<u8>);
30
31impl TopLevelTable for Glyf {
32    /// 'glyf'
33    const TAG: Tag = Tag::new(b"glyf");
34}
35
36/// A simple or composite glyph
37#[derive(Clone, Debug, PartialEq, Eq)]
38pub enum Glyph {
39    /// An empty glyph gets an entry in `loca`, but no data is written to `glyf`
40    Empty,
41    Simple(SimpleGlyph),
42    Composite(CompositeGlyph),
43}
44
45/// A Bounding box.
46///
47/// This should be the minimum rectangle which fully encloses the glyph outline;
48/// importantly this can only be determined by computing the individual Bezier
49/// segments, and cannot be determiend from points alone.
50#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
51pub struct Bbox {
52    pub x_min: i16,
53    pub y_min: i16,
54    pub x_max: i16,
55    pub y_max: i16,
56}
57
58impl Glyph {
59    /// The bounding box for the glyph
60    pub fn bbox(&self) -> Option<Bbox> {
61        match self {
62            Glyph::Empty => None,
63            Glyph::Simple(glyph) => Some(glyph.bbox),
64            Glyph::Composite(glyph) => Some(glyph.bbox),
65        }
66    }
67}
68
69impl Bbox {
70    /// Return the smallest bounding box covering `self` and `other`
71    pub fn union(self, other: Bbox) -> Bbox {
72        Bbox {
73            x_min: self.x_min.min(other.x_min),
74            y_min: self.y_min.min(other.y_min),
75            x_max: self.x_max.max(other.x_max),
76            y_max: self.y_max.max(other.y_max),
77        }
78    }
79}
80
81impl From<Rect> for Bbox {
82    fn from(value: Rect) -> Self {
83        Bbox {
84            x_min: value.min_x().ot_round(),
85            y_min: value.min_y().ot_round(),
86            x_max: value.max_x().ot_round(),
87            y_max: value.max_y().ot_round(),
88        }
89    }
90}
91
92impl FontWrite for Bbox {
93    fn write_into(&self, writer: &mut crate::TableWriter) {
94        let Bbox {
95            x_min,
96            y_min,
97            x_max,
98            y_max,
99        } = *self;
100        [x_min, y_min, x_max, y_max].write_into(writer)
101    }
102}
103
104impl<'a> FromObjRef<fontcull_read_fonts::tables::glyf::Glyph<'a>> for Glyph {
105    fn from_obj_ref(
106        from: &fontcull_read_fonts::tables::glyf::Glyph<'a>,
107        data: fontcull_read_fonts::FontData,
108    ) -> Self {
109        match from {
110            fontcull_read_fonts::tables::glyf::Glyph::Simple(glyph) => {
111                Self::Simple(SimpleGlyph::from_obj_ref(glyph, data))
112            }
113            fontcull_read_fonts::tables::glyf::Glyph::Composite(glyph) => {
114                Self::Composite(CompositeGlyph::from_obj_ref(glyph, data))
115            }
116        }
117    }
118}
119
120impl FromTableRef<fontcull_read_fonts::tables::glyf::Glyph<'_>> for Glyph {}
121
122impl<'a> FontRead<'a> for Glyph {
123    fn read(
124        data: fontcull_read_fonts::FontData<'a>,
125    ) -> Result<Self, fontcull_read_fonts::ReadError> {
126        fontcull_read_fonts::tables::glyf::Glyph::read(data).map(|g| Glyph::from_table_ref(&g))
127    }
128}
129
130impl From<SimpleGlyph> for Glyph {
131    fn from(value: SimpleGlyph) -> Self {
132        if value.contours.is_empty() {
133            Glyph::Empty
134        } else {
135            Glyph::Simple(value)
136        }
137    }
138}
139
140impl From<CompositeGlyph> for Glyph {
141    fn from(value: CompositeGlyph) -> Self {
142        Glyph::Composite(value)
143    }
144}
145
146impl Validate for Glyph {
147    fn validate_impl(&self, ctx: &mut ValidationCtx) {
148        match self {
149            Glyph::Empty => (),
150            Glyph::Simple(glyph) => glyph.validate_impl(ctx),
151            Glyph::Composite(glyph) => glyph.validate_impl(ctx),
152        }
153    }
154}
155
156impl FontWrite for Glyph {
157    fn write_into(&self, writer: &mut crate::TableWriter) {
158        match self {
159            Glyph::Empty => (),
160            Glyph::Simple(glyph) => glyph.write_into(writer),
161            Glyph::Composite(glyph) => glyph.write_into(writer),
162        }
163    }
164}
165
166impl Validate for Glyf {
167    fn validate_impl(&self, _ctx: &mut ValidationCtx) {}
168}
169
170impl FontWrite for Glyf {
171    fn write_into(&self, writer: &mut TableWriter) {
172        writer.write_slice(&self.0)
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn union_box() {
182        assert_eq!(
183            Bbox {
184                x_min: -1,
185                y_min: -2,
186                x_max: 5,
187                y_max: 6
188            },
189            Bbox {
190                x_min: 0,
191                y_min: 0,
192                x_max: 5,
193                y_max: 6
194            }
195            .union(Bbox {
196                x_min: -1,
197                y_min: -2,
198                x_max: 3,
199                y_max: 4
200            })
201        )
202    }
203}