1use crate::{
4 from_obj::{FromObjRef, FromTableRef, ToOwnedTable},
5 FontWrite,
6};
7
8use read_fonts::{tables::glyf::CompositeGlyphFlags, types::GlyphId16, FontRead};
9
10use super::Bbox;
11
12pub use read_fonts::tables::glyf::{Anchor, Transform};
13
14#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct CompositeGlyph {
17 pub bbox: Bbox,
18 components: Vec<Component>,
19 _instructions: Vec<u8>,
20}
21
22#[derive(Clone, Debug, PartialEq, Eq)]
24pub struct Component {
25 pub glyph: GlyphId16,
26 pub anchor: Anchor,
27 pub flags: ComponentFlags,
28 pub transform: Transform,
29}
30
31#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
42pub struct ComponentFlags {
43 pub round_xy_to_grid: bool,
45 pub use_my_metrics: bool,
48 pub scaled_component_offset: bool,
50 pub unscaled_component_offset: bool,
52 pub overlap_compound: bool,
54}
55
56impl FromObjRef<read_fonts::tables::glyf::CompositeGlyph<'_>> for CompositeGlyph {
57 fn from_obj_ref(
58 from: &read_fonts::tables::glyf::CompositeGlyph,
59 _data: read_fonts::FontData,
60 ) -> Self {
61 let bbox = Bbox {
62 x_min: from.x_min(),
63 y_min: from.y_min(),
64 x_max: from.x_max(),
65 y_max: from.y_max(),
66 };
67 let components = from
68 .components()
69 .map(|c| Component {
70 glyph: c.glyph,
71 anchor: c.anchor,
72 flags: c.flags.into(),
73 transform: c.transform,
74 })
75 .collect();
76 Self {
77 bbox,
78 components,
79 _instructions: from
80 .instructions()
81 .map(|v| v.to_owned())
82 .unwrap_or_default(),
83 }
84 }
85}
86
87impl FromTableRef<read_fonts::tables::glyf::CompositeGlyph<'_>> for CompositeGlyph {}
88
89impl<'a> FontRead<'a> for CompositeGlyph {
90 fn read(data: read_fonts::FontData<'a>) -> Result<Self, read_fonts::ReadError> {
91 read_fonts::tables::glyf::CompositeGlyph::read(data).map(|g| g.to_owned_table())
92 }
93}
94
95impl Component {
96 pub fn new(
98 glyph: GlyphId16,
99 anchor: Anchor,
100 transform: Transform,
101 flags: impl Into<ComponentFlags>,
102 ) -> Self {
103 Component {
104 glyph,
105 anchor,
106 flags: flags.into(),
107 transform,
108 }
109 }
110 fn compute_flag(&self) -> CompositeGlyphFlags {
113 self.anchor.compute_flags() | self.transform.compute_flags() | self.flags.into()
114 }
115
116 fn write_into(&self, writer: &mut crate::TableWriter, extra_flags: CompositeGlyphFlags) {
119 let flags = self.compute_flag() | extra_flags;
120 flags.bits().write_into(writer);
121 self.glyph.write_into(writer);
122 self.anchor.write_into(writer);
123 self.transform.write_into(writer);
124 }
125}
126
127#[derive(Clone, Copy, Debug)]
129#[non_exhaustive]
130pub struct NoComponents;
131
132impl std::fmt::Display for NoComponents {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 write!(f, "A composite glyph must contain at least one component")
135 }
136}
137
138impl std::error::Error for NoComponents {}
139
140impl CompositeGlyph {
141 pub fn new(component: Component, bbox: impl Into<Bbox>) -> Self {
148 Self {
149 bbox: bbox.into(),
150 components: vec![component],
151 _instructions: Default::default(),
152 }
153 }
154
155 pub fn add_component(&mut self, component: Component, bbox: impl Into<Bbox>) {
160 self.components.push(component);
161 self.bbox = self.bbox.union(bbox.into());
162 }
163
164 pub fn try_from_iter(
169 source: impl IntoIterator<Item = (Component, Bbox)>,
170 ) -> Result<Self, NoComponents> {
171 let mut components = Vec::new();
172 let mut union_box: Option<Bbox> = None;
173
174 for (component, bbox) in source {
175 components.push(component);
176 union_box.get_or_insert(bbox).union(bbox);
177 }
178
179 if components.is_empty() {
180 Err(NoComponents)
181 } else {
182 Ok(CompositeGlyph {
183 bbox: union_box.unwrap(),
184 components,
185 _instructions: Default::default(),
186 })
187 }
188 }
189
190 pub fn components(&self) -> &[Component] {
191 &self.components
192 }
193}
194
195impl FontWrite for CompositeGlyph {
196 fn write_into(&self, writer: &mut crate::TableWriter) {
197 const N_CONTOURS: i16 = -1;
198 N_CONTOURS.write_into(writer);
199 self.bbox.write_into(writer);
200 let (last, rest) = self
201 .components
202 .split_last()
203 .expect("empty composites checked in validation");
204 for comp in rest {
205 comp.write_into(writer, CompositeGlyphFlags::MORE_COMPONENTS);
206 }
207 let last_flags = if self._instructions.is_empty() {
208 CompositeGlyphFlags::empty()
209 } else {
210 CompositeGlyphFlags::WE_HAVE_INSTRUCTIONS
211 };
212 last.write_into(writer, last_flags);
213
214 if !self._instructions.is_empty() {
215 (self._instructions.len() as u16).write_into(writer);
216 self._instructions.write_into(writer);
217 }
218 writer.pad_to_2byte_aligned();
219 }
220}
221
222impl crate::validate::Validate for CompositeGlyph {
223 fn validate_impl(&self, ctx: &mut crate::codegen_prelude::ValidationCtx) {
224 if self.components.is_empty() {
225 ctx.report("composite glyph must have components");
226 }
227 if self._instructions.len() > u16::MAX as usize {
228 ctx.report("instructions len overflows");
229 }
230 }
231}
232
233impl FontWrite for Anchor {
234 fn write_into(&self, writer: &mut crate::TableWriter) {
235 let two_bytes = self
236 .compute_flags()
237 .contains(CompositeGlyphFlags::ARG_1_AND_2_ARE_WORDS);
238 match self {
239 Anchor::Offset { x, y } if !two_bytes => [*x as i8, *y as i8].write_into(writer),
240 Anchor::Offset { x, y } => [*x, *y].write_into(writer),
241 Anchor::Point { base, component } if !two_bytes => {
242 [*base as u8, *component as u8].write_into(writer)
243 }
244 Anchor::Point { base, component } => [*base, *component].write_into(writer),
245 }
246 }
247}
248
249impl FontWrite for Transform {
250 fn write_into(&self, writer: &mut crate::TableWriter) {
251 let flags = self.compute_flags();
252 if flags.contains(CompositeGlyphFlags::WE_HAVE_A_TWO_BY_TWO) {
253 [self.xx, self.yx, self.xy, self.yy].write_into(writer);
254 } else if flags.contains(CompositeGlyphFlags::WE_HAVE_AN_X_AND_Y_SCALE) {
255 [self.xx, self.yy].write_into(writer);
256 } else if flags.contains(CompositeGlyphFlags::WE_HAVE_A_SCALE) {
257 self.xx.write_into(writer)
258 }
259 }
260}
261
262impl From<CompositeGlyphFlags> for ComponentFlags {
263 fn from(src: CompositeGlyphFlags) -> ComponentFlags {
264 ComponentFlags {
265 round_xy_to_grid: src.contains(CompositeGlyphFlags::ROUND_XY_TO_GRID),
266 use_my_metrics: src.contains(CompositeGlyphFlags::USE_MY_METRICS),
267 scaled_component_offset: src.contains(CompositeGlyphFlags::SCALED_COMPONENT_OFFSET),
268 unscaled_component_offset: src.contains(CompositeGlyphFlags::UNSCALED_COMPONENT_OFFSET),
269 overlap_compound: src.contains(CompositeGlyphFlags::OVERLAP_COMPOUND),
270 }
271 }
272}
273
274impl From<ComponentFlags> for CompositeGlyphFlags {
275 fn from(value: ComponentFlags) -> Self {
276 (if value.round_xy_to_grid {
277 CompositeGlyphFlags::ROUND_XY_TO_GRID
278 } else {
279 Default::default()
280 }) | if value.use_my_metrics {
281 CompositeGlyphFlags::USE_MY_METRICS
282 } else {
283 Default::default()
284 } | if value.scaled_component_offset {
285 CompositeGlyphFlags::SCALED_COMPONENT_OFFSET
286 } else {
287 Default::default()
288 } | if value.unscaled_component_offset {
289 CompositeGlyphFlags::UNSCALED_COMPONENT_OFFSET
290 } else {
291 Default::default()
292 } | if value.overlap_compound {
293 CompositeGlyphFlags::OVERLAP_COMPOUND
294 } else {
295 Default::default()
296 }
297 }
298}
299
300#[cfg(test)]
301mod tests {
302
303 use read_fonts::{
304 tables::glyf as read_glyf, types::GlyphId, FontData, FontRead, FontRef, TableProvider,
305 };
306
307 use super::*;
308
309 #[test]
310 fn roundtrip_composite() {
311 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
312 let loca = font.loca(None).unwrap();
313 let glyf = font.glyf().unwrap();
314 let read_glyf::Glyph::Composite(orig) =
315 loca.get_glyf(GlyphId::new(2), &glyf).unwrap().unwrap()
316 else {
317 panic!("not a composite glyph")
318 };
319
320 let bbox = Bbox {
321 x_min: orig.x_min(),
322 y_min: orig.y_min(),
323 x_max: orig.x_max(),
324 y_max: orig.y_max(),
325 };
326 let mut iter = orig
327 .components()
328 .map(|comp| Component::new(comp.glyph, comp.anchor, comp.transform, comp.flags));
329 let mut composite = CompositeGlyph::new(iter.next().unwrap(), bbox);
330 composite.add_component(iter.next().unwrap(), bbox);
331 composite._instructions = orig.instructions().unwrap_or_default().to_vec();
332 assert!(iter.next().is_none());
333 let bytes = crate::dump_table(&composite).unwrap();
334 let ours = read_fonts::tables::glyf::CompositeGlyph::read(FontData::new(&bytes)).unwrap();
335
336 let our_comps = ours.components().collect::<Vec<_>>();
337 let orig_comps = orig.components().collect::<Vec<_>>();
338 assert_eq!(our_comps.len(), orig_comps.len());
339 assert_eq!(our_comps.len(), 2);
340 assert_eq!(&our_comps[0], &orig_comps[0]);
341 assert_eq!(&our_comps[1], &orig_comps[1]);
342 assert_eq!(ours.instructions(), orig.instructions());
343 assert_eq!(orig.offset_data().len(), bytes.len());
344
345 assert_eq!(orig.offset_data().as_ref(), bytes);
346 }
347}