gfx/thebes/gfxMathTable.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:563a1faaf958
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 "MathTableStructures.h"
8 #include "harfbuzz/hb.h"
9 #include <algorithm>
10
11 using namespace mozilla;
12
13 gfxMathTable::gfxMathTable(hb_blob_t* aMathTable)
14 : mMathTable(aMathTable)
15 , mGlyphConstruction(nullptr)
16 , mGlyphID(-1)
17 , mVertical(false)
18 {
19 }
20
21 gfxMathTable::~gfxMathTable()
22 {
23 hb_blob_destroy(mMathTable);
24 }
25
26 bool
27 gfxMathTable::HasValidHeaders()
28 {
29 const char* mathData = hb_blob_get_data(mMathTable, nullptr);
30 // Verify the MATH table header.
31 if (!ValidStructure(mathData, sizeof(MATHTableHeader))) {
32 return false;
33 }
34 const MATHTableHeader* header = GetMATHTableHeader();
35 if (uint32_t(header->mVersion) != 0x00010000 ||
36 !ValidOffset(mathData, uint16_t(header->mMathConstants)) ||
37 !ValidOffset(mathData, uint16_t(header->mMathGlyphInfo)) ||
38 !ValidOffset(mathData, uint16_t(header->mMathVariants))) {
39 return false;
40 }
41
42 // Verify the MathConstants header.
43 const MathConstants* mathconstants = GetMathConstants();
44 const char* start = reinterpret_cast<const char*>(mathconstants);
45 if (!ValidStructure(start, sizeof(MathConstants))) {
46 return false;
47 }
48
49 // Verify the MathGlyphInfo header.
50 const MathGlyphInfo* mathglyphinfo = GetMathGlyphInfo();
51 start = reinterpret_cast<const char*>(mathglyphinfo);
52 if (!ValidStructure(start, sizeof(MathGlyphInfo))) {
53 return false;
54 }
55
56 // Verify the MathVariants header.
57 const MathVariants* mathvariants = GetMathVariants();
58 start = reinterpret_cast<const char*>(mathvariants);
59 if (!ValidStructure(start, sizeof(MathVariants)) ||
60 !ValidStructure(start,
61 sizeof(MathVariants) + sizeof(Offset) *
62 (uint16_t(mathvariants->mVertGlyphCount) +
63 uint16_t(mathvariants->mHorizGlyphCount))) ||
64 !ValidOffset(start, uint16_t(mathvariants->mVertGlyphCoverage)) ||
65 !ValidOffset(start, uint16_t(mathvariants->mHorizGlyphCoverage))) {
66 return false;
67 }
68
69 return true;
70 }
71
72 int32_t
73 gfxMathTable::GetMathConstant(gfxFontEntry::MathConstant aConstant)
74 {
75 const MathConstants* mathconstants = GetMathConstants();
76
77 if (aConstant <= gfxFontEntry::ScriptScriptPercentScaleDown) {
78 return int16_t(mathconstants->mInt16[aConstant]);
79 }
80
81 if (aConstant <= gfxFontEntry::DisplayOperatorMinHeight) {
82 return
83 uint16_t(mathconstants->
84 mUint16[aConstant - gfxFontEntry::DelimitedSubFormulaMinHeight]);
85 }
86
87 if (aConstant <= gfxFontEntry::RadicalKernAfterDegree) {
88 return int16_t(mathconstants->
89 mMathValues[aConstant - gfxFontEntry::MathLeading].mValue);
90 }
91
92 return uint16_t(mathconstants->mRadicalDegreeBottomRaisePercent);
93 }
94
95 bool
96 gfxMathTable::GetMathItalicsCorrection(uint32_t aGlyphID,
97 int16_t* aItalicCorrection)
98 {
99 const MathGlyphInfo* mathglyphinfo = GetMathGlyphInfo();
100
101 // Get the offset of the italic correction and verify whether it is valid.
102 const char* start = reinterpret_cast<const char*>(mathglyphinfo);
103 uint16_t offset = mathglyphinfo->mMathItalicsCorrectionInfo;
104 if (offset == 0 || !ValidOffset(start, offset)) {
105 return false;
106 }
107 start += offset;
108
109 // Verify the validity of the MathItalicsCorrectionInfo and retrieve it.
110 if (!ValidStructure(start, sizeof(MathItalicsCorrectionInfo))) {
111 return false;
112 }
113 const MathItalicsCorrectionInfo* italicsCorrectionInfo =
114 reinterpret_cast<const MathItalicsCorrectionInfo*>(start);
115
116 // Get the coverage index for the glyph.
117 offset = italicsCorrectionInfo->mCoverage;
118 const Coverage* coverage =
119 reinterpret_cast<const Coverage*>(start + offset);
120 int32_t i = GetCoverageIndex(coverage, aGlyphID);
121
122 // Get the ItalicsCorrection.
123 uint16_t count = italicsCorrectionInfo->mItalicsCorrectionCount;
124 if (i < 0 || i >= count) {
125 return false;
126 }
127 start = reinterpret_cast<const char*>(italicsCorrectionInfo + 1);
128 if (!ValidStructure(start, count * sizeof(MathValueRecord))) {
129 return false;
130 }
131 const MathValueRecord* mathValueRecordArray =
132 reinterpret_cast<const MathValueRecord*>(start);
133
134 *aItalicCorrection = int16_t(mathValueRecordArray[i].mValue);
135 return true;
136 }
137
138 uint32_t
139 gfxMathTable::GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
140 uint16_t aSize)
141 {
142 // Select the glyph construction.
143 SelectGlyphConstruction(aGlyphID, aVertical);
144 if (!mGlyphConstruction) {
145 return 0;
146 }
147
148 // Verify the validity of the array of the MathGlyphVariantRecord's and
149 // whether there is a variant of the requested size.
150 uint16_t count = mGlyphConstruction->mVariantCount;
151 const char* start = reinterpret_cast<const char*>(mGlyphConstruction + 1);
152 if (aSize >= count ||
153 !ValidStructure(start, count * sizeof(MathGlyphVariantRecord))) {
154 return 0;
155 }
156
157 // Return the glyph index of the requested size variant.
158 const MathGlyphVariantRecord* recordArray =
159 reinterpret_cast<const MathGlyphVariantRecord*>(start);
160 return uint32_t(recordArray[aSize].mVariantGlyph);
161 }
162
163 bool
164 gfxMathTable::GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
165 uint32_t aGlyphs[4])
166 {
167 // Get the glyph assembly corresponding to that (aGlyphID, aVertical) pair.
168 const GlyphAssembly* glyphAssembly = GetGlyphAssembly(aGlyphID, aVertical);
169 if (!glyphAssembly) {
170 return false;
171 }
172
173 // Verify the validity of the array of GlyphPartRecord's and retrieve it.
174 uint16_t count = glyphAssembly->mPartCount;
175 const char* start = reinterpret_cast<const char*>(glyphAssembly + 1);
176 if (!ValidStructure(start, count * sizeof(GlyphPartRecord))) {
177 return false;
178 }
179 const GlyphPartRecord* recordArray =
180 reinterpret_cast<const GlyphPartRecord*>(start);
181
182 // XXXfredw The structure of the Open Type Math table is a bit more general
183 // than the one currently used by the nsMathMLChar code, so we try to fallback
184 // in reasonable way. We use the approach of the copyComponents function in
185 // github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py
186 //
187 // The nsMathMLChar code can use at most 3 non extender pieces (aGlyphs[0],
188 // aGlyphs[1] and aGlyphs[2]) and the extenders between these pieces should
189 // all be the same (aGlyphs[4]). Also, the parts of vertical assembly are
190 // stored from bottom to top in the Open Type MATH table while they are
191 // stored from top to bottom in nsMathMLChar.
192
193 // Count the number of non extender pieces
194 uint16_t nonExtenderCount = 0;
195 for (uint16_t i = 0; i < count; i++) {
196 if (!(uint16_t(recordArray[i].mPartFlags) & PART_FLAG_EXTENDER)) {
197 nonExtenderCount++;
198 }
199 }
200 if (nonExtenderCount > 3) {
201 // Not supported: too many pieces
202 return false;
203 }
204
205 // Now browse the list of pieces
206
207 // 0 = look for a left/bottom glyph
208 // 1 = look for an extender between left/bottom and mid
209 // 2 = look for a middle glyph
210 // 3 = look for an extender between middle and right/top
211 // 4 = look for a right/top glyph
212 // 5 = no more piece expected
213 uint8_t state = 0;
214
215 // First extender char found.
216 uint32_t extenderChar = 0;
217
218 // Clear the aGlyphs table.
219 memset(aGlyphs, 0, sizeof(uint32_t) * 4);
220
221 for (uint16_t i = 0; i < count; i++) {
222
223 bool isExtender = uint16_t(recordArray[i].mPartFlags) & PART_FLAG_EXTENDER;
224 uint32_t glyph = recordArray[i].mGlyph;
225
226 if ((state == 1 || state == 2) && nonExtenderCount < 3) {
227 // do not try to find a middle glyph
228 state += 2;
229 }
230
231 if (isExtender) {
232 if (!extenderChar) {
233 extenderChar = glyph;
234 aGlyphs[3] = extenderChar;
235 } else if (extenderChar != glyph) {
236 // Not supported: different extenders
237 return false;
238 }
239
240 if (state == 0) { // or state == 1
241 // ignore left/bottom piece and multiple successive extenders
242 state = 1;
243 } else if (state == 2) { // or state == 3
244 // ignore middle piece and multiple successive extenders
245 state = 3;
246 } else if (state >= 4) {
247 // Not supported: unexpected extender
248 return false;
249 }
250
251 continue;
252 }
253
254 if (state == 0) {
255 // copy left/bottom part
256 aGlyphs[mVertical ? 2 : 0] = glyph;
257 state = 1;
258 continue;
259 }
260
261 if (state == 1 || state == 2) {
262 // copy middle part
263 aGlyphs[1] = glyph;
264 state = 3;
265 continue;
266 }
267
268 if (state == 3 || state == 4) {
269 // copy right/top part
270 aGlyphs[mVertical ? 0 : 2] = glyph;
271 state = 5;
272 }
273
274 }
275
276 return true;
277 }
278
279 bool
280 gfxMathTable::ValidStructure(const char* aStart, uint16_t aSize)
281 {
282 unsigned int mathDataLength;
283 const char* mathData = hb_blob_get_data(mMathTable, &mathDataLength);
284 return (mathData <= aStart &&
285 aStart + aSize <= mathData + mathDataLength);
286 }
287
288 bool
289 gfxMathTable::ValidOffset(const char* aStart, uint16_t aOffset)
290 {
291 unsigned int mathDataLength;
292 const char* mathData = hb_blob_get_data(mMathTable, &mathDataLength);
293 return (mathData <= aStart + aOffset &&
294 aStart + aOffset < mathData + mathDataLength);
295 }
296
297 const MATHTableHeader*
298 gfxMathTable::GetMATHTableHeader()
299 {
300 const char* mathData = hb_blob_get_data(mMathTable, nullptr);
301 return reinterpret_cast<const MATHTableHeader*>(mathData);
302 }
303
304 const MathConstants*
305 gfxMathTable::GetMathConstants()
306 {
307 const char* mathData = hb_blob_get_data(mMathTable, nullptr);
308 return
309 reinterpret_cast<const MathConstants*>(mathData +
310 uint16_t(GetMATHTableHeader()->
311 mMathConstants));
312 }
313
314 const MathGlyphInfo*
315 gfxMathTable::GetMathGlyphInfo()
316 {
317 const char* mathData = hb_blob_get_data(mMathTable, nullptr);
318 return
319 reinterpret_cast<const MathGlyphInfo*>(mathData +
320 uint16_t(GetMATHTableHeader()->
321 mMathGlyphInfo));
322 }
323
324 const MathVariants*
325 gfxMathTable::GetMathVariants()
326 {
327 const char* mathData = hb_blob_get_data(mMathTable, nullptr);
328 return
329 reinterpret_cast<const MathVariants*>(mathData +
330 uint16_t(GetMATHTableHeader()->
331 mMathVariants));
332 }
333
334 const GlyphAssembly*
335 gfxMathTable::GetGlyphAssembly(uint32_t aGlyphID, bool aVertical)
336 {
337 // Select the glyph construction.
338 SelectGlyphConstruction(aGlyphID, aVertical);
339 if (!mGlyphConstruction) {
340 return nullptr;
341 }
342
343 // Get the offset of the glyph assembly and verify whether it is valid.
344 const char* start = reinterpret_cast<const char*>(mGlyphConstruction);
345 uint16_t offset = mGlyphConstruction->mGlyphAssembly;
346 if (offset == 0 || !ValidOffset(start, offset)) {
347 return nullptr;
348 }
349 start += offset;
350
351 // Verify the validity of the GlyphAssembly and return it.
352 if (!ValidStructure(start, sizeof(GlyphAssembly))) {
353 return nullptr;
354 }
355 return reinterpret_cast<const GlyphAssembly*>(start);
356 }
357
358 int32_t
359 gfxMathTable::GetCoverageIndex(const Coverage* aCoverage, uint32_t aGlyph)
360 {
361 if (uint16_t(aCoverage->mFormat) == 1) {
362 // Coverage Format 1: list of individual glyph indices in the glyph set.
363 const CoverageFormat1* table =
364 reinterpret_cast<const CoverageFormat1*>(aCoverage);
365 uint16_t count = table->mGlyphCount;
366 const char* start = reinterpret_cast<const char*>(table + 1);
367 if (ValidStructure(start, count * sizeof(GlyphID))) {
368 const GlyphID* glyphArray =
369 reinterpret_cast<const GlyphID*>(start);
370 uint32_t imin = 0, imax = count;
371 while (imin < imax) {
372 uint32_t imid = (imin + imax) >> 1;
373 uint16_t glyphMid = glyphArray[imid];
374 if (glyphMid == aGlyph) {
375 return imid;
376 }
377 if (glyphMid < aGlyph) {
378 imin = imid + 1;
379 } else {
380 imax = imid;
381 }
382 }
383 }
384 } else if (uint16_t(aCoverage->mFormat) == 2) {
385 // Coverage Format 2: ranges of consecutive indices.
386 const CoverageFormat2* table =
387 reinterpret_cast<const CoverageFormat2*>(aCoverage);
388 uint16_t count = table->mRangeCount;
389 const char* start = reinterpret_cast<const char*>(table + 1);
390 if (ValidStructure(start, count * sizeof(RangeRecord))) {
391 const RangeRecord* rangeArray =
392 reinterpret_cast<const RangeRecord*>(start);
393 uint32_t imin = 0, imax = count;
394 while (imin < imax) {
395 uint32_t imid = (imin + imax) >> 1;
396 uint16_t rStart = rangeArray[imid].mStart;
397 uint16_t rEnd = rangeArray[imid].mEnd;
398 if (rEnd < aGlyph) {
399 imin = imid + 1;
400 } else if (aGlyph < rStart) {
401 imax = imid;
402 } else {
403 return (uint16_t(rangeArray[imid].mStartCoverageIndex) +
404 aGlyph - rStart);
405 }
406 }
407 }
408 }
409 return -1;
410 }
411
412 void
413 gfxMathTable::SelectGlyphConstruction(uint32_t aGlyphID, bool aVertical)
414 {
415 if (mGlyphID == aGlyphID && mVertical == aVertical) {
416 // The (glyph, direction) pair is already selected: nothing to do.
417 return;
418 }
419
420 // Update our cached values.
421 mVertical = aVertical;
422 mGlyphID = aGlyphID;
423 mGlyphConstruction = nullptr;
424
425 // Get the coverage index for the new values.
426 const MathVariants* mathvariants = GetMathVariants();
427 const char* start = reinterpret_cast<const char*>(mathvariants);
428 uint16_t offset = (aVertical ?
429 mathvariants->mVertGlyphCoverage :
430 mathvariants->mHorizGlyphCoverage);
431 const Coverage* coverage =
432 reinterpret_cast<const Coverage*>(start + offset);
433 int32_t i = GetCoverageIndex(coverage, aGlyphID);
434
435 // Get the offset to the glyph construction.
436 uint16_t count = (aVertical ?
437 mathvariants->mVertGlyphCount :
438 mathvariants->mHorizGlyphCount);
439 start = reinterpret_cast<const char*>(mathvariants + 1);
440 if (i < 0 || i >= count) {
441 return;
442 }
443 if (!aVertical) {
444 start += uint16_t(mathvariants->mVertGlyphCount) * sizeof(Offset);
445 }
446 if (!ValidStructure(start, count * sizeof(Offset))) {
447 return;
448 }
449 const Offset* offsetArray = reinterpret_cast<const Offset*>(start);
450 offset = uint16_t(offsetArray[i]);
451
452 // Make mGlyphConstruction point to the desired glyph construction.
453 start = reinterpret_cast<const char*>(mathvariants);
454 if (!ValidStructure(start + offset, sizeof(MathGlyphConstruction))) {
455 return;
456 }
457 mGlyphConstruction =
458 reinterpret_cast<const MathGlyphConstruction*>(start + offset);
459 }

mercurial