summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Build/source/texk/web2c/xetexdir/ChangeLog7
-rw-r--r--Build/source/texk/web2c/xetexdir/XeTeXOTMath.cpp153
-rw-r--r--Build/source/texk/web2c/xetexdir/xetex.web193
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|.