diff options
Diffstat (limited to 'source/luametatex/source/tex/texfont.c')
-rw-r--r-- | source/luametatex/source/tex/texfont.c | 2062 |
1 files changed, 2062 insertions, 0 deletions
diff --git a/source/luametatex/source/tex/texfont.c b/source/luametatex/source/tex/texfont.c new file mode 100644 index 000000000..dd63044ec --- /dev/null +++ b/source/luametatex/source/tex/texfont.c @@ -0,0 +1,2062 @@ +/* + See license.txt in the root of this project. +*/ + +/*tex + + Here is the main font API implementation for the original pascal parts. Stuff to watch out for: + + \startitemize + + \startitem + Knuth had a |null_character| that was used when a character could not be found by the + |fetch()| routine, to signal an error. This has been deleted, but it may mean that the + output of luatex is incompatible with TeX after |fetch()| has detected an error + condition. + \stopitem + + \startitem + Knuth also had a |font_glue()| optimization. This has been removed because it was a bit + of dirty programming and it also was problematic |if 0 != null|. + \stopitem + + \stopitemize + +*/ + +# include "luametatex.h" + +# define proper_char_index(f, c) (c >= font_first_character(f) && c <= font_last_character(f)) + +inline static scaled tex_aux_font_x_scaled(scaled v) +{ + return v ? scaledround(0.000001 * (glyph_scale_par ? glyph_scale_par : 1000) * (glyph_x_scale_par ? glyph_x_scale_par : 1000) * v) : 0; +} + +inline static scaled tex_aux_font_y_scaled(scaled v) +{ + return v ? scaledround(0.000001 * (glyph_scale_par ? glyph_scale_par : 1000) * (glyph_y_scale_par ? glyph_y_scale_par : 1000) * v) : 0; +} + +inline static scaled tex_aux_glyph_x_scaled(halfword g, scaled v) +{ + return v ? scaledround(0.000001 * (glyph_scale(g) ? glyph_scale(g) : 1000) * (glyph_x_scale(g) ? glyph_x_scale(g) : 1000) * v) : 0; +} + +inline static scaled tex_aux_glyph_y_scaled(halfword g, scaled v) +{ + return v ? scaledround(0.000001 * (glyph_scale(g) ? glyph_scale(g) : 1000) * (glyph_y_scale(g) ? glyph_y_scale(g) : 1000) * v) : 0; +} + +font_state_info lmt_font_state = { + .fonts = NULL, + .adjust_stretch = 0, + .adjust_shrink = 0, + .adjust_step = 0, + .padding = 0, + .font_data = { + .minimum = min_font_size, + .maximum = max_font_size, + .size = memory_data_unset, + .step = stp_font_size, + .allocated = 0, + .itemsize = 1, + .top = 0, + .ptr = 0, + .initial = memory_data_unset, + .offset = 0, + }, +}; + +/*tex + There can be holes in the font id range. And \unknown\ nullfont is special! Contrary + to other places, here we don't reallocate an array of records but one of pointers. +*/ + +void tex_initialize_fonts(void) +{ + texfont **tmp = aux_allocate_clear_array(sizeof(texfont *), lmt_font_state.font_data.minimum, 0); + if (tmp) { + for (int i = 0; i < lmt_font_state.font_data.minimum; i++) { + tmp[i] = NULL; + } + lmt_font_state.fonts = tmp; + lmt_font_state.font_data.allocated += lmt_font_state.font_data.minimum * sizeof(texfont *); + lmt_font_state.font_data.top = lmt_font_state.font_data.minimum; + lmt_font_state.font_data.ptr = -1; /* we need to end up with id zero first */ + tex_create_null_font(); + } else { + tex_overflow_error("fonts", lmt_font_state.font_data.minimum); + } +} + +/*tex If a slot is not used .. so be it. We want sequential numbers. */ + +int tex_new_font_id(void) +{ + if (lmt_font_state.font_data.ptr < lmt_font_state.font_data.top) { + ++lmt_font_state.font_data.ptr; + return lmt_font_state.font_data.ptr; + } else if (lmt_font_state.font_data.top < lmt_font_state.font_data.maximum) { + texfont **tmp ; + int top = lmt_font_state.font_data.top + lmt_font_state.font_data.step; + if (top > lmt_font_state.font_data.maximum) { + top = lmt_font_state.font_data.maximum; + } + tmp = aux_reallocate_array(lmt_font_state.fonts, sizeof(texfont *), top, 0); + if (tmp) { + for (int i = lmt_font_state.font_data.top + 1; i < top; i++) { + tmp[i] = NULL; + } + lmt_font_state.fonts = tmp; + lmt_font_state.font_data.allocated += ((size_t) top - lmt_font_state.font_data.top) * sizeof(texfont *); + lmt_font_state.font_data.top = top; + lmt_font_state.font_data.ptr += 1; + return lmt_font_state.font_data.ptr; + } + } + tex_overflow_error("fonts", lmt_font_state.font_data.maximum); + return 0; +} + +int tex_get_font_max_id(void) +{ + return lmt_font_state.font_data.ptr; +} + +void tex_dump_font_data(dumpstream f) { + dump_int(f, lmt_font_state.font_data.ptr); +} + +void tex_undump_font_data(dumpstream f) { + int x; + undump_int(f, x); + lmt_font_state.font_data.ptr = 0; +} + +void tex_set_charinfo_vertical_parts(charinfo *ci, extinfo *ext) +{ + if (ci->math) { + if (ci->math->vertical_parts) { + extinfo *lst = ci->math->vertical_parts; + while (lst) { + extinfo *c = lst->next; + lmt_memory_free(lst); + lst = c; + } + } + ci->math->vertical_parts = ext; + } +} + +void tex_set_charinfo_horizontal_parts(charinfo *ci, extinfo *ext) +{ + if (ci->math) { + if (ci->math->horizontal_parts) { + extinfo *lst = ci->math->horizontal_parts; + while (lst) { + extinfo *c = lst->next; + lmt_memory_free(lst); + lst = c; + } + } + ci->math->horizontal_parts = ext; + } +} + +void tex_set_font_parameters(halfword f, int b) +{ + int i = font_parameter_count(f); + if (b > i) { + /*tex If really needed this can be a calloc. */ + int s = (b + 2) * (int) sizeof(int); + int *a = lmt_memory_realloc(font_parameter_base(f), (size_t) s); + if (a) { + lmt_font_state.font_data.allocated += (b - i + 1) * (int) sizeof(scaled); + font_parameter_base(f) = a; + font_parameter_count(f) = b; + while (i < b) { + font_parameter(f, ++i) = 0; + } + } else { + tex_overflow_error("font", s); + } + } +} + +/*tex Most stuff is zero: */ + +int tex_new_font(void) +{ + int size = sizeof(charinfo); + charinfo *ci = lmt_memory_calloc(1, (size_t) size); + if (ci) { + texfont *t = NULL; + size = sizeof(texfont); + t = lmt_memory_calloc(1, (size_t) size); + if (t) { + sa_tree_item sa_value = { 0 }; + int id = tex_new_font_id(); + lmt_font_state.font_data.allocated += size; + lmt_font_state.fonts[id] = t; + set_font_name(id, NULL); + set_font_original(id, NULL); + set_font_left_boundary(id, NULL); + set_font_right_boundary(id, NULL); + set_font_parameter_base(id, NULL); + set_font_math_parameter_base(id, NULL); + /*tex |ec = 0| */ + set_font_first_character(id, 1); + set_font_hyphen_char(id, '-'); + set_font_skew_char(id, -1); + /*tex allocate eight values including 0 */ + tex_set_font_parameters(id, 7); + for (int k = 0; k <= 7; k++) { + tex_set_font_parameter(id, k, 0); + } + /*tex character info zero is reserved for |notdef|. The stack size 1, default item value 0. */ + t->characters = sa_new_tree(1, 4, sa_value); + t->chardata = ci; + t->chardata_size = 1; + return id; + } + } + tex_overflow_error("font", size); + return 0; +} + +void tex_font_malloc_charinfo(halfword f, int num) +{ + int glyph = lmt_font_state.fonts[f]->chardata_size; + int size = (glyph + num) * sizeof(charinfo); + charinfo *data = lmt_memory_realloc(lmt_font_state.fonts[f]->chardata , (size_t) size); + if (data) { + lmt_font_state.font_data.allocated += num * sizeof(charinfo); + lmt_font_state.fonts[f]->chardata = data; + memset(&data[glyph], 0, (size_t) num * sizeof(charinfo)); + lmt_font_state.fonts[f]->chardata_size += num; + } else { + tex_overflow_error("font", size); + } +} + +void tex_char_malloc_mathinfo(charinfo *ci) +{ + int size = sizeof(mathinfo); + mathinfo *mi = lmt_memory_calloc(1, (size_t) size); + if (mi) { + mi->horizontal_parts = NULL; + mi->vertical_parts = NULL; + mi->top_left_math_kern_array = NULL; + mi->top_right_math_kern_array = NULL; + mi->bottom_right_math_kern_array = NULL; + mi->bottom_left_math_kern_array = NULL; + mi->top_left_kern = 0; + mi->top_right_kern = 0; + mi->bottom_left_kern = 0; + mi->bottom_right_kern = 0; + mi->left_margin = 0; + mi->right_margin = 0; + mi->top_margin = 0; + mi->bottom_margin = 0; + if (ci->math) { + /*tex This seldom or probably never happens. */ + tex_set_charinfo_vertical_parts(ci, NULL); + tex_set_charinfo_horizontal_parts(ci, NULL); + set_charinfo_top_left_math_kern_array(ci, NULL); + set_charinfo_top_right_math_kern_array(ci, NULL); + set_charinfo_bottom_right_math_kern_array(ci, NULL); + set_charinfo_bottom_left_math_kern_array(ci, NULL); + lmt_memory_free(ci->math); + } else { + lmt_font_state.font_data.allocated += size; + } + ci->math = mi; + } else { + tex_overflow_error("font", size); + } +} + +# define find_charinfo_id(f,c) (sa_get_item_4(lmt_font_state.fonts[f]->characters,c).int_value) + +charinfo *tex_get_charinfo(halfword f, int c) +{ + if (proper_char_index(f, c)) { + int glyph = sa_get_item_4(lmt_font_state.fonts[f]->characters, c).int_value; + if (! glyph) { + sa_tree_item sa_value = { 0 }; + int tglyph = ++lmt_font_state.fonts[f]->chardata_count; + if (tglyph >= lmt_font_state.fonts[f]->chardata_size) { + tex_font_malloc_charinfo(f, 256); + } + lmt_font_state.fonts[f]->chardata[tglyph].expansion = 1000; + sa_value.int_value = tglyph; + /*tex 1 means global */ + sa_set_item_4(lmt_font_state.fonts[f]->characters, c, sa_value, 1); + glyph = tglyph; + } + return &(lmt_font_state.fonts[f]->chardata[glyph]); + } else if (c == left_boundary_char) { + if (! font_has_left_boundary(f)) { + int size = sizeof(charinfo); + charinfo *ci = lmt_memory_calloc(1, (size_t) size); + if (ci) { + lmt_font_state.font_data.allocated += size; + set_font_left_boundary(f, ci); + } else { + tex_overflow_error("font", size); + } + } + return font_left_boundary(f); + } else if (c == right_boundary_char) { + if (! font_has_right_boundary(f)) { + int size = sizeof(charinfo); + charinfo *ci = lmt_memory_calloc(1, (size_t) size); + if (ci) { + lmt_font_state.font_data.allocated += size; + set_font_right_boundary(f, ci); + } else { + tex_overflow_error("font", size); + } + } + return font_right_boundary(f); + } else { + return &(lmt_font_state.fonts[f]->chardata[0]); + } +} + +static charinfo *tex_aux_char_info(halfword f, int c) +{ + if (f > lmt_font_state.font_data.ptr) { + return NULL; + } else if (proper_char_index(f, c)) { + return &(lmt_font_state.fonts[f]->chardata[(int) find_charinfo_id(f, c)]); + } else if (c == left_boundary_char) { + if (font_left_boundary(f)) { + return font_left_boundary(f); + } + } else if (c == right_boundary_char) { + if (font_right_boundary(f)) { + return font_right_boundary(f); + } + } + return &(lmt_font_state.fonts[f]->chardata[0]); +} + +void tex_char_process(halfword f, int c) +{ + if (tex_char_has_tag_from_font(f, c, callback_tag)) { + int callback_id = lmt_callback_defined(process_character_callback); + if (callback_id > 0) { + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dd->", f, c); + } + tex_char_reset_tag_from_font(f, c, callback_tag); + } +} + +int tex_char_exists(halfword f, int c) +{ + if (f > lmt_font_state.font_data.ptr) { + return 0; + } else if (proper_char_index(f, c)) { + return (int) find_charinfo_id(f, c); + } else if (c == left_boundary_char) { + if (font_has_left_boundary(f)) { + return 1; + } + } else if (c == right_boundary_char) { + if (font_has_right_boundary(f)) { + return 1; + } + } + return 0; +} + +/* + +static int check_math_char(halfword f, int c, int size) +{ + int callback_id = lmt_callback_defined(get_math_char_callback); + if (callback_id > 0) { + halfword s = c; + lmt_run_callback(lua_state.lua_instance, callback_id, "ddd->d", f, c, size, &s); + if (s && proper_char_index(f, s) && find_charinfo_id(f, s)) { + return s; + } + } + return c; +} +*/ + +int tex_math_char_exists(halfword f, int c, int size) +{ + (void) size; + return (f > 0 && f <= lmt_font_state.font_data.ptr && proper_char_index(f, c)); +} + +/*tex + There is a bit overhead due to first fetching but we don't need to check again, so that saves + a little. +*/ + +int tex_get_math_char(halfword f, int c, int size, scaled *scale) +{ + int id = find_charinfo_id(f, c); + texfont *tf = lmt_font_state.fonts[f]; + if (id && size && tf->compactmath) { + for (int i=1;i<=size;i++) { + charinfo *ci = &tf->chardata[id]; + if (ci->math) { + int s = ci->math->smaller; + if (s && proper_char_index(f, s)) { + id = find_charinfo_id(f, s); + if (id) { + /* todo: trace */ + c = s; + } else { + break; + } + } else { + break; + } + } else { + break; + } + } + } + if (scale) { + *scale = tex_get_math_font_scale(f, size); + if (! *scale) { + *scale = 1000; + } + } + /* + if (! id && ! tf->oldmath) { + c = check_math_char(f, c, size); + } + */ + return c; +} + +extinfo *tex_new_charinfo_part(int glyph, int startconnect, int endconnect, int advance, int extender) +{ + int size = sizeof(extinfo); + extinfo *ext = lmt_memory_malloc((size_t) size); + if (ext) { + ext->next = NULL; + ext->glyph = glyph; + ext->start_overlap = startconnect; + ext->end_overlap = endconnect; + ext->advance = advance; + ext->extender = extender; + } else { + tex_overflow_error("font", size); + } + return ext; +} + +void tex_add_charinfo_vertical_part(charinfo *ci, extinfo *ext) +{ + if (ci->math) { + if (ci->math->vertical_parts) { + extinfo *lst = ci->math->vertical_parts; + while (lst->next) + lst = lst->next; + lst->next = ext; + } else { + ci->math->vertical_parts = ext; + } + } +} + +void tex_add_charinfo_horizontal_part(charinfo *ci, extinfo *ext) +{ + if (ci->math) { + if (ci->math->horizontal_parts) { + extinfo *lst = ci->math->horizontal_parts; + while (lst->next) { + lst = lst->next; + } + lst->next = ext; + } else { + ci->math->horizontal_parts = ext; + } + } +} + +/*tex + + Note that many more small things like this are implemented as macros in the header file. + +*/ + +int tex_get_charinfo_math_kerns(charinfo *ci, int id) +{ + /*tex All callers check for |result > 0|. */ + if (ci->math) { + switch (id) { + case top_left_kern: + return ci->math->top_left_math_kerns; + case bottom_left_kern: + return ci->math->bottom_left_math_kerns; + case top_right_kern: + return ci->math->top_right_math_kerns; + case bottom_right_kern: + return ci->math->bottom_right_math_kerns; + default: + tex_confusion("weird math kern"); + break; + } + } + return 0; +} + +void tex_add_charinfo_math_kern(charinfo *ci, int id, scaled ht, scaled krn) +{ + if (ci->math) { + int k = 0; + int s = 0; + scaled *a = NULL; + switch (id) { + case top_right_kern: + { + k = ci->math->top_right_math_kerns; + s = 2 * (k + 1) * (int) sizeof(scaled); + a = lmt_memory_realloc(ci->math->top_right_math_kern_array, (size_t) s); + if (a) { + ci->math->top_right_math_kern_array = a; + ci->math->top_right_math_kerns++; + } + break; + } + case bottom_right_kern: + { + k = ci->math->bottom_right_math_kerns; + s = 2 * (k + 1) * (int) sizeof(scaled); + a = lmt_memory_realloc(ci->math->bottom_right_math_kern_array, (size_t) s); + if (a) { + ci->math->bottom_right_math_kern_array = a; + ci->math->bottom_right_math_kerns++; + } + break; + } + case bottom_left_kern: + { + k = ci->math->bottom_left_math_kerns; + s = 2 * (k + 1) * (int) sizeof(scaled); + a = lmt_memory_realloc(ci->math->bottom_left_math_kern_array, (size_t) s); + if (a) { + ci->math->bottom_left_math_kern_array = a; + ci->math->bottom_left_math_kerns++; + } + break; + } + case top_left_kern: + { + k = ci->math->top_left_math_kerns; + s = 2 * (k + 1) * (int) sizeof(scaled); + a = lmt_memory_realloc(ci->math->top_left_math_kern_array, (size_t) s); + if (a) { + ci->math->top_left_math_kern_array = a; + ci->math->top_left_math_kerns++; + } + break; + } + default: + tex_confusion("add math kern"); + return; + } + if (a) { + a[2 * k] = ht; + a[(2 * k) + 1] = krn; + } else { + tex_overflow_error("font", s); + } + } +} + +/*tex + + In \TEX, extensibles were fairly simple things. This function squeezes a \TFM\ extensible into + the vertical extender structures. |advance == 0| is a special case for \TFM\ fonts, because + finding the proper advance width during \TFM\ reading can be tricky. + + A small complication arises if |rep| is the only non-zero: it needs to be doubled as a + non-repeatable to avoid mayhem. + +*/ + +void tex_set_charinfo_extensible(charinfo *ci, int top, int bottom, int middle, int extender) +{ + if (ci->math) { + extinfo *ext; + /*tex Clear old data: */ + tex_set_charinfo_vertical_parts(ci, NULL); + if (bottom == 0 && top == 0 && middle == 0 && extender != 0) { + ext = tex_new_charinfo_part(extender, 0, 0, 0, math_extension_normal); + tex_add_charinfo_vertical_part(ci, ext); + ext = tex_new_charinfo_part(extender, 0, 0, 0, math_extension_repeat); + tex_add_charinfo_vertical_part(ci, ext); + } else { + if (bottom) { + ext = tex_new_charinfo_part(bottom, 0, 0, 0, math_extension_normal); + tex_add_charinfo_vertical_part(ci, ext); + } + if (extender) { + ext = tex_new_charinfo_part(extender, 0, 0, 0, math_extension_repeat); + tex_add_charinfo_vertical_part(ci, ext); + } + if (middle) { + ext = tex_new_charinfo_part(middle, 0, 0, 0, math_extension_normal); + tex_add_charinfo_vertical_part(ci, ext); + if (extender) { + ext = tex_new_charinfo_part(extender, 0, 0, 0, math_extension_repeat); + tex_add_charinfo_vertical_part(ci, ext); + } + } + if (top) { + ext = tex_new_charinfo_part(top, 0, 0, 0, math_extension_normal); + tex_add_charinfo_vertical_part(ci, ext); + } + } + } +} + +/*tex why not just preallocate for all math otf parameters */ + +void tex_set_font_math_parameters(halfword f, int b) +{ + int i = font_math_parameter_count(f); + if (i < b) { + size_t size = ((size_t) b + 2) * sizeof(scaled); + scaled *data = lmt_memory_realloc(font_math_parameter_base(f), size); + if (data) { + lmt_font_state.font_data.allocated += (int) (((size_t) b - i + 1) * sizeof(scaled)); + font_math_parameter_base(f) = data; + font_math_parameter_count(f) = b; + while (i < b) { + ++i; /* in macro, make the next a function */ + // set_font_math_parameter(f, i, undefined_math_parameter); + font_math_parameter(f, i) = undefined_math_parameter; + } + } else { + tex_overflow_error("font", (int) size); + } + } +} + +void tex_delete_font(int f) +{ + if (lmt_font_state.fonts[f]) { + tex_set_font_name(f, NULL); + tex_set_font_original(f, NULL); + set_font_left_boundary(f, NULL); + set_font_right_boundary(f, NULL); + for (int i = font_first_character(f); i <= font_last_character(f); i++) { + if (quick_char_exists(f, i)) { + charinfo *co = tex_aux_char_info(f, i); + set_charinfo_kerns(co, NULL); + set_charinfo_ligatures(co, NULL); + if (co->math) { + tex_set_charinfo_vertical_parts(co, NULL); + tex_set_charinfo_horizontal_parts(co, NULL); + set_charinfo_top_left_math_kern_array(co, NULL); + set_charinfo_top_right_math_kern_array(co, NULL); + set_charinfo_bottom_right_math_kern_array(co, NULL); + set_charinfo_bottom_left_math_kern_array(co, NULL); + set_charinfo_math(co, NULL); + } + } + } + /*tex free |notdef| */ + lmt_memory_free(lmt_font_state.fonts[f]->chardata); + sa_destroy_tree(lmt_font_state.fonts[f]->characters); + lmt_memory_free(font_parameter_base(f)); + if (font_math_parameter_base(f)) { + lmt_memory_free(font_math_parameter_base(f)); + } + lmt_memory_free(lmt_font_state.fonts[f]); + lmt_font_state.fonts[f] = NULL; + if (lmt_font_state.font_data.ptr == f) { + lmt_font_state.font_data.ptr--; + } + } +} + +void tex_create_null_font(void) +{ + int id = tex_new_font(); + tex_set_font_name(id, "nullfont"); + tex_set_font_original(id, "nullfont"); + /* set_font_touched(id, 1); */ +} + +int tex_is_valid_font(halfword f) +{ + return (f >= 0 && f <= lmt_font_state.font_data.ptr && lmt_font_state.fonts[f]); +} + +int tex_checked_font(halfword f) +{ + return (f >= 0 && f <= lmt_font_state.font_data.ptr && lmt_font_state.fonts[f]) ? f : null_font; +} + +halfword tex_get_font_identifier(halfword fontspec) +{ + if (fontspec) { + halfword fnt = font_spec_identifier(fontspec); + if ((fnt >= 0 && fnt <= lmt_font_state.font_data.ptr && lmt_font_state.fonts[fnt])) { + return fnt; + } + } + return null_font; +} + +/*tex + + Here come some subroutines to deal with expanded fonts. Returning 1 means that they are + identical. + +*/ + +ligatureinfo tex_get_ligature(halfword f, int lc, int rc) +{ + ligatureinfo t = { 0, 0, 0, 0 }; + if (lc != non_boundary_char && rc != non_boundary_char && tex_has_ligature(f, lc)) { + int k = 0; + charinfo *co = tex_aux_char_info(f, lc); + while (1) { + ligatureinfo u = charinfo_ligature(co, k); + if (ligature_end(u)) { + break; + } else if (ligature_char(u) == rc) { + return ligature_disabled(u) ? t : u; + } + k++; + } + } + return t; +} + +int tex_raw_get_kern(halfword f, int lc, int rc) +{ + if (lc != non_boundary_char && rc != non_boundary_char) { + int k = 0; + charinfo *co = tex_aux_char_info(f, lc); + while (1) { + kerninfo u = charinfo_kern(co, k); + if (kern_end(u)) { + break; + } else if (kern_char(u) == rc) { + return kern_disabled(u) ? 0 : kern_kern(u); + } + k++; + } + } + return 0; +} + +int tex_get_kern(halfword f, int lc, int rc) +{ + if (lc == non_boundary_char || rc == non_boundary_char || (! tex_has_kern(f, lc))) { + return 0; + } else { + return tex_raw_get_kern(f, lc, rc); + } +} + +scaled tex_valid_kern(halfword left, halfword right) +{ + if (node_type(left) == glyph_node && node_type(right) == glyph_node) { + halfword fl = glyph_font(left); + halfword fr = glyph_font(right); + halfword cl = glyph_character(left); + halfword cr = glyph_character(right); + 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)) { + return tex_raw_get_kern(fl, cl, cr); + } + } + return 0; +} + +/*tex + + Experiment: + +*/ + +halfword tex_checked_font_adjust(halfword adjust_spacing, halfword adjust_spacing_step, halfword adjust_spacing_shrink, halfword adjust_spacing_stretch) +{ + if (adjust_spacing >= adjust_spacing_full) { + if (adjust_spacing_step > 0) { + lmt_font_state.adjust_step = adjust_spacing_step; + lmt_font_state.adjust_shrink = adjust_spacing_shrink; + lmt_font_state.adjust_stretch = adjust_spacing_stretch; + if (lmt_font_state.adjust_step > 100) { + lmt_font_state.adjust_step = 100; + } + if (lmt_font_state.adjust_shrink < 0) { + lmt_font_state.adjust_shrink = 0; + } else if (lmt_font_state.adjust_shrink > 500) { + lmt_font_state.adjust_shrink = 500; + } + if (lmt_font_state.adjust_stretch < 0) { + lmt_font_state.adjust_stretch = 0; + } else if (lmt_font_state.adjust_stretch > 1000) { + lmt_font_state.adjust_stretch = 1000; + } + return adjust_spacing; + } + } else { + adjust_spacing = adjust_spacing_off; + } + lmt_font_state.adjust_step = 0; + lmt_font_state.adjust_shrink = 0; + lmt_font_state.adjust_stretch = 0; + return adjust_spacing; +} + +/*tex + + This returns the multiple of |font_step(f)| that is nearest to |e|. + +*/ + +int tex_fix_expand_value(halfword f, int e) +{ + int max_expand, neg; + if (e == 0) { + return 0; + } else if (e < 0) { + e = -e; + neg = 1; + max_expand = font_max_shrink(f); + } else { + neg = 0; + max_expand = font_max_stretch(f); + } + if (e > max_expand) { + e = max_expand; + } else { + int step = font_step(f); + if (e % step > 0) { + e = step * tex_round_xn_over_d(e, 1, step); + } + } + return neg ? -e : e; +} + +int tex_read_font_info(char *cnom, scaled s) +{ + int callback_id = lmt_callback_defined(define_font_callback); + if (callback_id > 0) { + int f = 0; + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "Sd->d", cnom, s, &f); + if (tex_is_valid_font(f)) { + tex_set_font_original(f, (char *) cnom); + return f; + } else { + return 0; + } + } else { + tex_normal_warning("fonts","no font has been read, you need to enable or fix the callback"); + return 0; + } +} + +/*tex Abstraction: */ + +halfword tex_get_font_parameter(halfword f, halfword code) /* todo: math */ +{ + if (font_parameter_count(f) < code) { + tex_set_font_parameters(f, code); + } + return font_parameter(f, code); +} + +void tex_set_font_parameter(halfword f, halfword code, scaled v) +{ + if (font_parameter_count(f) < code) { + tex_set_font_parameters(f, code); + } + font_parameter(f, code) = v; +} + +scaled tex_get_font_slant (halfword f) { return font_parameter(f, slant_code); } +scaled tex_get_font_space (halfword f) { return font_parameter(f, space_code); } +scaled tex_get_font_space_stretch (halfword f) { return font_parameter(f, space_stretch_code); } +scaled tex_get_font_space_shrink (halfword f) { return font_parameter(f, space_shrink_code); } +scaled tex_get_font_ex_height (halfword f) { return font_parameter(f, ex_height_code); } +scaled tex_get_font_em_width (halfword f) { return font_parameter(f, em_width_code); } +scaled tex_get_font_extra_space (halfword f) { return font_parameter(f, extra_space_code); } + +scaled tex_get_scaled_slant (halfword f) { return font_parameter(f, slant_code); } +scaled tex_get_scaled_space (halfword f) { return tex_aux_font_x_scaled(font_parameter(f, space_code)); } +scaled tex_get_scaled_space_stretch (halfword f) { return tex_aux_font_x_scaled(font_parameter(f, space_stretch_code)); } +scaled tex_get_scaled_space_shrink (halfword f) { return tex_aux_font_x_scaled(font_parameter(f, space_shrink_code)); } +scaled tex_get_scaled_ex_height (halfword f) { return tex_aux_font_y_scaled(font_parameter(f, ex_height_code)); } +scaled tex_get_scaled_em_width (halfword f) { return tex_aux_font_x_scaled(font_parameter(f, em_width_code)); } +scaled tex_get_scaled_extra_space (halfword f) { return tex_aux_font_x_scaled(font_parameter(f, extra_space_code)); } + +scaled tex_font_x_scaled (scaled v) { return tex_aux_font_x_scaled(v); } +scaled tex_font_y_scaled (scaled v) { return tex_aux_font_y_scaled(v); } + +halfword tex_get_scaled_parameter(halfword f, halfword code) /* todo: math */ +{ + if (font_parameter_count(f) < code) { + tex_set_font_parameters(f, code); + } + switch (code) { + case slant_code: + return font_parameter(f, code); + case ex_height_code: + return tex_aux_font_y_scaled(font_parameter(f, code)); + default: + return tex_aux_font_x_scaled(font_parameter(f, code)); + } +} + +void tex_set_scaled_parameter(halfword f, halfword code, scaled v) +{ + if (font_parameter_count(f) < code) { + tex_set_font_parameters(f, code); + } + font_parameter(f, code) = tex_aux_font_x_scaled(v); +} + +halfword tex_get_scaled_glue(halfword f) +{ + halfword p = tex_new_glue_node(zero_glue, space_skip_glue); + glue_amount(p) = tex_aux_font_x_scaled(font_parameter(f, space_code)); + glue_stretch(p) = tex_aux_font_x_scaled(font_parameter(f, space_stretch_code)); + glue_shrink(p) = tex_aux_font_x_scaled(font_parameter(f, space_shrink_code)); + glue_font(p) = f; + return p; +} + +halfword tex_get_scaled_parameter_glue(quarterword p, quarterword s) +{ + halfword n = tex_new_glue_node(zero_glue, s); + halfword g = glue_parameter(p); + // if (g) { + // memcpy((void *) (node_memory_state.nodes + n + 2), (void *) (node_memory_state.nodes + g + 2), (glue_spec_size - 2) * (sizeof(memoryword))); + // } + glue_amount(n) = tex_aux_font_x_scaled(glue_amount(g)); + glue_stretch(n) = tex_aux_font_x_scaled(glue_stretch(g)); + glue_shrink(n) = tex_aux_font_x_scaled(glue_shrink(g)); + return n; +} + +halfword tex_get_parameter_glue(quarterword p, quarterword s) +{ + halfword n = tex_new_glue_node(zero_glue, s); + halfword g = glue_parameter(p); + if (g) { + memcpy((void *) (lmt_node_memory_state.nodes + n + 2), (void *) (lmt_node_memory_state.nodes + g + 2), (glue_spec_size - 2) * (sizeof(memoryword))); + } + return n; +} + +/*tex Ligaturing starts here */ + +static void tex_aux_nesting_append(halfword nest1, halfword newn) +{ + halfword tail = node_tail(nest1); + tex_couple_nodes(tail ? tail : nest1, newn); + node_tail(nest1) = newn; +} + +static void tex_aux_nesting_prepend(halfword nest1, halfword newn) +{ + halfword head = node_next(nest1); + tex_couple_nodes(nest1, newn); + if (head) { + tex_couple_nodes(newn, head); + } else { + node_tail(nest1) = newn; + } +} + +static void tex_aux_nesting_prepend_list(halfword nest1, halfword newn) +{ + halfword head = node_next(nest1); + halfword tail = tex_tail_of_node_list(newn); + tex_couple_nodes(nest1, newn); + if (head) { + tex_couple_nodes(tail, head); + } else { + node_tail(nest1) = tail; + } +} + +int tex_valid_ligature(halfword left, halfword right, int *slot) +{ + if (node_type(left) != glyph_node) { + return -1; + } else if (glyph_font(left) != glyph_font(right)) { + return -1; + } else if (tex_has_glyph_option(left, glyph_option_no_right_ligature) || tex_has_glyph_option(right, glyph_option_no_left_ligature)) { + return -1; + } else { + ligatureinfo lig = tex_get_ligature(glyph_font(left), glyph_character(left), glyph_character(right)); + if (ligature_is_valid(lig)) { + *slot = ligature_replacement(lig); + return ligature_type(lig); + } else { + return -1; + } + } +} + +static int tex_aux_found_ligature(halfword left, halfword right) +{ + if (node_type(left) != glyph_node) { + return 0; + } else if (glyph_font(left) != glyph_font(right)) { + return 0; + } else if (tex_has_glyph_option(left, glyph_option_no_right_ligature) || tex_has_glyph_option(right, glyph_option_no_left_ligature)) { + return 0; + } else { + return ligature_is_valid(tex_get_ligature(glyph_font(left), glyph_character(left), glyph_character(right))); + } +} + +/*tex + We could be more efficient and reuse the possibly later removed node but it takes more code and + we don't have that many ligatures anyway. +*/ + +static int tex_aux_try_ligature(halfword *first, halfword forward) +{ + halfword cur = *first; + if (glyph_scale(cur) == glyph_scale(forward) && glyph_x_scale(cur) == glyph_x_scale(forward) && glyph_y_scale(cur) == glyph_y_scale(forward)) { + halfword slot; + halfword type = tex_valid_ligature(cur, forward, &slot); + if (type >= 0) { + int move_after = (type & 0x0C) >> 2; + int keep_right = (type & 0x01) != 0; + int keep_left = (type & 0x02) != 0; + halfword parent = (glyph_character(cur) >= 0) ? cur : ((glyph_character(forward) >= 0) ? forward : null); + halfword ligature = tex_new_glyph_node(glyph_ligature_subtype, glyph_font(cur), slot, parent); + if (keep_left) { + tex_couple_nodes(cur, ligature); + if (move_after) { + move_after--; + cur = ligature; + } + } else { + halfword prev = node_prev(cur); + tex_uncouple_node(cur); + tex_flush_node(cur); + tex_couple_nodes(prev, ligature); + cur = ligature; + } + if (keep_right) { + tex_couple_nodes(ligature, forward); + if (move_after) { + move_after--; + cur = forward; + } + } else { + halfword next = node_next(forward); + tex_uncouple_node(forward); + tex_flush_node(forward); + if (next) { + tex_couple_nodes(ligature, next); + } + } + *first = cur; + return 1; + } + } + return 0; +} + +/*tex + + There shouldn't be any ligatures here - we only add them at the end of |xxx_break| in a |DISC-1 + - DISC-2| situation and we stop processing |DISC-1| (we continue with |DISC-1|'s |post_| and + |no_break|. + +*/ + +static halfword tex_aux_handle_ligature_nesting(halfword root, halfword cur) +{ + if (cur) { + while (node_next(cur)) { + halfword fwd = node_next(cur); + if (node_type(cur) == glyph_node && node_type(fwd) == glyph_node && glyph_font(cur) == glyph_font(fwd) && tex_aux_try_ligature(&cur, fwd)) { + continue; + } + cur = node_next(cur); + } + node_tail(root) = cur; + } + return root; +} + +/*tex + + In \LUATEX\ we have a chained variant of discretionaries (init and select) but that never really + works out ok. It was there for basemode to be compatible with original \TEX\ but it was also means + for border cases that in practice never occur. A least no \CONTEXT\ user ever complained about + ligatures and hyphenation of these border cases. Keep in mind that in node mode (which we normally + use) the select discs never showed up anyway. Another reason for dropping these discretionaries is + that by not using them we get more predictable (or at least easier) handling of node lists that do + have (any kind of) discretionaries. It is still on my agenda to look into nested discretionaries + i.e. discs nodes in disc fields but it might never result in useable code. + + Remark: there is now a patch for \LUATEX\ that fixes some long pending issue with select discs but + still it's kind of fuzzy. It also complicates the par builder in a way that I don't really want + (at least in \CONTEXT). It was anyway a good reason for removing traces of these special disc nodes + in \LUAMETATEX. + +*/ + +static halfword tex_aux_handle_ligature_word(halfword cur) +{ + halfword right = null; + if (node_type(cur) == boundary_node) { + halfword prev = node_prev(cur); + halfword fwd = node_next(cur); + /*tex There is no need to uncouple |cur|, it is freed. */ + tex_flush_node(cur); + if (fwd) { + tex_couple_nodes(prev, fwd); + if (node_type(fwd) != glyph_node) { + return prev; + } else { + cur = fwd; + } + } else { + node_next(prev) = fwd; + return prev; + } + } else if (font_has_left_boundary(glyph_font(cur))) { + halfword prev = node_prev(cur); + halfword p = tex_new_glyph_node(glyph_unset_subtype, glyph_font(cur), left_boundary_char, cur); + tex_couple_nodes(prev, p); + tex_couple_nodes(p, cur); + cur = p; + } + if (font_has_right_boundary(glyph_font(cur))) { + right = tex_new_glyph_node(glyph_unset_subtype, glyph_font(cur), right_boundary_char, cur); + } + /* todo: switch */ + while (1) { + halfword t = node_type(cur); + /*tex A glyph followed by \unknown */ + if (t == glyph_node) { + halfword fwd = node_next(cur); + if (fwd) { + t = node_type(fwd); + if (t == glyph_node) { + /*tex a glyph followed by a glyph */ + if (glyph_font(cur) != glyph_font(fwd)) { + break; + } else if (tex_aux_try_ligature(&cur, fwd)) { + continue; + } + } else if (t == disc_node) { + /*tex a glyph followed by a disc */ + halfword pre = disc_pre_break_head(fwd); + halfword nob = disc_no_break_head(fwd); + halfword next, tail; + /*tex Check on: |a{b?}{?}{?}| and |a+b=>B| : |{B?}{?}{a?}| */ + /*tex Check on: |a{?}{?}{b?}| and |a+b=>B| : |{a?}{?}{B?}| */ + if ((pre && node_type(pre) == glyph_node && tex_aux_found_ligature(cur, pre)) + || (nob && node_type(nob) == glyph_node && tex_aux_found_ligature(cur, nob))) { + /*tex Move |cur| from before disc to skipped part */ + halfword prev = node_prev(cur); + tex_uncouple_node(cur); + tex_couple_nodes(prev, fwd); + tex_aux_nesting_prepend(disc_no_break(fwd), cur); + /*tex Now ligature the |pre_break|. */ + tex_aux_nesting_prepend(disc_pre_break(fwd), tex_copy_node(cur)); + /*tex As we have removed cur, we need to start again. */ + cur = prev; + } + /*tex Check on: |a{?}{?}{}b| and |a+b=>B| : |{a?}{?b}{B}|. */ + next = node_next(fwd); + if ((! nob) && next && node_type(next) == glyph_node && tex_aux_found_ligature(cur, next)) { + /*tex Move |cur| from before |disc| to |no_break| part. */ + halfword prev = node_prev(cur); + tex_uncouple_node(cur); + tex_couple_nodes(prev, fwd); + /*tex We {\em know} it's empty. */ + tex_couple_nodes(disc_no_break(fwd), cur); + /*tex Now copy |cur| the |pre_break|. */ + tex_aux_nesting_prepend(disc_pre_break(fwd), tex_copy_node(cur)); + /*tex Move next from after disc to |no_break| part. */ + tail = node_next(next); + tex_uncouple_node(next); + tex_try_couple_nodes(fwd, tail); + /*tex We {\em know} this works. */ + tex_couple_nodes(cur, next); + /*tex Make sure the list is correct. */ + disc_no_break_tail(fwd) = next; + /*tex Now copy next to the |post_break|. */ + tex_aux_nesting_append(disc_post_break(fwd), tex_copy_node(next)); + /*tex As we have removed cur, we need to start again. */ + cur = prev; + } + /*tex We are finished with the |pre_break|. */ + tex_aux_handle_ligature_nesting(disc_pre_break(fwd), disc_pre_break_head(fwd)); + } else if (t == boundary_node) { + halfword next = node_next(fwd); + tex_try_couple_nodes(cur, next); + tex_flush_node(fwd); + if (right) { + /*tex Shame, didn't need it. */ + tex_flush_node(right); + /*tex No need to reset |right|, we're going to leave the loop anyway. */ + } + break; + } else if (right) { + tex_couple_nodes(cur, right); + tex_couple_nodes(right, fwd); + right = null; + continue; + } else { + break; + } + } else { + /*tex The last character of a paragraph. */ + if (right) { + /*tex |par| prohibits the use of |couple_nodes| here. */ + tex_try_couple_nodes(cur, right); + right = null; + continue; + } else { + break; + } + } + /*tex A discretionary followed by \unknown */ + } else if (t == disc_node) { + /*tex If |{?}{x}{?}| or |{?}{?}{y}| then: */ + if (disc_no_break_head(cur) || disc_post_break_head(cur)) { + halfword fwd; + if (disc_post_break_head(cur)) { + tex_aux_handle_ligature_nesting(disc_post_break(cur), disc_post_break_head(cur)); + } + if (disc_no_break_head(cur)) { + tex_aux_handle_ligature_nesting(disc_no_break(cur), disc_no_break_head(cur)); + } + fwd = node_next(cur); + while (fwd) { + if (node_type(fwd) == glyph_node) { + halfword nob = disc_no_break_tail(cur); + halfword pst = disc_post_break_tail(cur); + if ((! nob || ! tex_aux_found_ligature(nob, fwd)) && (! pst || ! tex_aux_found_ligature(pst, fwd))) { + break; + } else { + halfword next = node_next(fwd); + tex_aux_nesting_append(disc_no_break(cur), tex_copy_node(fwd)); + tex_aux_handle_ligature_nesting(disc_no_break(cur), nob); + tex_uncouple_node(fwd); + tex_try_couple_nodes(cur, next); + tex_aux_nesting_append(disc_post_break(cur), fwd); + tex_aux_handle_ligature_nesting(disc_post_break(cur), pst); + fwd = node_next(cur); + } + } else { + break; + } + } + if (fwd && node_type(fwd) == disc_node) { + /*tex This only deals with simple pre-only discretionaries and a following glyph. */ + halfword next = node_next(fwd); + if (next + && ! disc_no_break_head(fwd) + && ! disc_post_break_head(fwd) + && node_type(next) == glyph_node + && ((disc_post_break_tail(cur) && tex_aux_found_ligature(disc_post_break_tail(cur), next)) || + (disc_no_break_tail (cur) && tex_aux_found_ligature(disc_no_break_tail (cur), next)))) { + halfword last = node_next(next); + tex_uncouple_node(next); + tex_try_couple_nodes(fwd, last); + /*tex Just a hidden flag, used for (base mode) experiments. */ + if (hyphenation_permitted(hyphenation_mode_par, lazy_ligatures_hyphenation_mode)) { + /*tex f-f-i -> f-fi */ + halfword tail = disc_no_break_tail(cur); + tex_aux_nesting_append(disc_no_break(cur), tex_copy_node(next)); + tex_aux_handle_ligature_nesting(disc_no_break(cur), tail); + tail = disc_post_break_tail(cur); + tex_aux_nesting_append(disc_post_break(cur), next); + tex_aux_handle_ligature_nesting(disc_post_break(cur), tail); + tex_try_couple_nodes(node_prev(fwd), node_next(fwd)); + tex_flush_node(fwd); + } else { + /*tex f-f-i -> ff-i : |{a-}{b}{AB} {-}{c}{}| => |{AB-}{c}{ABc}| */ + tex_aux_nesting_append(disc_post_break(fwd), tex_copy_node(next)); + if (disc_no_break_head(cur)) { + halfword tail; + tex_aux_nesting_prepend_list(disc_no_break(fwd), tex_copy_node_list(disc_no_break_head(cur), null)); + tail = disc_no_break_tail(fwd); + tex_aux_nesting_append(disc_no_break(fwd), next); + tex_aux_handle_ligature_nesting(disc_no_break(fwd), tail); + tex_aux_nesting_prepend_list(disc_pre_break(fwd), tex_copy_node_list(disc_no_break_head(cur), null)); + } + tex_try_couple_nodes(node_prev(cur), node_next(cur)); + tex_flush_node(cur); + cur = fwd; + } + } + } + } + } else { + /*tex We have glyph nor disc. */ + return cur; + } + /*tex Goto the next node, where |\par| allows |node_next(cur)| to be NULL. */ + cur = node_next(cur); + } + return cur; +} + + +/*tex The return value is the new tail, head should be a dummy: */ + +halfword tex_handle_ligaturing(halfword head, halfword tail) +{ + if (node_next(head)) { + /*tex A trick to allow explicit |node == null| tests. */ + halfword save_tail = null; + halfword cur, prev; + if (tail) { + save_tail = node_next(tail); + node_next(tail) = null; + } + prev = head; + cur = node_next(prev); + while (cur) { + if (node_type(cur) == glyph_node || node_type(cur) == boundary_node) { + cur = tex_aux_handle_ligature_word(cur); + } + prev = cur; + cur = node_next(cur); + } + if (! prev) { + prev = tail; + } + tex_try_couple_nodes(prev, save_tail); + // if (tail) { + // } + return prev; + } else { + return tail; + } +} + +/*tex Kerning starts here: */ + +static void tex_aux_add_kern_before(halfword left, halfword right) +{ + if ( + glyph_font(left) == glyph_font(right) && + glyph_scale(left) == glyph_scale(right) && + glyph_x_scale(left) == glyph_x_scale(right) && + glyph_y_scale(left) == glyph_y_scale(right) && + ! tex_has_glyph_option(left, glyph_option_no_right_kern) && + ! tex_has_glyph_option(right, glyph_option_no_left_kern) && + tex_has_kern(glyph_font(left), glyph_character(left)) + ) { + scaled k = tex_raw_get_kern(glyph_font(left), glyph_character(left), glyph_character(right)); + if (k) { + scaled kern = tex_new_kern_node(k, font_kern_subtype); + halfword prev = node_prev(right); + tex_couple_nodes(prev, kern); + tex_couple_nodes(kern, right); + tex_attach_attribute_list_copy(kern, left); + } + } +} + +static void tex_aux_add_kern_after(halfword left, halfword right, halfword aft) +{ + if ( + glyph_font(left) == glyph_font(right) && + glyph_scale(left) == glyph_scale(right) && + glyph_x_scale(left) == glyph_x_scale(right) && + glyph_y_scale(left) == glyph_y_scale(right) && + ! tex_has_glyph_option(left, glyph_option_no_right_kern) && + ! tex_has_glyph_option(right, glyph_option_no_left_kern) && + tex_has_kern(glyph_font(left), glyph_character(left)) + ) { + scaled k = tex_raw_get_kern(glyph_font(left), glyph_character(left), glyph_character(right)); + if (k) { + scaled kern = tex_new_kern_node(k, font_kern_subtype); + halfword next = node_next(aft); + tex_couple_nodes(aft, kern); + tex_try_couple_nodes(kern, next); + tex_attach_attribute_list_copy(kern, aft); + } + } +} + +static void tex_aux_do_handle_kerning(halfword root, halfword init_left, halfword init_right) +{ + halfword cur = node_next(root); + if (cur) { + halfword left = null; + if (node_type(cur) == glyph_node) { + if (init_left) { + tex_aux_add_kern_before(init_left, cur); + } + left = cur; + } + cur = node_next(cur); + while (cur) { + halfword t = node_type(cur); + if (t == glyph_node) { + if (left) { + tex_aux_add_kern_before(left, cur); + if (glyph_character(left) < 0) { + halfword prev = node_prev(left); + tex_couple_nodes(prev, cur); + tex_flush_node(left); + } + } + left = cur; + } else { + if (t == disc_node) { + halfword right = node_type(node_next(cur)) == glyph_node ? node_next(cur) : null; + tex_aux_do_handle_kerning(disc_pre_break(cur), left, null); + if (disc_pre_break_head(cur)) { + disc_pre_break_tail(cur) = tex_tail_of_node_list(disc_pre_break_head(cur)); + } + tex_aux_do_handle_kerning(disc_post_break(cur), null, right); + if (disc_post_break_head(cur)) { + disc_post_break_tail(cur) = tex_tail_of_node_list(disc_post_break_head(cur)); + } + tex_aux_do_handle_kerning(disc_no_break(cur), left, right); + if (disc_no_break_head(cur)) { + disc_no_break_tail(cur) = tex_tail_of_node_list(disc_no_break_head(cur)); + } + } + if (left) { + if (glyph_character(left) < 0) { + halfword prev = node_prev(left); + tex_couple_nodes(prev, cur); + tex_flush_node(left); + } + left = null; + } + } + cur = node_next(cur); + } + if (left) { + if (init_right) { + tex_aux_add_kern_after(left, init_right, left); + } + if (glyph_character(left) < 0) { + halfword prev = node_prev(left); + halfword next = node_next(left); + if (next) { + tex_couple_nodes(prev, next); + node_tail(root) = next; + } else if (prev != root) { + node_next(prev) = null; + node_tail(root) = prev; + } else { + node_next(root) = null; + node_tail(root) = null; + } + tex_flush_node(left); + } + } + } else if (init_left && init_right ) { + tex_aux_add_kern_after(init_left, init_right, root); + node_tail(root) = node_next(root); + } +} + +halfword tex_handle_kerning(halfword head, halfword tail) +{ + halfword save_link = null; + if (tail) { + save_link = node_next(tail); + node_next(tail) = null; + node_tail(head) = tail; + tex_aux_do_handle_kerning(head, null, null); + tail = node_tail(head); + if (tex_valid_node(save_link)) { + /* no need for check */ + tex_try_couple_nodes(tail, save_link); + } + } else { + node_tail(head) = null; + tex_aux_do_handle_kerning(head, null, null); + } + return tail; +} + +/*tex The ligaturing and kerning \LUA\ interface: */ + +static halfword tex_aux_run_lua_ligkern_callback(lua_State *L, halfword head, halfword group, halfword direction, int callback_id) +{ + int top = 0; + if (lmt_callback_okay(L, callback_id, &top)) { + int i; + lmt_node_list_to_lua(L, head); + lmt_push_group_code(L, group); + lua_pushinteger(L, direction); + i = lmt_callback_call(L, 3, 1, top); + if (i) { + lmt_callback_error(L, top, i); + } else { + head = lmt_node_list_from_lua(L, -1); + lmt_callback_wrapup(L, top); + } + } + return head; +} + +halfword tex_handle_glyphrun(halfword head, halfword group, halfword direction) +{ + if (head) { + int callback_id = lmt_callback_defined(glyph_run_callback); + if (callback_id) { + return tex_aux_run_lua_ligkern_callback(lmt_lua_state.lua_instance, head, group, direction, callback_id); + } else { + callback_id = lmt_callback_defined(ligaturing_callback); + if (callback_id) { + head = tex_aux_run_lua_ligkern_callback(lmt_lua_state.lua_instance, head, group, direction, callback_id); + } else { + tex_handle_ligaturing(head, null); + } + callback_id = lmt_callback_defined(kerning_callback); + if (callback_id) { + head = tex_aux_run_lua_ligkern_callback(lmt_lua_state.lua_instance, head, group, direction, callback_id); + } else { + halfword nest = tex_new_node(nesting_node, unset_nesting_code); + tex_couple_nodes(nest, head); + tex_aux_do_handle_kerning(nest, null, null); + head = node_next(nest); + node_prev(head) = null; + node_next(nest) = null; + tex_flush_node(nest); + } + } + } + return head; +} + +/*tex + + When the user defines |\font\f|, say, \TEX\ assigns an internal number to the user's font |\f|. + Adding this number to |font_id_base| gives the |eqtb| location of a \quote {frozen} control + sequence that will always select the + font. + + The variable |a| in the following code indicates the global nature of the value to be set. It's + used in the |define| macro. Here we're never global. + + There's not much scanner code here because the other scanners are defined where they make most + sense. + +*/ + +void tex_set_cur_font(halfword g, halfword f) +{ + update_tex_font(g, f); +} + +/*tex This prints a scaled real, rounded to five digits. */ + +static char *tex_aux_scaled_to_string(scaled s) +{ + static char result[16]; + int k = 0; + /*tex The amount of allowable inaccuracy: */ + scaled delta; + if (s < 0) { + /*tex Only print the sign, if negative */ + result[k++] = '-'; + s = -s; + } + { + int l = 0; + char digs[8] = { 0 }; + int n = s / unity; + /*tex Process the integer part: */ + do { + digs[l++] = (char) (n % 10); + n = n / 10;; + } while (n > 0); + while (l > 0) { + result[k++] = (char) (digs[--l] + '0'); + } + } + result[k++] = '.'; + s = 10 * (s % unity) + 5; + delta = 10; + do { + if (delta > unity) { + /*tex Round the last digit: */ + s = s + 0100000 - 050000; + } + result[k++] = (char) ('0' + (s / unity)); + s = 10 * (s % unity); + delta = delta * 10; + } while (s > delta); + result[k] = 0; + return (char *) result; +} + +/*tex + + Because we do fonts in \LUA\ we can decide to drop this one and assume a definition using the + token scanner. It also avoids the filename (split) mess. + +*/ + +int tex_tex_def_font(int a) +{ + if (! lmt_fileio_state.job_name) { + /*tex Avoid confusing |texput| with the font name. */ + tex_open_log_file(); + } + tex_get_r_token(); + if (tex_define_permitted(cur_cs, a)) { + /*tex The user's font identifier. */ + halfword u = cur_cs; + /*tex This runs through existing fonts. */ + halfword f; + /*tex Stated 'at' size, or negative of scaled magnification. */ + scaled s = -1000; + char *fn; + /*tex Here |a| detemines if we define global or not. */ + if (is_global(a)) { + update_tex_font_global(u, null_font); + } else { + update_tex_font_local(u, null_font); + } + fn = tex_read_file_name(1, NULL, NULL); + /*tex Scan the font size specification. */ + lmt_fileio_state.name_in_progress = 1; + if (tex_scan_keyword("at")) { + /*tex Put the positive 'at' size into |s|. */ + s = tex_scan_dimen(0, 0, 0, 0, NULL); + if ((s <= 0) || (s >= 01000000000)) { + char msg[256]; + snprintf(msg, 255, + "Improper 'at' size (%spt), replaced by 10pt", + tex_aux_scaled_to_string(s) + ); + tex_handle_error( + normal_error_type, + msg, + "I can only handle fonts at positive sizes that are less than 2048pt, so I've\n" + "changed what you said to 10pt." ); + s = 10 * unity; + } + } else if (tex_scan_keyword("scaled")) { + s = tex_scan_int(0, NULL); + if ((s <= 0) || (s > 32768)) { + char msg[256]; + snprintf(msg, 255, + "Illegal magnification has been changed to 1000 (%d)", + (int) s + ); + tex_handle_error( + normal_error_type, + msg, + "The magnification ratio must be between 1 and 32768." + ); + s = -1000; + } else { + s = -s; + } + } + lmt_fileio_state.name_in_progress = 0; + f = tex_read_font_info(fn, s); + eq_value(u) = f; + lmt_memory_free(fn); + return 1; + } else { + return 0; + } +} + +/*tex + + When \TEX\ wants to typeset a character that doesn't exist, the character node is not created; + thus the output routine can assume that characters exist when it sees them. The following + procedure prints a warning message unless the user has suppressed it. + +*/ + +void tex_char_warning(halfword f, int c) +{ + if (tracing_lost_chars_par > 0) { + /*tex saved value of |tracing_online| */ + int old_setting = tracing_online_par; + /*tex index to current digit; we assume that $0\L n<16^{22}$ */ + if (tracing_lost_chars_par > 1) { + tracing_online_par = 1; + } + tex_begin_diagnostic(); + tex_print_format("[font: missing character, character %c (%U), font '%s']", c, c, font_name(f)); + tex_end_diagnostic(); + tracing_online_par = old_setting; + } +} + +/* Getters. */ + +scaled tex_char_width_from_font(halfword f, halfword c) +{ + return tex_aux_char_info(f, c)->width; +} + +scaled tex_char_height_from_font(halfword f, halfword c) +{ + return tex_aux_char_info(f, c)->height; +} + +scaled tex_char_depth_from_font(halfword f, halfword c) +{ + return tex_aux_char_info(f, c)->depth; +} + +scaled tex_char_total_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->height + ci->depth; +} + +scaled tex_char_italic_from_font(halfword f, halfword c) +{ + return tex_aux_char_info(f, c)->italic; +} + + +// scaled tex_char_options_from_font(halfword f, halfword c) +// { +// charinfo *ci = tex_aux_char_info(f, c); +// return ci->math ? ci->math->options : 0; +// } +// +// int tex_char_has_option_from_font(halfword f, halfword c, int option) +// { +// charinfo *ci = tex_aux_char_info(f, c); +// return ci->math ? math_font_option(ci->math->options, option) : 0; +// } + +scaledwhd tex_char_whd_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return (scaledwhd) { + .wd = ci->width, + .ht = ci->height, + .dp = ci->depth, + .ic = ci->italic + }; +} + +scaled tex_char_ef_from_font(halfword f, halfword c) +{ + return tex_aux_char_info(f, c)->expansion; +} + +scaled tex_char_lp_from_font(halfword f, halfword c) +{ + return tex_aux_char_info(f, c)->leftprotrusion; +} + +scaled tex_char_rp_from_font(halfword f, halfword c) +{ + return tex_aux_char_info(f, c)->rightprotrusion; +} + +halfword tex_char_has_tag_from_font(halfword f, halfword c, halfword tag) +{ + return (charinfo_tag(tex_aux_char_info(f, c)->tagrem) & tag) == tag; +} + +void tex_char_reset_tag_from_font(halfword f, halfword c, halfword tag) +{ + charinfo *ci = tex_aux_char_info(f, c); + // tag = charinfo_tag(ci->tagrem) & ~(tag | charinfo_tag(ci->tagrem)); + tag = charinfo_tag(ci->tagrem) & ~(tag); + ci->tagrem = charinfo_tagrem(tag,charinfo_rem(ci->tagrem)); + +} + +halfword tex_char_tag_from_font(halfword f, halfword c) +{ + return charinfo_tag(tex_aux_char_info(f, c)->tagrem); +} + +halfword tex_char_remainder_from_font(halfword f, halfword c) +{ + return charinfo_rem(tex_aux_char_info(f, c)->tagrem); +} + +halfword tex_char_vertical_italic_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->math ? ci->math->vertical_italic : INT_MIN; +} + +halfword tex_char_top_accent_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->math ? ci->math->top_accent : INT_MIN; +} + +halfword tex_char_top_anchor_from_font(halfword f, halfword c) +{ + scaled n = tex_char_top_accent_from_font(f, c); + return n == INT_MIN ? 0 : n; +} + +halfword tex_char_bot_accent_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->math ? ci->math->bottom_accent : INT_MIN; +} + +halfword tex_char_bot_anchor_from_font(halfword f, halfword c) +{ + scaled n = tex_char_bot_accent_from_font(f, c); + return n == INT_MIN ? 0 : n; +} + +halfword tex_char_flat_accent_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->math ? ci->math->flat_accent : INT_MIN; +} + +scaled tex_char_top_left_kern_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->math ? ci->math->top_left_kern : 0; +} + +scaled tex_char_top_right_kern_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->math ? ci->math->top_right_kern : 0; +} + +scaled tex_char_bottom_left_kern_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->math ? ci->math->bottom_left_kern : 0; +} + +scaled tex_char_bottom_right_kern_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->math ? ci->math->bottom_right_kern : 0; +} + +extinfo *tex_char_vertical_parts_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->math ? ci->math->vertical_parts : NULL; +} + +extinfo *tex_char_horizontal_parts_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->math ? ci->math->horizontal_parts : NULL; +} + +scaled tex_char_left_margin_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->math ? ci->math->left_margin : 0; +} + +scaled tex_char_right_margin_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->math ? ci->math->right_margin : 0; +} + +scaled tex_char_top_margin_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->math ? ci->math->top_margin : 0; +} + +scaled tex_char_bottom_margin_from_font(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci->math ? ci->math->bottom_margin : 0; +} + +/* Nodes */ + +scaled tex_char_width_from_glyph(halfword g) +{ + charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g)); + return tex_aux_glyph_x_scaled(g, ci->width); +} + +scaled tex_char_height_from_glyph(halfword g) +{ + charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g)); + return tex_aux_glyph_y_scaled(g, ci->height); +} + +scaled tex_char_depth_from_glyph(halfword g) +{ + charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g)); + return tex_aux_glyph_y_scaled(g, ci->depth); +} + +scaled tex_char_total_from_glyph(halfword g) +{ + charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g)); + return tex_aux_glyph_y_scaled(g, ci->height + ci->depth); +} + +scaled tex_char_italic_from_glyph(halfword g) +{ + charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g)); + return tex_aux_glyph_x_scaled(g, ci->italic); +} + +// halfword tex_char_options_from_glyph(halfword g) +// { +// charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g)); +// return ci->math ? ci->math->options : 0; +// } + +// int tex_char_has_option_from_glyph(halfword g, int t) +// { +// if (node_type(g) == glyph_node) { +// charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g)); +// return ci->math ? math_font_option(ci->math->options, t) : 0; +// } else { +// return 0; +// } +// } + +scaledwhd tex_char_whd_from_glyph(halfword g) +{ + charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g)); + return (scaledwhd) { + .wd = tex_aux_glyph_x_scaled(g, ci->width), + .ht = tex_aux_glyph_y_scaled(g, ci->height), + .dp = tex_aux_glyph_y_scaled(g, ci->depth), + .ic = tex_aux_glyph_x_scaled(g, ci->italic) + }; +} + +scaled tex_char_width_italic_from_glyph(halfword g) +{ + charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g)); + return tex_aux_glyph_x_scaled(g, ci->width + ci->italic); +} + +/* More */ + +scaled tex_calculated_char_width(halfword f, halfword c, halfword ex) +{ + scaled wd = tex_aux_char_info(f, c)->width; + return ex ? tex_round_xn_over_d(wd, 1000 + ex, 1000) : wd; +} + +scaled tex_calculated_glyph_width(halfword g, halfword ex) +{ + charinfo *ci = tex_aux_char_info(glyph_font(g), glyph_character(g)); + scaled wd = tex_aux_glyph_x_scaled(g, ci->width); + return ex ? tex_round_xn_over_d(wd, 1000 + ex, 1000) : wd; +} + +/* Checkers: */ + +int tex_has_ligature(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci ? ci->ligatures != NULL : 0; +} + +int tex_has_kern(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci ? ci->kerns != NULL : 0; +} + +int tex_char_has_math(halfword f, halfword c) +{ + charinfo *ci = tex_aux_char_info(f, c); + return ci ? ci->math != NULL : 0; +} + +/* Setters: */ + +void tex_set_lpcode_in_font(halfword f, halfword c, halfword i) +{ + charinfo *ci = tex_aux_char_info(f, c); + if (ci) { + ci->leftprotrusion = i; + } +} + +void tex_set_rpcode_in_font(halfword f, halfword c, halfword i) +{ + charinfo *ci = tex_aux_char_info(f, c); + if (ci) { + ci->rightprotrusion = i; + } +} + +void tex_set_efcode_in_font(halfword f, halfword c, halfword i) { + charinfo *ci = tex_aux_char_info(f, c); + if (ci) { + ci->expansion = i; + } +} + +void tex_set_font_name(halfword f, const char *s) +{ + if (font_name(f)) { + lmt_memory_free(font_name(f)); + } + set_font_name(f, s ? lmt_memory_strdup(s) : NULL); +} + +void tex_set_font_original(halfword f, const char *s) +{ + if (font_original(f)) { + lmt_memory_free(font_original(f)); + } + set_font_original(f, s ? lmt_memory_strdup(s) : NULL); +} + +scaled tex_get_math_font_scale(halfword f, halfword size) +{ + scaled scale = 1000; + switch (size) { + case 2: scale = lmt_font_state.fonts[f]->mathscales[2] ? lmt_font_state.fonts[f]->mathscales[2] : glyph_scriptscript_scale_par; break; + case 1: scale = lmt_font_state.fonts[f]->mathscales[1] ? lmt_font_state.fonts[f]->mathscales[1] : glyph_script_scale_par; break; + case 0: scale = lmt_font_state.fonts[f]->mathscales[0] ? lmt_font_state.fonts[f]->mathscales[0] : glyph_text_scale_par; break; + } + return scale ? scale : 1000; +} + +/*tex + Experiment. +*/ + +void tex_run_font_spec(void) +{ + update_tex_font_identifier(font_spec_identifier(cur_chr)); + if (font_spec_scale(cur_chr) != unused_scale_value) { + update_tex_glyph_scale(font_spec_scale(cur_chr)); + } + if (font_spec_x_scale(cur_chr) != unused_scale_value) { + update_tex_glyph_x_scale(font_spec_x_scale(cur_chr)); + } + if (font_spec_y_scale(cur_chr) != unused_scale_value) { + update_tex_glyph_y_scale(font_spec_y_scale(cur_chr)); + } +} + |