1use super::{Property, PropertyId};
4use crate::context::PropertyHandlerContext;
5use crate::declaration::DeclarationList;
6use crate::error::{ParserError, PrinterError};
7use crate::macros::enum_property;
8use crate::prefixes::Feature;
9use crate::printer::Printer;
10use crate::stylesheet::PrinterOptions;
11use crate::traits::{Parse, PropertyHandler, ToCss, Zero};
12use crate::values::{
13 angle::Angle,
14 length::{Length, LengthPercentage},
15 percentage::NumberOrPercentage,
16};
17use crate::vendor_prefix::VendorPrefix;
18#[cfg(feature = "visitor")]
19use crate::visitor::Visit;
20use cssparser::*;
21use std::f32::consts::PI;
22
23#[derive(Debug, Clone, PartialEq, Default)]
25#[cfg_attr(feature = "visitor", derive(Visit))]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(transparent))]
27#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
28#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
29pub struct TransformList(pub Vec<Transform>);
30
31impl<'i> Parse<'i> for TransformList {
32 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
33 if input.try_parse(|input| input.expect_ident_matching("none")).is_ok() {
34 return Ok(TransformList(vec![]));
35 }
36
37 input.skip_whitespace();
38 let mut results = vec![Transform::parse(input)?];
39 loop {
40 input.skip_whitespace();
41 if let Ok(item) = input.try_parse(Transform::parse) {
42 results.push(item);
43 } else {
44 return Ok(TransformList(results));
45 }
46 }
47 }
48}
49
50impl ToCss for TransformList {
51 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
52 where
53 W: std::fmt::Write,
54 {
55 if self.0.is_empty() {
56 dest.write_str("none")?;
57 return Ok(());
58 }
59
60 if dest.minify {
63 let mut base = String::new();
64 self.to_css_base(&mut Printer::new(
65 &mut base,
66 PrinterOptions {
67 minify: true,
68 ..PrinterOptions::default()
69 },
70 ))?;
71
72 dest.write_str(&base)?;
73
74 return Ok(());
75 }
76 self.to_css_base(dest)
136 }
137}
138
139impl TransformList {
140 fn to_css_base<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
141 where
142 W: std::fmt::Write,
143 {
144 for item in &self.0 {
145 item.to_css(dest)?;
146 }
147 Ok(())
148 }
149
150 pub fn to_matrix(&self) -> Option<Matrix3d<f32>> {
152 let mut matrix = Matrix3d::identity();
153 for transform in &self.0 {
154 if let Some(m) = transform.to_matrix() {
155 matrix = m.multiply(&matrix);
156 } else {
157 return None;
158 }
159 }
160 Some(matrix)
161 }
162}
163
164#[derive(Debug, Clone, PartialEq)]
166#[cfg_attr(feature = "visitor", derive(Visit))]
167#[cfg_attr(
168 feature = "serde",
169 derive(serde::Serialize, serde::Deserialize),
170 serde(tag = "type", content = "value", rename_all = "camelCase")
171)]
172#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
173#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
174pub enum Transform {
175 Translate(LengthPercentage, LengthPercentage),
177 TranslateX(LengthPercentage),
179 TranslateY(LengthPercentage),
181 TranslateZ(Length),
183 Translate3d(LengthPercentage, LengthPercentage, Length),
185 Scale(NumberOrPercentage, NumberOrPercentage),
187 ScaleX(NumberOrPercentage),
189 ScaleY(NumberOrPercentage),
191 ScaleZ(NumberOrPercentage),
193 Scale3d(NumberOrPercentage, NumberOrPercentage, NumberOrPercentage),
195 Rotate(Angle),
197 RotateX(Angle),
199 RotateY(Angle),
201 RotateZ(Angle),
203 Rotate3d(f32, f32, f32, Angle),
205 Skew(Angle, Angle),
207 SkewX(Angle),
209 SkewY(Angle),
211 Perspective(Length),
213 Matrix(Matrix<f32>),
215 Matrix3d(Matrix3d<f32>),
217}
218
219#[derive(Debug, Clone, PartialEq)]
221#[cfg_attr(feature = "visitor", derive(Visit))]
222#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
223#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
224#[allow(missing_docs)]
225#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
226pub struct Matrix<T> {
227 pub a: T,
228 pub b: T,
229 pub c: T,
230 pub d: T,
231 pub e: T,
232 pub f: T,
233}
234
235impl Matrix<f32> {
236 pub fn to_matrix3d(&self) -> Matrix3d<f32> {
238 Matrix3d {
239 m11: self.a,
240 m12: self.b,
241 m13: 0.0,
242 m14: 0.0,
243 m21: self.c,
244 m22: self.d,
245 m23: 0.0,
246 m24: 0.0,
247 m31: 0.0,
248 m32: 0.0,
249 m33: 1.0,
250 m34: 0.0,
251 m41: self.e,
252 m42: self.f,
253 m43: 0.0,
254 m44: 1.0,
255 }
256 }
257}
258
259#[derive(Debug, Clone, PartialEq)]
261#[cfg_attr(feature = "visitor", derive(Visit))]
262#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
263#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
264#[allow(missing_docs)]
265pub struct Matrix3d<T> {
266 pub m11: T,
267 pub m12: T,
268 pub m13: T,
269 pub m14: T,
270 pub m21: T,
271 pub m22: T,
272 pub m23: T,
273 pub m24: T,
274 pub m31: T,
275 pub m32: T,
276 pub m33: T,
277 pub m34: T,
278 pub m41: T,
279 pub m42: T,
280 pub m43: T,
281 pub m44: T,
282}
283
284impl Matrix3d<f32> {
286 pub fn identity() -> Matrix3d<f32> {
288 Matrix3d {
289 m11: 1.0,
290 m12: 0.0,
291 m13: 0.0,
292 m14: 0.0,
293 m21: 0.0,
294 m22: 1.0,
295 m23: 0.0,
296 m24: 0.0,
297 m31: 0.0,
298 m32: 0.0,
299 m33: 1.0,
300 m34: 0.0,
301 m41: 0.0,
302 m42: 0.0,
303 m43: 0.0,
304 m44: 1.0,
305 }
306 }
307
308 pub fn translate(x: f32, y: f32, z: f32) -> Matrix3d<f32> {
310 Matrix3d {
311 m11: 1.0,
312 m12: 0.0,
313 m13: 0.0,
314 m14: 0.0,
315 m21: 0.0,
316 m22: 1.0,
317 m23: 0.0,
318 m24: 0.0,
319 m31: 0.0,
320 m32: 0.0,
321 m33: 1.0,
322 m34: 0.0,
323 m41: x,
324 m42: y,
325 m43: z,
326 m44: 1.0,
327 }
328 }
329
330 pub fn scale(x: f32, y: f32, z: f32) -> Matrix3d<f32> {
332 Matrix3d {
333 m11: x,
334 m12: 0.0,
335 m13: 0.0,
336 m14: 0.0,
337 m21: 0.0,
338 m22: y,
339 m23: 0.0,
340 m24: 0.0,
341 m31: 0.0,
342 m32: 0.0,
343 m33: z,
344 m34: 0.0,
345 m41: 0.0,
346 m42: 0.0,
347 m43: 0.0,
348 m44: 1.0,
349 }
350 }
351
352 pub fn rotate(x: f32, y: f32, z: f32, angle: f32) -> Matrix3d<f32> {
354 let length = (x * x + y * y + z * z).sqrt();
356 if length == 0.0 {
357 return Matrix3d::identity();
359 }
360
361 let x = x / length;
362 let y = y / length;
363 let z = z / length;
364
365 let half_angle = angle / 2.0;
366 let sin = half_angle.sin();
367 let sc = sin * half_angle.cos();
368 let sq = sin * sin;
369 let m11 = 1.0 - 2.0 * (y * y + z * z) * sq;
370 let m12 = 2.0 * (x * y * sq + z * sc);
371 let m13 = 2.0 * (x * z * sq - y * sc);
372 let m21 = 2.0 * (x * y * sq - z * sc);
373 let m22 = 1.0 - 2.0 * (x * x + z * z) * sq;
374 let m23 = 2.0 * (y * z * sq + x * sc);
375 let m31 = 2.0 * (x * z * sq + y * sc);
376 let m32 = 2.0 * (y * z * sq - x * sc);
377 let m33 = 1.0 - 2.0 * (x * x + y * y) * sq;
378 Matrix3d {
379 m11,
380 m12,
381 m13,
382 m14: 0.0,
383 m21,
384 m22,
385 m23,
386 m24: 0.0,
387 m31,
388 m32,
389 m33,
390 m34: 0.0,
391 m41: 0.0,
392 m42: 0.0,
393 m43: 0.0,
394 m44: 1.0,
395 }
396 }
397
398 pub fn skew(a: f32, b: f32) -> Matrix3d<f32> {
400 Matrix3d {
401 m11: 1.0,
402 m12: b.tan(),
403 m13: 0.0,
404 m14: 0.0,
405 m21: a.tan(),
406 m22: 1.0,
407 m23: 0.0,
408 m24: 0.0,
409 m31: 0.0,
410 m32: 0.0,
411 m33: 1.0,
412 m34: 0.0,
413 m41: 0.0,
414 m42: 0.0,
415 m43: 0.0,
416 m44: 1.0,
417 }
418 }
419
420 pub fn perspective(d: f32) -> Matrix3d<f32> {
422 Matrix3d {
423 m11: 1.0,
424 m12: 0.0,
425 m13: 0.0,
426 m14: 0.0,
427 m21: 0.0,
428 m22: 1.0,
429 m23: 0.0,
430 m24: 0.0,
431 m31: 0.0,
432 m32: 0.0,
433 m33: 1.0,
434 m34: -1.0 / d,
435 m41: 0.0,
436 m42: 0.0,
437 m43: 0.0,
438 m44: 1.0,
439 }
440 }
441
442 pub fn multiply(&self, other: &Self) -> Self {
444 Matrix3d {
445 m11: self.m11 * other.m11 + self.m12 * other.m21 + self.m13 * other.m31 + self.m14 * other.m41,
446 m12: self.m11 * other.m12 + self.m12 * other.m22 + self.m13 * other.m32 + self.m14 * other.m42,
447 m13: self.m11 * other.m13 + self.m12 * other.m23 + self.m13 * other.m33 + self.m14 * other.m43,
448 m14: self.m11 * other.m14 + self.m12 * other.m24 + self.m13 * other.m34 + self.m14 * other.m44,
449 m21: self.m21 * other.m11 + self.m22 * other.m21 + self.m23 * other.m31 + self.m24 * other.m41,
450 m22: self.m21 * other.m12 + self.m22 * other.m22 + self.m23 * other.m32 + self.m24 * other.m42,
451 m23: self.m21 * other.m13 + self.m22 * other.m23 + self.m23 * other.m33 + self.m24 * other.m43,
452 m24: self.m21 * other.m14 + self.m22 * other.m24 + self.m23 * other.m34 + self.m24 * other.m44,
453 m31: self.m31 * other.m11 + self.m32 * other.m21 + self.m33 * other.m31 + self.m34 * other.m41,
454 m32: self.m31 * other.m12 + self.m32 * other.m22 + self.m33 * other.m32 + self.m34 * other.m42,
455 m33: self.m31 * other.m13 + self.m32 * other.m23 + self.m33 * other.m33 + self.m34 * other.m43,
456 m34: self.m31 * other.m14 + self.m32 * other.m24 + self.m33 * other.m34 + self.m34 * other.m44,
457 m41: self.m41 * other.m11 + self.m42 * other.m21 + self.m43 * other.m31 + self.m44 * other.m41,
458 m42: self.m41 * other.m12 + self.m42 * other.m22 + self.m43 * other.m32 + self.m44 * other.m42,
459 m43: self.m41 * other.m13 + self.m42 * other.m23 + self.m43 * other.m33 + self.m44 * other.m43,
460 m44: self.m41 * other.m14 + self.m42 * other.m24 + self.m43 * other.m34 + self.m44 * other.m44,
461 }
462 }
463
464 pub fn is_2d(&self) -> bool {
466 self.m31 == 0.0
467 && self.m32 == 0.0
468 && self.m13 == 0.0
469 && self.m23 == 0.0
470 && self.m43 == 0.0
471 && self.m14 == 0.0
472 && self.m24 == 0.0
473 && self.m34 == 0.0
474 && self.m33 == 1.0
475 && self.m44 == 1.0
476 }
477
478 pub fn to_matrix2d(&self) -> Option<Matrix<f32>> {
481 if self.is_2d() {
482 return Some(Matrix {
483 a: self.m11,
484 b: self.m12,
485 c: self.m21,
486 d: self.m22,
487 e: self.m41,
488 f: self.m42,
489 });
490 }
491 None
492 }
493
494 pub fn scale_by_factor(&mut self, scaling_factor: f32) {
496 self.m11 *= scaling_factor;
497 self.m12 *= scaling_factor;
498 self.m13 *= scaling_factor;
499 self.m14 *= scaling_factor;
500 self.m21 *= scaling_factor;
501 self.m22 *= scaling_factor;
502 self.m23 *= scaling_factor;
503 self.m24 *= scaling_factor;
504 self.m31 *= scaling_factor;
505 self.m32 *= scaling_factor;
506 self.m33 *= scaling_factor;
507 self.m34 *= scaling_factor;
508 self.m41 *= scaling_factor;
509 self.m42 *= scaling_factor;
510 self.m43 *= scaling_factor;
511 self.m44 *= scaling_factor;
512 }
513
514 pub fn determinant(&self) -> f32 {
516 self.m14 * self.m23 * self.m32 * self.m41
517 - self.m13 * self.m24 * self.m32 * self.m41
518 - self.m14 * self.m22 * self.m33 * self.m41
519 + self.m12 * self.m24 * self.m33 * self.m41
520 + self.m13 * self.m22 * self.m34 * self.m41
521 - self.m12 * self.m23 * self.m34 * self.m41
522 - self.m14 * self.m23 * self.m31 * self.m42
523 + self.m13 * self.m24 * self.m31 * self.m42
524 + self.m14 * self.m21 * self.m33 * self.m42
525 - self.m11 * self.m24 * self.m33 * self.m42
526 - self.m13 * self.m21 * self.m34 * self.m42
527 + self.m11 * self.m23 * self.m34 * self.m42
528 + self.m14 * self.m22 * self.m31 * self.m43
529 - self.m12 * self.m24 * self.m31 * self.m43
530 - self.m14 * self.m21 * self.m32 * self.m43
531 + self.m11 * self.m24 * self.m32 * self.m43
532 + self.m12 * self.m21 * self.m34 * self.m43
533 - self.m11 * self.m22 * self.m34 * self.m43
534 - self.m13 * self.m22 * self.m31 * self.m44
535 + self.m12 * self.m23 * self.m31 * self.m44
536 + self.m13 * self.m21 * self.m32 * self.m44
537 - self.m11 * self.m23 * self.m32 * self.m44
538 - self.m12 * self.m21 * self.m33 * self.m44
539 + self.m11 * self.m22 * self.m33 * self.m44
540 }
541
542 pub fn inverse(&self) -> Option<Matrix3d<f32>> {
544 let mut det = self.determinant();
545 if det == 0.0 {
546 return None;
547 }
548
549 det = 1.0 / det;
550 Some(Matrix3d {
551 m11: det
552 * (self.m23 * self.m34 * self.m42 - self.m24 * self.m33 * self.m42 + self.m24 * self.m32 * self.m43
553 - self.m22 * self.m34 * self.m43
554 - self.m23 * self.m32 * self.m44
555 + self.m22 * self.m33 * self.m44),
556 m12: det
557 * (self.m14 * self.m33 * self.m42 - self.m13 * self.m34 * self.m42 - self.m14 * self.m32 * self.m43
558 + self.m12 * self.m34 * self.m43
559 + self.m13 * self.m32 * self.m44
560 - self.m12 * self.m33 * self.m44),
561 m13: det
562 * (self.m13 * self.m24 * self.m42 - self.m14 * self.m23 * self.m42 + self.m14 * self.m22 * self.m43
563 - self.m12 * self.m24 * self.m43
564 - self.m13 * self.m22 * self.m44
565 + self.m12 * self.m23 * self.m44),
566 m14: det
567 * (self.m14 * self.m23 * self.m32 - self.m13 * self.m24 * self.m32 - self.m14 * self.m22 * self.m33
568 + self.m12 * self.m24 * self.m33
569 + self.m13 * self.m22 * self.m34
570 - self.m12 * self.m23 * self.m34),
571 m21: det
572 * (self.m24 * self.m33 * self.m41 - self.m23 * self.m34 * self.m41 - self.m24 * self.m31 * self.m43
573 + self.m21 * self.m34 * self.m43
574 + self.m23 * self.m31 * self.m44
575 - self.m21 * self.m33 * self.m44),
576 m22: det
577 * (self.m13 * self.m34 * self.m41 - self.m14 * self.m33 * self.m41 + self.m14 * self.m31 * self.m43
578 - self.m11 * self.m34 * self.m43
579 - self.m13 * self.m31 * self.m44
580 + self.m11 * self.m33 * self.m44),
581 m23: det
582 * (self.m14 * self.m23 * self.m41 - self.m13 * self.m24 * self.m41 - self.m14 * self.m21 * self.m43
583 + self.m11 * self.m24 * self.m43
584 + self.m13 * self.m21 * self.m44
585 - self.m11 * self.m23 * self.m44),
586 m24: det
587 * (self.m13 * self.m24 * self.m31 - self.m14 * self.m23 * self.m31 + self.m14 * self.m21 * self.m33
588 - self.m11 * self.m24 * self.m33
589 - self.m13 * self.m21 * self.m34
590 + self.m11 * self.m23 * self.m34),
591 m31: det
592 * (self.m22 * self.m34 * self.m41 - self.m24 * self.m32 * self.m41 + self.m24 * self.m31 * self.m42
593 - self.m21 * self.m34 * self.m42
594 - self.m22 * self.m31 * self.m44
595 + self.m21 * self.m32 * self.m44),
596 m32: det
597 * (self.m14 * self.m32 * self.m41 - self.m12 * self.m34 * self.m41 - self.m14 * self.m31 * self.m42
598 + self.m11 * self.m34 * self.m42
599 + self.m12 * self.m31 * self.m44
600 - self.m11 * self.m32 * self.m44),
601 m33: det
602 * (self.m12 * self.m24 * self.m41 - self.m14 * self.m22 * self.m41 + self.m14 * self.m21 * self.m42
603 - self.m11 * self.m24 * self.m42
604 - self.m12 * self.m21 * self.m44
605 + self.m11 * self.m22 * self.m44),
606 m34: det
607 * (self.m14 * self.m22 * self.m31 - self.m12 * self.m24 * self.m31 - self.m14 * self.m21 * self.m32
608 + self.m11 * self.m24 * self.m32
609 + self.m12 * self.m21 * self.m34
610 - self.m11 * self.m22 * self.m34),
611 m41: det
612 * (self.m23 * self.m32 * self.m41 - self.m22 * self.m33 * self.m41 - self.m23 * self.m31 * self.m42
613 + self.m21 * self.m33 * self.m42
614 + self.m22 * self.m31 * self.m43
615 - self.m21 * self.m32 * self.m43),
616 m42: det
617 * (self.m12 * self.m33 * self.m41 - self.m13 * self.m32 * self.m41 + self.m13 * self.m31 * self.m42
618 - self.m11 * self.m33 * self.m42
619 - self.m12 * self.m31 * self.m43
620 + self.m11 * self.m32 * self.m43),
621 m43: det
622 * (self.m13 * self.m22 * self.m41 - self.m12 * self.m23 * self.m41 - self.m13 * self.m21 * self.m42
623 + self.m11 * self.m23 * self.m42
624 + self.m12 * self.m21 * self.m43
625 - self.m11 * self.m22 * self.m43),
626 m44: det
627 * (self.m12 * self.m23 * self.m31 - self.m13 * self.m22 * self.m31 + self.m13 * self.m21 * self.m32
628 - self.m11 * self.m23 * self.m32
629 - self.m12 * self.m21 * self.m33
630 + self.m11 * self.m22 * self.m33),
631 })
632 }
633
634 pub fn transpose(&self) -> Self {
636 Self {
637 m11: self.m11,
638 m12: self.m21,
639 m13: self.m31,
640 m14: self.m41,
641 m21: self.m12,
642 m22: self.m22,
643 m23: self.m32,
644 m24: self.m42,
645 m31: self.m13,
646 m32: self.m23,
647 m33: self.m33,
648 m34: self.m43,
649 m41: self.m14,
650 m42: self.m24,
651 m43: self.m34,
652 m44: self.m44,
653 }
654 }
655
656 pub fn multiply_vector(&self, pin: &[f32; 4]) -> [f32; 4] {
658 [
659 pin[0] * self.m11 + pin[1] * self.m21 + pin[2] * self.m31 + pin[3] * self.m41,
660 pin[0] * self.m12 + pin[1] * self.m22 + pin[2] * self.m32 + pin[3] * self.m42,
661 pin[0] * self.m13 + pin[1] * self.m23 + pin[2] * self.m33 + pin[3] * self.m43,
662 pin[0] * self.m14 + pin[1] * self.m24 + pin[2] * self.m34 + pin[3] * self.m44,
663 ]
664 }
665
666 pub fn decompose(&self) -> Option<TransformList> {
668 let combine = |a: [f32; 3], b: [f32; 3], ascl: f32, bscl: f32| {
671 [
672 (ascl * a[0]) + (bscl * b[0]),
673 (ascl * a[1]) + (bscl * b[1]),
674 (ascl * a[2]) + (bscl * b[2]),
675 ]
676 };
677
678 let dot = |a: [f32; 3], b: [f32; 3]| a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
680
681 let cross = |row1: [f32; 3], row2: [f32; 3]| {
683 [
684 row1[1] * row2[2] - row1[2] * row2[1],
685 row1[2] * row2[0] - row1[0] * row2[2],
686 row1[0] * row2[1] - row1[1] * row2[0],
687 ]
688 };
689
690 if self.m44 == 0.0 {
691 return None;
692 }
693
694 let scaling_factor = self.m44;
695
696 let mut matrix = self.clone();
698 matrix.scale_by_factor(1.0 / scaling_factor);
699
700 let mut perspective_matrix = matrix.clone();
703 perspective_matrix.m14 = 0.0;
704 perspective_matrix.m24 = 0.0;
705 perspective_matrix.m34 = 0.0;
706 perspective_matrix.m44 = 1.0;
707
708 if perspective_matrix.determinant() == 0.0 {
709 return None;
710 }
711
712 let mut transforms = vec![];
713
714 if matrix.m14 != 0.0 || matrix.m24 != 0.0 || matrix.m34 != 0.0 {
716 let right_hand_side: [f32; 4] = [matrix.m14, matrix.m24, matrix.m34, matrix.m44];
717
718 perspective_matrix = perspective_matrix.inverse().unwrap().transpose();
719 let perspective = perspective_matrix.multiply_vector(&right_hand_side);
720 if perspective[0] == 0.0 && perspective[1] == 0.0 && perspective[3] == 0.0 {
721 transforms.push(Transform::Perspective(Length::px(-1.0 / perspective[2])))
722 } else {
723 return None;
724 }
725 }
726
727 if matrix.m41 != 0.0 || matrix.m42 != 0.0 || matrix.m43 != 0.0 {
730 transforms.push(Transform::Translate3d(
731 LengthPercentage::px(matrix.m41),
732 LengthPercentage::px(matrix.m42),
733 Length::px(matrix.m43),
734 ));
735 }
736
737 let mut row = [
739 [matrix.m11, matrix.m12, matrix.m13],
740 [matrix.m21, matrix.m22, matrix.m23],
741 [matrix.m31, matrix.m32, matrix.m33],
742 ];
743
744 let row0len = (row[0][0] * row[0][0] + row[0][1] * row[0][1] + row[0][2] * row[0][2]).sqrt();
746 let mut scale_x = row0len;
747 row[0] = [row[0][0] / row0len, row[0][1] / row0len, row[0][2] / row0len];
748
749 let mut skew_x = dot(row[0], row[1]);
751 row[1] = combine(row[1], row[0], 1.0, -skew_x);
752
753 let row1len = (row[1][0] * row[1][0] + row[1][1] * row[1][1] + row[1][2] * row[1][2]).sqrt();
755 let mut scale_y = row1len;
756 row[1] = [row[1][0] / row1len, row[1][1] / row1len, row[1][2] / row1len];
757 skew_x /= scale_y;
758
759 let mut skew_y = dot(row[0], row[2]);
761 row[2] = combine(row[2], row[0], 1.0, -skew_y);
762 let mut skew_z = dot(row[1], row[2]);
763 row[2] = combine(row[2], row[1], 1.0, -skew_z);
764
765 let row2len = (row[2][0] * row[2][0] + row[2][1] * row[2][1] + row[2][2] * row[2][2]).sqrt();
767 let mut scale_z = row2len;
768 row[2] = [row[2][0] / row2len, row[2][1] / row2len, row[2][2] / row2len];
769 skew_y /= scale_z;
770 skew_z /= scale_z;
771
772 if skew_z != 0.0 {
773 return None; }
775
776 macro_rules! round {
778 ($var: ident) => {
779 $var = ($var * 100000.0).round() / 100000.0;
780 };
781 }
782
783 round!(skew_x);
784 round!(skew_y);
785 round!(skew_z);
786
787 if skew_x != 0.0 || skew_y != 0.0 || skew_z != 0.0 {
788 transforms.push(Transform::Skew(Angle::Rad(skew_x), Angle::Rad(skew_y)));
789 }
790
791 if dot(row[0], cross(row[1], row[2])) < 0.0 {
795 scale_x = -scale_x;
796 scale_y = -scale_y;
797 scale_z = -scale_z;
798 for i in 0..3 {
799 row[i][0] *= -1.0;
800 row[i][1] *= -1.0;
801 row[i][2] *= -1.0;
802 }
803 }
804
805 round!(scale_x);
806 round!(scale_y);
807 round!(scale_z);
808
809 if scale_x != 1.0 || scale_y != 1.0 || scale_z != 1.0 {
810 transforms.push(Transform::Scale3d(
811 NumberOrPercentage::Number(scale_x),
812 NumberOrPercentage::Number(scale_y),
813 NumberOrPercentage::Number(scale_z),
814 ))
815 }
816
817 let mut rotate_x = 0.5 * ((1.0 + row[0][0] - row[1][1] - row[2][2]).max(0.0)).sqrt();
819 let mut rotate_y = 0.5 * ((1.0 - row[0][0] + row[1][1] - row[2][2]).max(0.0)).sqrt();
820 let mut rotate_z = 0.5 * ((1.0 - row[0][0] - row[1][1] + row[2][2]).max(0.0)).sqrt();
821 let rotate_w = 0.5 * ((1.0 + row[0][0] + row[1][1] + row[2][2]).max(0.0)).sqrt();
822
823 if row[2][1] > row[1][2] {
824 rotate_x = -rotate_x
825 }
826
827 if row[0][2] > row[2][0] {
828 rotate_y = -rotate_y
829 }
830
831 if row[1][0] > row[0][1] {
832 rotate_z = -rotate_z
833 }
834
835 let len = (rotate_x * rotate_x + rotate_y * rotate_y + rotate_z * rotate_z).sqrt();
836 if len != 0.0 {
837 rotate_x /= len;
838 rotate_y /= len;
839 rotate_z /= len;
840 }
841 let a = 2.0 * len.atan2(rotate_w);
842
843 let max = rotate_x.max(rotate_y).max(rotate_z);
845 rotate_x /= max;
846 rotate_y /= max;
847 rotate_z /= max;
848
849 if a != 0.0 {
850 transforms.push(Transform::Rotate3d(rotate_x, rotate_y, rotate_z, Angle::Rad(a)))
851 }
852
853 if transforms.is_empty() {
854 return None;
855 }
856
857 Some(TransformList(transforms))
858 }
859}
860
861impl<'i> Parse<'i> for Transform {
862 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
863 let function = input.expect_function()?.clone();
864 input.parse_nested_block(|input| {
865 let location = input.current_source_location();
866 match_ignore_ascii_case! { &function,
867 "matrix" => {
868 let a = f32::parse(input)?;
869 input.expect_comma()?;
870 let b = f32::parse(input)?;
871 input.expect_comma()?;
872 let c = f32::parse(input)?;
873 input.expect_comma()?;
874 let d = f32::parse(input)?;
875 input.expect_comma()?;
876 let e = f32::parse(input)?;
877 input.expect_comma()?;
878 let f = f32::parse(input)?;
879 Ok(Transform::Matrix(Matrix { a, b, c, d, e, f }))
880 },
881 "matrix3d" => {
882 let m11 = f32::parse(input)?;
883 input.expect_comma()?;
884 let m12 = f32::parse(input)?;
885 input.expect_comma()?;
886 let m13 = f32::parse(input)?;
887 input.expect_comma()?;
888 let m14 = f32::parse(input)?;
889 input.expect_comma()?;
890 let m21 = f32::parse(input)?;
891 input.expect_comma()?;
892 let m22 = f32::parse(input)?;
893 input.expect_comma()?;
894 let m23 = f32::parse(input)?;
895 input.expect_comma()?;
896 let m24 = f32::parse(input)?;
897 input.expect_comma()?;
898 let m31 = f32::parse(input)?;
899 input.expect_comma()?;
900 let m32 = f32::parse(input)?;
901 input.expect_comma()?;
902 let m33 = f32::parse(input)?;
903 input.expect_comma()?;
904 let m34 = f32::parse(input)?;
905 input.expect_comma()?;
906 let m41 = f32::parse(input)?;
907 input.expect_comma()?;
908 let m42 = f32::parse(input)?;
909 input.expect_comma()?;
910 let m43 = f32::parse(input)?;
911 input.expect_comma()?;
912 let m44 = f32::parse(input)?;
913 Ok(Transform::Matrix3d(Matrix3d {
914 m11, m12, m13, m14,
915 m21, m22, m23, m24,
916 m31, m32, m33, m34,
917 m41, m42, m43, m44
918 }))
919 },
920 "translate" => {
921 let x = LengthPercentage::parse(input)?;
922 if input.try_parse(|input| input.expect_comma()).is_ok() {
923 let y = LengthPercentage::parse(input)?;
924 Ok(Transform::Translate(x, y))
925 } else {
926 Ok(Transform::Translate(x, LengthPercentage::zero()))
927 }
928 },
929 "translatex" => {
930 let x = LengthPercentage::parse(input)?;
931 Ok(Transform::TranslateX(x))
932 },
933 "translatey" => {
934 let y = LengthPercentage::parse(input)?;
935 Ok(Transform::TranslateY(y))
936 },
937 "translatez" => {
938 let z = Length::parse(input)?;
939 Ok(Transform::TranslateZ(z))
940 },
941 "translate3d" => {
942 let x = LengthPercentage::parse(input)?;
943 input.expect_comma()?;
944 let y = LengthPercentage::parse(input)?;
945 input.expect_comma()?;
946 let z = Length::parse(input)?;
947 Ok(Transform::Translate3d(x, y, z))
948 },
949 "scale" => {
950 let x = NumberOrPercentage::parse(input)?;
951 if input.try_parse(|input| input.expect_comma()).is_ok() {
952 let y = NumberOrPercentage::parse(input)?;
953 Ok(Transform::Scale(x, y))
954 } else {
955 Ok(Transform::Scale(x.clone(), x))
956 }
957 },
958 "scalex" => {
959 let x = NumberOrPercentage::parse(input)?;
960 Ok(Transform::ScaleX(x))
961 },
962 "scaley" => {
963 let y = NumberOrPercentage::parse(input)?;
964 Ok(Transform::ScaleY(y))
965 },
966 "scalez" => {
967 let z = NumberOrPercentage::parse(input)?;
968 Ok(Transform::ScaleZ(z))
969 },
970 "scale3d" => {
971 let x = NumberOrPercentage::parse(input)?;
972 input.expect_comma()?;
973 let y = NumberOrPercentage::parse(input)?;
974 input.expect_comma()?;
975 let z = NumberOrPercentage::parse(input)?;
976 Ok(Transform::Scale3d(x, y, z))
977 },
978 "rotate" => {
979 let angle = Angle::parse_with_unitless_zero(input)?;
980 Ok(Transform::Rotate(angle))
981 },
982 "rotatex" => {
983 let angle = Angle::parse_with_unitless_zero(input)?;
984 Ok(Transform::RotateX(angle))
985 },
986 "rotatey" => {
987 let angle = Angle::parse_with_unitless_zero(input)?;
988 Ok(Transform::RotateY(angle))
989 },
990 "rotatez" => {
991 let angle = Angle::parse_with_unitless_zero(input)?;
992 Ok(Transform::RotateZ(angle))
993 },
994 "rotate3d" => {
995 let x = f32::parse(input)?;
996 input.expect_comma()?;
997 let y = f32::parse(input)?;
998 input.expect_comma()?;
999 let z = f32::parse(input)?;
1000 input.expect_comma()?;
1001 let angle = Angle::parse_with_unitless_zero(input)?;
1002 Ok(Transform::Rotate3d(x, y, z, angle))
1003 },
1004 "skew" => {
1005 let x = Angle::parse_with_unitless_zero(input)?;
1006 if input.try_parse(|input| input.expect_comma()).is_ok() {
1007 let y = Angle::parse_with_unitless_zero(input)?;
1008 Ok(Transform::Skew(x, y))
1009 } else {
1010 Ok(Transform::Skew(x, Angle::Deg(0.0)))
1011 }
1012 },
1013 "skewx" => {
1014 let angle = Angle::parse_with_unitless_zero(input)?;
1015 Ok(Transform::SkewX(angle))
1016 },
1017 "skewy" => {
1018 let angle = Angle::parse_with_unitless_zero(input)?;
1019 Ok(Transform::SkewY(angle))
1020 },
1021 "perspective" => {
1022 let len = Length::parse(input)?;
1023 Ok(Transform::Perspective(len))
1024 },
1025 _ => Err(location.new_unexpected_token_error(
1026 cssparser::Token::Ident(function.clone())
1027 ))
1028 }
1029 })
1030 }
1031}
1032
1033impl ToCss for Transform {
1034 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1035 where
1036 W: std::fmt::Write,
1037 {
1038 use Transform::*;
1039 match self {
1040 Translate(x, y) => {
1041 if dest.minify && x.is_zero() && !y.is_zero() {
1042 dest.write_str("translateY(")?;
1043 y.to_css(dest)?
1044 } else {
1045 dest.write_str("translate(")?;
1046 x.to_css(dest)?;
1047 if !y.is_zero() {
1048 dest.delim(',', false)?;
1049 y.to_css(dest)?;
1050 }
1051 }
1052 dest.write_char(')')
1053 }
1054 TranslateX(x) => {
1055 dest.write_str(if dest.minify { "translate(" } else { "translateX(" })?;
1056 x.to_css(dest)?;
1057 dest.write_char(')')
1058 }
1059 TranslateY(y) => {
1060 dest.write_str("translateY(")?;
1061 y.to_css(dest)?;
1062 dest.write_char(')')
1063 }
1064 TranslateZ(z) => {
1065 dest.write_str("translateZ(")?;
1066 z.to_css(dest)?;
1067 dest.write_char(')')
1068 }
1069 Translate3d(x, y, z) => {
1070 if dest.minify && !x.is_zero() && y.is_zero() && z.is_zero() {
1071 dest.write_str("translate(")?;
1072 x.to_css(dest)?;
1073 } else if dest.minify && x.is_zero() && !y.is_zero() && z.is_zero() {
1074 dest.write_str("translateY(")?;
1075 y.to_css(dest)?;
1076 } else if dest.minify && x.is_zero() && y.is_zero() && !z.is_zero() {
1077 dest.write_str("translateZ(")?;
1078 z.to_css(dest)?;
1079 } else if dest.minify && z.is_zero() {
1080 dest.write_str("translate(")?;
1081 x.to_css(dest)?;
1082 dest.delim(',', false)?;
1083 y.to_css(dest)?;
1084 } else {
1085 dest.write_str("translate3d(")?;
1086 x.to_css(dest)?;
1087 dest.delim(',', false)?;
1088 y.to_css(dest)?;
1089 dest.delim(',', false)?;
1090 z.to_css(dest)?;
1091 }
1092 dest.write_char(')')
1093 }
1094 Scale(x, y) => {
1095 let x: f32 = x.into();
1096 let y: f32 = y.into();
1097 if dest.minify && x == 1.0 && y != 1.0 {
1098 dest.write_str("scaleY(")?;
1099 y.to_css(dest)?;
1100 } else if dest.minify && x != 1.0 && y == 1.0 {
1101 dest.write_str("scaleX(")?;
1102 x.to_css(dest)?;
1103 } else {
1104 dest.write_str("scale(")?;
1105 x.to_css(dest)?;
1106 if y != x {
1107 dest.delim(',', false)?;
1108 y.to_css(dest)?;
1109 }
1110 }
1111 dest.write_char(')')
1112 }
1113 ScaleX(x) => {
1114 dest.write_str("scaleX(")?;
1115 x.to_css(dest)?;
1116 dest.write_char(')')
1117 }
1118 ScaleY(y) => {
1119 dest.write_str("scaleY(")?;
1120 y.to_css(dest)?;
1121 dest.write_char(')')
1122 }
1123 ScaleZ(z) => {
1124 dest.write_str("scaleZ(")?;
1125 z.to_css(dest)?;
1126 dest.write_char(')')
1127 }
1128 Scale3d(x, y, z) => {
1129 let x: f32 = x.into();
1130 let y: f32 = y.into();
1131 let z: f32 = z.into();
1132 if dest.minify && z == 1.0 && x == y {
1133 dest.write_str("scale(")?;
1135 x.to_css(dest)?;
1136 } else if dest.minify && x != 1.0 && y == 1.0 && z == 1.0 {
1137 dest.write_str("scaleX(")?;
1139 x.to_css(dest)?;
1140 } else if dest.minify && x == 1.0 && y != 1.0 && z == 1.0 {
1141 dest.write_str("scaleY(")?;
1143 y.to_css(dest)?;
1144 } else if dest.minify && x == 1.0 && y == 1.0 && z != 1.0 {
1145 dest.write_str("scaleZ(")?;
1147 z.to_css(dest)?;
1148 } else if dest.minify && z == 1.0 {
1149 dest.write_str("scale(")?;
1151 x.to_css(dest)?;
1152 dest.delim(',', false)?;
1153 y.to_css(dest)?;
1154 } else {
1155 dest.write_str("scale3d(")?;
1156 x.to_css(dest)?;
1157 dest.delim(',', false)?;
1158 y.to_css(dest)?;
1159 dest.delim(',', false)?;
1160 z.to_css(dest)?;
1161 }
1162 dest.write_char(')')
1163 }
1164 Rotate(angle) => {
1165 dest.write_str("rotate(")?;
1166 angle.to_css_with_unitless_zero(dest)?;
1167 dest.write_char(')')
1168 }
1169 RotateX(angle) => {
1170 dest.write_str("rotateX(")?;
1171 angle.to_css_with_unitless_zero(dest)?;
1172 dest.write_char(')')
1173 }
1174 RotateY(angle) => {
1175 dest.write_str("rotateY(")?;
1176 angle.to_css_with_unitless_zero(dest)?;
1177 dest.write_char(')')
1178 }
1179 RotateZ(angle) => {
1180 dest.write_str(if dest.minify { "rotate(" } else { "rotateZ(" })?;
1181 angle.to_css_with_unitless_zero(dest)?;
1182 dest.write_char(')')
1183 }
1184 Rotate3d(x, y, z, angle) => {
1185 if dest.minify && *x == 1.0 && *y == 0.0 && *z == 0.0 {
1186 dest.write_str("rotateX(")?;
1188 angle.to_css_with_unitless_zero(dest)?;
1189 } else if dest.minify && *x == 0.0 && *y == 1.0 && *z == 0.0 {
1190 dest.write_str("rotateY(")?;
1192 angle.to_css_with_unitless_zero(dest)?;
1193 } else if dest.minify && *x == 0.0 && *y == 0.0 && *z == 1.0 {
1194 dest.write_str("rotate(")?;
1196 angle.to_css_with_unitless_zero(dest)?;
1197 } else {
1198 dest.write_str("rotate3d(")?;
1199 x.to_css(dest)?;
1200 dest.delim(',', false)?;
1201 y.to_css(dest)?;
1202 dest.delim(',', false)?;
1203 z.to_css(dest)?;
1204 dest.delim(',', false)?;
1205 angle.to_css_with_unitless_zero(dest)?;
1206 }
1207 dest.write_char(')')
1208 }
1209 Skew(x, y) => {
1210 if dest.minify && x.is_zero() && !y.is_zero() {
1211 dest.write_str("skewY(")?;
1212 y.to_css_with_unitless_zero(dest)?
1213 } else {
1214 dest.write_str("skew(")?;
1215 x.to_css(dest)?;
1216 if !y.is_zero() {
1217 dest.delim(',', false)?;
1218 y.to_css_with_unitless_zero(dest)?;
1219 }
1220 }
1221 dest.write_char(')')
1222 }
1223 SkewX(angle) => {
1224 dest.write_str(if dest.minify { "skew(" } else { "skewX(" })?;
1225 angle.to_css_with_unitless_zero(dest)?;
1226 dest.write_char(')')
1227 }
1228 SkewY(angle) => {
1229 dest.write_str("skewY(")?;
1230 angle.to_css_with_unitless_zero(dest)?;
1231 dest.write_char(')')
1232 }
1233 Perspective(len) => {
1234 dest.write_str("perspective(")?;
1235 len.to_css(dest)?;
1236 dest.write_char(')')
1237 }
1238 Matrix(super::transform::Matrix { a, b, c, d, e, f }) => {
1239 dest.write_str("matrix(")?;
1240 a.to_css(dest)?;
1241 dest.delim(',', false)?;
1242 b.to_css(dest)?;
1243 dest.delim(',', false)?;
1244 c.to_css(dest)?;
1245 dest.delim(',', false)?;
1246 d.to_css(dest)?;
1247 dest.delim(',', false)?;
1248 e.to_css(dest)?;
1249 dest.delim(',', false)?;
1250 f.to_css(dest)?;
1251 dest.write_char(')')
1252 }
1253 Matrix3d(super::transform::Matrix3d {
1254 m11,
1255 m12,
1256 m13,
1257 m14,
1258 m21,
1259 m22,
1260 m23,
1261 m24,
1262 m31,
1263 m32,
1264 m33,
1265 m34,
1266 m41,
1267 m42,
1268 m43,
1269 m44,
1270 }) => {
1271 dest.write_str("matrix3d(")?;
1272 m11.to_css(dest)?;
1273 dest.delim(',', false)?;
1274 m12.to_css(dest)?;
1275 dest.delim(',', false)?;
1276 m13.to_css(dest)?;
1277 dest.delim(',', false)?;
1278 m14.to_css(dest)?;
1279 dest.delim(',', false)?;
1280 m21.to_css(dest)?;
1281 dest.delim(',', false)?;
1282 m22.to_css(dest)?;
1283 dest.delim(',', false)?;
1284 m23.to_css(dest)?;
1285 dest.delim(',', false)?;
1286 m24.to_css(dest)?;
1287 dest.delim(',', false)?;
1288 m31.to_css(dest)?;
1289 dest.delim(',', false)?;
1290 m32.to_css(dest)?;
1291 dest.delim(',', false)?;
1292 m33.to_css(dest)?;
1293 dest.delim(',', false)?;
1294 m34.to_css(dest)?;
1295 dest.delim(',', false)?;
1296 m41.to_css(dest)?;
1297 dest.delim(',', false)?;
1298 m42.to_css(dest)?;
1299 dest.delim(',', false)?;
1300 m43.to_css(dest)?;
1301 dest.delim(',', false)?;
1302 m44.to_css(dest)?;
1303 dest.write_char(')')
1304 }
1305 }
1306 }
1307}
1308
1309impl Transform {
1310 pub fn to_matrix(&self) -> Option<Matrix3d<f32>> {
1312 macro_rules! to_radians {
1313 ($angle: ident) => {{
1314 let rad = $angle.to_radians();
1320 if rad < 0.0 || rad >= 2.0 * PI {
1321 return None;
1322 }
1323
1324 rad
1325 }};
1326 }
1327
1328 match &self {
1329 Transform::Translate(LengthPercentage::Dimension(x), LengthPercentage::Dimension(y)) => {
1330 if let (Some(x), Some(y)) = (x.to_px(), y.to_px()) {
1331 return Some(Matrix3d::translate(x, y, 0.0));
1332 }
1333 }
1334 Transform::TranslateX(LengthPercentage::Dimension(x)) => {
1335 if let Some(x) = x.to_px() {
1336 return Some(Matrix3d::translate(x, 0.0, 0.0));
1337 }
1338 }
1339 Transform::TranslateY(LengthPercentage::Dimension(y)) => {
1340 if let Some(y) = y.to_px() {
1341 return Some(Matrix3d::translate(0.0, y, 0.0));
1342 }
1343 }
1344 Transform::TranslateZ(z) => {
1345 if let Some(z) = z.to_px() {
1346 return Some(Matrix3d::translate(0.0, 0.0, z));
1347 }
1348 }
1349 Transform::Translate3d(LengthPercentage::Dimension(x), LengthPercentage::Dimension(y), z) => {
1350 if let (Some(x), Some(y), Some(z)) = (x.to_px(), y.to_px(), z.to_px()) {
1351 return Some(Matrix3d::translate(x, y, z));
1352 }
1353 }
1354 Transform::Scale(x, y) => return Some(Matrix3d::scale(x.into(), y.into(), 1.0)),
1355 Transform::ScaleX(x) => return Some(Matrix3d::scale(x.into(), 1.0, 1.0)),
1356 Transform::ScaleY(y) => return Some(Matrix3d::scale(1.0, y.into(), 1.0)),
1357 Transform::ScaleZ(z) => return Some(Matrix3d::scale(1.0, 1.0, z.into())),
1358 Transform::Scale3d(x, y, z) => return Some(Matrix3d::scale(x.into(), y.into(), z.into())),
1359 Transform::Rotate(angle) | Transform::RotateZ(angle) => {
1360 return Some(Matrix3d::rotate(0.0, 0.0, 1.0, to_radians!(angle)))
1361 }
1362 Transform::RotateX(angle) => return Some(Matrix3d::rotate(1.0, 0.0, 0.0, to_radians!(angle))),
1363 Transform::RotateY(angle) => return Some(Matrix3d::rotate(0.0, 1.0, 0.0, to_radians!(angle))),
1364 Transform::Rotate3d(x, y, z, angle) => return Some(Matrix3d::rotate(*x, *y, *z, to_radians!(angle))),
1365 Transform::Skew(x, y) => return Some(Matrix3d::skew(to_radians!(x), to_radians!(y))),
1366 Transform::SkewX(x) => return Some(Matrix3d::skew(to_radians!(x), 0.0)),
1367 Transform::SkewY(y) => return Some(Matrix3d::skew(0.0, to_radians!(y))),
1368 Transform::Perspective(len) => {
1369 if let Some(len) = len.to_px() {
1370 return Some(Matrix3d::perspective(len));
1371 }
1372 }
1373 Transform::Matrix(m) => return Some(m.to_matrix3d()),
1374 Transform::Matrix3d(m) => return Some(m.clone()),
1375 _ => {}
1376 }
1377 None
1378 }
1379}
1380
1381enum_property! {
1382 #[allow(missing_docs)]
1384 pub enum TransformStyle {
1385 Flat,
1386 Preserve3d,
1387 }
1388}
1389
1390enum_property! {
1391 pub enum TransformBox {
1393 ContentBox,
1395 BorderBox,
1397 FillBox,
1399 StrokeBox,
1401 ViewBox,
1403 }
1404}
1405
1406enum_property! {
1407 #[allow(missing_docs)]
1409 pub enum BackfaceVisibility {
1410 Visible,
1411 Hidden,
1412 }
1413}
1414
1415#[derive(Debug, Clone, PartialEq, Parse, ToCss)]
1417#[cfg_attr(feature = "visitor", derive(Visit))]
1418#[cfg_attr(
1419 feature = "serde",
1420 derive(serde::Serialize, serde::Deserialize),
1421 serde(tag = "type", content = "value", rename_all = "kebab-case")
1422)]
1423#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
1424#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
1425pub enum Perspective {
1426 None,
1428 Length(Length),
1430}
1431
1432#[derive(Debug, Clone, PartialEq)]
1434#[cfg_attr(feature = "visitor", derive(Visit))]
1435#[cfg_attr(
1436 feature = "serde",
1437 derive(serde::Serialize, serde::Deserialize),
1438 serde(rename_all = "lowercase")
1439)]
1440#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
1441#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
1442pub enum Translate {
1443 None,
1445
1446 #[cfg_attr(feature = "serde", serde(untagged))]
1448 XYZ {
1449 x: LengthPercentage,
1451 y: LengthPercentage,
1453 z: Length,
1455 },
1456}
1457
1458impl<'i> Parse<'i> for Translate {
1459 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
1460 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
1461 return Ok(Translate::None);
1462 }
1463
1464 let x = LengthPercentage::parse(input)?;
1465 let y = input.try_parse(LengthPercentage::parse);
1466 let z = if y.is_ok() {
1467 input.try_parse(Length::parse).ok()
1468 } else {
1469 None
1470 };
1471
1472 Ok(Translate::XYZ {
1473 x,
1474 y: y.unwrap_or(LengthPercentage::zero()),
1475 z: z.unwrap_or(Length::zero()),
1476 })
1477 }
1478}
1479
1480impl ToCss for Translate {
1481 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1482 where
1483 W: std::fmt::Write,
1484 {
1485 match self {
1486 Translate::None => {
1487 dest.write_str("none")?;
1488 }
1489 Translate::XYZ { x, y, z } => {
1490 x.to_css(dest)?;
1491 if !y.is_zero() || !z.is_zero() {
1492 dest.write_char(' ')?;
1493 y.to_css(dest)?;
1494 if !z.is_zero() {
1495 dest.write_char(' ')?;
1496 z.to_css(dest)?;
1497 }
1498 }
1499 }
1500 };
1501
1502 Ok(())
1503 }
1504}
1505
1506impl Translate {
1507 pub fn to_transform(&self) -> Transform {
1509 match self {
1510 Translate::None => {
1511 Transform::Translate3d(LengthPercentage::zero(), LengthPercentage::zero(), Length::zero())
1512 }
1513 Translate::XYZ { x, y, z } => Transform::Translate3d(x.clone(), y.clone(), z.clone()),
1514 }
1515 }
1516}
1517
1518#[derive(Debug, Clone, PartialEq)]
1520#[cfg_attr(feature = "visitor", derive(Visit))]
1521#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1522#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
1523#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
1524pub struct Rotate {
1525 pub x: f32,
1527 pub y: f32,
1529 pub z: f32,
1531 pub angle: Angle,
1533}
1534
1535impl<'i> Parse<'i> for Rotate {
1536 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
1537 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
1538 return Ok(Rotate {
1539 x: 0.0,
1540 y: 0.0,
1541 z: 1.0,
1542 angle: Angle::Deg(0.0),
1543 });
1544 }
1545
1546 let angle = input.try_parse(Angle::parse);
1547 let (x, y, z) = input
1548 .try_parse(|input| {
1549 let location = input.current_source_location();
1550 let ident = input.expect_ident()?;
1551 match_ignore_ascii_case! { &*ident,
1552 "x" => Ok((1.0, 0.0, 0.0)),
1553 "y" => Ok((0.0, 1.0, 0.0)),
1554 "z" => Ok((0.0, 0.0, 1.0)),
1555 _ => Err(location.new_unexpected_token_error(
1556 cssparser::Token::Ident(ident.clone())
1557 ))
1558 }
1559 })
1560 .or_else(
1561 |_: ParseError<'i, ParserError<'i>>| -> Result<_, ParseError<'i, ParserError<'i>>> {
1562 input.try_parse(|input| Ok((f32::parse(input)?, f32::parse(input)?, f32::parse(input)?)))
1563 },
1564 )
1565 .unwrap_or((0.0, 0.0, 1.0));
1566 let angle = angle.or_else(|_| Angle::parse(input))?;
1567 Ok(Rotate { x, y, z, angle })
1568 }
1569}
1570
1571impl ToCss for Rotate {
1572 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1573 where
1574 W: std::fmt::Write,
1575 {
1576 if self.x == 0.0 && self.y == 0.0 && self.z == 1.0 && self.angle.is_zero() {
1577 dest.write_str("none")?;
1578 return Ok(());
1579 }
1580
1581 if self.x == 1.0 && self.y == 0.0 && self.z == 0.0 {
1582 dest.write_str("x ")?;
1583 } else if self.x == 0.0 && self.y == 1.0 && self.z == 0.0 {
1584 dest.write_str("y ")?;
1585 } else if !(self.x == 0.0 && self.y == 0.0 && self.z == 1.0) {
1586 self.x.to_css(dest)?;
1587 dest.write_char(' ')?;
1588 self.y.to_css(dest)?;
1589 dest.write_char(' ')?;
1590 self.z.to_css(dest)?;
1591 dest.write_char(' ')?;
1592 }
1593
1594 self.angle.to_css(dest)
1595 }
1596}
1597
1598impl Rotate {
1599 pub fn to_transform(&self) -> Transform {
1601 Transform::Rotate3d(self.x, self.y, self.z, self.angle.clone())
1602 }
1603}
1604
1605#[derive(Debug, Clone, PartialEq)]
1607#[cfg_attr(feature = "visitor", derive(Visit))]
1608#[cfg_attr(
1609 feature = "serde",
1610 derive(serde::Serialize, serde::Deserialize),
1611 serde(rename_all = "lowercase")
1612)]
1613#[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))]
1614#[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))]
1615pub enum Scale {
1616 None,
1618
1619 #[cfg_attr(feature = "serde", serde(untagged))]
1621 XYZ {
1622 x: NumberOrPercentage,
1624 y: NumberOrPercentage,
1626 z: NumberOrPercentage,
1628 },
1629}
1630
1631impl<'i> Parse<'i> for Scale {
1632 fn parse<'t>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ParserError<'i>>> {
1633 if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() {
1634 return Ok(Scale::None);
1635 }
1636
1637 let x = NumberOrPercentage::parse(input)?;
1638 let y = input.try_parse(NumberOrPercentage::parse);
1639 let z = if y.is_ok() {
1640 input.try_parse(NumberOrPercentage::parse).ok()
1641 } else {
1642 None
1643 };
1644
1645 Ok(Scale::XYZ {
1646 x: x.clone(),
1647 y: y.unwrap_or(x),
1648 z: z.unwrap_or(NumberOrPercentage::Number(1.0)),
1649 })
1650 }
1651}
1652
1653impl ToCss for Scale {
1654 fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
1655 where
1656 W: std::fmt::Write,
1657 {
1658 match self {
1659 Scale::None => {
1660 dest.write_str("none")?;
1661 }
1662 Scale::XYZ { x, y, z } => {
1663 x.to_css(dest)?;
1664 let zv: f32 = z.into();
1665 if y != x || zv != 1.0 {
1666 dest.write_char(' ')?;
1667 y.to_css(dest)?;
1668 if zv != 1.0 {
1669 dest.write_char(' ')?;
1670 z.to_css(dest)?;
1671 }
1672 }
1673 }
1674 }
1675
1676 Ok(())
1677 }
1678}
1679
1680impl Scale {
1681 pub fn to_transform(&self) -> Transform {
1683 match self {
1684 Scale::None => Transform::Scale3d(
1685 NumberOrPercentage::Number(1.0),
1686 NumberOrPercentage::Number(1.0),
1687 NumberOrPercentage::Number(1.0),
1688 ),
1689 Scale::XYZ { x, y, z } => Transform::Scale3d(x.clone(), y.clone(), z.clone()),
1690 }
1691 }
1692}
1693
1694#[derive(Default)]
1695pub(crate) struct TransformHandler {
1696 transform: Option<(TransformList, VendorPrefix)>,
1697 translate: Option<Translate>,
1698 rotate: Option<Rotate>,
1699 scale: Option<Scale>,
1700 has_any: bool,
1701}
1702
1703impl<'i> PropertyHandler<'i> for TransformHandler {
1704 fn handle_property(
1705 &mut self,
1706 property: &Property<'i>,
1707 dest: &mut DeclarationList<'i>,
1708 context: &mut PropertyHandlerContext<'i, '_>,
1709 ) -> bool {
1710 use Property::*;
1711
1712 macro_rules! individual_property {
1713 ($prop: ident, $val: ident) => {
1714 if let Some((transform, _)) = &mut self.transform {
1715 transform.0.push($val.to_transform())
1716 } else {
1717 self.$prop = Some($val.clone());
1718 self.has_any = true;
1719 }
1720 };
1721 }
1722
1723 match property {
1724 Transform(val, vp) => {
1725 if let Some((cur, prefixes)) = &self.transform {
1728 if cur != val && !prefixes.contains(*vp) {
1729 self.flush(dest, context);
1730 }
1731 }
1732
1733 if let Some((transform, prefixes)) = &mut self.transform {
1735 *transform = val.clone();
1736 *prefixes |= *vp;
1737 } else {
1738 self.transform = Some((val.clone(), *vp));
1739 self.has_any = true;
1740 }
1741
1742 self.translate = None;
1743 self.rotate = None;
1744 self.scale = None;
1745 }
1746 Translate(val) => individual_property!(translate, val),
1747 Rotate(val) => individual_property!(rotate, val),
1748 Scale(val) => individual_property!(scale, val),
1749 Unparsed(val)
1750 if matches!(
1751 val.property_id,
1752 PropertyId::Transform(_) | PropertyId::Translate | PropertyId::Rotate | PropertyId::Scale
1753 ) =>
1754 {
1755 self.flush(dest, context);
1756 let prop = if matches!(val.property_id, PropertyId::Transform(_)) {
1757 Property::Unparsed(val.get_prefixed(context.targets, Feature::Transform))
1758 } else {
1759 property.clone()
1760 };
1761 dest.push(prop)
1762 }
1763 _ => return false,
1764 }
1765
1766 true
1767 }
1768
1769 fn finalize(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) {
1770 self.flush(dest, context);
1771 }
1772}
1773
1774impl TransformHandler {
1775 fn flush<'i>(&mut self, dest: &mut DeclarationList<'i>, context: &mut PropertyHandlerContext<'i, '_>) {
1776 if !self.has_any {
1777 return;
1778 }
1779
1780 self.has_any = false;
1781
1782 let transform = std::mem::take(&mut self.transform);
1783 let translate = std::mem::take(&mut self.translate);
1784 let rotate = std::mem::take(&mut self.rotate);
1785 let scale = std::mem::take(&mut self.scale);
1786
1787 if let Some((transform, prefix)) = transform {
1788 let prefix = context.targets.prefixes(prefix, Feature::Transform);
1789 dest.push(Property::Transform(transform, prefix))
1790 }
1791
1792 if let Some(translate) = translate {
1793 dest.push(Property::Translate(translate))
1794 }
1795
1796 if let Some(rotate) = rotate {
1797 dest.push(Property::Rotate(rotate))
1798 }
1799
1800 if let Some(scale) = scale {
1801 dest.push(Property::Scale(scale))
1802 }
1803 }
1804}