glifparser/
anchor.rs

1//! .glif `<anchor>` + ufo2ft `_` mark/base determination
2
3use integer_or_float::IntegerOrFloat;
4
5mod xml;
6
7use std::fmt::Debug;
8#[cfg(feature = "glifserde")]
9use serde::{Serialize, Deserialize};
10
11use crate::error::GlifParserError;
12use crate::pedantry::{FloatClass, Mend, Pedantry};
13use crate::point::PointData;
14use crate::string::GlifStringLenOne;
15
16#[cfg_attr(feature = "glifserde", derive(Serialize, Deserialize))]
17#[derive(Clone, Debug, Derivative, PartialEq, Eq, Unwrap, IsVariant)]
18#[derivative(Default(new="true"))]
19pub enum AnchorType {
20    Mark,
21    #[derivative(Default)]
22    Base,
23}
24
25impl<S> From<S> for AnchorType where S: AsRef<str> {
26    fn from(s: S) -> Self {
27        if s.as_ref().chars().nth(0) == Some('_') {
28            Self::Mark
29        } else {
30            Self::Base
31        }
32    }
33}
34
35pub trait FromOption<S: Into<AnchorType>>: Default {
36    fn from_option(s: Option<S>) -> AnchorType {
37        match s {
38            Some(ss) => ss.into(),
39            None => AnchorType::default()
40        }
41    }
42}
43impl<S: Into<Self>> FromOption<S> for AnchorType {}
44
45#[cfg_attr(feature = "glifserde", derive(Serialize, Deserialize))]
46#[derive(Constructor, Clone, Debug, Default, PartialEq)]
47pub struct Anchor<PD: PointData> {
48    pub x: f32,
49    pub y: f32,
50    #[cfg_attr(feature = "glifserde", serde(default))]
51    pub class: Option<String>,
52    #[cfg_attr(feature = "glifserde", serde(default))]
53    pub data: PD,
54    #[cfg_attr(feature = "glifserde", serde(default))]
55    pub atype: AnchorType,
56}
57
58impl<PD: PointData> Anchor<PD> {
59    pub fn from_glif(ga: &GlifAnchor, pedantry: Pedantry) -> Result<Self, GlifParserError> {
60        let (x, y) = (pedantry.level.maybe_round(ga.x, FloatClass::Anchor), pedantry.level.maybe_round(ga.y, FloatClass::Anchor));
61        let class = ga.class.as_ref().map(|gs|gs.to_string());
62        if pedantry.mend != Mend::Always && (!ga.x.holding_integer().is_ok() || !ga.y.holding_integer().is_ok()) {
63            return Err(GlifParserError::PedanticXmlParseError("Anchor was a float, not an integer!".to_string()))
64        }
65        let atype = AnchorType::from_option(class.as_ref());
66        Ok(Self {
67            x, y, class, atype, data: PD::default()
68        })
69    }
70}
71
72#[cfg_attr(feature = "glifserde", derive(Serialize, Deserialize))]
73#[derive(Constructor, Clone, Debug, Default, PartialEq)]
74pub struct GlifAnchor {
75    pub x: IntegerOrFloat,
76    pub y: IntegerOrFloat,
77    pub class: Option<GlifStringLenOne>,
78}