ironpress 1.4.1

Pure Rust HTML/CSS/Markdown to PDF converter with layout engine, LaTeX math, tables, images, custom fonts, and streaming output. No browser, no system dependencies.
Documentation
use lightningcss::rules::CssRule as LightningRule;
use lightningcss::stylesheet::{
    ParserFlags, ParserOptions, PrinterOptions, StyleAttribute, StyleSheet,
};
use lightningcss::traits::ToCss;

use super::{CssRule, StyleMap, inline::apply_declaration, rules::extract_pseudo_element};

pub(super) fn parse_inline_style_with_lightning(style: &str) -> Option<StyleMap> {
    let attribute = StyleAttribute::parse(style, parser_options(style)).ok()?;
    Some(declaration_block_to_style_map(&attribute.declarations))
}

pub(super) fn parse_stylesheet_rules_with_lightning(css: &str) -> Option<Vec<CssRule>> {
    let stylesheet = StyleSheet::parse(css, parser_options(css)).ok()?;
    let mut rules = Vec::new();

    for rule in &stylesheet.rules.0 {
        if let LightningRule::Style(style_rule) = rule {
            let declarations = declaration_block_to_style_map(&style_rule.declarations);
            if declarations.properties.is_empty() {
                continue;
            }

            for selector in &style_rule.selectors.0 {
                let selector = selector.to_css_string(PrinterOptions::default()).ok()?;
                let (selector, pseudo_element) = extract_pseudo_element(&selector);
                rules.push(CssRule {
                    selector,
                    declarations: declarations.clone(),
                    pseudo_element,
                });
            }
        }
    }

    Some(rules)
}

fn declaration_block_to_style_map(
    declarations: &lightningcss::declaration::DeclarationBlock<'_>,
) -> StyleMap {
    let mut map = StyleMap::new();

    for (property, is_important) in declarations.iter() {
        let property_id = property.property_id();
        let property_name = property_id.name().to_string();
        let value = match property.value_to_css_string(PrinterOptions::default()) {
            Ok(value) => value,
            Err(_) => continue,
        };
        apply_declaration(&mut map, &property_name, &value, is_important);
    }

    map
}

fn parser_options<'i>(input: &'i str) -> ParserOptions<'static, 'i> {
    let _ = input;
    ParserOptions {
        filename: String::new(),
        css_modules: None,
        source_index: 0,
        error_recovery: true,
        warnings: None,
        flags: ParserFlags::empty(),
    }
}