Bug 414277 - scale stretchy operators when there is no glyph of suitable size. r=karlt a=dbaron
authorFrédéric Wang <fred.wang@free.fr>
Fri, 20 Aug 2010 15:44:07 +1200
changeset 51009 ebe66e95925545f893c7fbe78a2dc4506895ca7b
parent 51008 8a52e79553b6a47a0f642e503abaf450bfe3a27d
child 51010 853a2178341d7c65c054c4186ba261dad5812c1a
push idunknown
push userunknown
push dateunknown
reviewerskarlt, dbaron
bugs414277
milestone2.0b5pre
Bug 414277 - scale stretchy operators when there is no glyph of suitable size. r=karlt a=dbaron
layout/mathml/nsMathMLChar.cpp
layout/mathml/nsMathMLChar.h
layout/mathml/nsMathMLOperators.cpp
layout/mathml/nsMathMLOperators.h
layout/mathml/nsMathMLmoFrame.cpp
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -867,21 +867,16 @@ nsMathMLChar::SetData(nsPresContext* aPr
   mBoundingMetrics.Clear();
   mGlyphTable = nsnull;
   // check if stretching is applicable ...
   if (gGlyphTableList && (1 == mData.Length())) {
     mDirection = nsMathMLOperators::GetStretchyDirection(mData);
     // default tentative table (not the one that is necessarily going
     // to be used)
     mGlyphTable = gGlyphTableList->GetGlyphTableFor(aPresContext, this);
-    // commom case: we won't bother with the stretching if there is
-    // no glyph table for us...
-    if (!mGlyphTable) { // TODO: consider scaling the base char
-      mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
-    }
   }
 }
 
 // -----------------------------------------------------------------------------------
 /*
  The Stretch:
  @param aContainerSize - suggested size for the stretched char
  @param aDesiredStretchSize - OUT parameter. The desired size
@@ -953,17 +948,17 @@ nsMathMLChar::SetData(nsPresContext* aPr
 
  4) If a match was not found in that glyph table, repeat from 2 to search the
     ordered list of stretchy fonts for the first font with a glyph table that
     provides a fit to the container size.  If no fit is found, the closest fit
     is used.
 
  Of note:
  When the pipeline completes successfully, the desired size of the
- stretched char can actually be slighthly larger or smaller than
+ stretched char can actually be slightly larger or smaller than
  aContainerSize. But it is the responsibility of the caller to
  account for the spacing when setting aContainerSize, and to leave
  any extra margin when placing the stretched char.
 */
 // -----------------------------------------------------------------------------------
 
 
 // plain TeX settings (TeXbook p.152)
@@ -1560,28 +1555,34 @@ nsMathMLChar::StretchInternal(nsPresCont
   }
 
   aRenderingContext.SetFont(font, aPresContext->GetUserFontSet());
   nsresult rv =
     aRenderingContext.GetBoundingMetrics(mData.get(), PRUint32(mData.Length()),
                                          aDesiredStretchSize);
   if (NS_FAILED(rv)) {
     NS_WARNING("GetBoundingMetrics failed");
+    mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
     return rv;
   }
 
+  if (!maxWidth) {
+    mScaleY = mScaleX = 1.0;
+    mUnscaledAscent = aDesiredStretchSize.ascent;
+  }
+
   ////////////////////////////////////////////////////////////////////////////////////
   // 1. Check the common situations where stretching is not actually needed
   ////////////////////////////////////////////////////////////////////////////////////
 
   // quick return if there is nothing special about this char
-  if (!mGlyphTable ||
-      (aStretchDirection != direction &&
+  if ((aStretchDirection != direction &&
        aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT) ||
       (aStretchHint & ~NS_STRETCH_MAXWIDTH) == NS_STRETCH_NONE) {
+    mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
     return NS_OK;
   }
 
   // if no specified direction, attempt to stretch in our preferred direction
   if (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT) {
     aStretchDirection = direction;
   }
 
@@ -1629,42 +1630,46 @@ nsMathMLChar::StretchInternal(nsPresCont
         aDesiredStretchSize.ascent =
           NSToCoordRound(scale * aDesiredStretchSize.ascent);
         aDesiredStretchSize.descent =
           NSToCoordRound(scale * aDesiredStretchSize.descent);
       }
     }
   }
 
-  if (!maxWidth && !largeop) {
+  nsBoundingMetrics initialSize = aDesiredStretchSize;
+  nscoord charSize =
+    isVertical ? initialSize.ascent + initialSize.descent
+    : initialSize.rightBearing - initialSize.leftBearing;
+
+  PRBool done = (mGlyphTable ? PR_FALSE : PR_TRUE);
+
+  if (!done && !maxWidth && !largeop) {
     // Doing Stretch() not GetMaxWidth(),
-    // and not a largeop in display mode; return if size fits
-    nscoord charSize =
-      isVertical ? aDesiredStretchSize.ascent + aDesiredStretchSize.descent
-      : aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing;
-
+    // and not a largeop in display mode; we're done if size fits
     if ((targetSize <= 0) || 
         ((isVertical && charSize >= targetSize) ||
          IsSizeOK(aPresContext, charSize, targetSize, aStretchHint)))
-      return NS_OK;
+      done = PR_TRUE;
   }
 
   ////////////////////////////////////////////////////////////////////////////////////
   // 2/3. Search for a glyph or set of part glyphs of appropriate size
   ////////////////////////////////////////////////////////////////////////////////////
 
-  font = mStyleContext->GetStyleFont()->mFont;
   nsAutoString cssFamilies;
-  cssFamilies = font.name;
 
-  PRBool done = PR_FALSE;
+  if (!done) {
+    font = mStyleContext->GetStyleFont()->mFont;
+    cssFamilies = font.name;
+  }
 
   // See if there are preferred fonts for the variants of this char
-  if (GetFontExtensionPref(prefBranch, mData[0], eExtension_variants,
-                           families)) {
+  if (!done && GetFontExtensionPref(prefBranch, mData[0], eExtension_variants,
+                                    families)) {
     font.name = families;
 
     StretchEnumContext enumData(this, aPresContext, aRenderingContext,
                                 aStretchDirection, targetSize, aStretchHint,
                                 aDesiredStretchSize, font.name);
     enumData.mTryParts = PR_FALSE;
 
     done = !font.EnumerateFamilies(StretchEnumContext::EnumCallback, &enumData);
@@ -1700,45 +1705,109 @@ nsMathMLChar::StretchInternal(nsPresCont
     StretchEnumContext enumData(this, aPresContext, aRenderingContext,
                                 aStretchDirection, targetSize, aStretchHint,
                                 aDesiredStretchSize, font.name);
     enumData.mTryParts = !largeopOnly;
 
     font.EnumerateFamilies(StretchEnumContext::EnumCallback, &enumData);
   }
 
+  if (!maxWidth) {
+    // Now, we know how we are going to draw the char. Update the member
+    // variables accordingly.
+    mDrawNormal = (mGlyph.font == -1);
+    mUnscaledAscent = aDesiredStretchSize.ascent;
+  }
+    
+  // stretchy character
+  if (stretchy) {
+    if (isVertical) {
+      float scale =
+        float(aContainerSize.ascent + aContainerSize.descent) /
+        (aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
+      if (!largeop || scale > 1.0) {
+        // make the character match the desired height.
+        mScaleY *= scale;
+        aDesiredStretchSize.ascent *= scale;
+        aDesiredStretchSize.descent *= scale;
+      }
+    } else {
+      float scale =
+        float(aContainerSize.rightBearing - aContainerSize.leftBearing) /
+        (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing);
+      if (!largeop || scale > 1.0) {
+        // make the character match the desired width.
+        mScaleX *= scale;
+        aDesiredStretchSize.leftBearing *= scale;
+        aDesiredStretchSize.rightBearing *= scale;
+        aDesiredStretchSize.width *= scale;
+      }
+    }
+  }
+
+  // We do not have a char variant for this largeop in display mode, so we
+  // apply a scale transform to the base char.
+  if (mGlyph.font == -1 && largeop) {
+    float scale;
+    float largeopFactor = M_SQRT2;
+
+    // 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);
+      mScaleX *= scale;
+      aDesiredStretchSize.leftBearing *= scale;
+      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;
+    }
+    if ((aDesiredStretchSize.ascent + aDesiredStretchSize.descent) <
+        largeopFactor * (initialSize.ascent + initialSize.descent)) {
+      scale = (largeopFactor *
+               (initialSize.ascent + initialSize.descent)) /
+        (aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
+      mScaleY *= scale;
+      aDesiredStretchSize.ascent *= scale;
+      aDesiredStretchSize.descent *= scale;
+    }
+  }
+
   return NS_OK;
 }
 
 nsresult
 nsMathMLChar::Stretch(nsPresContext*           aPresContext,
                       nsIRenderingContext&     aRenderingContext,
                       nsStretchDirection       aStretchDirection,
                       const nsBoundingMetrics& aContainerSize,
                       nsBoundingMetrics&       aDesiredStretchSize,
                       PRUint32                 aStretchHint)
 {
   NS_ASSERTION(!(aStretchHint &
-                 ~(NS_STRETCH_VARIABLE_MASK | NS_STRETCH_LARGEOP)),
+                 ~(NS_STRETCH_VARIABLE_MASK | NS_STRETCH_LARGEOP |
+                   NS_STRETCH_INTEGRAL)),
                "Unexpected stretch flags");
 
   // This will be updated if a better match than the base character is found
   mGlyph.font = -1;
 
   mDirection = aStretchDirection;
   nsresult rv =
     StretchInternal(aPresContext, aRenderingContext, mDirection,
                     aContainerSize, aDesiredStretchSize, aStretchHint);
 
-  if (mGlyph.font == -1) { // no stretch happened
-    // ensure that the char later behaves like a normal char
-    // (will be reset back to its intrinsic value in case of dynamic updates)
-    mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
-  }
-
   // Record the metrics
   mBoundingMetrics = aDesiredStretchSize;
 
   return rv;
 }
 
 // What happens here is that the StretchInternal algorithm is used but
 // modified by passing the NS_STRETCH_MAXWIDTH stretch hint.  That causes
@@ -1991,17 +2060,17 @@ nsMathMLChar::Display(nsDisplayListBuild
                       nsIFrame*               aForFrame,
                       const nsDisplayListSet& aLists,
                       const nsRect*           aSelectedRect)
 {
   nsresult rv = NS_OK;
   nsStyleContext* parentContext = mStyleContext->GetParent();
   nsStyleContext* styleContext = mStyleContext;
 
-  if (NS_STRETCH_DIRECTION_UNSUPPORTED == mDirection) {
+  if (mDrawNormal) {
     // normal drawing if there is nothing special about this char
     // Set default context to the parent context
     styleContext = parentContext;
   }
 
   if (!styleContext->GetStyleVisibility()->IsVisible())
     return NS_OK;
 
@@ -2033,25 +2102,38 @@ nsMathMLChar::Display(nsDisplayListBuild
 #endif
   }
   return aLists.Content()->AppendNewToTop(new (aBuilder)
         nsDisplayMathMLCharForeground(aForFrame, this,
                                       aSelectedRect && !aSelectedRect->IsEmpty()));
 }
 
 void
+nsMathMLChar::ApplyTransforms(nsIRenderingContext& aRenderingContext, nsRect &r)
+{
+  // apply the transforms
+  aRenderingContext.Translate(r.x, r.y);
+  aRenderingContext.Scale(mScaleX, mScaleY);
+
+  // update the bounding rectangle.
+  r.x = r.y = 0;
+  r.width /= mScaleX;
+  r.height /= mScaleY;
+}
+
+void
 nsMathMLChar::PaintForeground(nsPresContext* aPresContext,
                               nsIRenderingContext& aRenderingContext,
                               nsPoint aPt,
                               PRBool aIsSelected)
 {
   nsStyleContext* parentContext = mStyleContext->GetParent();
   nsStyleContext* styleContext = mStyleContext;
 
-  if (NS_STRETCH_DIRECTION_UNSUPPORTED == mDirection) {
+  if (mDrawNormal) {
     // normal drawing if there is nothing special about this char
     // Set default context to the parent context
     styleContext = parentContext;
   }
 
   // Set color ...
   nscolor fgColor = styleContext->GetStyleColor()->mColor;
   if (aIsSelected) {
@@ -2062,55 +2144,48 @@ nsMathMLChar::PaintForeground(nsPresCont
   aRenderingContext.SetColor(fgColor);
 
   nsFont theFont(styleContext->GetStyleFont()->mFont);
   if (! mFamily.IsEmpty()) {
     theFont.name = mFamily;
   }
   aRenderingContext.SetFont(theFont, aPresContext->GetUserFontSet());
 
-  if (NS_STRETCH_DIRECTION_UNSUPPORTED == mDirection) {
+  aRenderingContext.PushState();
+  nsRect r = mRect + aPt;
+  ApplyTransforms(aRenderingContext, r);
+
+  if (mDrawNormal) {
     // normal drawing if there is nothing special about this char ...
     // Grab some metrics to adjust the placements ...
     PRUint32 len = PRUint32(mData.Length());
 //printf("Painting %04X like a normal char\n", mData[0]);
 //aRenderingContext.SetColor(NS_RGB(255,0,0));
-    aRenderingContext.DrawString(mData.get(), len, mRect.x + aPt.x,
-                                 mRect.y + aPt.y + mBoundingMetrics.ascent);
+    aRenderingContext.DrawString(mData.get(), len, 0, mUnscaledAscent);
   }
   else {
     // Grab some metrics to adjust the placements ...
     // if there is a glyph of appropriate size, paint that glyph
     if (mGlyph.Exists()) {
 //printf("Painting %04X with a glyph of appropriate size\n", mData[0]);
 //aRenderingContext.SetColor(NS_RGB(0,0,255));
-      aRenderingContext.DrawString(&mGlyph.code, 1, mRect.x + aPt.x,
-                                   mRect.y + aPt.y + mBoundingMetrics.ascent);
+      aRenderingContext.DrawString(&mGlyph.code, 1, 0, mUnscaledAscent);
     }
     else { // paint by parts
-      // see if this is a composite char and let children paint themselves
-      if (!mParent && mSibling) { // only a "root" having child chars can enter here
-        for (nsMathMLChar* child = mSibling; child; child = child->mSibling) {
-//if (!mStyleContext->Equals(child->mStyleContext))
-//  printf("char contexts are out of sync\n");
-          child->PaintForeground(aPresContext, aRenderingContext, aPt,
-                                 aIsSelected);
-        }
-        return; // that's all folks
-       }
 //aRenderingContext.SetColor(NS_RGB(0,255,0));
-      nsRect r = mRect + aPt;
       if (NS_STRETCH_DIRECTION_VERTICAL == mDirection)
         PaintVertically(aPresContext, aRenderingContext, theFont, styleContext,
                         mGlyphTable, r);
       else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
         PaintHorizontally(aPresContext, aRenderingContext, theFont, styleContext,
                           mGlyphTable, r);
     }
   }
+
+  aRenderingContext.PopState();
 }
 
 /* =================================================================================
   And now the helper routines that actually do the job of painting the char by parts
  */
 
 class AutoPushClipRect {
   nsIRenderingContext& mCtx;
--- a/layout/mathml/nsMathMLChar.h
+++ b/layout/mathml/nsMathMLChar.h
@@ -54,19 +54,21 @@ enum {
   // Variable size stretches
   NS_STRETCH_VARIABLE_MASK = 0x0F,
   NS_STRETCH_NORMAL   = 0x01, // try to stretch to requested size
   NS_STRETCH_NEARER   = 0x02, // stretch very close to requested size
   NS_STRETCH_SMALLER  = 0x04, // don't stretch more than requested size
   NS_STRETCH_LARGER   = 0x08, // don't stretch less than requested size
   // A largeop in displaystyle
   NS_STRETCH_LARGEOP  = 0x10,
+  NS_STRETCH_INTEGRAL  = 0x20,
+
   // Intended for internal use:
   // Find the widest metrics that might be returned from a vertical stretch
-  NS_STRETCH_MAXWIDTH = 0x20
+  NS_STRETCH_MAXWIDTH = 0x40
 };
 
 // A single glyph in our internal representation is characterized by a 'code@font' 
 // pair. The 'code' is interpreted as a Unicode point or as the direct glyph index
 // (depending on the type of nsGlyphTable where this comes from). The 'font' is a
 // numeric identifier given to the font to which the glyph belongs.
 struct nsGlyphCode {
   PRUnichar code; 
@@ -100,16 +102,19 @@ class nsMathMLChar
 {
 public:
   // constructor and destructor
   nsMathMLChar(nsMathMLChar* aParent = nsnull) {
     MOZ_COUNT_CTOR(nsMathMLChar);
     mStyleContext = nsnull;
     mSibling = nsnull;
     mParent = aParent;
+    mUnscaledAscent = 0;
+    mScaleX = mScaleY = 1.0;
+    mDrawNormal = PR_TRUE;
   }
 
   ~nsMathMLChar() { // not a virtual destructor: this class is not intended to be subclassed
     MOZ_COUNT_DTOR(nsMathMLChar);
     // there is only one style context owned by the "root" char
     // and it may be used by child chars as well
     if (!mParent && mStyleContext) { // only the "root" need to release it
       mStyleContext->Release();
@@ -240,16 +245,22 @@ private:
   nsStretchDirection mDirection;
   nsBoundingMetrics  mBoundingMetrics;
   nsStyleContext*    mStyleContext;
   nsGlyphTable*      mGlyphTable;
   nsGlyphCode        mGlyph;
   // mFamily is non-empty when the family for the current size is different
   // from the family in the nsStyleContext.
   nsString           mFamily;
+  // mUnscaledAscent is the actual ascent of the char.
+  nscoord            mUnscaledAscent;
+  // mScaleX, mScaleY are the factors by which we scale the char.
+  float              mScaleX, mScaleY;
+  // mDrawNormal indicates whether we use special glyphs or not.
+  PRPackedBool       mDrawNormal;
 
   class StretchEnumContext;
   friend class StretchEnumContext;
 
   // helper methods
   nsresult
   StretchInternal(nsPresContext*           aPresContext,
                   nsIRenderingContext&     aRenderingContext,
@@ -278,11 +289,14 @@ private:
 
   nsresult
   PaintHorizontally(nsPresContext*       aPresContext,
                     nsIRenderingContext& aRenderingContext,
                     nsFont&              aFont,
                     nsStyleContext*      aStyleContext,
                     nsGlyphTable*        aGlyphTable,
                     nsRect&              aRect);
+
+  void
+  ApplyTransforms(nsIRenderingContext& aRenderingContext, nsRect &r);
 };
 
 #endif /* nsMathMLChar_h___ */
--- a/layout/mathml/nsMathMLOperators.cpp
+++ b/layout/mathml/nsMathMLOperators.cpp
@@ -110,16 +110,18 @@ SetBooleanProperty(OperatorData* aOperat
   else if (aName.EqualsLiteral("largeop"))
     aOperatorData->mFlags |= NS_MATHML_OPERATOR_LARGEOP;
   else if (aName.EqualsLiteral("separator"))
     aOperatorData->mFlags |=  NS_MATHML_OPERATOR_SEPARATOR;
   else if (aName.EqualsLiteral("movablelimits"))
     aOperatorData->mFlags |= NS_MATHML_OPERATOR_MOVABLELIMITS;
   else if (aName.EqualsLiteral("symmetric"))
     aOperatorData->mFlags |= NS_MATHML_OPERATOR_SYMMETRIC;
+  else if (aName.EqualsLiteral("integral"))
+    aOperatorData->mFlags |= NS_MATHML_OPERATOR_INTEGRAL;
 }
 
 static void
 SetProperty(OperatorData* aOperatorData,
             nsString      aName,
             nsString      aValue)
 {
   if (aName.IsEmpty() || aValue.IsEmpty())
--- a/layout/mathml/nsMathMLOperators.h
+++ b/layout/mathml/nsMathMLOperators.h
@@ -75,22 +75,23 @@ enum {
   // other bits used in the Operator Dictionary
   NS_MATHML_OPERATOR_STRETCHY           = 1<<4,
   NS_MATHML_OPERATOR_FENCE              = 1<<5,
   NS_MATHML_OPERATOR_ACCENT             = 1<<6,
   NS_MATHML_OPERATOR_LARGEOP            = 1<<7,
   NS_MATHML_OPERATOR_SEPARATOR          = 1<<8,
   NS_MATHML_OPERATOR_MOVABLELIMITS      = 1<<9,
   NS_MATHML_OPERATOR_SYMMETRIC          = 1<<10,
+  NS_MATHML_OPERATOR_INTEGRAL           = 1<<11,
 
   // Additional bits not stored in the dictionary
-  NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE   = 1<<11,
-  NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE   = 1<<12,
-  NS_MATHML_OPERATOR_LEFTSPACE_ATTR     = 1<<13,
-  NS_MATHML_OPERATOR_RIGHTSPACE_ATTR    = 1<<14
+  NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE   = 1<<12,
+  NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE   = 1<<13,
+  NS_MATHML_OPERATOR_LEFTSPACE_ATTR     = 1<<14,
+  NS_MATHML_OPERATOR_RIGHTSPACE_ATTR    = 1<<15
 };
 
 #define NS_MATHML_OPERATOR_SIZE_INFINITY NS_IEEEPositiveInfinity()
 
 // Style invariant characters (chars have their own intrinsic predefined style)
 enum eMATHVARIANT {
   eMATHVARIANT_NONE = -1,
   eMATHVARIANT_normal = 0,
@@ -220,16 +221,19 @@ public:
   (NS_MATHML_OPERATOR_SEPARATOR == ((_flags) & NS_MATHML_OPERATOR_SEPARATOR))
 
 #define NS_MATHML_OPERATOR_IS_MOVABLELIMITS(_flags) \
   (NS_MATHML_OPERATOR_MOVABLELIMITS == ((_flags) & NS_MATHML_OPERATOR_MOVABLELIMITS))
 
 #define NS_MATHML_OPERATOR_IS_SYMMETRIC(_flags) \
   (NS_MATHML_OPERATOR_SYMMETRIC == ((_flags) & NS_MATHML_OPERATOR_SYMMETRIC))
 
+#define NS_MATHML_OPERATOR_IS_INTEGRAL(_flags) \
+  (NS_MATHML_OPERATOR_INTEGRAL == ((_flags) & NS_MATHML_OPERATOR_INTEGRAL))
+
 #define NS_MATHML_OPERATOR_MINSIZE_IS_ABSOLUTE(_flags) \
   (NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE == ((_flags) & NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE))
 
 #define NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(_flags) \
   (NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE == ((_flags) & NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE))
 
 #define NS_MATHML_OPERATOR_HAS_LEFTSPACE_ATTR(_flags) \
   (NS_MATHML_OPERATOR_LEFTSPACE_ATTR == ((_flags) & NS_MATHML_OPERATOR_LEFTSPACE_ATTR))
--- a/layout/mathml/nsMathMLmoFrame.cpp
+++ b/layout/mathml/nsMathMLmoFrame.cpp
@@ -473,33 +473,35 @@ nsMathMLmoFrame::ProcessOperatorData()
   // XXX If an attribute can be forced to be true when it is false in the
   // dictionary, then the following code has to change...
 
   // For each attribute overriden by the user, turn off its bit flag.
   // symmetric|movablelimits|separator|largeop|accent|fence|stretchy|form
   // special: accent and movablelimits are handled above,
   // don't process them here
 
-  if (NS_MATHML_OPERATOR_IS_STRETCHY(mFlags)) {
-    GetAttribute(mContent, mPresentationData.mstyle,
-                 nsGkAtoms::stretchy_, value);
-    if (value.EqualsLiteral("false"))
-      mFlags &= ~NS_MATHML_OPERATOR_STRETCHY;
+  GetAttribute(mContent, mPresentationData.mstyle,
+               nsGkAtoms::stretchy_, value);
+  if (value.EqualsLiteral("false")) {
+    mFlags &= ~NS_MATHML_OPERATOR_STRETCHY;
+  } else if (value.EqualsLiteral("true")) {
+    mFlags |= NS_MATHML_OPERATOR_STRETCHY;
   }
   if (NS_MATHML_OPERATOR_IS_FENCE(mFlags)) {
     GetAttribute(mContent, mPresentationData.mstyle,
                  nsGkAtoms::fence_, value);
     if (value.EqualsLiteral("false"))
       mFlags &= ~NS_MATHML_OPERATOR_FENCE;
   }
-  if (NS_MATHML_OPERATOR_IS_LARGEOP(mFlags)) {
-    GetAttribute(mContent, mPresentationData.mstyle,
-                 nsGkAtoms::largeop_, value);
-    if (value.EqualsLiteral("false"))
-      mFlags &= ~NS_MATHML_OPERATOR_LARGEOP;
+  GetAttribute(mContent, mPresentationData.mstyle,
+               nsGkAtoms::largeop_, value);
+  if (value.EqualsLiteral("false")) {
+    mFlags &= ~NS_MATHML_OPERATOR_LARGEOP;
+  } else if (value.EqualsLiteral("true")) {
+    mFlags |= NS_MATHML_OPERATOR_LARGEOP;
   }
   if (NS_MATHML_OPERATOR_IS_SEPARATOR(mFlags)) {
     GetAttribute(mContent, mPresentationData.mstyle,
                  nsGkAtoms::separator_, value);
     if (value.EqualsLiteral("false"))
       mFlags &= ~NS_MATHML_OPERATOR_SEPARATOR;
   }
   GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::symmetric_,
@@ -591,16 +593,19 @@ GetStretchHint(nsOperatorFlags aFlags, n
     // set the largeop or largeopOnly flags to suitably cover all the
     // 8 possible cases depending on whether displaystyle, largeop,
     // stretchy are true or false (see bug 69325).
     // . largeopOnly is taken if largeop=true and stretchy=false
     // . largeop is taken if largeop=true and stretchy=true
     if (NS_MATHML_IS_DISPLAYSTYLE(aPresentationData.flags) &&
         NS_MATHML_OPERATOR_IS_LARGEOP(aFlags)) {
       stretchHint = NS_STRETCH_LARGEOP; // (largeopOnly, not mask!)
+      if (NS_MATHML_OPERATOR_IS_INTEGRAL(aFlags)) {
+        stretchHint |= NS_STRETCH_INTEGRAL;
+      }
       if (NS_MATHML_OPERATOR_IS_STRETCHY(aFlags)) {
         stretchHint |= NS_STRETCH_NEARER | NS_STRETCH_LARGER;
       }
     }
     else if(NS_MATHML_OPERATOR_IS_STRETCHY(aFlags)) {
       if (aIsVertical) {
         // TeX hint. Can impact some sloppy markups missing <mrow></mrow>
         stretchHint = NS_STRETCH_NEARER;