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