gfx/thebes/gfxMathTable.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial