vsvg/document/
mod.rs

1use crate::{LayerTrait, PathDataTrait, PathTrait, Transforms};
2use std::collections::BTreeMap;
3
4#[allow(clippy::module_inception)]
5mod document;
6mod flattened_document;
7mod metadata;
8
9use crate::document_to_svg_doc;
10use crate::stats::LayerStats;
11pub use document::Document;
12pub use flattened_document::FlattenedDocument;
13pub use metadata::DocumentMetadata;
14
15pub type LayerID = usize;
16
17pub trait DocumentTrait<L: LayerTrait<P, D>, P: PathTrait<D>, D: PathDataTrait>:
18    Transforms
19{
20    fn layers(&self) -> &BTreeMap<LayerID, L>;
21
22    fn layers_mut(&mut self) -> &mut BTreeMap<LayerID, L>;
23
24    fn metadata(&self) -> &DocumentMetadata;
25
26    fn metadata_mut(&mut self) -> &mut DocumentMetadata;
27
28    fn push_path(&mut self, id: LayerID, path: impl Into<P>) {
29        self.get_mut(id).push_path(path.into());
30    }
31
32    fn try_get(&self, id: LayerID) -> Option<&L> {
33        self.layers().get(&id)
34    }
35
36    #[must_use]
37    fn get_mut(&mut self, id: LayerID) -> &mut L {
38        self.layers_mut().entry(id).or_insert_with(|| L::default())
39    }
40
41    fn ensure_exists(&mut self, id: LayerID) {
42        let _ = self.get_mut(id);
43    }
44
45    fn for_each<F>(&mut self, f: F)
46    where
47        F: Fn(&mut L),
48    {
49        self.layers_mut().values_mut().for_each(f);
50    }
51
52    /// Merge all existing layers into layer 0, if any.
53    fn merge_layers(&mut self) {
54        if let Some((_, mut layer)) = self.layers_mut().pop_first() {
55            while let Some((_, other)) = self.layers_mut().pop_first() {
56                layer.merge(&other);
57            }
58
59            self.layers_mut().insert(0, layer);
60        }
61    }
62
63    #[must_use]
64    fn bounds(&self) -> Option<kurbo::Rect> {
65        if self.layers().is_empty() {
66            return None;
67        }
68
69        let mut values = self.layers().values();
70        let first = values.next().expect("not empty").bounds();
71        values.fold(first, |acc, layer| match (acc, layer.bounds()) {
72            (Some(acc), Some(layer)) => Some(acc.union(layer)),
73            (Some(acc), None) => Some(acc),
74            (None, Some(path)) => Some(path),
75            (None, None) => None,
76        })
77    }
78
79    #[must_use]
80    fn stats(&self) -> BTreeMap<LayerID, LayerStats> {
81        self.layers()
82            .iter()
83            .map(|(id, layer)| (*id, layer.stats()))
84            .collect()
85    }
86
87    fn to_svg_string(&self) -> Result<String, std::fmt::Error> {
88        use std::fmt::Write;
89
90        let doc = document_to_svg_doc(self);
91        let mut svg = String::new();
92        write!(svg, "{doc}").map(|()| svg)
93    }
94    fn to_svg(&self, writer: impl std::io::Write) -> std::io::Result<()> {
95        let doc = document_to_svg_doc(self);
96        svg::write(writer, &doc)
97    }
98
99    fn to_svg_file(&self, file_path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
100        let file = std::io::BufWriter::new(std::fs::File::create(file_path)?);
101        self.to_svg(file)
102    }
103}