From 06912c2a8348e512badbd80c81d53d1afb89a06e Mon Sep 17 00:00:00 2001 From: Ke Shi Date: Thu, 3 Oct 2024 19:51:57 +0800 Subject: [PATCH] [80_4] Improving the Typesetting of Limit Boxes ## What Improving the Typesetting of Limit Boxes. ## Why For some OpenType fonts, the heights of the upper and lower limits in the limit box environment are not suitable. ## How to test your changes? Open `devel/80_4.tmu`. Before (Anasa Math font), the upper limit is too high: ![image](https://github.com/user-attachments/assets/e5ac5e1c-d3dc-422f-82e5-723beeee16e3) After: ![image](https://github.com/user-attachments/assets/03ff3b2e-33d1-41a1-ae68-a891402240fd) --- devel/80_4.tmu | 18 ++++++ src/Graphics/Fonts/font.cpp | 6 ++ src/Graphics/Fonts/font.hpp | 9 ++- src/Plugins/Freetype/tt_face.cpp | 14 +++++ src/Plugins/Freetype/tt_face.hpp | 6 +- src/Plugins/Freetype/tt_tools.hpp | 34 +++++++++- src/Plugins/Freetype/unicode_font.cpp | 65 ++++++++++++++++++++ src/Typeset/Boxes/Composite/script_boxes.cpp | 12 ++++ 8 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 devel/80_4.tmu diff --git a/devel/80_4.tmu b/devel/80_4.tmu new file mode 100644 index 0000000000..1a603c36c0 --- /dev/null +++ b/devel/80_4.tmu @@ -0,0 +1,18 @@ +> + +> + +<\body> + <\equation*> + >|2>>>>lim>lim|2>>>> + + + +<\initial> + <\collection> + + + + + + diff --git a/src/Graphics/Fonts/font.cpp b/src/Graphics/Fonts/font.cpp index 529bc858e3..ac80b98303 100644 --- a/src/Graphics/Fonts/font.cpp +++ b/src/Graphics/Fonts/font.cpp @@ -94,6 +94,12 @@ font_rep::copy_math_pars (font fn) { wfn = fn->wfn; wline = fn->wline; wquad = fn->wquad; + + // opentype math parameters + upper_limit_gap_min = fn->upper_limit_gap_min; + upper_limit_baseline_rise_min= fn->upper_limit_baseline_rise_min; + lower_limit_gap_min = fn->lower_limit_gap_min; + lower_limit_baseline_drop_min= fn->lower_limit_baseline_drop_min; } void diff --git a/src/Graphics/Fonts/font.hpp b/src/Graphics/Fonts/font.hpp index 6d63b17870..21475fb54e 100644 --- a/src/Graphics/Fonts/font.hpp +++ b/src/Graphics/Fonts/font.hpp @@ -34,6 +34,7 @@ struct font_glyphs; #define MATH_TYPE_NORMAL 0 #define MATH_TYPE_STIX 1 #define MATH_TYPE_TEX_GYRE 2 +#define MATH_TYPE_OPENTYPE 3 #define START_OF_LINE 1 #define END_OF_LINE 2 @@ -52,7 +53,7 @@ struct font_glyphs; struct font_rep : rep { int type; // font type - int math_type; // For TeX Gyre math fonts and Stix + int math_type; // For TeX Gyre math fonts and Stix and OpenType fonts SI size; // requested size SI design_size; // design size in points/256 SI display_size; // display size in points/PIXEL @@ -73,6 +74,12 @@ struct font_rep : rep { SI ysup_hi_lim; // upper limit for supscripts SI yshift; // vertical script shift inside fractions + // only for opentype fonts + SI upper_limit_gap_min; + SI upper_limit_baseline_rise_min; + SI lower_limit_gap_min; + SI lower_limit_baseline_drop_min; + SI wpt; // width of one point in font SI hpt; // height of one point in font (usually wpt) SI wfn; // wpt * design size in points diff --git a/src/Plugins/Freetype/tt_face.cpp b/src/Plugins/Freetype/tt_face.cpp index c2d18495f8..1dc27ff12a 100644 --- a/src/Plugins/Freetype/tt_face.cpp +++ b/src/Plugins/Freetype/tt_face.cpp @@ -10,6 +10,8 @@ ******************************************************************************/ #include "tt_face.hpp" +#include "analyze.hpp" +#include "file.hpp" #include "font.hpp" #include "tm_debug.hpp" #include "tm_timer.hpp" @@ -54,6 +56,18 @@ tt_face_rep::tt_face_rep (string name) : rep (name) { } ft_select_charmap (ft_face, ft_encoding_adobe_custom); bad_face= false; + + // .ttf/.otf file may contain a math table + string font_suffix= locase_all (suffix (u)); + if (font_suffix == "ttf" || font_suffix == "otf") { + string buf; + if (!load_string (u, buf, false)) { + math_table= parse_mathtable (buf); + } + if (!is_nil (math_table) && DEBUG_STD) { + debug_fonts << "Math table loaded for " << name << "\n"; + } + } } tt_face diff --git a/src/Plugins/Freetype/tt_face.hpp b/src/Plugins/Freetype/tt_face.hpp index d1bac63627..5316d66022 100644 --- a/src/Plugins/Freetype/tt_face.hpp +++ b/src/Plugins/Freetype/tt_face.hpp @@ -12,14 +12,16 @@ #ifndef TT_FACE_H #define TT_FACE_H #include "Freetype/free_type.hpp" +#include "Freetype/tt_tools.hpp" #include "bitmap_font.hpp" #include "hashmap.hpp" RESOURCE (tt_face); struct tt_face_rep : rep { - bool bad_face; - FT_Face ft_face; + bool bad_face; + FT_Face ft_face; + ot_mathtable math_table; tt_face_rep (string name); }; diff --git a/src/Plugins/Freetype/tt_tools.hpp b/src/Plugins/Freetype/tt_tools.hpp index 03a2d20146..61490e93f3 100644 --- a/src/Plugins/Freetype/tt_tools.hpp +++ b/src/Plugins/Freetype/tt_tools.hpp @@ -12,7 +12,9 @@ #ifndef TT_TOOLS_H #define TT_TOOLS_H +#include "basic.hpp" #include "hashset.hpp" +#include "tm_debug.hpp" #include "tree.hpp" #include "url.hpp" @@ -137,7 +139,12 @@ enum MathConstantRecordEnum { radicalExtraAscender, radicalKernBeforeDegree, radicalKernAfterDegree, - otmathConstantsRecordsEnd // keep at the end + otmathConstantsRecordsEnd, // count the number of records + scriptPercentScaleDown, + scriptScriptPercentScaleDown, + delimitedSubFormulaMinHeight, + displayOperatorMinHeight, + radicalDegreeBottomRaisePercent }; struct DeviceTable { @@ -152,6 +159,9 @@ struct MathValueRecord { bool hasDevice; DeviceTable deviceTable; MathValueRecord () : hasDevice (false) {} + + // cast to int + operator int () const { return value; } }; struct MathConstantsTable { @@ -161,7 +171,27 @@ struct MathConstantsTable { unsigned int displayOperatorMinHeight; array records; int radicalDegreeBottomRaisePercent; - MathConstantsTable () : records (otmathConstantsRecordsEnd){}; + MathConstantsTable () + : records (MathConstantRecordEnum::otmathConstantsRecordsEnd){}; + + int operator[] (int i) { + if (i >= 0 && i < MathConstantRecordEnum::otmathConstantsRecordsEnd) + return records[i]; + switch (i) { + case MathConstantRecordEnum::scriptPercentScaleDown: + return scriptPercentScaleDown; + case MathConstantRecordEnum::scriptScriptPercentScaleDown: + return scriptScriptPercentScaleDown; + case MathConstantRecordEnum::delimitedSubFormulaMinHeight: + return delimitedSubFormulaMinHeight; + case MathConstantRecordEnum::displayOperatorMinHeight: + return displayOperatorMinHeight; + case MathConstantRecordEnum::radicalDegreeBottomRaisePercent: + return radicalDegreeBottomRaisePercent; + } + TM_FAILED ("MathConstantsTable: index out of range"); + return 0; // should never reach here + } }; struct MathKernTable { diff --git a/src/Plugins/Freetype/unicode_font.cpp b/src/Plugins/Freetype/unicode_font.cpp index 30402caa37..e180bfc920 100644 --- a/src/Plugins/Freetype/unicode_font.cpp +++ b/src/Plugins/Freetype/unicode_font.cpp @@ -12,6 +12,7 @@ #include "Freetype/free_type.hpp" #include "Freetype/tt_face.hpp" #include "Freetype/tt_file.hpp" +#include "Freetype/tt_tools.hpp" #include "analyze.hpp" #include "converter.hpp" #include "cork.hpp" @@ -140,6 +141,9 @@ struct unicode_font_rep : font_rep { hashmap native; // additional native (non unicode) characters + tt_face math_face; + ot_mathtable math_table; + unicode_font_rep (string name, string family, int size, int hdpi, int vdpi); void tex_gyre_operators (); @@ -164,6 +168,9 @@ struct unicode_font_rep : font_rep { SI get_rsub_correction (string s); SI get_rsup_correction (string s); SI get_wide_correction (string s, int mode); + + SI design_unit_to_metric (int du); + int metric_to_design_unit (SI m); }; /****************************************************************************** @@ -430,6 +437,23 @@ unicode_font_rep::unicode_font_rep (string name, string family2, int size2, above_correct= above_fira_italic_table (); } } + else { + // try to get OpenType math table + tt_face math_face2= tt_face (family); + if (!is_nil (math_face2->math_table)) { + this->math_face = math_face2; + this->math_table= math_face2->math_table; + math_type = MATH_TYPE_OPENTYPE; + upper_limit_gap_min= + design_unit_to_metric (math_table->constants_table[upperLimitGapMin]); + upper_limit_baseline_rise_min= design_unit_to_metric ( + math_table->constants_table[upperLimitBaselineRiseMin]); + lower_limit_gap_min= + design_unit_to_metric (math_table->constants_table[lowerLimitGapMin]); + lower_limit_baseline_drop_min= design_unit_to_metric ( + math_table->constants_table[lowerLimitBaselineDropMin]); + } + } } /****************************************************************************** @@ -975,6 +999,47 @@ unicode_font (string family, int size, int hdpi, int vdpi) { tm_new (name, family, size, hdpi, vdpi)); } +/****************************************************************************** + * OpenType + ******************************************************************************/ + +inline SI +unicode_font_rep::design_unit_to_metric (int du) { + // use 'm' as a reference character + static int units_of_m= 0; + static SI em = 0; + if (units_of_m == 0) { + // get the design units of the width of 'm' + FT_UInt glyph_index= FT_Get_Char_Index (math_face->ft_face, 'm'); + FT_Load_Glyph (math_face->ft_face, glyph_index, FT_LOAD_NO_SCALE); + units_of_m= math_face->ft_face->glyph->metrics.horiAdvance; + + // get the width of the character 'm' + metric ex; + get_extents ("m", ex); + em= ex->x2 - ex->x1; + } + return (SI) ((du * em) / units_of_m); +} + +inline int +unicode_font_rep::metric_to_design_unit (SI m) { + static int units_of_m= 0; + static SI em = 0; + if (units_of_m == 0) { + // get the units of the character 'm' + FT_UInt glyph_index= FT_Get_Char_Index (math_face->ft_face, 'm'); + FT_Load_Glyph (math_face->ft_face, glyph_index, FT_LOAD_NO_SCALE); + units_of_m= math_face->ft_face->glyph->metrics.horiAdvance; + + // get the width of the character 'm' + metric ex; + get_extents ("m", ex); + em= ex->x2 - ex->x1; + } + return (int) ((m * units_of_m) / em); +} + font unicode_font (string family, int size, int dpi) { return unicode_font (family, size, dpi, dpi); diff --git a/src/Typeset/Boxes/Composite/script_boxes.cpp b/src/Typeset/Boxes/Composite/script_boxes.cpp index 48ddd89e50..24c577b0ec 100644 --- a/src/Typeset/Boxes/Composite/script_boxes.cpp +++ b/src/Typeset/Boxes/Composite/script_boxes.cpp @@ -12,6 +12,7 @@ #include "Boxes/Composite/italic_correct.hpp" #include "Boxes/composite.hpp" #include "Boxes/construct.hpp" +#include "font.hpp" /****************************************************************************** * subroutine for scripts @@ -86,11 +87,18 @@ lim_box_rep::lim_box_rep (path ip, box r2, box lo, box hi, font fn2, bool gl) type= 0; if (!is_nil (lo)) type+= 1; if (!is_nil (hi)) type+= 2; + bool use_opentype= (fn->math_type == MATH_TYPE_OPENTYPE) && + (fn->lower_limit_gap_min > 0) && + (fn->lower_limit_baseline_drop_min > 0); if (!is_nil (lo)) { SI top= max (lo->y2, fn->y2 * script (fn->size, 1) / fn->size) + sep_lo; Y = ref->y1; X = ((SI) (ref->right_slope () * (Y + top - lo->y1))) + ((ref->x1 + ref->x2) >> 1); + if (use_opentype) { + top= lo->y2 + fn->lower_limit_gap_min; + top= max (top, fn->lower_limit_baseline_drop_min); + } insert (lo, X - (lo->x2 >> 1), Y - top); italic_correct (lo); } @@ -99,6 +107,10 @@ lim_box_rep::lim_box_rep (path ip, box r2, box lo, box hi, font fn2, bool gl) Y = ref->y2; X = ((SI) (ref->right_slope () * (Y + hi->y2 - bot))) + ((ref->x1 + ref->x2) >> 1); + if (use_opentype) { + bot= hi->y1 - fn->upper_limit_gap_min; + bot= min (bot, -fn->upper_limit_baseline_rise_min); + } insert (hi, X - (hi->x2 >> 1), Y - bot); italic_correct (hi); }