зеркало из https://github.com/mozilla/gecko-dev.git
Bug 407059 - Part 1: Add a gfxMathTable class to read the MATH table. r=jfkthame
This commit is contained in:
Родитель
94bbbf60fd
Коммит
e9daefe35d
|
@ -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
|
|
@ -38,6 +38,7 @@
|
|||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "gfxSVGGlyphs.h"
|
||||
#include "gfxMathTable.h"
|
||||
#include "gfx2DGlue.h"
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
|
@ -112,6 +113,7 @@ gfxFontEntry::gfxFontEntry() :
|
|||
mIgnoreGDEF(false),
|
||||
mIgnoreGSUB(false),
|
||||
mSVGInitialized(false),
|
||||
mMathInitialized(false),
|
||||
mHasSpaceFeaturesInitialized(false),
|
||||
mHasSpaceFeatures(false),
|
||||
mHasSpaceFeaturesKerning(false),
|
||||
|
@ -141,6 +143,7 @@ gfxFontEntry::gfxFontEntry(const nsAString& aName, bool aIsStandardFace) :
|
|||
mIgnoreGDEF(false),
|
||||
mIgnoreGSUB(false),
|
||||
mSVGInitialized(false),
|
||||
mMathInitialized(false),
|
||||
mHasSpaceFeaturesInitialized(false),
|
||||
mHasSpaceFeatures(false),
|
||||
mHasSpaceFeaturesKerning(false),
|
||||
|
@ -389,6 +392,78 @@ gfxFontEntry::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
|
||||
*
|
||||
|
|
|
@ -48,6 +48,7 @@ class gfxUserFontData;
|
|||
class gfxShapedText;
|
||||
class gfxShapedWord;
|
||||
class gfxSVGGlyphs;
|
||||
class gfxMathTable;
|
||||
class gfxTextContextPaint;
|
||||
class FontInfoData;
|
||||
|
||||
|
@ -311,6 +312,79 @@ public:
|
|||
// (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;
|
||||
}
|
||||
|
@ -433,6 +507,7 @@ public:
|
|||
bool mIgnoreGDEF : 1;
|
||||
bool mIgnoreGSUB : 1;
|
||||
bool mSVGInitialized : 1;
|
||||
bool mMathInitialized : 1;
|
||||
bool mHasSpaceFeaturesInitialized : 1;
|
||||
bool mHasSpaceFeatures : 1;
|
||||
bool mHasSpaceFeaturesKerning : 1;
|
||||
|
@ -458,6 +533,7 @@ public:
|
|||
nsAutoPtr<gfxSVGGlyphs> mSVGGlyphs;
|
||||
// list of gfxFonts that are using SVG glyphs
|
||||
nsTArray<gfxFont*> mFontsUsingSVGGlyphs;
|
||||
nsAutoPtr<gfxMathTable> mMathTable;
|
||||
nsTArray<gfxFontFeature> mFeatureSettings;
|
||||
uint32_t mLanguageOverride;
|
||||
|
||||
|
|
|
@ -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 <algorithm>
|
||||
|
||||
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<const char*>(mathconstants);
|
||||
if (!ValidStructure(start, sizeof(MathConstants))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify the MathGlyphInfo header.
|
||||
const MathGlyphInfo* mathglyphinfo = GetMathGlyphInfo();
|
||||
start = reinterpret_cast<const char*>(mathglyphinfo);
|
||||
if (!ValidStructure(start, sizeof(MathGlyphInfo))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify the MathVariants header.
|
||||
const MathVariants* mathvariants = GetMathVariants();
|
||||
start = reinterpret_cast<const char*>(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<const char*>(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<const MathItalicsCorrectionInfo*>(start);
|
||||
|
||||
// Get the coverage index for the glyph.
|
||||
offset = italicsCorrectionInfo->mCoverage;
|
||||
const Coverage* coverage =
|
||||
reinterpret_cast<const Coverage*>(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<const char*>(italicsCorrectionInfo + 1);
|
||||
if (!ValidStructure(start, count * sizeof(MathValueRecord))) {
|
||||
return false;
|
||||
}
|
||||
const MathValueRecord* mathValueRecordArray =
|
||||
reinterpret_cast<const MathValueRecord*>(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<const char*>(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<const MathGlyphVariantRecord*>(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<const char*>(glyphAssembly + 1);
|
||||
if (!ValidStructure(start, count * sizeof(GlyphPartRecord))) {
|
||||
return false;
|
||||
}
|
||||
const GlyphPartRecord* recordArray =
|
||||
reinterpret_cast<const GlyphPartRecord*>(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<const MATHTableHeader*>(mathData);
|
||||
}
|
||||
|
||||
const MathConstants*
|
||||
gfxMathTable::GetMathConstants()
|
||||
{
|
||||
const char* mathData = hb_blob_get_data(mMathTable, nullptr);
|
||||
return
|
||||
reinterpret_cast<const MathConstants*>(mathData +
|
||||
uint16_t(GetMATHTableHeader()->
|
||||
mMathConstants));
|
||||
}
|
||||
|
||||
const MathGlyphInfo*
|
||||
gfxMathTable::GetMathGlyphInfo()
|
||||
{
|
||||
const char* mathData = hb_blob_get_data(mMathTable, nullptr);
|
||||
return
|
||||
reinterpret_cast<const MathGlyphInfo*>(mathData +
|
||||
uint16_t(GetMATHTableHeader()->
|
||||
mMathGlyphInfo));
|
||||
}
|
||||
|
||||
const MathVariants*
|
||||
gfxMathTable::GetMathVariants()
|
||||
{
|
||||
const char* mathData = hb_blob_get_data(mMathTable, nullptr);
|
||||
return
|
||||
reinterpret_cast<const MathVariants*>(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<const char*>(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<const GlyphAssembly*>(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<const CoverageFormat1*>(aCoverage);
|
||||
uint16_t count = table->mGlyphCount;
|
||||
const char* start = reinterpret_cast<const char*>(table + 1);
|
||||
if (ValidStructure(start, count * sizeof(GlyphID))) {
|
||||
const GlyphID* glyphArray =
|
||||
reinterpret_cast<const GlyphID*>(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<const CoverageFormat2*>(aCoverage);
|
||||
uint16_t count = table->mRangeCount;
|
||||
const char* start = reinterpret_cast<const char*>(table + 1);
|
||||
if (ValidStructure(start, count * sizeof(RangeRecord))) {
|
||||
const RangeRecord* rangeArray =
|
||||
reinterpret_cast<const RangeRecord*>(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<const char*>(mathvariants);
|
||||
uint16_t offset = (aVertical ?
|
||||
mathvariants->mVertGlyphCoverage :
|
||||
mathvariants->mHorizGlyphCoverage);
|
||||
const Coverage* coverage =
|
||||
reinterpret_cast<const Coverage*>(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<const char*>(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<const Offset*>(start);
|
||||
offset = uint16_t(offsetArray[i]);
|
||||
|
||||
// Make mGlyphConstruction point to the desired glyph construction.
|
||||
start = reinterpret_cast<const char*>(mathvariants);
|
||||
if (!ValidStructure(start + offset, sizeof(MathGlyphConstruction))) {
|
||||
return;
|
||||
}
|
||||
mGlyphConstruction =
|
||||
reinterpret_cast<const MathGlyphConstruction*>(start + offset);
|
||||
}
|
|
@ -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
|
|
@ -26,6 +26,7 @@ EXPORTS += [
|
|||
'gfxGradientCache.h',
|
||||
'gfxImageSurface.h',
|
||||
'gfxLineSegment.h',
|
||||
'gfxMathTable.h',
|
||||
'gfxMatrix.h',
|
||||
'gfxPath.h',
|
||||
'gfxPattern.h',
|
||||
|
@ -235,6 +236,7 @@ UNIFIED_SOURCES += [
|
|||
'gfxGraphiteShaper.cpp',
|
||||
'gfxHarfBuzzShaper.cpp',
|
||||
'gfxImageSurface.cpp',
|
||||
'gfxMathTable.cpp',
|
||||
'gfxMatrix.cpp',
|
||||
'gfxPath.cpp',
|
||||
'gfxPattern.cpp',
|
||||
|
|
Загрузка…
Ссылка в новой задаче