1
1
//! Tests for geometries of SVG primitives
2
//!
3
//! These use the *.svg and *.svg.geom files in the tests/fixtures/primitive_geometries directory.
4
//!
5
//! Each .svg.geom is a JSON file formatted like this:
6
//!
7
//! ```json
8
//! {
9
//!     "#element_id": {
10
//!         "ink_rect": {
11
//!             "x": 5.0,
12
//!             "y": 15.0,
13
//!             "width": 40.0,
14
//!             "height": 50.0
15
//!         },
16
//!         "logical_rect": {
17
//!             "x": 10.0,
18
//!             "y": 20.0,
19
//!             "width": 30.0,
20
//!             "height": 40.0
21
//!         }
22
//!     }
23
//! }
24
//! ```
25
//!
26
//! Any number of element_ids may appear in the file.  For each of those, the `test()` function will
27
//! call `CairoRenderer::get_layer_geometry()` and compare its result against the provided rectangles.
28

            
29
use anyhow::{Context, Result};
30

            
31
use rsvg::tests_only::Rect;
32
use rsvg::{CairoRenderer, LengthUnit, Loader};
33
use serde::Deserialize;
34
use std::collections::BTreeMap;
35
use std::fs;
36

            
37
// Copy of cairo::Rectangle
38
//
39
// Somehow I can't make serde's "remote" work here, in combination with the BTreeMap below...
40
112
#[derive(Copy, Clone, Deserialize, Debug, PartialEq)]
41
struct Rectangle {
42
    x: f64,
43
    y: f64,
44
    width: f64,
45
    height: f64,
46
}
47

            
48
impl From<Rectangle> for Rect {
49
8
    fn from(r: Rectangle) -> Rect {
50
8
        Rect {
51
8
            x0: r.x,
52
8
            y0: r.y,
53
8
            x1: r.x + r.width,
54
8
            y1: r.y + r.height,
55
        }
56
8
    }
57
}
58

            
59
29
#[derive(Copy, Clone, Deserialize, Debug, PartialEq)]
60
struct ElementGeometry {
61
    ink_rect: Option<Rectangle>,
62
    logical_rect: Option<Rectangle>,
63
}
64

            
65
8
#[derive(Deserialize)]
66
struct Geometries(BTreeMap<String, ElementGeometry>);
67

            
68
4
fn read_geometries(path: &str) -> Result<Geometries> {
69
4
    let contents = fs::read_to_string(path).context(format!("could not read {:?}", path))?;
70
4
    serde_json::from_str(&contents).context(format!("could not parse JSON from {:?}", path))
71
4
}
72

            
73
// We create a struct with the id and geometry so that
74
// assert_eq!() in the tests will print out the element name for failures.
75
//
76
// Here we use rsvg::Rect as that one has an approx_eq() method.
77
8
#[derive(Debug, PartialEq)]
78
struct Element {
79
4
    id: String,
80
4
    ink_rect: Option<Rect>,
81
4
    logical_rect: Option<Rect>,
82
}
83

            
84
impl Element {
85
4
    fn from_element_geometry(id: &str, geom: &ElementGeometry) -> Element {
86
4
        Element {
87
4
            id: String::from(id),
88
4
            ink_rect: geom.ink_rect.map(Into::into),
89
4
            logical_rect: geom.logical_rect.map(Into::into),
90
        }
91
4
    }
92

            
93
4
    fn from_rectangles(
94
        id: &str,
95
        ink_rect: cairo::Rectangle,
96
        logical_rect: cairo::Rectangle,
97
    ) -> Element {
98
4
        Element {
99
4
            id: String::from(id),
100
4
            ink_rect: Some(ink_rect.into()),
101
4
            logical_rect: Some(logical_rect.into()),
102
        }
103
4
    }
104
}
105

            
106
4
fn test(svg_filename: &str, geometries_filename: &str) {
107
4
    let geometries = read_geometries(geometries_filename).expect("reading geometries JSON");
108

            
109
4
    let handle = Loader::new()
110
        .read_path(svg_filename)
111
        .expect("reading geometries SVG");
112
12
    let renderer = CairoRenderer::new(&handle);
113
12
    let dimensions = renderer.intrinsic_dimensions();
114
4
    let (svg_width, svg_height) = renderer
115
        .intrinsic_size_in_pixels()
116
        .expect("intrinsic size in pixels");
117

            
118
4
    assert!(matches!(dimensions.width.unit, LengthUnit::Px));
119
4
    assert!(matches!(dimensions.height.unit, LengthUnit::Px));
120
4
    assert_eq!(dimensions.width.length, svg_width);
121
4
    assert_eq!(dimensions.height.length, svg_height);
122

            
123
4
    for (id, expected) in geometries.0.iter() {
124
4
        let expected = Element::from_element_geometry(id, expected);
125

            
126
4
        let viewport = cairo::Rectangle::new(0.0, 0.0, svg_width, svg_height);
127

            
128
4
        let (ink_rect, logical_rect) = renderer
129
4
            .geometry_for_layer(Some(id), &viewport)
130
4
            .unwrap_or_else(|_| panic!("getting geometry for {}", id));
131

            
132
4
        let computed = Element::from_rectangles(id, ink_rect, logical_rect);
133

            
134
4
        assert_eq!(expected, computed);
135
4
    }
136
4
}
137

            
138
#[test]
139
2
fn rect() {
140
1
    test(
141
        "tests/fixtures/primitive_geometries/rect.svg",
142
        "tests/fixtures/primitive_geometries/rect.svg.geom",
143
    );
144
2
}
145

            
146
#[test]
147
2
fn rect_stroke() {
148
1
    test(
149
        "tests/fixtures/primitive_geometries/rect_stroke.svg",
150
        "tests/fixtures/primitive_geometries/rect_stroke.svg.geom",
151
    );
152
2
}
153

            
154
#[test]
155
2
fn rect_stroke_unfilled() {
156
1
    test(
157
        "tests/fixtures/primitive_geometries/rect_stroke_unfilled.svg",
158
        "tests/fixtures/primitive_geometries/rect_stroke_unfilled.svg.geom",
159
    );
160
2
}
161

            
162
#[test]
163
2
fn rect_isolate() {
164
1
    test(
165
        "tests/fixtures/primitive_geometries/rect_isolate.svg",
166
        "tests/fixtures/primitive_geometries/rect_isolate.svg.geom",
167
    );
168
2
}