summaryrefslogtreecommitdiffstats
path: root/gfx/thebes/gfxMathTable.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/thebes/gfxMathTable.cpp')
-rw-r--r--gfx/thebes/gfxMathTable.cpp200
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;
+}