use-svg 0.0.1

Practical SVG utility primitives
Documentation
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SvgViewBox {
    pub min_x: f64,
    pub min_y: f64,
    pub width: f64,
    pub height: f64,
}

impl SvgViewBox {
    #[must_use]
    pub fn new(min_x: f64, min_y: f64, width: f64, height: f64) -> Self {
        Self {
            min_x,
            min_y,
            width,
            height,
        }
    }
}

#[must_use]
pub fn parse_view_box(input: &str) -> Option<SvgViewBox> {
    let components: Vec<_> = input
        .split(|ch: char| ch.is_ascii_whitespace() || ch == ',')
        .filter(|component| !component.is_empty())
        .collect();

    if components.len() != 4 {
        return None;
    }

    let min_x = components[0].parse::<f64>().ok()?;
    let min_y = components[1].parse::<f64>().ok()?;
    let width = components[2].parse::<f64>().ok()?;
    let height = components[3].parse::<f64>().ok()?;

    if !min_x.is_finite()
        || !min_y.is_finite()
        || !width.is_finite()
        || !height.is_finite()
        || width < 0.0
        || height < 0.0
    {
        return None;
    }

    Some(SvgViewBox::new(min_x, min_y, width, height))
}

#[must_use]
pub fn format_view_box(view_box: SvgViewBox) -> String {
    format!(
        "{} {} {} {}",
        format_number(view_box.min_x),
        format_number(view_box.min_y),
        format_number(view_box.width),
        format_number(view_box.height)
    )
}

pub(crate) fn format_number(value: f64) -> String {
    if value.abs() < f64::EPSILON {
        return "0".to_string();
    }

    let rounded = value.round();
    if (value - rounded).abs() < 1.0e-12 {
        return format!("{}", rounded as i64);
    }

    let mut formatted = value.to_string();
    if formatted.contains('.') {
        while formatted.ends_with('0') {
            formatted.pop();
        }
        if formatted.ends_with('.') {
            formatted.pop();
        }
    }

    formatted
}