mplusfonts-macros 0.3.4

Procedural macros re-exported in the mplusfonts crate
Documentation
use std::iter;

use swash::shape::Shaper;
use swash::text::cluster::SourceRange;

use crate::mplus::bitmap::{CharDictionary, CharDictionaryKey, Glyph};

use super::glyph::{GlyphOffsets, GlyphSpacing};

pub struct StringRefList<'a>(pub Vec<&'a String>);

impl StringRefList<'_> {
    pub fn shape_and_render(
        &self,
        entries: CharDictionary,
        mut shaper: Shaper,
        mut render: impl FnMut(GlyphOffsets) -> Glyph,
        glyph_spacing: &GlyphSpacing,
        is_fallback: bool,
    ) {
        let Self(strings) = self;
        for string in strings.iter() {
            shaper.add_str(string);
            shaper.add_str("\n");
        }

        let mut strings = strings.iter();
        let mut string = "";
        let mut newline = false;
        let mut previous = None;
        shaper.shape_with(|glyph_cluster| {
            let SourceRange { start, end } = glyph_cluster.source;
            if start == 0 {
                newline = !newline;
                if newline {
                    string = strings.next().expect("expected string iterator to yield");
                } else {
                    return;
                }
            }

            let Ok(entry_key) = CharDictionaryKey::try_from(string, start as usize, end as usize)
            else {
                return;
            };
            let glyphs = glyph_cluster
                .glyphs
                .iter()
                .filter(|glyph| glyph.id > 0 || entry_key.as_ref() == "\u{FFFD}")
                .peekable();

            let mut advance_width = 0.0;
            let mut glyph_offsets = Vec::new();
            for (glyph, is_overlay) in glyphs.zip(iter::once(false).chain(iter::repeat(true))) {
                advance_width += glyph.advance;
                glyph_offsets.push({
                    let mut glyph_offsets = GlyphOffsets::from_glyph(glyph, is_overlay);
                    glyph_offsets.patch(glyph_spacing.is_code && !is_fallback);

                    glyph_offsets
                });
            }

            let advance_width = glyph_spacing.halfwidths(advance_width);

            if !glyph_offsets.is_empty() || glyph_cluster.is_empty() {
                if !entries.contains_key(&entry_key) {
                    let glyphs = glyph_offsets.into_iter().map(&mut render).collect();
                    entries.insert_glyphs(entry_key.clone(), glyphs);
                }

                if is_fallback {
                    return;
                }

                let to_entry_key = entry_key.clone();
                if let Some(previous) = previous.replace((entry_key, advance_width)) {
                    let (entry_key, advance_width) = previous;
                    entries.insert_advance_width(entry_key, to_entry_key, advance_width);
                }
            } else {
                previous = None;
            }
        });
    }
}