Skip to content
18 changes: 12 additions & 6 deletions bindings/python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ fn parse_url(url: Option<String>) -> PyResult<Option<url::Url>> {
})
}

/// 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>,
Expand All @@ -85,13 +85,15 @@ impl CSSInliner {
base_url: Option<String>,
load_remote_stylesheets: Option<bool>,
extra_css: Option<String>,
styles_as_attributes: Option<bool>,
) -> PyResult<Self> {
let options = rust_inline::InlineOptions {
inline_style_tags: inline_style_tags.unwrap_or(true),
remove_style_tags: remove_style_tags.unwrap_or(false),
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),
Expand All @@ -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,
Expand All @@ -129,24 +131,26 @@ fn inline(
base_url: Option<String>,
load_remote_stylesheets: Option<bool>,
extra_css: Option<&str>,
styles_as_attributes: Option<bool>,
) -> PyResult<String> {
let options = rust_inline::InlineOptions {
inline_style_tags: inline_style_tags.unwrap_or(true),
remove_style_tags: remove_style_tags.unwrap_or(false),
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,
Expand All @@ -155,13 +159,15 @@ fn inline_many(
base_url: Option<String>,
load_remote_stylesheets: Option<bool>,
extra_css: Option<&str>,
styles_as_attributes: Option<bool>,
) -> PyResult<Vec<String>> {
let options = rust_inline::InlineOptions {
inline_style_tags: inline_style_tags.unwrap_or(true),
remove_style_tags: remove_style_tags.unwrap_or(false),
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)
Expand Down
3 changes: 3 additions & 0 deletions bindings/python/tests-py/test_inlining.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -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)
4 changes: 4 additions & 0 deletions bindings/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ struct Options {
base_url: Option<String>,
load_remote_stylesheets: bool,
extra_css: Option<String>,
styles_as_attributes: bool,
}

impl Default for Options {
Expand All @@ -83,6 +84,7 @@ impl Default for Options {
base_url: None,
load_remote_stylesheets: true,
extra_css: None,
styles_as_attributes: false,
}
}
}
Expand All @@ -105,6 +107,7 @@ impl TryFrom<Options> 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,
})
}
}
Expand All @@ -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;
Expand Down
23 changes: 21 additions & 2 deletions css-inline/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Cow<'a, str>>,
/// Whether to break down styles into separate attributes
pub styles_as_attributes: bool,
}

impl<'a> InlineOptions<'a> {
Expand All @@ -87,6 +89,7 @@ impl<'a> InlineOptions<'a> {
base_url: None,
load_remote_stylesheets: true,
extra_css: None,
styles_as_attributes: false,
}
}

Expand Down Expand Up @@ -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> {
Expand All @@ -141,6 +151,7 @@ impl Default for InlineOptions<'_> {
base_url: None,
load_remote_stylesheets: true,
extra_css: None,
styles_as_attributes: false,
}
}
}
Expand Down Expand Up @@ -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.
Expand All @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions css-inline/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ OPTIONS:

--extra-css
Additional CSS to inline.

--styles-as-attributes
Will insert styles as separate attributes.
"#
)
.as_bytes();
Expand All @@ -58,6 +61,7 @@ struct Args {
extra_css: Option<String>,
load_remote_stylesheets: bool,
files: Vec<String>,
styles_as_attributes: bool,
}

fn parse_url(url: Option<String>) -> Result<Option<url::Url>, url::ParseError> {
Expand All @@ -83,6 +87,7 @@ fn main() -> Result<(), Box<dyn Error>> {
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 {
Expand All @@ -91,6 +96,7 @@ fn main() -> Result<(), Box<dyn Error>> {
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() {
Expand Down