texlive[55737] Build/source/texk/web2c/xetexdir: fix bugs in @<Attach

commits+kakuto at tug.org commits+kakuto at tug.org
Fri Jul 3 02:56:12 CEST 2020


Revision: 55737
          http://tug.org/svn/texlive?view=revision&revision=55737
Author:   kakuto
Date:     2020-07-03 02:56:12 +0200 (Fri, 03 Jul 2020)
Log Message:
-----------
fix bugs in @<Attach subscript OpenType math kerning@>, etc. (David Jones)

Modified Paths:
--------------
    trunk/Build/source/texk/web2c/xetexdir/ChangeLog
    trunk/Build/source/texk/web2c/xetexdir/XeTeXOTMath.cpp
    trunk/Build/source/texk/web2c/xetexdir/xetex.web

Modified: trunk/Build/source/texk/web2c/xetexdir/ChangeLog
===================================================================
--- trunk/Build/source/texk/web2c/xetexdir/ChangeLog	2020-07-02 23:53:10 UTC (rev 55736)
+++ trunk/Build/source/texk/web2c/xetexdir/ChangeLog	2020-07-03 00:56:12 UTC (rev 55737)
@@ -1,3 +1,10 @@
+2020-07-03  David Jones  <dmj at 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,

Modified: trunk/Build/source/texk/web2c/xetexdir/XeTeXOTMath.cpp
===================================================================
--- trunk/Build/source/texk/web2c/xetexdir/XeTeXOTMath.cpp	2020-07-02 23:53:10 UTC (rev 55736)
+++ trunk/Build/source/texk/web2c/xetexdir/XeTeXOTMath.cpp	2020-07-03 00:56:12 UTC (rev 55737)
@@ -280,7 +280,7 @@
 }
 
 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 @@
 #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));
 
-        return D2Fix(font->unitsToPoints(rval));
+    // 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.
+
+      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

Modified: trunk/Build/source/texk/web2c/xetexdir/xetex.web
===================================================================
--- trunk/Build/source/texk/web2c/xetexdir/xetex.web	2020-07-02 23:53:10 UTC (rev 55736)
+++ trunk/Build/source/texk/web2c/xetexdir/xetex.web	2020-07-03 00:56:12 UTC (rev 55737)
@@ -18316,8 +18316,16 @@
 @!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 @@
 
 @<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 @@
 
 @<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 @@
 @<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 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 @@
 @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);
+ at d is_valid_pointer(#) == ((# >= mem_min) and (# <= mem_end))
 
-if sub_kern<>0 then p:=attach_hkern_to_new_hlist(q, sub_kern);
+@ @<Fetch first character of a sub/superscript@> =
+    script_c := null;
+
+    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);
+
+            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 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);
+@ @<Attach superscript OpenType math kerning@> =
+begin
+    { if there is a superscript the kern will be added to |shift_amount(x)| }
 
-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);
+    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|.



More information about the tex-live-commits mailing list.