mplusfonts-macros 0.3.4

Procedural macros re-exported in the mplusfonts crate
Documentation
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};

use crate::mplus::charmap::{Charmap, CharmapEntry};

pub struct BitmapFont {
    pub charmap: Charmap,
    pub notdef: CharmapEntry,
    pub positions: u8,
    pub bit_depth: u8,
    pub size: f32,
    pub is_code: bool,
}

impl ToTokens for BitmapFont {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let Self {
            charmap,
            notdef,
            positions,
            bit_depth,
            size,
            is_code,
        } = self;

        let positions = *positions as usize;
        let params = match bit_depth {
            1 => quote!(::embedded_graphics::pixelcolor::BinaryColor, #positions),
            2 => quote!(::embedded_graphics::pixelcolor::Gray2, #positions),
            4 => quote!(::embedded_graphics::pixelcolor::Gray4, #positions),
            8 => quote!(::embedded_graphics::pixelcolor::Gray8, #positions),
            x => panic!("expected one of: `1`, `2`, `4`, `8`; found: `{x}`"),
        };
        let charmap = charmap_tokens(charmap, notdef, &params);
        let metrics = metrics_tokens(*size, *is_code);
        let underline = underline_tokens(*size);
        let strikethrough = strikethrough_tokens(*size);
        let font = quote! {
            ::mplusfonts::BitmapFont::<#params> {
                charmap: #charmap,
                metrics: #metrics,
                underline: #underline,
                strikethrough: #strikethrough,
            }
        };

        tokens.extend(font);
    }
}

fn charmap_tokens(charmap: &Charmap, notdef: &CharmapEntry, params: &impl ToTokens) -> TokenStream {
    let Charmap(payload, charmap) = charmap;
    let payload = payload.as_ref().unwrap_or(notdef);
    let leaf = quote! {
        ::mplusfonts::Charmap::Leaf(#payload)
    };
    if charmap.is_empty() {
        return leaf;
    }

    let static_ref = |charmap| {
        quote!(const {
            const DATA: ::mplusfonts::Charmap<#params> = #charmap;

            &DATA
        })
    };
    let map = charmap.iter().map(|(key, charmap)| {
        let charmap = charmap_tokens(charmap, payload, params);
        let value = static_ref(charmap);
        quote!(#key => #value)
    });
    let default = static_ref(leaf);
    let branch = quote! {
        ::mplusfonts::Charmap::Branch(
            |key| match key {
                #(#map,)*
                _ => #default
            }
        )
    };

    branch
}

fn metrics_tokens(size: f32, is_code: bool) -> TokenStream {
    let top = size * if is_code { 1.235 } else { 1.16 };
    let ascender = size * if is_code { 1.0 } else { 0.88 };
    let cap_height = size * 0.73;
    let x_height = size * 0.52;
    let baseline = 0f32;
    let descender = size * if is_code { -0.235 } else { -0.12 };
    let bottom = size * if is_code { -0.27 } else { -0.288 };
    let metrics = quote! {
        ::mplusfonts::BitmapFontMetrics {
            top: #top,
            ascender: #ascender,
            cap_height: #cap_height,
            x_height: #x_height,
            baseline: #baseline,
            descender: #descender,
            bottom: #bottom,
        }
    };

    metrics
}

fn underline_tokens(size: f32) -> TokenStream {
    let offset = size * -0.1;
    let height = size * 0.05;
    let dimensions = quote! {
        ::mplusfonts::DecorationDimensions {
            offset: #offset,
            height: #height,
        }
    };

    dimensions
}

fn strikethrough_tokens(size: f32) -> TokenStream {
    let offset = size * 0.312;
    let height = size * 0.05;
    let dimensions = quote! {
        ::mplusfonts::DecorationDimensions {
            offset: #offset,
            height: #height,
        }
    };

    dimensions
}