diff options
Diffstat (limited to '')
-rw-r--r-- | gfx/thebes/gfxMathTable.cpp | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/gfx/thebes/gfxMathTable.cpp b/gfx/thebes/gfxMathTable.cpp new file mode 100644 index 0000000000..6a27efff21 --- /dev/null +++ b/gfx/thebes/gfxMathTable.cpp @@ -0,0 +1,200 @@ +/* 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 "harfbuzz/hb.h" +#include "harfbuzz/hb-ot.h" + +#define FloatToFixed(f) (65536 * (f)) +#define FixedToFloat(f) ((f) * (1.0 / 65536.0)) + +using namespace mozilla; + +gfxMathTable::gfxMathTable(hb_face_t* aFace, gfxFloat aSize) { + mMathVariantCache.vertical = false; + mHBFont = hb_font_create(aFace); + if (mHBFont) { + hb_font_set_ppem(mHBFont, aSize, aSize); + uint32_t scale = FloatToFixed(aSize); + hb_font_set_scale(mHBFont, scale, scale); + } + + mMathVariantCache.glyphID = 0; + ClearCache(); +} + +gfxMathTable::~gfxMathTable() { + if (mHBFont) { + hb_font_destroy(mHBFont); + } +} + +gfxFloat gfxMathTable::Constant(MathConstant aConstant) const { + int32_t value = hb_ot_math_get_constant( + mHBFont, static_cast<hb_ot_math_constant_t>(aConstant)); + if (aConstant == ScriptPercentScaleDown || + aConstant == ScriptScriptPercentScaleDown || + aConstant == RadicalDegreeBottomRaisePercent) { + return value / 100.0; + } + return FixedToFloat(value); +} + +gfxFloat gfxMathTable::ItalicsCorrection(uint32_t aGlyphID) const { + return FixedToFloat( + hb_ot_math_get_glyph_italics_correction(mHBFont, aGlyphID)); +} + +uint32_t gfxMathTable::VariantsSize(uint32_t aGlyphID, bool aVertical, + uint16_t aSize) const { + UpdateMathVariantCache(aGlyphID, aVertical); + if (aSize < kMaxCachedSizeCount) { + return mMathVariantCache.sizes[aSize]; + } + + // If the size index exceeds the cache size, we just read the value with + // hb_ot_math_get_glyph_variants. + hb_direction_t direction = aVertical ? HB_DIRECTION_BTT : HB_DIRECTION_LTR; + hb_ot_math_glyph_variant_t variant; + unsigned int count = 1; + hb_ot_math_get_glyph_variants(mHBFont, aGlyphID, direction, aSize, &count, + &variant); + return count > 0 ? variant.glyph : 0; +} + +bool gfxMathTable::VariantsParts(uint32_t aGlyphID, bool aVertical, + uint32_t aGlyphs[4]) const { + UpdateMathVariantCache(aGlyphID, aVertical); + memcpy(aGlyphs, mMathVariantCache.parts, sizeof(mMathVariantCache.parts)); + return mMathVariantCache.arePartsValid; +} + +void gfxMathTable::ClearCache() const { + memset(mMathVariantCache.sizes, 0, sizeof(mMathVariantCache.sizes)); + memset(mMathVariantCache.parts, 0, sizeof(mMathVariantCache.parts)); + mMathVariantCache.arePartsValid = false; +} + +void gfxMathTable::UpdateMathVariantCache(uint32_t aGlyphID, + bool aVertical) const { + if (aGlyphID == mMathVariantCache.glyphID && + aVertical == mMathVariantCache.vertical) + return; + + mMathVariantCache.glyphID = aGlyphID; + mMathVariantCache.vertical = aVertical; + ClearCache(); + + // Cache the first size variants. + hb_direction_t direction = aVertical ? HB_DIRECTION_BTT : HB_DIRECTION_LTR; + hb_ot_math_glyph_variant_t variant[kMaxCachedSizeCount]; + unsigned int count = kMaxCachedSizeCount; + hb_ot_math_get_glyph_variants(mHBFont, aGlyphID, direction, 0, &count, + variant); + for (unsigned int i = 0; i < count; i++) { + mMathVariantCache.sizes[i] = variant[i].glyph; + } + + // Try and cache the parts of the glyph assembly. + // 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. + + hb_ot_math_glyph_part_t parts[5]; + count = MOZ_ARRAY_LENGTH(parts); + unsigned int offset = 0; + if (hb_ot_math_get_glyph_assembly(mHBFont, aGlyphID, direction, offset, + &count, parts, + NULL) > MOZ_ARRAY_LENGTH(parts)) + return; // Not supported: Too many pieces. + if (count <= 0) return; // Not supported: No pieces. + + // Count the number of non extender pieces + uint16_t nonExtenderCount = 0; + for (uint16_t i = 0; i < count; i++) { + if (!(parts[i].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER)) { + nonExtenderCount++; + } + } + if (nonExtenderCount > 3) { + // Not supported: too many pieces + return; + } + + // 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; + + for (uint16_t i = 0; i < count; i++) { + bool isExtender = parts[i].flags & HB_MATH_GLYPH_PART_FLAG_EXTENDER; + uint32_t glyph = parts[i].glyph; + + if ((state == 1 || state == 2) && nonExtenderCount < 3) { + // do not try to find a middle glyph + state += 2; + } + + if (isExtender) { + if (!extenderChar) { + extenderChar = glyph; + mMathVariantCache.parts[3] = extenderChar; + } else if (extenderChar != glyph) { + // Not supported: different extenders + return; + } + + 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; + } + + continue; + } + + if (state == 0) { + // copy left/bottom part + mMathVariantCache.parts[aVertical ? 2 : 0] = glyph; + state = 1; + continue; + } + + if (state == 1 || state == 2) { + // copy middle part + mMathVariantCache.parts[1] = glyph; + state = 3; + continue; + } + + if (state == 3 || state == 4) { + // copy right/top part + mMathVariantCache.parts[aVertical ? 0 : 2] = glyph; + state = 5; + } + } + + mMathVariantCache.arePartsValid = true; +} |