1
1
use matches::matches;
2
use rsvg::{CairoRenderer, Loader, LoadingError, SvgHandle};
3

            
4
use rsvg::test_utils::reference_utils::{Compare, Evaluate, Reference};
5
use rsvg::test_utils::{load_svg, render_document, setup_font_map, setup_language, SurfaceSize};
6

            
7
// https://gitlab.gnome.org/GNOME/librsvg/issues/335
8
#[test]
9
2
fn non_svg_root() {
10
1
    assert!(matches!(load_svg(b"<x></x>"), Err(LoadingError::NoSvgRoot)));
11
2
}
12

            
13
// https://gitlab.gnome.org/GNOME/librsvg/issues/496
14
#[test]
15
2
fn inf_width() {
16
1
    let svg = load_svg(
17
        br#"<?xml version="1.0" encoding="UTF-8"?>
18
<svg s="Pg" width="1001111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111" heiNht=" 00">
19
 [l<g mask="url(sHaf:ax-fwiw0\inside\ax-ide\ax-flow#o0" styli="fility:!.5;">>
20
  </g>
21
</svg>"#,
22
    ).unwrap();
23

            
24
1
    let _output_surf = render_document(
25
        &svg,
26
1
        SurfaceSize(150, 150),
27
1
        |cr| cr.translate(50.0, 50.0),
28
1
        cairo::Rectangle::new(0.0, 0.0, 50.0, 50.0),
29
    )
30
    .unwrap();
31
2
}
32

            
33
// https://gitlab.gnome.org/GNOME/librsvg/issues/547
34
#[test]
35
2
fn nonexistent_image_shouldnt_cancel_rendering() {
36
1
    let svg = load_svg(
37
        br#"<?xml version="1.0" encoding="UTF-8"?>
38
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
39
     width="50" height="50">
40
  <image xlink:href="nonexistent.png" width="10" height="10"/>
41
  <rect x="10" y="10" width="30" height="30" fill="blue"/>
42
</svg>
43
"#,
44
    )
45
    .unwrap();
46

            
47
1
    let output_surf = render_document(
48
        &svg,
49
1
        SurfaceSize(50, 50),
50
1
        |_| (),
51
1
        cairo::Rectangle::new(0.0, 0.0, 50.0, 50.0),
52
    )
53
    .unwrap();
54

            
55
1
    let reference_surf = cairo::ImageSurface::create(cairo::Format::ARgb32, 50, 50).unwrap();
56

            
57
    {
58
1
        let cr = cairo::Context::new(&reference_surf).unwrap();
59

            
60
1
        cr.rectangle(10.0, 10.0, 30.0, 30.0);
61
1
        cr.set_source_rgba(0.0, 0.0, 1.0, 1.0);
62
1
        cr.fill().unwrap();
63
1
    }
64

            
65
1
    Reference::from_surface(reference_surf)
66
        .compare(&output_surf)
67
1
        .evaluate(&output_surf, "nonexistent_image_shouldnt_cancel_rendering");
68
2
}
69

            
70
// https://gitlab.gnome.org/GNOME/librsvg/-/issues/568
71
#[test]
72
2
fn href_attribute_overrides_xlink_href() {
73
1
    let svg = load_svg(
74
        br##"<?xml version="1.0" encoding="UTF-8"?>
75
<svg xmlns="http://www.w3.org/2000/svg"
76
     xmlns:xlink="http://www.w3.org/1999/xlink"
77
     width="500" height="500">
78
  <defs>
79
    <rect id="one" x="100" y="100" width="100" height="100" fill="red"/>
80
    <rect id="two" x="100" y="100" width="100" height="100" fill="lime"/>
81
  </defs>
82

            
83
  <!-- Per https://svgwg.org/svg2-draft/linking.html#XLinkRefAttrs a plain
84
       href attribute overrides an xlink:href one in SVG2 -->
85
  <use xlink:href="#one" href="#two"/>
86
</svg>
87
"##,
88
    )
89
    .unwrap();
90

            
91
1
    let output_surf = render_document(
92
        &svg,
93
1
        SurfaceSize(500, 500),
94
1
        |_| (),
95
1
        cairo::Rectangle::new(0.0, 0.0, 500.0, 500.0),
96
    )
97
    .unwrap();
98

            
99
1
    let reference_surf = cairo::ImageSurface::create(cairo::Format::ARgb32, 500, 500).unwrap();
100

            
101
    {
102
1
        let cr = cairo::Context::new(&reference_surf).unwrap();
103

            
104
1
        cr.rectangle(100.0, 100.0, 100.0, 100.0);
105
1
        cr.set_source_rgba(0.0, 1.0, 0.0, 1.0);
106
1
        cr.fill().unwrap();
107
1
    }
108

            
109
1
    Reference::from_surface(reference_surf)
110
        .compare(&output_surf)
111
1
        .evaluate(&output_surf, "href_attribute_overrides_xlink_href");
112
2
}
113

            
114
// https://gitlab.gnome.org/GNOME/librsvg/-/issues/560
115
#[test]
116
2
fn nonexistent_filter_leaves_object_unfiltered() {
117
1
    let svg = load_svg(
118
        br##"<?xml version="1.0" encoding="UTF-8"?>
119
<svg xmlns="http://www.w3.org/2000/svg"
120
     width="500" height="500">
121
  <rect x="100" y="100" width="100" height="100" fill="lime" filter="url(#nonexistent)"/>
122
</svg>
123
"##,
124
    )
125
    .unwrap();
126

            
127
1
    let output_surf = render_document(
128
        &svg,
129
1
        SurfaceSize(500, 500),
130
1
        |_| (),
131
1
        cairo::Rectangle::new(0.0, 0.0, 500.0, 500.0),
132
    )
133
    .unwrap();
134

            
135
1
    let reference_surf = cairo::ImageSurface::create(cairo::Format::ARgb32, 500, 500).unwrap();
136

            
137
    {
138
1
        let cr = cairo::Context::new(&reference_surf).unwrap();
139

            
140
1
        cr.rectangle(100.0, 100.0, 100.0, 100.0);
141
1
        cr.set_source_rgba(0.0, 1.0, 0.0, 1.0);
142
1
        cr.fill().unwrap();
143
1
    }
144

            
145
1
    Reference::from_surface(reference_surf)
146
        .compare(&output_surf)
147
1
        .evaluate(&output_surf, "nonexistent_filter_leaves_object_unfiltered");
148
2
}
149

            
150
// https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint says this:
151
//
152
// A <paint> allows a paint server reference, to be optionally
153
// followed by a <color> or the keyword none. When this optional value
154
// is given, the <color> value or the value none is a fallback value
155
// to use if the paint server reference in the layer is invalid (due
156
// to pointing to an element that does not exist or which is not a
157
// valid paint server).
158
//
159
// I'm interpreting this to mean that if we have
160
// fill="url(#recursive_paint_server) fallback_color", then the
161
// recursive paint server is not valid, and should fall back to to the
162
// specified color.
163
#[test]
164
2
fn recursive_paint_servers_fallback_to_color() {
165
1
    let svg = load_svg(
166
        br##"<?xml version="1.0" encoding="UTF-8"?>
167
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
168
     width="200" height="200">
169
  <defs>
170
    <pattern id="p" width="10" height="10" xlink:href="#p"/>
171
    <linearGradient id="l" xlink:href="#r"/>
172
    <radialGradient id="r" xlink:href="#l"/>
173
  </defs>
174

            
175
  <!-- These two should not render as there is no fallback color -->
176
  <rect fill="url(#p)" x="0" y="0" width="100" height="100" />
177
  <rect fill="url(#l)" x="100" y="0" width="100" height="100" />
178

            
179
  <!-- These two should render with the fallback color -->
180
  <rect fill="url(#p) lime" x="0" y="100" width="100" height="100" />
181
  <rect fill="url(#l) lime" x="100" y="100" width="100" height="100" />
182
</svg>
183
"##,
184
    )
185
    .unwrap();
186

            
187
1
    let output_surf = render_document(
188
        &svg,
189
1
        SurfaceSize(200, 200),
190
1
        |_| (),
191
1
        cairo::Rectangle::new(0.0, 0.0, 200.0, 200.0),
192
    )
193
    .unwrap();
194

            
195
1
    let reference_surf = cairo::ImageSurface::create(cairo::Format::ARgb32, 200, 200).unwrap();
196

            
197
    {
198
1
        let cr = cairo::Context::new(&reference_surf).unwrap();
199

            
200
1
        cr.rectangle(0.0, 100.0, 200.0, 100.0);
201
1
        cr.set_source_rgba(0.0, 1.0, 0.0, 1.0);
202
1
        cr.fill().unwrap();
203
1
    }
204

            
205
1
    Reference::from_surface(reference_surf)
206
        .compare(&output_surf)
207
1
        .evaluate(&output_surf, "recursive_paint_servers_fallback_to_color");
208
2
}
209

            
210
3
fn test_renders_as_empty(svg: &SvgHandle, test_name: &str) {
211
3
    let output_surf = render_document(
212
        svg,
213
3
        SurfaceSize(100, 100),
214
3
        |_| (),
215
3
        cairo::Rectangle::new(0.0, 0.0, 100.0, 100.0),
216
    )
217
    .unwrap();
218

            
219
3
    let reference_surf = cairo::ImageSurface::create(cairo::Format::ARgb32, 100, 100).unwrap();
220

            
221
3
    Reference::from_surface(reference_surf)
222
        .compare(&output_surf)
223
3
        .evaluate(&output_surf, test_name);
224
3
}
225

            
226
// https://gitlab.gnome.org/GNOME/librsvg/-/issues/308
227
#[test]
228
2
fn recursive_use() {
229
1
    let svg = load_svg(
230
        br##"<?xml version="1.0" encoding="UTF-8"?>
231
<svg xmlns:xlink="http://www.w3.org/1999/xlink">
232
  <defs>
233
    <g id="one">
234
      <use xlink:href="#one"/>
235
    </g>
236
  </defs>
237

            
238
  <use xlink:href="#one"/>
239
</svg>
240
"##,
241
    )
242
    .unwrap();
243

            
244
1
    test_renders_as_empty(&svg, "308-recursive-use");
245
2
}
246

            
247
// https://gitlab.gnome.org/GNOME/librsvg/-/issues/308
248
#[test]
249
2
fn use_self_ref() {
250
1
    let svg = load_svg(
251
        br##"<?xml version="1.0" encoding="UTF-8"?>
252
<svg xmlns:xlink="http://www.w3.org/1999/xlink">
253
  <defs>
254
    <use id="one" xlink:href="#one"/>
255
  </defs>
256

            
257
  <use xlink:href="#one"/>
258
</svg>
259
"##,
260
    )
261
    .unwrap();
262

            
263
1
    test_renders_as_empty(&svg, "308-use-self-ref");
264
2
}
265

            
266
// https://gitlab.gnome.org/GNOME/librsvg/-/issues/308
267
#[test]
268
2
fn doubly_recursive_use() {
269
1
    let svg = load_svg(
270
        br##"<?xml version="1.0" encoding="UTF-8"?>
271
<svg xmlns:xlink="http://www.w3.org/1999/xlink">
272
  <defs>
273
    <g id="one">
274
      <use xlink:href="#two"/>
275
    </g>
276

            
277
    <g id="two">
278
      <use xlink:href="#one"/>
279
    </g>
280
  </defs>
281

            
282
  <use xlink:href="#one"/>
283
</svg>
284
"##,
285
    )
286
    .unwrap();
287

            
288
1
    test_renders_as_empty(&svg, "308-doubly-recursive-use");
289
2
}
290

            
291
// https://gitlab.gnome.org/GNOME/librsvg/-/issues/347
292
#[test]
293
2
fn test_text_bounds() {
294
1
    setup_font_map();
295

            
296
1
    let handle = Loader::new()
297
        .read_path("tests/fixtures/dimensions/bug347-wrapper.svg")
298
        .unwrap_or_else(|e| panic!("could not load: {}", e));
299

            
300
1
    let renderer = CairoRenderer::new(&handle).test_mode(true);
301

            
302
1
    let (ink_r, _) = renderer
303
        .geometry_for_layer(
304
1
            Some("#LabelA"),
305
1
            &cairo::Rectangle::new(0.0, 0.0, 248.0, 176.0),
306
        )
307
        .unwrap();
308

            
309
1
    assert!(ink_r.x() >= 80.0 && ink_r.x() < 80.1);
310

            
311
    // This is kind of suspicious, but we don't know the actual height of the
312
    // text set at y=49 in the test SVG.  However, this test is more "text
313
    // elements compute sensible bounds"; the bug #347 was that their ink_rect
314
    // was not being computed correctly at all.
315
1
    assert!(ink_r.y() > 48.0 && ink_r.y() < 49.0);
316
2
}
317

            
318
// https://gitlab.gnome.org/GNOME/librsvg/-/issues/703
319
#[test]
320
2
fn switch_element_should_ignore_elements_in_error() {
321
1
    setup_language();
322

            
323
1
    let svg = load_svg(
324
        br##"<?xml version="1.0" encoding="UTF-8"?>
325
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
326
  <switch>
327
    <rect x="10" y="10" width="10" height="10" systemLanguage="es_MX" id="es" fill="red"/>
328
    <rect x="10" y="10" width="10" height="10" id="no_lang" fill="blue"/>
329
  </switch>
330
</svg>
331
"##,
332
    )
333
    .unwrap();
334

            
335
1
    let output_surf = render_document(
336
        &svg,
337
1
        SurfaceSize(100, 100),
338
1
        |_| (),
339
1
        cairo::Rectangle::new(0.0, 0.0, 100.0, 100.0),
340
    )
341
    .unwrap();
342

            
343
1
    let reference_surf = cairo::ImageSurface::create(cairo::Format::ARgb32, 100, 100).unwrap();
344

            
345
    {
346
1
        let cr = cairo::Context::new(&reference_surf).unwrap();
347

            
348
1
        cr.rectangle(10.0, 10.0, 10.0, 10.0);
349
1
        cr.set_source_rgba(0.0, 0.0, 1.0, 1.0);
350
1
        cr.fill().unwrap();
351
1
    }
352

            
353
1
    Reference::from_surface(reference_surf)
354
        .compare(&output_surf)
355
        .evaluate(
356
            &output_surf,
357
            "switch_element_should_ignore_elements_in_error",
358
1
        );
359
2
}
360

            
361
// https://gitlab.gnome.org/GNOME/librsvg/-/issues/566
362
#[test]
363
2
fn accepted_children_inside_clip_path() {
364
1
    let svg = load_svg(
365
        br##"<?xml version="1.0" encoding="UTF-8"?>
366
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200">
367
  <defs>
368
    <clipPath id="one">
369
      <g>
370
        <rect x="10" y="10" width="50" height="50"/>
371
      </g>
372
    </clipPath>
373

            
374
    <clipPath id="two">
375
      <use xlink:href="#three"/>
376
    </clipPath>
377

            
378
    <use id="three" xlink:href="#four"/>
379

            
380
    <rect id="four" x="10" y="10" width="50" height="50"/>
381
  </defs>
382

            
383
  <rect x="10" y="10" width="100" height="100" fill="lime"/>
384

            
385
  <rect x="20" y="20" width="10" height="10" fill="red" clip-path="url(#one)"/>
386

            
387
  <rect x="40" y="40" width="10" height="10" fill="red" clip-path="url(#two)"/>
388
</svg>
389
"##,
390
    )
391
    .unwrap();
392

            
393
1
    let output_surf = render_document(
394
        &svg,
395
1
        SurfaceSize(200, 200),
396
1
        |_| (),
397
1
        cairo::Rectangle::new(0.0, 0.0, 200.0, 200.0),
398
    )
399
    .unwrap();
400

            
401
1
    let reference_surf = cairo::ImageSurface::create(cairo::Format::ARgb32, 200, 200).unwrap();
402

            
403
    {
404
1
        let cr = cairo::Context::new(&reference_surf).unwrap();
405

            
406
1
        cr.rectangle(10.0, 10.0, 100.0, 100.0);
407
1
        cr.set_source_rgba(0.0, 1.0, 0.0, 1.0);
408
1
        cr.fill().unwrap();
409
1
    }
410

            
411
1
    Reference::from_surface(reference_surf)
412
        .compare(&output_surf)
413
1
        .evaluate(&output_surf, "accepted_children_inside_clip_path");
414
2
}
415

            
416
#[test]
417
2
fn can_draw_to_non_image_surface() {
418
    // This tries to exercise the various tricky code paths in DrawingCtx::with_discrete_layer()
419
    // that depend on whether there are filter/masks/opacity - they are easy to break when
420
    // the application is using something other than a cairo::ImageSurface.
421
1
    let svg = load_svg(
422
        br##"<?xml version="1.0" encoding="UTF-8"?>
423
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="100">
424
  <!-- code path with opacity, no mask -->
425
  <rect x="0" y="0" width="100" height="100" fill="lime" opacity="0.5"/>
426

            
427
  <!-- code path with mask -->
428
  <mask id="mask" maskUnits="objectBoundingBox">
429
    <rect x="10%" y="10%" width="80%" height="80%" fill="white"/>
430
  </mask>
431
  <rect x="100" y="0" width="100" height="100" fill="lime" mask="url(#mask)"/>
432

            
433
  <!-- code path with filter -->
434
  <rect x="200" y="0" width="100" height="100" fill="lime" filter="blur(5)"/>
435

            
436
  <!-- code path with filter and mask-->
437
  <rect x="300" y="0" width="100" height="100" fill="lime" filter="blur(5)" mask="url(#mask)"/>
438
</svg>
439
"##,
440
    )
441
    .unwrap();
442

            
443
1
    let renderer = CairoRenderer::new(&svg);
444

            
445
1
    let viewport = cairo::Rectangle::new(0.0, 0.0, 200.0, 200.0);
446

            
447
    let output =
448
1
        cairo::RecordingSurface::create(cairo::Content::ColorAlpha, Some(viewport)).unwrap();
449

            
450
1
    let cr = cairo::Context::new(&output).expect("Failed to create a cairo context");
451
1
    renderer
452
        .render_document(&cr, &viewport)
453
        .expect("Failed to render to non-image surface");
454
2
}