texfont.c /size: 82 Kb    last modification: 2025-02-21 11:03
1/*
2    See license.txt in the root of this project.
3*/
4
5/*tex
6
7    Here is the main font API implementation for the original pascal parts. Stuff to watch out for:
8
9    \startitemize
10
11        \startitem
12            Knuth had a |null_character| that was used when a character could not be found by the
13            |fetch()| routine, to signal an error. This has been deleted, but it may mean that the
14            output of luatex is incompatible with TeX after |fetch()| has detected an error
15            condition.
16        \stopitem
17
18        \startitem
19            Knuth also had a |font_glue()| optimization. This has been removed because it was a bit
20            of dirty programming and it also was problematic |if 0 != null|.
21        \stopitem
22
23    \stopitemize
24
25*/
26
27# include "luametatex.h"
28
29/*tex 
30    Finally the base mode ligaturing and kerning code has also been made more consistent with the 
31    rest: abstraction, more tight equality testing, helpers, merged some experiments, etc. It will 
32    probably evolve a bit more; not that we use basemode frequently in \CONTEXT. Keep in mind that 
33    it is not that hard to mess up the list when using \LUA\ but we do little checking here. 
34
35    From now on base mode ligaturing and kerning will only be applied when |text_font_control| 
36    have the |text_control_base_ligaturing| and |text_control_base_kerning| bits set. 
37*/
38
39static inline halfword tex_aux_discretionary_node(halfword target, int location)
40{
41    switch (location) {
42        case pre_break_code : return disc_pre_break_node(target); 
43        case post_break_code: return disc_post_break_node(target);
44        case no_break_code  : return disc_no_break_node(target);  
45        default             : return null;     
46    }
47}
48
49static inline int tex_aux_same_font_properties(halfword a, halfword b) // also in kern 
50{
51    return node_type(a) == glyph_node && node_type(b) == glyph_node 
52     && glyph_font(a)    == glyph_font(b)
53     && glyph_x_scale(a) == glyph_x_scale(b)
54     && glyph_y_scale(a) == glyph_y_scale(b)
55     && glyph_scale(a)   == glyph_scale(b);
56}
57
58static inline int tex_aux_apply_base_kerning(halfword n)
59{
60    if (glyph_protected(n)) {
61        return 0;
62    } else {
63        halfword f = glyph_font(n);
64        if (f >= 0 && f <= lmt_font_state.font_data.ptr && lmt_font_state.fonts[f]) { 
65            return has_font_text_control(f, text_control_base_kerning);
66        } else {
67            return 0;
68        }
69    }
70}
71
72static inline int tex_aux_apply_base_ligaturing(halfword n)
73{
74    if (glyph_protected(n)) {
75        return 0;
76    } else {
77        halfword f = glyph_font(n);
78        if (f >= 0 && f <= lmt_font_state.font_data.ptr && lmt_font_state.fonts[f]) { 
79            return has_font_text_control(f, text_control_base_ligaturing);
80        } else {
81            return 0;
82        }
83    }
84}
85
86/* */
87
88static inline scaled tex_aux_font_x_scaled(scaled v)
89{
90    return v ? scaledround(0.000001 * (glyph_scale_par ? glyph_scale_par : 1000) * (glyph_x_scale_par ? glyph_x_scale_par : 1000) * v) : 0;
91}
92
93static inline scaled tex_aux_font_y_scaled(scaled v)
94{
95    return v ? scaledround(0.000001 * (glyph_scale_par ? glyph_scale_par : 1000) * (glyph_y_scale_par ? glyph_y_scale_par : 1000) * v) : 0;
96}
97
98static inline scaled tex_aux_glyph_x_scaled(halfword g, scaled v)
99{
100    return v ? scaledround(0.000001 * (glyph_scale(g) ? glyph_scale(g) : 1000) * (glyph_x_scale(g) ? glyph_x_scale(g) : 1000) * v) : 0;
101}
102
103static inline scaled tex_aux_glyph_y_scaled(halfword g, scaled v)
104{
105    return v ? scaledround(0.000001 * (glyph_scale(g) ? glyph_scale(g) : 1000) * (glyph_y_scale(g) ? glyph_y_scale(g) : 1000) * v) : 0;
106}
107
108font_state_info lmt_font_state = {
109    .fonts          = NULL,
110    .adjust_stretch = 0,
111    .adjust_shrink  = 0,
112    .adjust_step    = 0,
113    .padding        = 0,
114    .font_data      = {
115        .minimum   = min_font_size,
116        .maximum   = max_font_size,
117        .size      = memory_data_unset,
118        .step      = stp_font_size,
119        .allocated = 0,
120        .itemsize  = sizeof(texfont *),
121        .top       = 0,
122        .ptr       = 0,
123        .initial   = memory_data_unset,
124        .offset    = 0,
125        .extra     = 0, 
126    },
127};
128
129/*tex
130    There can be holes in the font id range. And \unknown\ nullfont is special! Contrary
131    to other places, here we don't reallocate an array of records but one of pointers.
132*/
133
134void tex_initialize_fonts(void)
135{
136    texfont **tmp = aux_allocate_clear_array(sizeof(texfont *), lmt_font_state.font_data.minimum, 0);
137    if (tmp) {
138        for (int i = 0; i < lmt_font_state.font_data.minimum; i++) {
139            tmp[i] = NULL;
140        }
141        lmt_font_state.fonts = tmp;
142        lmt_font_state.font_data.allocated = lmt_font_state.font_data.minimum;
143        lmt_font_state.font_data.top = lmt_font_state.font_data.minimum;
144        lmt_font_state.font_data.ptr = -1; /* we need to end up with id zero first */
145        tex_create_null_font();
146    } else {
147        tex_overflow_error("fonts", lmt_font_state.font_data.minimum);
148    }
149}
150
151/*tex If a slot is not used .. so be it. We want sequential numbers. */
152
153int tex_new_font_id(void)
154{
155    if (lmt_font_state.font_data.ptr < lmt_font_state.font_data.top) {
156        ++lmt_font_state.font_data.ptr;
157        return lmt_font_state.font_data.ptr;
158    } else if (lmt_font_state.font_data.top < lmt_font_state.font_data.maximum) {
159        texfont **tmp ;
160        int top = lmt_font_state.font_data.top + lmt_font_state.font_data.step;
161        if (top > lmt_font_state.font_data.maximum) {
162            top = lmt_font_state.font_data.maximum;
163        }
164        tmp = aux_reallocate_array(lmt_font_state.fonts, sizeof(texfont *), top, 0);
165        if (tmp) {
166            for (int i = lmt_font_state.font_data.top + 1; i < top; i++) {
167                tmp[i] = NULL;
168            }
169            lmt_font_state.fonts = tmp;
170            lmt_font_state.font_data.allocated = top;
171            lmt_font_state.font_data.top = top;
172            lmt_font_state.font_data.ptr += 1;
173            return lmt_font_state.font_data.ptr;
174        }
175    }
176    tex_overflow_error("fonts", lmt_font_state.font_data.maximum);
177    return 0;
178}
179
180int tex_get_font_max_id(void)
181{
182    return lmt_font_state.font_data.ptr;
183}
184
185void tex_dump_font_data(dumpstream f) {
186    dump_int(f, lmt_font_state.font_data.ptr);
187}
188
189void tex_undump_font_data(dumpstream f) {
190    int x;
191    undump_int(f, x);
192    lmt_font_state.font_data.ptr = 0;
193}
194
195void tex_set_charinfo_extensible_recipe(charinfo *ci, extinfo *ext)
196{
197    if (ci->math) {
198        extinfo *list = ci->math->extensible_recipe;
199        if (list) {
200            while (list) {
201                extinfo *c = list->next;
202                lmt_memory_free(list);
203                list = c;
204            }
205        }
206        ci->math->extensible_recipe = ext;
207    }
208}
209
210void tex_set_font_parameters(halfword f, int index)
211{
212    int i = font_parameter_count(f);
213    if (index > i) {
214        /*tex If really needed this can be a calloc. */
215        int size = (index + 2) * (int) sizeof(int);
216        int *list = lmt_memory_realloc(font_parameter_base(f), (size_t) size);
217        if (list) {
218            lmt_font_state.font_data.extra += (index - i + 1) * (int) sizeof(scaled);
219            font_parameter_base(f) = list;
220            font_parameter_count(f) = index;
221            while (i < index) {
222                font_parameter(f, ++i) = 0;
223            }
224        } else {
225            tex_overflow_error("font", size);
226        }
227    }
228}
229
230/*tex Most stuff is zero: */
231
232int tex_new_font(void)
233{
234    int size = sizeof(charinfo);
235    charinfo *ci = lmt_memory_calloc(1, (size_t) size);
236    if (ci) {
237        texfont *tf = NULL;
238        size = sizeof(texfont);
239        tf = lmt_memory_calloc(1, (size_t) size);
240        if (tf) {
241            sa_tree_item sa_value = { 0 };
242            int id = tex_new_font_id();
243            lmt_font_state.font_data.extra += size;
244            lmt_font_state.fonts[id] = tf;
245            set_font_name(id, NULL);
246            set_font_original(id, NULL);
247            set_font_left_boundary(id, NULL);
248            set_font_right_boundary(id, NULL);
249            set_font_parameter_base(id, NULL);
250            set_font_math_parameter_base(id, NULL);
251            /*tex |ec = 0| */
252            set_font_first_character(id, 1);
253            set_font_hyphen_char(id, '-');
254            set_font_skew_char(id, -1);
255            /*tex allocate eight values including 0 */
256            tex_set_font_parameters(id, 7);
257            for (int i = 0; i <= 7; i++) {
258                tex_set_font_parameter(id, i, 0);
259            }
260            /*tex character info zero is reserved for |notdef|. The stack size 1, default item value 0. */
261            tf->characters = sa_new_tree(fontchar_sparse_identifier, 1, 1, 4, sa_value);
262            tf->chardata = ci;
263            tf->chardata_size = 1;
264            tf->weight = 1.0;
265            return id;
266        }
267    }
268    tex_overflow_error("font", size);
269    return 0;
270}
271
272void tex_font_malloc_charinfo(halfword f, int index)
273{
274    int glyph = lmt_font_state.fonts[f]->chardata_size;
275    int size = (glyph + index) * sizeof(charinfo);
276    charinfo *data = lmt_memory_realloc(lmt_font_state.fonts[f]->chardata , (size_t) size);
277    if (data) {
278        lmt_font_state.font_data.extra += index * sizeof(charinfo);
279        lmt_font_state.fonts[f]->chardata = data;
280        memset(&data[glyph], 0, (size_t) index * sizeof(charinfo));
281        lmt_font_state.fonts[f]->chardata_size += index;
282    } else {
283        tex_overflow_error("font", size);
284    }
285}
286
287void tex_char_malloc_mathinfo(charinfo *ci)
288{
289    int size = sizeof(mathinfo);
290    mathinfo *mi = lmt_memory_calloc(1, (size_t) size);
291    if (mi) {
292        mi->extensible_recipe = NULL;
293        /* */
294        mi->top_left_math_kern_array = NULL;
295        mi->top_right_math_kern_array = NULL;
296        mi->bottom_right_math_kern_array = NULL;
297        mi->bottom_left_math_kern_array = NULL;
298        /* zero annyway: */
299        mi->top_left_kern = 0;
300        mi->top_right_kern = 0;
301        mi->bottom_left_kern = 0;
302        mi->bottom_right_kern = 0;
303        /* */
304        mi->left_margin = 0;
305        mi->right_margin = 0;
306        mi->top_margin = 0;
307        mi->bottom_margin = 0;
308        /* */
309        mi->top_overshoot = INT_MIN;
310        mi->bottom_overshoot = INT_MIN;
311        if (ci->math) {
312            /*tex This seldom or probably never happens. */
313            tex_set_charinfo_extensible_recipe(ci, NULL);
314            set_charinfo_top_left_math_kern_array(ci, NULL);
315            set_charinfo_top_right_math_kern_array(ci, NULL);
316            set_charinfo_bottom_right_math_kern_array(ci, NULL);
317            set_charinfo_bottom_left_math_kern_array(ci, NULL);
318            lmt_memory_free(ci->math);
319        } else {
320            lmt_font_state.font_data.extra += size;
321        }
322        ci->math = mi;
323    } else {
324        tex_overflow_error("font", size);
325    }
326}
327
328static inline int aux_find_charinfo_id(halfword f, int c) 
329{
330    sa_tree_item item; 
331    sa_get_item_4(lmt_font_state.fonts[f]->characters, c, &item);
332    return (int) item.int_value;
333}
334
335charinfo *tex_get_charinfo(halfword f, int c)
336{
337    if (proper_char_index(f, c)) {
338        sa_tree_item item;
339        int glyph; 
340        sa_get_item_4(lmt_font_state.fonts[f]->characters, c, &item);
341        glyph = (int) item.int_value;
342        if (! glyph) {
343            sa_tree_item sa_value = { 0 };
344            int tglyph = ++lmt_font_state.fonts[f]->chardata_count;
345            if (tglyph >= lmt_font_state.fonts[f]->chardata_size) {
346                tex_font_malloc_charinfo(f, 256);
347            }
348            lmt_font_state.fonts[f]->chardata[tglyph].expansion = scaling_factor;
349            sa_value.int_value = tglyph;
350            /*tex 1 means global */
351            sa_set_item_4(lmt_font_state.fonts[f]->characters, c, sa_value, 1);
352            glyph = tglyph;
353        }
354        return &(lmt_font_state.fonts[f]->chardata[glyph]);
355    } else if (c == left_boundary_char) {
356        if (! font_has_left_boundary(f)) {
357            int size = sizeof(charinfo);
358            charinfo *ci = lmt_memory_calloc(1, (size_t) size);
359            if (ci) {
360                lmt_font_state.font_data.extra += size;
361                set_font_left_boundary(f, ci);
362            } else {
363                tex_overflow_error("font", size);
364            }
365        }
366        return font_left_boundary(f);
367    } else if (c == right_boundary_char) {
368        if (! font_has_right_boundary(f)) {
369            int size = sizeof(charinfo);
370            charinfo *ci = lmt_memory_calloc(1, (size_t) size);
371            if (ci) {
372                lmt_font_state.font_data.extra += size;
373                set_font_right_boundary(f, ci);
374            } else {
375                tex_overflow_error("font", size);
376            }
377        }
378        return font_right_boundary(f);
379    } else {
380        return &(lmt_font_state.fonts[f]->chardata[0]);
381    }
382}
383
384static charinfo *tex_aux_char_info(halfword f, int c)
385{
386    if (f > lmt_font_state.font_data.ptr) {
387        return NULL;
388    } else if (proper_char_index(f, c)) {
389        return &(lmt_font_state.fonts[f]->chardata[(int) aux_find_charinfo_id(f, c)]);
390    } else if (c == left_boundary_char) {
391        if (font_left_boundary(f)) {
392            return font_left_boundary(f);
393        }
394    } else if (c == right_boundary_char) {
395        if (font_right_boundary(f)) {
396            return font_right_boundary(f);
397        }
398    }
399    return &(lmt_font_state.fonts[f]->chardata[0]);
400}
401
402// static scaled tex_aux_font_weight_done(halfword f, scaled v)
403// {
404//  // return v ? lround(65.536 * v * (double) lmt_font_state.fonts[f]->design_size / (double) lmt_font_state.fonts[f]->size) : 0;
405//     return v ? lround(v * lmt_font_state.fonts[f]->weight) : 0;
406// }
407
408static scaled tex_aux_glyph_weight_done(halfword g)
409{
410    return glyph_weight(g) && tex_has_glyph_option(g, glyph_option_weight_less) 
411        ? 0 : lround(glyph_weight(g) * lmt_font_state.fonts[glyph_font(g)]->weight);
412}
413
414void tex_char_process(halfword f, int c) 
415{
416    if (tex_char_has_tag_from_font(f, c, callback_tag)) { 
417        int callback_id = lmt_callback_defined(process_character_callback);
418        if (callback_id > 0) {
419            lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dd->", f, c);
420        }
421        tex_char_reset_tag_from_font(f, c, callback_tag);
422    }
423}
424
425int tex_char_exists(halfword f, int c)
426{
427    if (f > lmt_font_state.font_data.ptr) {
428        return 0;
429    } else if (proper_char_index(f, c)) {
430        return (int) aux_find_charinfo_id(f, c);
431    } else if (c == left_boundary_char) {
432        if (font_has_left_boundary(f)) {
433            return 1;
434        }
435    } else if (c == right_boundary_char) {
436        if (font_has_right_boundary(f)) {
437            return 1;
438        }
439    }
440    return 0;
441}
442
443/*
444
445static int check_math_char(halfword f, int c, int size)
446{
447    int callback_id = lmt_callback_defined(get_math_char_callback);
448    if (callback_id > 0) {
449        halfword s = c;
450        lmt_run_callback(lua_state.lua_instance, callback_id, "ddd->d", f, c, size, &s);
451        if (s && proper_char_index(f, s) && aux_find_charinfo_id(f, s)) {
452            return s;
453        }
454    }
455    return c;
456}
457*/
458
459int tex_math_char_exists(halfword f, int c, int size)
460{
461    (void) size;
462    return (f > 0 && f <= lmt_font_state.font_data.ptr && proper_char_index(f, c));
463}
464
465/*tex
466    There is a bit overhead due to first fetching but we don't need to check again, so that saves
467    a little.
468*/
469
470int tex_get_math_char(halfword f, int c, int size, scaled *scale, scaled *xscale, scaled *yscale, scaled *weight, int direction)
471{
472    int id = aux_find_charinfo_id(f, c);
473    texfont *tf = lmt_font_state.fonts[f];
474    if (id) { 
475        /* */
476        if (direction) { 
477            charinfo *ci = &tf->chardata[id];
478            int m = ci->math->mirror;
479            if (m && proper_char_index(f, m)) {
480                int mid = aux_find_charinfo_id(f, m);
481                if (mid) { 
482                    id = mid;
483                    c = m;
484                }
485            }
486        }
487        /* */
488        if (size && tf->compactmath) {
489            for (int i=1;i<=size;i++) {
490                charinfo *ci = &tf->chardata[id];
491                if (ci->math) {
492                    int s = ci->math->smaller;
493                    if (s && proper_char_index(f, s)) {
494                        id = aux_find_charinfo_id(f, s);
495                        if (id) {
496                            /* todo: trace */
497                            c = s;
498                        } else {
499                            break;
500                        }
501                    } else {
502                        break;
503                    }
504                } else {
505                    break;
506                }
507            }
508        }
509    }
510    if (scale) {
511        *scale = tex_get_math_font_scale(f, size);
512        *xscale = tex_get_math_font_x_scale(f, size);
513        *yscale = tex_get_math_font_y_scale(f, size);
514        *weight = tex_get_math_font_weight(f, size);
515        if (! *scale) {
516            *scale = scaling_factor;
517        }
518        if (! *xscale) {
519            *xscale = scaling_factor;
520        }
521        if (! *yscale) {
522            *yscale = scaling_factor;
523        }
524    }
525    return c;
526}
527
528void tex_append_charinfo_extensible_recipe(charinfo *ci, int glyph, int startconnect, int endconnect, int advance, int extender)
529{
530    if (ci->math) {
531        int size = sizeof(extinfo);
532        extinfo *ext = lmt_memory_malloc((size_t) size);
533        if (ext) {
534            extinfo *lst = ci->math->extensible_recipe;
535            ext->next = NULL;
536            ext->glyph = glyph;
537            ext->start_overlap = startconnect;
538            ext->end_overlap = endconnect;
539            ext->advance = advance;
540            ext->extender = extender;
541            if (lst) {
542                while (lst->next) {
543                    lst = lst->next;
544                }
545                lst->next = ext;
546            } else {
547                ci->math->extensible_recipe = ext;
548            }
549        } else {
550            tex_overflow_error("font", size);
551        }
552    }
553}
554
555int tex_get_charinfo_math_kerns(charinfo *ci, int id)
556{
557    /*tex All callers check for |result > 0|. */
558    if (ci->math) {
559        switch (id) {
560            case top_left_kern:
561                return ci->math->top_left_math_kerns;
562            case bottom_left_kern:
563                return ci->math->bottom_left_math_kerns;
564            case top_right_kern:
565                return ci->math->top_right_math_kerns;
566            case bottom_right_kern:
567                return ci->math->bottom_right_math_kerns;
568            default:
569                tex_confusion("weird math kern");
570                break;
571        }
572    }
573    return 0;
574}
575
576void tex_add_charinfo_math_kern(charinfo *ci, int id, scaled ht, scaled krn)
577{
578    if (ci->math) {
579        int k = 0;
580        int s = 0;
581        scaled *a = NULL;
582        switch (id) {
583            case top_right_kern:
584                {
585                    k = ci->math->top_right_math_kerns;
586                    s = 2 * (k + 1) * (int) sizeof(scaled);
587                    a = lmt_memory_realloc(ci->math->top_right_math_kern_array, (size_t) s);
588                    if (a) {
589                        ci->math->top_right_math_kern_array = a;
590                        ci->math->top_right_math_kerns++;
591                    }
592                    break;
593                }
594            case bottom_right_kern:
595                {
596                    k = ci->math->bottom_right_math_kerns;
597                    s = 2 * (k + 1) * (int) sizeof(scaled);
598                    a = lmt_memory_realloc(ci->math->bottom_right_math_kern_array, (size_t) s);
599                    if (a) {
600                        ci->math->bottom_right_math_kern_array = a;
601                        ci->math->bottom_right_math_kerns++;
602                    }
603                    break;
604                }
605            case bottom_left_kern:
606                {
607                    k = ci->math->bottom_left_math_kerns;
608                    s = 2 * (k + 1) * (int) sizeof(scaled);
609                    a = lmt_memory_realloc(ci->math->bottom_left_math_kern_array, (size_t) s);
610                    if (a) {
611                        ci->math->bottom_left_math_kern_array = a;
612                        ci->math->bottom_left_math_kerns++;
613                    }
614                    break;
615                }
616            case top_left_kern:
617                {
618                    k = ci->math->top_left_math_kerns;
619                    s = 2 * (k + 1) * (int) sizeof(scaled);
620                    a = lmt_memory_realloc(ci->math->top_left_math_kern_array, (size_t) s);
621                    if (a) {
622                        ci->math->top_left_math_kern_array = a;
623                        ci->math->top_left_math_kerns++;
624                    }
625                    break;
626                }
627            default:
628                tex_confusion("add math kern");
629                return;
630        }
631        if (a) {
632            a[2 * k] = ht;
633            a[(2 * k) + 1] = krn;
634        } else {
635            tex_overflow_error("font", s);
636        }
637    }
638}
639
640/*tex
641
642    In \TEX, extensibles were fairly simple things. This function squeezes a \TFM\ extensible into
643    the vertical extender structures. |advance == 0| is a special case for \TFM\ fonts, because
644    finding the proper advance width during \TFM\ reading can be tricky.
645
646    A small complication arises if |rep| is the only non-zero: it needs to be doubled as a
647    non-repeatable to avoid mayhem.
648
649    \starttyping
650    void tex_set_charinfo_extensible(charinfo *ci, int top, int bottom, int middle, int extender)
651    {
652        if (ci->math) {
653            extinfo *ext;
654            tex_set_charinfo_extensible_recipe(ci, NULL);
655            if (bottom == 0 && top == 0 && middle == 0 && extender != 0) {
656                ext = tex_new_charinfo_extensible_step(extender, 0, 0, 0, math_extension_normal);
657                tex_add_charinfo_extensible_step(ci, ext);
658                ext = tex_new_charinfo_extensible_step(extender, 0, 0, 0, math_extension_repeat);
659                tex_add_charinfo_extensible_step(ci, ext);
660            } else {
661                if (bottom) {
662                    ext = tex_new_charinfo_extensible_step(bottom, 0, 0, 0, math_extension_normal);
663                    tex_add_charinfo_extensible_step(ci, ext);
664                }
665                if (extender) {
666                    ext = tex_new_charinfo_extensible_step(extender, 0, 0, 0, math_extension_repeat);
667                    tex_add_charinfo_extensible_step(ci, ext);
668                }
669                if (middle) {
670                    ext = tex_new_charinfo_extensible_step(middle, 0, 0, 0, math_extension_normal);
671                    tex_add_charinfo_extensible_step(ci, ext);
672                    if (extender) {
673                        ext = tex_new_charinfo_extensible_step(extender, 0, 0, 0, math_extension_repeat);
674                        tex_add_charinfo_extensible_step(ci, ext);
675                    }
676                }
677                if (top) {
678                    ext = tex_new_charinfo_extensible_step(top, 0, 0, 0, math_extension_normal);
679                    tex_add_charinfo_extensible_step(ci, ext);
680                }
681            }
682        }
683    }
684    \stoptyping
685*/
686
687/*tex why not just preallocate for all math otf parameters */
688
689void tex_set_font_math_parameters(halfword f, int b)
690{
691    int i = font_math_parameter_count(f);
692    if (i < b) {
693        size_t size = ((size_t) b + 2) * sizeof(scaled);
694        scaled *data = lmt_memory_realloc(font_math_parameter_base(f), size);
695        if (data) {
696            lmt_font_state.font_data.extra += (int) (((size_t) b - i + 1) * sizeof(scaled));
697            font_math_parameter_base(f) = data;
698            font_math_parameter_count(f) = b;
699            while (i < b) {
700                ++i; /* in macro, make the next a function */
701             // set_font_math_parameter(f, i, undefined_math_parameter);
702                font_math_parameter(f, i) = undefined_math_parameter;
703            }
704        } else {
705            tex_overflow_error("font", (int) size);
706        }
707    }
708}
709
710void tex_delete_font(int f)
711{
712    if (lmt_font_state.fonts[f]) {
713        tex_set_font_name(f, NULL);
714        tex_set_font_original(f, NULL);
715        set_font_left_boundary(f, NULL);
716        set_font_right_boundary(f, NULL);
717        for (int i = font_first_character(f); i <= font_last_character(f); i++) {
718            if (tex_char_exists(f, i)) {
719                charinfo *co = tex_aux_char_info(f, i);
720                set_charinfo_kerns(co, NULL);
721                set_charinfo_ligatures(co, NULL);
722                if (co->math) {
723                    tex_set_charinfo_extensible_recipe(co, NULL);
724                    set_charinfo_top_left_math_kern_array(co, NULL);
725                    set_charinfo_top_right_math_kern_array(co, NULL);
726                    set_charinfo_bottom_right_math_kern_array(co, NULL);
727                    set_charinfo_bottom_left_math_kern_array(co, NULL);
728                    set_charinfo_math(co, NULL);
729                }
730            }
731        }
732        /*tex free |notdef| */
733        lmt_memory_free(lmt_font_state.fonts[f]->chardata);
734        sa_destroy_tree(lmt_font_state.fonts[f]->characters);
735        lmt_memory_free(font_parameter_base(f));
736        if (font_math_parameter_base(f)) {
737            lmt_memory_free(font_math_parameter_base(f));
738        }
739        lmt_memory_free(lmt_font_state.fonts[f]);
740        lmt_font_state.fonts[f] = NULL;
741        if (lmt_font_state.font_data.ptr == f) {
742            lmt_font_state.font_data.ptr--;
743        }
744    }
745}
746
747void tex_create_null_font(void)
748{
749    int id = tex_new_font();
750    tex_set_font_name(id, "nullfont");
751    tex_set_font_original(id, "nullfont");
752}
753
754int tex_is_valid_font(halfword f)
755{
756    return (f >= 0 && f <= lmt_font_state.font_data.ptr && lmt_font_state.fonts[f]);
757}
758
759int tex_checked_font(halfword f)
760{
761    return (f >= 0 && f <= lmt_font_state.font_data.ptr && lmt_font_state.fonts[f]) ? f : null_font;
762}
763
764halfword tex_get_font_identifier(halfword fontspec)
765{
766    if (fontspec) {
767        halfword fnt = font_spec_identifier(fontspec);
768        if ((fnt >= 0 && fnt <= lmt_font_state.font_data.ptr && lmt_font_state.fonts[fnt])) {
769            return fnt;
770        }
771    }
772    return null_font;
773}
774
775/*tex
776    Here come some subroutines to deal with expanded fonts. Returning 1 means that they are
777    identical.
778*/
779
780ligatureinfo tex_get_ligature(halfword f, int lc, int rc)
781{
782    ligatureinfo t = { 0, 0, 0, 0 };
783    if (lc != non_boundary_char && rc != non_boundary_char && tex_has_ligature(f, lc)) {
784        int k = 0;
785        charinfo *co = tex_aux_char_info(f, lc);
786        while (1) {
787            ligatureinfo u = charinfo_ligature(co, k);
788            if (ligature_end(u)) {
789                break;
790            } else if (ligature_char(u) == rc) {
791                return ligature_disabled(u) ? t : u;
792            }
793            k++;
794        }
795    }
796    return t;
797}
798
799int tex_raw_get_kern(halfword f, int lc, int rc)
800{
801    if (lc != non_boundary_char && rc != non_boundary_char) {
802        int k = 0;
803        charinfo *co = tex_aux_char_info(f, lc);
804        while (1) {
805            kerninfo u = charinfo_kern(co, k);
806            if (kern_end(u)) {
807                break;
808            } else if (kern_char(u) == rc) {
809                return kern_disabled(u) ? 0 : kern_kern(u);
810            }
811            k++;
812        }
813    }
814    return 0;
815}
816
817int tex_get_kern(halfword f, int lc, int rc)
818{
819    if (lc == non_boundary_char || rc == non_boundary_char || (! tex_has_kern(f, lc))) {
820        return 0;
821    } else {
822        return tex_raw_get_kern(f, lc, rc);
823    }
824}
825
826scaled tex_valid_kern(halfword left, halfword right)
827{
828    if (node_type(left) == glyph_node && node_type(right) == glyph_node) {
829        halfword fl = glyph_font(left);
830        halfword fr = glyph_font(right);
831        halfword cl = glyph_character(left);
832        halfword cr = glyph_character(right);
833        if (fl == fr && cl != non_boundary_char && cr != non_boundary_char && tex_has_kern(fl, cl) && ! tex_has_glyph_option(left, glyph_option_no_right_kern) && ! tex_has_glyph_option(right, glyph_option_no_left_kern)) {
834            return tex_raw_get_kern(fl, cl, cr);
835        }
836    }
837    return 0;
838}
839
840/*tex
841
842    Experiment:
843
844*/
845
846halfword tex_checked_font_adjust(halfword adjust_spacing, halfword adjust_spacing_step, halfword adjust_spacing_shrink, halfword adjust_spacing_stretch)
847{
848    if (adjust_spacing >= adjust_spacing_full) {
849        if (adjust_spacing_step > 0) {
850            lmt_font_state.adjust_step = adjust_spacing_step;
851            lmt_font_state.adjust_shrink = adjust_spacing_shrink;
852            lmt_font_state.adjust_stretch = adjust_spacing_stretch;
853            if (lmt_font_state.adjust_step > max_font_adjust_step) {
854                lmt_font_state.adjust_step = max_font_adjust_step;
855            }
856            if (lmt_font_state.adjust_shrink < 0) {
857                lmt_font_state.adjust_shrink = 0;
858            } else if (lmt_font_state.adjust_shrink > max_font_adjust_shrink_factor) {
859                lmt_font_state.adjust_shrink = max_font_adjust_shrink_factor;
860            }
861            if (lmt_font_state.adjust_stretch < 0) {
862                lmt_font_state.adjust_stretch = 0;
863            } else if (lmt_font_state.adjust_stretch > max_font_adjust_stretch_factor) {
864                lmt_font_state.adjust_stretch = max_font_adjust_stretch_factor;
865            }
866            return adjust_spacing;
867        }
868    } else {
869        adjust_spacing = adjust_spacing_off;
870    }
871    lmt_font_state.adjust_step = 0;
872    lmt_font_state.adjust_shrink = 0;
873    lmt_font_state.adjust_stretch = 0;
874    return adjust_spacing;
875}
876
877int tex_read_font_info(char *cnom, scaled s)
878{
879    int callback_id = lmt_callback_defined(define_font_callback);
880    if (callback_id > 0) {
881        int f = 0;
882        lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "Sd->d", cnom, s, &f);
883        if (tex_is_valid_font(f)) {
884            tex_set_font_original(f, (char *) cnom);
885            return f;
886        } else {
887            return 0;
888        }
889    } else {
890        tex_normal_warning("fonts","no font has been read, you need to enable or fix the callback");
891        return 0;
892    }
893}
894
895/*tex Abstraction: */
896
897halfword tex_get_font_parameter(halfword f, halfword code) /* todo: math */
898{
899    if (font_parameter_count(f) < code) {
900        tex_set_font_parameters(f, code);
901    }
902    return font_parameter(f, code);
903}
904
905void tex_set_font_parameter(halfword f, halfword code, scaled v)
906{
907    if (font_parameter_count(f) < code) {
908        tex_set_font_parameters(f, code);
909    }
910    font_parameter(f, code) = v;
911}
912
913scaled tex_get_font_slant           (halfword f) { return font_parameter(f, slant_code);         }
914scaled tex_get_font_space           (halfword f) { return font_parameter(f, space_code);         }
915scaled tex_get_font_space_stretch   (halfword f) { return font_parameter(f, space_stretch_code); }
916scaled tex_get_font_space_shrink    (halfword f) { return font_parameter(f, space_shrink_code);  }
917scaled tex_get_font_ex_height       (halfword f) { return font_parameter(f, ex_height_code);     }
918scaled tex_get_font_em_width        (halfword f) { return font_parameter(f, em_width_code);      }
919scaled tex_get_font_extra_space     (halfword f) { return font_parameter(f, extra_space_code);   }
920
921scaled tex_get_scaled_slant         (halfword f) { return                       font_parameter(f, slant_code);          }
922scaled tex_get_scaled_space         (halfword f) { return tex_aux_font_x_scaled(font_parameter(f, space_code));         }
923scaled tex_get_scaled_space_stretch (halfword f) { return tex_aux_font_x_scaled(font_parameter(f, space_stretch_code)); }
924scaled tex_get_scaled_space_shrink  (halfword f) { return tex_aux_font_x_scaled(font_parameter(f, space_shrink_code));  }
925scaled tex_get_scaled_ex_height     (halfword f) { return tex_aux_font_y_scaled(font_parameter(f, ex_height_code));     }
926scaled tex_get_scaled_em_width      (halfword f) { return tex_aux_font_x_scaled(font_parameter(f, em_width_code));      }
927scaled tex_get_scaled_extra_space   (halfword f) { return tex_aux_font_x_scaled(font_parameter(f, extra_space_code));   }
928
929scaled tex_font_x_scaled            (scaled v) { return tex_aux_font_x_scaled(v); }
930scaled tex_font_y_scaled            (scaled v) { return tex_aux_font_y_scaled(v); }
931
932halfword tex_get_scaled_parameter(halfword f, halfword code) /* todo: math */
933{
934    if (font_parameter_count(f) < code) {
935        tex_set_font_parameters(f, code);
936    }
937    switch (code) {
938        case slant_code:
939            return font_parameter(f, code);
940        case ex_height_code:
941            return tex_aux_font_y_scaled(font_parameter(f, code));
942        default:
943            return tex_aux_font_x_scaled(font_parameter(f, code));
944    }
945}
946
947void tex_set_scaled_parameter(halfword f, halfword code, scaled v)
948{
949    if (font_parameter_count(f) < code) {
950        tex_set_font_parameters(f, code);
951    }
952    font_parameter(f, code) = tex_aux_font_x_scaled(v);
953}
954
955halfword tex_get_scaled_glue(halfword f)
956{
957    halfword p = tex_new_glue_node(zero_glue, space_skip_glue);
958    glue_amount(p) = tex_aux_font_x_scaled(font_parameter(f, space_code));
959    glue_stretch(p) = tex_aux_font_x_scaled(font_parameter(f, space_stretch_code));
960    glue_shrink(p) = tex_aux_font_x_scaled(font_parameter(f, space_shrink_code));
961    glue_font(p) = f;
962    return p;
963}
964
965halfword tex_get_scaled_parameter_glue(quarterword p, quarterword s)
966{
967    halfword n = tex_new_glue_node(zero_glue, s);
968    halfword g = glue_parameter(p);
969 // if (g) {
970 //     memcpy((void *) (node_memory_state.nodes + n + 2), (void *) (node_memory_state.nodes + g + 2), (glue_spec_size - 2) * (sizeof(memoryword)));
971 // }
972    glue_amount(n) = tex_aux_font_x_scaled(glue_amount(g));
973    glue_stretch(n) = tex_aux_font_x_scaled(glue_stretch(g));
974    glue_shrink(n) = tex_aux_font_x_scaled(glue_shrink(g));
975    return n;
976}
977
978halfword tex_get_parameter_glue(quarterword p, quarterword s)
979{
980    halfword n = tex_new_glue_node(zero_glue, s);
981    halfword g = glue_parameter(p);
982    if (g) {
983        memcpy((void *) (lmt_node_memory_state.nodes + n + 2), (void *) (lmt_node_memory_state.nodes + g + 2), (glue_spec_size - 2) * (sizeof(memoryword)));
984    }
985    return n;
986}
987
988/*tex Ligaturing starts here */
989
990static void tex_aux_discretionary_append(halfword target, int location, halfword n)
991{
992    halfword node = tex_aux_discretionary_node(target, location);
993    if (node_tail(node)) { 
994        tex_couple_nodes(node_tail(node), n);
995    } else { 
996        node_head(node) = n;
997    }
998    node_tail(node) = n;
999}
1000
1001static void tex_aux_discretionary_prepend(halfword target, int location, halfword n)
1002{
1003    halfword node = tex_aux_discretionary_node(target, location);
1004    if (node_head(node)) { 
1005        tex_couple_nodes(n, node_head(node));
1006    } else {
1007        node_tail(node) = n;
1008    } 
1009    node_head(node) = n;
1010}
1011
1012static void tex_aux_nesting_prepend_list(halfword target, int location, halfword n) /* n is prepended to target */
1013{
1014    halfword node = tex_aux_discretionary_node(target, location);
1015    halfword copy = tex_copy_node_list(n, null);
1016    halfword tail = tex_tail_of_node_list(copy);
1017    if (node_head(node)) {
1018        tex_couple_nodes(tail, node_head(node));
1019    } else { 
1020        node_tail(node) = tail;
1021    }
1022    node_head(node) = copy;
1023}
1024
1025int tex_valid_ligature(halfword left, halfword right, int *slot)
1026{
1027    if (node_type(left) != glyph_node) {
1028        return -1;
1029    } else if (glyph_font(left) != glyph_font(right)) {
1030        return -1;
1031    } else if (tex_has_glyph_option(left, glyph_option_no_right_ligature) || tex_has_glyph_option(right, glyph_option_no_left_ligature)) {
1032        return -1;
1033    } else {
1034        ligatureinfo lig = tex_get_ligature(glyph_font(left), glyph_character(left), glyph_character(right));
1035        if (ligature_is_valid(lig)) {
1036            *slot = ligature_replacement(lig);
1037            return ligature_type(lig);
1038        } else {
1039            return -1;
1040        }
1041    }
1042}
1043
1044static int tex_aux_found_ligature(halfword left, halfword right)
1045{
1046    if (! left || ! right) {
1047        return 0;
1048    } else if (node_type(left) != glyph_node || node_type(right) != glyph_node) {
1049        return 0;
1050    } else if (glyph_font(left) != glyph_font(right)) {
1051        return 0;
1052    } else if (tex_has_glyph_option(left, glyph_option_no_right_ligature) || tex_has_glyph_option(right, glyph_option_no_left_ligature)) {
1053        return 0;
1054    } else {
1055        return ligature_is_valid(tex_get_ligature(glyph_font(left), glyph_character(left), glyph_character(right)));
1056    }
1057}
1058
1059/*tex
1060    In principle we only support simple ligatures, i.e.\ |move_after|, |keep_right| and |keep_left|
1061    are zero. At some point we might even drop special ones, including those with boundaries because 
1062    the likelyhood of encountering these in the \OPENTYPE\ arena is close to zero. 
1063*/
1064
1065static int tex_aux_try_ligature(halfword *first, halfword second, halfword *nextone)
1066{
1067    halfword current = *first;
1068    halfword slot;
1069    halfword type = tex_valid_ligature(current, second, &slot);
1070    if (type >= 0) {
1071        int move_after = (type & 0x0C) >> 2;
1072        int keep_right = (type & 0x01) != 0;
1073        int keep_left  = (type & 0x02) != 0;
1074        halfword next = node_next(second);
1075        if (keep_left && keep_right) {
1076            halfword ligature = tex_copy_node(current);
1077            glyph_character(ligature) = slot; 
1078            tex_couple_nodes(*first, ligature);
1079            tex_couple_nodes(ligature, second);
1080            if (nextone) {
1081                *nextone = second;
1082            }
1083        } else if (keep_right) { 
1084            glyph_character(*first) = slot; 
1085            if (nextone) {
1086                *nextone = second;
1087            }
1088        } else if (keep_left) { 
1089            glyph_character(second) = slot; 
1090            if (nextone) {
1091                *nextone = second;
1092            }
1093        } else { 
1094            glyph_character(*first) = slot; 
1095            tex_uncouple_node(second);
1096            tex_flush_node(second);
1097            tex_try_couple_nodes(*first, next);
1098            if (nextone) {
1099                *nextone = *first;
1100            }
1101        }
1102        /* untested */
1103        if (nextone) {
1104            while (move_after-- > 0 && *nextone) {
1105                *nextone = node_next(*nextone);
1106            }
1107        }
1108        return 1;
1109    } else {
1110        return 0;
1111    }
1112}
1113
1114static void tex_aux_handle_ligature_list(halfword target, int location)
1115{
1116    halfword node = tex_aux_discretionary_node(target, location);
1117    halfword head = node_head(node);
1118    halfword tail = node_tail(node);
1119    if (head && head != tail) {
1120        halfword current = head;
1121        while (node_next(current)) {
1122            halfword next = node_next(current);
1123            int ishead = current == head;
1124            halfword nextone = next;
1125            if (tex_aux_same_font_properties(current, next) && tex_aux_try_ligature(&current, next, &nextone)) {
1126                if (ishead) {
1127                    head = current;
1128                    node_head(node) = current;
1129                }
1130                current = nextone;
1131            } else { 
1132                current = next;
1133            }
1134        }
1135        node_tail(node) = current;
1136    }
1137}
1138
1139static void tex_aux_handle_ligature_pair(halfword target, int location)
1140{
1141    halfword node = tex_aux_discretionary_node(target, location);
1142    halfword head = node_head(node);
1143    halfword tail = node_tail(node);
1144    if (head && head != tail) {
1145        halfword previous = node_prev(tail);
1146        int ishead = previous == head;
1147        if (tex_aux_same_font_properties(previous, tail) && tex_aux_try_ligature(&previous, tail, NULL)) {
1148            if (ishead) {
1149                head = previous;
1150                node_head(node) = previous;
1151            }
1152            node_tail(node) = previous;
1153        }
1154    }
1155}
1156
1157/*tex
1158
1159    In \LUATEX\ we have a chained variant of discretionaries (init and select) but that never really
1160    works out ok. It was there for basemode to be compatible with original \TEX\ but it was also means
1161    for border cases that in practice never occur. A least no \CONTEXT\ user ever complained about
1162    ligatures and hyphenation of these border cases. Keep in mind that in node mode (which we normally
1163    use) the select discs never showed up anyway. Another reason for dropping these discretionaries is
1164    that by not using them we get more predictable (or at least easier) handling of node lists that do
1165    have (any kind of) discretionaries. It is still on my agenda to look into nested discretionaries
1166    i.e. discs nodes in disc fields but it might never result in useable code.
1167
1168    Todo: check this boundary mess (check for subtype too). 
1169    Todo: maybe get rid of weird ligatures, turn boundary into space and such.
1170
1171*/
1172
1173static halfword tex_aux_handle_ligature_word(halfword current)
1174{
1175    halfword right = null;
1176    halfword last = null; /* cf LuaTeX patch, an ancient border case buglet. */
1177    if (node_type(current) == boundary_node) {
1178        halfword previous = node_prev(current);
1179        halfword next = node_next(current);
1180        /*tex There is no need to uncouple |cur|, it is freed. */
1181        tex_flush_node(current);
1182        if (next) {
1183            tex_couple_nodes(previous, next);
1184            if (node_type(next) != glyph_node) {
1185                return previous;
1186            } else {
1187                current = next;
1188            }
1189        } else {
1190            node_next(previous) = next;
1191            return previous;
1192        }
1193    } else if (node_type(current) == glyph_node && font_has_left_boundary(glyph_font(current))) {
1194        halfword previous = node_prev(current);
1195        halfword glyph = tex_new_glyph_node(glyph_unset_subtype, glyph_font(current), left_boundary_char, current);
1196        tex_couple_nodes(previous, glyph);
1197        tex_couple_nodes(glyph, current);
1198        current = glyph;
1199    }
1200    if (node_type(current) == glyph_node && font_has_right_boundary(glyph_font(current))) {
1201        right = tex_new_glyph_node(glyph_unset_subtype, glyph_font(current), right_boundary_char, current);
1202    }
1203 // tex_print_node_list(current, "GOING",max_integer, max_integer);
1204    while (1) {
1205        halfword currenttype = node_type(current);
1206        /*tex A glyph followed by \unknown */
1207        if (currenttype == glyph_node) {
1208            if (tex_aux_apply_base_ligaturing(current)) {
1209                halfword forward = node_next(current);
1210                if (forward) {
1211                    halfword forwardtype = node_type(forward);
1212                    if (forwardtype == glyph_node) {
1213                        if (! tex_aux_apply_base_ligaturing(forward)) {
1214                          //  break;
1215                        } else if (! tex_aux_same_font_properties(current, forward)) {
1216                          //  break;
1217                        } else { 
1218                            halfword nextone = current; 
1219                            if (tex_aux_try_ligature(&current, forward, &nextone)) {
1220                                current = nextone; 
1221                                continue;
1222                            }
1223                        }
1224                    } else if (forwardtype == disc_node) {
1225                        /*tex a glyph followed by a disc */
1226                        halfword pre = disc_pre_break_head(forward);
1227                        halfword replace = disc_no_break_head(forward);
1228                        halfword next;
1229                        /*tex Check on: |a{b?}{?}{?}| and |a+b=>B| : |{B?}{?}{a?}| */
1230                        /*tex Check on: |a{?}{?}{b?}| and |a+b=>B| : |{a?}{?}{B?}| */
1231                        if (tex_aux_found_ligature(current, pre) || tex_aux_found_ligature(current, replace)) {
1232                            /*tex Move |cur| from before disc to skipped part */
1233                            halfword previous = node_prev(current);
1234                            tex_uncouple_node(current);
1235                            tex_couple_nodes(previous, forward);
1236                            tex_aux_discretionary_prepend(forward, no_break_code, current);
1237                            tex_aux_discretionary_prepend(forward, pre_break_code, tex_copy_node(current));
1238                            /*tex As we have removed cur, we need to start again. */
1239                            current = previous;
1240                        }
1241                        /*tex Check on: |a{?}{?}{}b| and |a+b=>B| : |{a?}{?b}{B}|. */
1242                        next = node_next(forward);
1243                        if (! replace && tex_aux_found_ligature(current, next)) {
1244                            /*tex Move |cur| from before |disc| to |no_break| part. */
1245                            halfword previous = node_prev(current);
1246                            halfword tail = node_next(next);
1247                            tex_uncouple_node(current);
1248                            tex_couple_nodes(previous, forward);
1249                            tex_aux_discretionary_prepend(forward, pre_break_code, tex_copy_node(current));
1250                            /*tex Move next from after disc to |no_break| part. */
1251                            tex_uncouple_node(next);
1252                            tex_try_couple_nodes(forward, tail);
1253                            /*tex We {\em know} this works. */
1254                            tex_couple_nodes(current, next);
1255                            /*tex Make sure the list is correct. */
1256                            tex_aux_discretionary_append(forward, post_break_code, tex_copy_node(next));
1257                            /*tex As we have removed cur, we need to start again. */
1258                            current = previous;
1259                        }
1260                        /*tex We are finished with the |pre_break|. */
1261                        tex_aux_handle_ligature_list(forward, pre_break_code);
1262                    } else if (forwardtype == boundary_node) {
1263                        halfword next = node_next(forward);
1264                        tex_try_couple_nodes(current, next);
1265                        tex_flush_node(forward);
1266                        if (right) {
1267                            /*tex Shame, didn't need it. */
1268                            tex_flush_node(right);
1269                            /*tex No need to reset |right|, we're going to leave the loop anyway. */
1270                        }
1271                        break;
1272                    } else if (right) {
1273                        tex_couple_nodes(current, right);
1274                        tex_couple_nodes(right, forward);
1275                        right = null;
1276                        continue;
1277                    } else {
1278                        break;
1279                    }
1280                } else {
1281                    /*tex The last character of a paragraph. */
1282                    if (right) {
1283                        /*tex |par| prohibits the use of |couple_nodes| here. */
1284                        tex_try_couple_nodes(current, right);
1285                        right = null;
1286                        continue;
1287                    } else {
1288                        break;
1289                    }
1290                }
1291                /*tex A discretionary followed by \unknown */
1292            }
1293        } else if (currenttype == disc_node) {
1294            /*tex If |{?}{x}{?}| or |{?}{?}{y}| then: */
1295            if (disc_no_break_head(current) || disc_post_break_head(current)) {
1296                /*tex Is this nesting okay (and needed)? */
1297                halfword forward;
1298                if (disc_post_break_head(current)) {
1299                    tex_aux_handle_ligature_list(current, post_break_code);
1300                }
1301                if (disc_no_break_head(current)) {
1302                    tex_aux_handle_ligature_list(current, no_break_code);
1303                }
1304                forward = node_next(current);
1305                while (forward && node_type(forward) == glyph_node && tex_aux_apply_base_ligaturing(forward)) {
1306                    halfword replace = disc_no_break_tail(current);
1307                    halfword post = disc_post_break_tail(current);
1308                    if (tex_aux_found_ligature(replace, forward) || tex_aux_found_ligature(post, forward)) {
1309                        tex_try_couple_nodes(current, node_next(forward));
1310                        tex_uncouple_node(forward);
1311                        tex_aux_discretionary_append(current, no_break_code, tex_copy_node(forward));
1312                        tex_aux_handle_ligature_pair(current, no_break_code);
1313                        tex_aux_handle_ligature_pair(current, post_break_code);
1314                        forward = node_next(current);
1315                    } else {
1316                        break;
1317                    }
1318                }
1319                if (forward && node_type(forward) == disc_node) {
1320                    /*tex This only deals with simple pre-only discretionaries and a following glyph. */
1321                    halfword next = node_next(forward);
1322                    if (next
1323                        && ! disc_no_break_head(forward)
1324                        && ! disc_post_break_head(forward)
1325                        && node_type(next) == glyph_node
1326                        && tex_aux_apply_base_ligaturing(next)
1327                        && ((disc_post_break_tail(current) && tex_aux_found_ligature(disc_post_break_tail(current), next)) ||
1328                            (disc_no_break_tail  (current) && tex_aux_found_ligature(disc_no_break_tail  (current), next)))) {
1329                        halfword last = node_next(next);
1330                        tex_uncouple_node(next);
1331                        tex_try_couple_nodes(forward, last);
1332                        /*tex Just a hidden flag, used for (base mode) experiments. */
1333                        if (hyphenation_permitted(hyphenation_mode_par, lazy_ligatures_hyphenation_mode)) {
1334                            /*tex f-f-i -> f-fi */
1335                            tex_aux_discretionary_append(current, no_break_code, tex_copy_node(next));
1336                            tex_aux_handle_ligature_pair(current,no_break_code);
1337                            tex_aux_discretionary_append(current, post_break_code, next);
1338                            tex_aux_handle_ligature_pair(current,post_break_code);
1339                            tex_try_couple_nodes(node_prev(forward), node_next(forward));
1340                            tex_flush_node(forward);
1341                        } else {
1342                            /*tex f-f-i -> ff-i : |{a-}{b}{AB} {-}{c}{}| => |{AB-}{c}{ABc}| */
1343                            tex_aux_discretionary_append(forward, post_break_code, tex_copy_node(next));
1344                            if (disc_no_break_head(current)) {
1345                                tex_aux_nesting_prepend_list(forward, no_break_code, disc_no_break_head(current));
1346                                tex_aux_discretionary_append(forward, no_break_code, next);
1347                                tex_aux_handle_ligature_pair(forward, no_break_code);
1348                                tex_aux_nesting_prepend_list(forward, pre_break_code, disc_no_break_head(current));
1349                            }
1350                            tex_try_couple_nodes(node_prev(current), node_next(current));
1351                            tex_flush_node(current);
1352                            current = forward;
1353                        }
1354                    }
1355                }
1356            }
1357        } else {
1358            /*tex We have glyph nor disc. */
1359            return last;
1360        }
1361        /*tex Goto the next node, where |\par| allows |node_next(cur)| to be NULL. */
1362        last = current;
1363        current = node_next(current);
1364    }
1365    return current;
1366}
1367
1368/*tex The return value is the new tail, head should be a dummy: */
1369
1370halfword tex_handle_ligaturing(halfword head, halfword tail)
1371{
1372    if (node_next(head)) {
1373        /*tex A trick to allow explicit |node == null| tests. */
1374        halfword save_tail = null;
1375        halfword current, previous;
1376        if (tail) {
1377            save_tail = node_next(tail);
1378            node_next(tail) = null;
1379        }
1380        previous = head;
1381        current = node_next(previous);
1382        while (current) {
1383            switch(node_type(current)) {
1384                case glyph_node:
1385                    if (tex_aux_apply_base_ligaturing(current)) {
1386                        current = tex_aux_handle_ligature_word(current);
1387                    }
1388                    break;
1389                case disc_node: 
1390                case boundary_node:
1391                    current = tex_aux_handle_ligature_word(current);
1392                    break;
1393            }
1394            previous = current;
1395            if (current) {
1396                current = node_next(current);
1397            }
1398        }
1399        if (! previous) {
1400            previous = tex_tail_of_node_list(head);
1401        }
1402        tex_try_couple_nodes(previous, save_tail);
1403        return previous;
1404    } else {
1405        return tail;
1406    }
1407}
1408
1409/*tex Kerning starts here: */
1410
1411static halfword tex_aux_add_kern_before(halfword left, halfword right)
1412{
1413    if (tex_aux_same_font_properties(left, right) &&
1414            ! tex_has_glyph_option(left, glyph_option_no_right_kern) &&
1415            ! tex_has_glyph_option(right, glyph_option_no_left_kern) &&
1416            tex_has_kern(glyph_font(left), glyph_character(left))
1417        ) {
1418        scaled k = tex_raw_get_kern(glyph_font(left), glyph_character(left), glyph_character(right));
1419        if (k) {
1420            scaled kern = tex_new_kern_node(k, font_kern_subtype);
1421            halfword previous = node_prev(right);
1422            tex_couple_nodes(previous, kern);
1423            tex_couple_nodes(kern, right);
1424            tex_attach_attribute_list_copy(kern, left);
1425            return kern;
1426        }
1427    }
1428    return null;
1429}
1430
1431static halfword tex_aux_add_kern_after(halfword left, halfword right, halfword after)
1432{
1433    if (tex_aux_same_font_properties(left, right) &&
1434            ! tex_has_glyph_option(left, glyph_option_no_right_kern) &&
1435            ! tex_has_glyph_option(right, glyph_option_no_left_kern) &&
1436            tex_has_kern(glyph_font(left), glyph_character(left))
1437        ) {
1438        scaled k = tex_raw_get_kern(glyph_font(left), glyph_character(left), glyph_character(right));
1439        if (k) {
1440            scaled kern = tex_new_kern_node(k, font_kern_subtype);
1441            halfword next = node_next(after);
1442            tex_couple_nodes(after, kern);
1443            tex_try_couple_nodes(kern, next);
1444            tex_attach_attribute_list_copy(kern, after);
1445            return kern;
1446        }
1447    }
1448    return null;
1449}
1450
1451static halfword tex_aux_do_handle_kerning(halfword root, halfword init_left, halfword init_right);
1452
1453static void tex_aux_handle_discretionary_kerning(halfword target, int location, halfword left, halfword right)
1454{
1455    halfword node = tex_aux_discretionary_node(target, location);
1456    if (node_head(node)) {
1457        halfword kern = tex_aux_do_handle_kerning(node_head(node), left, right);
1458        if (kern) { 
1459            node_head(node) = kern;
1460            node_tail(node) = tex_tail_of_node_list(node_head(node));
1461        }
1462    }
1463}
1464
1465static halfword tex_aux_do_handle_kerning(halfword root, halfword init_left, halfword init_right)
1466{
1467 // halfword head = node_next(root); // todo: get rid of this one 
1468    halfword head = root; // todo: get rid of this one 
1469    halfword current = head;
1470    halfword initial = null;
1471    if (current) {
1472        halfword left = null;
1473        if (node_type(current) == glyph_node && tex_aux_apply_base_kerning(current)) {
1474            if (init_left) {
1475                halfword kern = tex_aux_add_kern_before(init_left, current);
1476                if (current == head) {
1477                    initial = kern; 
1478                }
1479            }
1480            left = current;
1481        }
1482        current = node_next(current);
1483        while (current) {
1484            halfword currenttype = node_type(current);
1485            if (currenttype == glyph_node) { 
1486                if (tex_aux_apply_base_kerning(current)) {
1487                    if (left) {
1488                        tex_aux_add_kern_before(left, current);
1489                        if (glyph_character(left) < 0) {
1490                            halfword previous = node_prev(left);
1491                            tex_couple_nodes(previous, current);
1492                            tex_flush_node(left);
1493                        }
1494                    }
1495                    left = current;
1496                } else { 
1497                    left = null;
1498                }
1499            } else {
1500                if (currenttype == disc_node) {
1501                    halfword next = node_next(current);
1502                    halfword right = node_type(next) == glyph_node && tex_aux_apply_base_kerning(next) ? next : null;
1503                    tex_aux_handle_discretionary_kerning(current, pre_break_code, left, null);
1504                    tex_aux_handle_discretionary_kerning(current, post_break_code, null, right);
1505                    tex_aux_handle_discretionary_kerning(current, no_break_code, left, right);
1506                }
1507                if (left) {
1508                    if (glyph_character(left) < 0) { /* boundary ? */
1509                        halfword previous = node_prev(left);
1510                        tex_couple_nodes(previous, current);
1511                        tex_flush_node(left);
1512                    }
1513                    left = null;
1514                }
1515            }
1516            current = node_next(current);
1517        }
1518        if (left) {
1519            if (init_right) {
1520                tex_aux_add_kern_after(left, init_right, left);
1521            }
1522            if (glyph_character(left) < 0) {
1523                halfword previous = node_prev(left);
1524                halfword next = node_next(left);
1525                if (next) {
1526                    tex_couple_nodes(previous, next);
1527                    node_tail(root) = next;
1528                } else if (previous != root) {
1529                    node_next(previous) = null;
1530                    node_tail(root) = previous;
1531                } else {
1532                    node_next(root) = null;
1533                    node_tail(root) = null;
1534                }
1535                tex_flush_node(left);
1536            }
1537        }
1538    } else if (init_left && init_right ) {
1539        tex_aux_add_kern_after(init_left, init_right, root);
1540        node_tail(root) = node_next(root);
1541    }
1542    return initial; 
1543}
1544
1545halfword tex_handle_kerning(halfword head, halfword tail)
1546{
1547    halfword save_link = null;
1548    if (tail) {
1549        save_link = node_next(tail);
1550        node_next(tail) = null;
1551        node_tail(head) = tail;
1552        tex_aux_do_handle_kerning(node_next(head), null, null); /*tex There is no need to check initial here. */
1553        tail = node_tail(head);
1554        if (tex_valid_node(save_link)) {
1555            /* no need for check */
1556            tex_try_couple_nodes(tail, save_link);
1557        }
1558    } else {
1559        node_tail(head) = null;
1560        tex_aux_do_handle_kerning(node_next(head), null, null); /*tex There is no need to check initial here. */
1561    }
1562    return tail;
1563}
1564
1565/*tex The ligaturing and kerning \LUA\ interface: */
1566
1567static halfword tex_aux_run_lua_ligkern_callback(lua_State *L, halfword head, halfword group, halfword direction, int callback_id)
1568{
1569    int top = 0;
1570    if (lmt_callback_okay(L, callback_id, &top)) {
1571        int i;
1572        lmt_node_list_to_lua(L, head);
1573        lmt_push_group_code(L, group);
1574        lua_pushinteger(L, direction);
1575        i = lmt_callback_call(L, 3, 1, top);
1576        if (i) {
1577            lmt_callback_error(L, top, i);
1578        } else {
1579            head = lmt_node_list_from_lua(L, -1);
1580            lmt_callback_wrapup(L, top);
1581        }
1582    }
1583    return head;
1584}
1585
1586halfword tex_handle_glyphrun(halfword head, halfword group, halfword direction)
1587{
1588    if (head) {
1589        int callback_id = lmt_callback_defined(glyph_run_callback);
1590        if (callback_id) {
1591            return tex_aux_run_lua_ligkern_callback(lmt_lua_state.lua_instance, head, group, direction, callback_id);
1592        } else {
1593            callback_id = lmt_callback_defined(ligaturing_callback);
1594            if (callback_id) {
1595                head = tex_aux_run_lua_ligkern_callback(lmt_lua_state.lua_instance, head, group, direction, callback_id);
1596            } else {
1597                // what if disc at start 
1598                tex_handle_ligaturing(head, null);
1599            }
1600            callback_id = lmt_callback_defined(kerning_callback);
1601            if (callback_id) {
1602                head = tex_aux_run_lua_ligkern_callback(lmt_lua_state.lua_instance, head, group, direction, callback_id);
1603            } else {
1604                halfword kern = tex_aux_do_handle_kerning(head, null, null);
1605                if (kern) { 
1606                    head = kern; 
1607                }
1608            }
1609        }
1610    }
1611    return head;
1612}
1613
1614/*tex
1615
1616    When the user defines |\font\f|, say, \TEX\ assigns an internal number to the user's font |\f|.
1617    Adding this number to |font_id_base| gives the |eqtb| location of a \quote {frozen} control
1618    sequence that will always select the
1619    font.
1620
1621    The variable |a| in the following code indicates the global nature of the value to be set. It's
1622    used in the |define| macro. Here we're never global.
1623
1624    There's not much scanner code here because the other scanners are defined where they make most
1625    sense.
1626
1627*/
1628
1629void tex_set_cur_font(halfword g, halfword f)
1630{
1631    update_tex_font(g, f);
1632}
1633
1634/*tex
1635    Because we do fonts in \LUA\ we can decide to drop this one and assume a definition using the
1636    token scanner. It also avoids the filename (split) mess.
1637*/
1638
1639int tex_tex_def_font(int a)
1640{
1641    if (! lmt_fileio_state.job_name) {
1642        /*tex Avoid confusing |texput| with the font name. */
1643        tex_open_log_file();
1644    }
1645    tex_get_r_token();
1646    if (tex_define_permitted(cur_cs, a)) {
1647        /*tex The user's font identifier. */
1648        halfword u = cur_cs;
1649        /*tex This runs through existing fonts. */
1650        halfword f;
1651        /*tex Stated 'at' size, or negative of scaled magnification. */
1652        scaled s = -scaling_factor;
1653        char *fn;
1654        /*tex Here |a| determines if we define global or not. */
1655        if (is_global(a)) {
1656            update_tex_font_global(u, null_font);
1657        } else {
1658            update_tex_font_local(u, null_font);
1659        }
1660        fn = tex_read_file_name(1, NULL, NULL);
1661        /*tex Scan the font size specification. */
1662        lmt_fileio_state.name_in_progress = 1;
1663        if (tex_scan_keyword("at")) {
1664            /*tex Put the positive 'at' size into |s|. */
1665            s = tex_scan_dimension(0, 0, 0, 0, NULL, NULL);
1666            if ((s <= 0) || (s >= 0x8000000)) { 
1667                tex_handle_error(
1668                    normal_error_type,
1669                    "Improper 'at' size (%p), replaced by 10pt",
1670                    s,
1671                    "I can only handle fonts at positive sizes that are less than 2048pt, so I've\n"
1672                    "changed what you said to 10pt." 
1673                );
1674                s = 10 * unity;
1675            }
1676        } else if (tex_scan_keyword("scaled")) {
1677            s = tex_scan_integer(0, NULL, NULL);
1678            if ((s <= 0) || (s > 0x8000)) {
1679                tex_handle_error(
1680                    normal_error_type,
1681                    "Illegal magnification has been changed to 1000 (%i)",
1682                    s,
1683                    "The magnification ratio must be between 1 and 32768."
1684                );
1685                s = -scaling_factor;
1686            } else {
1687                s = -s;
1688            }
1689        }
1690        lmt_fileio_state.name_in_progress = 0;
1691        f = tex_read_font_info(fn, s);
1692        eq_value(u) = f;
1693        lmt_memory_free(fn);
1694        return 1;
1695    } else {
1696        return 0;
1697    }
1698}
1699
1700/*tex
1701
1702    When \TEX\ wants to typeset a character that doesn't exist, the character node is not created;
1703    thus the output routine can assume that characters exist when it sees them. The following
1704    procedure prints a warning message unless the user has suppressed it. When this feature was 
1705    extended in \LUATEX\ (2025) that was a good moment to get rid of the one occasion where we did
1706    not yet use the callback but the message instead (in math because then we really need the 
1707    character). Contrary to \LUATEX\ we never error because the backend where all is resolved can 
1708    do that if needed. 
1709
1710*/
1711
1712void tex_missing_character(halfword n, halfword f, halfword c, halfword where) 
1713{
1714    int callback_id = lmt_callback_defined(missing_character_callback);
1715    if (callback_id > 0) {
1716        lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dNdd->", where, n, f, c);
1717    } else if (tracing_lost_chars_par > 0) { 
1718        int old_setting = tracing_online_par;
1719        if (tracing_lost_chars_par > 1) {
1720            tracing_online_par = 1;
1721        }
1722        tex_begin_diagnostic();
1723        tex_print_format("[font: missing character, character %c (%U), font '%s', location %i]", c, c, font_name(f), where);
1724        tex_end_diagnostic();
1725        tracing_online_par = old_setting;
1726    }
1727}
1728
1729/* Getters. */
1730
1731scaled tex_char_width_from_font(halfword f, halfword c)
1732{
1733    return tex_aux_char_info(f, c)->width;
1734}
1735
1736scaled tex_char_height_from_font(halfword f, halfword c)
1737{
1738    return tex_aux_char_info(f, c)->height;
1739}
1740
1741scaled tex_char_depth_from_font(halfword f, halfword c)
1742{
1743    return tex_aux_char_info(f, c)->depth;
1744}
1745
1746scaled tex_char_total_from_font(halfword f, halfword c)
1747{
1748    charinfo *ci = tex_aux_char_info(f, c);
1749    return ci->height + ci->depth;
1750}
1751
1752scaled tex_char_italic_from_font(halfword f, halfword c)
1753{
1754    return tex_aux_char_info(f, c)->italic;
1755}
1756
1757// scaled tex_char_options_from_font(halfword f, halfword c)
1758// {
1759//     charinfo *ci = tex_aux_char_info(f, c);
1760//     return ci->math ? ci->math->options : 0;
1761// }
1762//
1763// int tex_char_has_option_from_font(halfword f, halfword c, int option)
1764// {
1765//     charinfo *ci = tex_aux_char_info(f, c);
1766//     return ci->math ? math_font_option(ci->math->options, option) : 0;
1767// }
1768
1769scaledwhd tex_char_whd_from_font(halfword f, halfword c)
1770{
1771    charinfo *ci = tex_aux_char_info(f, c);
1772    return (scaledwhd) {
1773        .wd = ci->width,
1774        .ht = ci->height,
1775        .dp = ci->depth,
1776        .ic = ci->italic
1777    };
1778}
1779
1780static charinfo *tex_aux_quality_char_info(halfword f, int c)
1781{
1782    if (f > lmt_font_state.font_data.ptr) {
1783        return NULL;
1784    } else if (proper_char_index(f, c)) {
1785        if (! has_font_text_control(f, text_control_quality_set)) { 
1786            int callback_id = lmt_callback_defined(quality_font_callback);
1787            if (callback_id > 0) {
1788                lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "d->", f);
1789                set_font_text_control(f, text_control_quality_set);
1790            }
1791        }
1792        return &(lmt_font_state.fonts[f]->chardata[(int) aux_find_charinfo_id(f, c)]);
1793    } else {
1794        return NULL;
1795    }
1796}
1797
1798scaled tex_char_ef_from_font(halfword f, halfword c)
1799{
1800    charinfo *co = tex_aux_quality_char_info(f, c); 
1801    return co ? co->expansion : 0;
1802}
1803
1804scaled tex_char_cf_from_font(halfword f, halfword c)
1805{
1806    charinfo *co = tex_aux_quality_char_info(f, c); 
1807    return co ? co->compression : 0;
1808}
1809
1810scaled tex_char_lp_from_font(halfword f, halfword c)
1811{
1812    charinfo *co = tex_aux_quality_char_info(f, c); 
1813    return co ? co->leftprotrusion : 0;
1814}
1815
1816scaled tex_char_rp_from_font(halfword f, halfword c)
1817{
1818    charinfo *co = tex_aux_quality_char_info(f, c); 
1819    return co ? co->rightprotrusion : 0;
1820}
1821
1822halfword tex_char_has_tag_from_font(halfword f, halfword c, halfword tag)
1823{
1824    return (tex_aux_char_info(f, c)->tag & tag) == tag;
1825}
1826
1827void tex_char_reset_tag_from_font(halfword f, halfword c, halfword tag)
1828{
1829    charinfo *ci = tex_aux_char_info(f, c);
1830    ci->tag = ci->tag & ~(tag);
1831}
1832
1833halfword tex_char_tag_from_font(halfword f, halfword c)
1834{
1835    return tex_aux_char_info(f, c)->tag;
1836}
1837
1838int tex_char_checked_tag(halfword tag)
1839{ 
1840    return tag & (
1841        horizontal_tag | vertical_tag 
1842      | extend_last_tag | italic_tag | n_ary_tag | radical_tag | punctuation_tag 
1843      | above_baseline_tag | below_baseline_tag | force_extensible_tag
1844    );
1845}
1846
1847halfword tex_char_next_from_font(halfword f, halfword c)
1848{
1849    charinfo *ci = tex_aux_char_info(f, c);
1850    return ci->math ? ci->math->next : -1;
1851}
1852
1853halfword tex_char_extensible_italic_from_font(halfword f, halfword c)
1854{
1855    charinfo *ci = tex_aux_char_info(f, c);
1856    return ci->math ? ci->math->extensible_italic : INT_MIN;
1857}
1858
1859halfword tex_char_unchecked_top_anchor_from_font(halfword f, halfword c)
1860{
1861    charinfo *ci = tex_aux_char_info(f, c);
1862    return ci->math ? ci->math->top_anchor : INT_MIN;
1863}
1864
1865halfword tex_char_top_anchor_from_font(halfword f, halfword c)
1866{
1867    scaled n = tex_char_unchecked_top_anchor_from_font(f, c);
1868    return n == INT_MIN ? 0 : n;
1869}
1870
1871halfword tex_char_unchecked_bottom_anchor_from_font(halfword f, halfword c)
1872{
1873    charinfo *ci = tex_aux_char_info(f, c);
1874    return ci->math ? ci->math->bottom_anchor : INT_MIN;
1875}
1876
1877halfword tex_char_bottom_anchor_from_font(halfword f, halfword c)
1878{
1879    scaled n = tex_char_unchecked_bottom_anchor_from_font(f, c);
1880    return n == INT_MIN ? 0 : n;
1881}
1882
1883halfword tex_char_flat_accent_from_font(halfword f, halfword c)
1884{
1885    charinfo *ci = tex_aux_char_info(f, c);
1886    return ci->math ? ci->math->flat_accent : INT_MIN;
1887}
1888
1889scaled tex_char_top_left_kern_from_font(halfword f, halfword c)
1890{
1891    charinfo *ci = tex_aux_char_info(f, c);
1892    return ci->math ? ci->math->top_left_kern : 0;
1893}
1894
1895scaled tex_char_top_right_kern_from_font(halfword f, halfword c)
1896{
1897    charinfo *ci = tex_aux_char_info(f, c);
1898    return ci->math ? ci->math->top_right_kern : 0;
1899}
1900
1901scaled tex_char_bottom_left_kern_from_font(halfword f, halfword c)
1902{
1903    charinfo *ci = tex_aux_char_info(f, c);
1904    return ci->math ? ci->math->bottom_left_kern : 0;
1905}
1906
1907scaled tex_char_bottom_right_kern_from_font(halfword f, halfword c)
1908{
1909    charinfo *ci = tex_aux_char_info(f, c);
1910    return ci->math ? ci->math->bottom_right_kern : 0;
1911}
1912
1913extinfo *tex_char_extensible_recipe_from_font(halfword f, halfword c)
1914{
1915    charinfo *ci = tex_aux_char_info(f, c);
1916    return ci->math ? ci->math->extensible_recipe : NULL;
1917}
1918
1919extinfo *tex_char_extensible_recipe_front_last(halfword f, halfword c)
1920{
1921    charinfo *ci = tex_aux_char_info(f, c);
1922    while (ci) { 
1923        halfword next = ci->math ? ci->math->next : -1;
1924        if (next > 0) { // no zero 
1925            ci = tex_aux_char_info(f, c);
1926        } else { 
1927            return ci->math ? ci->math->extensible_recipe : NULL;
1928        }
1929    }
1930    return NULL;
1931}
1932
1933scaled tex_char_left_margin_from_font(halfword f, halfword c)
1934{
1935    charinfo *ci = tex_aux_char_info(f, c);
1936    return ci->math ? ci->math->left_margin : 0;
1937}
1938
1939scaled tex_char_right_margin_from_font(halfword f, halfword c)
1940{
1941    charinfo *ci = tex_aux_char_info(f, c);
1942    return ci->math ? ci->math->right_margin : 0;
1943}
1944
1945scaled tex_char_top_margin_from_font(halfword f, halfword c)
1946{
1947    charinfo *ci = tex_aux_char_info(f, c);
1948    return ci->math ? ci->math->top_margin : 0;
1949}
1950
1951scaled tex_char_bottom_margin_from_font(halfword f, halfword c)
1952{
1953    charinfo *ci = tex_aux_char_info(f, c);
1954    return ci->math ? ci->math->bottom_margin : 0;
1955}
1956
1957scaled tex_char_top_overshoot_from_font(halfword f, halfword c)
1958{
1959    charinfo *ci = tex_aux_char_info(f, c);
1960    return ci->math ? ci->math->top_overshoot : 0;
1961}
1962
1963scaled tex_char_bottom_overshoot_from_font(halfword f, halfword c)
1964{
1965    charinfo *ci = tex_aux_char_info(f, c);
1966    return ci->math ? ci->math->bottom_overshoot : 0;
1967}
1968
1969scaled tex_char_inner_x_offset_from_font(halfword f, halfword c)
1970{
1971    charinfo *ci = tex_aux_char_info(f, c);
1972    return ci->math ? ci->math->inner_x_offset : 0;
1973}
1974
1975scaled tex_char_inner_y_offset_from_font(halfword f, halfword c)
1976{
1977    charinfo *ci = tex_aux_char_info(f, c);
1978    return ci->math ? ci->math->inner_y_offset : 0;
1979}
1980
1981/* Nodes */
1982
1983scaled tex_char_width_from_glyph(halfword g)
1984{
1985    charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g));
1986    return tex_aux_glyph_x_scaled(g, ci->width + 2 * tex_aux_glyph_weight_done(g));
1987}
1988
1989scaled tex_char_height_from_glyph(halfword g)
1990{
1991    charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g));
1992    return tex_aux_glyph_y_scaled(g, ci->height + (has_charinfo_tag(ci,below_baseline_tag) ? 0 : tex_aux_glyph_weight_done(g)));
1993}
1994
1995scaled tex_char_depth_from_glyph(halfword g)
1996{
1997    charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g));
1998    return tex_aux_glyph_y_scaled(g, ci->depth + (has_charinfo_tag(ci,above_baseline_tag) ? 0 : tex_aux_glyph_weight_done(g)));
1999}
2000
2001scaled tex_char_total_from_glyph(halfword g)
2002{
2003    charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g));
2004    scaled ht = ci->height + (has_charinfo_tag(ci,below_baseline_tag) ? 0 : tex_aux_glyph_weight_done(g));
2005    scaled dp = ci->depth + (has_charinfo_tag(ci,above_baseline_tag) ? 0 : tex_aux_glyph_weight_done(g));
2006    return tex_aux_glyph_y_scaled(g, (ht > 0 ? ht : 0) + (dp > 0 ? dp : 0)); /* so not progression */
2007}
2008
2009scaled tex_char_italic_from_glyph(halfword g)
2010{
2011    charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g));
2012 // return tex_aux_glyph_x_scaled(g, ci->italic + tex_aux_glyph_weight_done(g));
2013    return tex_aux_glyph_x_scaled(g, ci->italic);
2014}
2015
2016scaledkrn tex_char_corner_kerns_from_glyph(halfword g)
2017{
2018    charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g));
2019    if (ci->math) { 
2020        scaled w = tex_aux_glyph_weight_done(g);
2021        return (scaledkrn) { 
2022            .bl = ci->math->bottom_left_kern  ? tex_aux_glyph_y_scaled(g, ci->math->bottom_left_kern  + w) : 0,
2023            .br = ci->math->bottom_right_kern ? tex_aux_glyph_y_scaled(g, ci->math->bottom_right_kern + w) : 0,
2024            .tr = ci->math->top_right_kern    ? tex_aux_glyph_y_scaled(g, ci->math->top_right_kern    + w) : 0,
2025            .tl = ci->math->top_left_kern     ? tex_aux_glyph_y_scaled(g, ci->math->top_left_kern     + w) : 0,
2026        };
2027    } else { 
2028        return (scaledkrn) { 0, 0, 0, 0 }; 
2029    }
2030}
2031
2032scaled tex_char_left_protrusion_from_glyph(halfword g)
2033{
2034    charinfo *ci = tex_aux_quality_char_info(glyph_font(g), glyph_character(g));
2035    return ci ? tex_aux_glyph_x_scaled(g, ci->leftprotrusion + tex_aux_glyph_weight_done(g)) : 0;
2036}
2037
2038scaled tex_char_right_protrusion_from_glyph(halfword g)
2039{
2040    charinfo *ci = tex_aux_quality_char_info(glyph_font(g), glyph_character(g));
2041    return ci ? tex_aux_glyph_x_scaled(g, ci->rightprotrusion + tex_aux_glyph_weight_done(g)) : 0;
2042}
2043
2044// halfword tex_char_options_from_glyph(halfword g)
2045// {
2046//     charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g));
2047//     return ci->math ? ci->math->options : 0;
2048// }
2049
2050// int tex_char_has_option_from_glyph(halfword g, int t)
2051// {
2052//     if (node_type(g) == glyph_node) {
2053//         charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g));
2054//         return ci->math ? math_font_option(ci->math->options, t) : 0;
2055//     } else {
2056//         return 0;
2057//     }
2058// }
2059
2060scaledwhd tex_char_whd_from_glyph(halfword g)
2061{
2062    charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g));
2063    scaled w = tex_aux_glyph_weight_done(g);
2064    return (scaledwhd) {
2065        .wd = tex_aux_glyph_x_scaled(g, ci->width  + w * 2),
2066        .ht = tex_aux_glyph_y_scaled(g, ci->height + (has_charinfo_tag(ci,below_baseline_tag) ? 0 : w)),
2067        .dp = tex_aux_glyph_y_scaled(g, ci->depth  + (has_charinfo_tag(ci,above_baseline_tag) ? 0 : w)),
2068     // .ic = tex_aux_glyph_x_scaled(g, ci->italic + w),
2069        .ic = tex_aux_glyph_x_scaled(g, ci->italic),
2070    };
2071}
2072
2073scaled tex_char_width_italic_from_glyph(halfword g)
2074{
2075    charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g));
2076    /* How about weight here? */
2077    return tex_aux_glyph_x_scaled(g, ci->width + ci->italic);
2078}
2079
2080/* More */
2081
2082scaled tex_calculated_char_width(halfword f, halfword c, halfword ex)
2083{
2084    scaled wd = tex_aux_char_info(f, c)->width;
2085    return ex ? tex_round_xn_over_d(wd, scaling_factor + ex, scaling_factor) : wd;
2086}
2087
2088scaled tex_calculated_glyph_width(halfword g, halfword ex)
2089{
2090    charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g));
2091    scaled wd = tex_aux_glyph_x_scaled(g, ci->width + 2 * tex_aux_glyph_weight_done(g));
2092    return ex ? tex_round_xn_over_d(wd, scaling_factor + ex, scaling_factor) : wd;
2093}
2094
2095/* Checkers: */
2096
2097int tex_has_ligature(halfword f, halfword c)
2098{
2099    charinfo *ci = tex_aux_char_info(f, c);
2100    return ci ? ci->ligatures != NULL : 0;
2101}
2102
2103int tex_has_kern(halfword f, halfword c)
2104{
2105    charinfo *ci = tex_aux_char_info(f, c);
2106    return ci ? ci->kerns != NULL : 0;
2107}
2108
2109int tex_char_has_math(halfword f, halfword c)
2110{
2111    charinfo *ci = tex_aux_char_info(f, c);
2112    return ci ? ci->math != NULL : 0;
2113}
2114
2115/* Setters: */
2116
2117void tex_set_lpcode_in_font(halfword f, halfword c, halfword i)
2118{
2119    charinfo *ci = tex_aux_char_info(f, c);
2120    if (ci) {
2121        ci->leftprotrusion = i;
2122    }
2123}
2124
2125void tex_set_rpcode_in_font(halfword f, halfword c, halfword i)
2126{
2127    charinfo *ci = tex_aux_char_info(f, c);
2128    if (ci) {
2129        ci->rightprotrusion = i;
2130    }
2131}
2132
2133void tex_set_efcode_in_font(halfword f, halfword c, halfword i) {
2134    charinfo *ci = tex_aux_char_info(f, c);
2135    if (ci) {
2136        ci->expansion = i;
2137    }
2138}
2139
2140void tex_set_cfcode_in_font(halfword f, halfword c, halfword i) {
2141    charinfo *ci = tex_aux_char_info(f, c);
2142    if (ci) {
2143        ci->compression = i;
2144    }
2145}
2146
2147void tex_set_font_name(halfword f, const char *s)
2148{
2149    if (font_name(f)) {
2150        lmt_memory_free(font_name(f));
2151    }
2152    set_font_name(f, s ? lmt_memory_strdup(s) : NULL);
2153}
2154
2155void tex_set_font_original(halfword f, const char *s)
2156{
2157    if (font_original(f)) {
2158        lmt_memory_free(font_original(f));
2159    }
2160    set_font_original(f, s ? lmt_memory_strdup(s) : NULL);
2161}
2162
2163scaled tex_get_math_font_scale(halfword f, halfword size)
2164{
2165    scaled scale;
2166    switch (size) {
2167        case  2: scale = (lmt_font_state.fonts[f]->mathscales[2] ? lmt_font_state.fonts[f]->mathscales[2] : scaling_factor) * glyph_scriptscript_scale_par; break;
2168        case  1: scale = (lmt_font_state.fonts[f]->mathscales[1] ? lmt_font_state.fonts[f]->mathscales[1] : scaling_factor) * glyph_script_scale_par;       break;
2169        default: scale = (lmt_font_state.fonts[f]->mathscales[0] ? lmt_font_state.fonts[f]->mathscales[0] : scaling_factor) * glyph_text_scale_par;         break;
2170    }
2171    scale = scaledround(0.001 * (double) scale);
2172    return scale ? scale : scaling_factor;
2173}
2174
2175scaled tex_get_math_font_x_scale(halfword f, halfword size)
2176{
2177    scaled scale;
2178    switch (size) {
2179        case  2: scale = (lmt_font_state.fonts[f]->mathxscales[2] ? lmt_font_state.fonts[f]->mathxscales[2] : scaling_factor) * glyph_scriptscript_scale_par; break;
2180        case  1: scale = (lmt_font_state.fonts[f]->mathxscales[1] ? lmt_font_state.fonts[f]->mathxscales[1] : scaling_factor) * glyph_script_scale_par;       break;
2181        default: scale = (lmt_font_state.fonts[f]->mathxscales[0] ? lmt_font_state.fonts[f]->mathxscales[0] : scaling_factor) * glyph_text_scale_par;         break;
2182    }
2183    scale = scaledround(0.001 * (double) scale);
2184    return scale ? scale : scaling_factor;
2185}
2186
2187scaled tex_get_math_font_y_scale(halfword f, halfword size)
2188{
2189    scaled scale;
2190    switch (size) {
2191        case  2: scale = (lmt_font_state.fonts[f]->mathyscales[2] ? lmt_font_state.fonts[f]->mathyscales[2] : scaling_factor) * glyph_scriptscript_scale_par; break;
2192        case  1: scale = (lmt_font_state.fonts[f]->mathyscales[1] ? lmt_font_state.fonts[f]->mathyscales[1] : scaling_factor) * glyph_script_scale_par;       break;
2193        default: scale = (lmt_font_state.fonts[f]->mathyscales[0] ? lmt_font_state.fonts[f]->mathyscales[0] : scaling_factor) * glyph_text_scale_par;         break;
2194    }
2195    scale = scaledround(0.001 * (double) scale);
2196    return scale ? scale : scaling_factor;
2197}
2198
2199scaled tex_get_math_font_weight(halfword f, halfword size)
2200{
2201    switch (size) {
2202        case  2: return lmt_font_state.fonts[f]->mathweights[2];
2203        case  1: return lmt_font_state.fonts[f]->mathweights[1];
2204        default: return lmt_font_state.fonts[f]->mathweights[0];
2205    }
2206}
2207
2208// scaled tex_raw_math_font_scale(halfword f, halfword size)
2209// {
2210//     scaled scale;
2211//     switch (size) {
2212//         case  2: scale = (lmt_font_state.fonts[f]->mathscales[2] ? lmt_font_state.fonts[f]->mathscales[2] : scaling_factor) * glyph_scriptscript_scale_par; break;
2213//         case  1: scale = (lmt_font_state.fonts[f]->mathscales[1] ? lmt_font_state.fonts[f]->mathscales[1] : scaling_factor) * glyph_script_scale_par;       break;
2214//         default: scale = (lmt_font_state.fonts[f]->mathscales[0] ? lmt_font_state.fonts[f]->mathscales[0] : scaling_factor) * glyph_text_scale_par;         break;
2215//     }
2216//     return scale ? scale : scaling_factor * scaling_factor;
2217// }
2218// 
2219// scaled tex_raw_math_font_x_scale(halfword f, halfword size)
2220// {
2221//     scaled scale;
2222//     switch (size) {
2223//         case  2: scale = (lmt_font_state.fonts[f]->mathxscales[2] ? lmt_font_state.fonts[f]->mathxscales[2] : scaling_factor) * glyph_scriptscript_scale_par; break;
2224//         case  1: scale = (lmt_font_state.fonts[f]->mathxscales[1] ? lmt_font_state.fonts[f]->mathxscales[1] : scaling_factor) * glyph_script_scale_par;       break;
2225//         default: scale = (lmt_font_state.fonts[f]->mathxscales[0] ? lmt_font_state.fonts[f]->mathxscales[0] : scaling_factor) * glyph_text_scale_par;         break;
2226//     }
2227//     return scale ? scale : scaling_factor * scaling_factor;
2228// }
2229// 
2230// scaled tex_raw_math_font_y_scale(halfword f, halfword size)
2231// {
2232//     scaled scale;
2233//     switch (size) {
2234//         case  2: scale = (lmt_font_state.fonts[f]->mathyscales[2] ? lmt_font_state.fonts[f]->mathyscales[2] : scaling_factor) * glyph_scriptscript_scale_par; break;
2235//         case  1: scale = (lmt_font_state.fonts[f]->mathyscales[1] ? lmt_font_state.fonts[f]->mathyscales[1] : scaling_factor) * glyph_script_scale_par;       break;
2236//         default: scale = (lmt_font_state.fonts[f]->mathyscales[0] ? lmt_font_state.fonts[f]->mathyscales[0] : scaling_factor) * glyph_text_scale_par;         break;
2237//     }
2238//     return scale ? scale : scaling_factor * scaling_factor;
2239// }
2240
2241scaled tex_get_math_font_factor(halfword size)
2242{
2243    switch (size) {
2244        case  2: return glyph_scriptscript_scale_par;
2245        case  1: return glyph_script_scale_par;      
2246        default: return glyph_text_scale_par;        
2247    }
2248}
2249
2250/*tex
2251    Experiment.
2252*/
2253
2254// void tex_run_font_spec(void)
2255// {
2256//     update_tex_font_identifier(font_spec_identifier(cur_chr));
2257//     if (font_spec_scale(cur_chr) != unused_scale_value) {
2258//         update_tex_glyph_scale(font_spec_scale(cur_chr));
2259//     }
2260//     if (font_spec_x_scale(cur_chr) != unused_scale_value) {
2261//         update_tex_glyph_x_scale(font_spec_x_scale(cur_chr));
2262//     }
2263//     if (font_spec_y_scale(cur_chr) != unused_scale_value) {
2264//         update_tex_glyph_y_scale(font_spec_y_scale(cur_chr));
2265//     }
2266//     if (font_spec_slant(cur_chr)) {
2267//         update_tex_glyph_slant(font_spec_slant(cur_chr));
2268//     }
2269//     if (font_spec_weight(cur_chr)) {
2270//         update_tex_glyph_weight(font_spec_weight(cur_chr));
2271//     }
2272// }
2273
2274void tex_run_font_spec(void)
2275{
2276    update_tex_font_identifier(font_spec_identifier(cur_chr));
2277if (font_spec_property_is_set(cur_chr,font_spec_scale_set)) {
2278    if (font_spec_scale(cur_chr) != glyph_scale_par) {
2279        update_tex_glyph_scale(font_spec_scale(cur_chr));
2280    }
2281}
2282if (font_spec_property_is_set(cur_chr,font_spec_x_scale_set)) {
2283    if (font_spec_x_scale(cur_chr) != glyph_x_scale_par) {
2284        update_tex_glyph_x_scale(font_spec_x_scale(cur_chr));
2285    }
2286}
2287if (font_spec_property_is_set(cur_chr,font_spec_y_scale_set)) {
2288    if (font_spec_y_scale(cur_chr) != glyph_y_scale_par)  {
2289        update_tex_glyph_y_scale(font_spec_y_scale(cur_chr));
2290    }
2291}
2292if (font_spec_property_is_set(cur_chr,font_spec_slant_set)) {
2293    if (font_spec_y_scale(cur_chr) != glyph_slant_par) {
2294        update_tex_glyph_slant(font_spec_slant(cur_chr));
2295    }
2296}
2297if (font_spec_property_is_set(cur_chr,font_spec_weight_set)) {
2298    if (font_spec_weight(cur_chr) != glyph_weight_par) {
2299        update_tex_glyph_weight(font_spec_weight(cur_chr));
2300    }
2301}
2302}
2303