mplusfonts-macros 0.3.4

Procedural macros re-exported in the mplusfonts crate
Documentation
mod glyph;

use std::collections::BTreeMap;
use std::iter;
use std::sync::RwLock;
use std::thread;

use glyph::{GlyphDrawing, GlyphMetrics};

use crate::mplus::Arguments;
use crate::mplus::bitmap::CharDictionary;
use crate::mplus::charmap::CharmapEntry;

pub fn render(args: &Arguments) -> BTreeMap<String, CharmapEntry> {
    let entries = BTreeMap::new();
    let font = args.font.value();
    let pixels_per_em = args.size.into_value();
    let hint = args.hint.into_value();
    let glyph_metrics = GlyphMetrics::from_font(font, pixels_per_em, hint);
    let positions = args.positions.into_value();
    let bit_depth = args.bit_depth.into_value();
    let strings = args.sources.iter().flat_map(|source| source.strings(false));
    let indices = 0..thread::available_parallelism().map(Into::into).unwrap_or(1);
    let mut glyph_drawings: Vec<_> = iter::repeat_with(Vec::new).take(indices.end).collect();
    let mut indices = indices.cycle();
    for strings in strings {
        strings
            .iter()
            .flat_map(|string| string.chars())
            .filter_map(GlyphDrawing::get)
            .zip(iter::repeat_with(|| indices.next().unwrap_or_default()))
            .for_each(|(glyph_drawing, index)| glyph_drawings[index].push(glyph_drawing));
    }

    let entries = RwLock::new(entries);
    thread::scope(|scope| {
        let entries = CharDictionary::new(&entries);
        let glyph_metrics = &glyph_metrics;
        for glyph_drawings in glyph_drawings {
            scope.spawn(move || {
                for glyph_drawing in glyph_drawings {
                    let entry_key = glyph_drawing.key.into();
                    if !entries.contains_key(&entry_key) {
                        let glyphs = glyph_drawing.scale(positions, bit_depth, glyph_metrics);
                        entries.insert_glyphs(entry_key, glyphs);
                    }
                }
            });
        }
    });

    entries
        .into_inner()
        .expect("expected no-poison lock on entries")
}