lmtfontlib.c /size: 43 Kb    last modification: 2025-02-21 11:03
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6
7/*tex
8
9    There is not that much font related code because much is delegated to \LUA. We're actually back
10    to original \TEX, where only dimensions matter, plus some basic information about constructing
11    (base mode) ligatures and (base mode) kerning. Also, we need to store some math specific
12    properties of glyphs so that the math machinery can do its work.
13
14    Compared to traditional \TEX\ the most impressive extension is the amount of new math parameters.
15    There are also some new concepts, like staircase kerns. In the typesetting related code this is
16    reflected in dedicated code paths.
17
18    The code is different from the code in \LUATEX. Because we don't have a backend built in, we need
19    to store less. Also, there are quite some optimizations so that large fonts consume less memory.
20    After all, we have whatever is available already in \LUA\ tables. The engine only needs a few
21    dimensions to work with, plus some ligature and kern information for old school fonts, and when
22    applicable some additional data that relates to math. So, for instance, we no longer allocate
23    memory when we have no math.
24
25    We start with some tables to which we might do more with this data and add more entries at some
26    point. We are prepared.
27
28 */
29
30
31void lmt_fontlib_initialize(void) {
32    /* nothing */
33}
34
35static int valid_math_parameter(lua_State *L, int narg) {
36    const char *s = lua_tostring(L, narg);
37    if (s) {
38        for (int i = 1; lmt_interface.math_font_parameter_values[i].name; i++) {
39            if (lmt_interface.math_font_parameter_values[i].name == s) {
40                return i;
41            }
42        }
43    }
44    return -1;
45}
46
47/*
48    Most of these special ligature indicators have never been used by fonts but they are part of
49    \TEX's legacy so of course we keep them around!
50
51*/
52
53static const char *lmt_ligature_type_strings[] = {
54    "=:", "=:|", "|=:", "|=:|", "", "=:|>", "|=:>", "|=:|>", "", "", "", "|=:|>>", NULL
55};
56
57static int fontlib_aux_count_hash_items(lua_State *L)
58{
59    int n = 0;
60    if (lua_type(L, -1) == LUA_TTABLE) {
61        lua_pushnil(L);
62        while (lua_next(L, -2)) {
63            n++;
64            lua_pop(L, 1);
65        }
66    }
67    return n;
68}
69
70/*tex
71
72    Font parameters can be set by number or by name. There are seven basic \TEX\ parameters in text
73    mode but in math mode there can be numerous.
74
75*/
76
77static int fontlib_getparameterfields(lua_State *L)
78{
79    lua_createtable(L, 6, 0);
80    lua_push_key_at_index(L, slant,        1);
81    lua_push_key_at_index(L, space,        2);
82    lua_push_key_at_index(L, spacestretch, 3);
83    lua_push_key_at_index(L, spaceshrink,  4);
84    lua_push_key_at_index(L, quad,         5);
85    lua_push_key_at_index(L, extraspace,   6);
86    return 1;
87}
88
89static void fontlib_aux_read_lua_parameters(lua_State *L, int f)
90{
91    lua_push_key(parameters);
92    if (lua_rawget(L, -2) == LUA_TTABLE) {
93        /*tex We determine the the number of parameters in the |max(nofintegerkeys(L), 7)|. */
94        int maxindex = 7;
95        lua_pushnil(L);
96        while (lua_next(L, -2)) {
97            if (lua_type(L, -2) == LUA_TNUMBER) {
98                int i = (int) lua_tointeger(L, -2);
99                if (i > maxindex) {
100                    maxindex = i;
101                }
102            }
103            lua_pop(L, 1);
104        }
105        /*tex
106            We enlarge the parameter array. The first zeven values are already initialized to zero
107            when the font structure is allocated.
108        */
109        if (maxindex > 7) {
110            tex_set_font_parameters(f, maxindex);
111        }
112        /*tex
113            First we pick up the numeric entries. The values set with keys can later overload
114            these. It's there for old times sake, because numeric parameters are gone.
115        */
116        for (int i = 1; i <= maxindex; i++) {
117            if (lua_rawgeti(L, -1, i) == LUA_TNUMBER) {
118                halfword value = lmt_roundnumber(L, -1);
119                tex_set_font_parameter(f, i, value);
120            }
121            lua_pop(L, 1);
122        }
123        lua_pushnil(L);
124        while (lua_next(L, -2)) {
125            halfword value = lua_type(L, -1) == LUA_TNUMBER ? lmt_roundnumber(L, -1) : 0;
126            switch (lua_type(L, -2)) {
127                case LUA_TSTRING:
128                    {
129                        /* These can overload the already set-by-index values. */
130                        const char *s = lua_tostring(L, -2);
131                        if (lua_key_eq(s, slant)) {
132                            tex_set_font_parameter(f, slant_code, value);
133                        } else if (lua_key_eq(s, space)) {
134                            tex_set_font_parameter(f, space_code, value);
135                        } else if (lua_key_eq(s, spacestretch)) {
136                            tex_set_font_parameter(f, space_stretch_code, value);
137                        } else if (lua_key_eq(s, spaceshrink)) {
138                            tex_set_font_parameter(f, space_shrink_code, value);
139                        } else if (lua_key_eq(s, xheight)) {
140                            tex_set_font_parameter(f, ex_height_code, value);
141                        } else if (lua_key_eq(s, quad)) {
142                            tex_set_font_parameter(f, em_width_code, value);
143                        } else if (lua_key_eq(s, extraspace)) {
144                            tex_set_font_parameter(f, extra_space_code, value);
145                        } else { 
146                            break;
147                        }
148// lua_pop(L, 1);
149// lua_push_string(L, s);
150// lua_push_integer(L, value);
151// lua_rawset(L, -3);
152// return;
153                    }
154                    break;
155                case LUA_TNUMBER:
156                    {
157                        /* Math fonts can have more than 7. */
158                        int index = (int) lua_tointeger(L, -2);
159                        if (index >= 8) {
160                            tex_set_font_parameter(f, index, value);
161                        }
162                    }
163                    break;
164            }
165            lua_pop(L, 1);
166        }
167    }
168    lua_pop(L, 1);
169
170}
171
172static void fontlib_aux_read_lua_math_parameters(lua_State *L, int f)
173{
174    lua_push_key(MathConstants);
175    if (lua_rawget(L, -2) == LUA_TTABLE) {
176        lua_pushnil(L);
177        while (lua_next(L, -2)) {
178            int n = (int) lmt_roundnumber(L, -1);
179            int i = 0;
180            switch (lua_type(L, -2)) {
181                case LUA_TSTRING:
182                    i = valid_math_parameter(L, -2);
183                    break;
184                case LUA_TNUMBER:
185                    i = (int) lua_tointeger(L, -2);
186                    break;
187            }
188            if (i > 0) {
189             // set_font_math_parameter(f, i, n);
190                tex_set_font_math_parameters(f, i);
191             // if (n > undefined_math_parameter || i < - undefined_math_parameter) {
192             //     n = undefined_math_parameter;
193             // }
194                font_math_parameter(f, i) = n;
195            }
196            lua_pop(L, 1);
197        }
198    }
199    lua_pop(L, 1);
200}
201
202/*tex
203
204    Math kerns are tables that specify a staircase. There are upto four such lists, one for each
205    corner. Here is a complete example:
206
207    \starttyping
208    mathkerns = {
209        bottom_left  = { { height = 420, kern = 80  }, { height = 520, kern = 4   } },
210        bottom_right = { { height = 0,   kern = 48  } },
211        top_left     = { { height = 620, kern = 0   }, { height = 720, kern = -80 } },
212        top_right    = { { height = 676, kern = 115 }, { height = 776, kern = 45  } },
213    }
214    \stoptyping
215
216*/
217
218static void fontlib_aux_store_math_kerns(lua_State *L, int index, charinfo *co, int id)
219{
220    lua_push_key_by_index(index);
221    if (lua_rawget(L, -2) == LUA_TTABLE) {
222        lua_Integer k = lua_rawlen(L, -1);
223        if (k > 0) {
224            for (lua_Integer l = 1; l <= k; l++) {
225                if (lua_rawgeti(L, -1, l) == LUA_TTABLE) {
226                    scaled ht, krn;
227                    set_numeric_field_by_index(ht, height, 0);
228                    set_numeric_field_by_index(krn, kern, 0);
229                    if (krn || ht) {
230                        tex_add_charinfo_math_kern(co, id, ht, krn);
231                    }
232                }
233                lua_pop(L, 1);
234            }
235        }
236    }
237    lua_pop(L, 1);
238}
239
240static int fontlib_gettextcharacterfields(lua_State *L)
241{
242    lua_createtable(L, 12, 0);
243    lua_push_key_at_index(L, tag,              1);
244    lua_push_key_at_index(L, callback,         2);
245    lua_push_key_at_index(L, width,            3);
246    lua_push_key_at_index(L, height,           4);
247    lua_push_key_at_index(L, depth,            5);
248    lua_push_key_at_index(L, italic,           6);
249    lua_push_key_at_index(L, expansion,        7);
250    lua_push_key_at_index(L, compression,      8);
251    lua_push_key_at_index(L, leftprotrusion,   9);
252    lua_push_key_at_index(L, rightprotrusion, 10);
253    lua_push_key_at_index(L, kerns,           11); /* table */   
254    lua_push_key_at_index(L, ligatures,       12); /* table */   
255    return 1;
256}
257
258static int fontlib_getmathcharacterfields(lua_State *L)
259{
260    lua_createtable(L, 22, 0);
261    lua_push_key_at_index(L, smaller,          1);
262    lua_push_key_at_index(L, mirror,           2);
263    lua_push_key_at_index(L, flataccent,       3);
264    lua_push_key_at_index(L, topleft,          4);
265    lua_push_key_at_index(L, topright,         5);
266    lua_push_key_at_index(L, bottomleft,       6);
267    lua_push_key_at_index(L, bottomright,      7);
268    lua_push_key_at_index(L, leftmargin,       8);
269    lua_push_key_at_index(L, rightmargin,      9);
270    lua_push_key_at_index(L, topmargin,       10);
271    lua_push_key_at_index(L, bottommargin,    11);
272    lua_push_key_at_index(L, topovershoot,    12);
273    lua_push_key_at_index(L, bottomovershoot, 13);
274    lua_push_key_at_index(L, topanchor,       14);
275    lua_push_key_at_index(L, bottomanchor,    15);
276    lua_push_key_at_index(L, innerlocation,   16); /* string: left right top bottom */
277    lua_push_key_at_index(L, innerxoffset,    17); 
278    lua_push_key_at_index(L, inneryoffset,    18); 
279    lua_push_key_at_index(L, extensible,      19); /* boolean */
280    lua_push_key_at_index(L, keepbase,        20); /* boolean */
281    lua_push_key_at_index(L, parts,           21); /* table : { glyph extender advance start end } partsitalic partsorientation (horizontal vertical) */ 
282    lua_push_key_at_index(L, mathkerns,       22); /* table : topleft topright bottomleft bottomright */ 
283    return 1;
284}
285
286static void fontlib_aux_font_char_from_lua(lua_State *L, halfword f, int i, int has_math)
287{
288    if (lua_istable(L, -1)) {
289        /*tex We need an intermediate veriable: */
290        int target; 
291        const char *starget;
292        charinfo *co = tex_get_charinfo(f, i);
293        set_numeric_field_by_index(target, tag, 0);
294        set_charinfo_tag(co, target ? tex_char_checked_tag(target) : 0);
295        set_any_field_by_index(target, callback);
296        set_charinfo_tag(co, target ? callback_tag : 0);
297        set_numeric_field_by_index(target, width, 0);
298        set_charinfo_width(co, target);
299        set_numeric_field_by_index(target, height, 0);
300        set_charinfo_height(co, target);
301        set_numeric_field_by_index(target, depth, 0);
302        set_charinfo_depth(co, target);
303        set_numeric_field_by_index(target, italic, 0);
304        set_charinfo_italic(co, target);
305        set_numeric_field_by_index(target, expansion, scaling_factor);
306        if (target) { 
307            set_font_text_control(f, text_control_expansion);
308            set_charinfo_expansion(co, target);
309        }
310        set_numeric_field_by_index(target, compression, target);
311        if (target) { 
312            set_font_text_control(f, text_control_expansion);
313            set_charinfo_compression(co, target);
314        }
315        set_numeric_field_by_index(target, leftprotrusion, 0);
316        if (target) { 
317            set_font_text_control(f, text_control_left_protrusion);
318            set_charinfo_leftprotrusion(co, target);
319        }
320        set_numeric_field_by_index(target, rightprotrusion, 0);
321        if (target) { 
322            set_font_text_control(f, text_control_right_protrusion);
323            set_charinfo_rightprotrusion(co, target);
324        }
325        if (has_math) {
326            tex_char_malloc_mathinfo(co);
327            set_numeric_field_by_index(target, smaller, 0);
328            set_charinfo_smaller(co, target);
329            set_numeric_field_by_index(target, mirror, 0);
330            set_charinfo_mirror(co, target);
331            set_numeric_field_by_index(target, flataccent, INT_MIN);
332            set_charinfo_flat_accent(co, target);
333            /* */
334            set_numeric_field_by_index(target, topleft, 0);
335            set_charinfo_top_left_kern(co, target);
336            set_numeric_field_by_index(target, topright, 0);
337            set_charinfo_top_right_kern(co, target);
338            set_numeric_field_by_index(target, bottomright, 0);
339            set_charinfo_bottom_right_kern(co, target);
340            set_numeric_field_by_index(target, bottomleft, 0);
341            set_charinfo_bottom_left_kern(co, target);
342            /* */
343            set_numeric_field_by_index(target, leftmargin, 0);
344            set_charinfo_left_margin(co, target);
345            set_numeric_field_by_index(target, rightmargin, 0);
346            set_charinfo_right_margin(co, target);
347            set_numeric_field_by_index(target, topmargin, 0);
348            set_charinfo_top_margin(co, target);
349            set_numeric_field_by_index(target, bottommargin, 0);
350            set_charinfo_bottom_margin(co, target);
351            /* */
352            set_numeric_field_by_index(target, topovershoot, 0);
353            set_charinfo_top_overshoot(co, target);
354            set_numeric_field_by_index(target, bottomovershoot, 0);
355            set_charinfo_bottom_overshoot(co, target);
356            /* */
357            set_numeric_field_by_index(target, topanchor, INT_MIN);
358            set_charinfo_top_anchor(co, target);
359            set_numeric_field_by_index(target, bottomanchor, INT_MIN);
360            set_charinfo_bottom_anchor(co, target);
361            /* */
362            set_string_field_by_index(starget, innerlocation);
363            if (lua_key_eq(starget, left)) {
364                set_charinfo_tag(co, inner_left_tag);
365            } else if (lua_key_eq(starget, right)) {
366                set_charinfo_tag(co, inner_right_tag);
367            } else if (lua_key_eq(starget, top)) {
368                set_charinfo_tag(co, inner_top_tag);
369            } else if (lua_key_eq(starget, bottom)) {
370                set_charinfo_tag(co, inner_bottom_tag);
371            } 
372            set_numeric_field_by_index(target, innerxoffset, INT_MIN);
373            set_charinfo_inner_x_offset(co, target);
374            set_numeric_field_by_index(target, inneryoffset, INT_MIN);
375            set_charinfo_inner_y_offset(co, target);
376            /* */
377            set_numeric_field_by_index(target, next, -1);
378            if (target >= 0) {
379                set_charinfo_tag(co, list_tag);
380                set_charinfo_next(co, target);
381            }
382            set_boolean_field_by_index(target, extensible, 0);
383            if (target) {
384                set_charinfo_tag(co, extend_last_tag);
385            } 
386            set_boolean_field_by_index(target, keepbase, 0);
387            if (target) {
388                set_charinfo_tag(co, keep_base_tag);
389            } 
390            lua_push_key(parts);
391            if (lua_rawget(L, -2) == LUA_TTABLE) {
392                set_charinfo_tag(co, extensible_tag);
393                tex_set_charinfo_extensible_recipe(co, NULL);
394                for (lua_Integer k = 1; ; k++) {
395                    if (lua_rawgeti(L, -1, k) == LUA_TTABLE) {
396                        int glyph, startconnect, endconnect, advance, extender;
397                        set_numeric_field_by_index(glyph, glyph, 0);
398                        set_numeric_field_by_index(extender, extender, 0);
399                        set_numeric_field_by_index(startconnect, start, 0);
400                        set_numeric_field_by_index(endconnect, end, 0);
401                        set_numeric_field_by_index(advance, advance, 0);
402                        tex_append_charinfo_extensible_recipe(co, glyph, startconnect, endconnect, advance, extender);
403                        lua_pop(L, 1);
404                    } else {
405                        lua_pop(L, 1);
406                        break;
407                    }
408                }
409                lua_pop(L, 1);
410                set_numeric_field_by_index(target, partsitalic, 0);
411                set_charinfo_extensible_italic(co, target);
412                set_string_field_by_index(starget, partsorientation);
413                if (lua_key_eq(starget, horizontal)) {
414                    set_charinfo_tag(co, horizontal_tag);
415                } else if (lua_key_eq(starget, vertical)) {
416                    set_charinfo_tag(co, vertical_tag);
417                } 
418            } else {
419                lua_pop(L, 1);
420            }
421            lua_push_key(mathkerns);
422            if (lua_rawget(L, -2) == LUA_TTABLE) {
423                fontlib_aux_store_math_kerns(L, lua_key_index(topleft), co, top_left_kern);
424                fontlib_aux_store_math_kerns(L, lua_key_index(topright), co, top_right_kern);
425                fontlib_aux_store_math_kerns(L, lua_key_index(bottomright), co, bottom_right_kern);
426                fontlib_aux_store_math_kerns(L, lua_key_index(bottomleft), co, bottom_left_kern);
427            }
428            lua_pop(L, 1);
429        }
430        /*tex Maybe some kerns: */
431        lua_push_key(kerns);
432        if (lua_rawget(L, -2) == LUA_TTABLE) {
433            int count = fontlib_aux_count_hash_items(L);
434            if (count > 0) {
435                /*tex The kerns table is still on stack. */
436                kerninfo *ckerns = lmt_memory_calloc((size_t) count + 1, sizeof(kerninfo));
437                if (ckerns) {
438                    int ctr = 0;
439                    set_charinfo_tag(co, kerns_tag);
440                    /*tex Traverse the hash. */
441                    lua_pushnil(L);
442                    while (lua_next(L, -2)) {
443                        int k = non_boundary_char;
444                        switch (lua_type(L, -2)) {
445                            case LUA_TNUMBER:
446                                /*tex Adjacent char: */
447                                k = (int) lua_tointeger(L, -2);
448                                if (k < 0) {
449                                    k = non_boundary_char;
450                                }
451                                break;
452                            case LUA_TSTRING:
453                                {
454                                    const char *s = lua_tostring(L, -2);
455                                    if (lua_key_eq(s, rightboundary)) {
456                                        k = right_boundary_char;
457                                        if (! font_has_right_boundary(f)) {
458                                            set_font_right_boundary(f, tex_get_charinfo(f, right_boundary_char));
459                                        }
460                                    }
461                                }
462                                break;
463                        }
464                        target = lmt_roundnumber(L, -1);
465                        if (k != non_boundary_char) {
466                            set_kern_item(ckerns[ctr], k, target);
467                            ctr++;
468                        } else {
469                            tex_formatted_warning("font", "lua-loaded font %s char U+%X has an invalid kern field", font_name(f), (int) i);
470                        }
471                        lua_pop(L, 1);
472                    }
473                    /*tex A guard against empty tables. */
474                    if (ctr > 0) {
475                        set_kern_item(ckerns[ctr], end_kern, 0);
476                        set_charinfo_kerns(co, ckerns);
477                    } else {
478                        tex_formatted_warning("font", "lua-loaded font %s char U+%X has an invalid kerns field", font_name(f), (int) i);
479                    }
480                } else {
481                    tex_overflow_error("font", (count + 1) * sizeof(kerninfo));
482                }
483            }
484        }
485        lua_pop(L, 1);
486        /*tex Sometimes ligatures: */
487        lua_push_key(ligatures);
488        if (lua_rawget(L, -2) == LUA_TTABLE) {
489            int count = fontlib_aux_count_hash_items(L);
490            if (count > 0) {
491                /*tex The ligatures table still on stack. */
492                ligatureinfo *cligs = lmt_memory_calloc((size_t) count + 1, sizeof(ligatureinfo));
493                if (cligs) {
494                    int ctr = 0;
495                    set_charinfo_tag(co, ligatures_tag);
496                    /*tex Traverse the hash. */
497                    lua_pushnil(L);
498                    while (lua_next(L, -2)) {
499                        int k = non_boundary_char;
500                        int r = -1;
501                        switch (lua_type(L, -2)) {
502                            case LUA_TNUMBER:
503                                /*tex Adjacent char: */
504                                k = (int) lua_tointeger(L, -2);
505                                if (k < 0) {
506                                    k = non_boundary_char;
507                                }
508                                break;
509                            case LUA_TSTRING:
510                                {
511                                    const char *s = lua_tostring(L, -2);
512                                    if (lua_key_eq(s, rightboundary)) {
513                                        k = right_boundary_char;
514                                        if (! font_has_right_boundary(f)) {
515                                            set_font_right_boundary(f, tex_get_charinfo(f, right_boundary_char));
516                                        }
517                                    }
518                                }
519                                break;
520                        }
521                        if (lua_istable(L, -1)) {
522                            /*tex Ligature: */
523                            set_numeric_field_by_index(r, char, -1);
524                        }
525                        if (r != -1 && k != non_boundary_char) {
526                            int ligtarget = 0;
527                            lua_push_key(type);
528                            switch (lua_rawget(L, -2)) {
529                                case LUA_TNUMBER:
530                                    ligtarget = lmt_tointeger(L, -1);
531                                    break;
532                                case LUA_TSTRING:
533                                    {
534                                        const char *value = lua_tostring(L, -1);
535                                        int index = 0;
536                                        while (lmt_ligature_type_strings[index]) {
537                                            if (strcmp(lmt_ligature_type_strings[index], value) == 0) {
538                                                ligtarget = index;
539                                                break;
540                                            } else {
541                                                index++;
542                                            }
543                                        }
544                                    }
545                                    break;
546                                default:
547                                    break;
548                            }
549                            lua_pop(L, 1);
550                            set_ligature_item(cligs[ctr], (ligtarget * 2) + 1, k, r);
551                            ctr++;
552                        } else {
553                            tex_formatted_warning("font", "lua-loaded font %s char U+%X has an invalid ligature field", font_name(f), (int) i);
554                        }
555                        /*tex The iterator value: */
556                        lua_pop(L, 1);
557                    }
558                    /*tex A guard against empty tables. */
559                    if (ctr > 0) {
560                        set_ligature_item(cligs[ctr], 0, end_of_ligature_code, 0);
561                        set_charinfo_ligatures(co, cligs);
562                    } else {
563                        tex_formatted_warning("font", "lua-loaded font %s char U+%X has an invalid ligatures field", font_name(f), (int) i);
564                    }
565                } else {
566                    tex_overflow_error("font", (count + 1) * sizeof(ligatureinfo));
567                }
568            }
569        }
570        lua_pop(L, 1);
571    }
572}
573
574/*tex
575
576    The caller has to fix the state of the lua stack when there is an error!
577
578*/
579
580static int fontlib_getfontfields(lua_State *L)
581{
582    lua_createtable(L, 21, 0);
583    lua_push_key_at_index(L, name,                 1);
584    lua_push_key_at_index(L, designsize,           2);
585    lua_push_key_at_index(L, size,                 3);
586    lua_push_key_at_index(L, compactmath,          4);
587    lua_push_key_at_index(L, mathcontrol,          5);
588    lua_push_key_at_index(L, textcontrol,          6);
589    lua_push_key_at_index(L, textscale,            7);
590    lua_push_key_at_index(L, textxscale,           8);
591    lua_push_key_at_index(L, textyscale,           9);
592    lua_push_key_at_index(L, scriptscale,         10);
593    lua_push_key_at_index(L, scriptxscale,        11);
594    lua_push_key_at_index(L, scriptyscale,        12);
595    lua_push_key_at_index(L, scriptscriptscale,   13);
596    lua_push_key_at_index(L, scriptscriptxscale,  14);
597    lua_push_key_at_index(L, scriptscriptyscale,  15);
598    lua_push_key_at_index(L, hyphenchar,          16);
599    lua_push_key_at_index(L, skewchar,            17);
600    lua_push_key_at_index(L, nomath,              18);
601    lua_push_key_at_index(L, characters,          19);
602    lua_push_key_at_index(L, parameters,          20);
603    lua_push_key_at_index(L, MathConstants,       21);
604    return 1;
605}
606
607static int lmt_font_from_lua(lua_State *L, int f)
608{
609    /*tex The table is at stack |index -1| */
610    const char *nstr ;
611    set_string_field_by_index(nstr, name);
612    tex_set_font_name(f, nstr);
613    if (nstr) {
614        const char *ostr = NULL;
615        int no_math = 0;
616        int j;
617        set_string_field_by_index(ostr, original);
618        tex_set_font_original(f, ostr ? ostr : nstr);
619        set_numeric_field_by_index(j, designsize, 655360);
620        set_font_design_size(f, j);
621        set_numeric_field_by_index(j, size, font_design_size(f));
622        set_font_size(f, j);
623        set_boolean_field_by_index(j, compactmath, 0);
624        set_font_compactmath(f, j);
625        set_numeric_field_by_index(j, mathcontrol, 0);
626        set_font_mathcontrol(f, j);
627        set_numeric_field_by_index(j, textcontrol, 0);
628        set_font_textcontrol(f, j);
629        /* */
630        set_numeric_field_by_index(j, textscale, 0);
631        set_font_textsize(f, j);
632        set_numeric_field_by_index(j, scriptscale, 0);
633        set_font_scriptsize(f, j);
634        set_numeric_field_by_index(j, scriptscriptscale, 0);
635        set_font_scriptscriptsize(f, j);
636        /* */
637        set_numeric_field_by_index(j, textxscale, 0);
638        set_font_x_textsize(f, j);
639        set_numeric_field_by_index(j, scriptxscale, 0);
640        set_font_x_scriptsize(f, j);
641        set_numeric_field_by_index(j, scriptscriptxscale, 0);
642        set_font_x_scriptscriptsize(f, j);
643        /* */
644        set_numeric_field_by_index(j, textweight, 0);
645        set_font_textweight(f, j);
646        set_numeric_field_by_index(j, scriptweight, 0);
647        set_font_scriptweight(f, j);
648        set_numeric_field_by_index(j, scriptscriptweight, 0);
649        set_font_scriptscriptweight(f, j);
650        /* */
651        set_numeric_field_by_index(j, textyscale, 0);
652        set_font_y_textsize(f, j);
653        set_numeric_field_by_index(j, scriptyscale, 0);
654        set_font_y_scriptsize(f, j);
655        set_numeric_field_by_index(j, scriptscriptyscale, 0);
656        set_font_y_scriptscriptsize(f, j);
657        /* */
658        set_numeric_field_by_index(j, hyphenchar, default_hyphen_char_par);
659        set_font_hyphen_char(f, j);
660        set_numeric_field_by_index(j, skewchar, default_skew_char_par);
661        set_font_skew_char(f, j);
662        set_boolean_field_by_index(no_math, nomath, 0);
663        fontlib_aux_read_lua_parameters(L, f);
664        if (! no_math) {
665            fontlib_aux_read_lua_math_parameters(L, f);
666        }
667        lmt_font_state.fonts[f]->weight = 65.536 * lmt_font_state.fonts[f]->design_size / lmt_font_state.fonts[f]->size;
668        /*tex The characters. */
669        lua_push_key(characters);
670        if (lua_rawget(L, -2) == LUA_TTABLE) {
671            /*tex Find the array size values; |num| holds the number of characters to add. */
672            int num = 0;
673            int last = 0;
674            int first = -1;
675            /*tex The first key: */
676            lua_pushnil(L);
677            while (lua_next(L, -2)) {
678                if (lua_isnumber(L, -2)) {
679                    int i = (int) lua_tointeger(L, -2);
680                    if (i >= 0 && lua_istable(L, -1)) {
681                        num++;
682                        if (i > last) {
683                            last = i;
684                        }
685                        if (first < 0) {
686                            first = i;
687                        }
688                        if (first >= 0 && i < first) {
689                            first = i;
690                        }
691                    }
692                }
693                lua_pop(L, 1);
694            }
695            if (num > 0) {
696                tex_font_malloc_charinfo(f, num);
697                set_font_first_character(f, first);
698                set_font_last_character(f, last);
699                /*tex The first key: */
700                lua_pushnil(L);
701                while (lua_next(L, -2)) {
702                    switch (lua_type(L, -2)) {
703                        case LUA_TNUMBER:
704                            {
705                                int i = lmt_tointeger(L, -2);
706                                if (i >= 0) {
707                                    fontlib_aux_font_char_from_lua(L, f, i, ! no_math);
708                                }
709                            }
710                            break;
711                        case LUA_TSTRING:
712                            {
713                                const char *b = lua_tostring(L, -2);
714                                if (lua_key_eq(b, leftboundary)) {
715                                    fontlib_aux_font_char_from_lua(L, f, left_boundary_char, ! no_math);
716                                } else if (lua_key_eq(b, rightboundary)) {
717                                    fontlib_aux_font_char_from_lua(L, f, right_boundary_char, ! no_math);
718                                }
719                            }
720                            break;
721                    }
722                    lua_pop(L, 1);
723                }
724                lua_pop(L, 1);
725            } else {
726                tex_formatted_warning("font", "lua-loaded font '%d' with name '%s' has no characters", f, font_name(f));
727            }
728        } else {
729            tex_formatted_warning("font", "lua-loaded font '%d' with name '%s' has no character table", f, font_name(f));
730        }
731        return 1;
732    } else {
733        return tex_formatted_error("font", "lua-loaded font '%d' has no name!", f);
734    }
735}
736
737static int lmt_characters_from_lua(lua_State *L, int f)
738{
739    int no_math;
740    /*tex Speedup: */
741    set_boolean_field_by_index(no_math, nomath, 0);
742    /*tex The characters: */
743    lua_push_key(characters);
744    if (lua_rawget(L, -2) == LUA_TTABLE) {
745        /*tex Find the array size values; |num| has the amount. */
746        int num = 0;
747        int todo = 0;
748        int first = font_first_character(f);
749        int last = font_last_character(f);
750        /*tex First key: */
751        lua_pushnil(L);
752        while (lua_next(L, -2)) {
753            if (lua_isnumber(L, -2)) {
754                int i = lmt_tointeger(L, -2);
755                if (i >= 0 && lua_istable(L, -1)) {
756                    todo++;
757                 /* if (! tex_char_exists(f, i)) { */ /* else we add each time even when in range */
758                    if (! proper_char_index(f, i)) {
759                        num++;
760                        if (i > last) {
761                            last = i;
762                        }
763                        if (first < 0) {
764                            first = i;
765                        }
766                        if (first >= 0 && i < first) {
767                            first = i;
768                        }
769                    }
770                }
771            }
772            lua_pop(L, 1);
773        }
774        if (todo > 0) {
775            if (num > 0) { 
776                tex_font_malloc_charinfo(f, num);
777                set_font_first_character(f, first);
778                set_font_last_character(f, last);
779            }
780            /*tex First key: */
781            lua_pushnil(L);
782            while (lua_next(L, -2)) {
783                if (lua_type(L, -2) == LUA_TNUMBER) {
784                    int i = lmt_tointeger(L, -2);
785                    if (i >= 0) {
786                        if (tex_char_exists(f, i)) {
787                            charinfo *co = tex_get_charinfo(f, i);
788                            set_charinfo_ligatures(co, NULL);
789                            set_charinfo_kerns(co, NULL);
790                            set_charinfo_math(co, NULL);
791                            tex_set_charinfo_extensible_recipe(co, NULL);
792                        }
793                        fontlib_aux_font_char_from_lua(L, f, i, ! no_math);
794                    }
795                }
796                lua_pop(L, 1);
797            }
798            lua_pop(L, 1);
799        }
800    }
801    return 1;
802}
803
804static void lmt_quality_from_lua(lua_State *L, int f)
805{
806    lua_push_key(characters);
807    if (lua_rawget(L, -2) == LUA_TTABLE) {
808        int first = font_first_character(f);
809        int last = font_last_character(f);
810        lua_pushnil(L);
811        while (lua_next(L, -2)) {
812            if (lua_type(L, -2) == LUA_TNUMBER) {
813                int i = lmt_tointeger(L, -2);
814                if ((i >= first && i <= last) && tex_char_exists(f, i) && lua_istable(L, -1)) {
815                    int target; 
816                    charinfo *co = tex_get_charinfo(f, i);
817                    if (! has_charinfo_tag(co, expansion_tag)) {
818                        set_numeric_field_by_index(target, expansion, scaling_factor);
819                        set_charinfo_expansion(co, target);
820                        set_numeric_field_by_index(target, compression, target);
821                        set_charinfo_compression(co, target);
822                        set_charinfo_tag(co, expansion_tag);
823                    }
824                    if (! has_charinfo_tag(co, protrusion_tag)) {
825                        set_numeric_field_by_index(target, leftprotrusion, 0);
826                        if (target) { 
827                            set_font_text_control(f, text_control_left_protrusion);
828                            set_charinfo_leftprotrusion(co, target);
829                        }
830                        set_numeric_field_by_index(target, rightprotrusion, 0);
831                        if (target) { 
832                            set_font_text_control(f, text_control_right_protrusion);
833                            set_charinfo_rightprotrusion(co, target);
834                        }
835                        set_charinfo_tag(co, protrusion_tag);
836                    }
837                }
838            }
839            lua_pop(L, 1);
840        }
841        lua_pop(L, 1);
842    }
843}
844
845/*tex
846
847   The font library has helpers for defining the font and setting or getting the current font.
848   Internally fonts are represented by font identifiers: numbers. The zero value represents the
849   predefined |nullfont| instance. The only way to load a font in \LUAMETATEX\ is to use \LUA.
850
851*/
852
853static int fontlib_current(lua_State *L)
854{
855    int i = lmt_optinteger(L, 1, 0);
856    if (i > 0) {
857        if (tex_is_valid_font(i)) {
858            tex_set_cur_font(0, i);
859        } else {
860            luaL_error(L, "expected a valid font id");
861        }
862    }
863    lua_pushinteger(L, cur_font_par);
864    return 1;
865}
866
867static int fontlib_max(lua_State *L)
868{
869    lua_pushinteger(L, tex_get_font_max_id());
870    return 1;
871}
872
873static int fontlib_setfont(lua_State *L)
874{
875    int i = lmt_checkinteger(L, 1);
876    if (i) {
877        luaL_checktype(L, 2, LUA_TTABLE);
878        if (! tex_is_valid_font(i)) {
879            return luaL_error(L, "font with id %d is not a valid font", i);
880        } else {
881            lua_settop(L, 2);
882            lmt_font_from_lua(L, i);
883        }
884    }
885    return 0;
886}
887
888static int fontlib_addcharacters(lua_State *L)
889{
890    int i = lmt_checkinteger(L, 1);
891    if (i) {
892        luaL_checktype(L, 2, LUA_TTABLE);
893        if (tex_is_valid_font(i)) {
894            lua_settop(L, 2);
895            lmt_characters_from_lua(L, i);
896        } else {
897            return luaL_error(L, "invalid font id %d passed", i);
898        }
899    }
900    return 0;
901}
902
903static int fontlib_addquality(lua_State *L)
904{
905    int i = lmt_checkinteger(L, 1);
906    if (i) {
907        if (tex_is_valid_font(i)) {
908            if (! has_font_text_control(i, text_control_quality_set)) {
909                luaL_checktype(L, 2, LUA_TTABLE);
910                lua_settop(L, 2);
911                lmt_quality_from_lua(L, i);
912                set_font_text_control(i, text_control_quality_set);
913            }
914        } else {
915            return luaL_error(L, "invalid font id %d passed", i);
916        }
917    }
918    return 0;
919}
920
921/*tex |font.define(table)| */
922
923static int fontlib_define(lua_State *L)
924{
925    if (lua_type(L, 1) == LUA_TTABLE) {
926        int i = lmt_optinteger(L, 2, 0);
927        if (! i) {
928            i = tex_new_font();
929        } else if (! tex_is_valid_font(i)) {
930            return luaL_error(L, "invalid font id %d passed", i);
931        }
932        lua_settop(L, 1);
933        if (lmt_font_from_lua(L, i)) {
934            lua_pushinteger(L, i);
935            return 1;
936        } else {
937            lua_pop(L, 1);
938            tex_delete_font(i);
939            return luaL_error(L, "font creation failed, error in table");
940        }
941    } else {
942        return 0;
943    }
944}
945
946static int fontlib_id(lua_State *L)
947{
948    if (lua_type(L, 1) == LUA_TSTRING) {
949        size_t l;
950        const char *s = lua_tolstring(L, 1, &l);
951        int cs = tex_string_locate_only(s, l);
952        int f = -1;
953        if (cs == undefined_control_sequence || cs == undefined_cs_cmd || eq_type(cs) != set_font_cmd) {
954            lua_pushliteral(L, "not a valid font csname");
955        } else {
956            f = eq_value(cs);
957        }
958        lua_pushinteger(L, f);
959        return 1;
960    } else {
961        return luaL_error(L, "expected font csname string as argument");
962    }
963}
964
965/*tex
966
967    This returns the expected (!) next |fontid|, a first arg |true| will keep the id. This not
968    really robust as of course fonts can be defined in the meantime! In principle |define| could
969    handle that but then I also need to add similar functionality to \LUATEX.
970
971*/
972
973static int fontlib_nextid(lua_State *L)
974{
975    int keep = lua_toboolean(L, 1);
976    int id = tex_new_font();
977    lua_pushinteger(L, id);
978    if (! keep) {
979        tex_delete_font(id);
980    }
981    return 1;
982}
983
984/*tex
985
986    These are not really that useful but can be used to (for instance) mess with the nullfont
987    parameters that occasionally are used as defaults. We don't increase the font parameter array
988    when the upper bound is larger than the initial size. You can forget about that kind of abuse
989    in \LUAMETATEX.
990
991*/
992
993static int fontlib_aux_valid_fontdimen(lua_State *L, halfword *fnt, halfword *n)
994{
995    *fnt = lmt_tohalfword(L, 1);
996    *n = lmt_tohalfword(L, 2);
997    if (*n > 0 && *n <= font_parameter_count(*fnt)) {
998        return 1;
999    } else {
1000        return luaL_error(L, "font with id %i has only %d fontdimens", fnt, n);
1001    }
1002}
1003
1004static int fontlib_setfontdimen(lua_State *L)
1005{
1006    halfword fnt, n;
1007    if (fontlib_aux_valid_fontdimen(L, &fnt, &n)) {
1008        tex_set_font_parameter(fnt, n, lmt_tohalfword(L, 3));
1009    }
1010    return 0;
1011}
1012
1013static int fontlib_getfontdimen(lua_State *L)
1014{
1015    halfword fnt, n;
1016    if (fontlib_aux_valid_fontdimen(L, &fnt, &n)) {
1017        lua_pushinteger(L, font_parameter(fnt, n));
1018    } else {
1019        lua_pushnil(L);
1020    }
1021    return 1;
1022}
1023
1024static int fontlib_getmathspec(lua_State *L)
1025{
1026    if (lua_type(L, 1) == LUA_TSTRING) {
1027        size_t lname = 0;
1028        const char *name = lua_tolstring(L, 1, &lname);
1029        halfword cs = tex_string_locate_only(name, lname);
1030        if (eq_type(cs) == mathspec_cmd) {
1031            halfword ms = eq_value(cs);
1032            if (ms) {
1033                mathcodeval m = tex_get_math_spec(ms);
1034                lua_pushinteger(L, m.class_value);
1035                lua_pushinteger(L, m.family_value);
1036                lua_pushinteger(L, m.character_value);
1037                return 3;
1038            }
1039        }
1040    }
1041    return 0;
1042}
1043
1044static int fontlib_getfontspec(lua_State *L)
1045{
1046    if (lua_type(L, 1) == LUA_TSTRING) {
1047        size_t lname = 0;
1048        const char *name = lua_tolstring(L, 1, &lname);
1049        halfword cs = tex_string_locate_only(name, lname);
1050        if (eq_type(cs) == fontspec_cmd) {
1051            halfword fs = eq_value(cs);
1052            if (fs) {
1053                lua_pushinteger(L, font_spec_identifier(fs));
1054                lua_pushinteger(L, font_spec_scale(fs));
1055                lua_pushinteger(L, font_spec_x_scale(fs));
1056                lua_pushinteger(L, font_spec_y_scale(fs));
1057                lua_pushinteger(L, font_spec_slant(fs));
1058                lua_pushinteger(L, font_spec_weight(fs));
1059                return 6;
1060            }
1061        }
1062    }
1063    return 0;
1064}
1065
1066static int fontlib_getmathindex(lua_State *L) {
1067    halfword index = -1; 
1068    switch (lua_type(L, 1)) { 
1069        case LUA_TSTRING:
1070            index = valid_math_parameter(L, 1);
1071            break;
1072        case LUA_TNUMBER:
1073            index = lmt_tointeger(L, 1);
1074            break;
1075    }
1076    if (index > 0 && index < math_parameter_last_code) { 
1077        lua_pushinteger(L, index);
1078        lua_pushboolean(L, index >= math_parameter_first_engine_code); /* true == engine */
1079    } else { 
1080        lua_pushinteger(L, 0);
1081        lua_pushboolean(L, 0);
1082    }
1083    return 2;
1084}
1085
1086static int fontlib_xscaled(lua_State *L)
1087{
1088    lua_pushinteger(L, tex_font_x_scaled(lmt_optroundnumber(L, 1, 1)));
1089    return 1;
1090}
1091
1092static int fontlib_yscaled(lua_State *L)
1093{
1094    lua_pushinteger(L, tex_font_y_scaled(lmt_optroundnumber(L, 1, 1)));
1095    return 1;
1096}
1097
1098static const struct luaL_Reg fontlib_function_list[] = {
1099    { "current",                fontlib_current                },
1100    { "max",                    fontlib_max                    },
1101    { "setfont",                fontlib_setfont                },
1102    { "addcharacters",          fontlib_addcharacters          },
1103    { "addquality",             fontlib_addquality             },
1104    { "define",                 fontlib_define                 },
1105    { "nextid",                 fontlib_nextid                 },
1106    { "id",                     fontlib_id                     },
1107    { "getfontdimen",           fontlib_getfontdimen           },
1108    { "setfontdimen",           fontlib_setfontdimen           },
1109    { "getfontspec",            fontlib_getfontspec            },
1110    { "getmathspec",            fontlib_getmathspec            },
1111    { "getmathindex",           fontlib_getmathindex           },
1112    { "xscaled",                fontlib_xscaled                },
1113    { "yscaled",                fontlib_yscaled                },
1114    { "getparameterfields",     fontlib_getparameterfields     },
1115    { "getfontfields",          fontlib_getfontfields          },
1116    { "gettextcharacterfields", fontlib_gettextcharacterfields },
1117    { "getmathcharacterfields", fontlib_getmathcharacterfields },
1118    { NULL,                     NULL                           },
1119};
1120
1121int luaopen_font(lua_State *L)
1122{
1123    lua_newtable(L);
1124    luaL_setfuncs(L, fontlib_function_list, 0);
1125    return 1;
1126}
1127