tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gfxMathTable.cpp (6987B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "gfxMathTable.h"
      6 
      7 #include "harfbuzz/hb.h"
      8 #include "harfbuzz/hb-ot.h"
      9 #include "mozilla/StaticPrefs_mathml.h"
     10 
     11 #define FloatToFixed(f) (65536 * (f))
     12 #define FixedToFloat(f) ((f) * (1.0 / 65536.0))
     13 
     14 using namespace mozilla;
     15 
     16 gfxMathTable::gfxMathTable(hb_face_t* aFace, gfxFloat aSize) {
     17  mMathVariantCache.vertical = false;
     18  mMathVariantCache.isRTL = false;
     19  mHBFont = hb_font_create(aFace);
     20  if (mHBFont) {
     21    hb_font_set_ppem(mHBFont, aSize, aSize);
     22    uint32_t scale = FloatToFixed(aSize);
     23    hb_font_set_scale(mHBFont, scale, scale);
     24  }
     25 
     26  mMathVariantCache.glyphID = 0;
     27  ClearCache();
     28 }
     29 
     30 gfxMathTable::~gfxMathTable() {
     31  if (mHBFont) {
     32    hb_font_destroy(mHBFont);
     33  }
     34 }
     35 
     36 gfxFloat gfxMathTable::Constant(MathConstant aConstant) const {
     37  int32_t value = hb_ot_math_get_constant(
     38      mHBFont, static_cast<hb_ot_math_constant_t>(aConstant));
     39  if (aConstant == ScriptPercentScaleDown ||
     40      aConstant == ScriptScriptPercentScaleDown ||
     41      aConstant == RadicalDegreeBottomRaisePercent) {
     42    return value / 100.0;
     43  }
     44  return FixedToFloat(value);
     45 }
     46 
     47 gfxFloat gfxMathTable::ItalicsCorrection(uint32_t aGlyphID) const {
     48  return FixedToFloat(
     49      hb_ot_math_get_glyph_italics_correction(mHBFont, aGlyphID));
     50 }
     51 
     52 uint32_t gfxMathTable::VariantsSize(uint32_t aGlyphID, bool aVertical,
     53                                    bool aRTL, uint16_t aSize) const {
     54  UpdateMathVariantCache(aGlyphID, aVertical, aRTL);
     55  if (aSize < kMaxCachedSizeCount) {
     56    return mMathVariantCache.sizes[aSize];
     57  }
     58 
     59  // If the size index exceeds the cache size, we just read the value with
     60  // hb_ot_math_get_glyph_variants.
     61  hb_direction_t direction = aVertical
     62                                 ? HB_DIRECTION_BTT
     63                                 : (aRTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
     64  hb_ot_math_glyph_variant_t variant;
     65  unsigned int count = 1;
     66  hb_ot_math_get_glyph_variants(mHBFont, aGlyphID, direction, aSize, &count,
     67                                &variant);
     68  return count > 0 ? variant.glyph : 0;
     69 }
     70 
     71 bool gfxMathTable::VariantsParts(uint32_t aGlyphID, bool aVertical, bool aRTL,
     72                                 uint32_t aGlyphs[4]) const {
     73  UpdateMathVariantCache(aGlyphID, aVertical, aRTL);
     74  memcpy(aGlyphs, mMathVariantCache.parts, sizeof(mMathVariantCache.parts));
     75  return mMathVariantCache.arePartsValid;
     76 }
     77 
     78 void gfxMathTable::ClearCache() const {
     79  memset(mMathVariantCache.sizes, 0, sizeof(mMathVariantCache.sizes));
     80  memset(mMathVariantCache.parts, 0, sizeof(mMathVariantCache.parts));
     81  mMathVariantCache.arePartsValid = false;
     82 }
     83 
     84 void gfxMathTable::UpdateMathVariantCache(uint32_t aGlyphID, bool aVertical,
     85                                          bool aRTL) const {
     86  if (!StaticPrefs::mathml_rtl_operator_mirroring_enabled()) {
     87    aRTL = false;
     88  }
     89 
     90  if (aGlyphID == mMathVariantCache.glyphID &&
     91      aVertical == mMathVariantCache.vertical &&
     92      aRTL == mMathVariantCache.isRTL)
     93    return;
     94 
     95  mMathVariantCache.glyphID = aGlyphID;
     96  mMathVariantCache.vertical = aVertical;
     97  mMathVariantCache.isRTL = aRTL;
     98  ClearCache();
     99 
    100  // Cache the first size variants.
    101  hb_direction_t direction = aVertical
    102                                 ? HB_DIRECTION_BTT
    103                                 : (aRTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
    104  hb_ot_math_glyph_variant_t variant[kMaxCachedSizeCount];
    105  unsigned int count = kMaxCachedSizeCount;
    106  hb_ot_math_get_glyph_variants(mHBFont, aGlyphID, direction, 0, &count,
    107                                variant);
    108  for (unsigned int i = 0; i < count; i++) {
    109    mMathVariantCache.sizes[i] = variant[i].glyph;
    110  }
    111 
    112  // Try and cache the parts of the glyph assembly.
    113  // XXXfredw The structure of the Open Type Math table is a bit more general
    114  // than the one currently used by the nsMathMLChar code, so we try to fallback
    115  // in reasonable way. We use the approach of the copyComponents function in
    116  // github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py
    117  //
    118  // The nsMathMLChar code can use at most 3 non extender pieces (aGlyphs[0],
    119  // aGlyphs[1] and aGlyphs[2]) and the extenders between these pieces should
    120  // all be the same (aGlyphs[4]). Also, the parts of vertical assembly are
    121  // stored from bottom to top in the Open Type MATH table while they are
    122  // stored from top to bottom in nsMathMLChar.
    123 
    124  hb_ot_math_glyph_part_t parts[5];
    125  count = std::size(parts);
    126  unsigned int offset = 0;
    127  if (hb_ot_math_get_glyph_assembly(mHBFont, aGlyphID, direction, offset,
    128                                    &count, parts, NULL) > std::size(parts))
    129    return;                // Not supported: Too many pieces.
    130  if (count <= 0) return;  // Not supported: No pieces.
    131 
    132  // Count the number of non extender pieces
    133  uint16_t nonExtenderCount = 0;
    134  for (uint16_t i = 0; i < count; i++) {
    135    if (!(parts[i].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER)) {
    136      nonExtenderCount++;
    137    }
    138  }
    139  if (nonExtenderCount > 3) {
    140    // Not supported: too many pieces
    141    return;
    142  }
    143 
    144  // Now browse the list of pieces
    145 
    146  // 0 = look for a left/bottom glyph
    147  // 1 = look for an extender between left/bottom and mid
    148  // 2 = look for a middle glyph
    149  // 3 = look for an extender between middle and right/top
    150  // 4 = look for a right/top glyph
    151  // 5 = no more piece expected
    152  uint8_t state = 0;
    153 
    154  // First extender char found.
    155  uint32_t extenderChar = 0;
    156 
    157  for (uint16_t i = 0; i < count; i++) {
    158    bool isExtender = parts[i].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER;
    159    uint32_t glyph = parts[i].glyph;
    160 
    161    if ((state == 1 || state == 2) && nonExtenderCount < 3) {
    162      // do not try to find a middle glyph
    163      state += 2;
    164    }
    165 
    166    if (isExtender) {
    167      if (!extenderChar) {
    168        extenderChar = glyph;
    169        mMathVariantCache.parts[3] = extenderChar;
    170      } else if (extenderChar != glyph) {
    171        // Not supported: different extenders
    172        return;
    173      }
    174 
    175      if (state == 0) {  // or state == 1
    176        // ignore left/bottom piece and multiple successive extenders
    177        state = 1;
    178      } else if (state == 2) {  // or state == 3
    179        // ignore middle piece and multiple successive extenders
    180        state = 3;
    181      } else if (state >= 4) {
    182        // Not supported: unexpected extender
    183        return;
    184      }
    185 
    186      continue;
    187    }
    188 
    189    if (state == 0) {
    190      // copy left/bottom part
    191      mMathVariantCache.parts[aVertical ? 2 : 0] = glyph;
    192      state = 1;
    193      continue;
    194    }
    195 
    196    if (state == 1 || state == 2) {
    197      // copy middle part
    198      mMathVariantCache.parts[1] = glyph;
    199      state = 3;
    200      continue;
    201    }
    202 
    203    if (state == 3 || state == 4) {
    204      // copy right/top part
    205      mMathVariantCache.parts[aVertical ? 0 : 2] = glyph;
    206      state = 5;
    207    }
    208  }
    209 
    210  mMathVariantCache.arePartsValid = true;
    211 }