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
2
#[derive(Debug, Copy, Clone, PartialEq)]
31
1
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
12052292
    fn try_from(t: Transform) -> Result<ValidTransform, InvalidTransform> {
41
12052292
        if t.is_invertible() {
42
12052287
            Ok(ValidTransform(t))
43
        } else {
44
5
            Err(InvalidTransform)
45
        }
46
12052292
    }
47
}
48

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

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

            
57
/// A 2D transformation matrix.
58
1543828
#[derive(Debug, Copy, Clone, PartialEq)]
59
pub struct Transform {
60
3
    pub xx: f64,
61
3
    pub yx: f64,
62
3
    pub xy: f64,
63
3
    pub yy: f64,
64
3
    pub x0: f64,
65
3
    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
9397218
#[derive(Debug, Default, Clone, PartialEq)]
72
pub enum TransformProperty {
73
    #[default]
74
2485890
    None,
75
26
    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
500406
#[derive(Copy, Clone, Default, Debug, PartialEq)]
82
202
pub struct TransformAttribute(Transform);
83

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

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

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

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

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

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

            
111
4
                    let transform_matrix = match f {
112
                        Matrix(trans_matrix) => *trans_matrix,
113
2
                        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
1
                        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
1
                        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
4
                    final_transform = transform_matrix.post_transform(&final_transform);
125
                }
126

            
127
2
                final_transform
128
2
            }
129
        }
130
3
    }
131
}
132

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

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

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

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

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

            
172
29
        if parser.is_exhausted() {
173
            break;
174
        }
175
    }
176

            
177
25
    Ok(v)
178
61
}
179

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

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

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

            
197
    match name {
198
64
        "matrix" => parse_prop_matrix_args(parser),
199
62
        "translate" => parse_prop_translate_args(parser),
200
51
        "translateX" => parse_prop_translate_x_args(parser),
201
47
        "translateY" => parse_prop_translate_y_args(parser),
202
43
        "scale" => parse_prop_scale_args(parser),
203
34
        "scaleX" => parse_prop_scale_x_args(parser),
204
31
        "scaleY" => parse_prop_scale_y_args(parser),
205
28
        "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
            "expected matrix|translate|translateX|translateY|scale|scaleX|scaleY|rotate|skewX|skewY",
211
7
        ))),
212
    }
213
64
}
214

            
215
2
fn parse_prop_matrix_args<'i>(
216
    parser: &mut Parser<'i, '_>,
217
) -> Result<TransformFunction, ParseError<'i>> {
218
4
    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
3
        p.expect_comma()?;
229
1
        let y0 = f64::parse(p)?;
230

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

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

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

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

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

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

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

            
265
7
        Ok(TransformFunction::Translate(tx, ty))
266
11
    })
267
11
}
268

            
269
4
fn parse_prop_translate_x_args<'i>(
270
    parser: &mut Parser<'i, '_>,
271
) -> Result<TransformFunction, ParseError<'i>> {
272
8
    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
        }
280

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

            
285
4
fn parse_prop_translate_y_args<'i>(
286
    parser: &mut Parser<'i, '_>,
287
) -> Result<TransformFunction, ParseError<'i>> {
288
8
    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
        }
296

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

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

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

            
313
5
        Ok(TransformFunction::Scale(x, y))
314
9
    })
315
9
}
316

            
317
3
fn parse_prop_scale_x_args<'i>(
318
    parser: &mut Parser<'i, '_>,
319
) -> Result<TransformFunction, ParseError<'i>> {
320
6
    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
    parser: &mut Parser<'i, '_>,
329
) -> Result<TransformFunction, ParseError<'i>> {
330
6
    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
4
fn parse_prop_rotate_args<'i>(
338
    parser: &mut Parser<'i, '_>,
339
) -> Result<TransformFunction, ParseError<'i>> {
340
8
    parser.parse_nested_block(|p| {
341
4
        let angle = Angle::parse(p)?;
342

            
343
3
        Ok(TransformFunction::Rotate(angle))
344
4
    })
345
4
}
346

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

            
353
16
        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
    parser: &mut Parser<'i, '_>,
365
) -> Result<TransformFunction, ParseError<'i>> {
366
8
    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
    parser: &mut Parser<'i, '_>,
374
) -> Result<TransformFunction, ParseError<'i>> {
375
8
    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
20890549
    pub fn new_unchecked(xx: f64, yx: f64, xy: f64, yy: f64, x0: f64, y0: f64) -> Self {
384
20890549
        Self {
385
            xx,
386
            yx,
387
            xy,
388
            yy,
389
            x0,
390
            y0,
391
        }
392
20890549
    }
393

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

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

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

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

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

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

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

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

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

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

            
453
    #[inline]
454
348
    pub fn pre_rotate(&self, angle: Angle) -> Self {
455
348
        self.pre_transform(&Transform::new_rotate(angle))
456
348
    }
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
50651
    pub fn post_scale(&self, sx: f64, sy: f64) -> Self {
465
50651
        self.post_transform(&Transform::new_scale(sx, sy))
466
50651
    }
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
14067841
    fn determinant(&self) -> f64 {
475
14067841
        self.xx * self.yy - self.xy * self.yx
476
14067841
    }
477

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

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

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

            
489
2005411
        if det == 0.0 || !det.is_finite() {
490
2
            return None;
491
        }
492

            
493
2004905
        let inv_det = 1.0 / det;
494

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

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

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

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

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

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

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

            
535
7384709
            if x > xmax {
536
1130754
                xmax = x;
537
            }
538

            
539
7384709
            if y < ymin {
540
456
                ymin = y;
541
            }
542

            
543
7384709
            if y > ymax {
544
1137304
                ymax = y;
545
            }
546
        }
547

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

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

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

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

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

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

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

            
588
5710
    Ok(t)
589
5718
}
590

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

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

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

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

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

            
612
    match name {
613
6119
        "matrix" => parse_matrix_args(parser),
614
5734
        "translate" => parse_translate_args(parser),
615
551
        "scale" => parse_scale_args(parser),
616
163
        "rotate" => parse_rotate_args(parser),
617
14
        "skewX" => parse_skew_x_args(parser),
618
6
        "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
6119
}
624

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
690
29
                Ok((tx, ty))
691
149
            })
692
149
            .unwrap_or((0.0, 0.0));
693

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

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

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

            
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
2
    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

            
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

            
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
2
    }
766

            
767
    #[test]
768
2
    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

            
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
2
    }
786

            
787
    #[test]
788
2
    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
2
    }
792

            
793
    #[test]
794
2
    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
2
    }
798

            
799
    #[test]
800
2
    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

            
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
        );
810
2
    }
811

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

            
816
    #[test]
817
2
    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
2
    }
827

            
828
    #[test]
829
2
    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
        );
834

            
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
        );
839

            
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
        );
844
2
    }
845

            
846
    #[test]
847
2
    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
        );
852

            
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
        );
857

            
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
        );
862
2
    }
863

            
864
    #[test]
865
2
    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
        );
870

            
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
        );
875

            
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
        );
880
2
    }
881

            
882
    #[test]
883
2
    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
        );
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
        );
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
        );
896
2
    }
897

            
898
    #[test]
899
2
    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
        );
904
2
    }
905

            
906
    #[test]
907
2
    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
        );
912
2
    }
913

            
914
    #[test]
915
2
    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

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

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

            
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
        );
935
2
    }
936

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

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

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

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

            
967
    #[test]
968
2
    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
        )]);
972

            
973
2
        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
2
    }
977

            
978
    #[test]
979
2
    fn test_parse_transform_property_translate() {
980
2
        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
        )]);
984

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

            
990
2
        assert_eq!(
991
1
            parse_transform_prop("translate(1)").unwrap(),
992
1
            parse_transform_prop("translate(1, 0)").unwrap()
993
        );
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
2
    }
    #[test]
2
    fn test_parse_transform_property_translate_x() {
1
        let tptx = TransformProperty::List(vec![TransformFunction::TranslateX(
1
            Length::<Horizontal>::new(100.0, LengthUnit::Px),
        )]);
2
        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());
2
    }
    #[test]
2
    fn test_parse_transform_property_translate_y() {
1
        let tpty = TransformProperty::List(vec![TransformFunction::TranslateY(
1
            Length::<Vertical>::new(100.0, LengthUnit::Px),
        )]);
2
        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());
2
    }
    #[test]
2
    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());
2
    }
    #[test]
2
    fn test_parse_transform_property_scale() {
1
        let tps = TransformProperty::List(vec![TransformFunction::Scale(1.0, 10.0)]);
2
        assert_eq!(&tps, &parse_transform_prop("scale(1,10)").unwrap());
2
        assert_eq!(
1
            parse_transform_prop("scale(2)").unwrap(),
1
            parse_transform_prop("scale(2, 2)").unwrap()
        );
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());
2
    }
    #[test]
2
    fn test_parse_transform_property_scale_x() {
1
        let tpsx = TransformProperty::List(vec![TransformFunction::ScaleX(10.0)]);
2
        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());
2
    }
    #[test]
2
    fn test_parse_transform_property_scale_y() {
1
        let tpsy = TransformProperty::List(vec![TransformFunction::ScaleY(10.0)]);
2
        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());
2
    }
    #[test]
2
    fn test_parse_transform_property_rotate() {
        let tpr =
1
            TransformProperty::List(vec![TransformFunction::Rotate(Angle::from_degrees(100.0))]);
2
        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());
2
    }
    #[test]
2
    fn test_parse_transform_property_skew() {
2
        let tpsk = TransformProperty::List(vec![TransformFunction::Skew(
1
            Angle::from_degrees(90.0),
1
            Angle::from_degrees(120.0),
        )]);
2
        assert_eq!(&tpsk, &parse_transform_prop("skew(90deg,120deg)").unwrap());
2
        assert_eq!(
1
            parse_transform_prop("skew(45deg)").unwrap(),
1
            parse_transform_prop("skew(45deg, 0)").unwrap()
        );
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());
2
    }
    #[test]
2
    fn test_parse_transform_property_skew_x() {
        let tpskx =
1
            TransformProperty::List(vec![TransformFunction::SkewX(Angle::from_degrees(90.0))]);
2
        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());
2
    }
    #[test]
2
    fn test_parse_transform_property_skew_y() {
        let tpsky =
1
            TransformProperty::List(vec![TransformFunction::SkewY(Angle::from_degrees(90.0))]);
2
        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());
2
    }
}