//! .glif `` mod xml; use crate::error::GlifParserError; #[cfg(feature = "mfek")] use crate::glif::mfek::MFEKGlif; use crate::glif::{self, Glif}; use crate::matrix::{ApplyMatrix as _, GlifMatrix}; use crate::outline::Outline; use crate::point::PointData; use integer_or_float::IntegerOrFloat; use kurbo::Affine; pub use trees::{Forest, Node, Tree}; use IntegerOrFloat::Float; #[cfg(feature = "glifserde")] use serde::{Deserialize, Serialize}; use std::collections::HashSet; use std::path::{Path, PathBuf}; #[allow(non_snake_case)] // to match UFO spec https://unifiedfontobject.org/versions/ufo3/glyphs/glif/#component #[cfg_attr(feature = "glifserde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Default, Hash, PartialEq)] pub struct GlifComponent { pub base: String, pub filename: Option, pub xScale: IntegerOrFloat, pub xyScale: IntegerOrFloat, pub yxScale: IntegerOrFloat, pub yScale: IntegerOrFloat, pub xOffset: IntegerOrFloat, pub yOffset: IntegerOrFloat, pub identifier: Option, } #[cfg_attr(feature = "glifserde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Default, PartialEq)] /// A container meant for yelding a [`Forest>`]. /// /// Please see [`impl From for Result>, /// GlifParserError>`](#conversion). pub struct GlifComponents { pub root: String, pub vec: Vec, pub uniques: HashSet } impl GlifComponents { pub fn new() -> Self { Default::default() } } impl GlifComponent { pub fn new() -> Self { Self { xScale: Float(1.0), yScale: Float(1.0), ..Default::default() } } } impl GlifComponent { pub fn matrix(&self) -> GlifMatrix { GlifMatrix( self.xScale, self.xyScale, self.yxScale, self.yScale, self.xOffset, self.yOffset, ) } } #[cfg_attr(feature = "glifserde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Default, PartialEq)] pub struct Component { pub glif: Glif, pub matrix: Affine, } impl Component { pub fn new() -> Self { Self::default() } } #[cfg_attr(feature = "glifserde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Default, PartialEq)] pub struct ComponentRect { pub minx: f32, pub miny: f32, pub maxx: f32, pub maxy: f32, pub name: String, } impl ComponentRect { pub fn from_rect_and_name(minx: f32, miny: f32, maxx: f32, maxy: f32, name: String) -> Self { Self { minx, miny, maxx, maxy, name, } } } use std::fs; impl GlifComponent { /// Sets the filename of a component relative to its base's filename (`gliffn`) pub fn set_file_name>(&mut self, gliffn: F) { let mut retglifname = gliffn.as_ref().to_path_buf(); retglifname.set_file_name(glif::name_to_filename(&self.base, true)); self.filename = Some(retglifname); } /// Must have filename set, and that file must be readable, for this to work. pub fn to_component(&self) -> Result, GlifParserError> { let gliffn = &self .filename .as_ref() .ok_or(GlifParserError::GlifFilenameNotSet(self.base.clone()))?; let mut ret = Component::new(); ret.matrix = self.matrix().into(); ret.glif.name = self.base.clone(); ret.glif.filename = self.filename.clone(); let component_xml = fs::read_to_string(&gliffn).or(Err(GlifParserError::GlifFilenameNotSet( "Glif filename leads to unreadable file".to_string(), )))?; let mut newglif: Glif = glif::read(&component_xml)?; for component in newglif.components.vec.iter_mut() { component.set_file_name(&gliffn); } ret.glif.components = newglif.components; ret.glif.anchors = newglif.anchors; ret.glif.outline = newglif.outline; Ok(ret) } pub fn refers_to(&self, glif: &Glif) -> bool { self.base == glif.name } } pub trait FlattenedGlif where Self: Clone, { /// Check that all components in your .glif file really resolve, and if they do, get their /// contours and apply their matrices. If you want bounding rectangles as this process is done /// with a logical name for each rectangle you can draw, pass in `rects` as a `&mut` fn flattened(&self, rects: &mut Option>) -> Result; } fn apply_component_rect( last: &Node>, minx: &mut f32, miny: &mut f32, maxx: &mut f32, maxy: &mut f32, final_outline: &mut Outline, ) { let mut matrices = vec![]; matrices.push((*last).data().matrix); // Climb the tree, building a Vec of matrices for this component let mut pt = last.parent(); while let Some(parent) = pt { matrices.push(parent.data().matrix); pt = parent.parent(); } match (*last).data().glif.outline { Some(ref o) => { let mut to_transform = o.clone(); for i in 0..to_transform.len() { for j in 0..to_transform[i].len() { let is_first = i == 0 && j == 0; let mut p = to_transform[i][j].clone(); if p.x < *minx || is_first { *minx = p.x; } if p.y < *miny || is_first { *miny = p.y; } if p.x > *maxx || is_first { *maxx = p.x; } if p.y > *maxy || is_first { *maxy = p.y; } for m in &matrices { p.apply_matrix(*m); } to_transform[i][j] = p; } } final_outline.extend(to_transform); } None => {} } } #[rustfmt::skip] macro_rules! impl_flattened_glif { ($glifstruct:ident, $outline:ident) => { impl FlattenedGlif for $glifstruct { /// Flatten a UFO .glif with components. /// /// Can fail if the .glif's components form an infinite loop. // How this works is we start at the bottom of the tree, take all of the Affine matrices which // describe the transformation of the glyph's points, and continuously apply them until we run // out of nodes of the tree. Finally, we set our outline to be the final transformed outline, // and consider ourselves as no longer being made up of components. fn flattened(&self, rects: &mut Option>) -> Result { let mut ret = self.clone(); let components_r: Result>, _> = (ret.components).into(); let components = components_r?; let mut final_outline: Outline = Outline::new(); let mut component_rects = vec![]; for mut component in components { let (mut minx, mut miny, mut maxx, mut maxy) = (0., 0., 0., 0.); // This unwrap is safe because at this point we know we haven't yet exhausted the // Forest. let component_name = component.data().glif.name.clone(); apply_component_rect(&component, &mut minx, &mut miny, &mut maxx, &mut maxy, &mut final_outline); while let Some(last) = component.back() { apply_component_rect(&last, &mut minx, &mut miny, &mut maxx, &mut maxy, &mut final_outline); component.pop_back(); } component_rects.push(ComponentRect { minx, maxx, miny, maxy, name: component_name }); } ret.$outline = Some(final_outline); // If we were to leave this here, then API consumers would potentially draw component outlines on top of components. ret.components = GlifComponents::new(); if let Some(ptr) = rects { *ptr = component_rects; } Ok(ret) } } } } impl_flattened_glif!(Glif, outline); #[cfg(feature = "mfek")] impl_flattened_glif!(MFEKGlif, flattened); /// # Conversion /// /// This impl builds up a forest of trees for a glyph's components. Imagine a hungarumlaut (˝). /// /// This character may be built of glyph components, as such: /// /// ```plain /// hungarumlaut /// / \ /// / \ /// grave grave /// | | /// acute acute /// ``` /// /// This function will give you a Forest of both of the sub-trees. ([`Forest`]). The elements /// of a [`Forest`] are [`Tree`]. For safety reasons, this function cannot always return a /// [`Forest`]. Sometimes, .glif files can be malformed, containing components which refer to /// themselves or to components higher up the tree. impl From for Result>, GlifParserError> { fn from(mut glifcs: GlifComponents) -> Self { let mut forest = Forest::new(); let components: Vec<_> = glifcs.vec.drain(..).collect(); let cs = components.into_iter().map(|c| { glifcs.build_component_tree(c.to_component()?) }).collect::, GlifParserError>>()?; for c in cs { forest.push_back(c); } Ok(forest) } } /// Builds a tree of components in a recursive manner. It takes a mutable reference to a `HashSet` /// called `uniques`, which keeps track of the unique component names seen so far in the current /// subtree. This prevents cycles from occurring. /// /// If a loop is found in the tree (for example, gershayim refers to grave refers to grave), an /// error is returned with the appropriate message. The error type is /// [`GlifParserError::GlifComponentsCyclical`], which takes a formatted string to provide more /// details about the error. impl GlifComponents { /// You should not need to call this function directly; see [§ Conversion](#conversion). pub fn build_component_tree( &mut self, component: Component, ) -> Result>, GlifParserError> { let mut tree = Tree::new(component.clone()); for gc in component.glif.components.vec.into_iter() { if !self.uniques.insert(gc.base.clone()) { return Err(GlifParserError::GlifComponentsCyclical(format!( "in glif {}, {} refers to {} (trying to flatten {})", gc.base, component.glif.name, gc.base, self.root ))); } tree.push_back(self.build_component_tree(gc.to_component()?)?); self.uniques.remove(&gc.base); } Ok(tree) } }