Bug 407059 - Part 2: Make nsMathMLChar use the MATH table. r=karlt
☠☠ backed out by 545be5328235 ☠ ☠
authorFrédéric Wang <fred.wang@free.fr>
Tue, 22 Apr 2014 08:44:03 -0400
changeset 199078 cfcbc731d4db583918fe3131bf14466337e394e6
parent 199077 d8244f3ecdcbd49290c039f45d7be35dacdc8b20
child 199079 77593dd1b044f171e967afb6c36e70da77048929
push id486
push userasasaki@mozilla.com
push dateMon, 14 Jul 2014 18:39:42 +0000
treeherdermozilla-release@d33428174ff1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt
bugs407059
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 407059 - Part 2: Make nsMathMLChar use the MATH table. r=karlt
layout/mathml/nsMathMLChar.cpp
layout/mathml/nsMathMLChar.h
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -23,58 +23,103 @@
 #include "nsCSSRendering.h"
 #include "prprf.h"         // For PR_snprintf()
 
 #include "nsDisplayList.h"
 
 #include "nsMathMLOperators.h"
 #include <algorithm>
 
+#include "gfxMathTable.h"
+
 using namespace mozilla;
 
 //#define NOISY_SEARCH 1
 
+static const float kLargeOpFactor = float(M_SQRT2);
+static const float kIntegralFactor = 2.0;
+
 // -----------------------------------------------------------------------------
 static const nsGlyphCode kNullGlyph = {{{0, 0}}, 0};
 typedef enum {eExtension_base, eExtension_variants, eExtension_parts}
   nsMathfontPrefExtension;
 
 // -----------------------------------------------------------------------------
 // nsGlyphTable is a class that provides an interface for accessing glyphs
 // of stretchy chars. It acts like a table that stores the variants of bigger
 // sizes (if any) and the partial glyphs needed to build extensible symbols.
-// An instance of nsGlyphTable is associated to one primary font. Extra glyphs
-// can be taken in other additional fonts when stretching certain characters.
-// These supplementary fonts are referred to as "external" fonts to the table.
 //
-// Bigger sizes (if any) of the char can then be retrieved with
-// BigOf(aSize). Partial glyphs can be retrieved with ElementAt()
+// Bigger sizes (if any) of the char can then be retrieved with BigOf(...).
+// Partial glyphs can be retrieved with ElementAt(...).
 //
 // A table consists of "nsGlyphCode"s which are viewed either as Unicode
-// points or as direct glyph indices, depending on the type of the table.
-// XXX The latter is not yet supported.
+// points (for nsPropertiesTable) or as direct glyph indices (for
+// nsOpenTypeTable)
+// -----------------------------------------------------------------------------
+
+class nsGlyphTable {
+public:
+  virtual ~nsGlyphTable() {}
+
+  virtual const nsAString&
+  FontNameFor(const nsGlyphCode& aGlyphCode) const = 0;
+
+  // Getters for the parts
+  virtual nsGlyphCode ElementAt(gfxContext*   aThebesContext,
+                                int32_t       aAppUnitsPerDevPixel,
+                                gfxFontGroup* aFontGroup,
+                                char16_t      aChar,
+                                bool          aVertical,
+                                uint32_t      aPosition) = 0;
+  virtual nsGlyphCode BigOf(gfxContext*   aThebesContext,
+                            int32_t       aAppUnitsPerDevPixel,
+                            gfxFontGroup* aFontGroup,
+                            char16_t      aChar,
+                            bool          aVertical,
+                            uint32_t      aSize) = 0;
+
+  // True if this table contains parts to render this char
+  virtual bool HasPartsOf(gfxContext*   aThebesContext,
+                          int32_t       aAppUnitsPerDevPixel,
+                          gfxFontGroup* aFontGroup,
+                          char16_t      aChar,
+                          bool          aVertical) = 0;
+
+  virtual gfxTextRun* MakeTextRun(gfxContext*        aThebesContext,
+                                  int32_t            aAppUnitsPerDevPixel,
+                                  gfxFontGroup*      aFontGroup,
+                                  const nsGlyphCode& aGlyph) = 0;
+protected:
+  nsGlyphTable() : mCharCache(0) {}
+  // For speedy re-use, we always cache the last data used in the table.
+  // mCharCache is the Unicode point of the last char that was queried in this
+  // table.
+  char16_t mCharCache;
+};
+
+// An instance of nsPropertiesTable is associated with one primary font. Extra
+// glyphs can be taken in other additional fonts when stretching certain
+// characters.
+// These supplementary fonts are referred to as "external" fonts to the table.
 
 // General format of MathFont Property Files from which glyph data are
 // retrieved:
 // -----------------------------------------------------------------------------
 // Each font should have its set of glyph data. For example, the glyph data for
 // the "Symbol" font and the "MT Extra" font are in "mathfontSymbol.properties"
 // and "mathfontMTExtra.properties", respectively. The mathfont property file
 // is a set of all the stretchy MathML characters that can be rendered with that
 // font using larger and/or partial glyphs. The entry of each stretchy character
 // in the mathfont property file gives, in that order, the 4 partial glyphs:
 // Top (or Left), Middle, Bottom (or Right), Glue; and the variants of bigger
 // sizes (if any).
 // A position that is not relevant to a particular character is indicated there
 // with the UNICODE REPLACEMENT CHARACTER 0xFFFD.
 // -----------------------------------------------------------------------------
 
-#define NS_TABLE_TYPE_UNICODE       0
-#define NS_TABLE_TYPE_GLYPH_INDEX   1
-
 #define NS_TABLE_STATE_ERROR       -1
 #define NS_TABLE_STATE_EMPTY        0
 #define NS_TABLE_STATE_READY        1
 
 // helper to trim off comments from data in a MathFont Property File
 static void
 Clean(nsString& aValue)
 {
@@ -93,101 +138,124 @@ LoadProperties(const nsString& aName,
   uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
   uriStr.Append(aName);
   uriStr.StripWhitespace(); // that may come from aName
   uriStr.AppendLiteral(".properties");
   return NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(aProperties), 
                                                 NS_ConvertUTF16toUTF8(uriStr));
 }
 
-// -----------------------------------------------------------------------------
-
-class nsGlyphTable {
+class nsPropertiesTable MOZ_FINAL : public nsGlyphTable {
 public:
-  explicit nsGlyphTable(const nsString& aPrimaryFontName)
-    : mFontName(1), // ensure space for primary font name.
-      mState(NS_TABLE_STATE_EMPTY),
-      mCharCache(0)
+  explicit nsPropertiesTable(const nsString& aPrimaryFontName)
+    : mFontName(1) // ensure space for primary font name.
+    , mState(NS_TABLE_STATE_EMPTY)
   {
-    MOZ_COUNT_CTOR(nsGlyphTable);
+    MOZ_COUNT_CTOR(nsPropertiesTable);
     mFontName.AppendElement(aPrimaryFontName);
   }
 
-  // not a virtual destructor: this class is not intended to be subclassed
-  ~nsGlyphTable()
+  ~nsPropertiesTable()
   {
-    MOZ_COUNT_DTOR(nsGlyphTable);
+    MOZ_COUNT_DTOR(nsPropertiesTable);
   }
 
   const nsAString& PrimaryFontName() const
   {
     return mFontName[0];
   }
 
-  const nsAString& FontNameFor(const nsGlyphCode& aGlyphCode) const
+  const nsAString&
+  FontNameFor(const nsGlyphCode& aGlyphCode) const MOZ_OVERRIDE
   {
     NS_ASSERTION(!aGlyphCode.IsGlyphID(),
-                 "nsGlyphTable can only access glyphs by Unicode code point");
+                 "nsPropertiesTable can only access glyphs by code point");
     return mFontName[aGlyphCode.font];
   }
 
-  // Getters for the parts
-  nsGlyphCode ElementAt(char16_t aChar, uint32_t aPosition);
-  nsGlyphCode BigOf(char16_t aChar, int32_t aSize) {
-    return ElementAt(aChar, 4 + aSize);
+  virtual nsGlyphCode ElementAt(gfxContext*   aThebesContext,
+                                int32_t       aAppUnitsPerDevPixel,
+                                gfxFontGroup* aFontGroup,
+                                char16_t      aChar,
+                                bool          aVertical,
+                                uint32_t      aPosition) MOZ_OVERRIDE;
+
+  virtual nsGlyphCode BigOf(gfxContext*   aThebesContext,
+                            int32_t       aAppUnitsPerDevPixel,
+                            gfxFontGroup* aFontGroup,
+                            char16_t      aChar,
+                            bool          aVertical,
+                            uint32_t      aSize) MOZ_OVERRIDE
+  {
+    return ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
+                     aChar, aVertical, 4 + aSize);
   }
 
-  // True if this table contains parts to render this char
-  bool HasPartsOf(char16_t aChar) {
-    return (ElementAt(aChar, 0).Exists() || ElementAt(aChar, 1).Exists() ||
-            ElementAt(aChar, 2).Exists() || ElementAt(aChar, 3).Exists());
+  virtual bool HasPartsOf(gfxContext*   aThebesContext,
+                          int32_t       aAppUnitsPerDevPixel,
+                          gfxFontGroup* aFontGroup,
+                          char16_t      aChar,
+                          bool          aVertical) MOZ_OVERRIDE
+  {
+    return (ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
+                      aChar, aVertical, 0).Exists() ||
+            ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
+                      aChar, aVertical, 1).Exists() ||
+            ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
+                      aChar, aVertical, 2).Exists() ||
+            ElementAt(aThebesContext, aAppUnitsPerDevPixel, aFontGroup,
+                      aChar, aVertical, 3).Exists());
   }
 
-  gfxTextRun* MakeTextRun(gfxContext*        aThebesContext,
-                          int32_t            aAppUnitsPerDevPixel,
-                          gfxFontGroup*      aFontGroup,
-                          const nsGlyphCode& aGlyph);
+  virtual gfxTextRun* MakeTextRun(gfxContext*        aThebesContext,
+                                  int32_t            aAppUnitsPerDevPixel,
+                                  gfxFontGroup*      aFontGroup,
+                                  const nsGlyphCode& aGlyph) MOZ_OVERRIDE;
 private:
 
   // mFontName[0] is the primary font associated to this table. The others 
   // are possible "external" fonts for glyphs not in the primary font
   // but which are needed to stretch certain characters in the table
   nsTArray<nsString> mFontName;
 
   // Tri-state variable for error/empty/ready
   int32_t mState;
 
   // The set of glyph data in this table, as provided by the MathFont Property
   // File
   nsCOMPtr<nsIPersistentProperties> mGlyphProperties;
 
-  // For speedy re-use, we always cache the last data used in the table.
-  // mCharCache is the Unicode point of the last char that was queried in this
-  // table. mGlyphCache is a buffer containing the glyph data associated to
-  // that char. For a property line 'key = value' in the MathFont Property File,
+  // mGlyphCache is a buffer containing the glyph data associated with
+  // mCharCache.
+  // For a property line 'key = value' in the MathFont Property File,
   // mCharCache will retain the 'key' -- which is a Unicode point, while
   // mGlyphCache will retain the 'value', which is a consecutive list of
   // nsGlyphCodes, i.e., the pairs of 'code@font' needed by the char -- in
   // which 'code@0' can be specified
   // without the optional '@0'. However, to ease subsequent processing,
   // mGlyphCache excludes the '@' symbol and explicitly inserts all optional '0'
   // that indicates the primary font identifier. Specifically therefore, the
   // k-th glyph is characterized by :
   // 1) mGlyphCache[3*k],mGlyphCache[3*k+1] : its Unicode point
   // 2) mGlyphCache[3*k+2] : the numeric identifier of the font where it comes
   // from.
   // A font identifier of '0' means the default primary font associated to this
   // table. Other digits map to the "external" fonts that may have been
   // specified in the MathFont Property File.
   nsString  mGlyphCache;
-  char16_t mCharCache;
 };
 
+/* virtual */
 nsGlyphCode
-nsGlyphTable::ElementAt(char16_t aChar, uint32_t aPosition)
+nsPropertiesTable::ElementAt(gfxContext*   /* aThebesContext */,
+                             int32_t       /* aAppUnitsPerDevPixel */,
+                             gfxFontGroup* /* aFontGroup */,
+                             char16_t      aChar,
+                             bool          /* aVertical */,
+                             uint32_t      aPosition)
 {
   if (mState == NS_TABLE_STATE_ERROR) return kNullGlyph;
   // Load glyph properties if this is the first time we have been here
   if (mState == NS_TABLE_STATE_EMPTY) {
     nsresult rv = LoadProperties(mFontName[0], mGlyphProperties);
 #ifdef DEBUG
     nsAutoCString uriStr;
     uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
@@ -281,42 +349,229 @@ nsGlyphTable::ElementAt(char16_t aChar, 
   if (index+2 >= mGlyphCache.Length()) return kNullGlyph;
   nsGlyphCode ch;
   ch.code[0] = mGlyphCache.CharAt(index);
   ch.code[1] = mGlyphCache.CharAt(index + 1);
   ch.font = mGlyphCache.CharAt(index + 2);
   return ch.code[0] == char16_t(0xFFFD) ? kNullGlyph : ch;
 }
 
+/* virtual */
 gfxTextRun*
-nsGlyphTable::MakeTextRun(gfxContext*        aThebesContext,
-                          int32_t            aAppUnitsPerDevPixel,
-                          gfxFontGroup*      aFontGroup,
-                          const nsGlyphCode& aGlyph)
+nsPropertiesTable::MakeTextRun(gfxContext*        aThebesContext,
+                               int32_t            aAppUnitsPerDevPixel,
+                               gfxFontGroup*      aFontGroup,
+                               const nsGlyphCode& aGlyph)
 {
-  NS_ASSERTION(!aGlyph.IsGlyphID(), "not yet implemented");
+  NS_ASSERTION(!aGlyph.IsGlyphID(),
+               "nsPropertiesTable can only access glyphs by code point");
   return aFontGroup->
     MakeTextRun(aGlyph.code, aGlyph.Length(), aThebesContext,
                 aAppUnitsPerDevPixel, 0);
 }
 
+// An instance of nsOpenTypeTable is associated with one gfxFontEntry that
+// corresponds to an Open Type font with a MATH table. All the glyphs come from
+// the same font and the calls to access size variants and parts are directly
+// forwarded to the gfx code.
+class nsOpenTypeTable MOZ_FINAL : public nsGlyphTable {
+public:
+  ~nsOpenTypeTable()
+  {
+    MOZ_COUNT_DTOR(nsOpenTypeTable);
+  }
+
+  virtual nsGlyphCode ElementAt(gfxContext*   aThebesContext,
+                                int32_t       aAppUnitsPerDevPixel,
+                                gfxFontGroup* aFontGroup,
+                                char16_t      aChar,
+                                bool          aVertical,
+                                uint32_t      aPosition) MOZ_OVERRIDE;
+  virtual nsGlyphCode BigOf(gfxContext*   aThebesContext,
+                            int32_t       aAppUnitsPerDevPixel,
+                            gfxFontGroup* aFontGroup,
+                            char16_t      aChar,
+                            bool          aVertical,
+                            uint32_t      aSize) MOZ_OVERRIDE;
+  virtual bool HasPartsOf(gfxContext*   aThebesContext,
+                          int32_t       aAppUnitsPerDevPixel,
+                          gfxFontGroup* aFontGroup,
+                          char16_t      aChar,
+                          bool          aVertical) MOZ_OVERRIDE;
+
+  const nsAString&
+  FontNameFor(const nsGlyphCode& aGlyphCode) const MOZ_OVERRIDE {
+    NS_ASSERTION(aGlyphCode.IsGlyphID(),
+                 "nsOpenTypeTable can only access glyphs by id");
+    return mFontEntry->FamilyName();
+  }
+
+  virtual gfxTextRun* MakeTextRun(gfxContext*        aThebesContext,
+                                  int32_t            aAppUnitsPerDevPixel,
+                                  gfxFontGroup*      aFontGroup,
+                                  const nsGlyphCode& aGlyph) MOZ_OVERRIDE;
+
+  // This returns a new OpenTypeTable instance to give access to OpenType MATH
+  // table or nullptr if the font does not have such table. Ownership is passed
+  // to the caller.
+  static nsOpenTypeTable* Create(gfxFont* aFont)
+  {
+    if (!aFont->GetFontEntry()->TryGetMathTable(aFont)) {
+      return nullptr;
+    }
+    return new nsOpenTypeTable(aFont->GetFontEntry());
+  }
+
+private:
+  nsRefPtr<gfxFontEntry> mFontEntry;
+  uint32_t mGlyphID;
+
+  explicit nsOpenTypeTable(gfxFontEntry* aFontEntry)
+    : mFontEntry(aFontEntry) {
+    MOZ_COUNT_CTOR(nsOpenTypeTable);
+  }
+
+  void UpdateCache(gfxContext*   aThebesContext,
+                   int32_t       aAppUnitsPerDevPixel,
+                   gfxFontGroup* aFontGroup,
+                   char16_t      aChar);
+};
+
+void
+nsOpenTypeTable::UpdateCache(gfxContext*   aThebesContext,
+                             int32_t       aAppUnitsPerDevPixel,
+                             gfxFontGroup* aFontGroup,
+                             char16_t      aChar)
+{
+  if (mCharCache != aChar) {
+    nsAutoPtr<gfxTextRun> textRun;
+    textRun = aFontGroup->
+      MakeTextRun(&aChar, 1, aThebesContext, aAppUnitsPerDevPixel, 0);
+    const gfxTextRun::CompressedGlyph& data = textRun->GetCharacterGlyphs()[0];
+    if (data.IsSimpleGlyph()) {
+      mGlyphID = data.GetSimpleGlyph();
+    } else if (data.GetGlyphCount() == 1) {
+      mGlyphID = textRun->GetDetailedGlyphs(0)->mGlyphID;
+    } else {
+      mGlyphID = 0;
+    }
+    mCharCache = aChar;
+  }
+}
+
+/* virtual */
+nsGlyphCode
+nsOpenTypeTable::ElementAt(gfxContext*   aThebesContext,
+                           int32_t       aAppUnitsPerDevPixel,
+                           gfxFontGroup* aFontGroup,
+                           char16_t      aChar,
+                           bool          aVertical,
+                           uint32_t      aPosition)
+{
+  UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar);
+
+  uint32_t parts[4];
+  if (!mFontEntry->GetMathVariantsParts(mGlyphID, aVertical, parts)) {
+    return kNullGlyph;
+  }
+
+  uint32_t glyphID = parts[aPosition];
+  if (!glyphID) {
+    return kNullGlyph;
+  }
+  nsGlyphCode glyph;
+  glyph.glyphID = glyphID;
+  glyph.font = -1;
+  return glyph;
+}
+
+/* virtual */
+nsGlyphCode
+nsOpenTypeTable::BigOf(gfxContext*   aThebesContext,
+                       int32_t       aAppUnitsPerDevPixel,
+                       gfxFontGroup* aFontGroup,
+                       char16_t      aChar,
+                       bool          aVertical,
+                       uint32_t      aSize)
+{
+  UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar);
+
+  uint32_t glyphID =
+    mFontEntry->GetMathVariantsSize(mGlyphID, aVertical, aSize);
+  if (!glyphID) {
+    return kNullGlyph;
+  }
+
+  nsGlyphCode glyph;
+  glyph.glyphID = glyphID;
+  glyph.font = -1;
+  return glyph;
+}
+
+/* virtual */
+bool
+nsOpenTypeTable::HasPartsOf(gfxContext*   aThebesContext,
+                            int32_t       aAppUnitsPerDevPixel,
+                            gfxFontGroup* aFontGroup,
+                            char16_t      aChar,
+                            bool          aVertical)
+{
+  UpdateCache(aThebesContext, aAppUnitsPerDevPixel, aFontGroup, aChar);
+
+  uint32_t parts[4];
+  if (!mFontEntry->GetMathVariantsParts(mGlyphID, aVertical, parts)) {
+    return false;
+  }
+
+  return parts[0] || parts[1] || parts[2] || parts[3];
+}
+
+/* virtual */
+gfxTextRun*
+nsOpenTypeTable::MakeTextRun(gfxContext*        aThebesContext,
+                             int32_t            aAppUnitsPerDevPixel,
+                             gfxFontGroup*      aFontGroup,
+                             const nsGlyphCode& aGlyph)
+{
+  NS_ASSERTION(aGlyph.IsGlyphID(),
+               "nsOpenTypeTable can only access glyphs by id");
+
+  gfxTextRunFactory::Parameters params = {
+    aThebesContext, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
+  };
+  gfxTextRun* textRun = gfxTextRun::Create(&params, 1, aFontGroup, 0);
+  textRun->AddGlyphRun(aFontGroup->GetFontAt(0), gfxTextRange::kFontGroup, 0,
+                       false);
+  gfxTextRun::DetailedGlyph detailedGlyph;
+  detailedGlyph.mGlyphID = aGlyph.glyphID;
+  // We set the advance width to zero and this will be fixed in MeasureTextRun.
+  // XXXfredw: We should use gfxHarfbuzzShaper::GetGlyphHAdvance()
+  detailedGlyph.mAdvance = 0;
+  detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
+  gfxShapedText::CompressedGlyph g;
+  g.SetComplex(true, true, 1);
+  textRun->SetGlyphs(0, g, &detailedGlyph);
+
+  return textRun;
+}
+
 // -----------------------------------------------------------------------------
 // This is the list of all the applicable glyph tables.
 // We will maintain a single global instance that will only reveal those
 // glyph tables that are associated to fonts currently installed on the
 // user' system. The class is an XPCOM shutdown observer to allow us to
 // free its allocated data at shutdown
 
 class nsGlyphTableList : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
-  nsGlyphTable mUnicodeTable;
+  nsPropertiesTable mUnicodeTable;
 
   nsGlyphTableList()
     : mUnicodeTable(NS_LITERAL_STRING("Unicode"))
   {
     MOZ_COUNT_CTOR(nsGlyphTableList);
   }
 
   virtual ~nsGlyphTableList()
@@ -331,25 +586,24 @@ public:
   nsGlyphTable*
   AddGlyphTable(const nsString& aPrimaryFontName);
 
   // Find the glyph table in the list corresponding to the given font family.
   nsGlyphTable*
   GetGlyphTableFor(const nsAString& aFamily);
 
 private:
-  nsGlyphTable* TableAt(int32_t aIndex) {
-    return &mTableList.ElementAt(aIndex);
+  nsPropertiesTable* PropertiesTableAt(int32_t aIndex) {
+    return &mPropertiesTableList.ElementAt(aIndex);
   }
-  int32_t Count() {
-    return mTableList.Length();
+  int32_t PropertiesTableCount() {
+    return mPropertiesTableList.Length();
   }
-
   // List of glyph tables;
-  nsTArray<nsGlyphTable> mTableList;
+  nsTArray<nsPropertiesTable> mPropertiesTableList;
 };
 
 NS_IMPL_ISUPPORTS1(nsGlyphTableList, nsIObserver)
 
 // -----------------------------------------------------------------------------
 // Here is the global list of applicable glyph tables that we will be using
 static nsGlyphTableList* gGlyphTableList = nullptr;
 
@@ -400,25 +654,25 @@ nsGlyphTable*
 nsGlyphTableList::AddGlyphTable(const nsString& aPrimaryFontName)
 {
   // See if there is already a special table for this family.
   nsGlyphTable* glyphTable = GetGlyphTableFor(aPrimaryFontName);
   if (glyphTable != &mUnicodeTable)
     return glyphTable;
 
   // allocate a table
-  glyphTable = mTableList.AppendElement(aPrimaryFontName);
+  glyphTable = mPropertiesTableList.AppendElement(aPrimaryFontName);
   return glyphTable;
 }
 
 nsGlyphTable*
 nsGlyphTableList::GetGlyphTableFor(const nsAString& aFamily)
 {
-  for (int32_t i = 0; i < Count(); i++) {
-    nsGlyphTable* glyphTable = TableAt(i);
+  for (int32_t i = 0; i < PropertiesTableCount(); i++) {
+    nsPropertiesTable* glyphTable = PropertiesTableAt(i);
     const nsAString& fontName = glyphTable->PrimaryFontName();
     // TODO: would be nice to consider StripWhitespace and other aliasing
     if (fontName.Equals(aFamily, nsCaseInsensitiveStringComparator())) {
       return glyphTable;
     }
   }
   // Fall back to default Unicode table
   return &mUnicodeTable;
@@ -894,16 +1148,21 @@ MeasureTextRun(gfxContext* aThebesContex
                           aThebesContext, nullptr);
 
   nsBoundingMetrics bm;
   bm.leftBearing = NSToCoordFloor(metrics.mBoundingBox.X());
   bm.rightBearing = NSToCoordCeil(metrics.mBoundingBox.XMost());
   bm.ascent = NSToCoordCeil(-metrics.mBoundingBox.Y());
   bm.descent = NSToCoordCeil(metrics.mBoundingBox.YMost());
   bm.width = NSToCoordRound(metrics.mAdvanceWidth);
+  if (bm.width == 0) {
+    // The advance width was not set in nsGlyphTable::MakeTextRun, so we use
+    // the right bearing instead.
+    bm.width = bm.rightBearing;
+  }
 
   return bm;
 }
 
 class nsMathMLChar::StretchEnumContext {
 public:
   StretchEnumContext(nsMathMLChar*        aChar,
                      nsPresContext*       aPresContext,
@@ -925,18 +1184,22 @@ public:
       mTryVariants(true),
       mTryParts(true),
       mGlyphFound(aGlyphFound) {}
 
   static bool
   EnumCallback(const nsString& aFamily, bool aGeneric, void *aData);
 
 private:
-  bool TryVariants(nsGlyphTable* aGlyphTable, const nsAString& aFamily);
-  bool TryParts(nsGlyphTable* aGlyphTable, const nsAString& aFamily);
+  bool TryVariants(nsGlyphTable* aGlyphTable,
+                   nsRefPtr<gfxFontGroup>* aFontGroup,
+                   const nsAString& aFamily);
+  bool TryParts(nsGlyphTable* aGlyphTable,
+                nsRefPtr<gfxFontGroup>* aFontGroup,
+                const nsAString& aFamily);
 
   nsMathMLChar* mChar;
   nsPresContext* mPresContext;
   gfxContext* mThebesContext;
   const nsStretchDirection mDirection;
   const nscoord mTargetSize;
   const uint32_t mStretchHint;
   nsBoundingMetrics& mBoundingMetrics;
@@ -952,60 +1215,115 @@ private:
   bool&       mGlyphFound;
 };
 
 
 // 2. See if there are any glyphs of the appropriate size.
 // Returns true if the size is OK, false to keep searching.
 // Always updates the char if a better match is found.
 bool
-nsMathMLChar::StretchEnumContext::TryVariants(nsGlyphTable*    aGlyphTable,
-                                              const nsAString& aFamily)
+nsMathMLChar::
+StretchEnumContext::TryVariants(nsGlyphTable* aGlyphTable,
+                                nsRefPtr<gfxFontGroup>* aFontGroup,
+                                const nsAString& aFamily)
 {
   // Use our stretchy style context now that stretching is in progress
   nsStyleContext *sc = mChar->mStyleContext;
   nsFont font = sc->StyleFont()->mFont;
   // Ensure SetFontFamily will set the font
   font.name.Truncate();
 
   bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
+  nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
+  char16_t uchar = mChar->mData[0];
   bool largeop = (NS_STRETCH_LARGEOP & mStretchHint) != 0;
   bool largeopOnly =
     largeop && (NS_STRETCH_VARIABLE_MASK & mStretchHint) == 0;
   bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
 
   nscoord bestSize =
     isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
                : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
   bool haveBetter = false;
 
   // start at size = 1 (size = 0 is the char at its normal size)
   int32_t size = 1;
+  nsGlyphCode ch;
+  nscoord displayOperatorMinHeight = 0;
+  if (largeopOnly) {
+    NS_ASSERTION(isVertical, "Stretching should be in the vertical direction");
+    ch = aGlyphTable->BigOf(mThebesContext, oneDevPixel, *aFontGroup, uchar,
+                            isVertical, 0);
+    if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamily, font,
+                              aFontGroup)) {
+      return false;
+    }
+    gfxFont* mathFont = aFontGroup->get()->GetFontAt(0);
+    if (mathFont->GetFontEntry()->TryGetMathTable(mathFont)) {
+      // For OpenType MATH fonts, we will rely on the DisplayOperatorMinHeight
+      // to select the right size variant. Note that the value is sometimes too
+      // small so we use kLargeOpFactor/kIntegralFactor as a minimum value.
+      displayOperatorMinHeight =
+        NSToCoordRound(mathFont->GetFontEntry()->
+                       GetMathConstant(gfxFontEntry::DisplayOperatorMinHeight) *
+                       mathFont->GetAdjustedSize() * oneDevPixel);
+      nsAutoPtr<gfxTextRun> textRun;
+      textRun = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel,
+                                         *aFontGroup, ch);
+      nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun);
+      float largeopFactor = kLargeOpFactor;
+      if (NS_STRETCH_INTEGRAL & mStretchHint) {
+        // integrals are drawn taller
+        largeopFactor = kIntegralFactor;
+      }
+      nscoord minHeight = largeopFactor * (bm.ascent + bm.descent);
+      if (displayOperatorMinHeight < minHeight) {
+        displayOperatorMinHeight = minHeight;
+      }
+    }
+  }
 #ifdef NOISY_SEARCH
   printf("  searching in %s ...\n",
            NS_LossyConvertUTF16toASCII(aFamily).get());
 #endif
-
-  nsGlyphCode ch;
-  nsRefPtr<gfxFontGroup> fontGroup;
-  while ((ch = aGlyphTable->BigOf(mChar->mData[0], size)).Exists()) {
+  while ((ch = aGlyphTable->BigOf(mThebesContext, oneDevPixel, *aFontGroup,
+                                  uchar, isVertical, size)).Exists()) {
 
     if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamily, font,
-                              &fontGroup)) {
+                              aFontGroup)) {
       // if largeopOnly is set, break now
       if (largeopOnly) break;
       ++size;
       continue;
     }
 
     nsAutoPtr<gfxTextRun> textRun;
-    textRun = aGlyphTable->MakeTextRun(mThebesContext,
-                                       mPresContext->AppUnitsPerDevPixel(),
-                                       fontGroup, ch);
+    textRun = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel,
+                                       *aFontGroup, ch);
     nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun);
+    if (ch.IsGlyphID()) {
+      gfxFont* mathFont = aFontGroup->get()->GetFontAt(0);
+      if (mathFont->GetFontEntry()->TryGetMathTable(mathFont)) {
+        // MeasureTextRun has set the advance width to the right bearing. We now
+        // subtract the italic correction, so that nsMathMLmmultiscripts will
+        // place the scripts correctly.
+        // Note that STIX-Word does not provide italic corrections
+        // (http://sourceforge.net/p/stixfonts/tracking/50/)
+        gfxFloat italicCorrection;
+        if (mathFont->GetFontEntry()->
+            GetMathItalicsCorrection(ch.glyphID, &italicCorrection)) {
+          bm.width -=
+            NSToCoordRound(italicCorrection *
+                           mathFont->GetAdjustedSize() * oneDevPixel);
+          if (bm.width < 0) {
+            bm.width = 0;
+          }
+        }
+      }
+    }
 
     nscoord charSize =
       isVertical ? bm.ascent + bm.descent
       : bm.rightBearing - bm.leftBearing;
 
     if (largeopOnly ||
         IsSizeBetter(charSize, bestSize, mTargetSize, mStretchHint)) {
       mGlyphFound = true;
@@ -1035,64 +1353,66 @@ nsMathMLChar::StretchEnumContext::TryVar
     else {
 #ifdef NOISY_SEARCH
       printf("    size:%d Rejected!\n", size);
 #endif
       if (haveBetter)
         break; // Not making an futher progress, stop searching
     }
 
-    // if largeopOnly is set, break now
-    if (largeopOnly) break;
+    // If this a largeop only operator, we stop if the glyph is large enough.
+    if (largeopOnly && (bm.ascent + bm.descent) >= displayOperatorMinHeight) {
+      break;
+    }
     ++size;
   }
 
   return haveBetter &&
     (largeopOnly ||
      IsSizeOK(mPresContext, bestSize, mTargetSize, mStretchHint));
 }
 
 // 3. Build by parts.
 // Returns true if the size is OK, false to keep searching.
 // Always updates the char if a better match is found.
 bool
-nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable*    aGlyphTable,
+nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable* aGlyphTable,
+                                           nsRefPtr<gfxFontGroup>* aFontGroup,
                                            const nsAString& aFamily)
 {
-  if (!aGlyphTable->HasPartsOf(mChar->mData[0]))
-    return false; // to next table
-
-  // See if the parts of this table fit in the desired space //////////////////
-
   // Use our stretchy style context now that stretching is in progress
   nsFont font = mChar->mStyleContext->StyleFont()->mFont;
   // Ensure SetFontFamily will set the font
   font.name.Truncate();
 
   // Compute the bounding metrics of all partial glyphs
   nsAutoPtr<gfxTextRun> textRun[4];
   nsGlyphCode chdata[4];
   nsBoundingMetrics bmdata[4];
   nscoord sizedata[4];
 
   bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
+  nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
+  char16_t uchar = mChar->mData[0];
   bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
-  nsRefPtr<gfxFontGroup> fontGroup;
+  if (!aGlyphTable->HasPartsOf(mThebesContext, oneDevPixel, *aFontGroup,
+                               uchar, isVertical))
+    return false; // to next table
 
   for (int32_t i = 0; i < 4; i++) {
-    nsGlyphCode ch = aGlyphTable->ElementAt(mChar->mData[0], i);
+    nsGlyphCode ch = aGlyphTable->ElementAt(mThebesContext, oneDevPixel,
+                                            *aFontGroup, uchar, isVertical, i);
     chdata[i] = ch;
     if (ch.Exists()) {
       if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamily, font,
-                                &fontGroup))
+                                aFontGroup))
         return false;
 
-      textRun[i] = aGlyphTable->MakeTextRun(mThebesContext,
-                                            mPresContext->AppUnitsPerDevPixel(),
-                                            fontGroup, ch);
+      textRun[i] = aGlyphTable->MakeTextRun(mThebesContext, oneDevPixel,
+                                            *aFontGroup, ch);
       nsBoundingMetrics bm = MeasureTextRun(mThebesContext, textRun[i]);
 
       // TODO: For the generic Unicode table, ideally we should check that the
       // glyphs are actually found and that they each come from the same
       // font.
       bmdata[i] = bm;
       sizedata[i] = isVertical ? bm.ascent + bm.descent
                                : bm.rightBearing - bm.leftBearing;
@@ -1202,48 +1522,61 @@ nsMathMLChar::StretchEnumContext::TryPar
 
 // This is called for each family, whether it exists or not
 bool
 nsMathMLChar::StretchEnumContext::EnumCallback(const nsString& aFamily,
                                                bool aGeneric, void *aData)
 {
   StretchEnumContext* context = static_cast<StretchEnumContext*>(aData);
 
-  // See if there is a special table for the family, but always use the
-  // Unicode table for generic fonts.
-  nsGlyphTable* glyphTable = aGeneric ?
-    &gGlyphTableList->mUnicodeTable :
-    gGlyphTableList->GetGlyphTableFor(aFamily);
-
-  if (context->mTablesTried.Contains(glyphTable))
-    return true; // already tried this one
-
   // Check font family if it is not a generic one
   // We test with the kNullGlyph
   nsStyleContext *sc = context->mChar->mStyleContext;
   nsFont font = sc->StyleFont()->mFont;
   nsRefPtr<gfxFontGroup> fontGroup;
   if (!aGeneric && !context->mChar->SetFontFamily(context->mPresContext,
                                                   nullptr, kNullGlyph, aFamily,
                                                   font, &fontGroup))
      return true; // Could not set the family
 
-  // Now see if the table has a glyph that matches the container
+  // Determine the glyph table to use for this font.
+  nsAutoPtr<nsOpenTypeTable> openTypeTable;
+  nsGlyphTable* glyphTable;
+  if (aGeneric) {
+    // This is a generic font, use the Unicode table.
+    glyphTable = &gGlyphTableList->mUnicodeTable;
+  } else {
+    // If the font contains an Open Type MATH table, use it.
+    openTypeTable = nsOpenTypeTable::Create(fontGroup->GetFontAt(0));
+    if (openTypeTable) {
+      glyphTable = openTypeTable;
+    } else {
+      // Otherwise try to find a .properties file corresponding to that font
+      // family or fallback to the Unicode table.
+      glyphTable = gGlyphTableList->GetGlyphTableFor(aFamily);
+    }
+  }
 
-  // Only try this table once.
-  context->mTablesTried.AppendElement(glyphTable);
+  if (!openTypeTable) {
+    if (context->mTablesTried.Contains(glyphTable))
+      return true; // already tried this one
+    
+    // Only try this table once.
+    context->mTablesTried.AppendElement(glyphTable);
+  }
 
   // If the unicode table is being used, then search all font families.  If a
   // special table is being used then the font in this family should have the
   // specified glyphs.
   const nsAString& family = glyphTable == &gGlyphTableList->mUnicodeTable ?
     context->mFamilies : aFamily;
 
-  if((context->mTryVariants && context->TryVariants(glyphTable, family)) ||
-     (context->mTryParts && context->TryParts(glyphTable, family)))
+  if((context->mTryVariants &&
+      context->TryVariants(glyphTable, &fontGroup, family)) ||
+     (context->mTryParts && context->TryParts(glyphTable, &fontGroup, family)))
     return false; // no need to continue
 
   return true; // true means continue
 }
 
 nsresult
 nsMathMLChar::StretchInternal(nsPresContext*           aPresContext,
                               gfxContext*              aThebesContext,
@@ -1469,17 +1802,17 @@ nsMathMLChar::StretchInternal(nsPresCont
       }
     }
   }
 
   // We do not have a char variant for this largeop in display mode, so we
   // apply a scale transform to the base char.
   if (!glyphFound && largeop) {
     float scale;
-    float largeopFactor = float(M_SQRT2);
+    float largeopFactor = kLargeOpFactor;
 
     // increase the width if it is not largeopFactor times larger
     // than the initial one.
     if ((aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing) <
         largeopFactor * (initialSize.rightBearing - initialSize.leftBearing)) {
       scale = (largeopFactor *
                (initialSize.rightBearing - initialSize.leftBearing)) /
         (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing);
@@ -1490,17 +1823,17 @@ nsMathMLChar::StretchInternal(nsPresCont
       aDesiredStretchSize.rightBearing *= scale;
       aDesiredStretchSize.width *= scale;
     }
 
     // increase the height if it is not largeopFactor times larger
     // than the initial one.
     if (NS_STRETCH_INTEGRAL & aStretchHint) {
       // integrals are drawn taller
-      largeopFactor = 2.0;
+      largeopFactor = kIntegralFactor;
     }
     if ((aDesiredStretchSize.ascent + aDesiredStretchSize.descent) <
         largeopFactor * (initialSize.ascent + initialSize.descent)) {
       scale = (largeopFactor *
                (initialSize.ascent + initialSize.descent)) /
         (aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
       if (!maxWidth) {
         mScaleY *= scale;
--- a/layout/mathml/nsMathMLChar.h
+++ b/layout/mathml/nsMathMLChar.h
@@ -194,16 +194,18 @@ public:
   // the Get/Set AdditionalStyleContext() APIs. Owners of MathMLChars
   // should honor these APIs.
   nsStyleContext* GetStyleContext() const;
 
   void SetStyleContext(nsStyleContext* aStyleContext);
 
 protected:
   friend class nsGlyphTable;
+  friend class nsPropertiesTable;
+  friend class nsOpenTypeTable;
   nsString           mData;
 
 private:
   nsRect             mRect;
   nsStretchDirection mDirection;
   nsBoundingMetrics  mBoundingMetrics;
   nsStyleContext*    mStyleContext;
   // mGlyphs/mBmData are arrays describing the glyphs used to draw the operator.