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