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

            
14
use alloc::{
15
    string::{String, ToString},
16
    vec::Vec,
17
};
18
use core::fmt;
19

            
20
use azul_css::{
21
    props::{
22
        basic::{
23
            ColorF, ColorU, OptionColorU, OptionLayoutSize, PixelValue, SvgCubicCurve, SvgPoint,
24
            SvgQuadraticCurve, SvgRect, SvgVector,
25
        },
26
        style::{StyleTransform, StyleTransformOrigin, StyleTransformVec},
27
    },
28
    AzString, OptionString, StringVec, U32Vec,
29
};
30

            
31
use 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)
42
const DEFAULT_MITER_LIMIT: f32 = 4.0;
43
/// Default stroke width in pixels
44
const DEFAULT_LINE_WIDTH: f32 = 1.0;
45
/// Default tessellation tolerance in pixels (smaller = more vertices, higher quality)
46
const 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)]
51
pub 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)]
61
pub struct SvgLine {
62
    /// Start point of the line
63
    pub start: SvgPoint,
64
    /// End point of the line
65
    pub end: SvgPoint,
66
}
67

            
68
impl SvgLine {
69
    /// Creates a new line segment from start to end point
70
    #[inline]
71
420
    pub const fn new(start: SvgPoint, end: SvgPoint) -> Self {
72
420
        Self { start, end }
73
420
    }
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
    /// Computes the outward-facing normal vector for this line (opposite of `inwards_normal`).
94
    pub fn outwards_normal(&self) -> Option<SvgPoint> {
95
        let inwards = self.inwards_normal()?;
96
        Some(SvgPoint {
97
            x: -inwards.x,
98
            y: -inwards.y,
99
        })
100
    }
101

            
102
    /// Reverses the direction of the line by swapping start and end points.
103
    pub fn reverse(&mut self) {
104
        let temp = self.start;
105
        self.start = self.end;
106
        self.end = temp;
107
    }
108
    /// Returns the start point of the line.
109
504
    pub fn get_start(&self) -> SvgPoint {
110
504
        self.start
111
504
    }
112
    /// Returns the end point of the line.
113
168
    pub fn get_end(&self) -> SvgPoint {
114
168
        self.end
115
168
    }
116

            
117
    /// Returns the parametric `t` value (0.0–1.0) at the given arc-length offset.
118
    pub fn get_t_at_offset(&self, offset: f64) -> f64 {
119
        offset / self.get_length()
120
    }
121

            
122
    /// Returns the tangent vector of the line.
123
    /// For a line, the tangent is constant (same direction everywhere),
124
    /// so no `t` parameter is needed.
125
    pub fn get_tangent_vector_at_t(&self) -> SvgVector {
126
        let dx = self.end.x - self.start.x;
127
        let dy = self.end.y - self.start.y;
128
        SvgVector {
129
            x: dx as f64,
130
            y: dy as f64,
131
        }
132
        .normalize()
133
    }
134

            
135
    /// Returns the X coordinate at parametric position `t` (0.0 = start, 1.0 = end).
136
    pub fn get_x_at_t(&self, t: f64) -> f64 {
137
        self.start.x as f64 + (self.end.x as f64 - self.start.x as f64) * t
138
    }
139

            
140
    /// Returns the Y coordinate at parametric position `t` (0.0 = start, 1.0 = end).
141
    pub fn get_y_at_t(&self, t: f64) -> f64 {
142
        self.start.y as f64 + (self.end.y as f64 - self.start.y as f64) * t
143
    }
144

            
145
    /// Returns the Euclidean length of the line segment.
146
    pub fn get_length(&self) -> f64 {
147
        let dx = self.end.x - self.start.x;
148
        let dy = self.end.y - self.start.y;
149
        libm::hypotf(dx, dy) as f64
150
    }
151

            
152
    /// Returns the axis-aligned bounding rectangle of this line segment.
153
    pub fn get_bounds(&self) -> SvgRect {
154
        let min_x = self.start.x.min(self.end.x);
155
        let max_x = self.start.x.max(self.end.x);
156

            
157
        let min_y = self.start.y.min(self.end.y);
158
        let max_y = self.start.y.max(self.end.y);
159

            
160
        let width = (max_x - min_x).abs();
161
        let height = (max_y - min_y).abs();
162

            
163
        SvgRect {
164
            width,
165
            height,
166
            x: min_x,
167
            y: min_y,
168
            radius_top_left: 0.0,
169
            radius_top_right: 0.0,
170
            radius_bottom_left: 0.0,
171
            radius_bottom_right: 0.0,
172
        }
173
    }
174
}
175

            
176
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
177
#[repr(C, u8)]
178
pub enum SvgPathElement {
179
    Line(SvgLine),
180
    QuadraticCurve(SvgQuadraticCurve),
181
    CubicCurve(SvgCubicCurve),
182
}
183

            
184
impl_option!(
185
    SvgPathElement,
186
    OptionSvgPathElement,
187
    [Debug, Copy, Clone, PartialEq, PartialOrd]
188
);
189

            
190
impl SvgPathElement {
191
    /// Creates a line path element from a SvgLine
192
    #[inline]
193
    pub const fn line(l: SvgLine) -> Self {
194
        SvgPathElement::Line(l)
195
    }
196

            
197
    /// Creates a quadratic curve path element from a SvgQuadraticCurve
198
    #[inline]
199
    pub const fn quadratic_curve(qc: SvgQuadraticCurve) -> Self {
200
        SvgPathElement::QuadraticCurve(qc)
201
    }
202

            
203
    /// Creates a cubic curve path element from a SvgCubicCurve
204
    #[inline]
205
    pub const fn cubic_curve(cc: SvgCubicCurve) -> Self {
206
        SvgPathElement::CubicCurve(cc)
207
    }
208

            
209
    /// Sets the end point of this path element.
210
    pub fn set_last(&mut self, point: SvgPoint) {
211
        match self {
212
            SvgPathElement::Line(l) => l.end = point,
213
            SvgPathElement::QuadraticCurve(qc) => qc.end = point,
214
            SvgPathElement::CubicCurve(cc) => cc.end = point,
215
        }
216
    }
217

            
218
    /// Sets the start point of this path element.
219
    pub fn set_first(&mut self, point: SvgPoint) {
220
        match self {
221
            SvgPathElement::Line(l) => l.start = point,
222
            SvgPathElement::QuadraticCurve(qc) => qc.start = point,
223
            SvgPathElement::CubicCurve(cc) => cc.start = point,
224
        }
225
    }
226

            
227
    /// Reverses the direction of this path element.
228
    pub fn reverse(&mut self) {
229
        match self {
230
            SvgPathElement::Line(l) => l.reverse(),
231
            SvgPathElement::QuadraticCurve(qc) => qc.reverse(),
232
            SvgPathElement::CubicCurve(cc) => cc.reverse(),
233
        }
234
    }
235
    /// Returns the start point of this path element.
236
546
    pub fn get_start(&self) -> SvgPoint {
237
546
        match self {
238
504
            SvgPathElement::Line(l) => l.get_start(),
239
            SvgPathElement::QuadraticCurve(qc) => qc.get_start(),
240
42
            SvgPathElement::CubicCurve(cc) => cc.get_start(),
241
        }
242
546
    }
243
    /// Returns the end point of this path element.
244
210
    pub fn get_end(&self) -> SvgPoint {
245
210
        match self {
246
168
            SvgPathElement::Line(l) => l.get_end(),
247
            SvgPathElement::QuadraticCurve(qc) => qc.get_end(),
248
42
            SvgPathElement::CubicCurve(cc) => cc.get_end(),
249
        }
250
210
    }
251
    /// Returns the axis-aligned bounding rectangle of this path element.
252
    pub fn get_bounds(&self) -> SvgRect {
253
        match self {
254
            SvgPathElement::Line(l) => l.get_bounds(),
255
            SvgPathElement::QuadraticCurve(qc) => qc.get_bounds(),
256
            SvgPathElement::CubicCurve(cc) => cc.get_bounds(),
257
        }
258
    }
259
    /// Returns the arc length of this path element.
260
    pub fn get_length(&self) -> f64 {
261
        match self {
262
            SvgPathElement::Line(l) => l.get_length(),
263
            SvgPathElement::QuadraticCurve(qc) => qc.get_length(),
264
            SvgPathElement::CubicCurve(cc) => cc.get_length(),
265
        }
266
    }
267
    /// Returns the parametric `t` value at the given arc-length offset.
268
    pub fn get_t_at_offset(&self, offset: f64) -> f64 {
269
        match self {
270
            SvgPathElement::Line(l) => l.get_t_at_offset(offset),
271
            SvgPathElement::QuadraticCurve(qc) => qc.get_t_at_offset(offset),
272
            SvgPathElement::CubicCurve(cc) => cc.get_t_at_offset(offset),
273
        }
274
    }
275
    /// Returns the normalized tangent vector at parametric position `t`.
276
    pub fn get_tangent_vector_at_t(&self, t: f64) -> SvgVector {
277
        match self {
278
            SvgPathElement::Line(l) => l.get_tangent_vector_at_t(),
279
            SvgPathElement::QuadraticCurve(qc) => qc.get_tangent_vector_at_t(t),
280
            SvgPathElement::CubicCurve(cc) => cc.get_tangent_vector_at_t(t),
281
        }
282
    }
283
    /// Returns the X coordinate at parametric position `t`.
284
    pub fn get_x_at_t(&self, t: f64) -> f64 {
285
        match self {
286
            SvgPathElement::Line(l) => l.get_x_at_t(t),
287
            SvgPathElement::QuadraticCurve(qc) => qc.get_x_at_t(t),
288
            SvgPathElement::CubicCurve(cc) => cc.get_x_at_t(t),
289
        }
290
    }
291
    /// Returns the Y coordinate at parametric position `t`.
292
    pub fn get_y_at_t(&self, t: f64) -> f64 {
293
        match self {
294
            SvgPathElement::Line(l) => l.get_y_at_t(t),
295
            SvgPathElement::QuadraticCurve(qc) => qc.get_y_at_t(t),
296
            SvgPathElement::CubicCurve(cc) => cc.get_y_at_t(t),
297
        }
298
    }
299
}
300

            
301
impl_vec!(SvgPathElement, SvgPathElementVec, SvgPathElementVecDestructor, SvgPathElementVecDestructorType, SvgPathElementVecSlice, OptionSvgPathElement);
302
impl_vec_debug!(SvgPathElement, SvgPathElementVec);
303
impl_vec_clone!(
304
    SvgPathElement,
305
    SvgPathElementVec,
306
    SvgPathElementVecDestructor
307
);
308
impl_vec_partialeq!(SvgPathElement, SvgPathElementVec);
309
impl_vec_partialord!(SvgPathElement, SvgPathElementVec);
310

            
311
#[derive(Debug, Clone, PartialEq, PartialOrd)]
312
#[repr(C)]
313
pub struct SvgPath {
314
    pub items: SvgPathElementVec,
315
}
316

            
317
impl_option!(
318
    SvgPath,
319
    OptionSvgPath,
320
    copy = false,
321
    [Debug, Clone, PartialEq, PartialOrd]
322
);
323

            
324
impl SvgPath {
325
    /// Creates a new SvgPath from a vector of path elements
326
    #[inline]
327
    pub const fn create(items: SvgPathElementVec) -> Self {
328
        Self { items }
329
    }
330

            
331
    /// Returns the start point of the first element, or `None` if the path is empty.
332
    pub fn get_start(&self) -> Option<SvgPoint> {
333
        self.items.as_ref().first().map(|s| s.get_start())
334
    }
335

            
336
    /// Returns the end point of the last element, or `None` if the path is empty.
337
    pub fn get_end(&self) -> Option<SvgPoint> {
338
        self.items.as_ref().last().map(|s| s.get_end())
339
    }
340

            
341
    /// Closes the path by appending a line from the last point to the first point, if needed.
342
    pub fn close(&mut self) {
343
        let first = match self.items.as_ref().first() {
344
            Some(s) => s,
345
            None => return,
346
        };
347
        let last = match self.items.as_ref().last() {
348
            Some(s) => s,
349
            None => return,
350
        };
351
        if first.get_start() != last.get_end() {
352
            let mut elements = self.items.as_slice().to_vec();
353
            elements.push(SvgPathElement::Line(SvgLine {
354
                start: last.get_end(),
355
                end: first.get_start(),
356
            }));
357
            self.items = elements.into();
358
        }
359
    }
360

            
361
    /// Returns `true` if the path's first start point equals its last end point.
362
168
    pub fn is_closed(&self) -> bool {
363
168
        let first = self.items.as_ref().first();
364
168
        let last = self.items.as_ref().last();
365
168
        match (first, last) {
366
168
            (Some(f), Some(l)) => (f.get_start() == l.get_end()),
367
            _ => false,
368
        }
369
168
    }
370

            
371
    /// Reverses the order and direction of all elements in the path.
372
    pub fn reverse(&mut self) {
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
        // reverse the order of items in the vec
379
        vec.reverse();
380

            
381
        // reverse the order inside the item itself
382
        // i.e. swap line.start and line.end
383
        for item in vec.iter_mut() {
384
            item.reverse();
385
        }
386

            
387
        // swap back
388
        let mut vec = SvgPathElementVec::from_vec(vec);
389
        core::mem::swap(&mut vec, &mut self.items);
390
    }
391

            
392
    /// Joins another path onto the end of this one, interpolating the join point.
393
    pub fn join_with(&mut self, mut path: Self) -> Option<()> {
394
        let self_last_point = self.items.as_ref().last()?.get_end();
395
        let other_start_point = path.items.as_ref().first()?.get_start();
396
        let interpolated_join_point = SvgPoint {
397
            x: (self_last_point.x + other_start_point.x) / 2.0,
398
            y: (self_last_point.y + other_start_point.y) / 2.0,
399
        };
400

            
401
        // swap self.items with a default vec
402
        let mut vec = SvgPathElementVec::from_const_slice(&[]);
403
        core::mem::swap(&mut vec, &mut self.items);
404
        let mut vec = vec.into_library_owned_vec();
405

            
406
        let mut other = SvgPathElementVec::from_const_slice(&[]);
407
        core::mem::swap(&mut other, &mut path.items);
408
        let mut other = other.into_library_owned_vec();
409

            
410
        let vec_len = vec.len() - 1;
411
        vec.get_mut(vec_len)?.set_last(interpolated_join_point);
412
        other.get_mut(0)?.set_first(interpolated_join_point);
413
        vec.append(&mut other);
414

            
415
        // swap back
416
        let mut vec = SvgPathElementVec::from_vec(vec);
417
        core::mem::swap(&mut vec, &mut self.items);
418

            
419
        Some(())
420
    }
421
    /// Returns the axis-aligned bounding rectangle of the entire path.
422
    pub fn get_bounds(&self) -> SvgRect {
423
        let mut first_bounds = match self.items.as_ref().get(0) {
424
            Some(s) => s.get_bounds(),
425
            None => return SvgRect::default(),
426
        };
427

            
428
        for mp in self.items.as_ref().iter().skip(1) {
429
            let mp_bounds = mp.get_bounds();
430
            first_bounds.union_with(&mp_bounds);
431
        }
432

            
433
        first_bounds
434
    }
435
}
436

            
437
#[derive(Debug, Clone, PartialEq, PartialOrd)]
438
#[repr(C)]
439
pub struct SvgMultiPolygon {
440
    /// NOTE: If a ring represents a hole, simply reverse the order of points
441
    pub rings: SvgPathVec,
442
}
443

            
444
impl_option!(
445
    SvgMultiPolygon,
446
    OptionSvgMultiPolygon,
447
    copy = false,
448
    [Debug, Clone, PartialEq, PartialOrd]
449
);
450

            
451
impl SvgMultiPolygon {
452
    /// Creates a new SvgMultiPolygon from a vector of paths (rings)
453
    /// NOTE: If a ring represents a hole, simply reverse the order of points
454
    #[inline]
455
1
    pub const fn create(rings: SvgPathVec) -> Self {
456
1
        Self { rings }
457
1
    }
458

            
459
    /// Returns the axis-aligned bounding rectangle of all rings in this multi-polygon.
460
    pub fn get_bounds(&self) -> SvgRect {
461
        let mut first_bounds = match self
462
            .rings
463
            .get(0)
464
            .and_then(|b| b.items.get(0).map(|i| i.get_bounds()))
465
        {
466
            Some(s) => s,
467
            // Empty polygon has zero-sized bounds at origin
468
            None => return SvgRect::default(),
469
        };
470

            
471
        for ring in self.rings.iter() {
472
            for item in ring.items.iter() {
473
                first_bounds.union_with(&item.get_bounds());
474
            }
475
        }
476

            
477
        first_bounds
478
    }
479
}
480

            
481
impl_vec!(SvgPath, SvgPathVec, SvgPathVecDestructor, SvgPathVecDestructorType, SvgPathVecSlice, OptionSvgPath);
482
impl_vec_debug!(SvgPath, SvgPathVec);
483
impl_vec_clone!(SvgPath, SvgPathVec, SvgPathVecDestructor);
484
impl_vec_partialeq!(SvgPath, SvgPathVec);
485
impl_vec_partialord!(SvgPath, SvgPathVec);
486

            
487
impl_vec!(SvgMultiPolygon, SvgMultiPolygonVec, SvgMultiPolygonVecDestructor, SvgMultiPolygonVecDestructorType, SvgMultiPolygonVecSlice, OptionSvgMultiPolygon);
488
impl_vec_debug!(SvgMultiPolygon, SvgMultiPolygonVec);
489
impl_vec_clone!(
490
    SvgMultiPolygon,
491
    SvgMultiPolygonVec,
492
    SvgMultiPolygonVecDestructor
493
);
494
impl_vec_partialeq!(SvgMultiPolygon, SvgMultiPolygonVec);
495
impl_vec_partialord!(SvgMultiPolygon, SvgMultiPolygonVec);
496

            
497
/// One `SvgNode` corresponds to one SVG `<path></path>` element
498
#[derive(Debug, Clone, PartialOrd, PartialEq)]
499
#[repr(C, u8)]
500
pub enum SvgNode {
501
    /// Multiple multipolygons, merged to one CPU buf for efficient drawing
502
    MultiPolygonCollection(SvgMultiPolygonVec),
503
    MultiPolygon(SvgMultiPolygon),
504
    MultiShape(SvgSimpleNodeVec),
505
    Path(SvgPath),
506
    Circle(SvgCircle),
507
    Rect(SvgRect),
508
}
509

            
510
/// One `SvgSimpleNode` is either a path, a rect or a circle
511
#[derive(Debug, Clone, PartialOrd, PartialEq)]
512
#[repr(C, u8)]
513
pub enum SvgSimpleNode {
514
    Path(SvgPath),
515
    Circle(SvgCircle),
516
    Rect(SvgRect),
517
    CircleHole(SvgCircle),
518
    RectHole(SvgRect),
519
}
520

            
521
impl_option!(
522
    SvgSimpleNode,
523
    OptionSvgSimpleNode,
524
    copy = false,
525
    [Debug, Clone, PartialOrd, PartialEq]
526
);
527

            
528
impl_vec!(SvgSimpleNode, SvgSimpleNodeVec, SvgSimpleNodeVecDestructor, SvgSimpleNodeVecDestructorType, SvgSimpleNodeVecSlice, OptionSvgSimpleNode);
529
impl_vec_debug!(SvgSimpleNode, SvgSimpleNodeVec);
530
impl_vec_clone!(SvgSimpleNode, SvgSimpleNodeVec, SvgSimpleNodeVecDestructor);
531
impl_vec_partialeq!(SvgSimpleNode, SvgSimpleNodeVec);
532
impl_vec_partialord!(SvgSimpleNode, SvgSimpleNodeVec);
533

            
534
impl SvgSimpleNode {
535
    /// Returns the axis-aligned bounding rectangle of this node.
536
    pub fn get_bounds(&self) -> SvgRect {
537
        match self {
538
            SvgSimpleNode::Path(a) => a.get_bounds(),
539
            SvgSimpleNode::Circle(a) => a.get_bounds(),
540
            SvgSimpleNode::Rect(a) => a.clone(),
541
            SvgSimpleNode::CircleHole(a) => a.get_bounds(),
542
            SvgSimpleNode::RectHole(a) => a.clone(),
543
        }
544
    }
545
    /// Returns `true` if this node represents a closed shape.
546
    pub fn is_closed(&self) -> bool {
547
        match self {
548
            SvgSimpleNode::Path(a) => a.is_closed(),
549
            SvgSimpleNode::Circle(_) => true,
550
            SvgSimpleNode::Rect(_) => true,
551
            SvgSimpleNode::CircleHole(_) => true,
552
            SvgSimpleNode::RectHole(_) => true,
553
        }
554
    }
555
}
556

            
557
impl SvgNode {
558
    /// Returns the axis-aligned bounding rectangle of this SVG node.
559
    pub fn get_bounds(&self) -> SvgRect {
560
        match self {
561
            SvgNode::MultiPolygonCollection(a) => {
562
                let mut first_mp_bounds = match a.get(0) {
563
                    Some(s) => s.get_bounds(),
564
                    None => return SvgRect::default(),
565
                };
566
                for mp in a.iter().skip(1) {
567
                    let mp_bounds = mp.get_bounds();
568
                    first_mp_bounds.union_with(&mp_bounds);
569
                }
570

            
571
                first_mp_bounds
572
            }
573
            SvgNode::MultiPolygon(a) => a.get_bounds(),
574
            SvgNode::MultiShape(a) => {
575
                let mut first_mp_bounds = match a.get(0) {
576
                    Some(s) => s.get_bounds(),
577
                    None => return SvgRect::default(),
578
                };
579
                for mp in a.iter().skip(1) {
580
                    let mp_bounds = mp.get_bounds();
581
                    first_mp_bounds.union_with(&mp_bounds);
582
                }
583

            
584
                first_mp_bounds
585
            }
586
            SvgNode::Path(a) => a.get_bounds(),
587
            SvgNode::Circle(a) => a.get_bounds(),
588
            SvgNode::Rect(a) => a.clone(),
589
        }
590
    }
591
    /// Returns `true` if all sub-paths in this node are closed.
592
    pub fn is_closed(&self) -> bool {
593
        match self {
594
            SvgNode::MultiPolygonCollection(a) => {
595
                for mp in a.iter() {
596
                    for p in mp.rings.as_ref().iter() {
597
                        if !p.is_closed() {
598
                            return false;
599
                        }
600
                    }
601
                }
602

            
603
                true
604
            }
605
            SvgNode::MultiPolygon(a) => {
606
                for p in a.rings.as_ref().iter() {
607
                    if !p.is_closed() {
608
                        return false;
609
                    }
610
                }
611

            
612
                true
613
            }
614
            SvgNode::MultiShape(a) => {
615
                for p in a.as_ref().iter() {
616
                    if !p.is_closed() {
617
                        return false;
618
                    }
619
                }
620

            
621
                true
622
            }
623
            SvgNode::Path(a) => a.is_closed(),
624
            SvgNode::Circle(_) => true,
625
            SvgNode::Rect(_) => true,
626
        }
627
    }
628
}
629

            
630
/// An SVG node paired with its visual style (fill or stroke).
631
#[derive(Debug, Clone, PartialOrd, PartialEq)]
632
#[repr(C)]
633
pub struct SvgStyledNode {
634
    pub geometry: SvgNode,
635
    pub style: SvgStyle,
636
}
637

            
638
/// A 2D vertex used in tessellated SVG geometry.
639
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
640
#[repr(C)]
641
pub struct SvgVertex {
642
    pub x: f32,
643
    pub y: f32,
644
}
645

            
646
impl_option!(
647
    SvgVertex,
648
    OptionSvgVertex,
649
    [Debug, Copy, Clone, PartialOrd, PartialEq]
650
);
651

            
652
impl VertexLayoutDescription for SvgVertex {
653
    fn get_description() -> VertexLayout {
654
        VertexLayout {
655
            fields: vec![VertexAttribute {
656
                va_name: String::from("vAttrXY").into(),
657
                layout_location: None.into(),
658
                attribute_type: VertexAttributeType::Float,
659
                item_count: 2,
660
            }]
661
            .into(),
662
        }
663
    }
664
}
665

            
666
/// A 3D vertex with per-vertex RGBA color, used in multi-colored SVG tessellation.
667
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
668
#[repr(C)]
669
pub struct SvgColoredVertex {
670
    pub x: f32,
671
    pub y: f32,
672
    pub z: f32,
673
    pub r: f32,
674
    pub g: f32,
675
    pub b: f32,
676
    pub a: f32,
677
}
678

            
679
impl_option!(
680
    SvgColoredVertex,
681
    OptionSvgColoredVertex,
682
    [Debug, Copy, Clone, PartialOrd, PartialEq]
683
);
684

            
685
impl VertexLayoutDescription for SvgColoredVertex {
686
    fn get_description() -> VertexLayout {
687
        VertexLayout {
688
            fields: vec![
689
                VertexAttribute {
690
                    va_name: String::from("vAttrXY").into(),
691
                    layout_location: None.into(),
692
                    attribute_type: VertexAttributeType::Float,
693
                    item_count: 3,
694
                },
695
                VertexAttribute {
696
                    va_name: String::from("vColor").into(),
697
                    layout_location: None.into(),
698
                    attribute_type: VertexAttributeType::Float,
699
                    item_count: 4,
700
                },
701
            ]
702
            .into(),
703
        }
704
    }
705
}
706

            
707
/// A circle defined by center coordinates and radius.
708
#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
709
#[repr(C)]
710
pub struct SvgCircle {
711
    pub center_x: f32,
712
    pub center_y: f32,
713
    pub radius: f32,
714
}
715

            
716
impl SvgCircle {
717
    /// Returns `true` if the given point lies inside the circle.
718
    pub fn contains_point(&self, x: f32, y: f32) -> bool {
719
        let x_diff = libm::fabsf(x - self.center_x);
720
        let y_diff = libm::fabsf(y - self.center_y);
721
        (x_diff * x_diff) + (y_diff * y_diff) < (self.radius * self.radius)
722
    }
723
    /// Returns the axis-aligned bounding rectangle of this circle.
724
    pub fn get_bounds(&self) -> SvgRect {
725
        SvgRect {
726
            width: self.radius * 2.0,
727
            height: self.radius * 2.0,
728
            x: self.center_x - self.radius,
729
            y: self.center_y - self.radius,
730
            radius_top_left: 0.0,
731
            radius_top_right: 0.0,
732
            radius_bottom_left: 0.0,
733
            radius_bottom_right: 0.0,
734
        }
735
    }
736
}
737

            
738
#[derive(Debug, Clone, PartialEq, PartialOrd)]
739
#[repr(C)]
740
pub struct TessellatedSvgNode {
741
    pub vertices: SvgVertexVec,
742
    pub indices: U32Vec,
743
}
744

            
745
impl_option!(
746
    TessellatedSvgNode,
747
    OptionTessellatedSvgNode,
748
    copy = false,
749
    [Debug, Clone, PartialEq, PartialOrd]
750
);
751

            
752
impl Default for TessellatedSvgNode {
753
    fn default() -> Self {
754
        Self {
755
            vertices: Vec::new().into(),
756
            indices: Vec::new().into(),
757
        }
758
    }
759
}
760

            
761
impl_vec!(TessellatedSvgNode, TessellatedSvgNodeVec, TessellatedSvgNodeVecDestructor, TessellatedSvgNodeVecDestructorType, TessellatedSvgNodeVecSlice, OptionTessellatedSvgNode);
762
impl_vec_debug!(TessellatedSvgNode, TessellatedSvgNodeVec);
763
impl_vec_partialord!(TessellatedSvgNode, TessellatedSvgNodeVec);
764
impl_vec_clone!(
765
    TessellatedSvgNode,
766
    TessellatedSvgNodeVec,
767
    TessellatedSvgNodeVecDestructor
768
);
769
impl_vec_partialeq!(TessellatedSvgNode, TessellatedSvgNodeVec);
770

            
771
impl TessellatedSvgNode {
772
    pub fn empty() -> Self {
773
        Self::default()
774
    }
775
}
776

            
777
impl TessellatedSvgNodeVec {
778
    pub fn get_ref(&self) -> TessellatedSvgNodeVecRef {
779
        let slice = self.as_ref();
780
        TessellatedSvgNodeVecRef {
781
            ptr: slice.as_ptr(),
782
            len: slice.len(),
783
        }
784
    }
785
}
786

            
787
impl fmt::Debug for TessellatedSvgNodeVecRef {
788
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
789
        self.as_slice().fmt(f)
790
    }
791
}
792

            
793
// C ABI wrapper over &[TessellatedSvgNode]
794
#[repr(C)]
795
pub struct TessellatedSvgNodeVecRef {
796
    pub ptr: *const TessellatedSvgNode,
797
    pub len: usize,
798
}
799

            
800
impl Clone for TessellatedSvgNodeVecRef {
801
    fn clone(&self) -> Self {
802
        Self {
803
            ptr: self.ptr,
804
            len: self.len,
805
        }
806
    }
807
}
808

            
809
impl TessellatedSvgNodeVecRef {
810
    pub fn as_slice<'a>(&'a self) -> &'a [TessellatedSvgNode] {
811
        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
812
    }
813
}
814

            
815
#[derive(Debug, Clone, PartialEq, PartialOrd)]
816
#[repr(C)]
817
pub struct TessellatedColoredSvgNode {
818
    pub vertices: SvgColoredVertexVec,
819
    pub indices: U32Vec,
820
}
821

            
822
impl_option!(
823
    TessellatedColoredSvgNode,
824
    OptionTessellatedColoredSvgNode,
825
    copy = false,
826
    [Debug, Clone, PartialEq, PartialOrd]
827
);
828

            
829
impl Default for TessellatedColoredSvgNode {
830
    fn default() -> Self {
831
        Self {
832
            vertices: Vec::new().into(),
833
            indices: Vec::new().into(),
834
        }
835
    }
836
}
837

            
838
impl_vec!(TessellatedColoredSvgNode, TessellatedColoredSvgNodeVec, TessellatedColoredSvgNodeVecDestructor, TessellatedColoredSvgNodeVecDestructorType, TessellatedColoredSvgNodeVecSlice, OptionTessellatedColoredSvgNode);
839
impl_vec_debug!(TessellatedColoredSvgNode, TessellatedColoredSvgNodeVec);
840
impl_vec_partialord!(TessellatedColoredSvgNode, TessellatedColoredSvgNodeVec);
841
impl_vec_clone!(
842
    TessellatedColoredSvgNode,
843
    TessellatedColoredSvgNodeVec,
844
    TessellatedColoredSvgNodeVecDestructor
845
);
846
impl_vec_partialeq!(TessellatedColoredSvgNode, TessellatedColoredSvgNodeVec);
847

            
848
impl TessellatedColoredSvgNode {
849
    pub fn empty() -> Self {
850
        Self::default()
851
    }
852
}
853

            
854
impl TessellatedColoredSvgNodeVec {
855
    pub fn get_ref(&self) -> TessellatedColoredSvgNodeVecRef {
856
        let slice = self.as_ref();
857
        TessellatedColoredSvgNodeVecRef {
858
            ptr: slice.as_ptr(),
859
            len: slice.len(),
860
        }
861
    }
862
}
863

            
864
impl fmt::Debug for TessellatedColoredSvgNodeVecRef {
865
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
866
        self.as_slice().fmt(f)
867
    }
868
}
869

            
870
// C ABI wrapper over &[TessellatedColoredSvgNode]
871
#[repr(C)]
872
pub struct TessellatedColoredSvgNodeVecRef {
873
    pub ptr: *const TessellatedColoredSvgNode,
874
    pub len: usize,
875
}
876

            
877
impl Clone for TessellatedColoredSvgNodeVecRef {
878
    fn clone(&self) -> Self {
879
        Self {
880
            ptr: self.ptr,
881
            len: self.len,
882
        }
883
    }
884
}
885

            
886
impl TessellatedColoredSvgNodeVecRef {
887
    pub fn as_slice<'a>(&'a self) -> &'a [TessellatedColoredSvgNode] {
888
        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
889
    }
890
}
891

            
892
impl_vec!(SvgVertex, SvgVertexVec, SvgVertexVecDestructor, SvgVertexVecDestructorType, SvgVertexVecSlice, OptionSvgVertex);
893
impl_vec_debug!(SvgVertex, SvgVertexVec);
894
impl_vec_partialord!(SvgVertex, SvgVertexVec);
895
impl_vec_clone!(SvgVertex, SvgVertexVec, SvgVertexVecDestructor);
896
impl_vec_partialeq!(SvgVertex, SvgVertexVec);
897

            
898
impl_vec!(SvgColoredVertex, SvgColoredVertexVec, SvgColoredVertexVecDestructor, SvgColoredVertexVecDestructorType, SvgColoredVertexVecSlice, OptionSvgColoredVertex);
899
impl_vec_debug!(SvgColoredVertex, SvgColoredVertexVec);
900
impl_vec_partialord!(SvgColoredVertex, SvgColoredVertexVec);
901
impl_vec_clone!(
902
    SvgColoredVertex,
903
    SvgColoredVertexVec,
904
    SvgColoredVertexVecDestructor
905
);
906
impl_vec_partialeq!(SvgColoredVertex, SvgColoredVertexVec);
907

            
908
/// Computes the bbox size and transform matrix uniforms shared by SVG draw methods.
909
///
910
/// Converts `StyleTransform` list into column-major `[f32; 16]` for OpenGL,
911
/// and packages it along with the bbox size uniform.
912
fn compute_svg_transform_uniforms(
913
    target_size: PhysicalSizeU32,
914
    transforms: &[StyleTransform],
915
) -> (Uniform, Uniform) {
916
    let transform_origin = StyleTransformOrigin {
917
        x: PixelValue::px(target_size.width as f32 / 2.0),
918
        y: PixelValue::px(target_size.height as f32 / 2.0),
919
    };
920

            
921
    let computed_transform = ComputedTransform3D::from_style_transform_vec(
922
        transforms,
923
        &transform_origin,
924
        target_size.width as f32,
925
        target_size.height as f32,
926
        RotationMode::ForWebRender,
927
    );
928

            
929
    // NOTE: OpenGL draws are column-major, while ComputedTransform3D
930
    // is row-major! Need to transpose the matrix!
931
    let m = computed_transform.get_column_major().m;
932
    let matrix: [f32; 16] = core::array::from_fn(|i| m[i / 4][i % 4]);
933

            
934
    let bbox_uniform = Uniform {
935
        uniform_name: "vBboxSize".into(),
936
        uniform_type: UniformType::FloatVec2([
937
            target_size.width as f32,
938
            target_size.height as f32,
939
        ]),
940
    };
941

            
942
    let transform_uniform = Uniform {
943
        uniform_name: "vTransformMatrix".into(),
944
        uniform_type: UniformType::Matrix4 {
945
            transpose: false,
946
            matrix,
947
        },
948
    };
949

            
950
    (bbox_uniform, transform_uniform)
951
}
952

            
953
#[derive(Debug, Clone, PartialEq, PartialOrd)]
954
#[repr(C)]
955
pub struct TessellatedGPUSvgNode {
956
    pub vertex_index_buffer: VertexBuffer,
957
}
958

            
959
impl TessellatedGPUSvgNode {
960
    /// Uploads the tesselated SVG node to GPU memory
961
    pub fn new(node: &TessellatedSvgNode, gl: GlContextPtr) -> Self {
962
        let svg_shader_id = gl.ptr.svg_shader;
963
        Self {
964
            vertex_index_buffer: VertexBuffer::new(
965
                gl,
966
                svg_shader_id,
967
                node.vertices.as_ref(),
968
                node.indices.as_ref(),
969
                IndexBufferFormat::Triangles,
970
            ),
971
        }
972
    }
973

            
974
    /// Draw the vertex buffer to the texture with the given color and transform
975
    pub fn draw(
976
        &self,
977
        texture: &mut Texture,
978
        target_size: PhysicalSizeU32,
979
        color: ColorU,
980
        transforms: StyleTransformVec,
981
    ) -> bool {
982
        let (bbox_uniform, transform_uniform) =
983
            compute_svg_transform_uniforms(target_size, transforms.as_ref());
984

            
985
        let color: ColorF = color.into();
986

            
987
        let uniforms = [
988
            bbox_uniform,
989
            Uniform {
990
                uniform_name: "fDrawColor".into(),
991
                uniform_type: UniformType::FloatVec4([color.r, color.g, color.b, color.a]),
992
            },
993
            transform_uniform,
994
        ];
995

            
996
        GlShader::draw(
997
            texture.gl_context.ptr.svg_shader,
998
            texture,
999
            &[(&self.vertex_index_buffer, &uniforms[..])],
        );
        true
    }
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct TessellatedColoredGPUSvgNode {
    pub vertex_index_buffer: VertexBuffer,
}
impl TessellatedColoredGPUSvgNode {
    /// Uploads the tesselated SVG node to GPU memory
    pub fn new(node: &TessellatedColoredSvgNode, gl: GlContextPtr) -> Self {
        let svg_shader_id = gl.ptr.svg_multicolor_shader;
        Self {
            vertex_index_buffer: VertexBuffer::new(
                gl,
                svg_shader_id,
                node.vertices.as_ref(),
                node.indices.as_ref(),
                IndexBufferFormat::Triangles,
            ),
        }
    }
    /// Draw the vertex buffer to the texture with the given color and transform
    pub fn draw(
        &self,
        texture: &mut Texture,
        target_size: PhysicalSizeU32,
        transforms: StyleTransformVec,
    ) -> bool {
        let (bbox_uniform, transform_uniform) =
            compute_svg_transform_uniforms(target_size, transforms.as_ref());
        let uniforms = [bbox_uniform, transform_uniform];
        GlShader::draw(
            texture.gl_context.ptr.svg_multicolor_shader,
            texture,
            &[(&self.vertex_index_buffer, &uniforms[..])],
        );
        true
    }
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
#[repr(C, u8)]
pub enum SvgStyle {
    Fill(SvgFillStyle),
    Stroke(SvgStrokeStyle),
}
impl SvgStyle {
    pub fn get_antialias(&self) -> bool {
        match self {
            SvgStyle::Fill(f) => f.anti_alias,
            SvgStyle::Stroke(s) => s.anti_alias,
        }
    }
    pub fn get_high_quality_aa(&self) -> bool {
        match self {
            SvgStyle::Fill(f) => f.high_quality_aa,
            SvgStyle::Stroke(s) => s.high_quality_aa,
        }
    }
    pub fn get_transform(&self) -> SvgTransform {
        match self {
            SvgStyle::Fill(f) => f.transform,
            SvgStyle::Stroke(s) => s.transform,
        }
    }
}
/// SVG fill rule for determining the interior of a shape.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub enum SvgFillRule {
    Winding,
    EvenOdd,
}
impl Default for SvgFillRule {
12
    fn default() -> Self {
12
        SvgFillRule::Winding
12
    }
}
#[derive(Default, Debug, Copy, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct SvgTransform {
    pub sx: f32,
    pub kx: f32,
    pub ky: f32,
    pub sy: f32,
    pub tx: f32,
    pub ty: f32,
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct SvgFillStyle {
    /// See the SVG specification.
    ///
    /// Default value: `LineJoin::Miter`.
    pub line_join: SvgLineJoin,
    /// See the SVG specification.
    ///
    /// Must be greater than or equal to 1.0.
    /// Default value: `StrokeOptions::DEFAULT_MITER_LIMIT`.
    pub miter_limit: f32,
    /// Maximum allowed distance to the path when building an approximation.
    ///
    /// See [Flattening and tolerance](index.html#flattening-and-tolerance).
    /// Default value: `StrokeOptions::DEFAULT_TOLERANCE`.
    pub tolerance: f32,
    /// Whether to use the "winding" or "even / odd" fill rule when tesselating the path
    pub fill_rule: SvgFillRule,
    /// Whether to apply a transform to the points in the path (warning: will be done on the CPU -
    /// expensive)
    pub transform: SvgTransform,
    /// Whether the fill is intended to be anti-aliased (default: true)
    pub anti_alias: bool,
    /// Whether the anti-aliasing has to be of high quality (default: false)
    pub high_quality_aa: bool,
}
impl Default for SvgFillStyle {
12
    fn default() -> Self {
12
        Self {
12
            line_join: SvgLineJoin::Miter,
12
            miter_limit: DEFAULT_MITER_LIMIT,
12
            tolerance: DEFAULT_TOLERANCE,
12
            fill_rule: SvgFillRule::default(),
12
            transform: SvgTransform::default(),
12
            anti_alias: true,
12
            high_quality_aa: false,
12
        }
12
    }
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct SvgStrokeStyle {
    /// Dash pattern
    pub dash_pattern: OptionSvgDashPattern,
    /// Whether to apply a transform to the points in the path (warning: will be done on the CPU -
    /// expensive)
    pub transform: SvgTransform,
    /// What cap to use at the start of each sub-path.
    ///
    /// Default value: `LineCap::Butt`.
    pub start_cap: SvgLineCap,
    /// What cap to use at the end of each sub-path.
    ///
    /// Default value: `LineCap::Butt`.
    pub end_cap: SvgLineCap,
    /// See the SVG specification.
    ///
    /// Default value: `LineJoin::Miter`.
    pub line_join: SvgLineJoin,
    /// Line width
    ///
    /// Default value: `StrokeOptions::DEFAULT_LINE_WIDTH`.
    pub line_width: f32,
    /// See the SVG specification.
    ///
    /// Must be greater than or equal to 1.0.
    /// Default value: `StrokeOptions::DEFAULT_MITER_LIMIT`.
    pub miter_limit: f32,
    /// Maximum allowed distance to the path when building an approximation.
    ///
    /// See [Flattening and tolerance](index.html#flattening-and-tolerance).
    /// Default value: `StrokeOptions::DEFAULT_TOLERANCE`.
    pub tolerance: f32,
    /// Apply line width
    ///
    /// When set to false, the generated vertices will all be positioned in the centre
    /// of the line. The width can be applied later on (eg in a vertex shader) by adding
    /// the vertex normal multiplied by the line with to each vertex position.
    ///
    /// Default value: `true`. NOTE: currently unused!
    pub apply_line_width: bool,
    /// Whether the fill is intended to be anti-aliased (default: true)
    pub anti_alias: bool,
    /// Whether the anti-aliasing has to be of high quality (default: false)
    pub high_quality_aa: bool,
}
impl Default for SvgStrokeStyle {
2
    fn default() -> Self {
2
        Self {
2
            dash_pattern: OptionSvgDashPattern::None,
2
            transform: SvgTransform::default(),
2
            start_cap: SvgLineCap::default(),
2
            end_cap: SvgLineCap::default(),
2
            line_join: SvgLineJoin::default(),
2
            line_width: DEFAULT_LINE_WIDTH,
2
            miter_limit: DEFAULT_MITER_LIMIT,
2
            tolerance: DEFAULT_TOLERANCE,
2
            apply_line_width: true,
2
            anti_alias: true,
2
            high_quality_aa: false,
2
        }
2
    }
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct SvgDashPattern {
    pub offset: f32,
    pub length_1: f32,
    pub gap_1: f32,
    pub length_2: f32,
    pub gap_2: f32,
    pub length_3: f32,
    pub gap_3: f32,
}
impl_option!(
    SvgDashPattern,
    OptionSvgDashPattern,
    [Debug, Copy, Clone, PartialEq, PartialOrd]
);
/// The shape used at the end of open sub-paths when they are stroked.
#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[repr(C)]
pub enum SvgLineCap {
    Butt,
    Square,
    Round,
}
impl Default for SvgLineCap {
4
    fn default() -> Self {
4
        SvgLineCap::Butt
4
    }
}
/// The shape used at the corners of stroked paths.
#[derive(Debug, Copy, Clone, PartialEq, Hash, Eq, PartialOrd, Ord)]
#[repr(C)]
pub enum SvgLineJoin {
    Miter,
    MiterClip,
    Round,
    Bevel,
}
impl Default for SvgLineJoin {
2
    fn default() -> Self {
2
        SvgLineJoin::Miter
2
    }
}
pub use core::ffi::c_void;
#[derive(Debug, Clone)]
#[repr(C)]
pub struct SvgXmlNode {
    pub node: *const c_void, // usvg::Node
    pub run_destructor: bool,
}
#[derive(Debug, Clone)]
#[repr(C)]
pub struct Svg {
    pub tree: *const c_void, // *mut usvg::Tree,
    pub run_destructor: bool,
}
/// SVG `shape-rendering` property controlling quality vs speed tradeoffs.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[repr(C)]
pub enum ShapeRendering {
    OptimizeSpeed,
    CrispEdges,
    GeometricPrecision,
}
/// SVG `image-rendering` property controlling image quality vs speed.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[repr(C)]
pub enum ImageRendering {
    OptimizeQuality,
    OptimizeSpeed,
}
/// SVG `text-rendering` property controlling text quality vs speed.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[repr(C)]
pub enum TextRendering {
    OptimizeSpeed,
    OptimizeLegibility,
    GeometricPrecision,
}
/// Font database source for SVG text rendering.
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[repr(C)]
pub enum FontDatabase {
    Empty,
    System,
}
#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct SvgRenderOptions {
    pub target_size: OptionLayoutSize,
    pub background_color: OptionColorU,
    pub fit: SvgFitTo,
    pub transform: SvgRenderTransform,
}
#[derive(Debug, Default, Copy, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct SvgRenderTransform {
    pub sx: f32,
    pub kx: f32,
    pub ky: f32,
    pub sy: f32,
    pub tx: f32,
    pub ty: f32,
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
#[repr(C, u8)]
pub enum SvgFitTo {
    Original,
    Width(u32),
    Height(u32),
    Zoom(f32),
}
impl Default for SvgFitTo {
    fn default() -> Self {
        SvgFitTo::Original
    }
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct SvgParseOptions {
    /// SVG image path. Used to resolve relative image paths.
    pub relative_image_path: OptionString,
    /// Default font family. Will be used when no font-family attribute is set in the SVG. Default:
    /// Times New Roman
    pub default_font_family: AzString,
    /// A list of languages. Will be used to resolve a systemLanguage conditional attribute.
    /// Format: en, en-US. Default: [en]
    pub languages: StringVec,
    /// Target DPI. Impact units conversion. Default: 96.0
    pub dpi: f32,
    /// A default font size. Will be used when no font-size attribute is set in the SVG. Default:
    /// 12
    pub font_size: f32,
    /// Specifies the default shape rendering method. Will be used when an SVG element's
    /// shape-rendering property is set to auto. Default: GeometricPrecision
    pub shape_rendering: ShapeRendering,
    /// Specifies the default text rendering method. Will be used when an SVG element's
    /// text-rendering property is set to auto. Default: OptimizeLegibility
    pub text_rendering: TextRendering,
    /// Specifies the default image rendering method. Will be used when an SVG element's
    /// image-rendering property is set to auto. Default: OptimizeQuality
    pub image_rendering: ImageRendering,
    /// When empty, text elements will be skipped. Default: `System`
    pub fontdb: FontDatabase,
    /// Keep named groups. If set to true, all non-empty groups with id attribute will not be
    /// removed. Default: false
    pub keep_named_groups: bool,
}
impl Default for SvgParseOptions {
    fn default() -> Self {
        let lang_vec: Vec<AzString> = vec![String::from("en").into()];
        SvgParseOptions {
            relative_image_path: OptionString::None,
            default_font_family: "Times New Roman".to_string().into(),
            languages: lang_vec.into(),
            dpi: 96.0,
            font_size: 12.0,
            shape_rendering: ShapeRendering::GeometricPrecision,
            text_rendering: TextRendering::OptimizeLegibility,
            image_rendering: ImageRendering::OptimizeQuality,
            fontdb: FontDatabase::System,
            keep_named_groups: false,
        }
    }
}
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C)]
pub struct SvgXmlOptions {
    pub use_single_quote: bool,
    pub indent: Indent,
    pub attributes_indent: Indent,
}
impl Default for SvgXmlOptions {
    fn default() -> Self {
        SvgXmlOptions {
            use_single_quote: false,
            indent: Indent::Spaces(2),
            attributes_indent: Indent::Spaces(2),
        }
    }
}
#[derive(Debug, PartialEq, PartialOrd, Clone)]
#[repr(C, u8)]
pub enum SvgParseError {
    NoParserAvailable,
    ElementsLimitReached,
    NotAnUtf8Str,
    MalformedGZip,
    InvalidSize,
    ParsingFailed(XmlError),
}
impl fmt::Display for SvgParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::SvgParseError::*;
        match self {
            NoParserAvailable => write!(
                f,
                "Library was compiled without SVG support (no parser available)"
            ),
            ElementsLimitReached => write!(f, "Error parsing SVG: Elements limit reached"),
            NotAnUtf8Str => write!(f, "Error parsing SVG: Not an UTF-8 String"),
            MalformedGZip => write!(
                f,
                "Error parsing SVG: SVG is compressed with a malformed GZIP compression"
            ),
            InvalidSize => write!(f, "Error parsing SVG: Invalid size"),
            ParsingFailed(e) => write!(f, "Error parsing SVG: Parsing SVG as XML failed: {}", e),
        }
    }
}
impl_result!(
    SvgXmlNode,
    SvgParseError,
    ResultSvgXmlNodeSvgParseError,
    copy = false,
    [Debug, Clone]
);
impl_result!(
    Svg,
    SvgParseError,
    ResultSvgSvgParseError,
    copy = false,
    [Debug, Clone]
);
/// Indentation style for SVG XML serialization.
#[derive(Debug, Clone, PartialEq, PartialOrd)]
#[repr(C, u8)]
pub enum Indent {
    None,
    Spaces(u8),
    Tabs,
}