1use crate::{
4 from_obj::{FromObjRef, FromTableRef, ToOwnedTable},
5 FontWrite,
6};
7
8use read_fonts::{
9 tables::glyf::CompositeGlyphFlags,
10 types::{GlyphId, Uint24},
11 FontRead, ReadArgs,
12};
13
14use super::Bbox;
15
16pub use read_fonts::tables::glyf::{Anchor, Transform};
17
18#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct CompositeGlyph {
21 pub bbox: Bbox,
22 components: Vec<Component>,
23 instructions: Vec<u8>,
24}
25
26#[derive(Clone, Debug, PartialEq, Eq)]
28pub struct Component {
29 pub glyph: GlyphId,
30 pub anchor: Anchor,
31 pub flags: ComponentFlags,
32 pub transform: Transform,
33}
34
35#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
46pub struct ComponentFlags {
47 pub round_xy_to_grid: bool,
49 pub use_my_metrics: bool,
52 pub scaled_component_offset: bool,
54 pub unscaled_component_offset: bool,
56 pub overlap_compound: bool,
58}
59
60impl FromObjRef<read_fonts::tables::glyf::CompositeGlyph<'_>> for CompositeGlyph {
61 fn from_obj_ref(
62 from: &read_fonts::tables::glyf::CompositeGlyph,
63 _data: read_fonts::FontData,
64 ) -> Self {
65 let bbox = Bbox {
66 x_min: from.x_min(),
67 y_min: from.y_min(),
68 x_max: from.x_max(),
69 y_max: from.y_max(),
70 };
71 let components = from
72 .components()
73 .map(|c| Component {
74 glyph: c.glyph,
75 anchor: c.anchor,
76 flags: c.flags.into(),
77 transform: c.transform,
78 })
79 .collect();
80 Self {
81 bbox,
82 components,
83 instructions: from
84 .instructions()
85 .map(|v| v.to_owned())
86 .unwrap_or_default(),
87 }
88 }
89}
90
91impl FromTableRef<read_fonts::tables::glyf::CompositeGlyph<'_>> for CompositeGlyph {}
92
93impl ReadArgs for CompositeGlyph {
94 type Args = ();
95}
96
97impl<'a> FontRead<'a> for CompositeGlyph {
98 fn read_with_args(
99 data: read_fonts::FontData<'a>,
100 _: (),
101 ) -> Result<Self, read_fonts::ReadError> {
102 read_fonts::tables::glyf::CompositeGlyph::read(data).map(|g| g.to_owned_table())
103 }
104}
105
106impl Component {
107 pub fn new(
109 glyph: GlyphId,
110 anchor: Anchor,
111 transform: Transform,
112 flags: impl Into<ComponentFlags>,
113 ) -> Self {
114 Component {
115 glyph,
116 anchor,
117 flags: flags.into(),
118 transform,
119 }
120 }
121 fn compute_flag(&self) -> CompositeGlyphFlags {
124 let glyph_flag = if self.glyph.to_u32() > u16::MAX as u32 {
125 CompositeGlyphFlags::GID_IS_24_BIT
126 } else {
127 CompositeGlyphFlags::empty()
128 };
129 self.anchor.compute_flags()
130 | self.transform.compute_flags()
131 | self.flags.into()
132 | glyph_flag
133 }
134
135 fn write_into(&self, writer: &mut crate::TableWriter, extra_flags: CompositeGlyphFlags) {
138 let flags = self.compute_flag() | extra_flags;
139 flags.bits().write_into(writer);
140 if flags.contains(CompositeGlyphFlags::GID_IS_24_BIT) {
141 Uint24::new(self.glyph.to_u32()).write_into(writer);
142 } else {
143 (self.glyph.to_u32() as u16).write_into(writer);
144 }
145 self.anchor.write_into(writer);
146 self.transform.write_into(writer);
147 }
148}
149
150#[derive(Clone, Copy, Debug)]
152#[non_exhaustive]
153pub struct NoComponents;
154
155impl std::fmt::Display for NoComponents {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 write!(f, "A composite glyph must contain at least one component")
158 }
159}
160
161impl std::error::Error for NoComponents {}
162
163impl CompositeGlyph {
164 pub fn new(component: Component, bbox: impl Into<Bbox>) -> Self {
171 Self {
172 bbox: bbox.into(),
173 components: vec![component],
174 instructions: Default::default(),
175 }
176 }
177
178 pub fn add_component(&mut self, component: Component, bbox: impl Into<Bbox>) {
183 self.components.push(component);
184 self.bbox = self.bbox.union(bbox.into());
185 }
186
187 pub fn try_from_iter(
192 source: impl IntoIterator<Item = (Component, Bbox)>,
193 ) -> Result<Self, NoComponents> {
194 let mut components = Vec::new();
195 let mut union_box: Option<Bbox> = None;
196
197 for (component, bbox) in source {
198 components.push(component);
199 union_box.get_or_insert(bbox).union(bbox);
200 }
201
202 if components.is_empty() {
203 Err(NoComponents)
204 } else {
205 Ok(CompositeGlyph {
206 bbox: union_box.unwrap(),
207 components,
208 instructions: Default::default(),
209 })
210 }
211 }
212
213 pub fn components(&self) -> &[Component] {
214 &self.components
215 }
216
217 pub fn components_mut(&mut self) -> &mut [Component] {
218 &mut self.components
219 }
220
221 pub fn set_instructions(&mut self, instructions: &[u8]) {
222 self.instructions = instructions.to_vec();
223 }
224
225 pub fn instructions(&self) -> &[u8] {
226 &self.instructions
227 }
228}
229
230impl FontWrite for CompositeGlyph {
231 fn write_into(&self, writer: &mut crate::TableWriter) {
232 const N_CONTOURS: i16 = -1;
233 N_CONTOURS.write_into(writer);
234 self.bbox.write_into(writer);
235 let (last, rest) = self
236 .components
237 .split_last()
238 .expect("empty composites checked in validation");
239 for comp in rest {
240 comp.write_into(writer, CompositeGlyphFlags::MORE_COMPONENTS);
241 }
242 let last_flags = if self.instructions.is_empty() {
243 CompositeGlyphFlags::empty()
244 } else {
245 CompositeGlyphFlags::WE_HAVE_INSTRUCTIONS
246 };
247 last.write_into(writer, last_flags);
248
249 if !self.instructions.is_empty() {
250 (self.instructions.len() as u16).write_into(writer);
251 self.instructions.write_into(writer);
252 }
253 writer.pad_to_2byte_aligned();
254 }
255}
256
257impl crate::validate::Validate for CompositeGlyph {
258 fn validate_impl(&self, ctx: &mut crate::codegen_prelude::ValidationCtx) {
259 if self.components.is_empty() {
260 ctx.report("composite glyph must have components");
261 }
262 if self.instructions.len() > u16::MAX as usize {
263 ctx.report("instructions len overflows");
264 }
265 if self
266 .components
267 .iter()
268 .any(|component| component.glyph.to_u32() > 0xffffff)
269 {
270 ctx.report("component glyph identifier overflows 24 bits");
271 }
272 }
273}
274
275impl FontWrite for Anchor {
276 fn write_into(&self, writer: &mut crate::TableWriter) {
277 let two_bytes = self
278 .compute_flags()
279 .contains(CompositeGlyphFlags::ARG_1_AND_2_ARE_WORDS);
280 match self {
281 Anchor::Offset { x, y } if !two_bytes => [*x as i8, *y as i8].write_into(writer),
282 Anchor::Offset { x, y } => [*x, *y].write_into(writer),
283 Anchor::Point { base, component } if !two_bytes => {
284 [*base as u8, *component as u8].write_into(writer)
285 }
286 Anchor::Point { base, component } => [*base, *component].write_into(writer),
287 }
288 }
289}
290
291impl FontWrite for Transform {
292 fn write_into(&self, writer: &mut crate::TableWriter) {
293 let flags = self.compute_flags();
294 if flags.contains(CompositeGlyphFlags::WE_HAVE_A_TWO_BY_TWO) {
295 [self.xx, self.yx, self.xy, self.yy].write_into(writer);
296 } else if flags.contains(CompositeGlyphFlags::WE_HAVE_AN_X_AND_Y_SCALE) {
297 [self.xx, self.yy].write_into(writer);
298 } else if flags.contains(CompositeGlyphFlags::WE_HAVE_A_SCALE) {
299 self.xx.write_into(writer)
300 }
301 }
302}
303
304impl From<CompositeGlyphFlags> for ComponentFlags {
305 fn from(src: CompositeGlyphFlags) -> ComponentFlags {
306 ComponentFlags {
307 round_xy_to_grid: src.contains(CompositeGlyphFlags::ROUND_XY_TO_GRID),
308 use_my_metrics: src.contains(CompositeGlyphFlags::USE_MY_METRICS),
309 scaled_component_offset: src.contains(CompositeGlyphFlags::SCALED_COMPONENT_OFFSET),
310 unscaled_component_offset: src.contains(CompositeGlyphFlags::UNSCALED_COMPONENT_OFFSET),
311 overlap_compound: src.contains(CompositeGlyphFlags::OVERLAP_COMPOUND),
312 }
313 }
314}
315
316impl From<ComponentFlags> for CompositeGlyphFlags {
317 fn from(value: ComponentFlags) -> Self {
318 (if value.round_xy_to_grid {
319 CompositeGlyphFlags::ROUND_XY_TO_GRID
320 } else {
321 Default::default()
322 }) | if value.use_my_metrics {
323 CompositeGlyphFlags::USE_MY_METRICS
324 } else {
325 Default::default()
326 } | if value.scaled_component_offset {
327 CompositeGlyphFlags::SCALED_COMPONENT_OFFSET
328 } else {
329 Default::default()
330 } | if value.unscaled_component_offset {
331 CompositeGlyphFlags::UNSCALED_COMPONENT_OFFSET
332 } else {
333 Default::default()
334 } | if value.overlap_compound {
335 CompositeGlyphFlags::OVERLAP_COMPOUND
336 } else {
337 Default::default()
338 }
339 }
340}
341
342#[cfg(test)]
343mod tests {
344
345 use read_fonts::{
346 tables::glyf as read_glyf, types::GlyphId, FontData, FontRead, FontRef, TableProvider,
347 };
348
349 use super::*;
350
351 #[test]
352 fn roundtrip_extended_composite_components() {
353 let half = read_fonts::types::F2Dot14::from_f32(0.5);
354 let bbox = Bbox {
355 x_min: 0,
356 y_min: 0,
357 x_max: 80,
358 y_max: 80,
359 };
360 for gid in [65535, 65536, 0xffffff] {
361 for anchor in [
362 Anchor::Offset { x: 1, y: -2 },
363 Anchor::Point {
364 base: 256,
365 component: 512,
366 },
367 ] {
368 for transform in [
369 Transform::default(),
370 Transform {
371 xx: half,
372 yy: half,
373 ..Default::default()
374 },
375 Transform {
376 xx: half,
377 ..Default::default()
378 },
379 Transform {
380 xy: half,
381 ..Default::default()
382 },
383 ] {
384 let mut glyph = CompositeGlyph::new(
385 Component::new(
386 GlyphId::new(gid),
387 anchor,
388 transform,
389 ComponentFlags::default(),
390 ),
391 bbox,
392 );
393 glyph.add_component(
394 Component::new(
395 GlyphId::new(7),
396 Anchor::Offset { x: 3, y: 4 },
397 Transform::default(),
398 ComponentFlags::default(),
399 ),
400 bbox,
401 );
402 glyph.set_instructions(&[0xb0, 0, 0x21]);
403 let bytes = crate::dump_table(&glyph).unwrap();
404 let read = read_glyf::CompositeGlyph::read(bytes.as_slice().into()).unwrap();
405 let components: Vec<_> = read.components().collect();
406 let ids: Vec<_> = read.component_glyphs_and_flags().map(|c| c.0).collect();
407 assert_eq!(ids, [GlyphId::new(gid), GlyphId::new(7)]);
408 assert_eq!(components[0].glyph, ids[0]);
409 assert_eq!(components[0].anchor, anchor);
410 assert_eq!(components[0].transform, transform);
411 assert_eq!(
412 components[0]
413 .flags
414 .contains(CompositeGlyphFlags::GID_IS_24_BIT),
415 gid > 65535
416 );
417 assert!(!components[1]
418 .flags
419 .contains(CompositeGlyphFlags::GID_IS_24_BIT));
420 assert_eq!(
421 read.count_and_instructions(),
422 (2, Some(&[0xb0, 0, 0x21][..]))
423 );
424 let owned: CompositeGlyph = read.to_owned_table();
425 assert_eq!(owned, glyph);
426 assert_eq!(crate::dump_table(&owned).unwrap(), bytes);
427 }
428 }
429 }
430 let glyph = CompositeGlyph::new(
431 Component::new(
432 GlyphId::new(0x1000000),
433 Anchor::Offset { x: 0, y: 0 },
434 Transform::default(),
435 ComponentFlags::default(),
436 ),
437 bbox,
438 );
439 assert!(crate::dump_table(&glyph).is_err());
440 }
441
442 #[test]
443 fn roundtrip_composite() {
444 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
445 let loca = font.loca(None).unwrap();
446 let glyf = font.glyf().unwrap();
447 let read_glyf::Glyph::Composite(orig) = loca
448 .get(GlyphId::new(2), &glyf)
449 .and_then(|g| g.into_glyph())
450 .unwrap()
451 else {
452 panic!("not a composite glyph")
453 };
454
455 let bbox = Bbox {
456 x_min: orig.x_min(),
457 y_min: orig.y_min(),
458 x_max: orig.x_max(),
459 y_max: orig.y_max(),
460 };
461 let mut iter = orig
462 .components()
463 .map(|comp| Component::new(comp.glyph, comp.anchor, comp.transform, comp.flags));
464 let mut composite = CompositeGlyph::new(iter.next().unwrap(), bbox);
465 composite.add_component(iter.next().unwrap(), bbox);
466 composite.instructions = orig.instructions().unwrap_or_default().to_vec();
467 assert!(iter.next().is_none());
468 let bytes = crate::dump_table(&composite).unwrap();
469 let ours = read_fonts::tables::glyf::CompositeGlyph::read(FontData::new(&bytes)).unwrap();
470
471 let our_comps = ours.components().collect::<Vec<_>>();
472 let orig_comps = orig.components().collect::<Vec<_>>();
473 assert_eq!(our_comps.len(), orig_comps.len());
474 assert_eq!(our_comps.len(), 2);
475 assert_eq!(&our_comps[0], &orig_comps[0]);
476 assert_eq!(&our_comps[1], &orig_comps[1]);
477 assert_eq!(ours.instructions(), orig.instructions());
478 assert_eq!(orig.offset_data().len(), bytes.len());
479
480 assert_eq!(orig.offset_data().as_ref(), bytes);
481 }
482}