//! [`Glif`] (`` toplevel), read/write modules, + [`Lib`] use std::path; use crate::anchor::Anchor; use crate::component::GlifComponents; use crate::error::GlifParserError; use crate::guideline::Guideline; #[cfg(feature = "glifimage")] use crate::image::GlifImage; use crate::point::PointData; use crate::outline::Outline; mod conv; mod lib; pub use lib::Lib; mod read; pub use self::read::read_ufo_glif as read; pub use self::read::read_ufo_glif_pedantic as read_pedantic; pub use self::read::read_ufo_glif_from_filename as read_from_filename; pub use self::read::read_ufo_glif_from_filename_pedantic as read_from_filename_pedantic; mod write; pub use self::write::write_ufo_glif as write; pub use self::write::write_ufo_glif_to_filename as write_to_filename; #[cfg(feature = "mfek")] pub mod mfek; pub mod xml; pub use self::{read::FromXML, write::IntoXML, xml::XMLConversion}; #[cfg(feature = "glifserde")] use serde::{Deserialize, Serialize}; #[cfg(feature = "mfek")] pub use mfek::*; /// A UFO .glif /// /// TODO: use different generic types on Anchor and Guideline, making this declaration /// `Glif` #[cfg_attr(feature = "glifserde", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Default, PartialEq)] pub struct Glif { pub outline: Option>, pub anchors: Vec>, /// Note that these components are not yet parsed or checked for infinite loops. You need to /// call either ``GlifComponent::to_component_of`` on each of these, or ``Glif::flatten``. pub components: GlifComponents, /// .glif guidelines. Note: glif may have more guidelines, not listed here. It will also have /// an asecender and a descender, not listed here. You can get this info from `norad`, reading /// the parent UFO and telling it not to read glif's (via UfoDataRequest) since you're using /// this for that. // Command line MFEK programs can also get it from MFEKmetadata. pub guidelines: Vec>, /// glifparser does support reading the data of images and guessing their format, but in order /// to allow you to handle possibly erroneous files we don't do so by default. You need to call /// ``GlifImage::to_image_of`` to get an ``Image`` with data. #[cfg(feature = "glifimage")] pub images: Vec, pub width: Option, pub unicode: Vec, pub name: String, /// This is an arbitrary glyph comment, exactly like the comment field in FontForge SFD. pub note: Option, /// It's up to the API consumer to set this. #[cfg_attr(feature = "glifserde", serde(skip_serializing, skip_deserializing))] pub filename: Option, /// glif private library pub lib: Lib, } impl Glif { pub fn new() -> Self { Self::default() } pub fn name_to_filename(&self) -> String { name_to_filename(&self.name, true) } pub fn filename_is_sane(&self) -> Result { match &self.filename { Some(gfn) => { let gfn_fn = match gfn.file_name() { Some(gfn_fn) => gfn_fn, None => { return Err(GlifParserError::GlifFilenameInsane("Glif file name is directory".to_string())) } }; Ok(self.name_to_filename() == gfn_fn.to_str().ok_or(GlifParserError::GlifFilenameInsane("Glif file name has unknown encoding".to_string()))?) } None => Err(GlifParserError::GlifFilenameInsane("Glif file name is not set".to_string())) } } } pub trait GlifLike { fn name(&self) -> &String; fn filename(&self) -> &Option; } impl GlifLike for Glif { fn name(&self) -> &String { &self.name } fn filename(&self) -> &Option { &self.filename } } #[inline] pub fn name_to_filename(name: &str, append_extension: bool) -> String { let mut ret = String::new(); let chars: Vec = name.chars().collect(); for c in chars { ret.push(c); if ('A'..'Z').contains(&c) { ret.push('_'); } } if append_extension { ret.push_str(".glif"); } ret }