diff options
Diffstat (limited to 'source/luametatex/source/lua/lmtfontlib.c')
-rw-r--r-- | source/luametatex/source/lua/lmtfontlib.c | 1020 |
1 files changed, 1020 insertions, 0 deletions
diff --git a/source/luametatex/source/lua/lmtfontlib.c b/source/luametatex/source/lua/lmtfontlib.c new file mode 100644 index 000000000..09429d98a --- /dev/null +++ b/source/luametatex/source/lua/lmtfontlib.c @@ -0,0 +1,1020 @@ +/* + See license.txt in the root of this project. +*/ + +# include "luametatex.h" + +/*tex + + There is not that much font related code because much is delegated to \LUA. We're actually back + to original \TEX, where only dimensions matter, plus some basic information about constructing + (base mode) ligatures and (base mode) kerning. Also, we need to store some math specific + properties of glyphs so that the math machinery can do its work. + + Compared to traditional \TEX\ the most impressive extension is the amount of new math parameters. + There are also some new concepts, like staircase kerns. In the typesetting related code this is + reflected in dedicated code paths. + + The code is different from the code in \LUATEX. Because we don't have a backend built in, we need + to store less. Also, there are quite some optimizations so that large fonts consume less memory. + After all, we have whatever is available already in \LUA\ tables. The engine only needs a few + dimensions to work with, plus some ligature and kern information for old school fonts, and when + applicable some additional data that relates to math. So, for instance, we no longer allocate + memory when we have no math. + + We start with some tables to which we might do more with this data and add more entries at some + point. We are prepared. + + */ + + +void lmt_fontlib_initialize(void) { + /* nothing */ +} + +static int valid_math_parameter(lua_State *L, int narg) { + const char *s = lua_tostring(L, narg); + if (s) { + for (int i = 1; lmt_interface.math_font_parameter_values[i].name; i++) { + if (lmt_interface.math_font_parameter_values[i].name == s) { + return i; + } + } + } + return -1; +} + +/* + Most of these special ligature indicators have never been used by fonts but they are part of + \TEX's legacy so of course we keep them around! + +*/ + +static const char *lmt_ligature_type_strings[] = { + "=:", "=:|", "|=:", "|=:|", "", "=:|>", "|=:>", "|=:|>", "", "", "", "|=:|>>", NULL +}; + +static int fontlib_aux_count_hash_items(lua_State *L) +{ + int n = 0; + if (lua_type(L, -1) == LUA_TTABLE) { + lua_pushnil(L); + while (lua_next(L, -2)) { + n++; + lua_pop(L, 1); + } + } + return n; +} + +/*tex + + These macros set a field in the font or character records. Watch how we make a copy of a string! + +*/ + +/* +# define set_numeric_field_by_index(target,name,dflt) \ + lua_key_rawgeti(name); \ + target = (lua_type(L, -1) == LUA_TNUMBER) ? lmt_roundnumber(L, -1) : dflt ; \ + lua_pop(L, 1); + +# define set_boolean_field_by_index(target,name,dflt) \ + lua_key_rawgeti(name); \ + target = (lua_type(L, -1) == LUA_TBOOLEAN) ? lua_toboolean(L, -1) : dflt ; \ + lua_pop(L, 1); + +# define set_string_field_by_index(target,name) \ + lua_key_rawgeti(name); \ + target = (lua_type(L, -1) == LUA_TSTRING) ? lua_tostring(L, -1) : NULL ; \ + lua_pop(L, 1); +*/ + +# define set_numeric_field_by_index(target,name,dflt) \ + lua_push_key(name); \ + target = (lua_rawget(L, -2) == LUA_TNUMBER) ? lmt_roundnumber(L, -1) : dflt ; \ + lua_pop(L, 1); + +# define set_boolean_field_by_index(target,name,dflt) \ + lua_push_key(name); \ + target = (lua_rawget(L, -2) == LUA_TBOOLEAN) ? lua_toboolean(L, -1) : dflt ; \ + lua_pop(L, 1); + +# define set_string_field_by_index(target,name) \ + lua_push_key(name); \ + target = (lua_rawget(L, -2) == LUA_TSTRING) ? lua_tostring(L, -1) : NULL ; \ + lua_pop(L, 1); + +# define set_any_field_by_index(target,name) \ + lua_push_key(name); \ + target = (lua_rawget(L, -2) != LUA_TNIL); \ + lua_pop(L, 1); + +/*tex + + Font parameters can be set by number or by name. There are seven basic \TEX\ parameters in text + mode but in math mode there can be numerous. + +*/ + +static void fontlib_aux_read_lua_parameters(lua_State *L, int f) +{ + lua_push_key(parameters); + if (lua_rawget(L, -2) == LUA_TTABLE) { + /*tex We determine the the number of parameters in the |max(nofintegerkeys(L), 7)|. */ + int maxindex = 7; + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_type(L, -2) == LUA_TNUMBER) { + int i = (int) lua_tointeger(L, -2); + if (i > maxindex) { + maxindex = i; + } + } + lua_pop(L, 1); + } + /*tex + We enlarge the parameter array. The first zeven values are already initialized to zero + when the font structure is allocated. + */ + if (maxindex > 7) { + tex_set_font_parameters(f, maxindex); + } + /*tex + First we pick up the numeric entries. The values set with keys can later overload + these. It's there for old times sake, because numeric parameters are gone. + */ + for (int i = 1; i <= maxindex; i++) { + if (lua_rawgeti(L, -1, i) == LUA_TNUMBER) { + halfword value = lmt_roundnumber(L, -1); + tex_set_font_parameter(f, i, value); + } + lua_pop(L, 1); + } + lua_pushnil(L); + while (lua_next(L, -2)) { + halfword value = lua_type(L, -1) == LUA_TNUMBER ? lmt_roundnumber(L, -1) : 0; + switch (lua_type(L, -2)) { + case LUA_TSTRING: + { + /* These can overload the already set-by-index values. */ + const char *s = lua_tostring(L, -2); + if (lua_key_eq(s, slant)) { + tex_set_font_parameter(f, slant_code, value); + } else if (lua_key_eq(s, space)) { + tex_set_font_parameter(f, space_code, value); + } else if (lua_key_eq(s, spacestretch)) { + tex_set_font_parameter(f, space_stretch_code, value); + } else if (lua_key_eq(s, spaceshrink)) { + tex_set_font_parameter(f, space_shrink_code, value); + } else if (lua_key_eq(s, xheight)) { + tex_set_font_parameter(f, ex_height_code, value); + } else if (lua_key_eq(s, quad)) { + tex_set_font_parameter(f, em_width_code, value); + } else if (lua_key_eq(s, extraspace)) { + tex_set_font_parameter(f, extra_space_code, value); + } + } + break; + case LUA_TNUMBER: + { + /* Math fonts can have more than 7. */ + int index = (int) lua_tointeger(L, -2); + if (index >= 8) { + tex_set_font_parameter(f, index, value); + } + } + break; + } + lua_pop(L, 1); + } + } + lua_pop(L, 1); + +} + +static void fontlib_aux_read_lua_math_parameters(lua_State *L, int f) +{ + lua_push_key(MathConstants); + if (lua_rawget(L, -2) == LUA_TTABLE) { + lua_pushnil(L); + while (lua_next(L, -2)) { + int n = (int) lmt_roundnumber(L, -1); + int i = 0; + switch (lua_type(L, -2)) { + case LUA_TSTRING: + i = valid_math_parameter(L, -2); + break; + case LUA_TNUMBER: + i = (int) lua_tointeger(L, -2); + break; + } + if (i > 0) { + // set_font_math_parameter(f, i, n); + tex_set_font_math_parameters(f, i); + // if (n > undefined_math_parameter || i < - undefined_math_parameter) { + // n = undefined_math_parameter; + // } + font_math_parameter(f, i) = n; + } + lua_pop(L, 1); + } + set_font_oldmath(f, 0); + } else { + set_font_oldmath(f, 1); + } + lua_pop(L, 1); +} + +/*tex + + Math kerns are tables that specify a staircase. There are upto four such lists, one for each + corner. Here is a complete example: + + \starttyping + mathkerns = { + bottom_left = { { height = 420, kern = 80 }, { height = 520, kern = 4 } }, + bottom_right = { { height = 0, kern = 48 } }, + top_left = { { height = 620, kern = 0 }, { height = 720, kern = -80 } }, + top_right = { { height = 676, kern = 115 }, { height = 776, kern = 45 } }, + } + \stoptyping + +*/ + +static void fontlib_aux_store_math_kerns(lua_State *L, int index, charinfo *co, int id) +{ + lua_push_key_by_index(index); + if (lua_rawget(L, -2) == LUA_TTABLE) { + lua_Integer k = lua_rawlen(L, -1); + if (k > 0) { + for (lua_Integer l = 1; l <= k; l++) { + if (lua_rawgeti(L, -1, l) == LUA_TTABLE) { + scaled ht, krn; +// set_numeric_field_by_index(ht, height, min_infinity); +// set_numeric_field_by_index(krn, kern, min_infinity); +// if (krn > min_infinity && ht > min_infinity) { +// tex_add_charinfo_math_kern(co, id, ht, krn); +// } + set_numeric_field_by_index(ht, height, 0); + set_numeric_field_by_index(krn, kern, 0); + if (krn || ht) { + tex_add_charinfo_math_kern(co, id, ht, krn); + } + } + lua_pop(L, 1); + } + } + } + lua_pop(L, 1); +} + +static void fontlib_aux_font_char_from_lua(lua_State *L, halfword f, int i, int has_math) +{ + if (lua_istable(L, -1)) { + /*tex We need an intermediate veriable: */ + scaled target; + int state; + charinfo *co = tex_get_charinfo(f, i); + set_any_field_by_index(state, callback); + set_charinfo_tag(co, state ? callback_tag : 0); + set_numeric_field_by_index(target, width, 0); + set_charinfo_width(co, target); + set_numeric_field_by_index(target, height, 0); + set_charinfo_height(co, target); + set_numeric_field_by_index(target, depth, 0); + set_charinfo_depth(co, target); + set_numeric_field_by_index(target, italic, 0); + set_charinfo_italic(co, target); + set_numeric_field_by_index(target, expansion, 1000); + set_charinfo_expansion(co, target); + set_numeric_field_by_index(target, leftprotrusion, 0); + set_charinfo_leftprotrusion(co, target); + set_numeric_field_by_index(target, rightprotrusion, 0); + set_charinfo_rightprotrusion(co, target); + set_charinfo_tag(co, 0); + if (has_math) { + tex_char_malloc_mathinfo(co); + set_numeric_field_by_index(target, smaller, 0); + set_charinfo_smaller(co, target); + set_numeric_field_by_index(target, vitalic, 0); + set_charinfo_vertical_italic(co, target); + /* */ + set_numeric_field_by_index(target, topleft, 0); + set_charinfo_top_left_kern(co, target); + set_numeric_field_by_index(target, topright, 0); + set_charinfo_top_right_kern(co, target); + set_numeric_field_by_index(target, bottomright, 0); + set_charinfo_bottom_right_kern(co, target); + set_numeric_field_by_index(target, bottomleft, 0); + set_charinfo_bottom_left_kern(co, target); + /* */ + set_numeric_field_by_index(target, leftmargin, 0); + set_charinfo_left_margin(co, target); + set_numeric_field_by_index(target, rightmargin, 0); + set_charinfo_right_margin(co, target); + set_numeric_field_by_index(target, topmargin, 0); + set_charinfo_top_margin(co, target); + set_numeric_field_by_index(target, bottommargin, 0); + set_charinfo_bottom_margin(co, target); + /* */ + // set_numeric_field_by_index(target, options, 0); + // set_charinfo_options(co, target); + set_numeric_field_by_index(target, topaccent, INT_MIN); + set_charinfo_top_accent(co, target); + set_numeric_field_by_index(target, bottomaccent, INT_MIN); + set_charinfo_bottom_accent(co, target); + set_numeric_field_by_index(target, flataccent, INT_MIN); + set_charinfo_flat_accent(co, target); + set_numeric_field_by_index(target, next, -1); + if (target >= 0) { + set_charinfo_tag(co, list_tag); + set_charinfo_remainder(co, target); + } + lua_push_key(extensible); + switch (lua_rawget(L, -2)) { + case LUA_TTABLE: + { + int top, bottom, middle, extender; + set_numeric_field_by_index(top, top, 0); + set_numeric_field_by_index(bottom, bottom, 0); + set_numeric_field_by_index(middle, middle, 0); + set_numeric_field_by_index(extender, extender, 0); + if (top || bottom || middle || extender) { + set_charinfo_tag(co, extension_tag); + tex_set_charinfo_extensible(co, top, bottom, middle, extender); + } else { + tex_formatted_warning("font", "lua-loaded font %s char U+%X has an invalid extensible field", font_name(f), (int) i); + } + } + break; + case LUA_TBOOLEAN: + if (lua_toboolean(L, -2)) { + set_charinfo_tag(co, extend_last_tag); + } + break; + } + lua_pop(L, 1); + lua_push_key(hparts); + if (lua_rawget(L, -2) == LUA_TTABLE) { + set_charinfo_tag(co, extension_tag); + tex_set_charinfo_horizontal_parts(co, NULL); + for (lua_Integer k = 1; ; k++) { + if (lua_rawgeti(L, -1, k) == LUA_TTABLE) { + int glyph, startconnect, endconnect, advance, extender; + extinfo *h; + set_numeric_field_by_index(glyph, glyph, 0); + set_numeric_field_by_index(extender, extender, 0); + set_numeric_field_by_index(startconnect, start, 0); + set_numeric_field_by_index(endconnect, end, 0); + set_numeric_field_by_index(advance, advance, 0); + h = tex_new_charinfo_part(glyph, startconnect, endconnect, advance, extender); + tex_add_charinfo_horizontal_part(co, h); + lua_pop(L, 1); + } else { + lua_pop(L, 1); + break; + } + } + } + lua_pop(L, 1); + lua_push_key(vparts); + if (lua_rawget(L, -2) == LUA_TTABLE) { + set_charinfo_tag(co, extension_tag); + tex_set_charinfo_vertical_parts(co, NULL); + for (lua_Integer k = 1; ; k++) { + if (lua_rawgeti(L, -1, k) == LUA_TTABLE) { + int glyph, startconnect, endconnect, advance, extender; + extinfo *h; + set_numeric_field_by_index(glyph, glyph, 0); + set_numeric_field_by_index(extender, extender, 0); + set_numeric_field_by_index(startconnect, start, 0); + set_numeric_field_by_index(endconnect, end, 0); + set_numeric_field_by_index(advance, advance, 0); + h = tex_new_charinfo_part(glyph, startconnect, endconnect, advance, extender); + tex_add_charinfo_vertical_part(co, h); + lua_pop(L, 1); + } else { + lua_pop(L, 1); + break; + } + } + } + lua_pop(L, 1); + lua_push_key(mathkerns); + if (lua_rawget(L, -2) == LUA_TTABLE) { + fontlib_aux_store_math_kerns(L, lua_key_index(topleft), co, top_left_kern); + fontlib_aux_store_math_kerns(L, lua_key_index(topright), co, top_right_kern); + fontlib_aux_store_math_kerns(L, lua_key_index(bottomright), co, bottom_right_kern); + fontlib_aux_store_math_kerns(L, lua_key_index(bottomleft), co, bottom_left_kern); + } + lua_pop(L, 1); + } + /*tex Maybe some kerns: */ + lua_push_key(kerns); + if (lua_rawget(L, -2) == LUA_TTABLE) { + int count = fontlib_aux_count_hash_items(L); + if (count > 0) { + /*tex The kerns table is still on stack. */ + kerninfo *ckerns = lmt_memory_calloc((size_t) count + 1, sizeof(kerninfo)); + if (ckerns) { + int ctr = 0; + /*tex Traverse the hash. */ + lua_pushnil(L); + while (lua_next(L, -2)) { + int k = non_boundary_char; + switch (lua_type(L, -2)) { + case LUA_TNUMBER: + /*tex Adjacent char: */ + k = (int) lua_tointeger(L, -2); + if (k < 0) { + k = non_boundary_char; + } + break; + case LUA_TSTRING: + { + const char *s = lua_tostring(L, -2); + if (lua_key_eq(s, rightboundary)) { + k = right_boundary_char; + if (! font_has_right_boundary(f)) { + set_font_right_boundary(f, tex_get_charinfo(f, right_boundary_char)); + } + } + } + break; + } + target = lmt_roundnumber(L, -1); + if (k != non_boundary_char) { + set_kern_item(ckerns[ctr], k, target); + ctr++; + } else { + tex_formatted_warning("font", "lua-loaded font %s char U+%X has an invalid kern field", font_name(f), (int) i); + } + lua_pop(L, 1); + } + /*tex A guard against empty tables. */ + if (ctr > 0) { + set_kern_item(ckerns[ctr], end_kern, 0); + set_charinfo_kerns(co, ckerns); + } else { + tex_formatted_warning("font", "lua-loaded font %s char U+%X has an invalid kerns field", font_name(f), (int) i); + } + } else { + tex_overflow_error("font", (count + 1) * sizeof(kerninfo)); + } + } + } + lua_pop(L, 1); + /*tex Sometimes ligatures: */ + lua_push_key(ligatures); + if (lua_rawget(L, -2) == LUA_TTABLE) { + int count = fontlib_aux_count_hash_items(L); + if (count > 0) { + /*tex The ligatures table still on stack. */ + ligatureinfo *cligs = lmt_memory_calloc((size_t) count + 1, sizeof(ligatureinfo)); + if (cligs) { + int ctr = 0; + /*tex Traverse the hash. */ + lua_pushnil(L); + while (lua_next(L, -2)) { + int k = non_boundary_char; + int r = -1; + switch (lua_type(L, -2)) { + case LUA_TNUMBER: + /*tex Adjacent char: */ + k = (int) lua_tointeger(L, -2); + if (k < 0) { + k = non_boundary_char; + } + break; + case LUA_TSTRING: + { + const char *s = lua_tostring(L, -2); + if (lua_key_eq(s, rightboundary)) { + k = right_boundary_char; + if (! font_has_right_boundary(f)) { + set_font_right_boundary(f, tex_get_charinfo(f, right_boundary_char)); + } + } + } + break; + } + if (lua_istable(L, -1)) { + /*tex Ligature: */ + set_numeric_field_by_index(r, char, -1); + } + if (r != -1 && k != non_boundary_char) { + int ligtarget = 0; + lua_push_key(type); + switch (lua_rawget(L, -2)) { + case LUA_TNUMBER: + ligtarget = lmt_tointeger(L, -1); + break; + case LUA_TSTRING: + { + const char *value = lua_tostring(L, -1); + int index = 0; + while (lmt_ligature_type_strings[index]) { + if (strcmp(lmt_ligature_type_strings[index], value) == 0) { + ligtarget = index; + break; + } else { + index++; + } + } + } + break; + default: + break; + } + lua_pop(L, 1); + set_ligature_item(cligs[ctr], (ligtarget * 2) + 1, k, r); + ctr++; + } else { + tex_formatted_warning("font", "lua-loaded font %s char U+%X has an invalid ligature field", font_name(f), (int) i); + } + /*tex The iterator value: */ + lua_pop(L, 1); + } + /*tex A guard against empty tables. */ + if (ctr > 0) { + set_ligature_item(cligs[ctr], 0, end_of_ligature_code, 0); + set_charinfo_ligatures(co, cligs); + } else { + tex_formatted_warning("font", "lua-loaded font %s char U+%X has an invalid ligatures field", font_name(f), (int) i); + } + } else { + tex_overflow_error("font", (count + 1) * sizeof(ligatureinfo)); + } + } + } + lua_pop(L, 1); + } +} + +/*tex + + The caller has to fix the state of the lua stack when there is an error! + +*/ + +static int lmt_font_from_lua(lua_State *L, int f) +{ + /*tex The table is at stack |index -1| */ + const char *nstr ; + set_string_field_by_index(nstr, name); + tex_set_font_name(f, nstr); + if (nstr) { + const char *ostr = NULL; + int no_math = 0; + int j; + set_string_field_by_index(ostr, original); + tex_set_font_original(f, ostr ? ostr : nstr); + set_numeric_field_by_index(j, designsize, 655360); + set_font_design_size(f, j); + set_numeric_field_by_index(j, size, font_design_size(f)); + set_font_size(f, j); + set_boolean_field_by_index(j, oldmath, 0); + set_font_oldmath(f, j); + set_boolean_field_by_index(j, compactmath, 0); + set_font_compactmath(f, j); + set_numeric_field_by_index(j, mathcontrol, 0); + set_font_mathcontrol(f, j); + set_numeric_field_by_index(j, textcontrol, 0); + set_font_textcontrol(f, j); + set_numeric_field_by_index(j, textscale, 0); + set_font_textsize(f, j); + set_numeric_field_by_index(j, scriptscale, 0); + set_font_scriptsize(f, j); + set_numeric_field_by_index(j, scriptscriptscale, 0); + set_font_scriptscriptsize(f, j); + set_numeric_field_by_index(j, hyphenchar, default_hyphen_char_par); + set_font_hyphen_char(f, j); + set_numeric_field_by_index(j, skewchar, default_skew_char_par); + set_font_skew_char(f, j); + set_boolean_field_by_index(no_math, nomath, 0); + fontlib_aux_read_lua_parameters(L, f); + if (no_math) { + set_font_oldmath(f, 1); + } else { + fontlib_aux_read_lua_math_parameters(L, f); + set_boolean_field_by_index(j, oldmath, 0); + set_font_oldmath(f, j); + } + /*tex The characters. */ + lua_push_key(characters); + if (lua_rawget(L, -2) == LUA_TTABLE) { + /*tex Find the array size values; |num| holds the number of characters to add. */ + int num = 0; + int last = 0; + int first = -1; + /*tex The first key: */ + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_isnumber(L, -2)) { + int i = (int) lua_tointeger(L, -2); + if (i >= 0 && lua_istable(L, -1)) { + num++; + if (i > last) { + last = i; + } + if (first < 0) { + first = i; + } + if (first >= 0 && i < first) { + first = i; + } + } + } + lua_pop(L, 1); + } + if (num > 0) { + int fstep = 0; + tex_font_malloc_charinfo(f, num); + set_font_first_character(f, first); + set_font_last_character(f, last); + /*tex The first key: */ + lua_pushnil(L); + while (lua_next(L, -2)) { + switch (lua_type(L, -2)) { + case LUA_TNUMBER: + { + int i = lmt_tointeger(L, -2); + if (i >= 0) { + fontlib_aux_font_char_from_lua(L, f, i, ! no_math); + } + } + break; + case LUA_TSTRING: + { + const char *b = lua_tostring(L, -2); + if (lua_key_eq(b, leftboundary)) { + fontlib_aux_font_char_from_lua(L, f, left_boundary_char, ! no_math); + } else if (lua_key_eq(b, rightboundary)) { + fontlib_aux_font_char_from_lua(L, f, right_boundary_char, ! no_math); + } + } + break; + } + lua_pop(L, 1); + } + lua_pop(L, 1); + /*tex + + Handle font expansion last: We permits virtual fonts to use expansion as one + can always turn it off. + + */ + set_numeric_field_by_index(fstep, step, 0); + if (fstep > 0) { + int fstretch = 0; + int fshrink = 0; + if (fstep > 100) { + fstep = 100; + } + set_numeric_field_by_index(fshrink, shrink, 0); + set_numeric_field_by_index(fstretch, stretch, 0); + if (fshrink < 0) { + fshrink = 0; + } else if (fshrink > 500) { + fshrink = 500; + } + fshrink -= (fshrink % fstep); + if (fshrink < 0) { + fshrink = 0; + } + if (fstretch < 0) { + fstretch = 0; + } else if (fstretch > 1000) { + fstretch = 1000; + } + fstretch -= (fstretch % fstep); + if (fstretch < 0) { + fstretch = 0; + } + set_font_step(f, fstep); + set_font_max_stretch(f, fstretch); + set_font_max_shrink(f, fshrink); + } + } else { + tex_formatted_warning("font", "lua-loaded font '%d' with name '%s' has no characters", f, font_name(f)); + } + } else { + tex_formatted_warning("font", "lua-loaded font '%d' with name '%s' has no character table", f, font_name(f)); + } + return 1; + } else { + return tex_formatted_error("font", "lua-loaded font '%d' has no name!", f); + } +} + +static int lmt_characters_from_lua(lua_State *L, int f) +{ + int no_math; + /*tex Speedup: */ + set_boolean_field_by_index(no_math, nomath, 0); + /*tex The characters: */ + lua_push_key(characters); + if (lua_rawget(L, -2) == LUA_TTABLE) { + /*tex Find the array size values; |num| has the amount. */ + int num = 0; + int todo = 0; + int bc = font_first_character(f); + int ec = font_last_character(f); + /*tex First key: */ + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_isnumber(L, -2)) { + int i = lmt_tointeger(L, -2); + if (i >= 0 && lua_istable(L, -1)) { + todo++; + if (! quick_char_exists(f, i)) { + num++; + if (i > ec) { + ec = i; + } + if (bc < 0) { + bc = i; + } + if (bc >= 0 && i < bc) { + bc = i; + } + } + } + } + lua_pop(L, 1); + } + if (todo > 0) { + tex_font_malloc_charinfo(f, num); + set_font_first_character(f, bc); + set_font_last_character(f, ec); + /*tex First key: */ + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_type(L, -2) == LUA_TNUMBER) { + int i = lmt_tointeger(L, -2); + if (i >= 0) { + if (quick_char_exists(f, i)) { + charinfo *co = tex_get_charinfo(f, i); + set_charinfo_ligatures(co, NULL); + set_charinfo_kerns(co, NULL); + set_charinfo_math(co, NULL); + tex_set_charinfo_vertical_parts(co, NULL); + tex_set_charinfo_horizontal_parts(co, NULL); + } + fontlib_aux_font_char_from_lua(L, f, i, ! no_math); + } + } + lua_pop(L, 1); + } + lua_pop(L, 1); + } + } + return 1; +} + +/*tex + + The font library has helpers for defining the font and setting or getting the current font. + Internally fonts are represented by font identifiers: numbers. The zero value represents the + predefined |nullfont| instance. The only way to load a font in \LUAMETATEX\ is to use \LUA. + +*/ + +static int fontlib_current(lua_State *L) +{ + int i = lmt_optinteger(L, 1, 0); + if (i > 0) { + if (tex_is_valid_font(i)) { + tex_set_cur_font(0, i); + } else { + luaL_error(L, "expected a valid font id"); + } + } + lua_pushinteger(L, cur_font_par); + return 1; +} + +static int fontlib_max(lua_State *L) +{ + lua_pushinteger(L, tex_get_font_max_id()); + return 1; +} + +static int fontlib_setfont(lua_State *L) +{ + int i = lmt_checkinteger(L, 1); + if (i) { + luaL_checktype(L, 2, LUA_TTABLE); + if (! tex_is_valid_font(i)) { + return luaL_error(L, "font with id %d is not a valid font", i); + // } else if (font_touched(i)) { + // return luaL_error(L, "font with id %d has been accessed already, changing it is forbidden", i); + } else { + lua_settop(L, 2); + lmt_font_from_lua(L, i); + } + } + return 0; +} + +static int fontlib_addcharacters(lua_State *L) +{ + int i = lmt_checkinteger(L, 1); + if (i) { + luaL_checktype(L, 2, LUA_TTABLE); + if (tex_is_valid_font(i)) { + lua_settop(L, 2); + lmt_characters_from_lua(L, i); + } else { + return luaL_error(L, "invalid font id %d passed", i); + } + } + return 0; +} + +/*tex |font.define(table)| */ + +static int fontlib_define(lua_State *L) +{ + if (lua_type(L, 1) == LUA_TTABLE) { + int i = lmt_optinteger(L, 2, 0); + if (! i) { + i = tex_new_font(); + } else if (! tex_is_valid_font(i)) { + return luaL_error(L, "invalid font id %d passed", i); + } + lua_settop(L, 1); + if (lmt_font_from_lua(L, i)) { + lua_pushinteger(L, i); + return 1; + } else { + lua_pop(L, 1); + tex_delete_font(i); + return luaL_error(L, "font creation failed, error in table"); + } + } else { + return 0; + } +} + +static int fontlib_id(lua_State *L) +{ + if (lua_type(L, 1) == LUA_TSTRING) { + size_t l; + const char *s = lua_tolstring(L, 1, &l); + int cs = tex_string_locate(s, l, 0); + int f = -1; + if (cs == undefined_control_sequence || cs == undefined_cs_cmd || eq_type(cs) != set_font_cmd) { + lua_pushliteral(L, "not a valid font csname"); + } else { + f = eq_value(cs); + } + lua_pushinteger(L, f); + return 1; + } else { + return luaL_error(L, "expected font csname string as argument"); + } +} + +/*tex + + This returns the expected (!) next |fontid|, a first arg |true| will keep the id. This not + really robust as of course fonts can be defined in the meantime! In principle |define| could + handle that but then I also need to add similar functionality to \LUATEX. + +*/ + +static int fontlib_nextid(lua_State *L) +{ + int keep = lua_toboolean(L, 1); + int id = tex_new_font(); + lua_pushinteger(L, id); + if (! keep) { + tex_delete_font(id); + } + return 1; +} + +/*tex + + These are not really that useful but can be used to (for instance) mess with the nullfont + parameters that occasionally are used as defaults. We don't increase the font parameter array + when the upper bound is larger than the initial size. You can forget about that kind of abuse + in \LUAMETATEX. + +*/ + +static int fontlib_aux_valid_fontdimen(lua_State *L, halfword *fnt, halfword *n) +{ + *fnt = lmt_tohalfword(L, 1); + *n = lmt_tohalfword(L, 2); + if (*n > 0 && *n <= font_parameter_count(*fnt)) { + return 1; + } else { + return luaL_error(L, "font with id %i has only %d fontdimens", fnt, n); + } +} + +static int fontlib_setfontdimen(lua_State *L) +{ + halfword fnt, n; + if (fontlib_aux_valid_fontdimen(L, &fnt, &n)) { + tex_set_font_parameter(fnt, n, lmt_tohalfword(L, 3)); + } + return 0; +} + +static int fontlib_getfontdimen(lua_State *L) +{ + halfword fnt, n; + if (fontlib_aux_valid_fontdimen(L, &fnt, &n)) { + lua_pushinteger(L, font_parameter(fnt, n)); + } else { + lua_pushnil(L); + } + return 1; +} + +static int fontlib_getmathspec(lua_State *L) +{ + if (lua_type(L, 1) == LUA_TSTRING) { + size_t lname = 0; + const char *name = lua_tolstring(L, 1, &lname); + halfword cs = tex_string_locate(name, lname, 0); + if (eq_type(cs) == mathspec_cmd) { + halfword ms = eq_value(cs); + if (ms) { + mathcodeval m = tex_get_math_spec(ms); + lua_pushinteger(L, m.class_value); + lua_pushinteger(L, m.family_value); + lua_pushinteger(L, m.character_value); + return 3; + } + } + } + return 0; +} + +static int fontlib_getfontspec(lua_State *L) +{ + if (lua_type(L, 1) == LUA_TSTRING) { + size_t lname = 0; + const char *name = lua_tolstring(L, 1, &lname); + halfword cs = tex_string_locate(name, lname, 0); + if (eq_type(cs) == fontspec_cmd) { + halfword fs = eq_value(cs); + if (fs) { + lua_pushinteger(L, font_spec_identifier(fs)); + lua_pushinteger(L, font_spec_scale(fs)); + lua_pushinteger(L, font_spec_x_scale(fs)); + lua_pushinteger(L, font_spec_y_scale(fs)); + return 4; + } + } + } + return 0; +} + +static int fontlib_getmathindex(lua_State *L) { + halfword index = -1; + switch (lua_type(L, 1)) { + case LUA_TSTRING: + index = valid_math_parameter(L, 1); + break; + case LUA_TNUMBER: + index = lmt_tointeger(L, 1); + break; + } + if (index > 0 && index < math_parameter_last_code) { + lua_pushinteger(L, index); + lua_pushboolean(L, index >= math_parameter_first_engine_code); /* true == engine */ + } else { + lua_pushinteger(L, 0); + lua_pushboolean(L, 0); + } + return 2; +} + +static const struct luaL_Reg fontlib_function_list[] = { + { "current", fontlib_current }, + { "max", fontlib_max }, + { "setfont", fontlib_setfont }, + { "addcharacters", fontlib_addcharacters }, + { "define", fontlib_define }, + { "nextid", fontlib_nextid }, + { "id", fontlib_id }, + { "getfontdimen", fontlib_getfontdimen }, + { "setfontdimen", fontlib_setfontdimen }, + { "getfontspec", fontlib_getfontspec }, + { "getmathspec", fontlib_getmathspec }, + { "getmathindex", fontlib_getmathindex }, + { NULL, NULL }, +}; + +int luaopen_font(lua_State *L) +{ + lua_newtable(L); + luaL_setfuncs(L, fontlib_function_list, 0); + return 1; +} |