webfont-generator 0.8.0

Generate webfonts (SVG, TTF, EOT, WOFF, WOFF2) from SVG icons
Documentation
use std::collections::{HashMap, HashSet};
use std::io::Error;

use crate::sfnt::SerializedFontTables;

mod compress;
mod glyf;
mod prepare;
mod serialize;
#[cfg(test)]
mod tests;

#[derive(Clone, Default)]
pub(crate) struct Woff2TransformCache {
    entries: HashMap<u64, Woff2TransformPayload>,
    #[cfg(test)]
    pub compile_count: usize,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub(super) struct Woff2TransformPayload {
    pub transformed: Vec<u8>,
    pub normalized_glyf_len: usize,
    pub normalized_glyf_checksum: u32,
    pub normalized_loca_format: i16,
    pub normalized_loca_len: usize,
    pub normalized_loca_checksum: u32,
}

impl Woff2TransformCache {
    #[cfg(test)]
    pub(crate) fn len(&self) -> usize {
        self.entries.len()
    }

    pub(super) fn transformed(&self, key: &u64) -> Option<Woff2TransformPayload> {
        self.entries.get(key).cloned()
    }
    pub(super) fn insert(&mut self, key: u64, payload: Woff2TransformPayload) {
        #[cfg(test)]
        {
            self.compile_count += 1;
        }
        self.entries.insert(key, payload);
    }
    pub(super) fn retain(&mut self, used_keys: &HashSet<u64>) {
        self.entries.retain(|key, _| used_keys.contains(key));
    }
}

pub(crate) fn tables_to_woff2(
    tables: &SerializedFontTables,
    quality: u8,
    cache: Option<&mut Woff2TransformCache>,
) -> Result<Vec<u8>, Error> {
    encode(tables, quality, cache)
}

fn encode(
    tables: &SerializedFontTables,
    quality: u8,
    cache: Option<&mut Woff2TransformCache>,
) -> Result<Vec<u8>, Error> {
    let mut prepared = prepare::prepare(tables, cache)?;
    let compressed = compress::compress_stream(std::mem::take(&mut prepared.stream), quality)?;
    serialize::assemble(&prepared, &compressed)
}

#[cfg(test)]
pub(crate) fn ttf_to_woff2(ttf: &[u8], quality: u8) -> Result<Vec<u8>, Error> {
    use std::io::ErrorKind;

    ::woff::version2::compress(ttf, "", quality.min(11) as usize, true)
        .ok_or_else(|| Error::new(ErrorKind::InvalidData, "WOFF2 compression failed"))
}

#[cfg(feature = "bench")]
pub(crate) struct PreparedWoff2(prepare::PreparedWoff2);

#[cfg(feature = "bench")]
pub(crate) fn prepare_woff2(
    tables: &SerializedFontTables,
    cache: &mut Woff2TransformCache,
) -> Result<PreparedWoff2, Error> {
    prepare::prepare(tables, Some(cache)).map(PreparedWoff2)
}

#[cfg(feature = "bench")]
pub(crate) fn compress_prepared_woff2(
    prepared: &PreparedWoff2,
    quality: u8,
) -> Result<usize, Error> {
    compress::compress(&prepared.0, quality).map(|compressed| compressed.len())
}