Coverage Report

Created: 2018-09-25 14:53

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