1use std::path;
4
5use crate::anchor::Anchor;
6use crate::component::GlifComponents;
7use crate::error::GlifParserError;
8use crate::guideline::Guideline;
9#[cfg(feature = "glifimage")]
10use crate::image::GlifImage;
11use crate::point::PointData;
12use crate::outline::Outline;
13
14mod conv;
15mod lib;
16pub use lib::Lib;
17mod read;
18pub use self::read::read_ufo_glif as read;
19pub use self::read::read_ufo_glif_pedantic as read_pedantic;
20pub use self::read::read_ufo_glif_from_filename as read_from_filename;
21pub use self::read::read_ufo_glif_from_filename_pedantic as read_from_filename_pedantic;
22mod write;
23pub use self::write::write_ufo_glif as write;
24pub use self::write::write_ufo_glif_to_filename as write_to_filename;
25#[cfg(feature = "mfek")]
26pub mod mfek;
27pub mod xml;
28pub use self::{read::FromXML, write::IntoXML, xml::XMLConversion};
29
30#[cfg(feature = "glifserde")]
31use serde::{Deserialize, Serialize};
32
33#[cfg(feature = "mfek")]
34pub use mfek::*;
35
36#[cfg_attr(feature = "glifserde", derive(Serialize, Deserialize))]
41#[derive(Clone, Debug, Default, PartialEq)]
42pub struct Glif<PD: PointData> {
43 pub outline: Option<Outline<PD>>,
44 pub anchors: Vec<Anchor<PD>>,
45 pub components: GlifComponents,
48 pub guidelines: Vec<Guideline<PD>>,
54 #[cfg(feature = "glifimage")]
58 pub images: Vec<GlifImage>,
59 pub width: Option<u64>,
60 pub unicode: Vec<char>,
61 pub name: String,
62 pub note: Option<String>,
64 #[cfg_attr(feature = "glifserde", serde(skip_serializing, skip_deserializing))]
66 pub filename: Option<path::PathBuf>,
67 pub lib: Lib,
69}
70
71impl<PD: PointData> Glif<PD> {
72 pub fn new() -> Self {
73 Self::default()
74 }
75
76 pub fn name_to_filename(&self) -> String {
77 name_to_filename(&self.name, true)
78 }
79
80 pub fn filename_is_sane(&self) -> Result<bool, GlifParserError> {
81 match &self.filename {
82 Some(gfn) => {
83 let gfn_fn = match gfn.file_name() {
84 Some(gfn_fn) => gfn_fn,
85 None => { return Err(GlifParserError::GlifFilenameInsane("Glif file name is directory".to_string())) }
86 };
87
88 Ok(self.name_to_filename() == gfn_fn.to_str().ok_or(GlifParserError::GlifFilenameInsane("Glif file name has unknown encoding".to_string()))?)
89 }
90 None => Err(GlifParserError::GlifFilenameInsane("Glif file name is not set".to_string()))
91 }
92 }
93
94}
95
96pub trait GlifLike {
97 fn name(&self) -> &String;
98 fn filename(&self) -> &Option<path::PathBuf>;
99}
100
101impl<PD: PointData> GlifLike for Glif<PD> {
102 fn name(&self) -> &String {
103 &self.name
104 }
105 fn filename(&self) -> &Option<path::PathBuf> {
106 &self.filename
107 }
108}
109
110#[inline]
111pub fn name_to_filename(name: &str, append_extension: bool) -> String {
112 let mut ret = String::new();
113 let chars: Vec<char> = name.chars().collect();
114 for c in chars {
115 ret.push(c);
116 if ('A'..'Z').contains(&c) {
117 ret.push('_');
118 }
119 }
120 if append_extension {
121 ret.push_str(".glif");
122 }
123 ret
124}