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 179922 cfcbc731d4db583918fe3131bf14466337e394e6
parent 179921 d8244f3ecdcbd49290c039f45d7be35dacdc8b20
child 179923 77593dd1b044f171e967afb6c36e70da77048929
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewerskarlt
bugs407059
milestone31.0a1
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.