1
//! Handling of transform values.
2
//!
3
//! This module contains the following:
4
//!
5
//! * [`Transform`] to represent 2D transforms in general; it's just a matrix.
6
//!
7
//! * [`TransformProperty`] for the [`transform` property][prop] in SVG2/CSS3.
8
//!
9
//! * [`Transform`] also handles the [`transform` attribute][attr] in SVG1.1, which has a different
10
//!   grammar than the `transform` property from SVG2.
11
//!
12
//! [prop]: https://www.w3.org/TR/css-transforms-1/#transform-property
13
//! [attr]: https://www.w3.org/TR/SVG11/coords.html#TransformAttribute
14

            
15
use cssparser::{Parser, Token};
16
use std::ops::Deref;
17

            
18
use crate::angle::Angle;
19
use crate::error::*;
20
use crate::length::*;
21
use crate::parsers::{optional_comma, Parse};
22
use crate::properties::ComputedValues;
23
use crate::property_macros::Property;
24
use crate::rect::Rect;
25

            
26
/// A transform that has been checked to be invertible.
27
///
28
/// We need to validate user-supplied transforms before setting them on Cairo,
29
/// so we use this type for that.
30
#[derive(Debug, Default, Copy, Clone, PartialEq)]
31
pub struct ValidTransform(Transform);
32

            
33
impl TryFrom<Transform> for ValidTransform {
34
    type Error = InvalidTransform;
35

            
36
    /// Validates a [`Transform`] before converting it to a [`ValidTransform`].
37
    ///
38
    /// A transform is valid if it is invertible.  For example, a
39
    /// matrix with all-zeros is not invertible, and it is invalid.
40
97328605
    fn try_from(t: Transform) -> Result<ValidTransform, InvalidTransform> {
41
97328605
        if t.is_invertible() {
42
97328528
            Ok(ValidTransform(t))
43
        } else {
44
77
            Err(InvalidTransform)
45
        }
46
97328605
    }
47
}
48

            
49
impl Deref for ValidTransform {
50
    type Target = Transform;
51

            
52
319476965
    fn deref(&self) -> &Transform {
53
319476965
        &self.0
54
319476965
    }
55
}
56

            
57
/// A 2D transformation matrix.
58
#[derive(Debug, Copy, Clone, PartialEq)]
59
pub struct Transform {
60
    pub xx: f64,
61
    pub yx: f64,
62
    pub xy: f64,
63
    pub yy: f64,
64
    pub x0: f64,
65
    pub y0: f64,
66
}
67

            
68
/// The `transform` property from the CSS Transforms Module Level 1.
69
///
70
/// CSS Transforms 1: <https://www.w3.org/TR/css-transforms-1/#transform-property>
71
#[derive(Debug, Default, Clone, PartialEq)]
72
pub enum TransformProperty {
73
    #[default]
74
    None,
75
    List(Vec<TransformFunction>),
76
}
77

            
78
/// The `transform` attribute from SVG1.1
79
///
80
/// SVG1.1: <https://www.w3.org/TR/SVG11/coords.html#TransformAttribute>
81
#[derive(Copy, Clone, Default, Debug, PartialEq)]
82
pub struct TransformAttribute(Transform);
83

            
84
impl Property for TransformProperty {
85
27920537
    fn inherits_automatically() -> bool {
86
27920537
        false
87
27920537
    }
88

            
89
27920575
    fn compute(&self, _v: &ComputedValues) -> Self {
90
27920575
        self.clone()
91
27920575
    }
92
}
93

            
94
impl TransformProperty {
95
39
    pub fn to_transform(&self) -> Transform {
96
39
        // From the spec (https://www.w3.org/TR/css-transforms-1/#current-transformation-matrix):
97
39
        // Start with the identity matrix.
98
39
        // TODO: implement (#685) - Translate by the computed X and Y of transform-origin
99
39
        // Multiply by each of the transform functions in transform property from left to right
100
39
        // TODO: implement - Translate by the negated computed X and Y values of transform-origin
101
39

            
102
39
        match self {
103
1
            TransformProperty::None => Transform::identity(),
104

            
105
38
            TransformProperty::List(l) => {
106
38
                let mut final_transform = Transform::identity();
107

            
108
76
                for f in l.iter() {
109
76
                    use TransformFunction::*;
110

            
111
76
                    let transform_matrix = match f {
112
                        Matrix(trans_matrix) => *trans_matrix,
113
38
                        Translate(h, v) => Transform::new_translate(h.length, v.length),
114
                        TranslateX(h) => Transform::new_translate(h.length, 0.0),
115
                        TranslateY(v) => Transform::new_translate(0.0, v.length),
116
19
                        Scale(x, y) => Transform::new_scale(*x, *y),
117
                        ScaleX(x) => Transform::new_scale(*x, 1.0),
118
                        ScaleY(y) => Transform::new_scale(1.0, *y),
119
19
                        Rotate(a) => Transform::new_rotate(*a),
120
                        Skew(ax, ay) => Transform::new_skew(*ax, *ay),
121
                        SkewX(ax) => Transform::new_skew(*ax, Angle::new(0.0)),
122
                        SkewY(ay) => Transform::new_skew(Angle::new(0.0), *ay),
123
                    };
124
76
                    final_transform = transform_matrix.post_transform(&final_transform);
125
                }
126

            
127
38
                final_transform
128
            }
129
        }
130
39
    }
131
}
132

            
133
// https://www.w3.org/TR/css-transforms-1/#typedef-transform-function
134
#[derive(Debug, Clone, PartialEq)]
135
pub enum TransformFunction {
136
    Matrix(Transform),
137
    Translate(Length<Horizontal>, Length<Vertical>),
138
    TranslateX(Length<Horizontal>),
139
    TranslateY(Length<Vertical>),
140
    Scale(f64, f64),
141
    ScaleX(f64),
142
    ScaleY(f64),
143
    Rotate(Angle),
144
    Skew(Angle, Angle),
145
    SkewX(Angle),
146
    SkewY(Angle),
147
}
148

            
149
impl Parse for TransformProperty {
150
101
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<TransformProperty, ParseError<'i>> {
151
101
        if parser
152
101
            .try_parse(|p| p.expect_ident_matching("none"))
153
101
            .is_ok()
154
        {
155
2
            Ok(TransformProperty::None)
156
        } else {
157
99
            let t = parse_transform_prop_function_list(parser)?;
158

            
159
63
            Ok(TransformProperty::List(t))
160
        }
161
101
    }
162
}
163

            
164
99
fn parse_transform_prop_function_list<'i>(
165
99
    parser: &mut Parser<'i, '_>,
166
99
) -> Result<Vec<TransformFunction>, ParseError<'i>> {
167
99
    let mut v = Vec::<TransformFunction>::new();
168

            
169
    loop {
170
137
        v.push(parse_transform_prop_function_command(parser)?);
171

            
172
101
        if parser.is_exhausted() {
173
63
            break;
174
38
        }
175
    }
176

            
177
63
    Ok(v)
178
99
}
179

            
180
137
fn parse_transform_prop_function_command<'i>(
181
137
    parser: &mut Parser<'i, '_>,
182
137
) -> Result<TransformFunction, ParseError<'i>> {
183
137
    let loc = parser.current_source_location();
184
137

            
185
137
    match parser.next()?.clone() {
186
136
        Token::Function(ref name) => parse_transform_prop_function_internal(name, parser),
187
        tok => Err(loc.new_unexpected_token_error(tok.clone())),
188
    }
189
137
}
190

            
191
136
fn parse_transform_prop_function_internal<'i>(
192
136
    name: &str,
193
136
    parser: &mut Parser<'i, '_>,
194
136
) -> Result<TransformFunction, ParseError<'i>> {
195
136
    let loc = parser.current_source_location();
196
136

            
197
136
    match name {
198
136
        "matrix" => parse_prop_matrix_args(parser),
199
134
        "translate" => parse_prop_translate_args(parser),
200
87
        "translateX" => parse_prop_translate_x_args(parser),
201
83
        "translateY" => parse_prop_translate_y_args(parser),
202
79
        "scale" => parse_prop_scale_args(parser),
203
52
        "scaleX" => parse_prop_scale_x_args(parser),
204
49
        "scaleY" => parse_prop_scale_y_args(parser),
205
46
        "rotate" => parse_prop_rotate_args(parser),
206
24
        "skew" => parse_prop_skew_args(parser),
207
15
        "skewX" => parse_prop_skew_x_args(parser),
208
11
        "skewY" => parse_prop_skew_y_args(parser),
209
7
        _ => Err(loc.new_custom_error(ValueErrorKind::parse_error(
210
7
            "expected matrix|translate|translateX|translateY|scale|scaleX|scaleY|rotate|skewX|skewY",
211
7
        ))),
212
    }
213
136
}
214

            
215
2
fn parse_prop_matrix_args<'i>(
216
2
    parser: &mut Parser<'i, '_>,
217
2
) -> Result<TransformFunction, ParseError<'i>> {
218
2
    parser.parse_nested_block(|p| {
219
2
        let xx = f64::parse(p)?;
220
2
        p.expect_comma()?;
221
1
        let yx = f64::parse(p)?;
222
1
        p.expect_comma()?;
223
1
        let xy = f64::parse(p)?;
224
1
        p.expect_comma()?;
225
1
        let yy = f64::parse(p)?;
226
1
        p.expect_comma()?;
227
1
        let x0 = f64::parse(p)?;
228
1
        p.expect_comma()?;
229
1
        let y0 = f64::parse(p)?;
230

            
231
1
        Ok(TransformFunction::Matrix(Transform::new_unchecked(
232
1
            xx, yx, xy, yy, x0, y0,
233
1
        )))
234
2
    })
235
2
}
236

            
237
97
fn length_is_in_pixels<N: Normalize>(l: &Length<N>) -> bool {
238
97
    l.unit == LengthUnit::Px
239
97
}
240

            
241
4
fn only_pixels_error<'i>(loc: cssparser::SourceLocation) -> ParseError<'i> {
242
4
    loc.new_custom_error(ValueErrorKind::parse_error(
243
4
        "only translations in pixels are supported for now",
244
4
    ))
245
4
}
246

            
247
47
fn parse_prop_translate_args<'i>(
248
47
    parser: &mut Parser<'i, '_>,
249
47
) -> Result<TransformFunction, ParseError<'i>> {
250
47
    parser.parse_nested_block(|p| {
251
47
        let loc = p.current_source_location();
252

            
253
47
        let tx: Length<Horizontal> = Length::parse(p)?;
254

            
255
47
        let ty: Length<Vertical> = if p.try_parse(|p| p.expect_comma()).is_ok() {
256
45
            Length::parse(p)?
257
        } else {
258
2
            Length::new(0.0, LengthUnit::Px)
259
        };
260

            
261
45
        if !(length_is_in_pixels(&tx) && length_is_in_pixels(&ty)) {
262
2
            return Err(only_pixels_error(loc));
263
43
        }
264
43

            
265
43
        Ok(TransformFunction::Translate(tx, ty))
266
47
    })
267
47
}
268

            
269
4
fn parse_prop_translate_x_args<'i>(
270
4
    parser: &mut Parser<'i, '_>,
271
4
) -> Result<TransformFunction, ParseError<'i>> {
272
4
    parser.parse_nested_block(|p| {
273
4
        let loc = p.current_source_location();
274

            
275
4
        let tx: Length<Horizontal> = Length::parse(p)?;
276

            
277
4
        if !length_is_in_pixels(&tx) {
278
1
            return Err(only_pixels_error(loc));
279
3
        }
280
3

            
281
3
        Ok(TransformFunction::TranslateX(tx))
282
4
    })
283
4
}
284

            
285
4
fn parse_prop_translate_y_args<'i>(
286
4
    parser: &mut Parser<'i, '_>,
287
4
) -> Result<TransformFunction, ParseError<'i>> {
288
4
    parser.parse_nested_block(|p| {
289
4
        let loc = p.current_source_location();
290

            
291
4
        let ty: Length<Vertical> = Length::parse(p)?;
292

            
293
4
        if !length_is_in_pixels(&ty) {
294
1
            return Err(only_pixels_error(loc));
295
3
        }
296
3

            
297
3
        Ok(TransformFunction::TranslateY(ty))
298
4
    })
299
4
}
300

            
301
27
fn parse_prop_scale_args<'i>(
302
27
    parser: &mut Parser<'i, '_>,
303
27
) -> Result<TransformFunction, ParseError<'i>> {
304
27
    parser.parse_nested_block(|p| {
305
27
        let x = f64::parse(p)?;
306

            
307
25
        let y = if p.try_parse(|p| p.expect_comma()).is_ok() {
308
4
            f64::parse(p)?
309
        } else {
310
21
            x
311
        };
312

            
313
23
        Ok(TransformFunction::Scale(x, y))
314
27
    })
315
27
}
316

            
317
3
fn parse_prop_scale_x_args<'i>(
318
3
    parser: &mut Parser<'i, '_>,
319
3
) -> Result<TransformFunction, ParseError<'i>> {
320
3
    parser.parse_nested_block(|p| {
321
3
        let x = f64::parse(p)?;
322

            
323
2
        Ok(TransformFunction::ScaleX(x))
324
3
    })
325
3
}
326

            
327
3
fn parse_prop_scale_y_args<'i>(
328
3
    parser: &mut Parser<'i, '_>,
329
3
) -> Result<TransformFunction, ParseError<'i>> {
330
3
    parser.parse_nested_block(|p| {
331
3
        let y = f64::parse(p)?;
332

            
333
2
        Ok(TransformFunction::ScaleY(y))
334
3
    })
335
3
}
336

            
337
22
fn parse_prop_rotate_args<'i>(
338
22
    parser: &mut Parser<'i, '_>,
339
22
) -> Result<TransformFunction, ParseError<'i>> {
340
22
    parser.parse_nested_block(|p| {
341
22
        let angle = Angle::parse(p)?;
342

            
343
21
        Ok(TransformFunction::Rotate(angle))
344
22
    })
345
22
}
346

            
347
9
fn parse_prop_skew_args<'i>(
348
9
    parser: &mut Parser<'i, '_>,
349
9
) -> Result<TransformFunction, ParseError<'i>> {
350
9
    parser.parse_nested_block(|p| {
351
9
        let ax = Angle::parse(p)?;
352

            
353
8
        let ay = if p.try_parse(|p| p.expect_comma()).is_ok() {
354
7
            Angle::parse(p)?
355
        } else {
356
1
            Angle::from_degrees(0.0)
357
        };
358

            
359
6
        Ok(TransformFunction::Skew(ax, ay))
360
9
    })
361
9
}
362

            
363
4
fn parse_prop_skew_x_args<'i>(
364
4
    parser: &mut Parser<'i, '_>,
365
4
) -> Result<TransformFunction, ParseError<'i>> {
366
4
    parser.parse_nested_block(|p| {
367
4
        let angle = Angle::parse(p)?;
368
4
        Ok(TransformFunction::SkewX(angle))
369
4
    })
370
4
}
371

            
372
4
fn parse_prop_skew_y_args<'i>(
373
4
    parser: &mut Parser<'i, '_>,
374
4
) -> Result<TransformFunction, ParseError<'i>> {
375
4
    parser.parse_nested_block(|p| {
376
4
        let angle = Angle::parse(p)?;
377
4
        Ok(TransformFunction::SkewY(angle))
378
4
    })
379
4
}
380

            
381
impl Transform {
382
    #[inline]
383
220191374
    pub fn new_unchecked(xx: f64, yx: f64, xy: f64, yy: f64, x0: f64, y0: f64) -> Self {
384
220191374
        Self {
385
220191374
            xx,
386
220191374
            yx,
387
220191374
            xy,
388
220191374
            yy,
389
220191374
            x0,
390
220191374
            y0,
391
220191374
        }
392
220191374
    }
393

            
394
    #[inline]
395
142818024
    pub fn identity() -> Self {
396
142818024
        Self::new_unchecked(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
397
142818024
    }
398

            
399
    #[inline]
400
19182288
    pub fn new_translate(tx: f64, ty: f64) -> Self {
401
19182288
        Self::new_unchecked(1.0, 0.0, 0.0, 1.0, tx, ty)
402
19182288
    }
403

            
404
    #[inline]
405
1961384
    pub fn new_scale(sx: f64, sy: f64) -> Self {
406
1961384
        Self::new_unchecked(sx, 0.0, 0.0, sy, 0.0, 0.0)
407
1961384
    }
408

            
409
    #[inline]
410
6148
    pub fn new_rotate(a: Angle) -> Self {
411
6148
        let (s, c) = a.radians().sin_cos();
412
6148
        Self::new_unchecked(c, s, -s, c, 0.0, 0.0)
413
6148
    }
414

            
415
    #[inline]
416
195
    pub fn new_skew(ax: Angle, ay: Angle) -> Self {
417
195
        Self::new_unchecked(1.0, ay.radians().tan(), ax.radians().tan(), 1.0, 0.0, 0.0)
418
195
    }
419

            
420
    #[must_use]
421
105964044
    pub fn multiply(t1: &Transform, t2: &Transform) -> Self {
422
105964044
        #[allow(clippy::suspicious_operation_groupings)]
423
105964044
        Transform {
424
105964044
            xx: t1.xx * t2.xx + t1.yx * t2.xy,
425
105964044
            yx: t1.xx * t2.yx + t1.yx * t2.yy,
426
105964044
            xy: t1.xy * t2.xx + t1.yy * t2.xy,
427
105964044
            yy: t1.xy * t2.yx + t1.yy * t2.yy,
428
105964044
            x0: t1.x0 * t2.xx + t1.y0 * t2.xy + t2.x0,
429
105964044
            y0: t1.x0 * t2.yx + t1.y0 * t2.yy + t2.y0,
430
105964044
        }
431
105964044
    }
432

            
433
    #[inline]
434
95358203
    pub fn pre_transform(&self, t: &Transform) -> Self {
435
95358203
        Self::multiply(t, self)
436
95358203
    }
437

            
438
    #[inline]
439
10605829
    pub fn post_transform(&self, t: &Transform) -> Self {
440
10605829
        Self::multiply(self, t)
441
10605829
    }
442

            
443
    #[inline]
444
50384
    pub fn pre_translate(&self, x: f64, y: f64) -> Self {
445
50384
        self.pre_transform(&Transform::new_translate(x, y))
446
50384
    }
447

            
448
    #[inline]
449
991956
    pub fn pre_scale(&self, sx: f64, sy: f64) -> Self {
450
991956
        self.pre_transform(&Transform::new_scale(sx, sy))
451
991956
    }
452

            
453
    #[inline]
454
6129
    pub fn pre_rotate(&self, angle: Angle) -> Self {
455
6129
        self.pre_transform(&Transform::new_rotate(angle))
456
6129
    }
457

            
458
    #[inline]
459
    pub fn post_translate(&self, x: f64, y: f64) -> Self {
460
        self.post_transform(&Transform::new_translate(x, y))
461
    }
462

            
463
    #[inline]
464
962255
    pub fn post_scale(&self, sx: f64, sy: f64) -> Self {
465
962255
        self.post_transform(&Transform::new_scale(sx, sy))
466
962255
    }
467

            
468
    #[inline]
469
    pub fn post_rotate(&self, angle: Angle) -> Self {
470
        self.post_transform(&Transform::new_rotate(angle))
471
    }
472

            
473
    #[inline]
474
135482364
    fn determinant(&self) -> f64 {
475
135482364
        self.xx * self.yy - self.xy * self.yx
476
135482364
    }
477

            
478
    #[inline]
479
97341851
    pub fn is_invertible(&self) -> bool {
480
97341851
        let det = self.determinant();
481
97341851

            
482
97341851
        det != 0.0 && det.is_finite()
483
97341851
    }
484

            
485
    #[must_use]
486
38140513
    pub fn invert(&self) -> Option<Self> {
487
38140513
        let det = self.determinant();
488
38140513

            
489
38140513
        if det == 0.0 || !det.is_finite() {
490
20
            return None;
491
38140493
        }
492
38140493

            
493
38140493
        let inv_det = 1.0 / det;
494
38140493

            
495
38140493
        Some(Transform::new_unchecked(
496
38140493
            inv_det * self.yy,
497
38140493
            inv_det * (-self.yx),
498
38140493
            inv_det * (-self.xy),
499
38140493
            inv_det * self.xx,
500
38140493
            inv_det * (self.xy * self.y0 - self.yy * self.x0),
501
38140493
            inv_det * (self.yx * self.x0 - self.xx * self.y0),
502
38140493
        ))
503
38140513
    }
504

            
505
    #[inline]
506
279593122
    pub fn transform_distance(&self, dx: f64, dy: f64) -> (f64, f64) {
507
279593122
        (dx * self.xx + dy * self.xy, dx * self.yx + dy * self.yy)
508
279593122
    }
509

            
510
    #[inline]
511
279556280
    pub fn transform_point(&self, px: f64, py: f64) -> (f64, f64) {
512
279556280
        let (x, y) = self.transform_distance(px, py);
513
279556280
        (x + self.x0, y + self.y0)
514
279556280
    }
515

            
516
46831740
    pub fn transform_rect(&self, rect: &Rect) -> Rect {
517
46831740
        let points = [
518
46831740
            self.transform_point(rect.x0, rect.y0),
519
46831740
            self.transform_point(rect.x1, rect.y0),
520
46831740
            self.transform_point(rect.x0, rect.y1),
521
46831740
            self.transform_point(rect.x1, rect.y1),
522
46831740
        ];
523
46831740

            
524
46831740
        let (mut xmin, mut ymin, mut xmax, mut ymax) = {
525
46831740
            let (x, y) = points[0];
526
46831740

            
527
46831740
            (x, y, x, y)
528
46831740
        };
529

            
530
140495220
        for &(x, y) in points.iter().skip(1) {
531
140495220
            if x < xmin {
532
123196
                xmin = x;
533
140372024
            }
534

            
535
140495220
            if x > xmax {
536
21427619
                xmax = x;
537
119067601
            }
538

            
539
140495220
            if y < ymin {
540
8607
                ymin = y;
541
140486613
            }
542

            
543
140495220
            if y > ymax {
544
21546711
                ymax = y;
545
118948509
            }
546
        }
547

            
548
46831740
        Rect {
549
46831740
            x0: xmin,
550
46831740
            y0: ymin,
551
46831740
            x1: xmax,
552
46831740
            y1: ymax,
553
46831740
        }
554
46831740
    }
555
}
556

            
557
impl Default for Transform {
558
    #[inline]
559
113930786
    fn default() -> Transform {
560
113930786
        Transform::identity()
561
113930786
    }
562
}
563

            
564
impl TransformAttribute {
565
9608549
    pub fn to_transform(self) -> Transform {
566
9608549
        self.0
567
9608549
    }
568
}
569

            
570
impl Parse for TransformAttribute {
571
107664
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<TransformAttribute, ParseError<'i>> {
572
107664
        Ok(TransformAttribute(parse_transform_list(parser)?))
573
107664
    }
574
}
575

            
576
107664
fn parse_transform_list<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
577
107664
    let mut t = Transform::identity();
578

            
579
    loop {
580
221350
        if parser.is_exhausted() {
581
106307
            break;
582
115043
        }
583
115043

            
584
115043
        t = parse_transform_command(parser)?.post_transform(&t);
585
113686
        optional_comma(parser);
586
    }
587

            
588
106307
    Ok(t)
589
107664
}
590

            
591
115043
fn parse_transform_command<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
592
115043
    let loc = parser.current_source_location();
593
115043

            
594
115043
    match parser.next()?.clone() {
595
115005
        Token::Function(ref name) => parse_transform_function(name, parser),
596

            
597
38
        Token::Ident(ref name) => {
598
38
            parser.expect_parenthesis_block()?;
599
36
            parse_transform_function(name, parser)
600
        }
601

            
602
        tok => Err(loc.new_unexpected_token_error(tok.clone())),
603
    }
604
115043
}
605

            
606
115041
fn parse_transform_function<'i>(
607
115041
    name: &str,
608
115041
    parser: &mut Parser<'i, '_>,
609
115041
) -> Result<Transform, ParseError<'i>> {
610
115041
    let loc = parser.current_source_location();
611
115041

            
612
115041
    match name {
613
115041
        "matrix" => parse_matrix_args(parser),
614
106791
        "translate" => parse_translate_args(parser),
615
9520
        "scale" => parse_scale_args(parser),
616
2632
        "rotate" => parse_rotate_args(parser),
617
194
        "skewX" => parse_skew_x_args(parser),
618
78
        "skewY" => parse_skew_y_args(parser),
619
        _ => Err(loc.new_custom_error(ValueErrorKind::parse_error(
620
            "expected matrix|translate|scale|rotate|skewX|skewY",
621
        ))),
622
    }
623
115041
}
624

            
625
8250
fn parse_matrix_args<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
626
8250
    parser.parse_nested_block(|p| {
627
8250
        let xx = f64::parse(p)?;
628
8250
        optional_comma(p);
629

            
630
8250
        let yx = f64::parse(p)?;
631
6901
        optional_comma(p);
632

            
633
6901
        let xy = f64::parse(p)?;
634
6901
        optional_comma(p);
635

            
636
6901
        let yy = f64::parse(p)?;
637
6901
        optional_comma(p);
638

            
639
6901
        let x0 = f64::parse(p)?;
640
6901
        optional_comma(p);
641

            
642
6901
        let y0 = f64::parse(p)?;
643

            
644
6900
        Ok(Transform::new_unchecked(xx, yx, xy, yy, x0, y0))
645
8250
    })
646
8250
}
647

            
648
97271
fn parse_translate_args<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
649
97271
    parser.parse_nested_block(|p| {
650
97271
        let tx = f64::parse(p)?;
651

            
652
97271
        let ty = p
653
97271
            .try_parse(|p| {
654
97271
                optional_comma(p);
655
97271
                f64::parse(p)
656
97271
            })
657
97271
            .unwrap_or(0.0);
658
97271

            
659
97271
        Ok(Transform::new_translate(tx, ty))
660
97271
    })
661
97271
}
662

            
663
6888
fn parse_scale_args<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
664
6888
    parser.parse_nested_block(|p| {
665
6888
        let x = f64::parse(p)?;
666

            
667
6888
        let y = p
668
6888
            .try_parse(|p| {
669
6888
                optional_comma(p);
670
6888
                f64::parse(p)
671
6888
            })
672
6888
            .unwrap_or(x);
673
6888

            
674
6888
        Ok(Transform::new_scale(x, y))
675
6888
    })
676
6888
}
677

            
678
2438
fn parse_rotate_args<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
679
2438
    parser.parse_nested_block(|p| {
680
2438
        let angle = Angle::from_degrees(f64::parse(p)?);
681

            
682
2438
        let (tx, ty) = p
683
2438
            .try_parse(|p| -> Result<_, ParseError<'_>> {
684
2438
                optional_comma(p);
685
2438
                let tx = f64::parse(p)?;
686

            
687
461
                optional_comma(p);
688
461
                let ty = f64::parse(p)?;
689

            
690
461
                Ok((tx, ty))
691
2438
            })
692
2438
            .unwrap_or((0.0, 0.0));
693
2438

            
694
2438
        Ok(Transform::new_translate(tx, ty)
695
2438
            .pre_rotate(angle)
696
2438
            .pre_translate(-tx, -ty))
697
2438
    })
698
2438
}
699

            
700
116
fn parse_skew_x_args<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
701
116
    parser.parse_nested_block(|p| {
702
116
        let angle = Angle::from_degrees(f64::parse(p)?);
703
116
        Ok(Transform::new_skew(angle, Angle::new(0.0)))
704
116
    })
705
116
}
706

            
707
78
fn parse_skew_y_args<'i>(parser: &mut Parser<'i, '_>) -> Result<Transform, ParseError<'i>> {
708
78
    parser.parse_nested_block(|p| {
709
78
        let angle = Angle::from_degrees(f64::parse(p)?);
710
77
        Ok(Transform::new_skew(Angle::new(0.0), angle))
711
78
    })
712
78
}
713

            
714
#[cfg(test)]
715
mod tests {
716
    use super::*;
717
    use float_cmp::ApproxEq;
718
    use std::f64;
719

            
720
5
    fn rotation_transform(deg: f64, tx: f64, ty: f64) -> Transform {
721
5
        Transform::new_translate(tx, ty)
722
5
            .pre_rotate(Angle::from_degrees(deg))
723
5
            .pre_translate(-tx, -ty)
724
5
    }
725

            
726
27
    fn parse_transform(s: &str) -> Result<Transform, ParseError<'_>> {
727
27
        let transform_attr = TransformAttribute::parse_str(s)?;
728
19
        Ok(transform_attr.to_transform())
729
27
    }
730

            
731
63
    fn parse_transform_prop(s: &str) -> Result<TransformProperty, ParseError<'_>> {
732
63
        TransformProperty::parse_str(s)
733
63
    }
734

            
735
28
    fn assert_transform_eq(t1: &Transform, t2: &Transform) {
736
28
        let epsilon = 8.0 * f64::EPSILON; // kind of arbitrary, but allow for some sloppiness
737
28

            
738
28
        assert!(t1.xx.approx_eq(t2.xx, (epsilon, 1)));
739
28
        assert!(t1.yx.approx_eq(t2.yx, (epsilon, 1)));
740
28
        assert!(t1.xy.approx_eq(t2.xy, (epsilon, 1)));
741
28
        assert!(t1.yy.approx_eq(t2.yy, (epsilon, 1)));
742
28
        assert!(t1.x0.approx_eq(t2.x0, (epsilon, 1)));
743
28
        assert!(t1.y0.approx_eq(t2.y0, (epsilon, 1)));
744
28
    }
745

            
746
    #[test]
747
1
    fn test_multiply() {
748
1
        let t1 = Transform::identity();
749
1
        let t2 = Transform::new_unchecked(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
750
1
        assert_transform_eq(&Transform::multiply(&t1, &t2), &t2);
751
1
        assert_transform_eq(&Transform::multiply(&t2, &t1), &t2);
752
1

            
753
1
        let t1 = Transform::new_unchecked(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
754
1
        let t2 = Transform::new_unchecked(0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
755
1
        let r = Transform::new_unchecked(0.0, 0.0, 0.0, 0.0, 5.0, 6.0);
756
1
        assert_transform_eq(&Transform::multiply(&t1, &t2), &t2);
757
1
        assert_transform_eq(&Transform::multiply(&t2, &t1), &r);
758
1

            
759
1
        let t1 = Transform::new_unchecked(0.5, 0.0, 0.0, 0.5, 10.0, 10.0);
760
1
        let t2 = Transform::new_unchecked(1.0, 0.0, 0.0, 1.0, -10.0, -10.0);
761
1
        let r1 = Transform::new_unchecked(0.5, 0.0, 0.0, 0.5, 0.0, 0.0);
762
1
        let r2 = Transform::new_unchecked(0.5, 0.0, 0.0, 0.5, 5.0, 5.0);
763
1
        assert_transform_eq(&Transform::multiply(&t1, &t2), &r1);
764
1
        assert_transform_eq(&Transform::multiply(&t2, &t1), &r2);
765
1
    }
766

            
767
    #[test]
768
1
    fn test_invert() {
769
1
        let t = Transform::new_unchecked(2.0, 0.0, 0.0, 0.0, 0.0, 0.0);
770
1
        assert!(!t.is_invertible());
771
1
        assert!(t.invert().is_none());
772

            
773
1
        let t = Transform::identity();
774
1
        assert!(t.is_invertible());
775
1
        assert!(t.invert().is_some());
776
1
        let i = t.invert().unwrap();
777
1
        assert_transform_eq(&i, &Transform::identity());
778
1

            
779
1
        let t = Transform::new_unchecked(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
780
1
        assert!(t.is_invertible());
781
1
        assert!(t.invert().is_some());
782
1
        let i = t.invert().unwrap();
783
1
        assert_transform_eq(&t.pre_transform(&i), &Transform::identity());
784
1
        assert_transform_eq(&t.post_transform(&i), &Transform::identity());
785
1
    }
786

            
787
    #[test]
788
1
    pub fn test_transform_point() {
789
1
        let t = Transform::new_translate(10.0, 10.0);
790
1
        assert_eq!((11.0, 11.0), t.transform_point(1.0, 1.0));
791
1
    }
792

            
793
    #[test]
794
1
    pub fn test_transform_distance() {
795
1
        let t = Transform::new_translate(10.0, 10.0).pre_scale(2.0, 1.0);
796
1
        assert_eq!((2.0, 1.0), t.transform_distance(1.0, 1.0));
797
1
    }
798

            
799
    #[test]
800
1
    fn parses_valid_transform() {
801
1
        let t = Transform::new_unchecked(1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
802
1
        let s = Transform::new_unchecked(10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
803
1
        let r = rotation_transform(30.0, 10.0, 10.0);
804
1

            
805
1
        let a = Transform::multiply(&s, &t);
806
1
        assert_transform_eq(
807
1
            &parse_transform("translate(20, 30), scale (10) rotate (30 10 10)").unwrap(),
808
1
            &Transform::multiply(&r, &a),
809
1
        );
810
1
    }
811

            
812
8
    fn assert_parse_error(s: &str) {
813
8
        assert!(parse_transform(s).is_err());
814
8
    }
815

            
816
    #[test]
817
1
    fn syntax_error_yields_parse_error() {
818
1
        assert_parse_error("foo");
819
1
        assert_parse_error("matrix (1 2 3 4 5)");
820
1
        assert_parse_error("translate(1 2 3 4 5)");
821
1
        assert_parse_error("translate (1,)");
822
1
        assert_parse_error("scale (1,)");
823
1
        assert_parse_error("skewX (1,2)");
824
1
        assert_parse_error("skewY ()");
825
1
        assert_parse_error("skewY");
826
1
    }
827

            
828
    #[test]
829
1
    fn parses_matrix() {
830
1
        assert_transform_eq(
831
1
            &parse_transform("matrix (1 2 3 4 5 6)").unwrap(),
832
1
            &Transform::new_unchecked(1.0, 2.0, 3.0, 4.0, 5.0, 6.0),
833
1
        );
834
1

            
835
1
        assert_transform_eq(
836
1
            &parse_transform("matrix(1,2,3,4 5 6)").unwrap(),
837
1
            &Transform::new_unchecked(1.0, 2.0, 3.0, 4.0, 5.0, 6.0),
838
1
        );
839
1

            
840
1
        assert_transform_eq(
841
1
            &parse_transform("matrix (1,2.25,-3.25e2,4 5 6)").unwrap(),
842
1
            &Transform::new_unchecked(1.0, 2.25, -325.0, 4.0, 5.0, 6.0),
843
1
        );
844
1
    }
845

            
846
    #[test]
847
1
    fn parses_translate() {
848
1
        assert_transform_eq(
849
1
            &parse_transform("translate(-1 -2)").unwrap(),
850
1
            &Transform::new_unchecked(1.0, 0.0, 0.0, 1.0, -1.0, -2.0),
851
1
        );
852
1

            
853
1
        assert_transform_eq(
854
1
            &parse_transform("translate(-1, -2)").unwrap(),
855
1
            &Transform::new_unchecked(1.0, 0.0, 0.0, 1.0, -1.0, -2.0),
856
1
        );
857
1

            
858
1
        assert_transform_eq(
859
1
            &parse_transform("translate(-1)").unwrap(),
860
1
            &Transform::new_unchecked(1.0, 0.0, 0.0, 1.0, -1.0, 0.0),
861
1
        );
862
1
    }
863

            
864
    #[test]
865
1
    fn parses_scale() {
866
1
        assert_transform_eq(
867
1
            &parse_transform("scale (-1)").unwrap(),
868
1
            &Transform::new_unchecked(-1.0, 0.0, 0.0, -1.0, 0.0, 0.0),
869
1
        );
870
1

            
871
1
        assert_transform_eq(
872
1
            &parse_transform("scale(-1 -2)").unwrap(),
873
1
            &Transform::new_unchecked(-1.0, 0.0, 0.0, -2.0, 0.0, 0.0),
874
1
        );
875
1

            
876
1
        assert_transform_eq(
877
1
            &parse_transform("scale(-1, -2)").unwrap(),
878
1
            &Transform::new_unchecked(-1.0, 0.0, 0.0, -2.0, 0.0, 0.0),
879
1
        );
880
1
    }
881

            
882
    #[test]
883
1
    fn parses_rotate() {
884
1
        assert_transform_eq(
885
1
            &parse_transform("rotate (30)").unwrap(),
886
1
            &rotation_transform(30.0, 0.0, 0.0),
887
1
        );
888
1
        assert_transform_eq(
889
1
            &parse_transform("rotate (30,-1,-2)").unwrap(),
890
1
            &rotation_transform(30.0, -1.0, -2.0),
891
1
        );
892
1
        assert_transform_eq(
893
1
            &parse_transform("rotate(30, -1, -2)").unwrap(),
894
1
            &rotation_transform(30.0, -1.0, -2.0),
895
1
        );
896
1
    }
897

            
898
    #[test]
899
1
    fn parses_skew_x() {
900
1
        assert_transform_eq(
901
1
            &parse_transform("skewX (30)").unwrap(),
902
1
            &Transform::new_skew(Angle::from_degrees(30.0), Angle::new(0.0)),
903
1
        );
904
1
    }
905

            
906
    #[test]
907
1
    fn parses_skew_y() {
908
1
        assert_transform_eq(
909
1
            &parse_transform("skewY (30)").unwrap(),
910
1
            &Transform::new_skew(Angle::new(0.0), Angle::from_degrees(30.0)),
911
1
        );
912
1
    }
913

            
914
    #[test]
915
1
    fn parses_transform_list() {
916
1
        let t = Transform::new_unchecked(1.0, 0.0, 0.0, 1.0, 20.0, 30.0);
917
1
        let s = Transform::new_unchecked(10.0, 0.0, 0.0, 10.0, 0.0, 0.0);
918
1
        let r = rotation_transform(30.0, 10.0, 10.0);
919
1

            
920
1
        assert_transform_eq(
921
1
            &parse_transform("scale(10)rotate(30, 10, 10)").unwrap(),
922
1
            &Transform::multiply(&r, &s),
923
1
        );
924
1

            
925
1
        assert_transform_eq(
926
1
            &parse_transform("translate(20, 30), scale (10)").unwrap(),
927
1
            &Transform::multiply(&s, &t),
928
1
        );
929
1

            
930
1
        let a = Transform::multiply(&s, &t);
931
1
        assert_transform_eq(
932
1
            &parse_transform("translate(20, 30), scale (10) rotate (30 10 10)").unwrap(),
933
1
            &Transform::multiply(&r, &a),
934
1
        );
935
1
    }
936

            
937
    #[test]
938
1
    fn parses_empty() {
939
1
        assert_transform_eq(&parse_transform("").unwrap(), &Transform::identity());
940
1
    }
941

            
942
    #[test]
943
1
    fn test_parse_transform_property_none() {
944
1
        assert_eq!(
945
1
            parse_transform_prop("none").unwrap(),
946
1
            TransformProperty::None
947
1
        );
948
1
    }
949

            
950
    #[test]
951
1
    fn none_transform_is_identity() {
952
1
        assert_eq!(
953
1
            parse_transform_prop("none").unwrap().to_transform(),
954
1
            Transform::identity()
955
1
        );
956
1
    }
957

            
958
    #[test]
959
1
    fn empty_transform_property_is_error() {
960
1
        // https://www.w3.org/TR/css-transforms-1/#transform-property
961
1
        //
962
1
        // <transform-list> = <transform-function>+
963
1
        //                                        ^ one or more required
964
1
        assert!(parse_transform_prop("").is_err());
965
1
    }
966

            
967
    #[test]
968
1
    fn test_parse_transform_property_matrix() {
969
1
        let tp = TransformProperty::List(vec![TransformFunction::Matrix(
970
1
            Transform::new_unchecked(1.0, 2.0, 3.0, 4.0, 5.0, 6.0),
971
1
        )]);
972
1

            
973
1
        assert_eq!(&tp, &parse_transform_prop("matrix(1,2,3,4,5,6)").unwrap());
974
1
        assert!(parse_transform_prop("matrix(1 2 3 4 5 6)").is_err());
975
1
        assert!(parse_transform_prop("Matrix(1,2,3,4,5,6)").is_err());
976
1
    }
977

            
978
    #[test]
979
1
    fn test_parse_transform_property_translate() {
980
1
        let tpt = TransformProperty::List(vec![TransformFunction::Translate(
981
1
            Length::<Horizontal>::new(100.0, LengthUnit::Px),
982
1
            Length::<Vertical>::new(200.0, LengthUnit::Px),
983
1
        )]);
984
1

            
985
1
        assert_eq!(
986
1
            &tpt,
987
1
            &parse_transform_prop("translate(100px,200px)").unwrap()
988
1
        );
989

            
990
1
        assert_eq!(
991
1
            parse_transform_prop("translate(1)").unwrap(),
992
1
            parse_transform_prop("translate(1, 0)").unwrap()
993
1
        );
994

            
995
1
        assert!(parse_transform_prop("translate(100, foo)").is_err());
996
1
        assert!(parse_transform_prop("translate(100, )").is_err());
997
1
        assert!(parse_transform_prop("translate(100 200)").is_err());
998
1
        assert!(parse_transform_prop("translate(1px,2px,3px,4px)").is_err());
999
1
    }
    #[test]
1
    fn test_parse_transform_property_translate_x() {
1
        let tptx = TransformProperty::List(vec![TransformFunction::TranslateX(
1
            Length::<Horizontal>::new(100.0, LengthUnit::Px),
1
        )]);
1

            
1
        assert_eq!(&tptx, &parse_transform_prop("translateX(100px)").unwrap());
1
        assert!(parse_transform_prop("translateX(1)").is_ok());
1
        assert!(parse_transform_prop("translateX(100 100)").is_err());
1
        assert!(parse_transform_prop("translatex(1px)").is_err());
1
        assert!(parse_transform_prop("translatex(1rad)").is_err());
1
    }
    #[test]
1
    fn test_parse_transform_property_translate_y() {
1
        let tpty = TransformProperty::List(vec![TransformFunction::TranslateY(
1
            Length::<Vertical>::new(100.0, LengthUnit::Px),
1
        )]);
1

            
1
        assert_eq!(&tpty, &parse_transform_prop("translateY(100px)").unwrap());
1
        assert!(parse_transform_prop("translateY(1)").is_ok());
1
        assert!(parse_transform_prop("translateY(100 100)").is_err());
1
        assert!(parse_transform_prop("translatey(1px)").is_err());
1
    }
    #[test]
1
    fn test_translate_only_supports_pixel_units() {
1
        assert!(parse_transform_prop("translate(1in, 2)").is_err());
1
        assert!(parse_transform_prop("translate(1, 2in)").is_err());
1
        assert!(parse_transform_prop("translateX(1cm)").is_err());
1
        assert!(parse_transform_prop("translateY(1cm)").is_err());
1
    }
    #[test]
1
    fn test_parse_transform_property_scale() {
1
        let tps = TransformProperty::List(vec![TransformFunction::Scale(1.0, 10.0)]);
1

            
1
        assert_eq!(&tps, &parse_transform_prop("scale(1,10)").unwrap());
1
        assert_eq!(
1
            parse_transform_prop("scale(2)").unwrap(),
1
            parse_transform_prop("scale(2, 2)").unwrap()
1
        );
1
        assert!(parse_transform_prop("scale(100, foo)").is_err());
1
        assert!(parse_transform_prop("scale(100, )").is_err());
1
        assert!(parse_transform_prop("scale(1 10)").is_err());
1
        assert!(parse_transform_prop("scale(1px,10px)").is_err());
1
        assert!(parse_transform_prop("scale(1%)").is_err());
1
    }
    #[test]
1
    fn test_parse_transform_property_scale_x() {
1
        let tpsx = TransformProperty::List(vec![TransformFunction::ScaleX(10.0)]);
1

            
1
        assert_eq!(&tpsx, &parse_transform_prop("scaleX(10)").unwrap());
1
        assert!(parse_transform_prop("scaleX(100 100)").is_err());
1
        assert!(parse_transform_prop("scalex(10)").is_err());
1
        assert!(parse_transform_prop("scaleX(10px)").is_err());
1
    }
    #[test]
1
    fn test_parse_transform_property_scale_y() {
1
        let tpsy = TransformProperty::List(vec![TransformFunction::ScaleY(10.0)]);
1

            
1
        assert_eq!(&tpsy, &parse_transform_prop("scaleY(10)").unwrap());
1
        assert!(parse_transform_prop("scaleY(10 1)").is_err());
1
        assert!(parse_transform_prop("scaleY(1px)").is_err());
1
    }
    #[test]
1
    fn test_parse_transform_property_rotate() {
1
        let tpr =
1
            TransformProperty::List(vec![TransformFunction::Rotate(Angle::from_degrees(100.0))]);
1
        assert_eq!(&tpr, &parse_transform_prop("rotate(100deg)").unwrap());
1
        assert!(parse_transform_prop("rotate(100deg 100)").is_err());
1
        assert!(parse_transform_prop("rotate(3px)").is_err());
1
    }
    #[test]
1
    fn test_parse_transform_property_skew() {
1
        let tpsk = TransformProperty::List(vec![TransformFunction::Skew(
1
            Angle::from_degrees(90.0),
1
            Angle::from_degrees(120.0),
1
        )]);
1

            
1
        assert_eq!(&tpsk, &parse_transform_prop("skew(90deg,120deg)").unwrap());
1
        assert_eq!(
1
            parse_transform_prop("skew(45deg)").unwrap(),
1
            parse_transform_prop("skew(45deg, 0)").unwrap()
1
        );
1
        assert!(parse_transform_prop("skew(1.0,1.0)").is_ok());
1
        assert!(parse_transform_prop("skew(1rad,1rad)").is_ok());
1
        assert!(parse_transform_prop("skew(100, foo)").is_err());
1
        assert!(parse_transform_prop("skew(100, )").is_err());
1
        assert!(parse_transform_prop("skew(1.0px)").is_err());
1
        assert!(parse_transform_prop("skew(1.0,1.0,1deg)").is_err());
1
    }
    #[test]
1
    fn test_parse_transform_property_skew_x() {
1
        let tpskx =
1
            TransformProperty::List(vec![TransformFunction::SkewX(Angle::from_degrees(90.0))]);
1

            
1
        assert_eq!(&tpskx, &parse_transform_prop("skewX(90deg)").unwrap());
1
        assert!(parse_transform_prop("skewX(1.0)").is_ok());
1
        assert!(parse_transform_prop("skewX(1rad)").is_ok());
1
        assert!(parse_transform_prop("skewx(1.0)").is_err());
1
        assert!(parse_transform_prop("skewX(1.0,1.0)").is_err());
1
    }
    #[test]
1
    fn test_parse_transform_property_skew_y() {
1
        let tpsky =
1
            TransformProperty::List(vec![TransformFunction::SkewY(Angle::from_degrees(90.0))]);
1

            
1
        assert_eq!(&tpsky, &parse_transform_prop("skewY(90deg)").unwrap());
1
        assert!(parse_transform_prop("skewY(1.0)").is_ok());
1
        assert!(parse_transform_prop("skewY(1rad)").is_ok());
1
        assert!(parse_transform_prop("skewy(1.0)").is_err());
1
        assert!(parse_transform_prop("skewY(1.0,1.0)").is_err());
1
    }
}