Skip to main content

azul_core/
svg.rs

1//! SVG rendering and path tessellation.
2//!
3//! This module provides functionality for parsing, manipulating, and rendering SVG paths.
4//! It includes:
5//!
6//! - **Path tessellation**: Converts SVG paths into triangle meshes for GPU rendering
7//! - **Stroke generation**: Creates stroked paths with various line join and cap styles
8//! - **Transform support**: Applies CSS transforms to SVG elements
9//! - **Style parsing**: Handles SVG fill, stroke, opacity, and other attributes
10//!
11//! The module uses Lyon for geometric tessellation and generates vertex/index buffers
12//! that can be uploaded to WebRender for hardware-accelerated rendering.
13
14use alloc::{
15    string::{String, ToString},
16    vec::Vec,
17};
18use core::fmt;
19
20use azul_css::{
21    props::{
22        basic::{
23            ColorF, ColorU, OptionColorU, OptionLayoutSize, PixelValue, SvgCubicCurve, SvgPoint,
24            SvgQuadraticCurve, SvgRect, SvgVector,
25        },
26        style::{StyleTransformOrigin, StyleTransformVec},
27    },
28    AzString, OptionString, StringVec, U32Vec,
29};
30
31use crate::{
32    geom::PhysicalSizeU32,
33    gl::{
34        GlContextPtr, GlShader, IndexBufferFormat, Texture, Uniform, UniformType, VertexAttribute,
35        VertexAttributeType, VertexBuffer, VertexLayout, VertexLayoutDescription,
36    },
37    transform::{ComputedTransform3D, RotationMode},
38    xml::XmlError,
39};
40
41/// Default miter limit for stroke joins (ratio of miter length to stroke width)
42const DEFAULT_MITER_LIMIT: f32 = 4.0;
43/// Default stroke width in pixels
44const DEFAULT_LINE_WIDTH: f32 = 1.0;
45/// Default tessellation tolerance in pixels (smaller = more vertices, higher quality)
46const DEFAULT_TOLERANCE: f32 = 0.1;
47
48/// Represents the dimensions of an SVG viewport or element.
49#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
50#[repr(C)]
51pub struct SvgSize {
52    /// Width in SVG user units
53    pub width: f32,
54    /// Height in SVG user units
55    pub height: f32,
56}
57
58/// A line segment in 2D space.
59#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
60#[repr(C)]
61pub struct SvgLine {
62    /// Start point of the line
63    pub start: SvgPoint,
64    /// End point of the line
65    pub end: SvgPoint,
66}
67
68impl SvgLine {
69    /// Creates a new line segment from start to end point
70    #[inline]
71    pub const fn new(start: SvgPoint, end: SvgPoint) -> Self {
72        Self { start, end }
73    }
74
75    /// Computes the inward-facing normal vector for this line.
76    ///
77    /// The normal points 90 degrees to the right of the line direction.
78    /// Returns `None` if the line has zero length.
79    pub fn inwards_normal(&self) -> Option<SvgPoint> {
80        let dx = self.end.x - self.start.x;
81        let dy = self.end.y - self.start.y;
82        let edge_length = (dx * dx + dy * dy).sqrt();
83        let x = -dy / edge_length;
84        let y = dx / edge_length;
85
86        if x.is_finite() && y.is_finite() {
87            Some(SvgPoint { x, y })
88        } else {
89            None
90        }
91    }
92
93    pub fn outwards_normal(&self) -> Option<SvgPoint> {
94        let inwards = self.inwards_normal()?;
95        Some(SvgPoint {
96            x: -inwards.x,
97            y: -inwards.y,
98        })
99    }
100
101    pub fn reverse(&mut self) {
102        let temp = self.start;
103        self.start = self.end;
104        self.end = temp;
105    }
106    pub fn get_start(&self) -> SvgPoint {
107        self.start
108    }
109    pub fn get_end(&self) -> SvgPoint {
110        self.end
111    }
112
113    pub fn get_t_at_offset(&self, offset: f64) -> f64 {
114        offset / self.get_length()
115    }
116
117    /// Returns the tangent vector of the line.
118    /// For a line, the tangent is constant (same direction everywhere),
119    /// so no `t` parameter is needed.
120    pub fn get_tangent_vector_at_t(&self) -> SvgVector {
121        let dx = self.end.x - self.start.x;
122        let dy = self.end.y - self.start.y;
123        SvgVector {
124            x: dx as f64,
125            y: dy as f64,
126        }
127        .normalize()
128    }
129
130    pub fn get_x_at_t(&self, t: f64) -> f64 {
131        self.start.x as f64 + (self.end.x as f64 - self.start.x as f64) * t
132    }
133
134    pub fn get_y_at_t(&self, t: f64) -> f64 {
135        self.start.y as f64 + (self.end.y as f64 - self.start.y as f64) * t
136    }
137
138    pub fn get_length(&self) -> f64 {
139        let dx = self.end.x - self.start.x;
140        let dy = self.end.y - self.start.y;
141        libm::hypotf(dx, dy) as f64
142    }
143
144    pub fn get_bounds(&self) -> SvgRect {
145        let min_x = self.start.x.min(self.end.x);
146        let max_x = self.start.x.max(self.end.x);
147
148        let min_y = self.start.y.min(self.end.y);
149        let max_y = self.start.y.max(self.end.y);
150
151        let width = (max_x - min_x).abs();
152        let height = (max_y - min_y).abs();
153
154        SvgRect {
155            width,
156            height,
157            x: min_x,
158            y: min_y,
159            ..SvgRect::default()
160        }
161    }
162}
163
164#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
165#[repr(C, u8)]
166pub enum SvgPathElement {
167    Line(SvgLine),
168    QuadraticCurve(SvgQuadraticCurve),
169    CubicCurve(SvgCubicCurve),
170}
171
172impl_option!(
173    SvgPathElement,
174    OptionSvgPathElement,
175    [Debug, Copy, Clone, PartialEq, PartialOrd]
176);
177
178impl SvgPathElement {
179    /// Creates a line path element from a SvgLine
180    #[inline]
181    pub const fn line(l: SvgLine) -> Self {
182        SvgPathElement::Line(l)
183    }
184
185    /// Creates a quadratic curve path element from a SvgQuadraticCurve
186    #[inline]
187    pub const fn quadratic_curve(qc: SvgQuadraticCurve) -> Self {
188        SvgPathElement::QuadraticCurve(qc)
189    }
190
191    /// Creates a cubic curve path element from a SvgCubicCurve
192    #[inline]
193    pub const fn cubic_curve(cc: SvgCubicCurve) -> Self {
194        SvgPathElement::CubicCurve(cc)
195    }
196
197    pub fn set_last(&mut self, point: SvgPoint) {
198        match self {
199            SvgPathElement::Line(l) => l.end = point,
200            SvgPathElement::QuadraticCurve(qc) => qc.end = point,
201            SvgPathElement::CubicCurve(cc) => cc.end = point,
202        }
203    }
204
205    pub fn set_first(&mut self, point: SvgPoint) {
206        match self {
207            SvgPathElement::Line(l) => l.start = point,
208            SvgPathElement::QuadraticCurve(qc) => qc.start = point,
209            SvgPathElement::CubicCurve(cc) => cc.start = point,
210        }
211    }
212
213    pub fn reverse(&mut self) {
214        match self {
215            SvgPathElement::Line(l) => l.reverse(),
216            SvgPathElement::QuadraticCurve(qc) => qc.reverse(),
217            SvgPathElement::CubicCurve(cc) => cc.reverse(),
218        }
219    }
220    pub fn get_start(&self) -> SvgPoint {
221        match self {
222            SvgPathElement::Line(l) => l.get_start(),
223            SvgPathElement::QuadraticCurve(qc) => qc.get_start(),
224            SvgPathElement::CubicCurve(cc) => cc.get_start(),
225        }
226    }
227    pub fn get_end(&self) -> SvgPoint {
228        match self {
229            SvgPathElement::Line(l) => l.get_end(),
230            SvgPathElement::QuadraticCurve(qc) => qc.get_end(),
231            SvgPathElement::CubicCurve(cc) => cc.get_end(),
232        }
233    }
234    pub fn get_bounds(&self) -> SvgRect {
235        match self {
236            SvgPathElement::Line(l) => l.get_bounds(),
237            SvgPathElement::QuadraticCurve(qc) => qc.get_bounds(),
238            SvgPathElement::CubicCurve(cc) => cc.get_bounds(),
239        }
240    }
241    pub fn get_length(&self) -> f64 {
242        match self {
243            SvgPathElement::Line(l) => l.get_length(),
244            SvgPathElement::QuadraticCurve(qc) => qc.get_length(),
245            SvgPathElement::CubicCurve(cc) => cc.get_length(),
246        }
247    }
248    pub fn get_t_at_offset(&self, offset: f64) -> f64 {
249        match self {
250            SvgPathElement::Line(l) => l.get_t_at_offset(offset),
251            SvgPathElement::QuadraticCurve(qc) => qc.get_t_at_offset(offset),
252            SvgPathElement::CubicCurve(cc) => cc.get_t_at_offset(offset),
253        }
254    }
255    pub fn get_tangent_vector_at_t(&self, t: f64) -> SvgVector {
256        match self {
257            SvgPathElement::Line(l) => l.get_tangent_vector_at_t(),
258            SvgPathElement::QuadraticCurve(qc) => qc.get_tangent_vector_at_t(t),
259            SvgPathElement::CubicCurve(cc) => cc.get_tangent_vector_at_t(t),
260        }
261    }
262    pub fn get_x_at_t(&self, t: f64) -> f64 {
263        match self {
264            SvgPathElement::Line(l) => l.get_x_at_t(t),
265            SvgPathElement::QuadraticCurve(qc) => qc.get_x_at_t(t),
266            SvgPathElement::CubicCurve(cc) => cc.get_x_at_t(t),
267        }
268    }
269    pub fn get_y_at_t(&self, t: f64) -> f64 {
270        match self {
271            SvgPathElement::Line(l) => l.get_y_at_t(t),
272            SvgPathElement::QuadraticCurve(qc) => qc.get_y_at_t(t),
273            SvgPathElement::CubicCurve(cc) => cc.get_y_at_t(t),
274        }
275    }
276}
277
278impl_vec!(SvgPathElement, SvgPathElementVec, SvgPathElementVecDestructor, SvgPathElementVecDestructorType, SvgPathElementVecSlice, OptionSvgPathElement);
279impl_vec_debug!(SvgPathElement, SvgPathElementVec);
280impl_vec_clone!(
281    SvgPathElement,
282    SvgPathElementVec,
283    SvgPathElementVecDestructor
284);
285impl_vec_partialeq!(SvgPathElement, SvgPathElementVec);
286impl_vec_partialord!(SvgPathElement, SvgPathElementVec);
287
288#[derive(Debug, Clone, PartialEq, PartialOrd)]
289#[repr(C)]
290pub struct SvgPath {
291    pub items: SvgPathElementVec,
292}
293
294impl_option!(
295    SvgPath,
296    OptionSvgPath,
297    copy = false,
298    [Debug, Clone, PartialEq, PartialOrd]
299);
300
301impl SvgPath {
302    /// Creates a new SvgPath from a vector of path elements
303    #[inline]
304    pub const fn new(items: SvgPathElementVec) -> Self {
305        Self { items }
306    }
307
308    pub fn get_start(&self) -> Option<SvgPoint> {
309        self.items.as_ref().first().map(|s| s.get_start())
310    }
311
312    pub fn get_end(&self) -> Option<SvgPoint> {
313        self.items.as_ref().last().map(|s| s.get_end())
314    }
315
316    pub fn close(&mut self) {
317        let first = match self.items.as_ref().first() {
318            Some(s) => s,
319            None => return,
320        };
321        let last = match self.items.as_ref().last() {
322            Some(s) => s,
323            None => return,
324        };
325        if first.get_start() != last.get_end() {
326            let mut elements = self.items.as_slice().to_vec();
327            elements.push(SvgPathElement::Line(SvgLine {
328                start: last.get_end(),
329                end: first.get_start(),
330            }));
331            self.items = elements.into();
332        }
333    }
334
335    pub fn is_closed(&self) -> bool {
336        let first = self.items.as_ref().first();
337        let last = self.items.as_ref().last();
338        match (first, last) {
339            (Some(f), Some(l)) => (f.get_start() == l.get_end()),
340            _ => false,
341        }
342    }
343
344    // reverses the order of elements in the path
345    pub fn reverse(&mut self) {
346        // swap self.items with a default vec
347        let mut vec = SvgPathElementVec::from_const_slice(&[]);
348        core::mem::swap(&mut vec, &mut self.items);
349        let mut vec = vec.into_library_owned_vec();
350
351        // reverse the order of items in the vec
352        vec.reverse();
353
354        // reverse the order inside the item itself
355        // i.e. swap line.start and line.end
356        for item in vec.iter_mut() {
357            item.reverse();
358        }
359
360        // swap back
361        let mut vec = SvgPathElementVec::from_vec(vec);
362        core::mem::swap(&mut vec, &mut self.items);
363    }
364
365    pub fn join_with(&mut self, mut path: Self) -> Option<()> {
366        let self_last_point = self.items.as_ref().last()?.get_end();
367        let other_start_point = path.items.as_ref().first()?.get_start();
368        let interpolated_join_point = SvgPoint {
369            x: (self_last_point.x + other_start_point.x) / 2.0,
370            y: (self_last_point.y + other_start_point.y) / 2.0,
371        };
372
373        // swap self.items with a default vec
374        let mut vec = SvgPathElementVec::from_const_slice(&[]);
375        core::mem::swap(&mut vec, &mut self.items);
376        let mut vec = vec.into_library_owned_vec();
377
378        let mut other = SvgPathElementVec::from_const_slice(&[]);
379        core::mem::swap(&mut other, &mut path.items);
380        let mut other = other.into_library_owned_vec();
381
382        let vec_len = vec.len() - 1;
383        vec.get_mut(vec_len)?.set_last(interpolated_join_point);
384        other.get_mut(0)?.set_first(interpolated_join_point);
385        vec.append(&mut other);
386
387        // swap back
388        let mut vec = SvgPathElementVec::from_vec(vec);
389        core::mem::swap(&mut vec, &mut self.items);
390
391        Some(())
392    }
393    pub fn get_bounds(&self) -> SvgRect {
394        let mut first_bounds = match self.items.as_ref().get(0) {
395            Some(s) => s.get_bounds(),
396            None => return SvgRect::default(),
397        };
398
399        for mp in self.items.as_ref().iter().skip(1) {
400            let mp_bounds = mp.get_bounds();
401            first_bounds.union_with(&mp_bounds);
402        }
403
404        first_bounds
405    }
406}
407
408#[derive(Debug, Clone, PartialEq, PartialOrd)]
409#[repr(C)]
410pub struct SvgMultiPolygon {
411    /// NOTE: If a ring represends a hole, simply reverse the order of points
412    pub rings: SvgPathVec,
413}
414
415impl_option!(
416    SvgMultiPolygon,
417    OptionSvgMultiPolygon,
418    copy = false,
419    [Debug, Clone, PartialEq, PartialOrd]
420);
421
422impl SvgMultiPolygon {
423    /// Creates a new SvgMultiPolygon from a vector of paths (rings)
424    /// NOTE: If a ring represents a hole, simply reverse the order of points
425    #[inline]
426    pub const fn new(rings: SvgPathVec) -> Self {
427        Self { rings }
428    }
429
430    pub fn get_bounds(&self) -> SvgRect {
431        let mut first_bounds = match self
432            .rings
433            .get(0)
434            .and_then(|b| b.items.get(0).map(|i| i.get_bounds()))
435        {
436            Some(s) => s,
437            // Empty polygon has zero-sized bounds at origin
438            None => return SvgRect::default(),
439        };
440
441        for ring in self.rings.iter() {
442            for item in ring.items.iter() {
443                first_bounds.union_with(&item.get_bounds());
444            }
445        }
446
447        first_bounds
448    }
449}
450
451impl_vec!(SvgPath, SvgPathVec, SvgPathVecDestructor, SvgPathVecDestructorType, SvgPathVecSlice, OptionSvgPath);
452impl_vec_debug!(SvgPath, SvgPathVec);
453impl_vec_clone!(SvgPath, SvgPathVec, SvgPathVecDestructor);
454impl_vec_partialeq!(SvgPath, SvgPathVec);
455impl_vec_partialord!(SvgPath, SvgPathVec);
456
457impl_vec!(SvgMultiPolygon, SvgMultiPolygonVec, SvgMultiPolygonVecDestructor, SvgMultiPolygonVecDestructorType, SvgMultiPolygonVecSlice, OptionSvgMultiPolygon);
458impl_vec_debug!(SvgMultiPolygon, SvgMultiPolygonVec);
459impl_vec_clone!(
460    SvgMultiPolygon,
461    SvgMultiPolygonVec,
462    SvgMultiPolygonVecDestructor
463);
464impl_vec_partialeq!(SvgMultiPolygon, SvgMultiPolygonVec);
465impl_vec_partialord!(SvgMultiPolygon, SvgMultiPolygonVec);
466
467/// One `SvgNode` corresponds to one SVG `<path></path>` element
468#[derive(Debug, Clone, PartialOrd, PartialEq)]
469#[repr(C, u8)]
470pub enum SvgNode {
471    /// Multiple multipolygons, merged to one CPU buf for efficient drawing
472    MultiPolygonCollection(SvgMultiPolygonVec),
473    MultiPolygon(SvgMultiPolygon),
474    MultiShape(SvgSimpleNodeVec),
475    Path(SvgPath),
476    Circle(SvgCircle),
477    Rect(SvgRect),
478}
479
480/// One `SvgSimpleNode` is either a path, a rect or a circle
481#[derive(Debug, Clone, PartialOrd, PartialEq)]
482#[repr(C, u8)]
483pub enum SvgSimpleNode {
484    Path(SvgPath),
485    Circle(SvgCircle),
486    Rect(SvgRect),
487    CircleHole(SvgCircle),
488    RectHole(SvgRect),
489}
490
491impl_option!(
492    SvgSimpleNode,
493    OptionSvgSimpleNode,
494    copy = false,
495    [Debug, Clone, PartialOrd, PartialEq]
496);
497
498impl_vec!(SvgSimpleNode, SvgSimpleNodeVec, SvgSimpleNodeVecDestructor, SvgSimpleNodeVecDestructorType, SvgSimpleNodeVecSlice, OptionSvgSimpleNode);
499impl_vec_debug!(SvgSimpleNode, SvgSimpleNodeVec);
500impl_vec_clone!(SvgSimpleNode, SvgSimpleNodeVec, SvgSimpleNodeVecDestructor);
501impl_vec_partialeq!(SvgSimpleNode, SvgSimpleNodeVec);
502impl_vec_partialord!(SvgSimpleNode, SvgSimpleNodeVec);
503
504impl SvgSimpleNode {
505    pub fn get_bounds(&self) -> SvgRect {
506        match self {
507            SvgSimpleNode::Path(a) => a.get_bounds(),
508            SvgSimpleNode::Circle(a) => a.get_bounds(),
509            SvgSimpleNode::Rect(a) => a.clone(),
510            SvgSimpleNode::CircleHole(a) => a.get_bounds(),
511            SvgSimpleNode::RectHole(a) => a.clone(),
512        }
513    }
514    pub fn is_closed(&self) -> bool {
515        match self {
516            SvgSimpleNode::Path(a) => a.is_closed(),
517            SvgSimpleNode::Circle(a) => true,
518            SvgSimpleNode::Rect(a) => true,
519            SvgSimpleNode::CircleHole(a) => true,
520            SvgSimpleNode::RectHole(a) => true,
521        }
522    }
523}
524
525impl SvgNode {
526    pub fn get_bounds(&self) -> SvgRect {
527        match self {
528            SvgNode::MultiPolygonCollection(a) => {
529                let mut first_mp_bounds = match a.get(0) {
530                    Some(s) => s.get_bounds(),
531                    None => return SvgRect::default(),
532                };
533                for mp in a.iter().skip(1) {
534                    let mp_bounds = mp.get_bounds();
535                    first_mp_bounds.union_with(&mp_bounds);
536                }
537
538                first_mp_bounds
539            }
540            SvgNode::MultiPolygon(a) => a.get_bounds(),
541            SvgNode::MultiShape(a) => {
542                let mut first_mp_bounds = match a.get(0) {
543                    Some(s) => s.get_bounds(),
544                    None => return SvgRect::default(),
545                };
546                for mp in a.iter().skip(1) {
547                    let mp_bounds = mp.get_bounds();
548                    first_mp_bounds.union_with(&mp_bounds);
549                }
550
551                first_mp_bounds
552            }
553            SvgNode::Path(a) => a.get_bounds(),
554            SvgNode::Circle(a) => a.get_bounds(),
555            SvgNode::Rect(a) => a.clone(),
556        }
557    }
558    pub fn is_closed(&self) -> bool {
559        match self {
560            SvgNode::MultiPolygonCollection(a) => {
561                for mp in a.iter() {
562                    for p in mp.rings.as_ref().iter() {
563                        if !p.is_closed() {
564                            return false;
565                        }
566                    }
567                }
568
569                true
570            }
571            SvgNode::MultiPolygon(a) => {
572                for p in a.rings.as_ref().iter() {
573                    if !p.is_closed() {
574                        return false;
575                    }
576                }
577
578                true
579            }
580            SvgNode::MultiShape(a) => {
581                for p in a.as_ref().iter() {
582                    if !p.is_closed() {
583                        return false;
584                    }
585                }
586
587                true
588            }
589            SvgNode::Path(a) => a.is_closed(),
590            SvgNode::Circle(a) => true,
591            SvgNode::Rect(a) => true,
592        }
593    }
594}
595
596#[derive(Debug, Clone, PartialOrd, PartialEq)]
597#[repr(C)]
598pub struct SvgStyledNode {
599    pub geometry: SvgNode,
600    pub style: SvgStyle,
601}
602
603#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
604#[repr(C)]
605pub struct SvgVertex {
606    pub x: f32,
607    pub y: f32,
608}
609
610impl_option!(
611    SvgVertex,
612    OptionSvgVertex,
613    [Debug, Copy, Clone, PartialOrd, PartialEq]
614);
615
616impl VertexLayoutDescription for SvgVertex {
617    fn get_description() -> VertexLayout {
618        VertexLayout {
619            fields: vec![VertexAttribute {
620                va_name: String::from("vAttrXY").into(),
621                layout_location: None.into(),
622                attribute_type: VertexAttributeType::Float,
623                item_count: 2,
624            }]
625            .into(),
626        }
627    }
628}
629
630#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
631#[repr(C)]
632pub struct SvgColoredVertex {
633    pub x: f32,
634    pub y: f32,
635    pub z: f32,
636    pub r: f32,
637    pub g: f32,
638    pub b: f32,
639    pub a: f32,
640}
641
642impl_option!(
643    SvgColoredVertex,
644    OptionSvgColoredVertex,
645    [Debug, Copy, Clone, PartialOrd, PartialEq]
646);
647
648impl VertexLayoutDescription for SvgColoredVertex {
649    fn get_description() -> VertexLayout {
650        VertexLayout {
651            fields: vec![
652                VertexAttribute {
653                    va_name: String::from("vAttrXY").into(),
654                    layout_location: None.into(),
655                    attribute_type: VertexAttributeType::Float,
656                    item_count: 3,
657                },
658                VertexAttribute {
659                    va_name: String::from("vColor").into(),
660                    layout_location: None.into(),
661                    attribute_type: VertexAttributeType::Float,
662                    item_count: 4,
663                },
664            ]
665            .into(),
666        }
667    }
668}
669
670#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
671#[repr(C)]
672pub struct SvgCircle {
673    pub center_x: f32,
674    pub center_y: f32,
675    pub radius: f32,
676}
677
678impl SvgCircle {
679    pub fn contains_point(&self, x: f32, y: f32) -> bool {
680        let x_diff = libm::fabsf(x - self.center_x);
681        let y_diff = libm::fabsf(y - self.center_y);
682        (x_diff * x_diff) + (y_diff * y_diff) < (self.radius * self.radius)
683    }
684    pub fn get_bounds(&self) -> SvgRect {
685        SvgRect {
686            width: self.radius * 2.0,
687            height: self.radius * 2.0,
688            x: self.center_x - self.radius,
689            y: self.center_y - self.radius,
690            radius_top_left: 0.0,
691            radius_top_right: 0.0,
692            radius_bottom_left: 0.0,
693            radius_bottom_right: 0.0,
694        }
695    }
696}
697
698#[derive(Debug, Clone, PartialEq, PartialOrd)]
699#[repr(C)]
700pub struct TessellatedSvgNode {
701    pub vertices: SvgVertexVec,
702    pub indices: U32Vec,
703}
704
705impl_option!(
706    TessellatedSvgNode,
707    OptionTessellatedSvgNode,
708    copy = false,
709    [Debug, Clone, PartialEq, PartialOrd]
710);
711
712impl Default for TessellatedSvgNode {
713    fn default() -> Self {
714        Self {
715            vertices: Vec::new().into(),
716            indices: Vec::new().into(),
717        }
718    }
719}
720
721impl_vec!(TessellatedSvgNode, TessellatedSvgNodeVec, TessellatedSvgNodeVecDestructor, TessellatedSvgNodeVecDestructorType, TessellatedSvgNodeVecSlice, OptionTessellatedSvgNode);
722impl_vec_debug!(TessellatedSvgNode, TessellatedSvgNodeVec);
723impl_vec_partialord!(TessellatedSvgNode, TessellatedSvgNodeVec);
724impl_vec_clone!(
725    TessellatedSvgNode,
726    TessellatedSvgNodeVec,
727    TessellatedSvgNodeVecDestructor
728);
729impl_vec_partialeq!(TessellatedSvgNode, TessellatedSvgNodeVec);
730
731impl TessellatedSvgNode {
732    pub fn empty() -> Self {
733        Self::default()
734    }
735}
736
737impl TessellatedSvgNodeVec {
738    pub fn get_ref(&self) -> TessellatedSvgNodeVecRef {
739        let slice = self.as_ref();
740        TessellatedSvgNodeVecRef {
741            ptr: slice.as_ptr(),
742            len: slice.len(),
743        }
744    }
745}
746
747impl fmt::Debug for TessellatedSvgNodeVecRef {
748    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
749        self.as_slice().fmt(f)
750    }
751}
752
753// C ABI wrapper over &[TessellatedSvgNode]
754#[repr(C)]
755pub struct TessellatedSvgNodeVecRef {
756    pub ptr: *const TessellatedSvgNode,
757    pub len: usize,
758}
759
760impl Clone for TessellatedSvgNodeVecRef {
761    fn clone(&self) -> Self {
762        Self {
763            ptr: self.ptr,
764            len: self.len,
765        }
766    }
767}
768
769impl TessellatedSvgNodeVecRef {
770    pub fn as_slice<'a>(&'a self) -> &'a [TessellatedSvgNode] {
771        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
772    }
773}
774
775#[derive(Debug, Clone, PartialEq, PartialOrd)]
776#[repr(C)]
777pub struct TessellatedColoredSvgNode {
778    pub vertices: SvgColoredVertexVec,
779    pub indices: U32Vec,
780}
781
782impl_option!(
783    TessellatedColoredSvgNode,
784    OptionTessellatedColoredSvgNode,
785    copy = false,
786    [Debug, Clone, PartialEq, PartialOrd]
787);
788
789impl Default for TessellatedColoredSvgNode {
790    fn default() -> Self {
791        Self {
792            vertices: Vec::new().into(),
793            indices: Vec::new().into(),
794        }
795    }
796}
797
798impl_vec!(TessellatedColoredSvgNode, TessellatedColoredSvgNodeVec, TessellatedColoredSvgNodeVecDestructor, TessellatedColoredSvgNodeVecDestructorType, TessellatedColoredSvgNodeVecSlice, OptionTessellatedColoredSvgNode);
799impl_vec_debug!(TessellatedColoredSvgNode, TessellatedColoredSvgNodeVec);
800impl_vec_partialord!(TessellatedColoredSvgNode, TessellatedColoredSvgNodeVec);
801impl_vec_clone!(
802    TessellatedColoredSvgNode,
803    TessellatedColoredSvgNodeVec,
804    TessellatedColoredSvgNodeVecDestructor
805);
806impl_vec_partialeq!(TessellatedColoredSvgNode, TessellatedColoredSvgNodeVec);
807
808impl TessellatedColoredSvgNode {
809    pub fn empty() -> Self {
810        Self::default()
811    }
812}
813
814impl TessellatedColoredSvgNodeVec {
815    pub fn get_ref(&self) -> TessellatedColoredSvgNodeVecRef {
816        let slice = self.as_ref();
817        TessellatedColoredSvgNodeVecRef {
818            ptr: slice.as_ptr(),
819            len: slice.len(),
820        }
821    }
822}
823
824impl fmt::Debug for TessellatedColoredSvgNodeVecRef {
825    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
826        self.as_slice().fmt(f)
827    }
828}
829
830// C ABI wrapper over &[TessellatedColoredSvgNode]
831#[repr(C)]
832pub struct TessellatedColoredSvgNodeVecRef {
833    pub ptr: *const TessellatedColoredSvgNode,
834    pub len: usize,
835}
836
837impl Clone for TessellatedColoredSvgNodeVecRef {
838    fn clone(&self) -> Self {
839        Self {
840            ptr: self.ptr,
841            len: self.len,
842        }
843    }
844}
845
846impl TessellatedColoredSvgNodeVecRef {
847    pub fn as_slice<'a>(&'a self) -> &'a [TessellatedColoredSvgNode] {
848        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
849    }
850}
851
852impl_vec!(SvgVertex, SvgVertexVec, SvgVertexVecDestructor, SvgVertexVecDestructorType, SvgVertexVecSlice, OptionSvgVertex);
853impl_vec_debug!(SvgVertex, SvgVertexVec);
854impl_vec_partialord!(SvgVertex, SvgVertexVec);
855impl_vec_clone!(SvgVertex, SvgVertexVec, SvgVertexVecDestructor);
856impl_vec_partialeq!(SvgVertex, SvgVertexVec);
857
858impl_vec!(SvgColoredVertex, SvgColoredVertexVec, SvgColoredVertexVecDestructor, SvgColoredVertexVecDestructorType, SvgColoredVertexVecSlice, OptionSvgColoredVertex);
859impl_vec_debug!(SvgColoredVertex, SvgColoredVertexVec);
860impl_vec_partialord!(SvgColoredVertex, SvgColoredVertexVec);
861impl_vec_clone!(
862    SvgColoredVertex,
863    SvgColoredVertexVec,
864    SvgColoredVertexVecDestructor
865);
866impl_vec_partialeq!(SvgColoredVertex, SvgColoredVertexVec);
867
868#[derive(Debug, Clone, PartialEq, PartialOrd)]
869#[repr(C)]
870pub struct TessellatedGPUSvgNode {
871    pub vertex_index_buffer: VertexBuffer,
872}
873
874impl TessellatedGPUSvgNode {
875    /// Uploads the tesselated SVG node to GPU memory
876    pub fn new(node: &TessellatedSvgNode, gl: GlContextPtr) -> Self {
877        let svg_shader_id = gl.ptr.svg_shader;
878        Self {
879            vertex_index_buffer: VertexBuffer::new(
880                gl,
881                svg_shader_id,
882                node.vertices.as_ref(),
883                node.indices.as_ref(),
884                IndexBufferFormat::Triangles,
885            ),
886        }
887    }
888
889    /// Draw the vertex buffer to the texture with the given color and transform
890    pub fn draw(
891        &self,
892        texture: &mut Texture,
893        target_size: PhysicalSizeU32,
894        color: ColorU,
895        transforms: StyleTransformVec,
896    ) -> bool {
897        let transform_origin = StyleTransformOrigin {
898            x: PixelValue::px(target_size.width as f32 / 2.0),
899            y: PixelValue::px(target_size.height as f32 / 2.0),
900        };
901
902        let computed_transform = ComputedTransform3D::from_style_transform_vec(
903            transforms.as_ref(),
904            &transform_origin,
905            target_size.width as f32,
906            target_size.height as f32,
907            RotationMode::ForWebRender,
908        );
909
910        // NOTE: OpenGL draws are column-major, while ComputedTransform3D
911        // is row-major! Need to transpose the matrix!
912        let column_major = computed_transform.get_column_major();
913
914        let color: ColorF = color.into();
915
916        // uniforms for the SVG shader
917        let uniforms = [
918            Uniform {
919                uniform_name: "vBboxSize".into(),
920                uniform_type: UniformType::FloatVec2([
921                    target_size.width as f32,
922                    target_size.height as f32,
923                ]),
924            },
925            Uniform {
926                uniform_name: "fDrawColor".into(),
927                uniform_type: UniformType::FloatVec4([color.r, color.g, color.b, color.a]),
928            },
929            Uniform {
930                uniform_name: "vTransformMatrix".into(),
931                uniform_type: UniformType::Matrix4 {
932                    transpose: false,
933                    matrix: unsafe { core::mem::transmute(column_major.m) },
934                },
935            },
936        ];
937
938        GlShader::draw(
939            texture.gl_context.ptr.svg_shader,
940            texture,
941            &[(&self.vertex_index_buffer, &uniforms[..])],
942        );
943
944        true
945    }
946}
947
948#[derive(Debug, Clone, PartialEq, PartialOrd)]
949#[repr(C)]
950pub struct TessellatedColoredGPUSvgNode {
951    pub vertex_index_buffer: VertexBuffer,
952}
953
954impl TessellatedColoredGPUSvgNode {
955    /// Uploads the tesselated SVG node to GPU memory
956    pub fn new(node: &TessellatedColoredSvgNode, gl: GlContextPtr) -> Self {
957        let svg_shader_id = gl.ptr.svg_multicolor_shader;
958        Self {
959            vertex_index_buffer: VertexBuffer::new(
960                gl,
961                svg_shader_id,
962                node.vertices.as_ref(),
963                node.indices.as_ref(),
964                IndexBufferFormat::Triangles,
965            ),
966        }
967    }
968
969    /// Draw the vertex buffer to the texture with the given color and transform
970    pub fn draw(
971        &self,
972        texture: &mut Texture,
973        target_size: PhysicalSizeU32,
974        transforms: StyleTransformVec,
975    ) -> bool {
976        use azul_css::props::basic::PixelValue;
977
978        use crate::gl::{GlShader, Uniform, UniformType};
979
980        let transform_origin = StyleTransformOrigin {
981            x: PixelValue::px(target_size.width as f32 / 2.0),
982            y: PixelValue::px(target_size.height as f32 / 2.0),
983        };
984
985        let computed_transform = ComputedTransform3D::from_style_transform_vec(
986            transforms.as_ref(),
987            &transform_origin,
988            target_size.width as f32,
989            target_size.height as f32,
990            RotationMode::ForWebRender,
991        );
992
993        // NOTE: OpenGL draws are column-major, while ComputedTransform3D
994        // is row-major! Need to transpose the matrix!
995        let column_major = computed_transform.get_column_major();
996
997        // uniforms for the SVG shader
998        let uniforms = [
999            Uniform {
1000                uniform_name: "vBboxSize".into(),
1001                uniform_type: UniformType::FloatVec2([
1002                    target_size.width as f32,
1003                    target_size.height as f32,
1004                ]),
1005            },
1006            Uniform {
1007                uniform_name: "vTransformMatrix".into(),
1008                uniform_type: UniformType::Matrix4 {
1009                    transpose: false,
1010                    matrix: unsafe { core::mem::transmute(column_major.m) },
1011                },
1012            },
1013        ];
1014
1015        GlShader::draw(
1016            texture.gl_context.ptr.svg_multicolor_shader,
1017            texture,
1018            &[(&self.vertex_index_buffer, &uniforms[..])],
1019        );
1020
1021        true
1022    }
1023}
1024
1025#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1026#[repr(C, u8)]
1027pub enum SvgStyle {
1028    Fill(SvgFillStyle),
1029    Stroke(SvgStrokeStyle),
1030}
1031
1032impl SvgStyle {
1033    pub fn get_antialias(&self) -> bool {
1034        match self {
1035            SvgStyle::Fill(f) => f.anti_alias,
1036            SvgStyle::Stroke(s) => s.anti_alias,
1037        }
1038    }
1039    pub fn get_high_quality_aa(&self) -> bool {
1040        match self {
1041            SvgStyle::Fill(f) => f.high_quality_aa,
1042            SvgStyle::Stroke(s) => s.high_quality_aa,
1043        }
1044    }
1045    pub fn get_transform(&self) -> SvgTransform {
1046        match self {
1047            SvgStyle::Fill(f) => f.transform,
1048            SvgStyle::Stroke(s) => s.transform,
1049        }
1050    }
1051}
1052#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1053#[repr(C)]
1054pub enum SvgFillRule {
1055    Winding,
1056    EvenOdd,
1057}
1058
1059impl Default for SvgFillRule {
1060    fn default() -> Self {
1061        SvgFillRule::Winding
1062    }
1063}
1064
1065#[derive(Default, Debug, Copy, Clone, PartialEq, PartialOrd)]
1066#[repr(C)]
1067pub struct SvgTransform {
1068    pub sx: f32,
1069    pub kx: f32,
1070    pub ky: f32,
1071    pub sy: f32,
1072    pub tx: f32,
1073    pub ty: f32,
1074}
1075
1076#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1077#[repr(C)]
1078pub struct SvgFillStyle {
1079    /// See the SVG specification.
1080    ///
1081    /// Default value: `LineJoin::Miter`.
1082    pub line_join: SvgLineJoin,
1083    /// See the SVG specification.
1084    ///
1085    /// Must be greater than or equal to 1.0.
1086    /// Default value: `StrokeOptions::DEFAULT_MITER_LIMIT`.
1087    pub miter_limit: f32,
1088    /// Maximum allowed distance to the path when building an approximation.
1089    ///
1090    /// See [Flattening and tolerance](index.html#flattening-and-tolerance).
1091    /// Default value: `StrokeOptions::DEFAULT_TOLERANCE`.
1092    pub tolerance: f32,
1093    /// Whether to use the "winding" or "even / odd" fill rule when tesselating the path
1094    pub fill_rule: SvgFillRule,
1095    /// Whether to apply a transform to the points in the path (warning: will be done on the CPU -
1096    /// expensive)
1097    pub transform: SvgTransform,
1098    /// Whether the fill is intended to be anti-aliased (default: true)
1099    pub anti_alias: bool,
1100    /// Whether the anti-aliasing has to be of high quality (default: false)
1101    pub high_quality_aa: bool,
1102}
1103
1104impl Default for SvgFillStyle {
1105    fn default() -> Self {
1106        Self {
1107            line_join: SvgLineJoin::Miter,
1108            miter_limit: DEFAULT_MITER_LIMIT,
1109            tolerance: DEFAULT_TOLERANCE,
1110            fill_rule: SvgFillRule::default(),
1111            transform: SvgTransform::default(),
1112            anti_alias: true,
1113            high_quality_aa: false,
1114        }
1115    }
1116}
1117
1118#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1119#[repr(C)]
1120pub struct SvgStrokeStyle {
1121    /// What cap to use at the start of each sub-path.
1122    ///
1123    /// Default value: `LineCap::Butt`.
1124    pub start_cap: SvgLineCap,
1125    /// What cap to use at the end of each sub-path.
1126    ///
1127    /// Default value: `LineCap::Butt`.
1128    pub end_cap: SvgLineCap,
1129    /// See the SVG specification.
1130    ///
1131    /// Default value: `LineJoin::Miter`.
1132    pub line_join: SvgLineJoin,
1133    /// Dash pattern
1134    pub dash_pattern: OptionSvgDashPattern,
1135    /// Line width
1136    ///
1137    /// Default value: `StrokeOptions::DEFAULT_LINE_WIDTH`.
1138    pub line_width: f32,
1139    /// See the SVG specification.
1140    ///
1141    /// Must be greater than or equal to 1.0.
1142    /// Default value: `StrokeOptions::DEFAULT_MITER_LIMIT`.
1143    pub miter_limit: f32,
1144    /// Maximum allowed distance to the path when building an approximation.
1145    ///
1146    /// See [Flattening and tolerance](index.html#flattening-and-tolerance).
1147    /// Default value: `StrokeOptions::DEFAULT_TOLERANCE`.
1148    pub tolerance: f32,
1149    /// Apply line width
1150    ///
1151    /// When set to false, the generated vertices will all be positioned in the centre
1152    /// of the line. The width can be applied later on (eg in a vertex shader) by adding
1153    /// the vertex normal multiplied by the line with to each vertex position.
1154    ///
1155    /// Default value: `true`. NOTE: currently unused!
1156    pub apply_line_width: bool,
1157    /// Whether to apply a transform to the points in the path (warning: will be done on the CPU -
1158    /// expensive)
1159    pub transform: SvgTransform,
1160    /// Whether the fill is intended to be anti-aliased (default: true)
1161    pub anti_alias: bool,
1162    /// Whether the anti-aliasing has to be of high quality (default: false)
1163    pub high_quality_aa: bool,
1164}
1165
1166impl Default for SvgStrokeStyle {
1167    fn default() -> Self {
1168        Self {
1169            start_cap: SvgLineCap::default(),
1170            end_cap: SvgLineCap::default(),
1171            line_join: SvgLineJoin::default(),
1172            dash_pattern: OptionSvgDashPattern::None,
1173            line_width: DEFAULT_LINE_WIDTH,
1174            miter_limit: DEFAULT_MITER_LIMIT,
1175            tolerance: DEFAULT_TOLERANCE,
1176            apply_line_width: true,
1177            anti_alias: true,
1178            high_quality_aa: false,
1179            transform: SvgTransform::default(),
1180        }
1181    }
1182}
1183
1184#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1185#[repr(C)]
1186pub struct SvgDashPattern {
1187    pub offset: f32,
1188    pub length_1: f32,
1189    pub gap_1: f32,
1190    pub length_2: f32,
1191    pub gap_2: f32,
1192    pub length_3: f32,
1193    pub gap_3: f32,
1194}
1195
1196impl_option!(
1197    SvgDashPattern,
1198    OptionSvgDashPattern,
1199    [Debug, Copy, Clone, PartialEq, PartialOrd]
1200);
1201
1202#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
1203#[repr(C)]
1204pub enum SvgLineCap {
1205    Butt,
1206    Square,
1207    Round,
1208}
1209
1210impl Default for SvgLineCap {
1211    fn default() -> Self {
1212        SvgLineCap::Butt
1213    }
1214}
1215
1216#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
1217#[repr(C)]
1218pub enum SvgLineJoin {
1219    Miter,
1220    MiterClip,
1221    Round,
1222    Bevel,
1223}
1224
1225impl Default for SvgLineJoin {
1226    fn default() -> Self {
1227        SvgLineJoin::Miter
1228    }
1229}
1230
1231#[allow(non_camel_case_types)]
1232pub enum c_void {}
1233
1234pub type GlyphId = u16;
1235
1236#[derive(Debug, Clone)]
1237#[repr(C)]
1238pub struct SvgXmlNode {
1239    pub node: *const c_void, // usvg::Node
1240    pub run_destructor: bool,
1241}
1242
1243#[derive(Debug, Clone)]
1244#[repr(C)]
1245pub struct Svg {
1246    pub tree: *const c_void, // *mut usvg::Tree,
1247    pub run_destructor: bool,
1248}
1249
1250#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1251#[repr(C)]
1252pub enum ShapeRendering {
1253    OptimizeSpeed,
1254    CrispEdges,
1255    GeometricPrecision,
1256}
1257
1258#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1259#[repr(C)]
1260pub enum ImageRendering {
1261    OptimizeQuality,
1262    OptimizeSpeed,
1263}
1264
1265#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1266#[repr(C)]
1267pub enum TextRendering {
1268    OptimizeSpeed,
1269    OptimizeLegibility,
1270    GeometricPrecision,
1271}
1272
1273#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
1274#[repr(C)]
1275pub enum FontDatabase {
1276    Empty,
1277    System,
1278}
1279
1280#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
1281#[repr(C)]
1282pub struct SvgRenderOptions {
1283    pub target_size: OptionLayoutSize,
1284    pub background_color: OptionColorU,
1285    pub fit: SvgFitTo,
1286    pub transform: SvgRenderTransform,
1287}
1288
1289#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
1290#[repr(C)]
1291pub struct SvgRenderTransform {
1292    pub sx: f32,
1293    pub kx: f32,
1294    pub ky: f32,
1295    pub sy: f32,
1296    pub tx: f32,
1297    pub ty: f32,
1298}
1299
1300#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
1301#[repr(C, u8)]
1302pub enum SvgFitTo {
1303    Original,
1304    Width(u32),
1305    Height(u32),
1306    Zoom(f32),
1307}
1308
1309impl Default for SvgFitTo {
1310    fn default() -> Self {
1311        SvgFitTo::Original
1312    }
1313}
1314
1315#[derive(Debug, Clone, PartialEq, PartialOrd)]
1316#[repr(C)]
1317pub struct SvgParseOptions {
1318    /// SVG image path. Used to resolve relative image paths.
1319    pub relative_image_path: OptionString,
1320    /// Target DPI. Impact units conversion. Default: 96.0
1321    pub dpi: f32,
1322    /// Default font family. Will be used when no font-family attribute is set in the SVG. Default:
1323    /// Times New Roman
1324    pub default_font_family: AzString,
1325    /// A default font size. Will be used when no font-size attribute is set in the SVG. Default:
1326    /// 12
1327    pub font_size: f32,
1328    /// A list of languages. Will be used to resolve a systemLanguage conditional attribute.
1329    /// Format: en, en-US. Default: [en]
1330    pub languages: StringVec,
1331    /// Specifies the default shape rendering method. Will be used when an SVG element's
1332    /// shape-rendering property is set to auto. Default: GeometricPrecision
1333    pub shape_rendering: ShapeRendering,
1334    /// Specifies the default text rendering method. Will be used when an SVG element's
1335    /// text-rendering property is set to auto. Default: OptimizeLegibility
1336    pub text_rendering: TextRendering,
1337    /// Specifies the default image rendering method. Will be used when an SVG element's
1338    /// image-rendering property is set to auto. Default: OptimizeQuality
1339    pub image_rendering: ImageRendering,
1340    /// Keep named groups. If set to true, all non-empty groups with id attribute will not be
1341    /// removed. Default: false
1342    pub keep_named_groups: bool,
1343    /// When empty, text elements will be skipped. Default: `System`
1344    pub fontdb: FontDatabase,
1345}
1346
1347impl Default for SvgParseOptions {
1348    fn default() -> Self {
1349        let lang_vec: Vec<AzString> = vec![String::from("en").into()];
1350        SvgParseOptions {
1351            relative_image_path: OptionString::None,
1352            dpi: 96.0,
1353            default_font_family: "Times New Roman".to_string().into(),
1354            font_size: 12.0,
1355            languages: lang_vec.into(),
1356            shape_rendering: ShapeRendering::GeometricPrecision,
1357            text_rendering: TextRendering::OptimizeLegibility,
1358            image_rendering: ImageRendering::OptimizeQuality,
1359            keep_named_groups: false,
1360            fontdb: FontDatabase::System,
1361        }
1362    }
1363}
1364
1365#[derive(Debug, Clone, PartialEq, PartialOrd)]
1366#[repr(C)]
1367pub struct SvgXmlOptions {
1368    pub use_single_quote: bool,
1369    pub indent: Indent,
1370    pub attributes_indent: Indent,
1371}
1372
1373impl Default for SvgXmlOptions {
1374    fn default() -> Self {
1375        SvgXmlOptions {
1376            use_single_quote: false,
1377            indent: Indent::Spaces(2),
1378            attributes_indent: Indent::Spaces(2),
1379        }
1380    }
1381}
1382
1383#[derive(Debug, PartialEq, PartialOrd, Clone)]
1384#[repr(C, u8)]
1385pub enum SvgParseError {
1386    NoParserAvailable,
1387    ElementsLimitReached,
1388    NotAnUtf8Str,
1389    MalformedGZip,
1390    InvalidSize,
1391    ParsingFailed(XmlError),
1392}
1393
1394impl fmt::Display for SvgParseError {
1395    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1396        use self::SvgParseError::*;
1397        match self {
1398            NoParserAvailable => write!(
1399                f,
1400                "Library was compiled without SVG support (no parser available)"
1401            ),
1402            InvalidFileSuffix => write!(f, "Error parsing SVG: Invalid file suffix"),
1403            FileOpenFailed => write!(f, "Error parsing SVG: Failed to open file"),
1404            NotAnUtf8Str => write!(f, "Error parsing SVG: Not an UTF-8 String"),
1405            MalformedGZip => write!(
1406                f,
1407                "Error parsing SVG: SVG is compressed with a malformed GZIP compression"
1408            ),
1409            InvalidSize => write!(f, "Error parsing SVG: Invalid size"),
1410            ParsingFailed(e) => write!(f, "Error parsing SVG: Parsing SVG as XML failed: {}", e),
1411        }
1412    }
1413}
1414
1415impl_result!(
1416    SvgXmlNode,
1417    SvgParseError,
1418    ResultSvgXmlNodeSvgParseError,
1419    copy = false,
1420    [Debug, Clone]
1421);
1422impl_result!(
1423    Svg,
1424    SvgParseError,
1425    ResultSvgSvgParseError,
1426    copy = false,
1427    [Debug, Clone]
1428);
1429
1430#[derive(Debug, Clone, PartialEq, PartialOrd)]
1431#[repr(C, u8)]
1432pub enum Indent {
1433    None,
1434    Spaces(u8),
1435    Tabs,
1436}