# HG changeset patch # Parent 6148c7e5202b1bb991490f340b03aa1bc7e681a1 # User Frédéric Wang Part 1: add a gfxMathTable class to read the MATH table. r=jfkthame, b=407059 diff --git a/gfx/thebes/MathTableStructures.h b/gfx/thebes/MathTableStructures.h new file mode 100644 --- /dev/null +++ b/gfx/thebes/MathTableStructures.h @@ -0,0 +1,121 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This file contains the structures described in Microsoft's document +// "The MATH table and OpenType Features for Math Processing" (not yet public). +// +// Arrays of varying size are indicated in comments. Typically, gfxMathTable +// will read the header of the structure first, verify that there is enough +// space for the specified arrays and then use a pointer to browse these arrays. + +#ifndef MATH_TABLE_STRUCTURE_H +#define MATH_TABLE_STRUCTURE_H + +#include "gfxFontUtils.h" + +typedef mozilla::AutoSwap_PRUint16 Count16; +typedef mozilla::AutoSwap_PRUint16 GlyphID; +typedef mozilla::AutoSwap_PRUint16 Offset; + +struct MathValueRecord { + mozilla::AutoSwap_PRInt16 mValue; + Offset mDeviceTable; +}; + +struct RangeRecord { + GlyphID mStart; + GlyphID mEnd; + mozilla::AutoSwap_PRUint16 mStartCoverageIndex; +}; + +struct Coverage { + mozilla::AutoSwap_PRUint16 mFormat; +}; + +struct CoverageFormat1 { + mozilla::AutoSwap_PRUint16 mFormat; + Count16 mGlyphCount; + // GlyphID mGlyphArray[mGlyphCount] +}; + +struct CoverageFormat2 { + mozilla::AutoSwap_PRUint16 mFormat; + Count16 mRangeCount; + // RangeRecord mRangeArray[mRangeCount]; +}; + +struct MATHTableHeader { + mozilla::AutoSwap_PRUint32 mVersion; + Offset mMathConstants; + Offset mMathGlyphInfo; + Offset mMathVariants; +}; + +struct MathConstants { + mozilla::AutoSwap_PRInt16 mInt16[gfxFontEntry::ScriptScriptPercentScaleDown - + gfxFontEntry::ScriptPercentScaleDown + 1]; + mozilla::AutoSwap_PRUint16 mUint16[gfxFontEntry::DisplayOperatorMinHeight - + gfxFontEntry:: + DelimitedSubFormulaMinHeight + 1]; + MathValueRecord mMathValues[gfxFontEntry::RadicalKernAfterDegree - + gfxFontEntry::MathLeading + 1]; + mozilla::AutoSwap_PRUint16 mRadicalDegreeBottomRaisePercent; +}; + +struct MathGlyphInfo { + Offset mMathItalicsCorrectionInfo; + Offset mMathTopAccentAttachment; + Offset mExtendedShapeCoverage; + Offset mMathKernInfo; +}; + +struct MathItalicsCorrectionInfo { + Offset mCoverage; + Count16 mItalicsCorrectionCount; + // MathValueRecord mItalicsCorrection[mItalicsCorrectionCount] +}; + +struct MathVariants { + mozilla::AutoSwap_PRUint16 mMinConnectorOverlap; + Offset mVertGlyphCoverage; + Offset mHorizGlyphCoverage; + Count16 mVertGlyphCount; + Count16 mHorizGlyphCount; + // Offset mVertGlyphConstruction[mVertGlyphCount]; + // Offset mHorizGlyphConstruction[mHorizGlyphCount]; +}; + +struct MathGlyphVariantRecord { + GlyphID mVariantGlyph; + mozilla::AutoSwap_PRUint16 mAdvanceMeasurement; +}; + +struct MathGlyphConstruction { + Offset mGlyphAssembly; + Count16 mVariantCount; + // MathGlyphVariantRecord mMathGlyphVariantRecord[mVariantCount] +}; + +struct GlyphPartRecord { + GlyphID mGlyph; + mozilla::AutoSwap_PRUint16 mStartConnectorLength; + mozilla::AutoSwap_PRUint16 mEndConnectorLength; + mozilla::AutoSwap_PRUint16 mFullAdvance; + mozilla::AutoSwap_PRUint16 mPartFlags; +}; + +// PartFlags enumeration currently uses only one bit: +// 0x0001 If set, the part can be skipped or repeated. +// 0xFFFE Reserved. +enum { + PART_FLAG_EXTENDER = 0x01 +}; + +struct GlyphAssembly { + MathValueRecord mItalicsCorrection; + Count16 mPartCount; + // GlyphPartRecord mPartRecords[mPartCount] +}; + +#endif diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -33,16 +33,17 @@ #include "nsStyleConsts.h" #include "mozilla/FloatingPoint.h" #include "mozilla/Likely.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/Telemetry.h" #include "gfxSVGGlyphs.h" +#include "gfxMathTable.h" #include "gfx2DGlue.h" #if defined(XP_MACOSX) #include "nsCocoaFeatures.h" #endif #include "cairo.h" #include "gfxFontTest.h" @@ -107,16 +108,17 @@ gfxFontEntry::gfxFontEntry() : mIsBadUnderlineFont(false), mIsUserFont(false), mIsLocalUserFont(false), mStandardFace(false), mSymbolFont(false), mIgnoreGDEF(false), mIgnoreGSUB(false), mSVGInitialized(false), + mMathInitialized(false), mHasSpaceFeaturesInitialized(false), mHasSpaceFeatures(false), mHasSpaceFeaturesKerning(false), mHasSpaceFeaturesNonKerning(false), mSkipDefaultFeatureSpaceCheck(false), mCheckedForGraphiteTables(false), mHasCmapTable(false), mGrFaceInitialized(false), @@ -136,16 +138,17 @@ gfxFontEntry::gfxFontEntry(const nsAStri mName(aName), mItalic(false), mFixedPitch(false), mIsProxy(false), mIsValid(true), mIsBadUnderlineFont(false), mIsUserFont(false), mIsLocalUserFont(false), mStandardFace(aIsStandardFace), mSymbolFont(false), mIgnoreGDEF(false), mIgnoreGSUB(false), mSVGInitialized(false), + mMathInitialized(false), mHasSpaceFeaturesInitialized(false), mHasSpaceFeatures(false), mHasSpaceFeaturesKerning(false), mHasSpaceFeaturesNonKerning(false), mSkipDefaultFeatureSpaceCheck(false), mCheckedForGraphiteTables(false), mHasCmapTable(false), mGrFaceInitialized(false), @@ -384,16 +387,88 @@ void gfxFontEntry::NotifyGlyphsChanged() { for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) { gfxFont* font = mFontsUsingSVGGlyphs[i]; font->NotifyGlyphsChanged(); } } +bool +gfxFontEntry::TryGetMathTable(gfxFont* aFont) +{ + if (!mMathInitialized) { + mMathInitialized = true; + + // If UnitsPerEm is not known/valid, we can't use MATH table + if (UnitsPerEm() == kInvalidUPEM) { + return false; + } + + // We don't use AutoTable here because we'll pass ownership of this + // blob to the gfxMathTable, once we've confirmed the table exists + hb_blob_t *mathTable = GetFontTable(TRUETYPE_TAG('M','A','T','H')); + if (!mathTable) { + return false; + } + + // gfxMathTable will hb_blob_destroy() the table when it is finished + // with it. + mMathTable = new gfxMathTable(mathTable); + if (!mMathTable->HasValidHeaders()) { + mMathTable = nullptr; + return false; + } + } + + return !!mMathTable; +} + +gfxFloat +gfxFontEntry::GetMathConstant(gfxFontEntry::MathConstant aConstant) +{ + NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first."); + gfxFloat value = mMathTable->GetMathConstant(aConstant); + if (aConstant == gfxFontEntry::ScriptPercentScaleDown || + aConstant == gfxFontEntry::ScriptScriptPercentScaleDown || + aConstant == gfxFontEntry::RadicalDegreeBottomRaisePercent) { + return value / 100.0; + } + return value / mUnitsPerEm; +} + +bool +gfxFontEntry::GetMathItalicsCorrection(uint32_t aGlyphID, + gfxFloat* aItalicCorrection) +{ + NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first."); + int16_t italicCorrection; + if (!mMathTable->GetMathItalicsCorrection(aGlyphID, &italicCorrection)) { + return false; + } + *aItalicCorrection = gfxFloat(italicCorrection) / mUnitsPerEm; + return true; +} + +uint32_t +gfxFontEntry::GetMathVariantsSize(uint32_t aGlyphID, bool aVertical, + uint16_t aSize) +{ + NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first."); + return mMathTable->GetMathVariantsSize(aGlyphID, aVertical, aSize); +} + +bool +gfxFontEntry::GetMathVariantsParts(uint32_t aGlyphID, bool aVertical, + uint32_t aGlyphs[4]) +{ + NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first."); + return mMathTable->GetMathVariantsParts(aGlyphID, aVertical, aGlyphs); +} + /** * FontTableBlobData * * See FontTableHashEntry for the general strategy. */ class gfxFontEntry::FontTableBlobData { public: diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -43,16 +43,17 @@ class gfxTextRun; class gfxFont; class gfxFontFamily; class gfxFontGroup; class gfxUserFontSet; class gfxUserFontData; class gfxShapedText; class gfxShapedWord; class gfxSVGGlyphs; +class gfxMathTable; class gfxTextContextPaint; class FontInfoData; class nsILanguageAtomService; #define FONT_MAX_SIZE 2000.0 #define NO_FONT_LANGUAGE_OVERRIDE 0 @@ -308,16 +309,89 @@ public: bool GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId, gfxRect *aResult); bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, int aDrawMode, gfxTextContextPaint *aContextPaint); // Call this when glyph geometry or rendering has changed // (e.g. animated SVG glyphs) void NotifyGlyphsChanged(); + enum MathConstant { + // The order of the constants must match the order of the fields + // defined in the MATH table. + ScriptPercentScaleDown, + ScriptScriptPercentScaleDown, + DelimitedSubFormulaMinHeight, + DisplayOperatorMinHeight, + MathLeading, + AxisHeight, + AccentBaseHeight, + FlattenedAccentBaseHeight, + SubscriptShiftDown, + SubscriptTopMax, + SubscriptBaselineDropMin, + SuperscriptShiftUp, + SuperscriptShiftUpCramped, + SuperscriptBottomMin, + SuperscriptBaselineDropMax, + SubSuperscriptGapMin, + SuperscriptBottomMaxWithSubscript, + SpaceAfterScript, + UpperLimitGapMin, + UpperLimitBaselineRiseMin, + LowerLimitGapMin, + LowerLimitBaselineDropMin, + StackTopShiftUp, + StackTopDisplayStyleShiftUp, + StackBottomShiftDown, + StackBottomDisplayStyleShiftDown, + StackGapMin, + StackDisplayStyleGapMin, + StretchStackTopShiftUp, + StretchStackBottomShiftDown, + StretchStackGapAboveMin, + StretchStackGapBelowMin, + FractionNumeratorShiftUp, + FractionNumeratorDisplayStyleShiftUp, + FractionDenominatorShiftDown, + FractionDenominatorDisplayStyleShiftDown, + FractionNumeratorGapMin, + FractionNumDisplayStyleGapMin, + FractionRuleThickness, + FractionDenominatorGapMin, + FractionDenomDisplayStyleGapMin, + SkewedFractionHorizontalGap, + SkewedFractionVerticalGap, + OverbarVerticalGap, + OverbarRuleThickness, + OverbarExtraAscender, + UnderbarVerticalGap, + UnderbarRuleThickness, + UnderbarExtraDescender, + RadicalVerticalGap, + RadicalDisplayStyleVerticalGap, + RadicalRuleThickness, + RadicalExtraAscender, + RadicalKernBeforeDegree, + RadicalKernAfterDegree, + RadicalDegreeBottomRaisePercent + }; + + // Call TryGetMathTable to try to load the Open Type MATH table. The other + // functions forward the call to the gfxMathTable class. The GetMath...() + // functions MUST NOT be called unless TryGetMathTable() has returned true. + bool TryGetMathTable(gfxFont* aFont); + gfxFloat GetMathConstant(MathConstant aConstant); + bool GetMathItalicsCorrection(uint32_t aGlyphID, + gfxFloat* aItalicCorrection); + uint32_t GetMathVariantsSize(uint32_t aGlyphID, bool aVertical, + uint16_t aSize); + bool GetMathVariantsParts(uint32_t aGlyphID, bool aVertical, + uint32_t aGlyphs[4]); + virtual bool MatchesGenericFamily(const nsACString& aGeneric) const { return true; } virtual bool SupportsLangGroup(nsIAtom *aLangGroup) const { return true; } // Access to raw font table data (needed for Harfbuzz): @@ -430,16 +504,17 @@ public: bool mIsBadUnderlineFont : 1; bool mIsUserFont : 1; bool mIsLocalUserFont : 1; bool mStandardFace : 1; bool mSymbolFont : 1; bool mIgnoreGDEF : 1; bool mIgnoreGSUB : 1; bool mSVGInitialized : 1; + bool mMathInitialized : 1; bool mHasSpaceFeaturesInitialized : 1; bool mHasSpaceFeatures : 1; bool mHasSpaceFeaturesKerning : 1; bool mHasSpaceFeaturesNonKerning : 1; bool mSkipDefaultFeatureSpaceCheck : 1; bool mHasGraphiteTables : 1; bool mCheckedForGraphiteTables : 1; bool mHasCmapTable : 1; @@ -455,16 +530,17 @@ public: nsRefPtr mCharacterMap; uint32_t mUVSOffset; nsAutoArrayPtr mUVSData; nsAutoPtr mUserFontData; nsAutoPtr mSVGGlyphs; // list of gfxFonts that are using SVG glyphs nsTArray mFontsUsingSVGGlyphs; + nsAutoPtr mMathTable; nsTArray mFeatureSettings; uint32_t mLanguageOverride; protected: friend class gfxPlatformFontList; friend class gfxMacPlatformFontList; friend class gfxUserFcFontEntry; friend class gfxFontFamily; diff --git a/gfx/thebes/gfxMathTable.cpp b/gfx/thebes/gfxMathTable.cpp new file mode 100644 --- /dev/null +++ b/gfx/thebes/gfxMathTable.cpp @@ -0,0 +1,459 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gfxMathTable.h" + +#include "MathTableStructures.h" +#include "harfbuzz/hb.h" +#include + +using namespace mozilla; + +gfxMathTable::gfxMathTable(hb_blob_t* aMathTable) + : mMathTable(aMathTable) + , mGlyphConstruction(nullptr) + , mGlyphID(-1) + , mVertical(false) +{ +} + +gfxMathTable::~gfxMathTable() +{ + hb_blob_destroy(mMathTable); +} + +bool +gfxMathTable::HasValidHeaders() +{ + const char* mathData = hb_blob_get_data(mMathTable, nullptr); + // Verify the MATH table header. + if (!ValidStructure(mathData, sizeof(MATHTableHeader))) { + return false; + } + const MATHTableHeader* header = GetMATHTableHeader(); + if (uint32_t(header->mVersion) != 0x00010000 || + !ValidOffset(mathData, uint16_t(header->mMathConstants)) || + !ValidOffset(mathData, uint16_t(header->mMathGlyphInfo)) || + !ValidOffset(mathData, uint16_t(header->mMathVariants))) { + return false; + } + + // Verify the MathConstants header. + const MathConstants* mathconstants = GetMathConstants(); + const char* start = reinterpret_cast(mathconstants); + if (!ValidStructure(start, sizeof(MathConstants))) { + return false; + } + + // Verify the MathGlyphInfo header. + const MathGlyphInfo* mathglyphinfo = GetMathGlyphInfo(); + start = reinterpret_cast(mathglyphinfo); + if (!ValidStructure(start, sizeof(MathGlyphInfo))) { + return false; + } + + // Verify the MathVariants header. + const MathVariants* mathvariants = GetMathVariants(); + start = reinterpret_cast(mathvariants); + if (!ValidStructure(start, sizeof(MathVariants)) || + !ValidStructure(start, + sizeof(MathVariants) + sizeof(Offset) * + (uint16_t(mathvariants->mVertGlyphCount) + + uint16_t(mathvariants->mHorizGlyphCount))) || + !ValidOffset(start, uint16_t(mathvariants->mVertGlyphCoverage)) || + !ValidOffset(start, uint16_t(mathvariants->mHorizGlyphCoverage))) { + return false; + } + + return true; +} + +int32_t +gfxMathTable::GetMathConstant(gfxFontEntry::MathConstant aConstant) +{ + const MathConstants* mathconstants = GetMathConstants(); + + if (aConstant <= gfxFontEntry::ScriptScriptPercentScaleDown) { + return int16_t(mathconstants->mInt16[aConstant]); + } + + if (aConstant <= gfxFontEntry::DisplayOperatorMinHeight) { + return + uint16_t(mathconstants-> + mUint16[aConstant - gfxFontEntry::DelimitedSubFormulaMinHeight]); + } + + if (aConstant <= gfxFontEntry::RadicalKernAfterDegree) { + return int16_t(mathconstants-> + mMathValues[aConstant - gfxFontEntry::MathLeading].mValue); + } + + return uint16_t(mathconstants->mRadicalDegreeBottomRaisePercent); +} + +bool +gfxMathTable::GetMathItalicsCorrection(uint32_t aGlyphID, + int16_t* aItalicCorrection) +{ + const MathGlyphInfo* mathglyphinfo = GetMathGlyphInfo(); + + // Get the offset of the italic correction and verify whether it is valid. + const char* start = reinterpret_cast(mathglyphinfo); + uint16_t offset = mathglyphinfo->mMathItalicsCorrectionInfo; + if (offset == 0 || !ValidOffset(start, offset)) { + return false; + } + start += offset; + + // Verify the validity of the MathItalicsCorrectionInfo and retrieve it. + if (!ValidStructure(start, sizeof(MathItalicsCorrectionInfo))) { + return false; + } + const MathItalicsCorrectionInfo* italicsCorrectionInfo = + reinterpret_cast(start); + + // Get the coverage index for the glyph. + offset = italicsCorrectionInfo->mCoverage; + const Coverage* coverage = + reinterpret_cast(start + offset); + int32_t i = GetCoverageIndex(coverage, aGlyphID); + + // Get the ItalicsCorrection. + uint16_t count = italicsCorrectionInfo->mItalicsCorrectionCount; + if (i < 0 || i >= count) { + return false; + } + start = reinterpret_cast(italicsCorrectionInfo + 1); + if (!ValidStructure(start, count * sizeof(MathValueRecord))) { + return false; + } + const MathValueRecord* mathValueRecordArray = + reinterpret_cast(start); + + *aItalicCorrection = int16_t(mathValueRecordArray[i].mValue); + return true; +} + +uint32_t +gfxMathTable::GetMathVariantsSize(uint32_t aGlyphID, bool aVertical, + uint16_t aSize) +{ + // Select the glyph construction. + SelectGlyphConstruction(aGlyphID, aVertical); + if (!mGlyphConstruction) { + return 0; + } + + // Verify the validity of the array of the MathGlyphVariantRecord's and + // whether there is a variant of the requested size. + uint16_t count = mGlyphConstruction->mVariantCount; + const char* start = reinterpret_cast(mGlyphConstruction + 1); + if (aSize >= count || + !ValidStructure(start, count * sizeof(MathGlyphVariantRecord))) { + return 0; + } + + // Return the glyph index of the requested size variant. + const MathGlyphVariantRecord* recordArray = + reinterpret_cast(start); + return uint32_t(recordArray[aSize].mVariantGlyph); +} + +bool +gfxMathTable::GetMathVariantsParts(uint32_t aGlyphID, bool aVertical, + uint32_t aGlyphs[4]) +{ + // Get the glyph assembly corresponding to that (aGlyphID, aVertical) pair. + const GlyphAssembly* glyphAssembly = GetGlyphAssembly(aGlyphID, aVertical); + if (!glyphAssembly) { + return false; + } + + // Verify the validity of the array of GlyphPartRecord's and retrieve it. + uint16_t count = glyphAssembly->mPartCount; + const char* start = reinterpret_cast(glyphAssembly + 1); + if (!ValidStructure(start, count * sizeof(GlyphPartRecord))) { + return false; + } + const GlyphPartRecord* recordArray = + reinterpret_cast(start); + + // XXXfredw The structure of the Open Type Math table is a bit more general + // than the one currently used by the nsMathMLChar code, so we try to fallback + // in reasonable way. We use the approach of the copyComponents function in + // github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py + // + // The nsMathMLChar code can use at most 3 non extender pieces (aGlyphs[0], + // aGlyphs[1] and aGlyphs[2]) and the extenders between these pieces should + // all be the same (aGlyphs[4]). Also, the parts of vertical assembly are + // stored from bottom to top in the Open Type MATH table while they are + // stored from top to bottom in nsMathMLChar. + + // Count the number of non extender pieces + uint16_t nonExtenderCount = 0; + for (uint16_t i = 0; i < count; i++) { + if (!(uint16_t(recordArray[i].mPartFlags) & PART_FLAG_EXTENDER)) { + nonExtenderCount++; + } + } + if (nonExtenderCount > 3) { + // Not supported: too many pieces + return false; + } + + // Now browse the list of pieces + + // 0 = look for a left/bottom glyph + // 1 = look for an extender between left/bottom and mid + // 2 = look for a middle glyph + // 3 = look for an extender between middle and right/top + // 4 = look for a right/top glyph + // 5 = no more piece expected + uint8_t state = 0; + + // First extender char found. + uint32_t extenderChar = 0; + + // Clear the aGlyphs table. + memset(aGlyphs, 0, sizeof(uint32_t) * 4); + + for (uint16_t i = 0; i < count; i++) { + + bool isExtender = uint16_t(recordArray[i].mPartFlags) & PART_FLAG_EXTENDER; + uint32_t glyph = recordArray[i].mGlyph; + + if ((state == 1 || state == 2) && nonExtenderCount < 3) { + // do not try to find a middle glyph + state += 2; + } + + if (isExtender) { + if (!extenderChar) { + extenderChar = glyph; + aGlyphs[3] = extenderChar; + } else if (extenderChar != glyph) { + // Not supported: different extenders + return false; + } + + if (state == 0) { // or state == 1 + // ignore left/bottom piece and multiple successive extenders + state = 1; + } else if (state == 2) { // or state == 3 + // ignore middle piece and multiple successive extenders + state = 3; + } else if (state >= 4) { + // Not supported: unexpected extender + return false; + } + + continue; + } + + if (state == 0) { + // copy left/bottom part + aGlyphs[mVertical ? 2 : 0] = glyph; + state = 1; + continue; + } + + if (state == 1 || state == 2) { + // copy middle part + aGlyphs[1] = glyph; + state = 3; + continue; + } + + if (state == 3 || state == 4) { + // copy right/top part + aGlyphs[mVertical ? 0 : 2] = glyph; + state = 5; + } + + } + + return true; +} + +bool +gfxMathTable::ValidStructure(const char* aStart, uint16_t aSize) +{ + unsigned int mathDataLength; + const char* mathData = hb_blob_get_data(mMathTable, &mathDataLength); + return (mathData <= aStart && + aStart + aSize <= mathData + mathDataLength); +} + +bool +gfxMathTable::ValidOffset(const char* aStart, uint16_t aOffset) +{ + unsigned int mathDataLength; + const char* mathData = hb_blob_get_data(mMathTable, &mathDataLength); + return (mathData <= aStart + aOffset && + aStart + aOffset < mathData + mathDataLength); +} + +const MATHTableHeader* +gfxMathTable::GetMATHTableHeader() +{ + const char* mathData = hb_blob_get_data(mMathTable, nullptr); + return reinterpret_cast(mathData); +} + +const MathConstants* +gfxMathTable::GetMathConstants() +{ + const char* mathData = hb_blob_get_data(mMathTable, nullptr); + return + reinterpret_cast(mathData + + uint16_t(GetMATHTableHeader()-> + mMathConstants)); +} + +const MathGlyphInfo* +gfxMathTable::GetMathGlyphInfo() +{ + const char* mathData = hb_blob_get_data(mMathTable, nullptr); + return + reinterpret_cast(mathData + + uint16_t(GetMATHTableHeader()-> + mMathGlyphInfo)); +} + +const MathVariants* +gfxMathTable::GetMathVariants() +{ + const char* mathData = hb_blob_get_data(mMathTable, nullptr); + return + reinterpret_cast(mathData + + uint16_t(GetMATHTableHeader()-> + mMathVariants)); +} + +const GlyphAssembly* +gfxMathTable::GetGlyphAssembly(uint32_t aGlyphID, bool aVertical) +{ + // Select the glyph construction. + SelectGlyphConstruction(aGlyphID, aVertical); + if (!mGlyphConstruction) { + return nullptr; + } + + // Get the offset of the glyph assembly and verify whether it is valid. + const char* start = reinterpret_cast(mGlyphConstruction); + uint16_t offset = mGlyphConstruction->mGlyphAssembly; + if (offset == 0 || !ValidOffset(start, offset)) { + return nullptr; + } + start += offset; + + // Verify the validity of the GlyphAssembly and return it. + if (!ValidStructure(start, sizeof(GlyphAssembly))) { + return nullptr; + } + return reinterpret_cast(start); +} + +int32_t +gfxMathTable::GetCoverageIndex(const Coverage* aCoverage, uint32_t aGlyph) +{ + if (uint16_t(aCoverage->mFormat) == 1) { + // Coverage Format 1: list of individual glyph indices in the glyph set. + const CoverageFormat1* table = + reinterpret_cast(aCoverage); + uint16_t count = table->mGlyphCount; + const char* start = reinterpret_cast(table + 1); + if (ValidStructure(start, count * sizeof(GlyphID))) { + const GlyphID* glyphArray = + reinterpret_cast(start); + uint32_t imin = 0, imax = count; + while (imin < imax) { + uint32_t imid = (imin + imax) >> 1; + uint16_t glyphMid = glyphArray[imid]; + if (glyphMid == aGlyph) { + return imid; + } + if (glyphMid < aGlyph) { + imin = imid + 1; + } else { + imax = imid; + } + } + } + } else if (uint16_t(aCoverage->mFormat) == 2) { + // Coverage Format 2: ranges of consecutive indices. + const CoverageFormat2* table = + reinterpret_cast(aCoverage); + uint16_t count = table->mRangeCount; + const char* start = reinterpret_cast(table + 1); + if (ValidStructure(start, count * sizeof(RangeRecord))) { + const RangeRecord* rangeArray = + reinterpret_cast(start); + uint32_t imin = 0, imax = count; + while (imin < imax) { + uint32_t imid = (imin + imax) >> 1; + uint16_t rStart = rangeArray[imid].mStart; + uint16_t rEnd = rangeArray[imid].mEnd; + if (rEnd < aGlyph) { + imin = imid + 1; + } else if (aGlyph < rStart) { + imax = imid; + } else { + return (uint16_t(rangeArray[imid].mStartCoverageIndex) + + aGlyph - rStart); + } + } + } + } + return -1; +} + +void +gfxMathTable::SelectGlyphConstruction(uint32_t aGlyphID, bool aVertical) +{ + if (mGlyphID == aGlyphID && mVertical == aVertical) { + // The (glyph, direction) pair is already selected: nothing to do. + return; + } + + // Update our cached values. + mVertical = aVertical; + mGlyphID = aGlyphID; + mGlyphConstruction = nullptr; + + // Get the coverage index for the new values. + const MathVariants* mathvariants = GetMathVariants(); + const char* start = reinterpret_cast(mathvariants); + uint16_t offset = (aVertical ? + mathvariants->mVertGlyphCoverage : + mathvariants->mHorizGlyphCoverage); + const Coverage* coverage = + reinterpret_cast(start + offset); + int32_t i = GetCoverageIndex(coverage, aGlyphID); + + // Get the offset to the glyph construction. + uint16_t count = (aVertical ? + mathvariants->mVertGlyphCount : + mathvariants->mHorizGlyphCount); + start = reinterpret_cast(mathvariants + 1); + if (i < 0 || i >= count) { + return; + } + if (!aVertical) { + start += uint16_t(mathvariants->mVertGlyphCount) * sizeof(Offset); + } + if (!ValidStructure(start, count * sizeof(Offset))) { + return; + } + const Offset* offsetArray = reinterpret_cast(start); + offset = uint16_t(offsetArray[i]); + + // Make mGlyphConstruction point to the desired glyph construction. + start = reinterpret_cast(mathvariants); + if (!ValidStructure(start + offset, sizeof(MathGlyphConstruction))) { + return; + } + mGlyphConstruction = + reinterpret_cast(start + offset); +} diff --git a/gfx/thebes/gfxMathTable.h b/gfx/thebes/gfxMathTable.h new file mode 100644 --- /dev/null +++ b/gfx/thebes/gfxMathTable.h @@ -0,0 +1,122 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GFX_MATH_TABLE_H +#define GFX_MATH_TABLE_H + +#include "gfxFont.h" + +struct Coverage; +struct GlyphAssembly; +struct MATHTableHeader; +struct MathConstants; +struct MathGlyphConstruction; +struct MathGlyphInfo; +struct MathVariants; + +/** + * Used by |gfxFontEntry| to represent the MATH table of an OpenType font. + * Each |gfxFontEntry| owns at most one |gfxMathTable| instance. + */ +class gfxMathTable +{ +public: + /** + * @param aMathTable The MATH table from the OpenType font + * + * The gfxMathTable object takes over ownership of the blob references + * that are passed in, and will hb_blob_destroy() them when finished; + * the caller should -not- destroy this reference. + */ + gfxMathTable(hb_blob_t* aMathTable); + + /** + * Releases our reference to the MATH table and cleans up everything else. + */ + ~gfxMathTable(); + + /** + * Returns the value of the specified constant from the MATH table. + */ + int32_t GetMathConstant(gfxFontEntry::MathConstant aConstant); + + /** + * If the MATH table contains an italic correction for that glyph, this + * function gets the value and returns true. Otherwise it returns false. + */ + bool + GetMathItalicsCorrection(uint32_t aGlyphID, int16_t* aItalicCorrection); + + /** + * @param aGlyphID glyph index of the character we want to stretch + * @param aVertical direction of the stretching (vertical/horizontal) + * @param aSize the desired size variant + * + * Returns the glyph index of the desired size variant or 0 if there is not + * any such size variant. + */ + uint32_t GetMathVariantsSize(uint32_t aGlyphID, bool aVertical, + uint16_t aSize); + + /** + * @param aGlyphID glyph index of the character we want to stretch + * @param aVertical direction of the stretching (vertical/horizontal) + * @param aGlyphs pre-allocated buffer of 4 elements where the glyph + * indexes (or 0 for absent parts) will be stored. The parts are stored in + * the order expected by the nsMathMLChar: Top (or Left), Middle, Bottom + * (or Right), Glue. + * + * Tries to fill-in aGlyphs with the relevant glyph indexes and returns + * whether the operation was successful. The function returns false if + * there is not any assembly for the character we want to stretch or if + * the format is not supported by the nsMathMLChar code. + * + */ + bool GetMathVariantsParts(uint32_t aGlyphID, bool aVertical, + uint32_t aGlyphs[4]); + +protected: + friend class gfxFontEntry; + // This allows gfxFontEntry to verify the validity of the main headers + // before starting to use the MATH table. + bool HasValidHeaders(); + +private: + // HarfBuzz blob where the MATH table is stored. + hb_blob_t* mMathTable; + + // Cached values for the latest (mGlyphID, mVertical) pair that has been + // accessed and the corresponding glyph construction. These are verified + // by SelectGlyphConstruction and updated if necessary. + // mGlyphConstruction will be set to nullptr if no construction is defined + // for the glyph. If non-null, its mGlyphAssembly and mVariantCount fields + // may be safely read, but no further validation will have been done. + const MathGlyphConstruction* mGlyphConstruction; + uint32_t mGlyphID; + bool mVertical; + void SelectGlyphConstruction(uint32_t aGlyphID, bool aVertical); + + // Access to some structures of the MATH table. + // These accessors just return a pointer, but do NOT themselves check the + // validity of anything. Until we've checked that HasValidHeaders (which + // does validate them) returns true, they might return pointers that cannot + // even safely be dereferenced. GetGlyphAssembly may return nullptr if the + // given glyph has no assembly defined. + const MATHTableHeader* GetMATHTableHeader(); + const MathConstants* GetMathConstants(); + const MathGlyphInfo* GetMathGlyphInfo(); + const MathVariants* GetMathVariants(); + const GlyphAssembly* GetGlyphAssembly(uint32_t aGlyphID, bool aVertical); + + // Verify whether a structure or an offset belongs to the math data and can + // be read safely. + bool ValidStructure(const char* aStructStart, uint16_t aStructSize); + bool ValidOffset(const char* aOffsetStart, uint16_t aOffset); + + // Get the coverage index of a glyph index from an Open Type coverage table + // or -1 if the glyph index is not found. + int32_t GetCoverageIndex(const Coverage* aCoverage, uint32_t aGlyph); +}; + +#endif diff --git a/gfx/thebes/moz.build b/gfx/thebes/moz.build --- a/gfx/thebes/moz.build +++ b/gfx/thebes/moz.build @@ -21,16 +21,17 @@ EXPORTS += [ 'gfxFontConstants.h', 'gfxFontFeatures.h', 'gfxFontInfoLoader.h', 'gfxFontTest.h', 'gfxFontUtils.h', 'gfxGradientCache.h', 'gfxImageSurface.h', 'gfxLineSegment.h', + 'gfxMathTable.h', 'gfxMatrix.h', 'gfxPath.h', 'gfxPattern.h', 'gfxPlatform.h', 'gfxPoint.h', 'gfxPoint3D.h', 'gfxPointH3D.h', 'gfxPrefs.h', @@ -230,16 +231,17 @@ UNIFIED_SOURCES += [ 'gfxFontFeatures.cpp', 'gfxFontInfoLoader.cpp', 'gfxFontMissingGlyphs.cpp', 'gfxFontTest.cpp', 'gfxGradientCache.cpp', 'gfxGraphiteShaper.cpp', 'gfxHarfBuzzShaper.cpp', 'gfxImageSurface.cpp', + 'gfxMathTable.cpp', 'gfxMatrix.cpp', 'gfxPath.cpp', 'gfxPattern.cpp', 'gfxRect.cpp', 'gfxReusableImageSurfaceWrapper.cpp', 'gfxReusableSharedImageSurfaceWrapper.cpp', 'gfxScriptItemizer.cpp', 'gfxSkipChars.cpp',