@@ -2,6 +2,7 @@ use cssparser::*;
22use crate :: traits:: { Parse , ToCss } ;
33use crate :: macros:: enum_property;
44use crate :: values:: length:: { Length , LengthPercentage } ;
5+ use crate :: values:: color:: CssColor ;
56use crate :: printer:: Printer ;
67use bitflags:: bitflags;
78use std:: fmt:: Write ;
@@ -278,3 +279,225 @@ impl ToCss for TextIndent {
278279 Ok ( ( ) )
279280 }
280281}
282+
283+ // https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-decoration-line-property
284+ bitflags ! {
285+ pub struct TextDecorationLine : u8 {
286+ const Underline = 0b00000001 ;
287+ const Overline = 0b00000010 ;
288+ const LineThrough = 0b00000100 ;
289+ const Blink = 0b00001000 ;
290+ const SpellingError = 0b00010000 ;
291+ const GrammarError = 0b00100000 ;
292+ }
293+ }
294+
295+ impl Default for TextDecorationLine {
296+ fn default ( ) -> TextDecorationLine {
297+ TextDecorationLine :: empty ( )
298+ }
299+ }
300+
301+ impl Parse for TextDecorationLine {
302+ fn parse < ' i , ' t > ( input : & mut Parser < ' i , ' t > ) -> Result < Self , ParseError < ' i , ( ) > > {
303+ let mut value = TextDecorationLine :: empty ( ) ;
304+ let mut any = false ;
305+
306+ loop {
307+ let flag: Result < _ , ParseError < ' i , ( ) > > = input. try_parse ( |input| {
308+ let location = input. current_source_location ( ) ;
309+ let ident = input. expect_ident ( ) ?;
310+ Ok ( match_ignore_ascii_case ! { & ident,
311+ "none" if value. is_empty( ) => TextDecorationLine :: empty( ) ,
312+ "underline" => TextDecorationLine :: Underline ,
313+ "overline" => TextDecorationLine :: Overline ,
314+ "line-through" => TextDecorationLine :: LineThrough ,
315+ "blink" =>TextDecorationLine :: Blink ,
316+ "spelling-error" if value. is_empty( ) => TextDecorationLine :: SpellingError ,
317+ "grammar-error" if value. is_empty( ) => TextDecorationLine :: GrammarError ,
318+ _ => return Err ( location. new_unexpected_token_error(
319+ cssparser:: Token :: Ident ( ident. clone( ) )
320+ ) )
321+ } )
322+ } ) ;
323+
324+ if let Ok ( flag) = flag {
325+ value |= flag;
326+ any = true ;
327+ } else {
328+ break
329+ }
330+ }
331+
332+ if !any {
333+ return Err ( input. new_error ( BasicParseErrorKind :: QualifiedRuleInvalid ) )
334+ }
335+
336+ Ok ( value)
337+ }
338+ }
339+
340+ impl ToCss for TextDecorationLine {
341+ fn to_css < W > ( & self , dest : & mut Printer < W > ) -> std:: fmt:: Result where W : std:: fmt:: Write {
342+ if self . is_empty ( ) {
343+ return dest. write_str ( "none" )
344+ }
345+
346+ if self . contains ( TextDecorationLine :: SpellingError ) {
347+ return dest. write_str ( "spelling-error" )
348+ }
349+
350+ if self . contains ( TextDecorationLine :: GrammarError ) {
351+ return dest. write_str ( "grammar-error" )
352+ }
353+
354+ let mut needs_space = false ;
355+ macro_rules! val {
356+ ( $val: ident, $str: expr) => {
357+ if self . contains( TextDecorationLine :: $val) {
358+ if needs_space {
359+ dest. write_char( ' ' ) ?;
360+ }
361+ dest. write_str( $str) ?;
362+ needs_space = true ;
363+ }
364+ } ;
365+ }
366+
367+ val ! ( Underline , "underline" ) ;
368+ val ! ( Overline , "overline" ) ;
369+ val ! ( LineThrough , "line-through" ) ;
370+ val ! ( Blink , "blink" ) ;
371+ Ok ( ( ) )
372+ }
373+ }
374+
375+ // https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-decoration-style-property
376+ enum_property ! ( TextDecorationStyle ,
377+ Solid ,
378+ Double ,
379+ Dotted ,
380+ Dashed ,
381+ Wavy
382+ ) ;
383+
384+ impl Default for TextDecorationStyle {
385+ fn default ( ) -> TextDecorationStyle {
386+ TextDecorationStyle :: Solid
387+ }
388+ }
389+
390+ /// https://www.w3.org/TR/2020/WD-css-text-decor-4-20200506/#text-decoration-width-property
391+ #[ derive( Debug , Clone , PartialEq ) ]
392+ pub enum TextDecorationThickness {
393+ Auto ,
394+ FromFont ,
395+ LengthPercentage ( LengthPercentage )
396+ }
397+
398+ impl Default for TextDecorationThickness {
399+ fn default ( ) -> TextDecorationThickness {
400+ TextDecorationThickness :: Auto
401+ }
402+ }
403+
404+ impl Parse for TextDecorationThickness {
405+ fn parse < ' i , ' t > ( input : & mut Parser < ' i , ' t > ) -> Result < Self , ParseError < ' i , ( ) > > {
406+ if input. try_parse ( |input| input. expect_ident_matching ( "auto" ) ) . is_ok ( ) {
407+ return Ok ( TextDecorationThickness :: Auto )
408+ }
409+
410+ if input. try_parse ( |input| input. expect_ident_matching ( "from-font" ) ) . is_ok ( ) {
411+ return Ok ( TextDecorationThickness :: FromFont )
412+ }
413+
414+ let lp = LengthPercentage :: parse ( input) ?;
415+ Ok ( TextDecorationThickness :: LengthPercentage ( lp) )
416+ }
417+ }
418+
419+ impl ToCss for TextDecorationThickness {
420+ fn to_css < W > ( & self , dest : & mut Printer < W > ) -> std:: fmt:: Result where W : std:: fmt:: Write {
421+ match self {
422+ TextDecorationThickness :: Auto => dest. write_str ( "auto" ) ,
423+ TextDecorationThickness :: FromFont => dest. write_str ( "from-font" ) ,
424+ TextDecorationThickness :: LengthPercentage ( lp) => lp. to_css ( dest)
425+ }
426+ }
427+ }
428+
429+ #[ derive( Debug , Clone , PartialEq ) ]
430+ pub struct TextDecoration {
431+ line : TextDecorationLine ,
432+ thickness : TextDecorationThickness ,
433+ style : TextDecorationStyle ,
434+ color : CssColor
435+ }
436+
437+ impl Parse for TextDecoration {
438+ fn parse < ' i , ' t > ( input : & mut Parser < ' i , ' t > ) -> Result < Self , ParseError < ' i , ( ) > > {
439+ let mut line = None ;
440+ let mut thickness = None ;
441+ let mut style = None ;
442+ let mut color = None ;
443+
444+ loop {
445+ macro_rules! prop {
446+ ( $key: ident, $type: ident) => {
447+ if $key. is_none( ) {
448+ if let Ok ( val) = input. try_parse( $type:: parse) {
449+ $key = Some ( val) ;
450+ continue
451+ }
452+ }
453+ } ;
454+ }
455+
456+ prop ! ( line, TextDecorationLine ) ;
457+ prop ! ( thickness, TextDecorationThickness ) ;
458+ prop ! ( style, TextDecorationStyle ) ;
459+ prop ! ( color, CssColor ) ;
460+ break
461+ }
462+
463+ Ok ( TextDecoration {
464+ line : line. unwrap_or_default ( ) ,
465+ thickness : thickness. unwrap_or_default ( ) ,
466+ style : style. unwrap_or_default ( ) ,
467+ color : color. unwrap_or ( CssColor :: current_color ( ) )
468+ } )
469+ }
470+ }
471+
472+ impl ToCss for TextDecoration {
473+ fn to_css < W > ( & self , dest : & mut Printer < W > ) -> std:: fmt:: Result where W : std:: fmt:: Write {
474+ self . line . to_css ( dest) ?;
475+ if self . line . is_empty ( ) {
476+ return Ok ( ( ) )
477+ }
478+
479+ let mut needs_space = true ;
480+ if self . thickness != TextDecorationThickness :: default ( ) {
481+ dest. write_char ( ' ' ) ?;
482+ self . thickness . to_css ( dest) ?;
483+ needs_space = true ;
484+ }
485+
486+ if self . style != TextDecorationStyle :: default ( ) {
487+ if needs_space {
488+ dest. write_char ( ' ' ) ?;
489+ }
490+ self . style . to_css ( dest) ?;
491+ needs_space = true ;
492+ }
493+
494+ if self . color != CssColor :: current_color ( ) {
495+ if needs_space {
496+ dest. write_char ( ' ' ) ?;
497+ }
498+ self . color . to_css ( dest) ?;
499+ }
500+
501+ Ok ( ( ) )
502+ }
503+ }
0 commit comments