diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index 00eec162..4cc6d4dd 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -65,12 +65,12 @@ fn parse_url(url: Option) -> PyResult> { }) } -/// CSSInliner(inline_style_tags=True, remove_style_tags=False, base_url=None, load_remote_stylesheets=True, extra_css=None) +/// CSSInliner(inline_style_tags=True, remove_style_tags=False, base_url=None, load_remote_stylesheets=True, extra_css=None, styles_as_attributes=False) /// /// Customizable CSS inliner. #[pyclass] #[pyo3( - text_signature = "(inline_style_tags=True, remove_style_tags=False, base_url=None, load_remote_stylesheets=True, extra_css=None)" + text_signature = "(inline_style_tags=True, remove_style_tags=False, base_url=None, load_remote_stylesheets=True, extra_css=None, styles_as_attributes=False)" )] struct CSSInliner { inner: rust_inline::CSSInliner<'static>, @@ -85,6 +85,7 @@ impl CSSInliner { base_url: Option, load_remote_stylesheets: Option, extra_css: Option, + styles_as_attributes: Option, ) -> PyResult { let options = rust_inline::InlineOptions { inline_style_tags: inline_style_tags.unwrap_or(true), @@ -92,6 +93,7 @@ impl CSSInliner { base_url: parse_url(base_url)?, load_remote_stylesheets: load_remote_stylesheets.unwrap_or(true), extra_css: extra_css.map(Cow::Owned), + styles_as_attributes: styles_as_attributes.unwrap_or(false), }; Ok(CSSInliner { inner: rust_inline::CSSInliner::new(options), @@ -115,12 +117,12 @@ impl CSSInliner { } } -/// inline(html, inline_style_tags=True, remove_style_tags=False, base_url=None, load_remote_stylesheets=True, extra_css=None) +/// inline(html, inline_style_tags=True, remove_style_tags=False, base_url=None, load_remote_stylesheets=True, extra_css=None, styles_as_attributes=False) /// /// Inline CSS in the given HTML document #[pyfunction] #[pyo3( - text_signature = "(html, inline_style_tags=True, remove_style_tags=False, base_url=None, load_remote_stylesheets=True, extra_css=None)" + text_signature = "(html, inline_style_tags=True, remove_style_tags=False, base_url=None, load_remote_stylesheets=True, extra_css=None, styles_as_attributes=False)" )] fn inline( html: &str, @@ -129,6 +131,7 @@ fn inline( base_url: Option, load_remote_stylesheets: Option, extra_css: Option<&str>, + styles_as_attributes: Option, ) -> PyResult { let options = rust_inline::InlineOptions { inline_style_tags: inline_style_tags.unwrap_or(true), @@ -136,17 +139,18 @@ fn inline( base_url: parse_url(base_url)?, load_remote_stylesheets: load_remote_stylesheets.unwrap_or(true), extra_css: extra_css.map(Cow::Borrowed), + styles_as_attributes: styles_as_attributes.unwrap_or(false), }; let inliner = rust_inline::CSSInliner::new(options); Ok(inliner.inline(html).map_err(InlineErrorWrapper)?) } -/// inline_many(htmls, inline_style_tags=True, remove_style_tags=False, base_url=None, load_remote_stylesheets=True, extra_css=None) +/// inline_many(htmls, inline_style_tags=True, remove_style_tags=False, base_url=None, load_remote_stylesheets=True, extra_css=None, styles_as_attributes=False) /// /// Inline CSS in multiple HTML documents #[pyfunction] #[pyo3( - text_signature = "(htmls, inline_style_tags=True, remove_style_tags=False, base_url=None, load_remote_stylesheets=True, extra_css=None)" + text_signature = "(htmls, inline_style_tags=True, remove_style_tags=False, base_url=None, load_remote_stylesheets=True, extra_css=None, styles_as_attributes=False)" )] fn inline_many( htmls: &PyList, @@ -155,6 +159,7 @@ fn inline_many( base_url: Option, load_remote_stylesheets: Option, extra_css: Option<&str>, + styles_as_attributes: Option, ) -> PyResult> { let options = rust_inline::InlineOptions { inline_style_tags: inline_style_tags.unwrap_or(true), @@ -162,6 +167,7 @@ fn inline_many( base_url: parse_url(base_url)?, load_remote_stylesheets: load_remote_stylesheets.unwrap_or(true), extra_css: extra_css.map(Cow::Borrowed), + styles_as_attributes: styles_as_attributes.unwrap_or(false), }; let inliner = rust_inline::CSSInliner::new(options); inline_many_impl(&inliner, htmls) diff --git a/bindings/python/tests-py/test_inlining.py b/bindings/python/tests-py/test_inlining.py index 8f69f575..701d347e 100644 --- a/bindings/python/tests-py/test_inlining.py +++ b/bindings/python/tests-py/test_inlining.py @@ -89,6 +89,7 @@ def test_invalid_base_url(): base_url=provisional.urls() | st.none(), load_remote_stylesheets=st.booleans() | st.none(), extra_css=st.text() | st.none(), + styles_as_attributes=st.booleans() | st.none(), ) @settings(max_examples=1000) def test_random_input( @@ -98,6 +99,7 @@ def test_random_input( base_url, load_remote_stylesheets, extra_css, + styles_as_attributes, ): with suppress(ValueError): inliner = css_inline.CSSInliner( @@ -106,5 +108,6 @@ def test_random_input( base_url=base_url, load_remote_stylesheets=load_remote_stylesheets, extra_css=extra_css, + styles_as_attributes=styles_as_attributes, ) inliner.inline(document) diff --git a/bindings/wasm/src/lib.rs b/bindings/wasm/src/lib.rs index 58c13a87..c16d0dc2 100644 --- a/bindings/wasm/src/lib.rs +++ b/bindings/wasm/src/lib.rs @@ -73,6 +73,7 @@ struct Options { base_url: Option, load_remote_stylesheets: bool, extra_css: Option, + styles_as_attributes: bool, } impl Default for Options { @@ -83,6 +84,7 @@ impl Default for Options { base_url: None, load_remote_stylesheets: true, extra_css: None, + styles_as_attributes: false, } } } @@ -105,6 +107,7 @@ impl TryFrom for rust_inline::InlineOptions<'_> { base_url: parse_url(value.base_url)?, load_remote_stylesheets: value.load_remote_stylesheets, extra_css: value.extra_css.map(Cow::Owned), + styles_as_attributes: value.styles_as_attributes, }) } } @@ -129,6 +132,7 @@ interface InlineOptions { base_url?: string, load_remote_stylesheets?: boolean, extra_css?: string, + styles_as_attributes?: boolean, } export function inline(html: string, options?: InlineOptions): string; diff --git a/css-inline/src/lib.rs b/css-inline/src/lib.rs index 54fc6709..baea7cf4 100644 --- a/css-inline/src/lib.rs +++ b/css-inline/src/lib.rs @@ -74,6 +74,8 @@ pub struct InlineOptions<'a> { // Python wrapper for `CSSInliner` and `&str` in Rust & simple functions on the Python side /// Additional CSS to inline. pub extra_css: Option>, + /// Whether to break down styles into separate attributes + pub styles_as_attributes: bool, } impl<'a> InlineOptions<'a> { @@ -87,6 +89,7 @@ impl<'a> InlineOptions<'a> { base_url: None, load_remote_stylesheets: true, extra_css: None, + styles_as_attributes: false, } } @@ -125,6 +128,13 @@ impl<'a> InlineOptions<'a> { self } + /// Insert styles as attributes + #[must_use] + pub fn styles_as_attributes(mut self, styles_as_attributes: bool) -> Self { + self.styles_as_attributes = styles_as_attributes; + self + } + /// Create a new `CSSInliner` instance from this options. #[must_use] pub const fn build(self) -> CSSInliner<'a> { @@ -141,6 +151,7 @@ impl Default for InlineOptions<'_> { base_url: None, load_remote_stylesheets: true, extra_css: None, + styles_as_attributes: false, } } } @@ -284,7 +295,7 @@ impl<'a> CSSInliner<'a> { } } if let Some(extra_css) = &self.options.extra_css { - process_css(&document, extra_css, &mut styles); + process_css(&document, extra_css.as_ref(), &mut styles); } for (node_id, styles) in styles { // SAFETY: All nodes are alive as long as `document` is in scope. @@ -299,7 +310,15 @@ impl<'a> CSSInliner<'a> { .attributes .try_borrow_mut() { - if let Some(existing_style) = attributes.get_mut("style") { + if self.options.styles_as_attributes { + if let Some(_existing_style) = attributes.get_mut("style") { + // TODO + } else { + for (name, (_, value)) in styles { + attributes.insert(name, value); + } + } + } else if let Some(existing_style) = attributes.get_mut("style") { *existing_style = merge_styles(existing_style, &styles)?; } else { let mut final_styles = String::with_capacity(128); diff --git a/css-inline/src/main.rs b/css-inline/src/main.rs index 855b06df..551825fa 100644 --- a/css-inline/src/main.rs +++ b/css-inline/src/main.rs @@ -47,6 +47,9 @@ OPTIONS: --extra-css Additional CSS to inline. + + --styles-as-attributes + Will insert styles as separate attributes. "# ) .as_bytes(); @@ -58,6 +61,7 @@ struct Args { extra_css: Option, load_remote_stylesheets: bool, files: Vec, + styles_as_attributes: bool, } fn parse_url(url: Option) -> Result, url::ParseError> { @@ -83,6 +87,7 @@ fn main() -> Result<(), Box> { base_url: args.opt_value_from_str("--base-url")?, extra_css: args.opt_value_from_str("--extra-css")?, load_remote_stylesheets: args.contains("--load-remote-stylesheets"), + styles_as_attributes: args.opt_value_from_str("--styles-as-attributes")?.unwrap_or(false), files: args.free()?, }; let options = InlineOptions { @@ -91,6 +96,7 @@ fn main() -> Result<(), Box> { base_url: parse_url(args.base_url)?, load_remote_stylesheets: args.load_remote_stylesheets, extra_css: args.extra_css.as_deref().map(Cow::Borrowed), + styles_as_attributes: args.styles_as_attributes, }; let inliner = CSSInliner::new(options); if args.files.is_empty() {