lb_rs/model/svg/
buffer.rs

1use std::collections::HashMap;
2use std::fmt::Write;
3
4use bezier_rs::{Bezier, Subpath};
5use glam::{DAffine2, DMat2, DVec2};
6use indexmap::IndexMap;
7use usvg::{
8    fontdb::Database,
9    tiny_skia_path::{PathSegment, Point},
10    Options, Transform,
11};
12use usvg::{Color, Paint};
13use uuid::Uuid;
14
15use super::element::{DynamicColor, Stroke, WeakImage, WeakImages};
16use super::{
17    diff::DiffState,
18    element::{Element, ManipulatorGroupId, Path},
19};
20
21const ZOOM_G_ID: &str = "lb_master_transform";
22const WEAK_IMAGE_G_ID: &str = "lb_images";
23
24#[derive(Default, Clone)]
25pub struct Buffer {
26    pub elements: IndexMap<Uuid, Element>,
27    pub weak_images: WeakImages,
28    pub master_transform: Transform,
29    id_map: HashMap<Uuid, String>,
30}
31
32impl Buffer {
33    pub fn new(content: &str) -> Self {
34        let mut elements = IndexMap::default();
35        let mut master_transform = Transform::identity();
36        let mut id_map = HashMap::default();
37        let mut weak_images = WeakImages::default();
38
39        let maybe_tree = usvg::Tree::from_str(content, &Options::default(), &Database::default());
40
41        if let Err(err) = maybe_tree {
42            println!("{:#?}", err);
43        } else {
44            let utree = maybe_tree.unwrap();
45
46            utree.root().children().iter().for_each(|u_el| {
47                parse_child(
48                    u_el,
49                    &mut elements,
50                    &mut master_transform,
51                    &mut id_map,
52                    &mut weak_images,
53                )
54            });
55        }
56
57        Self { elements, master_transform, id_map, weak_images }
58    }
59
60    pub fn reload(
61        local_elements: &mut IndexMap<Uuid, Element>, local_weak_images: &mut WeakImages,
62        local_master_transform: Transform, base_buffer: &Self, remote_buffer: &Self,
63    ) {
64        // todo: convert weak images
65        for (id, base_img) in base_buffer.weak_images.iter() {
66            if let Some(remote_img) = remote_buffer.weak_images.get(id) {
67                if remote_img != base_img {
68                    local_weak_images.insert(*id, *remote_img);
69                }
70            } else {
71                // this was deleted remotly
72                local_weak_images.remove(id);
73                local_elements.shift_remove(id);
74            }
75        }
76
77        for (id, remote_img) in remote_buffer.weak_images.iter() {
78            if !base_buffer.weak_images.contains_key(id) {
79                local_weak_images.insert(*id, *remote_img);
80            }
81        }
82
83        base_buffer
84            .elements
85            .iter()
86            .filter_map(|(id, el)| if let Element::Path(p) = el { Some((id, p)) } else { None })
87            .for_each(|(id, base_path)| {
88                if let Some(Element::Path(remote_path)) = remote_buffer.elements.get(id) {
89                    if remote_path != base_path {
90                        // this element was changed remotly
91                        let mut transformed_path = remote_path.clone();
92                        transformed_path.diff_state.transformed = Some(local_master_transform);
93                        transformed_path.diff_state.transformed = None;
94                        transformed_path
95                            .data
96                            .apply_transform(u_transform_to_bezier(&local_master_transform));
97                        transformed_path.diff_state.data_changed = true;
98
99                        local_elements.insert(*id, Element::Path(transformed_path.clone()));
100                    }
101                } else {
102                    // this was deletd remotly
103                    local_elements.shift_remove(id);
104                }
105            });
106
107        remote_buffer
108            .elements
109            .iter()
110            .filter_map(|(id, el)| if let Element::Path(p) = el { Some((id, p)) } else { None })
111            .enumerate()
112            .for_each(|(i, (id, remote_el))| {
113                if !base_buffer.elements.contains_key(id) {
114                    let mut transformed_path = remote_el.clone();
115                    transformed_path.diff_state.transformed = Some(local_master_transform);
116                    transformed_path
117                        .data
118                        .apply_transform(u_transform_to_bezier(&local_master_transform));
119
120                    transformed_path.diff_state.data_changed = true;
121                    transformed_path.diff_state.transformed = None;
122
123                    local_elements.insert_before(i, *id, Element::Path(transformed_path));
124                }
125            });
126    }
127
128    pub fn insert(&mut self, id: Uuid, mut el: Element) {
129        match el {
130            Element::Path(ref mut path) => {
131                path.diff_state.data_changed = true;
132                path.diff_state.transformed = None
133            }
134            Element::Image(ref mut image) => {
135                image.diff_state.data_changed = true;
136                image.diff_state.transformed = None
137            }
138            _ => {}
139        }
140        self.elements.insert_before(0, id, el);
141    }
142
143    pub fn hard_remove(&mut self, id: Uuid) {
144        self.elements.shift_remove(&id);
145    }
146
147    /// soft remove that marks the element as deleted but retains it in memeory
148    pub fn remove(&mut self, id: Uuid) {
149        if let Some(el) = self.elements.get_mut(&id) {
150            match el {
151                Element::Path(ref mut path) => {
152                    path.deleted = true;
153                    path.diff_state.delete_changed = true;
154                }
155                Element::Image(ref mut image) => {
156                    image.deleted = true;
157                    image.diff_state.delete_changed = true;
158                }
159                _ => {}
160            }
161        }
162    }
163
164    pub fn serialize(&self) -> String {
165        let mut root = r#"<svg xmlns="http://www.w3.org/2000/svg">"#.into();
166        let mut weak_images = WeakImages::default();
167        for (index, el) in self.elements.iter().enumerate() {
168            match el.1 {
169                Element::Path(p) => {
170                    if p.deleted {
171                        continue;
172                    }
173                    let mut curv_attrs = " ".to_string();
174                    // if it's empty then the curve will not be converted to string via bezier_rs
175                    if let Some(stroke) = p.stroke {
176                        curv_attrs = format!(
177                            "stroke-width='{}' stroke='rgba({},{},{},{})' fill='none' id='{}' transform='{}'",
178                            stroke.width,
179                            stroke.color.light.red,
180                            stroke.color.light.green,
181                            stroke.color.light.blue,
182                            stroke.opacity,
183                            self.id_map.get(el.0).unwrap_or(&el.0.to_string()),
184                            to_svg_transform(p.transform)
185                        );
186                    }
187
188                    let mut data = p.data.clone();
189                    data.apply_transform(u_transform_to_bezier(
190                        &self.master_transform.invert().unwrap_or_default(),
191                    ));
192
193                    if data.len() > 1 {
194                        data.to_svg(&mut root, curv_attrs, "".into(), "".into(), "".into())
195                    }
196                }
197                Element::Image(img) => {
198                    if img.deleted {
199                        continue;
200                    }
201
202                    let mut weak_image: WeakImage = img.into_weak(index);
203
204                    weak_image.transform(self.master_transform.invert().unwrap_or_default());
205
206                    weak_images.insert(*el.0, weak_image);
207                }
208                Element::Text(_) => {}
209            }
210        }
211
212        let zoom_level = format!(
213            r#"<g id="{}" transform="matrix({} {} {} {} {} {})"></g>"#,
214            ZOOM_G_ID,
215            self.master_transform.sx,
216            self.master_transform.kx,
217            self.master_transform.ky,
218            self.master_transform.sy,
219            self.master_transform.tx,
220            self.master_transform.ty
221        );
222
223        weak_images.extend(self.weak_images.iter());
224
225        if !weak_images.is_empty() {
226            let binary_data = bincode::serialize(&weak_images).expect("Failed to serialize");
227            let base64_data = base64::encode(&binary_data);
228
229            let _ = write!(
230                &mut root,
231                "<g id=\"{}\"> <g id=\"{}\"></g></g>",
232                WEAK_IMAGE_G_ID, base64_data
233            );
234        }
235
236        let _ = write!(&mut root, "{} </svg>", zoom_level);
237        root
238    }
239}
240
241pub fn parse_child(
242    u_el: &usvg::Node, elements: &mut IndexMap<Uuid, Element>, master_transform: &mut Transform,
243    id_map: &mut HashMap<Uuid, String>, weak_images: &mut WeakImages,
244) {
245    match &u_el {
246        usvg::Node::Group(group) => {
247            if group.id().eq(ZOOM_G_ID) {
248                *master_transform = group.transform();
249            } else if group.id().eq(WEAK_IMAGE_G_ID) {
250                if let Some(usvg::Node::Group(weak_images_g)) = group.children().first() {
251                    let base64 = base64::decode(weak_images_g.id().as_bytes())
252                        .expect("Failed to decode base64");
253
254                    let decoded: WeakImages = bincode::deserialize(&base64).unwrap();
255                    *weak_images = decoded;
256                }
257            } else {
258                group.children().iter().for_each(|u_el| {
259                    parse_child(u_el, elements, master_transform, id_map, weak_images)
260                });
261            }
262        }
263
264        usvg::Node::Image(_) => {}
265        usvg::Node::Path(path) => {
266            let diff_state = DiffState { data_changed: true, ..Default::default() };
267
268            let id = get_internal_id(path.id(), id_map);
269
270            let stroke = if let Some(s) = path.stroke() {
271                if let Paint::Color(color) = *s.paint() {
272                    let canvas_colors = get_canvas_colors();
273
274                    let maybe_dynamic_color = if let Some(dynamic_color) = canvas_colors
275                        .iter()
276                        .find(|c| c.light.eq(&color) || c.dark.eq(&color))
277                    {
278                        *dynamic_color
279                    } else {
280                        DynamicColor { light: color, dark: color }
281                    };
282
283                    Some(Stroke {
284                        color: maybe_dynamic_color,
285                        opacity: s.opacity().get(),
286                        width: s.width().get(),
287                    })
288                } else {
289                    None
290                }
291            } else {
292                None
293            };
294            let mut data = usvg_d_to_subpath(path);
295
296            data.apply_transform(u_transform_to_bezier(
297                &path.abs_transform().invert().unwrap_or_default(),
298            ));
299
300            elements.insert(
301                id,
302                Element::Path(Path {
303                    data,
304                    visibility: path.visibility(),
305                    fill: path.fill().cloned(),
306                    stroke,
307                    transform: path.abs_transform(),
308                    diff_state,
309                    deleted: false,
310                    opacity: 1.0,
311                }),
312            );
313        }
314        _ => {}
315    }
316}
317
318fn get_internal_id(svg_id: &str, id_map: &mut HashMap<Uuid, String>) -> Uuid {
319    let id: Uuid = svg_id.parse().unwrap_or(Uuid::new_v4());
320
321    if id_map.insert(id, svg_id.to_owned()).is_some() {
322        warn!(id = svg_id, "found elements  with duplicate id");
323    }
324    id
325}
326
327pub fn get_canvas_colors() -> Vec<DynamicColor> {
328    let mut highlighter_colors = get_highlighter_colors();
329    highlighter_colors.append(&mut get_pen_colors());
330
331    highlighter_colors
332}
333
334pub fn get_highlighter_colors() -> Vec<DynamicColor> {
335    let yellow =
336        DynamicColor { light: Color::new_rgb(244, 250, 65), dark: Color::new_rgb(244, 250, 65) };
337    let blue =
338        DynamicColor { light: Color::new_rgb(65, 194, 250), dark: Color::new_rgb(65, 194, 250) };
339    let pink =
340        DynamicColor { light: Color::new_rgb(254, 110, 175), dark: Color::new_rgb(254, 110, 175) };
341    vec![yellow, blue, pink]
342}
343
344pub fn get_pen_colors() -> Vec<DynamicColor> {
345    let red =
346        DynamicColor { light: Color::new_rgb(218, 21, 21), dark: Color::new_rgb(174, 33, 33) };
347    let orange =
348        DynamicColor { light: Color::new_rgb(255, 149, 0), dark: Color::new_rgb(255, 159, 10) };
349    let yellow =
350        DynamicColor { light: Color::new_rgb(255, 204, 0), dark: Color::new_rgb(255, 214, 10) };
351    let green =
352        DynamicColor { light: Color::new_rgb(42, 136, 49), dark: Color::new_rgb(56, 176, 65) };
353    let teal =
354        DynamicColor { light: Color::new_rgb(0, 128, 128), dark: Color::new_rgb(0, 147, 147) };
355    let cyan =
356        DynamicColor { light: Color::new_rgb(85, 190, 240), dark: Color::new_rgb(90, 200, 245) };
357    let blue =
358        DynamicColor { light: Color::new_rgb(62, 130, 230), dark: Color::new_rgb(54, 116, 207) };
359    let indigo =
360        DynamicColor { light: Color::new_rgb(75, 0, 130), dark: Color::new_rgb(64, 0, 110) };
361    let purple =
362        DynamicColor { light: Color::new_rgb(128, 0, 128), dark: Color::new_rgb(147, 0, 147) };
363    let magenta =
364        DynamicColor { light: Color::new_rgb(175, 82, 222), dark: Color::new_rgb(191, 90, 242) };
365    let pink =
366        DynamicColor { light: Color::new_rgb(255, 105, 180), dark: Color::new_rgb(255, 120, 190) };
367    let brown =
368        DynamicColor { light: Color::new_rgb(139, 69, 19), dark: Color::new_rgb(101, 53, 17) };
369
370    let fg = DynamicColor { light: Color::black(), dark: Color::white() };
371
372    vec![fg, red, orange, yellow, green, teal, cyan, blue, indigo, purple, brown, magenta, pink]
373}
374
375fn usvg_d_to_subpath(path: &usvg::Path) -> Subpath<ManipulatorGroupId> {
376    let mut prev = Point::default();
377    let mut subpath: Subpath<ManipulatorGroupId> = Subpath::new(vec![], false);
378    for segment in path.data().segments() {
379        match segment {
380            PathSegment::MoveTo(p) => {
381                prev = p;
382            }
383            PathSegment::CubicTo(p1, p2, p3) => {
384                let bez = Bezier::from_cubic_coordinates(
385                    prev.x.into(),
386                    prev.y.into(),
387                    p1.x.into(),
388                    p1.y.into(),
389                    p2.x.into(),
390                    p2.y.into(),
391                    p3.x.into(),
392                    p3.y.into(),
393                );
394                subpath.append_bezier(&bez, bezier_rs::AppendType::IgnoreStart);
395                prev = p3;
396            }
397            PathSegment::LineTo(p) => {
398                let bez = Bezier::from_linear_coordinates(
399                    prev.x.into(),
400                    prev.y.into(),
401                    p.x.into(),
402                    p.y.into(),
403                );
404                subpath.append_bezier(&bez, bezier_rs::AppendType::IgnoreStart);
405                prev = p;
406            }
407            _ => {}
408        }
409    }
410
411    let t = path.abs_transform();
412
413    subpath.apply_transform(DAffine2 {
414        matrix2: DMat2 {
415            x_axis: DVec2 { x: t.sx.into(), y: t.ky.into() },
416            y_axis: DVec2 { x: t.kx.into(), y: t.sy.into() },
417        },
418        translation: DVec2 { x: t.tx.into(), y: t.ty.into() },
419    });
420
421    subpath
422}
423
424pub fn u_transform_to_bezier(src: &Transform) -> DAffine2 {
425    glam::DAffine2 {
426        matrix2: DMat2 {
427            x_axis: DVec2 { x: src.sx.into(), y: src.ky.into() },
428            y_axis: DVec2 { x: src.kx.into(), y: src.sy.into() },
429        },
430        translation: glam::DVec2 { x: src.tx.into(), y: src.ty.into() },
431    }
432}
433
434fn to_svg_transform(transform: Transform) -> String {
435    format!(
436        "matrix({} {} {} {} {} {})",
437        transform.sx, transform.ky, transform.kx, transform.sy, transform.tx, transform.ty
438    )
439}