11# frozen_string_literal: true
22
33require 'ostruct'
4+ require 'set'
45
56module PrawnHtml
67 class Attributes < OpenStruct
7- attr_reader :styles
8+ attr_reader :initial , : styles
89
910 STYLES_APPLY = {
1011 block : %i[ align bottom leading left margin_left padding_left position right top ] ,
@@ -19,13 +20,13 @@ class Attributes < OpenStruct
1920 'color' => { key : :color , set : :convert_color } ,
2021 'font-family' => { key : :font , set : :unquote } ,
2122 'font-size' => { key : :size , set : :convert_size } ,
22- 'font-style' => { key : :styles , set : :append_styles } ,
23- 'font-weight' => { key : :styles , set : :append_styles } ,
23+ 'font-style' => { key : :styles , set : :append_styles , values : %i[ italic ] } ,
24+ 'font-weight' => { key : :styles , set : :append_styles , values : %i[ bold ] } ,
2425 'href' => { key : :link , set : :copy_value } ,
2526 'letter-spacing' => { key : :character_spacing , set : :convert_float } ,
2627 'list-style-type' => { key : :list_style_type , set : :unquote } ,
27- 'text-decoration' => { key : :styles , set : :append_text_decoration } ,
28- 'vertical-align' => { key : :styles , set : :append_styles } ,
28+ 'text-decoration' => { key : :styles , set : :append_styles , values : %i[ underline ] } ,
29+ 'vertical-align' => { key : :styles , set : :append_styles , values : %i[ subscript superscript ] } ,
2930 'white-space' => { key : :white_space , set : :convert_symbol } ,
3031 # tag opening styles
3132 'break-before' => { key : :break_before , set : :convert_symbol } ,
@@ -44,7 +45,9 @@ class Attributes < OpenStruct
4445 'position' => { key : :position , set : :convert_symbol } ,
4546 'right' => { key : :right , set : :convert_size , options : :width } ,
4647 'text-align' => { key : :align , set : :convert_symbol } ,
47- 'top' => { key : :top , set : :convert_size , options : :height }
48+ 'top' => { key : :top , set : :convert_size , options : :height } ,
49+ # special styles
50+ 'text-decoration-line-through' => { key : :callback , set : :callback_strike_through }
4851 } . freeze
4952
5053 STYLES_MERGE = %i[ margin_left padding_left ] . freeze
@@ -53,6 +56,7 @@ class Attributes < OpenStruct
5356 def initialize ( attributes = { } )
5457 super
5558 @styles = { } # result styles
59+ @initial = Set . new
5660 end
5761
5862 # Processes the data attributes
@@ -74,6 +78,33 @@ def merge_text_styles!(text_styles, options: {})
7478 process_styles ( hash_styles , options : options ) unless hash_styles . empty?
7579 end
7680
81+ # Remove an attribute value from the context styles
82+ #
83+ # @param context_styles [Hash] hash of the context styles that will be updated
84+ # @param rule [Hash] rule from the STYLES_LIST to lookup in the context style for value removal
85+ def remove_value ( context_styles , rule )
86+ if rule [ :set ] == :append_styles
87+ context_styles [ rule [ :key ] ] -= rule [ :values ] if context_styles [ :styles ]
88+ else
89+ default = Context ::DEFAULT_STYLES [ rule [ :key ] ]
90+ default ? ( context_styles [ rule [ :key ] ] = default ) : context_styles . delete ( rule [ :key ] )
91+ end
92+ end
93+
94+ # Update context styles applying the initial rules (if set)
95+ #
96+ # @param context_styles [Hash] hash of the context styles that will be updated
97+ #
98+ # @return [Hash] the update context styles
99+ def update_styles ( context_styles )
100+ initial . each do |rule |
101+ next unless rule
102+
103+ remove_value ( context_styles , rule )
104+ end
105+ context_styles
106+ end
107+
77108 class << self
78109 # Merges attributes
79110 #
@@ -105,29 +136,30 @@ def parse_styles(styles)
105136 def process_styles ( hash_styles , options :)
106137 hash_styles . each do |key , value |
107138 rule = evaluate_rule ( key , value )
139+ next unless rule
140+
108141 apply_rule! ( merged_styles : @styles , rule : rule , value : value , options : options )
109142 end
110143 @styles
111144 end
112145
113146 def evaluate_rule ( rule_key , attr_value )
114- rule = STYLES_LIST [ rule_key ]
115- if rule && rule [ :set ] == :append_text_decoration
116- return { key : :callback , set : :callback_strike_through } if attr_value == 'line-through'
117-
118- return { key : :styles , set : :append_styles }
119- end
120- rule
147+ key = nil
148+ key = 'text-decoration-line-through' if rule_key == 'text-decoration' && attr_value == 'line-through'
149+ key ||= rule_key
150+ STYLES_LIST [ key ]
121151 end
122152
123153 def apply_rule! ( merged_styles :, rule :, value :, options :)
124- return unless rule
154+ return ( @initial << rule ) if value == 'initial'
125155
126156 if rule [ :set ] == :append_styles
127- ( merged_styles [ rule [ :key ] ] ||= [ ] ) << Utils . normalize_style ( value )
157+ val = Utils . normalize_style ( value )
158+ ( merged_styles [ rule [ :key ] ] ||= [ ] ) << val if val
128159 else
129160 opts = rule [ :options ] ? options [ rule [ :options ] ] : nil
130- merged_styles [ rule [ :key ] ] = Utils . send ( rule [ :set ] , value , options : opts )
161+ val = Utils . send ( rule [ :set ] , value , options : opts )
162+ merged_styles [ rule [ :key ] ] = val if val
131163 end
132164 end
133165 end
0 commit comments