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