retex-lua 1.2.0

Embedded Lua and the LuaTeX Lua API for retex
Documentation
//! Converting LuaTeX font tables (as returned by `define_font` callbacks
//! or `font.define`) into engine fonts (LuaTeX manual chapter 6).

use std::collections::HashMap;

use mlua::Table;
use retex_engine::fonts::{CharMetrics, FontInfo, FontKind, LigKernStep, LigOp};

/// Builds a [`FontInfo`] from a Lua font table.
pub fn font_from_table(t: &Table) -> mlua::Result<FontInfo> {
    let mut info = FontInfo::null();
    info.name = t
        .get::<Option<mlua::String>>("name")?
        .map(|s| s.as_bytes().to_vec())
        .unwrap_or_default();
    info.size = t.get::<Option<i32>>("size")?.unwrap_or(655360);
    // LuaTeX's default design size is 10pt, whatever the size.
    info.design_size = t.get::<Option<i32>>("designsize")?.unwrap_or(655360);
    info.checksum = t.get::<Option<i64>>("checksum")?.unwrap_or(0) as u32;
    let mut params = vec![0; 8];
    // stretch, shrink and step replace a later \expandglyphsinfont.
    if let (Some(st), Some(sh), Some(sp)) = (
        t.get::<Option<i32>>("stretch")?,
        t.get::<Option<i32>>("shrink")?,
        t.get::<Option<i32>>("step")?,
    ) && (st > 0 || sh > 0)
        && sp > 0
    {
        info.expansion = Some(retex_engine::expansion::FontExpansion {
            stretch: st,
            shrink: sh,
            step: sp,
        });
    }
    if let Some(p) = t.get::<Option<Table>>("parameters")? {
        let names = [
            "slant",
            "space",
            "space_stretch",
            "space_shrink",
            "x_height",
            "quad",
            "extra_space",
        ];
        for (i, n) in names.iter().enumerate() {
            if let Some(v) = p.get::<Option<i32>>(*n)? {
                params[i + 1] = v;
            } else if let Some(v) = p.get::<Option<i32>>(i as i64 + 1)? {
                params[i + 1] = v;
            }
        }
    }
    info.params = params;
    let mut chars: HashMap<_, _, retex_engine::hash::FastHash> = HashMap::default();
    if let Some(cs) = t.get::<Option<Table>>("characters")? {
        for pair in cs.pairs::<i64, Table>() {
            let (c, ct) = pair?;
            chars.insert(
                c as u32,
                CharMetrics {
                    width: ct.get::<Option<i32>>("width")?.unwrap_or(0),
                    height: ct.get::<Option<i32>>("height")?.unwrap_or(0),
                    depth: ct.get::<Option<i32>>("depth")?.unwrap_or(0),
                    italic: ct.get::<Option<i32>>("italic")?.unwrap_or(0),
                    index: ct.get::<Option<u32>>("index")?.unwrap_or(c as u32),
                },
            );
            // `ligatures[next] = { char, type }` and `kerns[next]`.
            let mut steps = Vec::new();
            if let Some(l) = ct.get::<Option<Table>>("ligatures")? {
                for pair in l.pairs::<i64, Table>() {
                    let (next, lt) = pair?;
                    let chr = lt.get::<Option<u32>>("char")?.unwrap_or(0);
                    let op = lt.get::<Option<u8>>("type")?.unwrap_or(0);
                    steps.push(LigKernStep {
                        next: next as u32,
                        op: LigOp::Lig { op, chr },
                    });
                }
            }
            if let Some(k) = ct.get::<Option<Table>>("kerns")? {
                for pair in k.pairs::<i64, i32>() {
                    let (next, amount) = pair?;
                    steps.push(LigKernStep {
                        next: next as u32,
                        op: LigOp::Kern(amount),
                    });
                }
            }
            if !steps.is_empty() {
                steps.sort_by_key(|st| st.next);
                info.lig_kern.insert(c as u32, steps.into_boxed_slice());
            }
        }
    }
    info.chars = chars;
    if let Some(mc) = t.get::<Option<Table>>("MathConstants")? {
        let mut m = retex_engine::fonts::MathConstants::new();
        for pair in mc.pairs::<String, f64>() {
            let (k, v) = pair?;
            m.insert(k, v.round() as i32);
        }
        info.math_constants = Some(std::rc::Rc::new(m));
    }
    info.hyphen_char = t.get::<Option<i32>>("hyphenchar")?.unwrap_or(i32::MIN);
    info.skew_char = t.get::<Option<i32>>("skewchar")?.unwrap_or(i32::MIN);
    info.kind = FontKind::OpenType;
    Ok(info)
}