1
1
use rsvg::{CairoRenderer, IntrinsicDimensions, Length, LengthUnit, RenderingError};
2

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

            
6
#[test]
7
2
fn no_intrinsic_dimensions() {
8
1
    let svg = load_svg(
9
        br#"<?xml version="1.0" encoding="UTF-8"?>
10
<svg xmlns="http://www.w3.org/2000/svg"/>
11
"#,
12
    )
13
    .unwrap();
14

            
15
2
    assert_eq!(
16
1
        CairoRenderer::new(&svg).intrinsic_dimensions(),
17
1
        IntrinsicDimensions {
18
1
            width: Length::new(1.0, LengthUnit::Percent),
19
1
            height: Length::new(1.0, LengthUnit::Percent),
20
1
            vbox: None,
21
        }
22
    );
23
2
}
24

            
25
#[test]
26
2
fn has_intrinsic_dimensions() {
27
1
    let svg = load_svg(
28
        br#"<?xml version="1.0" encoding="UTF-8"?>
29
<svg xmlns="http://www.w3.org/2000/svg" width="10cm" height="20" viewBox="0 0 100 200"/>
30
"#,
31
    )
32
    .unwrap();
33

            
34
2
    assert_eq!(
35
1
        CairoRenderer::new(&svg).intrinsic_dimensions(),
36
1
        IntrinsicDimensions {
37
1
            width: Length::new(10.0, LengthUnit::Cm),
38
1
            height: Length::new(20.0, LengthUnit::Px),
39
1
            vbox: Some(cairo::Rectangle::new(0.0, 0.0, 100.0, 200.0)),
40
        }
41
    );
42
2
}
43

            
44
#[test]
45
2
fn intrinsic_size_in_pixels() {
46
1
    let svg = load_svg(
47
        br#"<?xml version="1.0" encoding="UTF-8"?>
48
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="20" viewBox="0 0 100 200"/>
49
"#,
50
    )
51
    .unwrap();
52

            
53
2
    assert_eq!(
54
1
        CairoRenderer::new(&svg).intrinsic_size_in_pixels(),
55
        Some((10.0, 20.0)),
56
    );
57
2
}
58

            
59
#[test]
60
2
fn no_intrinsic_size_in_pixels_with_percent_dimensions() {
61
1
    let svg = load_svg(
62
        br#"<?xml version="1.0" encoding="UTF-8"?>
63
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 100 200"/>
64
"#,
65
    )
66
    .unwrap();
67

            
68
2
    assert_eq!(CairoRenderer::new(&svg).intrinsic_size_in_pixels(), None);
69
2
}
70

            
71
#[test]
72
2
fn no_intrinsic_size_in_pixels_with_no_dimensions() {
73
1
    let svg = load_svg(
74
        br#"<?xml version="1.0" encoding="UTF-8"?>
75
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 200"/>
76
"#,
77
    )
78
    .unwrap();
79

            
80
2
    assert_eq!(CairoRenderer::new(&svg).intrinsic_size_in_pixels(), None);
81
2
}
82

            
83
#[test]
84
2
fn no_intrinsic_size_in_pixels_with_one_missing_dimension() {
85
1
    let svg = load_svg(
86
        br#"<?xml version="1.0" encoding="UTF-8"?>
87
<svg xmlns="http://www.w3.org/2000/svg" width="100" viewBox="0 0 100 200"/>
88
"#,
89
    )
90
    .unwrap();
91

            
92
2
    assert_eq!(CairoRenderer::new(&svg).intrinsic_size_in_pixels(), None);
93
2
}
94

            
95
#[test]
96
2
fn root_geometry_with_percent_viewport() {
97
1
    let svg = load_svg(
98
        br#"<?xml version="1.0" encoding="UTF-8"?>
99
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
100
  <rect x="10" y="20" width="30" height="40"/>
101
</svg>
102
"#,
103
    )
104
    .unwrap();
105

            
106
1
    let renderer = CairoRenderer::new(&svg);
107

            
108
1
    let viewport = cairo::Rectangle::new(0.0, 0.0, 100.0, 100.0);
109

            
110
1
    let (ink_r, logical_r) = renderer.geometry_for_layer(None, &viewport).unwrap();
111

            
112
1
    let rect = cairo::Rectangle::new(10.0, 20.0, 30.0, 40.0);
113

            
114
1
    assert_eq!((ink_r, logical_r), (rect, rect));
115
2
}
116

            
117
#[test]
118
2
fn layer_geometry_with_offset_viewport() {
119
1
    let svg = load_svg(
120
        br#"<?xml version="1.0" encoding="UTF-8"?>
121
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
122
  <rect x="10" y="20" width="30" height="40"/>
123
</svg>
124
"#,
125
    )
126
    .unwrap();
127

            
128
1
    let renderer = CairoRenderer::new(&svg);
129

            
130
1
    let viewport = cairo::Rectangle::new(100.0, 100.0, 100.0, 100.0);
131

            
132
1
    let (ink_r, logical_r) = renderer.geometry_for_layer(None, &viewport).unwrap();
133

            
134
1
    let rect = cairo::Rectangle::new(110.0, 120.0, 30.0, 40.0);
135

            
136
1
    assert_eq!((ink_r, logical_r), (rect, rect));
137
2
}
138

            
139
#[test]
140
2
fn layer_geometry_with_viewbox_and_offset_viewport() {
141
1
    let svg = load_svg(
142
        br#"<?xml version="1.0" encoding="UTF-8"?>
143
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="100 100 100 100">
144
  <rect x="110" y="120" width="30" height="40"/>
145
</svg>
146
"#,
147
    )
148
    .unwrap();
149

            
150
1
    let renderer = CairoRenderer::new(&svg);
151

            
152
1
    let viewport = cairo::Rectangle::new(100.0, 100.0, 100.0, 100.0);
153

            
154
1
    let (ink_r, logical_r) = renderer.geometry_for_layer(None, &viewport).unwrap();
155

            
156
1
    let rect = cairo::Rectangle::new(110.0, 120.0, 30.0, 40.0);
157

            
158
1
    assert_eq!((ink_r, logical_r), (rect, rect));
159
2
}
160

            
161
#[test]
162
2
fn layer_geometry_with_no_width_height() {
163
1
    let svg = load_svg(
164
        br#"<?xml version="1.0" encoding="UTF-8"?>
165
<svg xmlns="http://www.w3.org/2000/svg" viewBox="100 100 200 200">
166
  <rect x="110" y="120" width="30" height="40"/>
167
</svg>
168
"#,
169
    )
170
    .unwrap();
171

            
172
1
    let renderer = CairoRenderer::new(&svg);
173

            
174
1
    let viewport = cairo::Rectangle::new(100.0, 100.0, 100.0, 100.0);
175

            
176
1
    let (ink_r, logical_r) = renderer.geometry_for_layer(None, &viewport).unwrap();
177

            
178
1
    let rect = cairo::Rectangle::new(105.0, 110.0, 15.0, 20.0);
179

            
180
1
    assert_eq!((ink_r, logical_r), (rect, rect));
181
2
}
182

            
183
#[test]
184
2
fn layer_geometry_with_no_intrinsic_dimensions() {
185
1
    let svg = load_svg(
186
        br#"<?xml version="1.0" encoding="UTF-8"?>
187
<svg xmlns="http://www.w3.org/2000/svg">
188
  <rect x="110" y="120" width="50" height="40"/>
189
</svg>
190
"#,
191
    )
192
    .unwrap();
193

            
194
1
    let renderer = CairoRenderer::new(&svg);
195

            
196
1
    let viewport = cairo::Rectangle::new(100.0, 100.0, 100.0, 100.0);
197

            
198
1
    let (ink_r, logical_r) = renderer.geometry_for_layer(None, &viewport).unwrap();
199

            
200
    // The SVG document above has no width/height nor viewBox, which means it should
201
    // start with an identity transform for its coordinate space.  Since the viewport
202
    // is just offset by (100, 100), this just translates the coordinates of the <rect>.
203
1
    let rect = cairo::Rectangle::new(210.0, 220.0, 50.0, 40.0);
204

            
205
1
    assert_eq!((ink_r, logical_r), (rect, rect));
206
2
}
207

            
208
#[test]
209
2
fn layer_geometry_with_percentage_viewport() {
210
1
    let svg = load_svg(
211
        br#"<?xml version="1.0" encoding="UTF-8"?>
212
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="50%">
213
  <rect x="10" y="20" width="50" height="40"/>
214
</svg>
215
"#,
216
    )
217
    .unwrap();
218

            
219
1
    let renderer = CairoRenderer::new(&svg);
220

            
221
1
    let viewport = cairo::Rectangle::new(100.0, 100.0, 100.0, 100.0);
222

            
223
1
    let (ink_r, logical_r) = renderer.geometry_for_layer(None, &viewport).unwrap();
224

            
225
    // Lack of viewBox means we use an identity transform, so the <rect> is just
226
    // offset by (100, 100) because of the viewport.
227
1
    let rect = cairo::Rectangle::new(110.0, 120.0, 50.0, 40.0);
228

            
229
1
    assert_eq!((ink_r, logical_r), (rect, rect));
230
2
}
231

            
232
#[test]
233
2
fn layer_geometry_with_percent_viewport() {
234
1
    let svg = load_svg(
235
        br#"<?xml version="1.0" encoding="UTF-8"?>
236
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
237
  <rect id="foo" x="10" y="20" width="30" height="40"/>
238
</svg>
239
"#,
240
    )
241
    .unwrap();
242

            
243
1
    let renderer = CairoRenderer::new(&svg);
244

            
245
1
    let viewport = cairo::Rectangle::new(0.0, 0.0, 100.0, 100.0);
246

            
247
1
    let (ink_r, logical_r) = renderer
248
1
        .geometry_for_layer(Some("#foo"), &viewport)
249
        .unwrap();
250

            
251
1
    let rect = cairo::Rectangle::new(10.0, 20.0, 30.0, 40.0);
252

            
253
1
    assert_eq!((ink_r, logical_r), (rect, rect));
254
2
}
255

            
256
#[test]
257
2
fn layer_geometry_viewport_viewbox() {
258
1
    let svg = load_svg(
259
        br#"<?xml version="1.0" encoding="UTF-8"?>
260
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="400" viewBox="0 0 100 400">
261
  <rect id="one" x="0" y="0" width="100" height="200" fill="rgb(0,255,0)"/>
262
  <rect id="two" x="0" y="200" width="100" height="200" fill="rgb(0,0,255)"/>
263
</svg>
264
"#,
265
    )
266
    .unwrap();
267

            
268
1
    let renderer = CairoRenderer::new(&svg);
269

            
270
1
    let viewport = cairo::Rectangle::new(0.0, 0.0, 100.0, 400.0);
271

            
272
1
    let (ink_r, logical_r) = renderer
273
1
        .geometry_for_layer(Some("#two"), &viewport)
274
        .unwrap();
275

            
276
1
    let rect = cairo::Rectangle::new(0.0, 200.0, 100.0, 200.0);
277

            
278
1
    assert_eq!((ink_r, logical_r), (rect, rect));
279
2
}
280

            
281
#[test]
282
2
fn layer_geometry_for_nonexistent_element() {
283
1
    let svg = load_svg(
284
        br#"<?xml version="1.0" encoding="UTF-8"?>
285
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"/>
286
"#,
287
    )
288
    .unwrap();
289

            
290
1
    let viewport = cairo::Rectangle::new(0.0, 0.0, 100.0, 100.0);
291

            
292
1
    let renderer = CairoRenderer::new(&svg);
293

            
294
1
    assert!(matches!(
295
1
        renderer.geometry_for_layer(Some("#foo"), &viewport),
296
        Err(RenderingError::IdNotFound)
297
    ));
298
2
}
299

            
300
#[test]
301
2
fn layer_geometry_for_invalid_id() {
302
1
    let svg = load_svg(
303
        br#"<?xml version="1.0" encoding="UTF-8"?>
304
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"/>
305
"#,
306
    )
307
    .unwrap();
308

            
309
1
    let viewport = cairo::Rectangle::new(0.0, 0.0, 100.0, 100.0);
310

            
311
1
    let renderer = CairoRenderer::new(&svg);
312
1
    assert!(matches!(
313
1
        renderer.geometry_for_layer(Some("foo"), &viewport),
314
        Err(RenderingError::InvalidId(_))
315
    ));
316

            
317
1
    assert!(matches!(
318
1
        renderer.geometry_for_layer(Some("foo.svg#foo"), &viewport),
319
        Err(RenderingError::InvalidId(_))
320
    ));
321

            
322
1
    assert!(matches!(
323
1
        renderer.geometry_for_layer(Some(""), &viewport),
324
        Err(RenderingError::InvalidId(_))
325
    ));
326
2
}
327

            
328
#[test]
329
2
fn render_to_viewport_with_different_size() {
330
1
    let svg = load_svg(
331
        br#"<?xml version="1.0" encoding="UTF-8"?>
332
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48">
333
  <rect x="8" y="8" width="32" height="32" fill="blue"/>
334
</svg>
335
"#,
336
    )
337
    .unwrap();
338

            
339
1
    let output_surf = render_document(
340
        &svg,
341
        SurfaceSize(128, 128),
342
1
        |_cr| (),
343
1
        cairo::Rectangle::new(0.0, 0.0, 128.0, 128.0),
344
    )
345
    .unwrap();
346

            
347
1
    let reference_surf = cairo::ImageSurface::create(cairo::Format::ARgb32, 128, 128).unwrap();
348

            
349
    {
350
1
        let cr = cairo::Context::new(&reference_surf).expect("Failed to create a cairo context");
351

            
352
1
        cr.scale(128.0 / 48.0, 128.0 / 48.0);
353

            
354
1
        cr.rectangle(8.0, 8.0, 32.0, 32.0);
355
1
        cr.set_source_rgba(0.0, 0.0, 1.0, 1.0);
356
1
        cr.fill().unwrap();
357
1
    }
358

            
359
1
    Reference::from_surface(reference_surf)
360
        .compare(&output_surf)
361
1
        .evaluate(&output_surf, "render_to_viewport_with_different_size");
362
2
}
363

            
364
#[test]
365
2
fn render_to_offsetted_viewport() {
366
1
    let svg = load_svg(
367
        br#"<?xml version="1.0" encoding="UTF-8"?>
368
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48">
369
  <rect x="8" y="8" width="32" height="32" fill="blue"/>
370
</svg>
371
"#,
372
    )
373
    .unwrap();
374

            
375
1
    let output_surf = render_document(
376
        &svg,
377
        SurfaceSize(100, 100),
378
1
        |_cr| (),
379
1
        cairo::Rectangle::new(10.0, 20.0, 48.0, 48.0),
380
    )
381
    .unwrap();
382

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

            
385
    {
386
1
        let cr = cairo::Context::new(&reference_surf).expect("Failed to create a cairo context");
387

            
388
1
        cr.translate(10.0, 20.0);
389

            
390
1
        cr.rectangle(8.0, 8.0, 32.0, 32.0);
391
1
        cr.set_source_rgba(0.0, 0.0, 1.0, 1.0);
392
1
        cr.fill().unwrap();
393
1
    }
394

            
395
1
    Reference::from_surface(reference_surf)
396
        .compare(&output_surf)
397
1
        .evaluate(&output_surf, "render_to_offsetted_viewport");
398
2
}
399

            
400
#[test]
401
2
fn render_to_viewport_with_transform() {
402
1
    let svg = load_svg(
403
        br#"<?xml version="1.0" encoding="UTF-8"?>
404
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48">
405
  <g transform="translate(-10, -10)">
406
    <path fill="blue" d="M 18 18 l 32 0 l 0 32 l -32 0 z"/>
407
  </g>
408
</svg>
409
"#,
410
    )
411
    .unwrap();
412

            
413
1
    let output_surf = render_document(
414
        &svg,
415
        SurfaceSize(100, 100),
416
1
        |cr| cr.translate(10.0, 20.0),
417
1
        cairo::Rectangle::new(0.0, 0.0, 48.0, 48.0),
418
    )
419
    .unwrap();
420

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

            
423
    {
424
1
        let cr = cairo::Context::new(&reference_surf).expect("Failed to create a cairo context");
425

            
426
1
        cr.translate(10.0, 20.0);
427
1
        cr.translate(-10.0, -10.0);
428

            
429
1
        cr.rectangle(18.0, 18.0, 32.0, 32.0);
430
1
        cr.set_source_rgba(0.0, 0.0, 1.0, 1.0);
431
1
        cr.fill().unwrap();
432
1
    }
433

            
434
1
    Reference::from_surface(reference_surf)
435
        .compare(&output_surf)
436
1
        .evaluate(&output_surf, "render_to_viewport_with_transform");
437
2
}
438

            
439
#[test]
440
2
fn clip_on_transformed_viewport() {
441
1
    let svg = load_svg(
442
        br##"<?xml version="1.0" encoding="UTF-8"?>
443
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
444
  <defs>
445
    <clipPath id="one" clipPathUnits="objectBoundingBox">
446
      <path d="M 0.5 0.0 L 1.0 0.5 L 0.5 1.0 L 0.0 0.5 Z"/>
447
    </clipPath>
448
  </defs>
449
  <g clip-path="url(#one)">
450
    <rect x="10" y="10" width="40" height="40" fill="blue"/>
451
    <rect x="50" y="50" width="40" height="40" fill="#00ff00"/>
452
  </g>
453
</svg>
454
"##,
455
    )
456
    .unwrap();
457

            
458
1
    let output_surf = render_document(
459
        &svg,
460
        SurfaceSize(200, 200),
461
1
        |cr| cr.translate(50.0, 50.0),
462
1
        cairo::Rectangle::new(0.0, 0.0, 100.0, 100.0),
463
    )
464
    .unwrap();
465

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

            
468
    {
469
1
        let cr = cairo::Context::new(&reference_surf).expect("Failed to create a cairo context");
470

            
471
1
        cr.translate(50.0, 50.0);
472

            
473
1
        cr.push_group();
474

            
475
1
        cr.rectangle(10.0, 10.0, 40.0, 40.0);
476
1
        cr.set_source_rgba(0.0, 0.0, 1.0, 1.0);
477
1
        cr.fill().unwrap();
478

            
479
1
        cr.rectangle(50.0, 50.0, 40.0, 40.0);
480
1
        cr.set_source_rgba(0.0, 1.0, 0.0, 1.0);
481
1
        cr.fill().unwrap();
482

            
483
1
        cr.pop_group_to_source().unwrap();
484

            
485
1
        cr.move_to(50.0, 10.0);
486
1
        cr.line_to(90.0, 50.0);
487
1
        cr.line_to(50.0, 90.0);
488
1
        cr.line_to(10.0, 50.0);
489
1
        cr.close_path();
490

            
491
1
        cr.clip();
492
1
        cr.paint().unwrap();
493
1
    }
494

            
495
1
    Reference::from_surface(reference_surf)
496
        .compare(&output_surf)
497
1
        .evaluate(&output_surf, "clip_on_transformed_viewport");
498
2
}
499

            
500
#[test]
501
2
fn mask_on_transformed_viewport() {
502
1
    let svg = load_svg(
503
        br##"<?xml version="1.0" encoding="UTF-8"?>
504
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
505
  <defs>
506
    <mask id="one" maskContentUnits="objectBoundingBox">
507
      <path d="M 0.5 0.0 L 1.0 0.5 L 0.5 1.0 L 0.0 0.5 Z" fill="white"/>
508
    </mask>
509
  </defs>
510
  <g mask="url(#one)">
511
    <rect x="10" y="10" width="40" height="40" fill="blue"/>
512
    <rect x="50" y="50" width="40" height="40" fill="#00ff00"/>
513
  </g>
514
</svg>
515
"##,
516
    )
517
    .unwrap();
518

            
519
1
    let output_surf = render_document(
520
        &svg,
521
        SurfaceSize(200, 200),
522
1
        |cr| cr.translate(50.0, 50.0),
523
1
        cairo::Rectangle::new(0.0, 0.0, 100.0, 100.0),
524
    )
525
    .unwrap();
526

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

            
529
    {
530
1
        let cr = cairo::Context::new(&reference_surf).expect("Failed to create a cairo context");
531

            
532
1
        cr.translate(50.0, 50.0);
533

            
534
1
        cr.push_group();
535

            
536
1
        cr.rectangle(10.0, 10.0, 40.0, 40.0);
537
1
        cr.set_source_rgba(0.0, 0.0, 1.0, 1.0);
538
1
        cr.fill().unwrap();
539

            
540
1
        cr.rectangle(50.0, 50.0, 40.0, 40.0);
541
1
        cr.set_source_rgba(0.0, 1.0, 0.0, 1.0);
542
1
        cr.fill().unwrap();
543

            
544
1
        cr.pop_group_to_source().unwrap();
545

            
546
1
        cr.move_to(50.0, 10.0);
547
1
        cr.line_to(90.0, 50.0);
548
1
        cr.line_to(50.0, 90.0);
549
1
        cr.line_to(10.0, 50.0);
550
1
        cr.close_path();
551

            
552
1
        cr.clip();
553
1
        cr.paint().unwrap();
554
1
    }
555

            
556
1
    Reference::from_surface(reference_surf)
557
        .compare(&output_surf)
558
1
        .evaluate(&output_surf, "mask_on_transformed_viewport");
559
2
}