mkwebfont_fontops 0.4.0

Internal crate for mkwebfont.
Documentation
use crate::{
    font_info::{FontFaceWrapper, FontStyle},
    gfonts::gfonts_list::GfontsList,
};
use anyhow::Result;
use bincode::{config::standard, Decode, Encode};
use mkwebfont_common::{
    character_set::{CharacterSet, CompressedCharacterSet},
    compression::zstd_decompress,
    download_cache::DownloadInfo,
    join_set::JoinSet,
};
use std::sync::{Arc, LazyLock};
use tracing::info;

#[derive(Debug, Clone, Decode, Encode)]
pub struct FallbackInfo {
    pub fonts: Vec<FallbackComponent>,
}
impl FallbackInfo {
    pub fn load<'a>() -> &'a FallbackInfo {
        static CACHE: LazyLock<FallbackInfo> = LazyLock::new(|| {
            let data = include_bytes!("fallback_info.bin.zst");
            let decompressed = zstd_decompress(data).unwrap();
            bincode::decode_from_slice(&decompressed, standard())
                .unwrap()
                .0
        });
        &*CACHE
    }

    pub fn build_stack(chars: &CharacterSet) -> Vec<String> {
        let mut chars = chars.clone();
        let loaded = Self::load::<'static>();

        let mut list = Vec::new();
        for font in &loaded.fonts {
            let new_chars = &chars - CharacterSet::decompress(&font.codepoints);
            if new_chars != chars {
                chars = new_chars;
                list.push(font.name.clone())
            }
        }
        list
    }

    pub async fn load_needed_fonts(chars: &CharacterSet) -> Result<Vec<FontFaceWrapper>> {
        let mut chars = chars.clone();
        let loaded = Self::load::<'static>();

        let mut joins = JoinSet::new();
        for font in &loaded.fonts {
            let new_chars = &chars - CharacterSet::decompress(&font.codepoints);
            if new_chars != chars {
                info!("Loading font: (Fallback) {}", font.name);
                chars = new_chars;
                joins.spawn(font.load());
            }
        }
        let files = joins.join_vec().await?;

        let mut joins = JoinSet::new();
        for file in files {
            joins.spawn(async move { FontFaceWrapper::load(None, file) });
        }
        joins.join_vec().await
    }
}

#[derive(Debug, Clone, Decode, Encode)]
pub struct FallbackComponent {
    pub name: String,
    pub source: FallbackDownloadSource,
    pub codepoints: CompressedCharacterSet,
}
impl FallbackComponent {
    pub async fn load(&self) -> Result<Vec<Arc<[u8]>>> {
        let mut results = Vec::new();
        match &self.source {
            FallbackDownloadSource::GFonts(font) => {
                for style in &GfontsList::find_font(&font).unwrap().styles {
                    if style.style == FontStyle::Regular {
                        results.push(style.info.load().await?);
                    }
                }
            }
            FallbackDownloadSource::Download(info) => {
                for dl in info {
                    results.push(dl.load().await?);
                }
            }
        }
        Ok(results)
    }
}

#[derive(Debug, Clone, Decode, Encode)]
pub enum FallbackDownloadSource {
    GFonts(String),
    Download(Vec<DownloadInfo>),
}