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 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 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 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 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 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 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}