|
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 } |