1use crate::{
4 from_obj::{FromObjRef, FromTableRef, ToOwnedTable},
5 FontWrite,
6};
7
8use read_fonts::{tables::glyf::CompositeGlyphFlags, types::GlyphId16, FontRead, ReadArgs};
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 ReadArgs for CompositeGlyph {
90 type Args = ();
91}
92
93impl<'a> FontRead<'a> for CompositeGlyph {
94 fn read_with_args(
95 data: read_fonts::FontData<'a>,
96 _: (),
97 ) -> Result<Self, read_fonts::ReadError> {
98 read_fonts::tables::glyf::CompositeGlyph::read(data).map(|g| g.to_owned_table())
99 }
100}
101
102impl Component {
103 pub fn new(
105 glyph: GlyphId16,
106 anchor: Anchor,
107 transform: Transform,
108 flags: impl Into<ComponentFlags>,
109 ) -> Self {
110 Component {
111 glyph,
112 anchor,
113 flags: flags.into(),
114 transform,
115 }
116 }
117 fn compute_flag(&self) -> CompositeGlyphFlags {
120 self.anchor.compute_flags() | self.transform.compute_flags() | self.flags.into()
121 }
122
123 fn write_into(&self, writer: &mut crate::TableWriter, extra_flags: CompositeGlyphFlags) {
126 let flags = self.compute_flag() | extra_flags;
127 flags.bits().write_into(writer);
128 self.glyph.write_into(writer);
129 self.anchor.write_into(writer);
130 self.transform.write_into(writer);
131 }
132}
133
134#[derive(Clone, Copy, Debug)]
136#[non_exhaustive]
137pub struct NoComponents;
138
139impl std::fmt::Display for NoComponents {
140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141 write!(f, "A composite glyph must contain at least one component")
142 }
143}
144
145impl std::error::Error for NoComponents {}
146
147impl CompositeGlyph {
148 pub fn new(component: Component, bbox: impl Into<Bbox>) -> Self {
155 Self {
156 bbox: bbox.into(),
157 components: vec![component],
158 instructions: Default::default(),
159 }
160 }
161
162 pub fn add_component(&mut self, component: Component, bbox: impl Into<Bbox>) {
167 self.components.push(component);
168 self.bbox = self.bbox.union(bbox.into());
169 }
170
171 pub fn try_from_iter(
176 source: impl IntoIterator<Item = (Component, Bbox)>,
177 ) -> Result<Self, NoComponents> {
178 let mut components = Vec::new();
179 let mut union_box: Option<Bbox> = None;
180
181 for (component, bbox) in source {
182 components.push(component);
183 union_box.get_or_insert(bbox).union(bbox);
184 }
185
186 if components.is_empty() {
187 Err(NoComponents)
188 } else {
189 Ok(CompositeGlyph {
190 bbox: union_box.unwrap(),
191 components,
192 instructions: Default::default(),
193 })
194 }
195 }
196
197 pub fn components(&self) -> &[Component] {
198 &self.components
199 }
200
201 pub fn components_mut(&mut self) -> &mut [Component] {
202 &mut self.components
203 }
204
205 pub fn set_instructions(&mut self, instructions: &[u8]) {
206 self.instructions = instructions.to_vec();
207 }
208
209 pub fn instructions(&self) -> &[u8] {
210 &self.instructions
211 }
212}
213
214impl FontWrite for CompositeGlyph {
215 fn write_into(&self, writer: &mut crate::TableWriter) {
216 const N_CONTOURS: i16 = -1;
217 N_CONTOURS.write_into(writer);
218 self.bbox.write_into(writer);
219 let (last, rest) = self
220 .components
221 .split_last()
222 .expect("empty composites checked in validation");
223 for comp in rest {
224 comp.write_into(writer, CompositeGlyphFlags::MORE_COMPONENTS);
225 }
226 let last_flags = if self.instructions.is_empty() {
227 CompositeGlyphFlags::empty()
228 } else {
229 CompositeGlyphFlags::WE_HAVE_INSTRUCTIONS
230 };
231 last.write_into(writer, last_flags);
232
233 if !self.instructions.is_empty() {
234 (self.instructions.len() as u16).write_into(writer);
235 self.instructions.write_into(writer);
236 }
237 writer.pad_to_2byte_aligned();
238 }
239}
240
241impl crate::validate::Validate for CompositeGlyph {
242 fn validate_impl(&self, ctx: &mut crate::codegen_prelude::ValidationCtx) {
243 if self.components.is_empty() {
244 ctx.report("composite glyph must have components");
245 }
246 if self.instructions.len() > u16::MAX as usize {
247 ctx.report("instructions len overflows");
248 }
249 }
250}
251
252impl FontWrite for Anchor {
253 fn write_into(&self, writer: &mut crate::TableWriter) {
254 let two_bytes = self
255 .compute_flags()
256 .contains(CompositeGlyphFlags::ARG_1_AND_2_ARE_WORDS);
257 match self {
258 Anchor::Offset { x, y } if !two_bytes => [*x as i8, *y as i8].write_into(writer),
259 Anchor::Offset { x, y } => [*x, *y].write_into(writer),
260 Anchor::Point { base, component } if !two_bytes => {
261 [*base as u8, *component as u8].write_into(writer)
262 }
263 Anchor::Point { base, component } => [*base, *component].write_into(writer),
264 }
265 }
266}
267
268impl FontWrite for Transform {
269 fn write_into(&self, writer: &mut crate::TableWriter) {
270 let flags = self.compute_flags();
271 if flags.contains(CompositeGlyphFlags::WE_HAVE_A_TWO_BY_TWO) {
272 [self.xx, self.yx, self.xy, self.yy].write_into(writer);
273 } else if flags.contains(CompositeGlyphFlags::WE_HAVE_AN_X_AND_Y_SCALE) {
274 [self.xx, self.yy].write_into(writer);
275 } else if flags.contains(CompositeGlyphFlags::WE_HAVE_A_SCALE) {
276 self.xx.write_into(writer)
277 }
278 }
279}
280
281impl From<CompositeGlyphFlags> for ComponentFlags {
282 fn from(src: CompositeGlyphFlags) -> ComponentFlags {
283 ComponentFlags {
284 round_xy_to_grid: src.contains(CompositeGlyphFlags::ROUND_XY_TO_GRID),
285 use_my_metrics: src.contains(CompositeGlyphFlags::USE_MY_METRICS),
286 scaled_component_offset: src.contains(CompositeGlyphFlags::SCALED_COMPONENT_OFFSET),
287 unscaled_component_offset: src.contains(CompositeGlyphFlags::UNSCALED_COMPONENT_OFFSET),
288 overlap_compound: src.contains(CompositeGlyphFlags::OVERLAP_COMPOUND),
289 }
290 }
291}
292
293impl From<ComponentFlags> for CompositeGlyphFlags {
294 fn from(value: ComponentFlags) -> Self {
295 (if value.round_xy_to_grid {
296 CompositeGlyphFlags::ROUND_XY_TO_GRID
297 } else {
298 Default::default()
299 }) | if value.use_my_metrics {
300 CompositeGlyphFlags::USE_MY_METRICS
301 } else {
302 Default::default()
303 } | if value.scaled_component_offset {
304 CompositeGlyphFlags::SCALED_COMPONENT_OFFSET
305 } else {
306 Default::default()
307 } | if value.unscaled_component_offset {
308 CompositeGlyphFlags::UNSCALED_COMPONENT_OFFSET
309 } else {
310 Default::default()
311 } | if value.overlap_compound {
312 CompositeGlyphFlags::OVERLAP_COMPOUND
313 } else {
314 Default::default()
315 }
316 }
317}
318
319#[cfg(test)]
320mod tests {
321
322 use read_fonts::{
323 tables::glyf as read_glyf, types::GlyphId, FontData, FontRead, FontRef, TableProvider,
324 };
325
326 use super::*;
327
328 #[test]
329 fn roundtrip_composite() {
330 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
331 let loca = font.loca(None).unwrap();
332 let glyf = font.glyf().unwrap();
333 let read_glyf::Glyph::Composite(orig) =
334 loca.get_glyf(GlyphId::new(2), &glyf).unwrap().unwrap()
335 else {
336 panic!("not a composite glyph")
337 };
338
339 let bbox = Bbox {
340 x_min: orig.x_min(),
341 y_min: orig.y_min(),
342 x_max: orig.x_max(),
343 y_max: orig.y_max(),
344 };
345 let mut iter = orig
346 .components()
347 .map(|comp| Component::new(comp.glyph, comp.anchor, comp.transform, comp.flags));
348 let mut composite = CompositeGlyph::new(iter.next().unwrap(), bbox);
349 composite.add_component(iter.next().unwrap(), bbox);
350 composite.instructions = orig.instructions().unwrap_or_default().to_vec();
351 assert!(iter.next().is_none());
352 let bytes = crate::dump_table(&composite).unwrap();
353 let ours = read_fonts::tables::glyf::CompositeGlyph::read(FontData::new(&bytes)).unwrap();
354
355 let our_comps = ours.components().collect::<Vec<_>>();
356 let orig_comps = orig.components().collect::<Vec<_>>();
357 assert_eq!(our_comps.len(), orig_comps.len());
358 assert_eq!(our_comps.len(), 2);
359 assert_eq!(&our_comps[0], &orig_comps[0]);
360 assert_eq!(&our_comps[1], &orig_comps[1]);
361 assert_eq!(ours.instructions(), orig.instructions());
362 assert_eq!(orig.offset_data().len(), bytes.len());
363
364 assert_eq!(orig.offset_data().as_ref(), bytes);
365 }
366}