1use alloc::{
4 string::{String, ToString},
5 vec::Vec,
6};
7use core::{fmt, num::ParseFloatError};
8
9#[cfg(feature = "parser")]
10use crate::props::basic::{
11 error::{InvalidValueErr, InvalidValueErrOwned},
12 length::parse_float_value,
13 parse::{parse_parentheses, ParenthesisParseError, ParenthesisParseErrorOwned},
14};
15use crate::{
16 format_rust_code::GetHash,
17 props::{
18 basic::{
19 color::{parse_css_color, ColorU, CssColorParseError, CssColorParseErrorOwned},
20 length::{FloatValue, PercentageParseError, PercentageValue},
21 pixel::{
22 parse_pixel_value, CssPixelValueParseError, CssPixelValueParseErrorOwned,
23 PixelValue,
24 },
25 },
26 formatter::PrintAsCssValue,
27 style::{
28 box_shadow::{
29 parse_style_box_shadow, CssShadowParseError, CssShadowParseErrorOwned,
30 StyleBoxShadow,
31 },
32 effects::{parse_style_mix_blend_mode, MixBlendModeParseError, StyleMixBlendMode},
33 },
34 },
35};
36
37#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
40#[repr(C, u8)]
41pub enum StyleFilter {
42 Blend(StyleMixBlendMode),
43 Flood(ColorU),
44 Blur(StyleBlur),
45 Opacity(PercentageValue),
46 ColorMatrix(StyleColorMatrix),
47 DropShadow(StyleBoxShadow),
48 ComponentTransfer,
49 Offset(StyleFilterOffset),
50 Composite(StyleCompositeFilter),
51}
52
53impl_vec!(
54 StyleFilter,
55 StyleFilterVec,
56 StyleFilterVecDestructor,
57 StyleFilterVecDestructorType
58);
59impl_vec_clone!(StyleFilter, StyleFilterVec, StyleFilterVecDestructor);
60impl_vec_debug!(StyleFilter, StyleFilterVec);
61impl_vec_eq!(StyleFilter, StyleFilterVec);
62impl_vec_ord!(StyleFilter, StyleFilterVec);
63impl_vec_hash!(StyleFilter, StyleFilterVec);
64impl_vec_partialeq!(StyleFilter, StyleFilterVec);
65impl_vec_partialord!(StyleFilter, StyleFilterVec);
66
67#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
68#[repr(C)]
69pub struct StyleBlur {
70 pub width: PixelValue,
71 pub height: PixelValue,
72}
73
74#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
77#[repr(C)]
78pub struct StyleColorMatrix {
79 pub m0: FloatValue,
80 pub m1: FloatValue,
81 pub m2: FloatValue,
82 pub m3: FloatValue,
83 pub m4: FloatValue,
84 pub m5: FloatValue,
85 pub m6: FloatValue,
86 pub m7: FloatValue,
87 pub m8: FloatValue,
88 pub m9: FloatValue,
89 pub m10: FloatValue,
90 pub m11: FloatValue,
91 pub m12: FloatValue,
92 pub m13: FloatValue,
93 pub m14: FloatValue,
94 pub m15: FloatValue,
95 pub m16: FloatValue,
96 pub m17: FloatValue,
97 pub m18: FloatValue,
98 pub m19: FloatValue,
99}
100
101impl StyleColorMatrix {
102 pub fn as_slice(&self) -> [FloatValue; 20] {
104 [
105 self.m0, self.m1, self.m2, self.m3, self.m4, self.m5, self.m6, self.m7, self.m8,
106 self.m9, self.m10, self.m11, self.m12, self.m13, self.m14, self.m15, self.m16,
107 self.m17, self.m18, self.m19,
108 ]
109 }
110}
111
112#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
113#[repr(C)]
114pub struct StyleFilterOffset {
115 pub x: PixelValue,
116 pub y: PixelValue,
117}
118
119#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
122#[repr(C)]
123pub struct ArithmeticCoefficients {
124 pub k1: FloatValue,
125 pub k2: FloatValue,
126 pub k3: FloatValue,
127 pub k4: FloatValue,
128}
129
130#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
131#[repr(C, u8)]
132pub enum StyleCompositeFilter {
133 Over,
134 In,
135 Atop,
136 Out,
137 Xor,
138 Lighter,
139 Arithmetic(ArithmeticCoefficients),
140}
141
142impl PrintAsCssValue for StyleFilterVec {
145 fn print_as_css_value(&self) -> String {
146 self.as_ref()
147 .iter()
148 .map(|f| f.print_as_css_value())
149 .collect::<Vec<_>>()
150 .join(" ")
151 }
152}
153
154impl crate::format_rust_code::FormatAsRustCode for StyleFilterVec {
156 fn format_as_rust_code(&self, _tabs: usize) -> String {
157 format!(
158 "StyleFilterVec::from_const_slice(STYLE_FILTER_{}_ITEMS)",
159 self.get_hash()
160 )
161 }
162}
163
164impl PrintAsCssValue for StyleFilter {
165 fn print_as_css_value(&self) -> String {
166 match self {
167 StyleFilter::Blend(mode) => format!("blend({})", mode.print_as_css_value()),
168 StyleFilter::Flood(c) => format!("flood({})", c.to_hash()),
169 StyleFilter::Blur(c) => {
170 if c.width == c.height {
171 format!("blur({})", c.width)
172 } else {
173 format!("blur({} {})", c.width, c.height)
174 }
175 }
176 StyleFilter::Opacity(c) => format!("opacity({})", c),
177 StyleFilter::ColorMatrix(c) => format!(
178 "color-matrix({})",
179 c.as_slice()
180 .iter()
181 .map(|s| format!("{}", s))
182 .collect::<Vec<_>>()
183 .join(" ")
184 ),
185 StyleFilter::DropShadow(shadow) => {
186 format!("drop-shadow({})", shadow.print_as_css_value())
187 }
188 StyleFilter::ComponentTransfer => "component-transfer".to_string(),
189 StyleFilter::Offset(o) => format!("offset({} {})", o.x, o.y),
190 StyleFilter::Composite(c) => format!("composite({})", c.print_as_css_value()),
191 }
192 }
193}
194
195impl PrintAsCssValue for StyleCompositeFilter {
196 fn print_as_css_value(&self) -> String {
197 match self {
198 StyleCompositeFilter::Over => "over".to_string(),
199 StyleCompositeFilter::In => "in".to_string(),
200 StyleCompositeFilter::Atop => "atop".to_string(),
201 StyleCompositeFilter::Out => "out".to_string(),
202 StyleCompositeFilter::Xor => "xor".to_string(),
203 StyleCompositeFilter::Lighter => "lighter".to_string(),
204 StyleCompositeFilter::Arithmetic(fv) => {
205 format!("arithmetic {} {} {} {}", fv.k1, fv.k2, fv.k3, fv.k4)
206 }
207 }
208 }
209}
210
211#[cfg(feature = "parser")]
214mod parser {
215 use super::*;
216 use crate::props::basic::parse_percentage_value;
217
218 #[derive(Clone, PartialEq)]
221 pub enum CssStyleFilterParseError<'a> {
222 InvalidFilter(&'a str),
223 InvalidParenthesis(ParenthesisParseError<'a>),
224 Shadow(CssShadowParseError<'a>),
225 BlendMode(InvalidValueErr<'a>),
226 Color(CssColorParseError<'a>),
227 Opacity(PercentageParseError),
228 Blur(CssStyleBlurParseError<'a>),
229 ColorMatrix(CssStyleColorMatrixParseError<'a>),
230 Offset(CssStyleFilterOffsetParseError<'a>),
231 Composite(CssStyleCompositeFilterParseError<'a>),
232 }
233
234 impl_debug_as_display!(CssStyleFilterParseError<'a>);
235 impl_display! { CssStyleFilterParseError<'a>, {
236 InvalidFilter(e) => format!("Invalid filter function: \"{}\"", e),
237 InvalidParenthesis(e) => format!("Invalid filter syntax - parenthesis error: {}", e),
238 Shadow(e) => format!("Error parsing drop-shadow(): {}", e),
239 BlendMode(e) => format!("Error parsing blend(): invalid value \"{}\"", e.0),
240 Color(e) => format!("Error parsing flood(): {}", e),
241 Opacity(e) => format!("Error parsing opacity(): {}", e),
242 Blur(e) => format!("Error parsing blur(): {}", e),
243 ColorMatrix(e) => format!("Error parsing color-matrix(): {}", e),
244 Offset(e) => format!("Error parsing offset(): {}", e),
245 Composite(e) => format!("Error parsing composite(): {}", e),
246 }}
247
248 impl_from!(
249 ParenthesisParseError<'a>,
250 CssStyleFilterParseError::InvalidParenthesis
251 );
252 impl_from!(InvalidValueErr<'a>, CssStyleFilterParseError::BlendMode);
253 impl_from!(CssStyleBlurParseError<'a>, CssStyleFilterParseError::Blur);
254 impl_from!(CssColorParseError<'a>, CssStyleFilterParseError::Color);
255 impl_from!(
256 CssStyleColorMatrixParseError<'a>,
257 CssStyleFilterParseError::ColorMatrix
258 );
259 impl_from!(
260 CssStyleFilterOffsetParseError<'a>,
261 CssStyleFilterParseError::Offset
262 );
263 impl_from!(
264 CssStyleCompositeFilterParseError<'a>,
265 CssStyleFilterParseError::Composite
266 );
267 impl_from!(CssShadowParseError<'a>, CssStyleFilterParseError::Shadow);
268
269 impl<'a> From<PercentageParseError> for CssStyleFilterParseError<'a> {
270 fn from(p: PercentageParseError) -> Self {
271 Self::Opacity(p)
272 }
273 }
274
275 impl<'a> From<MixBlendModeParseError<'a>> for CssStyleFilterParseError<'a> {
276 fn from(e: MixBlendModeParseError<'a>) -> Self {
277 match e {
279 MixBlendModeParseError::InvalidValue(err) => Self::BlendMode(err),
280 }
281 }
282 }
283
284 #[derive(Debug, Clone, PartialEq)]
285 pub enum CssStyleFilterParseErrorOwned {
286 InvalidFilter(String),
287 InvalidParenthesis(ParenthesisParseErrorOwned),
288 Shadow(CssShadowParseErrorOwned),
289 BlendMode(InvalidValueErrOwned),
290 Color(CssColorParseErrorOwned),
291 Opacity(PercentageParseError),
292 Blur(CssStyleBlurParseErrorOwned),
293 ColorMatrix(CssStyleColorMatrixParseErrorOwned),
294 Offset(CssStyleFilterOffsetParseErrorOwned),
295 Composite(CssStyleCompositeFilterParseErrorOwned),
296 }
297
298 impl<'a> CssStyleFilterParseError<'a> {
299 pub fn to_contained(&self) -> CssStyleFilterParseErrorOwned {
300 match self {
301 Self::InvalidFilter(s) => {
302 CssStyleFilterParseErrorOwned::InvalidFilter(s.to_string())
303 }
304 Self::InvalidParenthesis(e) => {
305 CssStyleFilterParseErrorOwned::InvalidParenthesis(e.to_contained())
306 }
307 Self::Shadow(e) => CssStyleFilterParseErrorOwned::Shadow(e.to_contained()),
308 Self::BlendMode(e) => CssStyleFilterParseErrorOwned::BlendMode(e.to_contained()),
309 Self::Color(e) => CssStyleFilterParseErrorOwned::Color(e.to_contained()),
310 Self::Opacity(e) => CssStyleFilterParseErrorOwned::Opacity(e.clone()),
311 Self::Blur(e) => CssStyleFilterParseErrorOwned::Blur(e.to_contained()),
312 Self::ColorMatrix(e) => {
313 CssStyleFilterParseErrorOwned::ColorMatrix(e.to_contained())
314 }
315 Self::Offset(e) => CssStyleFilterParseErrorOwned::Offset(e.to_contained()),
316 Self::Composite(e) => CssStyleFilterParseErrorOwned::Composite(e.to_contained()),
317 }
318 }
319 }
320
321 impl CssStyleFilterParseErrorOwned {
322 pub fn to_shared<'a>(&'a self) -> CssStyleFilterParseError<'a> {
323 match self {
324 Self::InvalidFilter(s) => CssStyleFilterParseError::InvalidFilter(s),
325 Self::InvalidParenthesis(e) => {
326 CssStyleFilterParseError::InvalidParenthesis(e.to_shared())
327 }
328 Self::Shadow(e) => CssStyleFilterParseError::Shadow(e.to_shared()),
329 Self::BlendMode(e) => CssStyleFilterParseError::BlendMode(e.to_shared()),
330 Self::Color(e) => CssStyleFilterParseError::Color(e.to_shared()),
331 Self::Opacity(e) => CssStyleFilterParseError::Opacity(e.clone()),
332 Self::Blur(e) => CssStyleFilterParseError::Blur(e.to_shared()),
333 Self::ColorMatrix(e) => CssStyleFilterParseError::ColorMatrix(e.to_shared()),
334 Self::Offset(e) => CssStyleFilterParseError::Offset(e.to_shared()),
335 Self::Composite(e) => CssStyleFilterParseError::Composite(e.to_shared()),
336 }
337 }
338 }
339
340 #[derive(Clone, PartialEq)]
343 pub enum CssStyleBlurParseError<'a> {
344 Pixel(CssPixelValueParseError<'a>),
345 TooManyComponents(&'a str),
346 }
347
348 impl_debug_as_display!(CssStyleBlurParseError<'a>);
349 impl_display! { CssStyleBlurParseError<'a>, {
350 Pixel(e) => format!("Invalid pixel value: {}", e),
351 TooManyComponents(input) => format!("Expected 1 or 2 components, got more: \"{}\"", input),
352 }}
353 impl_from!(CssPixelValueParseError<'a>, CssStyleBlurParseError::Pixel);
354
355 #[derive(Debug, Clone, PartialEq)]
356 pub enum CssStyleBlurParseErrorOwned {
357 Pixel(CssPixelValueParseErrorOwned),
358 TooManyComponents(String),
359 }
360
361 impl<'a> CssStyleBlurParseError<'a> {
362 pub fn to_contained(&self) -> CssStyleBlurParseErrorOwned {
363 match self {
364 Self::Pixel(e) => CssStyleBlurParseErrorOwned::Pixel(e.to_contained()),
365 Self::TooManyComponents(s) => {
366 CssStyleBlurParseErrorOwned::TooManyComponents(s.to_string())
367 }
368 }
369 }
370 }
371
372 impl CssStyleBlurParseErrorOwned {
373 pub fn to_shared<'a>(&'a self) -> CssStyleBlurParseError<'a> {
374 match self {
375 Self::Pixel(e) => CssStyleBlurParseError::Pixel(e.to_shared()),
376 Self::TooManyComponents(s) => CssStyleBlurParseError::TooManyComponents(s),
377 }
378 }
379 }
380
381 #[derive(Clone, PartialEq)]
382 pub enum CssStyleColorMatrixParseError<'a> {
383 Float(ParseFloatError),
384 WrongNumberOfComponents {
385 expected: usize,
386 got: usize,
387 input: &'a str,
388 },
389 }
390
391 impl_debug_as_display!(CssStyleColorMatrixParseError<'a>);
392 impl_display! { CssStyleColorMatrixParseError<'a>, {
393 Float(e) => format!("Error parsing floating-point value: {}", e),
394 WrongNumberOfComponents { expected, got, input } => format!("Expected {} components, got {}: \"{}\"", expected, got, input),
395 }}
396 impl<'a> From<ParseFloatError> for CssStyleColorMatrixParseError<'a> {
397 fn from(p: ParseFloatError) -> Self {
398 Self::Float(p)
399 }
400 }
401
402 #[derive(Debug, Clone, PartialEq)]
403 pub enum CssStyleColorMatrixParseErrorOwned {
404 Float(ParseFloatError),
405 WrongNumberOfComponents {
406 expected: usize,
407 got: usize,
408 input: String,
409 },
410 }
411
412 impl<'a> CssStyleColorMatrixParseError<'a> {
413 pub fn to_contained(&self) -> CssStyleColorMatrixParseErrorOwned {
414 match self {
415 Self::Float(e) => CssStyleColorMatrixParseErrorOwned::Float(e.clone()),
416 Self::WrongNumberOfComponents {
417 expected,
418 got,
419 input,
420 } => CssStyleColorMatrixParseErrorOwned::WrongNumberOfComponents {
421 expected: *expected,
422 got: *got,
423 input: input.to_string(),
424 },
425 }
426 }
427 }
428
429 impl CssStyleColorMatrixParseErrorOwned {
430 pub fn to_shared<'a>(&'a self) -> CssStyleColorMatrixParseError<'a> {
431 match self {
432 Self::Float(e) => CssStyleColorMatrixParseError::Float(e.clone()),
433 Self::WrongNumberOfComponents {
434 expected,
435 got,
436 input,
437 } => CssStyleColorMatrixParseError::WrongNumberOfComponents {
438 expected: *expected,
439 got: *got,
440 input,
441 },
442 }
443 }
444 }
445
446 #[derive(Clone, PartialEq)]
447 pub enum CssStyleFilterOffsetParseError<'a> {
448 Pixel(CssPixelValueParseError<'a>),
449 WrongNumberOfComponents {
450 expected: usize,
451 got: usize,
452 input: &'a str,
453 },
454 }
455
456 impl_debug_as_display!(CssStyleFilterOffsetParseError<'a>);
457 impl_display! { CssStyleFilterOffsetParseError<'a>, {
458 Pixel(e) => format!("Invalid pixel value: {}", e),
459 WrongNumberOfComponents { expected, got, input } => format!("Expected {} components, got {}: \"{}\"", expected, got, input),
460 }}
461 impl_from!(
462 CssPixelValueParseError<'a>,
463 CssStyleFilterOffsetParseError::Pixel
464 );
465
466 #[derive(Debug, Clone, PartialEq)]
467 pub enum CssStyleFilterOffsetParseErrorOwned {
468 Pixel(CssPixelValueParseErrorOwned),
469 WrongNumberOfComponents {
470 expected: usize,
471 got: usize,
472 input: String,
473 },
474 }
475
476 impl<'a> CssStyleFilterOffsetParseError<'a> {
477 pub fn to_contained(&self) -> CssStyleFilterOffsetParseErrorOwned {
478 match self {
479 Self::Pixel(e) => CssStyleFilterOffsetParseErrorOwned::Pixel(e.to_contained()),
480 Self::WrongNumberOfComponents {
481 expected,
482 got,
483 input,
484 } => CssStyleFilterOffsetParseErrorOwned::WrongNumberOfComponents {
485 expected: *expected,
486 got: *got,
487 input: input.to_string(),
488 },
489 }
490 }
491 }
492
493 impl CssStyleFilterOffsetParseErrorOwned {
494 pub fn to_shared<'a>(&'a self) -> CssStyleFilterOffsetParseError<'a> {
495 match self {
496 Self::Pixel(e) => CssStyleFilterOffsetParseError::Pixel(e.to_shared()),
497 Self::WrongNumberOfComponents {
498 expected,
499 got,
500 input,
501 } => CssStyleFilterOffsetParseError::WrongNumberOfComponents {
502 expected: *expected,
503 got: *got,
504 input,
505 },
506 }
507 }
508 }
509
510 #[derive(Clone, PartialEq)]
511 pub enum CssStyleCompositeFilterParseError<'a> {
512 Invalid(InvalidValueErr<'a>),
513 Float(ParseFloatError),
514 WrongNumberOfComponents {
515 expected: usize,
516 got: usize,
517 input: &'a str,
518 },
519 }
520
521 impl_debug_as_display!(CssStyleCompositeFilterParseError<'a>);
522 impl_display! { CssStyleCompositeFilterParseError<'a>, {
523 Invalid(s) => format!("Invalid composite operator: {}", s.0),
524 Float(e) => format!("Error parsing floating-point value for arithmetic(): {}", e),
525 WrongNumberOfComponents { expected, got, input } => format!("Expected {} components for arithmetic(), got {}: \"{}\"", expected, got, input),
526 }}
527 impl_from!(
528 InvalidValueErr<'a>,
529 CssStyleCompositeFilterParseError::Invalid
530 );
531 impl<'a> From<ParseFloatError> for CssStyleCompositeFilterParseError<'a> {
532 fn from(p: ParseFloatError) -> Self {
533 Self::Float(p)
534 }
535 }
536
537 #[derive(Debug, Clone, PartialEq)]
538 pub enum CssStyleCompositeFilterParseErrorOwned {
539 Invalid(InvalidValueErrOwned),
540 Float(ParseFloatError),
541 WrongNumberOfComponents {
542 expected: usize,
543 got: usize,
544 input: String,
545 },
546 }
547
548 impl<'a> CssStyleCompositeFilterParseError<'a> {
549 pub fn to_contained(&self) -> CssStyleCompositeFilterParseErrorOwned {
550 match self {
551 Self::Invalid(e) => {
552 CssStyleCompositeFilterParseErrorOwned::Invalid(e.to_contained())
553 }
554 Self::Float(e) => CssStyleCompositeFilterParseErrorOwned::Float(e.clone()),
555 Self::WrongNumberOfComponents {
556 expected,
557 got,
558 input,
559 } => CssStyleCompositeFilterParseErrorOwned::WrongNumberOfComponents {
560 expected: *expected,
561 got: *got,
562 input: input.to_string(),
563 },
564 }
565 }
566 }
567
568 impl CssStyleCompositeFilterParseErrorOwned {
569 pub fn to_shared<'a>(&'a self) -> CssStyleCompositeFilterParseError<'a> {
570 match self {
571 Self::Invalid(e) => CssStyleCompositeFilterParseError::Invalid(e.to_shared()),
572 Self::Float(e) => CssStyleCompositeFilterParseError::Float(e.clone()),
573 Self::WrongNumberOfComponents {
574 expected,
575 got,
576 input,
577 } => CssStyleCompositeFilterParseError::WrongNumberOfComponents {
578 expected: *expected,
579 got: *got,
580 input,
581 },
582 }
583 }
584 }
585
586 pub fn parse_style_filter_vec<'a>(
590 input: &'a str,
591 ) -> Result<StyleFilterVec, CssStyleFilterParseError<'a>> {
592 let mut filters = Vec::new();
593 let mut remaining = input.trim();
594 while !remaining.is_empty() {
595 let (filter, rest) = parse_one_filter_function(remaining)?;
596 filters.push(filter);
597 remaining = rest.trim_start();
598 }
599 Ok(filters.into())
600 }
601
602 fn parse_one_filter_function<'a>(
605 input: &'a str,
606 ) -> Result<(StyleFilter, &'a str), CssStyleFilterParseError<'a>> {
607 let open_paren = input
608 .find('(')
609 .ok_or(CssStyleFilterParseError::InvalidFilter(input))?;
610 let func_name = &input[..open_paren];
611
612 let mut balance = 1;
613 let mut close_paren = 0;
614 for (i, c) in input.char_indices().skip(open_paren + 1) {
615 if c == '(' {
616 balance += 1;
617 } else if c == ')' {
618 balance -= 1;
619 if balance == 0 {
620 close_paren = i;
621 break;
622 }
623 }
624 }
625
626 if balance != 0 {
627 return Err(ParenthesisParseError::UnclosedBraces.into());
628 }
629
630 let full_function = &input[..=close_paren];
631 let rest = &input[(close_paren + 1)..];
632
633 let filter = parse_style_filter(full_function)?;
634 Ok((filter, rest))
635 }
636
637 pub fn parse_style_filter<'a>(
639 input: &'a str,
640 ) -> Result<StyleFilter, CssStyleFilterParseError<'a>> {
641 let (filter_type, filter_values) = parse_parentheses(
642 input,
643 &[
644 "blend",
645 "flood",
646 "blur",
647 "opacity",
648 "color-matrix",
649 "drop-shadow",
650 "component-transfer",
651 "offset",
652 "composite",
653 ],
654 )?;
655
656 match filter_type {
657 "blend" => Ok(StyleFilter::Blend(parse_style_mix_blend_mode(
658 filter_values,
659 )?)),
660 "flood" => Ok(StyleFilter::Flood(parse_css_color(filter_values)?)),
661 "blur" => Ok(StyleFilter::Blur(parse_style_blur(filter_values)?)),
662 "opacity" => {
663 let val = parse_percentage_value(filter_values)?;
664 let normalized = val.normalized();
666 if normalized < 0.0 || normalized > 1.0 {
667 return Err(CssStyleFilterParseError::Opacity(
668 PercentageParseError::InvalidUnit(filter_values.to_string().into()),
669 ));
670 }
671 Ok(StyleFilter::Opacity(val))
672 }
673 "color-matrix" => Ok(StyleFilter::ColorMatrix(parse_color_matrix(filter_values)?)),
674 "drop-shadow" => Ok(StyleFilter::DropShadow(parse_style_box_shadow(
675 filter_values,
676 )?)),
677 "component-transfer" => Ok(StyleFilter::ComponentTransfer),
678 "offset" => Ok(StyleFilter::Offset(parse_filter_offset(filter_values)?)),
679 "composite" => Ok(StyleFilter::Composite(parse_filter_composite(
680 filter_values,
681 )?)),
682 _ => unreachable!(),
683 }
684 }
685
686 fn parse_style_blur<'a>(input: &'a str) -> Result<StyleBlur, CssStyleBlurParseError<'a>> {
687 let mut iter = input.split_whitespace();
688 let width_str = iter.next().unwrap_or("");
689 let height_str = iter.next();
690
691 if iter.next().is_some() {
692 return Err(CssStyleBlurParseError::TooManyComponents(input));
693 }
694
695 let width = parse_pixel_value(width_str)?;
696 let height = match height_str {
697 Some(s) => parse_pixel_value(s)?,
698 None => width, };
700
701 Ok(StyleBlur { width, height })
702 }
703
704 fn parse_color_matrix<'a>(
705 input: &'a str,
706 ) -> Result<StyleColorMatrix, CssStyleColorMatrixParseError<'a>> {
707 let components: Vec<_> = input.split_whitespace().collect();
708 if components.len() != 20 {
709 return Err(CssStyleColorMatrixParseError::WrongNumberOfComponents {
710 expected: 20,
711 got: components.len(),
712 input,
713 });
714 }
715
716 let mut values = [FloatValue::const_new(0); 20];
717 for (i, comp) in components.iter().enumerate() {
718 values[i] = parse_float_value(comp)?;
719 }
720
721 Ok(StyleColorMatrix {
722 m0: values[0],
723 m1: values[1],
724 m2: values[2],
725 m3: values[3],
726 m4: values[4],
727 m5: values[5],
728 m6: values[6],
729 m7: values[7],
730 m8: values[8],
731 m9: values[9],
732 m10: values[10],
733 m11: values[11],
734 m12: values[12],
735 m13: values[13],
736 m14: values[14],
737 m15: values[15],
738 m16: values[16],
739 m17: values[17],
740 m18: values[18],
741 m19: values[19],
742 })
743 }
744
745 fn parse_filter_offset<'a>(
746 input: &'a str,
747 ) -> Result<StyleFilterOffset, CssStyleFilterOffsetParseError<'a>> {
748 let components: Vec<_> = input.split_whitespace().collect();
749 if components.len() != 2 {
750 return Err(CssStyleFilterOffsetParseError::WrongNumberOfComponents {
751 expected: 2,
752 got: components.len(),
753 input,
754 });
755 }
756
757 let x = parse_pixel_value(components[0])?;
758 let y = parse_pixel_value(components[1])?;
759
760 Ok(StyleFilterOffset { x, y })
761 }
762
763 fn parse_filter_composite<'a>(
764 input: &'a str,
765 ) -> Result<StyleCompositeFilter, CssStyleCompositeFilterParseError<'a>> {
766 let mut iter = input.split_whitespace();
767 let operator = iter.next().unwrap_or("");
768
769 match operator {
770 "over" => Ok(StyleCompositeFilter::Over),
771 "in" => Ok(StyleCompositeFilter::In),
772 "atop" => Ok(StyleCompositeFilter::Atop),
773 "out" => Ok(StyleCompositeFilter::Out),
774 "xor" => Ok(StyleCompositeFilter::Xor),
775 "lighter" => Ok(StyleCompositeFilter::Lighter),
776 "arithmetic" => {
777 let mut values = [FloatValue::const_new(0); 4];
778 for (i, val) in values.iter_mut().enumerate() {
779 let s = iter.next().ok_or(
780 CssStyleCompositeFilterParseError::WrongNumberOfComponents {
781 expected: 4,
782 got: i,
783 input,
784 },
785 )?;
786 *val = parse_float_value(s)?;
787 }
788 Ok(StyleCompositeFilter::Arithmetic(ArithmeticCoefficients {
789 k1: values[0],
790 k2: values[1],
791 k3: values[2],
792 k4: values[3],
793 }))
794 }
795 _ => Err(InvalidValueErr(operator).into()),
796 }
797 }
798}
799#[cfg(feature = "parser")]
800pub use parser::{
801 parse_style_filter_vec, CssStyleBlurParseError, CssStyleBlurParseErrorOwned,
802 CssStyleColorMatrixParseError, CssStyleColorMatrixParseErrorOwned,
803 CssStyleCompositeFilterParseError, CssStyleCompositeFilterParseErrorOwned,
804 CssStyleFilterOffsetParseError, CssStyleFilterOffsetParseErrorOwned, CssStyleFilterParseError,
805 CssStyleFilterParseErrorOwned,
806};
807
808#[cfg(all(test, feature = "parser"))]
809mod tests {
810 use super::*;
811 use crate::props::style::filter::parser::parse_style_filter;
812
813 #[test]
814 fn test_parse_single_filter_functions() {
815 let blur = parse_style_filter("blur(5px)").unwrap();
817 assert!(matches!(blur, StyleFilter::Blur(_)));
818 if let StyleFilter::Blur(b) = blur {
819 assert_eq!(b.width, PixelValue::px(5.0));
820 assert_eq!(b.height, PixelValue::px(5.0));
821 }
822
823 let blur2 = parse_style_filter("blur(2px 4px)").unwrap();
825 if let StyleFilter::Blur(b) = blur2 {
826 assert_eq!(b.width, PixelValue::px(2.0));
827 assert_eq!(b.height, PixelValue::px(4.0));
828 }
829
830 let shadow = parse_style_filter("drop-shadow(10px 5px 5px #888)").unwrap();
832 assert!(matches!(shadow, StyleFilter::DropShadow(_)));
833 if let StyleFilter::DropShadow(s) = shadow {
834 assert_eq!(s.offset_x.inner, PixelValue::px(10.0));
835 assert_eq!(s.blur_radius.inner, PixelValue::px(5.0));
836 assert_eq!(s.color, ColorU::new_rgb(0x88, 0x88, 0x88));
837 }
838
839 let opacity = parse_style_filter("opacity(50%)").unwrap();
841 assert!(matches!(opacity, StyleFilter::Opacity(_)));
842 if let StyleFilter::Opacity(p) = opacity {
843 assert_eq!(p.normalized(), 0.5);
844 }
845
846 let flood = parse_style_filter("flood(red)").unwrap();
848 assert_eq!(flood, StyleFilter::Flood(ColorU::RED));
849
850 let composite = parse_style_filter("composite(in)").unwrap();
852 assert_eq!(composite, StyleFilter::Composite(StyleCompositeFilter::In));
853
854 let offset = parse_style_filter("offset(10px 20%)").unwrap();
856 if let StyleFilter::Offset(o) = offset {
857 assert_eq!(o.x, PixelValue::px(10.0));
858 assert_eq!(o.y, PixelValue::percent(20.0));
859 }
860 }
861
862 #[test]
863 fn test_parse_filter_vec() {
864 let filters =
865 parse_style_filter_vec("blur(5px) drop-shadow(10px 5px #888) opacity(0.8)").unwrap();
866 assert_eq!(filters.len(), 3);
867 assert!(matches!(filters.as_slice()[0], StyleFilter::Blur(_)));
868 assert!(matches!(filters.as_slice()[1], StyleFilter::DropShadow(_)));
869 assert!(matches!(filters.as_slice()[2], StyleFilter::Opacity(_)));
870 }
871
872 #[test]
873 fn test_parse_filter_errors() {
874 assert!(parse_style_filter_vec("blurry(5px)").is_err());
876 assert!(parse_style_filter_vec("blur(5px 10px 15px)").is_err());
878 assert!(parse_style_filter_vec("opacity(2)").is_err()); assert!(parse_style_filter_vec("blur(5px").is_err());
881 }
882}