@@ -105,6 +105,7 @@ pub mod error;
105105mod parse;
106106
107107pub use error:: InlineError ;
108+ use std:: collections:: HashMap ;
108109
109110#[ derive( Debug ) ]
110111struct Rule {
@@ -151,14 +152,16 @@ pub fn inline(html: &str) -> Result<String, InlineError> {
151152 . filter_map ( |node| node. into_element_ref ( ) )
152153 . filter ( |element| rule. selectors . matches ( element) ) ;
153154 for matching_element in matching_elements {
154- let style = rule
155- . declarations
156- . iter ( )
157- . map ( |& ( ref key, ref value) | format ! ( "{}:{};" , key, value) ) ;
158- matching_element
159- . attributes
160- . borrow_mut ( )
161- . insert ( "style" , style. collect ( ) ) ;
155+ let mut attributes = matching_element. attributes . borrow_mut ( ) ;
156+ let style = if let Some ( existing_style) = attributes. get ( "style" ) {
157+ merge_styles ( existing_style, & rule. declarations ) ?
158+ } else {
159+ rule. declarations
160+ . iter ( )
161+ . map ( |& ( ref key, ref value) | format ! ( "{}:{};" , key, value) )
162+ . collect ( )
163+ } ;
164+ attributes. insert ( "style" , style) ;
162165 }
163166 }
164167
@@ -173,6 +176,28 @@ pub fn inline(html: &str) -> Result<String, InlineError> {
173176 Ok ( String :: from_utf8_lossy ( & out) . to_string ( ) )
174177}
175178
179+ fn merge_styles ( existing_style : & str , new_styles : & [ Declaration ] ) -> Result < String , InlineError > {
180+ // Parse existing declarations in "style" attribute
181+ let mut input = cssparser:: ParserInput :: new ( existing_style) ;
182+ let mut parser = cssparser:: Parser :: new ( & mut input) ;
183+ let declarations =
184+ cssparser:: DeclarationListParser :: new ( & mut parser, parse:: CSSDeclarationListParser ) ;
185+ // Merge existing with the new ones
186+ let mut styles: HashMap < String , String > = HashMap :: new ( ) ;
187+ for declaration in declarations. into_iter ( ) {
188+ let ( property, value) = declaration?;
189+ styles. insert ( property. to_string ( ) , value. to_string ( ) ) ;
190+ }
191+ for ( property, value) in new_styles. iter ( ) {
192+ styles. insert ( property. to_string ( ) , value. to_string ( ) ) ;
193+ }
194+ // Create a new declarations list
195+ Ok ( styles
196+ . iter ( )
197+ . map ( |( key, value) | format ! ( "{}:{};" , key, value) )
198+ . collect :: < String > ( ) )
199+ }
200+
176201#[ cfg( test) ]
177202mod tests {
178203 use crate :: * ;
@@ -190,9 +215,9 @@ p.footer { font-size: 1px}
190215</style>
191216</head>
192217<body>
193- <h1>Hi! </h1>
218+ <h1>Big Text </h1>
194219<p><strong>Yes!</strong></p>
195- <p class="footer">Feetnuts </p>
220+ <p class="footer">Foot notes </p>
196221</body>
197222</html>"# ;
198223
@@ -213,12 +238,51 @@ p.footer { font-size: 1px}
213238</style>
214239</head>
215240<body>
216- <h1 style="color:red;">Hi! </h1>
241+ <h1 style="color:red;">Big Text </h1>
217242<p style="font-size:2px ;"><strong style="text-decoration:none
218243 ;">Yes!</strong></p>
219- <p class="footer" style="font-size: 1px;">Feetnuts </p>
244+ <p class="footer" style="font-size: 1px;">Foot notes </p>
220245
221246</body></html>"#
222247 )
223248 }
249+
250+ #[ test]
251+ fn test_merge_styles ( ) {
252+ let html = r#"<html>
253+ <head>
254+ <title>Test</title>
255+ <style>
256+ h1 { color:red; }
257+ </style>
258+ </head>
259+ <body>
260+ <h1 style="font-size: 1px">Big Text</h1>
261+ </body>
262+ </html>"# ;
263+ let inlined = inline ( html) . expect ( "Should be valid" ) ;
264+ let valid = ( inlined
265+ == r#"<html><head>
266+ <title>Test</title>
267+ <style>
268+ h1 { color:red; }
269+ </style>
270+ </head>
271+ <body>
272+ <h1 style="color:red;font-size: 1px;">Big Text</h1>
273+
274+ </body></html>"# )
275+ || ( inlined
276+ == r#"<html><head>
277+ <title>Test</title>
278+ <style>
279+ h1 { color:red; }
280+ </style>
281+ </head>
282+ <body>
283+ <h1 style="font-size: 1px;color:red;">Big Text</h1>
284+
285+ </body></html>"# ) ;
286+ assert ! ( valid, inlined)
287+ }
224288}
0 commit comments