1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxMathTable.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,459 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "gfxMathTable.h" 1.9 + 1.10 +#include "MathTableStructures.h" 1.11 +#include "harfbuzz/hb.h" 1.12 +#include <algorithm> 1.13 + 1.14 +using namespace mozilla; 1.15 + 1.16 +gfxMathTable::gfxMathTable(hb_blob_t* aMathTable) 1.17 + : mMathTable(aMathTable) 1.18 + , mGlyphConstruction(nullptr) 1.19 + , mGlyphID(-1) 1.20 + , mVertical(false) 1.21 +{ 1.22 +} 1.23 + 1.24 +gfxMathTable::~gfxMathTable() 1.25 +{ 1.26 + hb_blob_destroy(mMathTable); 1.27 +} 1.28 + 1.29 +bool 1.30 +gfxMathTable::HasValidHeaders() 1.31 +{ 1.32 + const char* mathData = hb_blob_get_data(mMathTable, nullptr); 1.33 + // Verify the MATH table header. 1.34 + if (!ValidStructure(mathData, sizeof(MATHTableHeader))) { 1.35 + return false; 1.36 + } 1.37 + const MATHTableHeader* header = GetMATHTableHeader(); 1.38 + if (uint32_t(header->mVersion) != 0x00010000 || 1.39 + !ValidOffset(mathData, uint16_t(header->mMathConstants)) || 1.40 + !ValidOffset(mathData, uint16_t(header->mMathGlyphInfo)) || 1.41 + !ValidOffset(mathData, uint16_t(header->mMathVariants))) { 1.42 + return false; 1.43 + } 1.44 + 1.45 + // Verify the MathConstants header. 1.46 + const MathConstants* mathconstants = GetMathConstants(); 1.47 + const char* start = reinterpret_cast<const char*>(mathconstants); 1.48 + if (!ValidStructure(start, sizeof(MathConstants))) { 1.49 + return false; 1.50 + } 1.51 + 1.52 + // Verify the MathGlyphInfo header. 1.53 + const MathGlyphInfo* mathglyphinfo = GetMathGlyphInfo(); 1.54 + start = reinterpret_cast<const char*>(mathglyphinfo); 1.55 + if (!ValidStructure(start, sizeof(MathGlyphInfo))) { 1.56 + return false; 1.57 + } 1.58 + 1.59 + // Verify the MathVariants header. 1.60 + const MathVariants* mathvariants = GetMathVariants(); 1.61 + start = reinterpret_cast<const char*>(mathvariants); 1.62 + if (!ValidStructure(start, sizeof(MathVariants)) || 1.63 + !ValidStructure(start, 1.64 + sizeof(MathVariants) + sizeof(Offset) * 1.65 + (uint16_t(mathvariants->mVertGlyphCount) + 1.66 + uint16_t(mathvariants->mHorizGlyphCount))) || 1.67 + !ValidOffset(start, uint16_t(mathvariants->mVertGlyphCoverage)) || 1.68 + !ValidOffset(start, uint16_t(mathvariants->mHorizGlyphCoverage))) { 1.69 + return false; 1.70 + } 1.71 + 1.72 + return true; 1.73 +} 1.74 + 1.75 +int32_t 1.76 +gfxMathTable::GetMathConstant(gfxFontEntry::MathConstant aConstant) 1.77 +{ 1.78 + const MathConstants* mathconstants = GetMathConstants(); 1.79 + 1.80 + if (aConstant <= gfxFontEntry::ScriptScriptPercentScaleDown) { 1.81 + return int16_t(mathconstants->mInt16[aConstant]); 1.82 + } 1.83 + 1.84 + if (aConstant <= gfxFontEntry::DisplayOperatorMinHeight) { 1.85 + return 1.86 + uint16_t(mathconstants-> 1.87 + mUint16[aConstant - gfxFontEntry::DelimitedSubFormulaMinHeight]); 1.88 + } 1.89 + 1.90 + if (aConstant <= gfxFontEntry::RadicalKernAfterDegree) { 1.91 + return int16_t(mathconstants-> 1.92 + mMathValues[aConstant - gfxFontEntry::MathLeading].mValue); 1.93 + } 1.94 + 1.95 + return uint16_t(mathconstants->mRadicalDegreeBottomRaisePercent); 1.96 +} 1.97 + 1.98 +bool 1.99 +gfxMathTable::GetMathItalicsCorrection(uint32_t aGlyphID, 1.100 + int16_t* aItalicCorrection) 1.101 +{ 1.102 + const MathGlyphInfo* mathglyphinfo = GetMathGlyphInfo(); 1.103 + 1.104 + // Get the offset of the italic correction and verify whether it is valid. 1.105 + const char* start = reinterpret_cast<const char*>(mathglyphinfo); 1.106 + uint16_t offset = mathglyphinfo->mMathItalicsCorrectionInfo; 1.107 + if (offset == 0 || !ValidOffset(start, offset)) { 1.108 + return false; 1.109 + } 1.110 + start += offset; 1.111 + 1.112 + // Verify the validity of the MathItalicsCorrectionInfo and retrieve it. 1.113 + if (!ValidStructure(start, sizeof(MathItalicsCorrectionInfo))) { 1.114 + return false; 1.115 + } 1.116 + const MathItalicsCorrectionInfo* italicsCorrectionInfo = 1.117 + reinterpret_cast<const MathItalicsCorrectionInfo*>(start); 1.118 + 1.119 + // Get the coverage index for the glyph. 1.120 + offset = italicsCorrectionInfo->mCoverage; 1.121 + const Coverage* coverage = 1.122 + reinterpret_cast<const Coverage*>(start + offset); 1.123 + int32_t i = GetCoverageIndex(coverage, aGlyphID); 1.124 + 1.125 + // Get the ItalicsCorrection. 1.126 + uint16_t count = italicsCorrectionInfo->mItalicsCorrectionCount; 1.127 + if (i < 0 || i >= count) { 1.128 + return false; 1.129 + } 1.130 + start = reinterpret_cast<const char*>(italicsCorrectionInfo + 1); 1.131 + if (!ValidStructure(start, count * sizeof(MathValueRecord))) { 1.132 + return false; 1.133 + } 1.134 + const MathValueRecord* mathValueRecordArray = 1.135 + reinterpret_cast<const MathValueRecord*>(start); 1.136 + 1.137 + *aItalicCorrection = int16_t(mathValueRecordArray[i].mValue); 1.138 + return true; 1.139 +} 1.140 + 1.141 +uint32_t 1.142 +gfxMathTable::GetMathVariantsSize(uint32_t aGlyphID, bool aVertical, 1.143 + uint16_t aSize) 1.144 +{ 1.145 + // Select the glyph construction. 1.146 + SelectGlyphConstruction(aGlyphID, aVertical); 1.147 + if (!mGlyphConstruction) { 1.148 + return 0; 1.149 + } 1.150 + 1.151 + // Verify the validity of the array of the MathGlyphVariantRecord's and 1.152 + // whether there is a variant of the requested size. 1.153 + uint16_t count = mGlyphConstruction->mVariantCount; 1.154 + const char* start = reinterpret_cast<const char*>(mGlyphConstruction + 1); 1.155 + if (aSize >= count || 1.156 + !ValidStructure(start, count * sizeof(MathGlyphVariantRecord))) { 1.157 + return 0; 1.158 + } 1.159 + 1.160 + // Return the glyph index of the requested size variant. 1.161 + const MathGlyphVariantRecord* recordArray = 1.162 + reinterpret_cast<const MathGlyphVariantRecord*>(start); 1.163 + return uint32_t(recordArray[aSize].mVariantGlyph); 1.164 +} 1.165 + 1.166 +bool 1.167 +gfxMathTable::GetMathVariantsParts(uint32_t aGlyphID, bool aVertical, 1.168 + uint32_t aGlyphs[4]) 1.169 +{ 1.170 + // Get the glyph assembly corresponding to that (aGlyphID, aVertical) pair. 1.171 + const GlyphAssembly* glyphAssembly = GetGlyphAssembly(aGlyphID, aVertical); 1.172 + if (!glyphAssembly) { 1.173 + return false; 1.174 + } 1.175 + 1.176 + // Verify the validity of the array of GlyphPartRecord's and retrieve it. 1.177 + uint16_t count = glyphAssembly->mPartCount; 1.178 + const char* start = reinterpret_cast<const char*>(glyphAssembly + 1); 1.179 + if (!ValidStructure(start, count * sizeof(GlyphPartRecord))) { 1.180 + return false; 1.181 + } 1.182 + const GlyphPartRecord* recordArray = 1.183 + reinterpret_cast<const GlyphPartRecord*>(start); 1.184 + 1.185 + // XXXfredw The structure of the Open Type Math table is a bit more general 1.186 + // than the one currently used by the nsMathMLChar code, so we try to fallback 1.187 + // in reasonable way. We use the approach of the copyComponents function in 1.188 + // github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py 1.189 + // 1.190 + // The nsMathMLChar code can use at most 3 non extender pieces (aGlyphs[0], 1.191 + // aGlyphs[1] and aGlyphs[2]) and the extenders between these pieces should 1.192 + // all be the same (aGlyphs[4]). Also, the parts of vertical assembly are 1.193 + // stored from bottom to top in the Open Type MATH table while they are 1.194 + // stored from top to bottom in nsMathMLChar. 1.195 + 1.196 + // Count the number of non extender pieces 1.197 + uint16_t nonExtenderCount = 0; 1.198 + for (uint16_t i = 0; i < count; i++) { 1.199 + if (!(uint16_t(recordArray[i].mPartFlags) & PART_FLAG_EXTENDER)) { 1.200 + nonExtenderCount++; 1.201 + } 1.202 + } 1.203 + if (nonExtenderCount > 3) { 1.204 + // Not supported: too many pieces 1.205 + return false; 1.206 + } 1.207 + 1.208 + // Now browse the list of pieces 1.209 + 1.210 + // 0 = look for a left/bottom glyph 1.211 + // 1 = look for an extender between left/bottom and mid 1.212 + // 2 = look for a middle glyph 1.213 + // 3 = look for an extender between middle and right/top 1.214 + // 4 = look for a right/top glyph 1.215 + // 5 = no more piece expected 1.216 + uint8_t state = 0; 1.217 + 1.218 + // First extender char found. 1.219 + uint32_t extenderChar = 0; 1.220 + 1.221 + // Clear the aGlyphs table. 1.222 + memset(aGlyphs, 0, sizeof(uint32_t) * 4); 1.223 + 1.224 + for (uint16_t i = 0; i < count; i++) { 1.225 + 1.226 + bool isExtender = uint16_t(recordArray[i].mPartFlags) & PART_FLAG_EXTENDER; 1.227 + uint32_t glyph = recordArray[i].mGlyph; 1.228 + 1.229 + if ((state == 1 || state == 2) && nonExtenderCount < 3) { 1.230 + // do not try to find a middle glyph 1.231 + state += 2; 1.232 + } 1.233 + 1.234 + if (isExtender) { 1.235 + if (!extenderChar) { 1.236 + extenderChar = glyph; 1.237 + aGlyphs[3] = extenderChar; 1.238 + } else if (extenderChar != glyph) { 1.239 + // Not supported: different extenders 1.240 + return false; 1.241 + } 1.242 + 1.243 + if (state == 0) { // or state == 1 1.244 + // ignore left/bottom piece and multiple successive extenders 1.245 + state = 1; 1.246 + } else if (state == 2) { // or state == 3 1.247 + // ignore middle piece and multiple successive extenders 1.248 + state = 3; 1.249 + } else if (state >= 4) { 1.250 + // Not supported: unexpected extender 1.251 + return false; 1.252 + } 1.253 + 1.254 + continue; 1.255 + } 1.256 + 1.257 + if (state == 0) { 1.258 + // copy left/bottom part 1.259 + aGlyphs[mVertical ? 2 : 0] = glyph; 1.260 + state = 1; 1.261 + continue; 1.262 + } 1.263 + 1.264 + if (state == 1 || state == 2) { 1.265 + // copy middle part 1.266 + aGlyphs[1] = glyph; 1.267 + state = 3; 1.268 + continue; 1.269 + } 1.270 + 1.271 + if (state == 3 || state == 4) { 1.272 + // copy right/top part 1.273 + aGlyphs[mVertical ? 0 : 2] = glyph; 1.274 + state = 5; 1.275 + } 1.276 + 1.277 + } 1.278 + 1.279 + return true; 1.280 +} 1.281 + 1.282 +bool 1.283 +gfxMathTable::ValidStructure(const char* aStart, uint16_t aSize) 1.284 +{ 1.285 + unsigned int mathDataLength; 1.286 + const char* mathData = hb_blob_get_data(mMathTable, &mathDataLength); 1.287 + return (mathData <= aStart && 1.288 + aStart + aSize <= mathData + mathDataLength); 1.289 +} 1.290 + 1.291 +bool 1.292 +gfxMathTable::ValidOffset(const char* aStart, uint16_t aOffset) 1.293 +{ 1.294 + unsigned int mathDataLength; 1.295 + const char* mathData = hb_blob_get_data(mMathTable, &mathDataLength); 1.296 + return (mathData <= aStart + aOffset && 1.297 + aStart + aOffset < mathData + mathDataLength); 1.298 +} 1.299 + 1.300 +const MATHTableHeader* 1.301 +gfxMathTable::GetMATHTableHeader() 1.302 +{ 1.303 + const char* mathData = hb_blob_get_data(mMathTable, nullptr); 1.304 + return reinterpret_cast<const MATHTableHeader*>(mathData); 1.305 +} 1.306 + 1.307 +const MathConstants* 1.308 +gfxMathTable::GetMathConstants() 1.309 +{ 1.310 + const char* mathData = hb_blob_get_data(mMathTable, nullptr); 1.311 + return 1.312 + reinterpret_cast<const MathConstants*>(mathData + 1.313 + uint16_t(GetMATHTableHeader()-> 1.314 + mMathConstants)); 1.315 +} 1.316 + 1.317 +const MathGlyphInfo* 1.318 +gfxMathTable::GetMathGlyphInfo() 1.319 +{ 1.320 + const char* mathData = hb_blob_get_data(mMathTable, nullptr); 1.321 + return 1.322 + reinterpret_cast<const MathGlyphInfo*>(mathData + 1.323 + uint16_t(GetMATHTableHeader()-> 1.324 + mMathGlyphInfo)); 1.325 +} 1.326 + 1.327 +const MathVariants* 1.328 +gfxMathTable::GetMathVariants() 1.329 +{ 1.330 + const char* mathData = hb_blob_get_data(mMathTable, nullptr); 1.331 + return 1.332 + reinterpret_cast<const MathVariants*>(mathData + 1.333 + uint16_t(GetMATHTableHeader()-> 1.334 + mMathVariants)); 1.335 +} 1.336 + 1.337 +const GlyphAssembly* 1.338 +gfxMathTable::GetGlyphAssembly(uint32_t aGlyphID, bool aVertical) 1.339 +{ 1.340 + // Select the glyph construction. 1.341 + SelectGlyphConstruction(aGlyphID, aVertical); 1.342 + if (!mGlyphConstruction) { 1.343 + return nullptr; 1.344 + } 1.345 + 1.346 + // Get the offset of the glyph assembly and verify whether it is valid. 1.347 + const char* start = reinterpret_cast<const char*>(mGlyphConstruction); 1.348 + uint16_t offset = mGlyphConstruction->mGlyphAssembly; 1.349 + if (offset == 0 || !ValidOffset(start, offset)) { 1.350 + return nullptr; 1.351 + } 1.352 + start += offset; 1.353 + 1.354 + // Verify the validity of the GlyphAssembly and return it. 1.355 + if (!ValidStructure(start, sizeof(GlyphAssembly))) { 1.356 + return nullptr; 1.357 + } 1.358 + return reinterpret_cast<const GlyphAssembly*>(start); 1.359 +} 1.360 + 1.361 +int32_t 1.362 +gfxMathTable::GetCoverageIndex(const Coverage* aCoverage, uint32_t aGlyph) 1.363 +{ 1.364 + if (uint16_t(aCoverage->mFormat) == 1) { 1.365 + // Coverage Format 1: list of individual glyph indices in the glyph set. 1.366 + const CoverageFormat1* table = 1.367 + reinterpret_cast<const CoverageFormat1*>(aCoverage); 1.368 + uint16_t count = table->mGlyphCount; 1.369 + const char* start = reinterpret_cast<const char*>(table + 1); 1.370 + if (ValidStructure(start, count * sizeof(GlyphID))) { 1.371 + const GlyphID* glyphArray = 1.372 + reinterpret_cast<const GlyphID*>(start); 1.373 + uint32_t imin = 0, imax = count; 1.374 + while (imin < imax) { 1.375 + uint32_t imid = (imin + imax) >> 1; 1.376 + uint16_t glyphMid = glyphArray[imid]; 1.377 + if (glyphMid == aGlyph) { 1.378 + return imid; 1.379 + } 1.380 + if (glyphMid < aGlyph) { 1.381 + imin = imid + 1; 1.382 + } else { 1.383 + imax = imid; 1.384 + } 1.385 + } 1.386 + } 1.387 + } else if (uint16_t(aCoverage->mFormat) == 2) { 1.388 + // Coverage Format 2: ranges of consecutive indices. 1.389 + const CoverageFormat2* table = 1.390 + reinterpret_cast<const CoverageFormat2*>(aCoverage); 1.391 + uint16_t count = table->mRangeCount; 1.392 + const char* start = reinterpret_cast<const char*>(table + 1); 1.393 + if (ValidStructure(start, count * sizeof(RangeRecord))) { 1.394 + const RangeRecord* rangeArray = 1.395 + reinterpret_cast<const RangeRecord*>(start); 1.396 + uint32_t imin = 0, imax = count; 1.397 + while (imin < imax) { 1.398 + uint32_t imid = (imin + imax) >> 1; 1.399 + uint16_t rStart = rangeArray[imid].mStart; 1.400 + uint16_t rEnd = rangeArray[imid].mEnd; 1.401 + if (rEnd < aGlyph) { 1.402 + imin = imid + 1; 1.403 + } else if (aGlyph < rStart) { 1.404 + imax = imid; 1.405 + } else { 1.406 + return (uint16_t(rangeArray[imid].mStartCoverageIndex) + 1.407 + aGlyph - rStart); 1.408 + } 1.409 + } 1.410 + } 1.411 + } 1.412 + return -1; 1.413 +} 1.414 + 1.415 +void 1.416 +gfxMathTable::SelectGlyphConstruction(uint32_t aGlyphID, bool aVertical) 1.417 +{ 1.418 + if (mGlyphID == aGlyphID && mVertical == aVertical) { 1.419 + // The (glyph, direction) pair is already selected: nothing to do. 1.420 + return; 1.421 + } 1.422 + 1.423 + // Update our cached values. 1.424 + mVertical = aVertical; 1.425 + mGlyphID = aGlyphID; 1.426 + mGlyphConstruction = nullptr; 1.427 + 1.428 + // Get the coverage index for the new values. 1.429 + const MathVariants* mathvariants = GetMathVariants(); 1.430 + const char* start = reinterpret_cast<const char*>(mathvariants); 1.431 + uint16_t offset = (aVertical ? 1.432 + mathvariants->mVertGlyphCoverage : 1.433 + mathvariants->mHorizGlyphCoverage); 1.434 + const Coverage* coverage = 1.435 + reinterpret_cast<const Coverage*>(start + offset); 1.436 + int32_t i = GetCoverageIndex(coverage, aGlyphID); 1.437 + 1.438 + // Get the offset to the glyph construction. 1.439 + uint16_t count = (aVertical ? 1.440 + mathvariants->mVertGlyphCount : 1.441 + mathvariants->mHorizGlyphCount); 1.442 + start = reinterpret_cast<const char*>(mathvariants + 1); 1.443 + if (i < 0 || i >= count) { 1.444 + return; 1.445 + } 1.446 + if (!aVertical) { 1.447 + start += uint16_t(mathvariants->mVertGlyphCount) * sizeof(Offset); 1.448 + } 1.449 + if (!ValidStructure(start, count * sizeof(Offset))) { 1.450 + return; 1.451 + } 1.452 + const Offset* offsetArray = reinterpret_cast<const Offset*>(start); 1.453 + offset = uint16_t(offsetArray[i]); 1.454 + 1.455 + // Make mGlyphConstruction point to the desired glyph construction. 1.456 + start = reinterpret_cast<const char*>(mathvariants); 1.457 + if (!ValidStructure(start + offset, sizeof(MathGlyphConstruction))) { 1.458 + return; 1.459 + } 1.460 + mGlyphConstruction = 1.461 + reinterpret_cast<const MathGlyphConstruction*>(start + offset); 1.462 +}