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

     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/. */
     5 #include "gfxMathTable.h"
     7 #include "MathTableStructures.h"
     8 #include "harfbuzz/hb.h"
     9 #include <algorithm>
    11 using namespace mozilla;
    13 gfxMathTable::gfxMathTable(hb_blob_t* aMathTable)
    14   : mMathTable(aMathTable)
    15   , mGlyphConstruction(nullptr)
    16   , mGlyphID(-1)
    17   , mVertical(false)
    18 {
    19 }
    21 gfxMathTable::~gfxMathTable()
    22 {
    23   hb_blob_destroy(mMathTable);
    24 }
    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   }
    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   }
    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   }
    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   }
    69   return true;
    70 }
    72 int32_t
    73 gfxMathTable::GetMathConstant(gfxFontEntry::MathConstant aConstant)
    74 {
    75   const MathConstants* mathconstants = GetMathConstants();
    77   if (aConstant <= gfxFontEntry::ScriptScriptPercentScaleDown) {
    78     return int16_t(mathconstants->mInt16[aConstant]);
    79   }
    81   if (aConstant <= gfxFontEntry::DisplayOperatorMinHeight) {
    82     return
    83       uint16_t(mathconstants->
    84                mUint16[aConstant - gfxFontEntry::DelimitedSubFormulaMinHeight]);
    85   }
    87   if (aConstant <= gfxFontEntry::RadicalKernAfterDegree) {
    88     return int16_t(mathconstants->
    89                    mMathValues[aConstant - gfxFontEntry::MathLeading].mValue);
    90   }
    92   return uint16_t(mathconstants->mRadicalDegreeBottomRaisePercent);
    93 }
    95 bool
    96 gfxMathTable::GetMathItalicsCorrection(uint32_t aGlyphID,
    97                                        int16_t* aItalicCorrection)
    98 {
    99   const MathGlyphInfo* mathglyphinfo = GetMathGlyphInfo();
   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;
   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);
   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);
   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);
   134   *aItalicCorrection = int16_t(mathValueRecordArray[i].mValue);
   135   return true;
   136 }
   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   }
   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   }
   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 }
   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   }
   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);
   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.
   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   }
   205   // Now browse the list of pieces
   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;
   215   // First extender char found.
   216   uint32_t extenderChar = 0;
   218   // Clear the aGlyphs table.
   219   memset(aGlyphs, 0, sizeof(uint32_t) * 4);
   221   for (uint16_t i = 0; i < count; i++) {
   223     bool isExtender = uint16_t(recordArray[i].mPartFlags) & PART_FLAG_EXTENDER;
   224     uint32_t glyph = recordArray[i].mGlyph;
   226     if ((state == 1 || state == 2) && nonExtenderCount < 3) {
   227       // do not try to find a middle glyph
   228       state += 2;
   229     }
   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       }
   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       }
   251       continue;
   252     }
   254     if (state == 0) {
   255       // copy left/bottom part
   256       aGlyphs[mVertical ? 2 : 0] = glyph;
   257       state = 1;
   258       continue;
   259     }
   261     if (state == 1 || state == 2) {
   262       // copy middle part
   263       aGlyphs[1] = glyph;
   264       state = 3;
   265       continue;
   266     }
   268     if (state == 3 || state == 4) {
   269       // copy right/top part
   270       aGlyphs[mVertical ? 0 : 2] = glyph;
   271       state = 5;
   272     }
   274   }
   276   return true;
   277 }
   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 }
   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 }
   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 }
   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 }
   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 }
   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 }
   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   }
   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;
   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 }
   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 }
   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   }
   420   // Update our cached values.
   421   mVertical = aVertical;
   422   mGlyphID = aGlyphID;
   423   mGlyphConstruction = nullptr;
   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);
   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]);
   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