rust-office-charts 0.1.7

Render Office Open XML charts to SVG from native Rust or WebAssembly
Documentation
const CARLITO: &[u8] = include_bytes!("font_metrics/carlito.bin");

fn u16_at(data: &[u8], offset: usize) -> u16 {
    u16::from_le_bytes(data[offset..offset + 2].try_into().unwrap())
}

fn codepoint(data: &[u8], index: usize) -> u32 {
    let offset = 4 + index * 4;
    u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap())
}

pub(super) fn width(text: &str, size: f64, family: &str, bold: bool, italic: bool) -> Option<f64> {
    let primary = family.split(',').next()?.trim().trim_matches(['\'', '"']);
    let data: &[u8] = match primary.to_ascii_lowercase().as_str() {
        "calibri" | "carlito" => CARLITO,
        "arial" | "arimo" => include_bytes!("font_metrics/arimo.bin"),
        "times new roman" | "tinos" => include_bytes!("font_metrics/tinos.bin"),
        "courier new" | "cousine" => include_bytes!("font_metrics/cousine.bin"),
        _ => return None,
    };
    let count = usize::from(u16_at(data, 2));
    let style = usize::from(bold) + 2 * usize::from(italic);
    let start = 4 + count * 4 + style * count * 2;
    let mut advance = 0_u64;
    for character in text.chars() {
        let cp = character as u32;
        let (mut low, mut high) = (0, count);
        while low < high {
            let mid = (low + high) / 2;
            if codepoint(data, mid) < cp {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
        if low == count || codepoint(data, low) != cp {
            return None;
        }
        advance += u64::from(u16_at(data, start + low * 2));
    }
    Some(advance as f64 * size / f64::from(u16_at(data, 0)))
}