diff options
-rw-r--r-- | Build/source/texk/web2c/xetexdir/ChangeLog | 7 | ||||
-rw-r--r-- | Build/source/texk/web2c/xetexdir/XeTeXOTMath.cpp | 153 | ||||
-rw-r--r-- | Build/source/texk/web2c/xetexdir/xetex.web | 193 |
3 files changed, 285 insertions, 68 deletions
diff --git a/Build/source/texk/web2c/xetexdir/ChangeLog b/Build/source/texk/web2c/xetexdir/ChangeLog index 45eb633a952..bab5c229d83 100644 --- a/Build/source/texk/web2c/xetexdir/ChangeLog +++ b/Build/source/texk/web2c/xetexdir/ChangeLog @@ -1,3 +1,10 @@ +2020-07-03 David Jones <dmj@ams.org> + + * XeTeXOTMath.cpp: New implementation of get_ot_math_kern(). + * xetex.web: Fix bugs in + @<Attach subscript OpenType math kerning@> and + @<Attach superscript OpenType math kerning@> + 2020-05-04 Andreas Scherer <https://ascherer.github.io> * xetex.ch, diff --git a/Build/source/texk/web2c/xetexdir/XeTeXOTMath.cpp b/Build/source/texk/web2c/xetexdir/XeTeXOTMath.cpp index d1a4ab432be..667f3c62558 100644 --- a/Build/source/texk/web2c/xetexdir/XeTeXOTMath.cpp +++ b/Build/source/texk/web2c/xetexdir/XeTeXOTMath.cpp @@ -280,7 +280,7 @@ ot_min_connector_overlap(int f) } static int -getMathKernAt(int f, int g, hb_ot_math_kern_t side, int height) +getMathKernAt(int f, int g, int height, hb_ot_math_kern_t side) { hb_position_t rval = 0; @@ -324,49 +324,138 @@ glyph_depth(int f, int g) #define sub_cmd 1 int -get_ot_math_kern(int f, int g, int sf, int sg, int cmd, int shift) +get_ot_math_kern(int f, int g, // Base font and glyph number + int sf, int sg, // Sub/superscript font and glyph number + int cmd, // sup_cmd or sub_cmd + int shift_scaled // TeX scaled points + ) { - int rval = 0; + int rval = 0; - if (fontarea[f] == OTGR_FONT_FLAG) { - XeTeXFontInst* font = (XeTeXFontInst*)getFont((XeTeXLayoutEngine)fontlayoutengine[f]); - int kern = 0, skern = 0; - float corr_height_top = 0.0, corr_height_bot = 0.0; + // Cf. https://docs.microsoft.com/en-us/typography/opentype/spec/math#mathkerninfo-table - if (cmd == sup_cmd) { // superscript - corr_height_top = font->pointsToUnits(glyph_height(f, g)); - corr_height_bot = -font->pointsToUnits(glyph_depth(sf, sg) + Fix2D(shift)); + if (fontarea[f] == OTGR_FONT_FLAG && fontarea[sf] == OTGR_FONT_FLAG) { + XeTeXFontInst* font = (XeTeXFontInst*) getFont((XeTeXLayoutEngine)fontlayoutengine[f]); - kern = getMathKernAt(f, g, HB_OT_MATH_KERN_TOP_RIGHT, corr_height_top); - skern = getMathKernAt(sf, sg, HB_OT_MATH_KERN_BOTTOM_LEFT, corr_height_top); - rval = kern + skern; + XeTeXFontInst* sfont = (XeTeXFontInst*) getFont((XeTeXLayoutEngine)fontlayoutengine[sf]); - kern = getMathKernAt(f, g, HB_OT_MATH_KERN_TOP_RIGHT, corr_height_bot); - skern = getMathKernAt(sf, sg, HB_OT_MATH_KERN_BOTTOM_LEFT, corr_height_bot); - if ((kern + skern) < rval) - rval = kern + skern; + // Do calculations in glyph units. - } else if (cmd == sub_cmd) { // subscript - corr_height_top = font->pointsToUnits(glyph_height(sf, sg) - Fix2D(shift)); - corr_height_bot = -font->pointsToUnits(glyph_depth(f, g)); + // The next four lines could be streamlined by having + // glyph_height() and glyph_depth() return the metrics in glyph + // units, but that would require tweaking getGlyphHeightDepth(), + // which is used elsewhere. - kern = getMathKernAt(f, g, HB_OT_MATH_KERN_BOTTOM_RIGHT, corr_height_top); - skern = getMathKernAt(sf, sg, HB_OT_MATH_KERN_TOP_LEFT, corr_height_top); - rval = kern + skern; + int g_height = font->pointsToUnits(glyph_height(f, g)); + int g_depth = font->pointsToUnits(glyph_depth(f, g)); - kern = getMathKernAt(f, g, HB_OT_MATH_KERN_BOTTOM_RIGHT, corr_height_bot); - skern = getMathKernAt(sf, sg, HB_OT_MATH_KERN_TOP_LEFT, corr_height_bot); - if ((kern + skern) < rval) - rval = kern + skern; + int sg_height = sfont->pointsToUnits(glyph_height(sf, sg)); + int sg_depth = sfont->pointsToUnits(glyph_depth(sf, sg)); - } else { - assert(0); // we should not reach here - } + // Convert the shift amount to base glyph units. + int shift = font->pointsToUnits(Fix2D(shift_scaled)); + + // Multiply sub/superscript glyph units by scale_factor to make + // them commensurate with base glyph units. + + float f_size = font->getPointSize(); + float sf_size = sfont->getPointSize(); + + assert(f_size != 0.0); + + float scale_factor = sf_size / f_size; + + if (cmd == sup_cmd) { // superscript + // (1) Calculate the kern at the bottom of the superscript-glyph + // bounding box. + + // In base glyph units, the bottom of the superscript is at a + // distance of (shift - scale_factor * sg_depth) above the + // baseline. + + int kern = getMathKernAt(f, g, shift - scale_factor * sg_depth, + HB_OT_MATH_KERN_TOP_RIGHT); + + // In superscript glyph units, the bottom of the superscript is + // a distance of sg_depth below the baseline. + + int skern = getMathKernAt(sf, sg, -sg_depth, + HB_OT_MATH_KERN_BOTTOM_LEFT); + + int top_kern = kern + scale_factor * skern; + + // (2) Calculate the kern at the top of the base-glyph bounding + // box. + + // In base glyph units, the top of the base glyph is a distance + // of g_height above the baseline. + + kern = getMathKernAt(f, g, g_height, HB_OT_MATH_KERN_TOP_RIGHT); + + // In superscript glyph units, the top of the base glyph is a + // distance of (g_height - shift)/scale_factor above the + // baseline. - return D2Fix(font->unitsToPoints(rval)); + skern = getMathKernAt(sf, sg, (g_height - shift) / scale_factor, + HB_OT_MATH_KERN_BOTTOM_LEFT); + + int bot_kern = kern + scale_factor * skern; + + // (3) The spec says "Take the minimum of these two sums," but + // surely we want the kern that results in the greater + // separation between the base and the superscript? That + // corresponds to the maximum. (In the case where both kerns + // are negative, this is the same as the kern with the minimum + // *absolute* value, which is presumably what the spec means.) + + rval = (top_kern > bot_kern) ? top_kern : bot_kern; + } else if (cmd == sub_cmd) { // subscript + // (1) Calculate the kern at the top of the subscript-glyph + // bounding box. + + // In base glyph units,, the top of the subscript is at a height + // of scale_factor * sg_height - shift. + + int kern = getMathKernAt(f, g, scale_factor * sg_height - shift, + HB_OT_MATH_KERN_BOTTOM_RIGHT); + + // In subscript glyph units, the top of the subscript-glyph + // bounding box is sg_height above the baseline. + + int skern = getMathKernAt(sf, sg, sg_height, + HB_OT_MATH_KERN_TOP_LEFT); + + int top_kern = kern + scale_factor * skern; + + // (2) Calculate the kern at the bottom of the base-glyph + // bounding box. + + // In base glyph units, the bottom of the base-glyph is at + // g_depth below the baseline: + + kern = getMathKernAt(f, g, -g_depth, + HB_OT_MATH_KERN_BOTTOM_RIGHT); + + // In subscript glyph units, the bottom of the base glyph is at + // a height of shift - g_depth above the baseline, translated + // into subscript glyph units. + + skern = getMathKernAt(sf, sg, (shift - g_depth) / scale_factor, + HB_OT_MATH_KERN_TOP_LEFT); + + int bot_kern = kern + scale_factor * skern; + + // (3) See above. + + rval = (top_kern > bot_kern) ? top_kern : bot_kern; + } else { + assert(0); // we should not reach here } - return 0; + rval = D2Fix(font->unitsToPoints(rval)); + } + + return rval; } int diff --git a/Build/source/texk/web2c/xetexdir/xetex.web b/Build/source/texk/web2c/xetexdir/xetex.web index 6830634f036..56f22364f9e 100644 --- a/Build/source/texk/web2c/xetexdir/xetex.web +++ b/Build/source/texk/web2c/xetexdir/xetex.web @@ -18316,8 +18316,16 @@ var p,@!x,@!y,@!z:pointer; {temporary registers for box construction} @!script_c:pointer; {temprary native character for sub/superscript} @!script_g:quarterword; {temporary register for sub/superscript native glyph id} @!script_f:internal_font_number; {temporary register for sub/superscript font} + @!sup_g: quarterword; { superscript native glyph id} + @!sup_f: internal_font_number; { superscript font } + @!sub_g: quarterword; { subscript native glyph id } + @!sub_f: internal_font_number; { subscript font } @!t:integer; {subsidiary size code} @!save_f:internal_font_number; + @!script_head: pointer; { scratch var for OpenType s*scripts } + @!script_ptr: pointer; { scratch var for OpenType s*scripts } + @!saved_math_style: small_number; { scratch var for OpenType s*scripts } + @!this_math_style: small_number; { scratch var for OpenType s*scripts } begin p:=new_hlist(q); script_c:=null; script_g:=0; script_f:=0; sup_kern:=0; sub_kern:=0; if is_char_node(p) or is_glyph_node(p) then @@ -18348,6 +18356,13 @@ should not exceed the baseline plus four-fifths of the x-height. @<Construct a subscript box |x| when there is no superscript@>= begin + script_head := subscr(q); + + @<Fetch first character of a sub/superscript@>; + + sub_g := script_g; + sub_f := script_f; + save_f:=cur_f; x:=clean_box(subscr(q),sub_style(cur_style)); cur_f:=save_f; @@ -18367,6 +18382,14 @@ one-fourth of the x-height. @<Construct a superscript box |x|@>= begin + + script_head := supscr(q); + + @<Fetch first character of a sub/superscript@>; + + sup_g := script_g; + sup_f := script_f; + save_f:=cur_f; x:=clean_box(supscr(q),sup_style(cur_style)); cur_f:=save_f; @@ -18392,6 +18415,14 @@ is at least as high as the baseline plus four-fifths of the x-height. @<Construct a sub/superscript combination box |x|...@>= begin save_f:=cur_f; + + script_head := subscr(q); + + @<Fetch first character of a sub/superscript@>; + + sub_g := script_g; + sub_f := script_f; + y:=clean_box(subscr(q),sub_style(cur_style)); cur_f:=save_f; width(y):=width(y)+script_space; @@ -18414,7 +18445,12 @@ if clr>0 then if is_new_mathfont(cur_f) then begin @<Attach subscript OpenType math kerning@>@/ @<Attach superscript OpenType math kerning@> - end; + end + else + begin + sup_kern := 0; + sub_kern := 0; + end; shift_amount(x):=sup_kern+delta-sub_kern; {superscript is |delta| to the right of the subscript} p:=new_kern((shift_up-depth(x))-(height(y)-shift_down)); link(x):=p; link(p):=y; x:=vpack(x,natural); shift_amount(x):=shift_down; @@ -18428,44 +18464,129 @@ The following definitions should be kept in sync with \.{XeTeXOTMath.cpp}. @d sup_cmd=0 {superscript kern type for |get_ot_math_kern|} @d sub_cmd=1 {subscript kern type for |get_ot_math_kern|} -@<Attach subscript OpenType math kerning@>= -begin if math_type(subscr(q))=math_char then - begin save_f:=cur_f; - fetch(subscr(q)); - if is_new_mathfont(cur_f) then - begin script_c:=new_native_character(cur_f, qo(cur_c)); - script_g:=get_native_glyph(script_c, 0); - script_f:=cur_f; - end else - begin script_g:=0; script_f:=0 - end; - cur_f:=save_f; - end; -if is_glyph_node(p) then - sub_kern:=get_ot_math_kern(native_font(p), native_glyph(p), script_f, script_g, sub_cmd, shift_down); +@d is_valid_pointer(#) == ((# >= mem_min) and (# <= mem_end)) -if sub_kern<>0 then p:=attach_hkern_to_new_hlist(q, sub_kern); -end; +@ @<Fetch first character of a sub/superscript@> = + script_c := null; -@ @<Attach superscript OpenType math kerning@>= -begin if math_type(supscr(q))=math_char then - begin save_f:=cur_f; - fetch(supscr(q)); - if is_new_mathfont(cur_f) then - begin script_c:=new_native_character(cur_f, qo(cur_c)); - script_g:=get_native_glyph(script_c, 0); - script_f:=cur_f; - end else - begin script_g:=0; script_f:=0 - end; - cur_f:=save_f; - end; -if is_glyph_node(p) then - sup_kern:=get_ot_math_kern(native_font(p), native_glyph(p), script_f, script_g, sup_cmd, shift_up); + script_g := qi(0); + script_f := null_font; + + this_math_style := sub_style(cur_style); + + { Loop through the sub_mlist looking for the first character-like + thing. Ignore kerns or glue so that, for example, changing + $P_j$ to $P_{\!j}$ will have a predictable effect. Intercept + style_nodes and execute them. If we encounter a choice_node, + follow the appropriate branch. Anything else halts the search + and inhibits OpenType kerning. } + + { Don't try to do anything clever if the nucleus of the + script_head is empty, e.g., $P_{^j}$ and the such. } + + if math_type(script_head) = sub_mlist then + begin + script_ptr := info(script_head); -if (sup_kern<>0) and (math_type(subscr(q))=empty) then - {if there is a superscript the kern will be added to |shift_amount(x)|} - p:=attach_hkern_to_new_hlist(q, sup_kern); + script_head := null; + + while is_valid_pointer(script_ptr) do + begin + case type(script_ptr) of + kern_node, glue_node: do_nothing; + + style_node: begin + this_math_style := subtype(script_ptr); + end; + + choice_node: do_nothing; { see below } + + ord_noad, op_noad, bin_noad, rel_noad, open_noad, + close_noad, punct_noad: begin + script_head := nucleus(script_ptr); + script_ptr := null; + end; + + othercases script_ptr := null { end the search } + endcases; + + if is_valid_pointer(script_ptr) then + if type(script_ptr) = choice_node then + case this_math_style div 2 of + 0: script_ptr := display_mlist(script_ptr); + 1: script_ptr := text_mlist(script_ptr); + 2: script_ptr := script_mlist(script_ptr); + 3: script_ptr := script_script_mlist(script_ptr); + end + else + script_ptr := link(script_ptr); + end; + end; + + if is_valid_pointer(script_head) and math_type(script_head) = math_char then + begin + save_f := cur_f; + + saved_math_style := cur_style; + cur_style := this_math_style; + + @<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>; + + fetch(script_head); + + if is_new_mathfont(cur_f) then + begin + script_c := new_native_character(cur_f, qo(cur_c)); + script_g := get_native_glyph(script_c, 0); + script_f := cur_f; { script font } + end; + + cur_f := save_f; + + cur_style := saved_math_style; + + @<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>; + end; + + { The remaining case is math_type(script_head) = sub_box. + Although it would be possible to deconstruct the box node to + find the first glyph, it will most likely be from a text font + without MATH kerning, so there's probably no point. } + +@ @<Attach subscript OpenType math kerning@> = +begin + if is_glyph_node(p) then + begin + sub_kern := get_ot_math_kern(native_font(p), + native_glyph(p), + sub_f, + sub_g, + sub_cmd, + shift_down); + + if sub_kern <> 0 then + p := attach_hkern_to_new_hlist(q, sub_kern); + end; + +end; + +@ @<Attach superscript OpenType math kerning@> = +begin + { if there is a superscript the kern will be added to |shift_amount(x)| } + + if math_type(subscr(q)) = empty then + if is_glyph_node(p) then + begin + sup_kern := get_ot_math_kern(native_font(p), + native_glyph(p), + sup_f, + sup_g, + sup_cmd, + shift_up); + + if sup_kern <> 0 then + p := attach_hkern_to_new_hlist(q, sup_kern); + end; end; @ We have now tied up all the loose ends of the first pass of |mlist_to_hlist|. |