retex-lua 1.0.0

Embedded Lua and the LuaTeX Lua API for retex
Documentation
//! `luaotfload.aux`: the package author's interface of luaotfload
//! (luaotfload manual, section 11.2), as far as packages use it.
//!
//! The behaviour was established black-box with lualatex (luaotfload
//! 3.29):
//!
//! * `provides_script(id, script)`, `provides_language(id, script, lang)`
//!   return `true` or `false`. Script and language tags compare
//!   case-insensitively; `dflt` names the `DFLT` script and the default
//!   language system.
//! * `provides_feature(id, script, lang, feature)` returns `true` when the
//!   feature (case-sensitive) is enabled for the language system in
//!   `GSUB` or `GPOS`, and *nothing* (no value) otherwise. For fonts that
//!   are not OpenType fonts and for unknown ids, all three return `false`.
//! * `get_math_dimension(font, name)` takes a font id or a font identifier
//!   name and returns the `MathConstants` entry `name` (an integer for
//!   percentages, an unrounded float for lengths), or 0.
//! * `font_has_glyph(id, code)` tells whether the font has the character;
//!   `slot_of_name(id, name)` gives the character code of the glyph named
//!   `name` (or `false`), `gid_of_name(id, name)` its glyph id (or nothing).

use mlua::{Lua, MultiValue, Table, Value};
use retex_engine::Engine;
use retex_font::texfont::MathValue;

use crate::luaotfload::loaded_font;
use crate::with_engine;

/// Resolves a font argument: an id, or the name of a font identifier.
fn font_arg(e: &Engine, v: &Value) -> Option<u32> {
    let id = match v {
        Value::Integer(n) => *n,
        Value::Number(n) => *n as i64,
        Value::String(s) => {
            let cs = e.hash.lookup(&s.as_bytes())?;
            let m = e.eqtb.meaning(cs);
            if m.cmd != retex_engine::cmd::Cmd::SetFont {
                return None;
            }
            i64::from(m.chr)
        }
        _ => return None,
    };
    (id >= 0 && e.font_exists(id as i32)).then_some(id as u32)
}

fn str_arg(v: Option<&Value>) -> String {
    match v {
        Some(Value::String(s)) => s.to_string_lossy(),
        Some(Value::Integer(n)) => n.to_string(),
        _ => String::new(),
    }
}

/// Installs the native aux functions into `native` as `aux_<name>`.
pub fn install(lua: &Lua, native: &Table) -> mlua::Result<()> {
    native.set(
        "aux_provides_script",
        lua.create_function(|lua, args: MultiValue| {
            let a: Vec<Value> = args.into_iter().collect();
            let script = str_arg(a.get(1));
            with_engine(lua, |e| {
                let f = a.first().and_then(|v| font_arg(e, v));
                f.and_then(|f| loaded_font(e, f))
                    .is_some_and(|lf| lf.file.provides_script(&script))
            })
        })?,
    )?;
    native.set(
        "aux_provides_language",
        lua.create_function(|lua, args: MultiValue| {
            let a: Vec<Value> = args.into_iter().collect();
            let (script, lang) = (str_arg(a.get(1)), str_arg(a.get(2)));
            with_engine(lua, |e| {
                let f = a.first().and_then(|v| font_arg(e, v));
                f.and_then(|f| loaded_font(e, f))
                    .is_some_and(|lf| lf.file.provides_language(&script, &lang))
            })
        })?,
    )?;
    native.set(
        "aux_provides_feature",
        lua.create_function(|lua, args: MultiValue| {
            let a: Vec<Value> = args.into_iter().collect();
            let (script, lang, feat) = (str_arg(a.get(1)), str_arg(a.get(2)), str_arg(a.get(3)));
            let r = with_engine(lua, |e| {
                let f = a.first().and_then(|v| font_arg(e, v));
                match f.and_then(|f| loaded_font(e, f)) {
                    None => Some(false),
                    Some(lf) => lf
                        .file
                        .provides_feature(&script, &lang, &feat)
                        .then_some(true),
                }
            })?;
            Ok(match r {
                Some(b) => MultiValue::from_vec(vec![Value::Boolean(b)]),
                None => MultiValue::new(),
            })
        })?,
    )?;
    native.set(
        "aux_get_math_dimension",
        lua.create_function(|lua, (font, name): (Value, Value)| {
            let name = str_arg(Some(&name));
            let v = with_engine(lua, |e| {
                let f = font_arg(e, &font)?;
                loaded_font(e, f)?.tex.math_constant(&name)
            })?;
            Ok(match v {
                Some(MathValue::Percent(p)) => Value::Integer(i64::from(p)),
                Some(MathValue::Scaled(x)) => Value::Number(x),
                None => Value::Integer(0),
            })
        })?,
    )?;
    native.set(
        "aux_font_has_glyph",
        lua.create_function(|lua, (font, code): (Value, i64)| {
            with_engine(lua, |e| {
                font_arg(e, &font).is_some_and(|f| {
                    u32::try_from(code).is_ok_and(|c| e.font(f).chars.contains_key(&c))
                })
            })
        })?,
    )?;
    native.set(
        "aux_slot_of_name",
        lua.create_function(|lua, (font, name): (Value, Value)| {
            let name = str_arg(Some(&name));
            let r = with_engine(lua, |e| {
                let lf = loaded_font(e, font_arg(e, &font)?)?;
                let g = lf.file.glyph_by_name(&name)?;
                lf.tex.code_of_glyph(g)
            })?;
            Ok(r.map_or(Value::Boolean(false), |c| Value::Integer(i64::from(c))))
        })?,
    )?;
    native.set(
        "aux_gid_of_name",
        lua.create_function(|lua, (font, name): (Value, Value)| {
            let name = str_arg(Some(&name));
            let r = with_engine(lua, |e| {
                let lf = loaded_font(e, font_arg(e, &font)?)?;
                lf.file.glyph_by_name(&name)
            })?;
            Ok(match r {
                Some(g) => MultiValue::from_vec(vec![Value::Integer(i64::from(g))]),
                None => MultiValue::new(),
            })
        })?,
    )?;
    Ok(())
}