Mirroring of operators for arabic math (Bug 208309) - part 1. r=karlt
authorFrédéric Wang <fred.wang@free.fr>
Wed, 21 Dec 2011 17:22:00 -0500
changeset 85664 e8809bd36bb0a7a87971964f36f4c7c3a760b64d
parent 85663 80ecebafe90b32e2f433730988a5665c2475efd2
child 85665 e604d9c39dd316930d59d7e5d71bb69fad5c0d38
push id674
push userffxbld
push dateTue, 13 Mar 2012 21:17:50 +0000
treeherdermozilla-beta@e3c4c92dec31 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt
bugs208309
milestone12.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
Mirroring of operators for arabic math (Bug 208309) - part 1. r=karlt
layout/mathml/nsMathMLChar.cpp
layout/mathml/nsMathMLChar.h
layout/mathml/nsMathMLOperators.cpp
layout/mathml/nsMathMLOperators.h
layout/mathml/nsMathMLmencloseFrame.cpp
layout/mathml/nsMathMLmfencedFrame.cpp
layout/mathml/nsMathMLmfencedFrame.h
layout/mathml/nsMathMLmoFrame.cpp
layout/mathml/nsMathMLmrootFrame.cpp
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -1698,24 +1698,26 @@ nsMathMLChar::StretchInternal(nsPresCont
 }
 
 nsresult
 nsMathMLChar::Stretch(nsPresContext*           aPresContext,
                       nsRenderingContext&     aRenderingContext,
                       nsStretchDirection       aStretchDirection,
                       const nsBoundingMetrics& aContainerSize,
                       nsBoundingMetrics&       aDesiredStretchSize,
-                      PRUint32                 aStretchHint)
+                      PRUint32                 aStretchHint,
+                      bool                     aRTL)
 {
   NS_ASSERTION(!(aStretchHint &
                  ~(NS_STRETCH_VARIABLE_MASK | NS_STRETCH_LARGEOP |
                    NS_STRETCH_INTEGRAL)),
                "Unexpected stretch flags");
 
   mDrawNormal = true;
+  mMirrored = aRTL && nsMathMLOperators::IsMirrorableOperator(mData);
   mScaleY = mScaleX = 1.0;
   mDirection = aStretchDirection;
   nsresult rv =
     StretchInternal(aPresContext, aRenderingContext, mDirection,
                     aContainerSize, aDesiredStretchSize, aStretchHint);
 
   // Record the metrics
   mBoundingMetrics = aDesiredStretchSize;
@@ -1791,20 +1793,21 @@ nsMathMLChar::ComposeChildren(nsPresCont
   }
   nscoord dx = 0, dy = 0;
   for (i = 0, child = mSibling; child; child = child->mSibling, i++) {
     // child chars should just inherit our values - which may change between calls...
     child->mData = mData;
     child->mDirection = mDirection;
     child->mStyleContext = mStyleContext;
     child->mGlyphTable = aGlyphTable; // the child is associated to this table
+    child->mMirrored = mMirrored;
     // there goes the Stretch() ...
     nsBoundingMetrics childSize;
     nsresult rv = child->Stretch(aPresContext, aRenderingContext, mDirection,
-                                 splitSize, childSize, aStretchHint);
+                                 splitSize, childSize, aStretchHint, mMirrored);
     // check if something went wrong or the child couldn't fit in the alloted space
     if (NS_FAILED(rv) || (NS_STRETCH_DIRECTION_UNSUPPORTED == child->mDirection)) {
       delete mSibling; // don't leave a dangling list behind ...
       mSibling = nsnull;
       return NS_ERROR_FAILURE;
     }
     child->SetRect(nsRect(dx, dy, childSize.width, childSize.ascent+childSize.descent));
     if (0 == i)
@@ -2023,18 +2026,23 @@ nsMathMLChar::Display(nsDisplayListBuild
         nsDisplayMathMLCharForeground(aBuilder, aForFrame, this,
                                       aSelectedRect && !aSelectedRect->IsEmpty()));
 }
 
 void
 nsMathMLChar::ApplyTransforms(nsRenderingContext& aRenderingContext, nsRect &r)
 {
   // apply the transforms
-  aRenderingContext.Translate(r.TopLeft());
-  aRenderingContext.Scale(mScaleX, mScaleY);
+  if (mMirrored) {
+    aRenderingContext.Translate(r.TopRight());
+    aRenderingContext.Scale(-mScaleX, mScaleY);
+  } else {
+    aRenderingContext.Translate(r.TopLeft());
+    aRenderingContext.Scale(mScaleX, mScaleY);
+  }
 
   // update the bounding rectangle.
   r.x = r.y = 0;
   r.width /= mScaleX;
   r.height /= mScaleY;
 }
 
 void
--- a/layout/mathml/nsMathMLChar.h
+++ b/layout/mathml/nsMathMLChar.h
@@ -107,16 +107,17 @@ public:
   nsMathMLChar(nsMathMLChar* aParent = nsnull) {
     MOZ_COUNT_CTOR(nsMathMLChar);
     mStyleContext = nsnull;
     mSibling = nsnull;
     mParent = aParent;
     mUnscaledAscent = 0;
     mScaleX = mScaleY = 1.0;
     mDrawNormal = true;
+    mMirrored = false;
   }
 
   ~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();
@@ -141,17 +142,18 @@ public:
   // @param aContainerSize - IN - suggested size for the stretched char
   // @param aDesiredStretchSize - OUT - the size that the char wants
   nsresult
   Stretch(nsPresContext*           aPresContext,
           nsRenderingContext&     aRenderingContext,
           nsStretchDirection       aStretchDirection,
           const nsBoundingMetrics& aContainerSize,
           nsBoundingMetrics&       aDesiredStretchSize,
-          PRUint32                 aStretchHint = NS_STRETCH_NORMAL);
+          PRUint32                 aStretchHint,
+          bool                     aRTL);
 
   void
   SetData(nsPresContext* aPresContext,
           nsString&       aData);
 
   void
   GetData(nsString& aData) {
     aData = mData;
@@ -253,16 +255,18 @@ private:
   // 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.
   bool               mDrawNormal;
+  // mMirrored indicates whether the character is mirrored. 
+  bool               mMirrored;
 
   class StretchEnumContext;
   friend class StretchEnumContext;
 
   // helper methods
   nsresult
   StretchInternal(nsPresContext*           aPresContext,
                   nsRenderingContext&     aRenderingContext,
--- a/layout/mathml/nsMathMLOperators.cpp
+++ b/layout/mathml/nsMathMLOperators.cpp
@@ -112,16 +112,18 @@ SetBooleanProperty(OperatorData* aOperat
   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;
+  else if (aName.EqualsLiteral("mirrorable"))
+    aOperatorData->mFlags |= NS_MATHML_OPERATOR_MIRRORABLE;
 }
 
 static void
 SetProperty(OperatorData* aOperatorData,
             nsString      aName,
             nsString      aValue)
 {
   if (aName.IsEmpty() || aValue.IsEmpty())
@@ -510,16 +512,30 @@ nsMathMLOperators::IsMutableOperator(con
   nsOperatorFlags allFlags =
     flags[NS_MATHML_OPERATOR_FORM_INFIX] |
     flags[NS_MATHML_OPERATOR_FORM_POSTFIX] |
     flags[NS_MATHML_OPERATOR_FORM_PREFIX];
   return NS_MATHML_OPERATOR_IS_STRETCHY(allFlags) ||
          NS_MATHML_OPERATOR_IS_LARGEOP(allFlags);
 }
 
+/* static */ bool
+nsMathMLOperators::IsMirrorableOperator(const nsString& aOperator)
+{
+  // LookupOperator will search infix, postfix and prefix forms of aOperator and
+  // return the first form found. It is assumed that all these forms have same
+  // mirrorability.
+  nsOperatorFlags flags = 0;
+  float dummy;
+  nsMathMLOperators::LookupOperator(aOperator,
+                                    NS_MATHML_OPERATOR_FORM_INFIX,
+                                    &flags, &dummy, &dummy);
+  return NS_MATHML_OPERATOR_IS_MIRRORABLE(flags);
+}
+
 /* static */ nsStretchDirection
 nsMathMLOperators::GetStretchyDirection(const nsString& aOperator)
 {
   // LookupOperator will search infix, postfix and prefix forms of aOperator and
   // return the first form found. It is assumed that all these forms have same
   // direction.
   nsOperatorFlags flags = 0;
   float dummy;
--- a/layout/mathml/nsMathMLOperators.h
+++ b/layout/mathml/nsMathMLOperators.h
@@ -76,22 +76,23 @@ enum {
   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,
+  NS_MATHML_OPERATOR_MIRRORABLE         = 1<<12,
 
   // Additional bits not stored in the dictionary
-  NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE   = 1<<12,
-  NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE   = 1<<13,
-  NS_MATHML_OPERATOR_LSPACE_ATTR     = 1<<14,
-  NS_MATHML_OPERATOR_RSPACE_ATTR    = 1<<15
+  NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE   = 1<<13,
+  NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE   = 1<<14,
+  NS_MATHML_OPERATOR_LSPACE_ATTR     = 1<<15,
+  NS_MATHML_OPERATOR_RSPACE_ATTR    = 1<<16
 };
 
 #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,
@@ -145,16 +146,20 @@ public:
                    float*                aLeadingSpace,
                    float*                aTrailingSpace);
 
   // IsMutableOperator:
   // Return true if the operator exists and is stretchy or largeop
   static bool
   IsMutableOperator(const nsString& aOperator);
 
+  // Helper functions used by the nsMathMLChar class.
+  static bool
+  IsMirrorableOperator(const nsString& aOperator);
+
   // Helper function used by the nsMathMLChar class.
   static nsStretchDirection GetStretchyDirection(const nsString& aOperator);
 
   // Return the variant type of one Unicode Mathematical Alphanumeric Symbol
   // character (which may be represented by a surrogate pair), or return
   // eMATHVARIANT_NONE if aChar is not such a character.
   static eMATHVARIANT LookupInvariantChar(const nsAString& aChar);
 
@@ -225,16 +230,19 @@ public:
   (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_IS_MIRRORABLE(_flags) \
+  (NS_MATHML_OPERATOR_MIRRORABLE == ((_flags) & NS_MATHML_OPERATOR_MIRRORABLE))
+
 #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_LSPACE_ATTR(_flags) \
   (NS_MATHML_OPERATOR_LSPACE_ATTR == ((_flags) & NS_MATHML_OPERATOR_LSPACE_ATTR))
--- a/layout/mathml/nsMathMLmencloseFrame.cpp
+++ b/layout/mathml/nsMathMLmencloseFrame.cpp
@@ -492,17 +492,17 @@ nsMathMLmencloseFrame::PlaceInternal(nsR
       nsBoundingMetrics contSize = bmBase;
       contSize.ascent = mRuleThickness;
       contSize.descent = bmBase.ascent + bmBase.descent + psi;
 
       // height(longdiv) should be >= height(base) + psi + mRuleThickness
       mMathMLChar[mLongDivCharIndex].Stretch(PresContext(), aRenderingContext,
                                              NS_STRETCH_DIRECTION_VERTICAL,
                                              contSize, bmLongdivChar,
-                                             NS_STRETCH_LARGER);
+                                             NS_STRETCH_LARGER, false);
       mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar);
 
       // Update horizontal parameters
       dx_left = NS_MAX(dx_left, bmLongdivChar.width);
 
       // Update vertical parameters
       longdivAscent = bmBase.ascent + psi + mRuleThickness;
       longdivDescent = NS_MAX(bmBase.descent,
@@ -534,17 +534,18 @@ nsMathMLmencloseFrame::PlaceInternal(nsR
       nsBoundingMetrics contSize = bmBase;
       contSize.ascent = mRuleThickness;
       contSize.descent = bmBase.ascent + bmBase.descent + psi;
 
       // height(radical) should be >= height(base) + psi + mRuleThickness
       mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aRenderingContext,
                                              NS_STRETCH_DIRECTION_VERTICAL,
                                              contSize, bmRadicalChar,
-                                             NS_STRETCH_LARGER);
+                                             NS_STRETCH_LARGER,
+                                             NS_MATHML_IS_RTL(mPresentationData.flags));
       mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar);
 
       // Update horizontal parameters
       *dx_leading = NS_MAX(*dx_leading, bmRadicalChar.width);
 
       // Update vertical parameters
       radicalAscent = bmBase.ascent + psi + mRuleThickness;
       radicalDescent = NS_MAX(bmBase.descent,
--- a/layout/mathml/nsMathMLmfencedFrame.cpp
+++ b/layout/mathml/nsMathMLmfencedFrame.cpp
@@ -352,44 +352,45 @@ nsMathMLmfencedFrame::Reflow(nsPresConte
   // adjust the origin of children.
 
   // we need to center around the axis
   nscoord delta = NS_MAX(containerSize.ascent - axisHeight, 
                          containerSize.descent + axisHeight);
   containerSize.ascent = delta + axisHeight;
   containerSize.descent = delta - axisHeight;
 
+  bool isRTL = NS_MATHML_IS_RTL(mPresentationData.flags);
+
   /////////////////
   // opening fence ...
   ReflowChar(aPresContext, *aReflowState.rendContext, mOpenChar,
              NS_MATHML_OPERATOR_FORM_PREFIX, font->mScriptLevel, 
-             axisHeight, leading, em, containerSize, ascent, descent);
+             axisHeight, leading, em, containerSize, ascent, descent, isRTL);
   /////////////////
   // separators ...
   for (i = 0; i < mSeparatorsCount; i++) {
     ReflowChar(aPresContext, *aReflowState.rendContext, &mSeparatorsChar[i],
                NS_MATHML_OPERATOR_FORM_INFIX, font->mScriptLevel,
-               axisHeight, leading, em, containerSize, ascent, descent);
+               axisHeight, leading, em, containerSize, ascent, descent, isRTL);
   }
   /////////////////
   // closing fence ...
   ReflowChar(aPresContext, *aReflowState.rendContext, mCloseChar,
              NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel,
-             axisHeight, leading, em, containerSize, ascent, descent);
+             axisHeight, leading, em, containerSize, ascent, descent, isRTL);
 
   //////////////////
   // Adjust the origins of each child.
   // and update our bounding metrics
 
   i = 0;
   nscoord dx = 0;
   nsBoundingMetrics bm;
   bool firstTime = true;
   nsMathMLChar *leftChar, *rightChar;
-  bool isRTL = NS_MATHML_IS_RTL(mPresentationData.flags);
   if (isRTL) {
     leftChar = mCloseChar;
     rightChar = mOpenChar;
   } else {
     leftChar = mOpenChar;
     rightChar = mCloseChar;
   }
 
@@ -494,28 +495,30 @@ nsMathMLmfencedFrame::ReflowChar(nsPresC
                                  nsMathMLChar*        aMathMLChar,
                                  nsOperatorFlags      aForm,
                                  PRInt32              aScriptLevel,
                                  nscoord              axisHeight,
                                  nscoord              leading,
                                  nscoord              em,
                                  nsBoundingMetrics&   aContainerSize,
                                  nscoord&             aAscent,
-                                 nscoord&             aDescent)
+                                 nscoord&             aDescent,
+                                 bool                 aRTL)
 {
   if (aMathMLChar && 0 < aMathMLChar->Length()) {
     nscoord leftSpace;
     nscoord rightSpace;
     GetCharSpacing(aMathMLChar, aForm, aScriptLevel, em, leftSpace, rightSpace);
 
     // stretch the char to the appropriate height if it is not big enough.
     nsBoundingMetrics charSize;
     nsresult res = aMathMLChar->Stretch(aPresContext, aRenderingContext,
                                         NS_STRETCH_DIRECTION_VERTICAL,
-                                        aContainerSize, charSize);
+                                        aContainerSize, charSize,
+                                        NS_STRETCH_NORMAL, aRTL);
 
     if (NS_STRETCH_DIRECTION_UNSUPPORTED != aMathMLChar->GetStretchDirection()) {
       // has changed... so center the char around the axis
       nscoord height = charSize.ascent + charSize.descent;
       charSize.ascent = height/2 + axisHeight;
       charSize.descent = height - charSize.ascent;
     }
     else {
--- a/layout/mathml/nsMathMLmfencedFrame.h
+++ b/layout/mathml/nsMathMLmfencedFrame.h
@@ -100,17 +100,18 @@ public:
              nsMathMLChar*        aMathMLChar,
              nsOperatorFlags      aForm,
              PRInt32              aScriptLevel,
              nscoord              axisHeight,
              nscoord              leading,
              nscoord              em,
              nsBoundingMetrics&   aContainerSize,
              nscoord&             aAscent,
-             nscoord&             aDescent);
+             nscoord&             aDescent,
+             bool                 aRTL);
 
   static void
   PlaceChar(nsMathMLChar*      aMathMLChar,
             nscoord            aDesiredSize,
             nsBoundingMetrics& bm,
             nscoord&           dx);
 
 protected:
--- a/layout/mathml/nsMathMLmoFrame.cpp
+++ b/layout/mathml/nsMathMLmoFrame.cpp
@@ -755,17 +755,20 @@ nsMathMLmoFrame::Stretch(nsRenderingCont
           container.descent = aContainerSize.descent;
           container.ascent = height - container.descent;
         }
       }
     }
 
     // let the MathMLChar stretch itself...
     nsresult res = mMathMLChar.Stretch(PresContext(), aRenderingContext,
-                                       aStretchDirection, container, charSize, stretchHint);
+                                       aStretchDirection, container, charSize,
+                                       stretchHint,
+                                       NS_MATHML_IS_RTL(mPresentationData.
+                                                        flags));
     if (NS_FAILED(res)) {
       // gracefully handle cases where stretching the char failed (i.e., GetBoundingMetrics failed)
       // clear our 'form' to behave as if the operator wasn't in the dictionary
       mFlags &= ~NS_MATHML_OPERATOR_FORM;
       useMathMLChar = false;
     }
   }
 
--- a/layout/mathml/nsMathMLmrootFrame.cpp
+++ b/layout/mathml/nsMathMLmrootFrame.cpp
@@ -306,17 +306,18 @@ nsMathMLmrootFrame::Reflow(nsPresContext
   contSize.descent = bmBase.ascent + bmBase.descent + psi;
   contSize.ascent = ruleThickness;
 
   // height(radical) should be >= height(base) + psi + ruleThickness
   nsBoundingMetrics radicalSize;
   mSqrChar.Stretch(aPresContext, renderingContext,
                    NS_STRETCH_DIRECTION_VERTICAL, 
                    contSize, radicalSize,
-                   NS_STRETCH_LARGER);
+                   NS_STRETCH_LARGER,
+                   NS_MATHML_IS_RTL(mPresentationData.flags));
   // radicalSize have changed at this point, and should match with
   // the bounding metrics of the char
   mSqrChar.GetBoundingMetrics(bmSqr);
 
   // Update the desired size for the container (like msqrt, index is not yet included)
   // the baseline will be that of the base.
   mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness;
   mBoundingMetrics.descent =