glifparser/glif/mfek/
layer.rs

1use serde::Serialize;
2use serde::Deserialize;
3use kurbo::Affine;
4use plist;
5
6use super::{MFEKOutline, LayerOperation};
7use crate::color::Color;
8use crate::glif::name_to_filename;
9use crate::image::GlifImage;
10use crate::point::PointData;
11
12macro_rules! DEFAULT_LAYER_FORMAT_STR {() => {"Layer {}"}}
13pub const DEFAULT_LAYER_FORMAT_STR: &str = DEFAULT_LAYER_FORMAT_STR!();
14
15#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
16pub struct Layer<PD: PointData> {
17    pub name: String,
18    pub visible: bool,
19    pub color: Option<Color>,
20    pub outline: MFEKOutline<PD>,
21    pub operation: Option<LayerOperation>,
22    pub images: Vec<(GlifImage, Affine)>,
23}
24
25pub struct CubicLayer<PD: PointData> (Layer<PD>);
26
27impl<PD: PointData> Layer<PD> {
28    pub fn to_glyphs_dir(&self, idx: usize) -> String {
29        if idx == 0 {
30            String::from("glyphs")
31        } else {
32            format!("glyphs.{}", name_to_filename(&self.name, false))
33        }
34    }
35}
36
37/// Create UFO(3) specification layer layerinfo.plist's for our glyph.
38pub trait ToLayerInfoPlist {
39    /// # Safety
40    ///
41    /// plist::Value guaranteed to be of variant plist::Dictionary
42    fn to_layerinfo_plist(&self) -> Option<plist::Value>;
43}
44
45/// Create UFO(3) specification layer layercontents.plist's for our glyph.
46pub trait ToLayerContentsPlist {
47    /// # Safety
48    ///
49    /// plist::Value guaranteed to be of variant plist::Array
50    fn to_layercontents_plist(&self) -> plist::Value;
51
52    /// # Safety
53    ///
54    /// plist::Value guaranteed to be of variant plist::Array
55    fn merge_layercontents_plists(&self, other: plist::Value) -> plist::Value;
56}
57
58impl<PD: PointData> ToLayerInfoPlist for Layer<PD> {
59    fn to_layerinfo_plist(&self) -> Option<plist::Value> {
60        let color = if let Some(color) = self.color {
61            color
62        } else {
63            return None
64        };
65
66        let mut layerinfo: plist::Dictionary = plist::Dictionary::new();
67
68        layerinfo.insert(String::from("color"), plist::Value::String(color.to_string()));
69
70        Some(plist::Value::Dictionary(layerinfo))
71    }
72}
73
74impl<PD: PointData> ToLayerContentsPlist for &[Layer<PD>] {
75    fn to_layercontents_plist(&self) -> plist::Value {
76        let mut ret: Vec<plist::Value> = Vec::new();
77
78        for (i, layer) in self.iter().enumerate() {
79            if !layer.visible { continue }
80            let key = if layer.name == format!(DEFAULT_LAYER_FORMAT_STR!(), 0) {
81                String::from("public.default") // https://unifiedfontobject.org/versions/ufo3/layercontents.plist/#publicdefault
82            } else {
83                layer.name.clone()
84            };
85            let value = layer.to_glyphs_dir(i);
86            ret.push(plist::Value::Array(vec![plist::Value::String(key), plist::Value::String(value)]));
87        }
88
89        plist::Value::Array(ret)
90    }
91    fn merge_layercontents_plists(&self, other: plist::Value) -> plist::Value {
92        let our_lc = self.to_layercontents_plist();
93        let inner_ours: Vec<plist::Value> = our_lc.into_array().unwrap(); // Safe. Cf. to_layercontents_plist return type
94        let mut inner_theirs: Vec<plist::Value> = other.into_array().unwrap();
95        for l in inner_ours.iter() {
96            if !inner_theirs.contains(&l) {
97                inner_theirs.push(l.clone());
98            }
99        }
100        plist::Value::Array(inner_theirs)
101    }
102}