DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (2cd3752963fc)

VCS Links

Macros

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "gfxMathTable.h"

#include "harfbuzz/hb.h"
#include "harfbuzz/hb-ot.h"

#define FloatToFixed(f) (65536 * (f))
#define FixedToFloat(f) ((f) * (1.0 / 65536.0))

using namespace mozilla;

gfxMathTable::gfxMathTable(hb_face_t *aFace, gfxFloat aSize)
{
  mHBFont = hb_font_create(aFace);
  if (mHBFont) {
    hb_font_set_ppem(mHBFont, aSize, aSize);
    uint32_t scale = FloatToFixed(aSize);
    hb_font_set_scale(mHBFont, scale, scale);
  }

  mMathVariantCache.glyphID = 0;
  ClearCache();
}

gfxMathTable::~gfxMathTable()
{
  if (mHBFont) {
      hb_font_destroy(mHBFont);
  }
}

gfxFloat
gfxMathTable::Constant(MathConstant aConstant) const
{
  int32_t value = hb_ot_math_get_constant(mHBFont, static_cast<hb_ot_math_constant_t>(aConstant));
  if (aConstant == ScriptPercentScaleDown ||
      aConstant == ScriptScriptPercentScaleDown ||
      aConstant == RadicalDegreeBottomRaisePercent) {
    return value / 100.0;
  }
  return FixedToFloat(value);
}

gfxFloat
gfxMathTable::ItalicsCorrection(uint32_t aGlyphID) const
{
  return FixedToFloat(hb_ot_math_get_glyph_italics_correction(mHBFont, aGlyphID));
}

uint32_t
gfxMathTable::VariantsSize(uint32_t aGlyphID, bool aVertical,
                           uint16_t aSize) const
{
  UpdateMathVariantCache(aGlyphID, aVertical);
  if (aSize < kMaxCachedSizeCount) {
    return mMathVariantCache.sizes[aSize];
  }

  // If the size index exceeds the cache size, we just read the value with
  // hb_ot_math_get_glyph_variants.
  hb_direction_t direction = aVertical ? HB_DIRECTION_BTT : HB_DIRECTION_LTR;
  hb_ot_math_glyph_variant_t variant;
  unsigned int count = 1;
  hb_ot_math_get_glyph_variants(mHBFont, aGlyphID, direction, aSize, &count,
                                &variant);
  return count > 0 ? variant.glyph : 0;
}

bool
gfxMathTable::VariantsParts(uint32_t aGlyphID, bool aVertical,
                            uint32_t aGlyphs[4]) const
{
  UpdateMathVariantCache(aGlyphID, aVertical);
  memcpy(aGlyphs, mMathVariantCache.parts, sizeof(mMathVariantCache.parts));
  return mMathVariantCache.arePartsValid;
}

void
gfxMathTable::ClearCache() const
{
  memset(mMathVariantCache.sizes, 0, sizeof(mMathVariantCache.sizes));
  memset(mMathVariantCache.parts, 0, sizeof(mMathVariantCache.parts));
  mMathVariantCache.arePartsValid = false;
}

void
gfxMathTable::UpdateMathVariantCache(uint32_t aGlyphID, bool aVertical) const
{
  if (aGlyphID == mMathVariantCache.glyphID &&
      aVertical == mMathVariantCache.vertical)
    return;

  mMathVariantCache.glyphID = aGlyphID;
  mMathVariantCache.vertical = aVertical;
  ClearCache();

  // Cache the first size variants.
  hb_direction_t direction = aVertical ? HB_DIRECTION_BTT : HB_DIRECTION_LTR;
  hb_ot_math_glyph_variant_t variant[kMaxCachedSizeCount];
  unsigned int count = kMaxCachedSizeCount;
  hb_ot_math_get_glyph_variants(mHBFont, aGlyphID, direction, 0, &count,
                                variant);
  for (unsigned int i = 0; i < count; i++) {
    mMathVariantCache.sizes[i] = variant[i].glyph;
  }

  // Try and cache the parts of the glyph assembly.
  // XXXfredw The structure of the Open Type Math table is a bit more general
  // than the one currently used by the nsMathMLChar code, so we try to fallback
  // in reasonable way. We use the approach of the copyComponents function in
  // github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py
  //
  // The nsMathMLChar code can use at most 3 non extender pieces (aGlyphs[0],
  // aGlyphs[1] and aGlyphs[2]) and the extenders between these pieces should
  // all be the same (aGlyphs[4]). Also, the parts of vertical assembly are
  // stored from bottom to top in the Open Type MATH table while they are
  // stored from top to bottom in nsMathMLChar.

  hb_ot_math_glyph_part_t parts[5];
  count = MOZ_ARRAY_LENGTH(parts);
  unsigned int offset = 0;
  if (hb_ot_math_get_glyph_assembly(mHBFont, aGlyphID, direction, offset, &count, parts, NULL) > MOZ_ARRAY_LENGTH(parts))
    return; // Not supported: Too many pieces.
  if (count <= 0)
    return; // Not supported: No pieces.

  // Count the number of non extender pieces
  uint16_t nonExtenderCount = 0;
  for (uint16_t i = 0; i < count; i++) {
    if (!(parts[i].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER)) {
      nonExtenderCount++;
    }
  }
  if (nonExtenderCount > 3) {
    // Not supported: too many pieces
    return;
  }

  // Now browse the list of pieces

  // 0 = look for a left/bottom glyph
  // 1 = look for an extender between left/bottom and mid
  // 2 = look for a middle glyph
  // 3 = look for an extender between middle and right/top
  // 4 = look for a right/top glyph
  // 5 = no more piece expected
  uint8_t state = 0;

  // First extender char found.
  uint32_t extenderChar = 0;

  for (uint16_t i = 0; i < count; i++) {

    bool isExtender = parts[i].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER;
    uint32_t glyph = parts[i].glyph;

    if ((state == 1 || state == 2) && nonExtenderCount < 3) {
      // do not try to find a middle glyph
      state += 2;
    }

    if (isExtender) {
      if (!extenderChar) {
        extenderChar = glyph;
        mMathVariantCache.parts[3] = extenderChar;
      } else if (extenderChar != glyph)  {
        // Not supported: different extenders
        return;
      }

      if (state == 0) { // or state == 1
        // ignore left/bottom piece and multiple successive extenders
        state = 1;
      } else if (state == 2) { // or state == 3
        // ignore middle piece and multiple successive extenders
        state = 3;
      } else if (state >= 4) {
        // Not supported: unexpected extender
        return;
      }

      continue;
    }

    if (state == 0) {
      // copy left/bottom part
      mMathVariantCache.parts[aVertical ? 2 : 0] = glyph;
      state = 1;
      continue;
    }

    if (state == 1 || state == 2) {
      // copy middle part
      mMathVariantCache.parts[1] = glyph;
      state = 3;
      continue;
    }

    if (state == 3 || state == 4) {
      // copy right/top part
      mMathVariantCache.parts[aVertical ? 0 : 2] = glyph;
      state = 5;
    }

  }

  mMathVariantCache.arePartsValid = true;
}