freya-html 0.5.0

HTML + CSS rendering for Freya using Blitz
Documentation
use std::{
    cell::RefCell,
    collections::VecDeque,
    rc::Rc,
    sync::Arc,
};

use freya_core::prelude::{
    Bytes,
    GlobalContexts,
};
use freya_engine::prelude::Typeface;
use parley::fontique::{
    Blob,
    Collection,
    CollectionOptions,
    GenericFamily,
    SourceCache,
};

pub(crate) fn font_context(fonts: &[Bytes]) -> blitz_dom::FontContext {
    let mut context = match GlobalContexts::try_get() {
        Some(contexts) => contexts.get_context_or_insert(system_font_context),
        None => system_font_context(),
    };

    if fonts.is_empty() {
        return context;
    }

    let mut family_ids = Vec::new();
    for data in fonts {
        let decoded = blitz_dom::decode_font_bytes(data).into_owned();
        let registered = context
            .collection
            .register_fonts(Blob::new(Arc::new(decoded) as _), None);
        family_ids.extend(registered.into_iter().map(|(family_id, _)| family_id));
    }
    for generic in [
        GenericFamily::SansSerif,
        GenericFamily::Serif,
        GenericFamily::Monospace,
        GenericFamily::SystemUi,
    ] {
        context
            .collection
            .append_generic_families(generic, family_ids.iter().copied());
    }
    context
}

fn system_font_context() -> blitz_dom::FontContext {
    let mut context = blitz_dom::FontContext {
        source_cache: SourceCache::new_shared(),
        collection: Collection::new(CollectionOptions {
            shared: false,
            system_fonts: !cfg!(target_os = "emscripten"),
        }),
    };
    context
        .collection
        .register_fonts(Blob::new(Arc::new(blitz_dom::BULLET_FONT) as _), None);
    context
}

struct CachedTypeface {
    font: peniko::FontData,
    normalized_coords: Vec<i16>,
    typeface: Typeface,
}

#[derive(Clone, Default)]
pub(crate) struct TypefaceCache {
    resources: Rc<RefCell<VecDeque<CachedTypeface>>>,
}

impl TypefaceCache {
    pub(crate) fn get() -> Self {
        match GlobalContexts::try_get() {
            Some(contexts) => contexts.get_context_or_insert(Self::default),
            None => Self::default(),
        }
    }

    pub(crate) fn read(
        &self,
        font: &peniko::FontData,
        normalized_coords: &[i16],
    ) -> Option<Typeface> {
        let mut resources = self.resources.borrow_mut();
        let index = resources.iter().position(|entry| {
            entry.font.index == font.index
                && entry.font.data.id() == font.data.id()
                && entry.normalized_coords == normalized_coords
        })?;
        let entry = resources.remove(index)?;
        let typeface = entry.typeface.clone();
        resources.push_back(entry);
        Some(typeface)
    }

    pub(crate) fn insert(
        &self,
        font: peniko::FontData,
        normalized_coords: &[i16],
        typeface: Typeface,
    ) {
        let mut resources = self.resources.borrow_mut();
        if resources.len() == 32 {
            resources.pop_front();
        }
        resources.push_back(CachedTypeface {
            font,
            normalized_coords: normalized_coords.to_vec(),
            typeface,
        });
    }
}