layout/mathml/nsMathMLmencloseFrame.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsMathMLmencloseFrame.h"
     7 #include "nsPresContext.h"
     8 #include "nsRenderingContext.h"
     9 #include "nsWhitespaceTokenizer.h"
    11 #include "nsDisplayList.h"
    12 #include "gfxContext.h"
    13 #include "nsMathMLChar.h"
    14 #include <algorithm>
    16 //
    17 // <menclose> -- enclose content with a stretching symbol such
    18 // as a long division sign. - implementation
    20 // longdiv:
    21 // Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis
    22 // renders better with current font support.
    23 static const char16_t kLongDivChar = ')';
    25 // radical: 'SQUARE ROOT'
    26 static const char16_t kRadicalChar = 0x221A;
    28 // updiagonalstrike
    29 static const uint8_t kArrowHeadSize = 10;
    31 nsIFrame*
    32 NS_NewMathMLmencloseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    33 {
    34   return new (aPresShell) nsMathMLmencloseFrame(aContext);
    35 }
    37 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame)
    39 nsMathMLmencloseFrame::nsMathMLmencloseFrame(nsStyleContext* aContext) :
    40   nsMathMLContainerFrame(aContext), mNotationsToDraw(0),
    41   mLongDivCharIndex(-1), mRadicalCharIndex(-1), mContentWidth(0)
    42 {
    43 }
    45 nsMathMLmencloseFrame::~nsMathMLmencloseFrame()
    46 {
    47 }
    49 nsresult nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask)
    50 {
    51   // Is the char already allocated?
    52   if ((mask == NOTATION_LONGDIV && mLongDivCharIndex >= 0) ||
    53       (mask == NOTATION_RADICAL && mRadicalCharIndex >= 0))
    54     return NS_OK;
    56   // No need to track the style context given to our MathML chars.
    57   // The Style System will use Get/SetAdditionalStyleContext() to keep it
    58   // up-to-date if dynamic changes arise.
    59   uint32_t i = mMathMLChar.Length();
    60   nsAutoString Char;
    62   if (!mMathMLChar.AppendElement())
    63     return NS_ERROR_OUT_OF_MEMORY;
    65   if (mask == NOTATION_LONGDIV) {
    66     Char.Assign(kLongDivChar);
    67     mLongDivCharIndex = i;
    68   } else if (mask == NOTATION_RADICAL) {
    69     Char.Assign(kRadicalChar);
    70     mRadicalCharIndex = i;
    71   }
    73   nsPresContext *presContext = PresContext();
    74   mMathMLChar[i].SetData(presContext, Char);
    75   ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar[i]);
    77   return NS_OK;
    78 }
    80 /*
    81  * Add a notation to draw, if the argument is the name of a known notation.
    82  * @param aNotation string name of a notation
    83  */
    84 nsresult nsMathMLmencloseFrame::AddNotation(const nsAString& aNotation)
    85 {
    86   nsresult rv;
    88   if (aNotation.EqualsLiteral("longdiv")) {
    89     rv = AllocateMathMLChar(NOTATION_LONGDIV);
    90     NS_ENSURE_SUCCESS(rv, rv);
    91     mNotationsToDraw |= NOTATION_LONGDIV;
    92   } else if (aNotation.EqualsLiteral("actuarial")) {
    93     mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_TOP);
    94   } else if (aNotation.EqualsLiteral("radical")) {
    95     rv = AllocateMathMLChar(NOTATION_RADICAL);
    96     NS_ENSURE_SUCCESS(rv, rv);
    97     mNotationsToDraw |= NOTATION_RADICAL;
    98   } else if (aNotation.EqualsLiteral("box")) {
    99     mNotationsToDraw |= (NOTATION_LEFT | NOTATION_RIGHT |
   100                          NOTATION_TOP | NOTATION_BOTTOM);
   101   } else if (aNotation.EqualsLiteral("roundedbox")) {
   102     mNotationsToDraw |= NOTATION_ROUNDEDBOX;
   103   } else if (aNotation.EqualsLiteral("circle")) {
   104     mNotationsToDraw |= NOTATION_CIRCLE;
   105   } else if (aNotation.EqualsLiteral("left")) {
   106     mNotationsToDraw |= NOTATION_LEFT;
   107   } else if (aNotation.EqualsLiteral("right")) {
   108     mNotationsToDraw |= NOTATION_RIGHT;
   109   } else if (aNotation.EqualsLiteral("top")) {
   110     mNotationsToDraw |= NOTATION_TOP;
   111   } else if (aNotation.EqualsLiteral("bottom")) {
   112     mNotationsToDraw |= NOTATION_BOTTOM;
   113   } else if (aNotation.EqualsLiteral("updiagonalstrike")) {
   114     mNotationsToDraw |= NOTATION_UPDIAGONALSTRIKE;
   115   } else if (aNotation.EqualsLiteral("updiagonalarrow")) {
   116     mNotationsToDraw |= NOTATION_UPDIAGONALARROW;
   117   } else if (aNotation.EqualsLiteral("downdiagonalstrike")) {
   118     mNotationsToDraw |= NOTATION_DOWNDIAGONALSTRIKE;
   119   } else if (aNotation.EqualsLiteral("verticalstrike")) {
   120     mNotationsToDraw |= NOTATION_VERTICALSTRIKE;
   121   } else if (aNotation.EqualsLiteral("horizontalstrike")) {
   122     mNotationsToDraw |= NOTATION_HORIZONTALSTRIKE;
   123   } else if (aNotation.EqualsLiteral("madruwb")) {
   124     mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_BOTTOM);
   125   }
   127   return NS_OK;
   128 }
   130 /*
   131  * Initialize the list of notations to draw
   132  */
   133 void nsMathMLmencloseFrame::InitNotations()
   134 {
   135   mNotationsToDraw = 0;
   136   mLongDivCharIndex = mRadicalCharIndex = -1;
   137   mMathMLChar.Clear();
   139   nsAutoString value;
   141   if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::notation_, value)) {
   142     // parse the notation attribute
   143     nsWhitespaceTokenizer tokenizer(value);
   145     while (tokenizer.hasMoreTokens())
   146       AddNotation(tokenizer.nextToken());
   148     if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
   149       // For <menclose notation="updiagonalstrike updiagonalarrow">, if
   150       // the two notations are drawn then the strike line may cause the point of
   151       // the arrow to be too wide. Hence we will only draw the updiagonalarrow
   152       // and the arrow shaft may be thought to be the updiagonalstrike.
   153       mNotationsToDraw &= ~NOTATION_UPDIAGONALSTRIKE;
   154     }
   155   } else {
   156     // default: longdiv
   157     if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV)))
   158       return;
   159     mNotationsToDraw = NOTATION_LONGDIV;
   160   }
   161 }
   163 NS_IMETHODIMP
   164 nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame* aParent)
   165 {
   166   // let the base class get the default from our parent
   167   nsMathMLContainerFrame::InheritAutomaticData(aParent);
   169   mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY;
   171   InitNotations();
   173   return NS_OK;
   174 }
   176 NS_IMETHODIMP
   177 nsMathMLmencloseFrame::TransmitAutomaticData()
   178 {
   179   if (IsToDraw(NOTATION_RADICAL)) {
   180     // The TeXBook (Ch 17. p.141) says that \sqrt is cramped 
   181     UpdatePresentationDataFromChildAt(0, -1,
   182                                       NS_MATHML_COMPRESSED,
   183                                       NS_MATHML_COMPRESSED);
   184   }
   186   return NS_OK;
   187 }
   189 void
   190 nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   191                                         const nsRect&           aDirtyRect,
   192                                         const nsDisplayListSet& aLists)
   193 {
   194   /////////////
   195   // paint the menclosed content
   196   nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
   198   if (NS_MATHML_HAS_ERROR(mPresentationData.flags))
   199     return;
   201   nsRect mencloseRect = nsIFrame::GetRect();
   202   mencloseRect.x = mencloseRect.y = 0;
   204   if (IsToDraw(NOTATION_RADICAL)) {
   205     mMathMLChar[mRadicalCharIndex].Display(aBuilder, this, aLists, 0);
   207     nsRect rect;
   208     mMathMLChar[mRadicalCharIndex].GetRect(rect);
   209     rect.MoveBy(StyleVisibility()->mDirection ? -mContentWidth : rect.width, 0);
   210     rect.SizeTo(mContentWidth, mRuleThickness);
   211     DisplayBar(aBuilder, this, rect, aLists);
   212   }
   214   if (IsToDraw(NOTATION_LONGDIV)) {
   215     mMathMLChar[mLongDivCharIndex].Display(aBuilder, this, aLists, 1);
   217     nsRect rect;
   218     mMathMLChar[mLongDivCharIndex].GetRect(rect);
   219     rect.SizeTo(rect.width + mContentWidth, mRuleThickness);
   220     DisplayBar(aBuilder, this, rect, aLists);
   221   }
   223   if (IsToDraw(NOTATION_TOP)) {
   224     nsRect rect(0, 0, mencloseRect.width, mRuleThickness);
   225     DisplayBar(aBuilder, this, rect, aLists);
   226   }
   228   if (IsToDraw(NOTATION_BOTTOM)) {
   229     nsRect rect(0, mencloseRect.height - mRuleThickness,
   230                 mencloseRect.width, mRuleThickness);
   231     DisplayBar(aBuilder, this, rect, aLists);
   232   }
   234   if (IsToDraw(NOTATION_LEFT)) {
   235     nsRect rect(0, 0, mRuleThickness, mencloseRect.height);
   236     DisplayBar(aBuilder, this, rect, aLists);
   237   }
   239   if (IsToDraw(NOTATION_RIGHT)) {
   240     nsRect rect(mencloseRect.width - mRuleThickness, 0,
   241                 mRuleThickness, mencloseRect.height);
   242     DisplayBar(aBuilder, this, rect, aLists);
   243   }
   245   if (IsToDraw(NOTATION_ROUNDEDBOX)) {
   246     DisplayNotation(aBuilder, this, mencloseRect, aLists,
   247                     mRuleThickness, NOTATION_ROUNDEDBOX);
   248   }
   250   if (IsToDraw(NOTATION_CIRCLE)) {
   251     DisplayNotation(aBuilder, this, mencloseRect, aLists,
   252                     mRuleThickness, NOTATION_CIRCLE);
   253   }
   255   if (IsToDraw(NOTATION_UPDIAGONALSTRIKE)) {
   256     DisplayNotation(aBuilder, this, mencloseRect, aLists,
   257                     mRuleThickness, NOTATION_UPDIAGONALSTRIKE);
   258   }
   260   if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
   261     DisplayNotation(aBuilder, this, mencloseRect, aLists,
   262                     mRuleThickness, NOTATION_UPDIAGONALARROW);
   263   }
   265   if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE)) {
   266     DisplayNotation(aBuilder, this, mencloseRect, aLists,
   267                     mRuleThickness, NOTATION_DOWNDIAGONALSTRIKE);
   268   }
   270   if (IsToDraw(NOTATION_HORIZONTALSTRIKE)) {
   271     nsRect rect(0, mencloseRect.height / 2 - mRuleThickness / 2,
   272                 mencloseRect.width, mRuleThickness);
   273     DisplayBar(aBuilder, this, rect, aLists);
   274   }
   276   if (IsToDraw(NOTATION_VERTICALSTRIKE)) {
   277     nsRect rect(mencloseRect.width / 2 - mRuleThickness / 2, 0,
   278                 mRuleThickness, mencloseRect.height);
   279     DisplayBar(aBuilder, this, rect, aLists);
   280   }
   281 }
   283 /* virtual */ nsresult
   284 nsMathMLmencloseFrame::MeasureForWidth(nsRenderingContext& aRenderingContext,
   285                                        nsHTMLReflowMetrics& aDesiredSize)
   286 {
   287   return PlaceInternal(aRenderingContext, false, aDesiredSize, true);
   288 }
   290 /* virtual */ nsresult
   291 nsMathMLmencloseFrame::Place(nsRenderingContext& aRenderingContext,
   292                              bool                 aPlaceOrigin,
   293                              nsHTMLReflowMetrics& aDesiredSize)
   294 {
   295   return PlaceInternal(aRenderingContext, aPlaceOrigin, aDesiredSize, false);
   296 }
   298 /* virtual */ nsresult
   299 nsMathMLmencloseFrame::PlaceInternal(nsRenderingContext& aRenderingContext,
   300                                      bool                 aPlaceOrigin,
   301                                      nsHTMLReflowMetrics& aDesiredSize,
   302                                      bool                 aWidthOnly)
   303 {
   304   ///////////////
   305   // Measure the size of our content using the base class to format like an
   306   // inferred mrow.
   307   nsHTMLReflowMetrics baseSize(aDesiredSize.GetWritingMode());
   308   nsresult rv =
   309     nsMathMLContainerFrame::Place(aRenderingContext, false, baseSize);
   311   if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
   312       DidReflowChildren(GetFirstPrincipalChild());
   313       return rv;
   314     }
   316   nsBoundingMetrics bmBase = baseSize.mBoundingMetrics;
   317   nscoord dx_left = 0, dx_right = 0;
   318   nsBoundingMetrics bmLongdivChar, bmRadicalChar;
   319   nscoord radicalAscent = 0, radicalDescent = 0;
   320   nscoord longdivAscent = 0, longdivDescent = 0;
   321   nscoord psi = 0;
   323   ///////////////
   324   // Thickness of bars and font metrics
   325   nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
   327   nscoord mEmHeight;
   328   nsRefPtr<nsFontMetrics> fm;
   329   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
   330   aRenderingContext.SetFont(fm);
   331   GetRuleThickness(aRenderingContext, fm, mRuleThickness);
   332   GetEmHeight(fm, mEmHeight);
   334   char16_t one = '1';
   335   nsBoundingMetrics bmOne = aRenderingContext.GetBoundingMetrics(&one, 1);
   337   ///////////////
   338   // General rules: the menclose element takes the size of the enclosed content.
   339   // We add a padding when needed.
   341   // determine padding & psi
   342   nscoord padding = 3 * mRuleThickness;
   343   nscoord delta = padding % onePixel;
   344   if (delta)
   345     padding += onePixel - delta; // round up
   347   if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
   348       nscoord phi;
   349       // Rule 11, App. G, TeXbook
   350       // psi = clearance between rule and content
   351       if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK)
   352         phi = fm->XHeight();
   353       else
   354         phi = mRuleThickness;
   355       psi = mRuleThickness + phi / 4;
   357       delta = psi % onePixel;
   358       if (delta)
   359         psi += onePixel - delta; // round up
   360     }
   362   if (mRuleThickness < onePixel)
   363     mRuleThickness = onePixel;
   365   // Set horizontal parameters
   366   if (IsToDraw(NOTATION_ROUNDEDBOX) ||
   367       IsToDraw(NOTATION_TOP) ||
   368       IsToDraw(NOTATION_LEFT) ||
   369       IsToDraw(NOTATION_BOTTOM) ||
   370       IsToDraw(NOTATION_CIRCLE))
   371     dx_left = padding;
   373   if (IsToDraw(NOTATION_ROUNDEDBOX) ||
   374       IsToDraw(NOTATION_TOP) ||
   375       IsToDraw(NOTATION_RIGHT) ||
   376       IsToDraw(NOTATION_BOTTOM) ||
   377       IsToDraw(NOTATION_CIRCLE))
   378     dx_right = padding;
   380   // Set vertical parameters
   381   if (IsToDraw(NOTATION_RIGHT) ||
   382       IsToDraw(NOTATION_LEFT) ||
   383       IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
   384       IsToDraw(NOTATION_UPDIAGONALARROW) ||
   385       IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
   386       IsToDraw(NOTATION_VERTICALSTRIKE) ||
   387       IsToDraw(NOTATION_CIRCLE) ||
   388       IsToDraw(NOTATION_ROUNDEDBOX) ||
   389       IsToDraw(NOTATION_RADICAL) ||
   390       IsToDraw(NOTATION_LONGDIV)) {
   391       // set a minimal value for the base height
   392       bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent);
   393       bmBase.descent = std::max(0, bmBase.descent);
   394   }
   396   mBoundingMetrics.ascent = bmBase.ascent;
   397   mBoundingMetrics.descent = bmBase.descent;
   399   if (IsToDraw(NOTATION_ROUNDEDBOX) ||
   400       IsToDraw(NOTATION_TOP) ||
   401       IsToDraw(NOTATION_LEFT) ||
   402       IsToDraw(NOTATION_RIGHT) ||
   403       IsToDraw(NOTATION_CIRCLE))
   404     mBoundingMetrics.ascent += padding;
   406   if (IsToDraw(NOTATION_ROUNDEDBOX) ||
   407       IsToDraw(NOTATION_LEFT) ||
   408       IsToDraw(NOTATION_RIGHT) ||
   409       IsToDraw(NOTATION_BOTTOM) ||
   410       IsToDraw(NOTATION_CIRCLE))
   411     mBoundingMetrics.descent += padding;
   413   ///////////////
   414   // updiagonal arrow notation. We need enough space at the top right corner to
   415   // draw the arrow head.
   416   if (IsToDraw(NOTATION_UPDIAGONALARROW)) {
   417     // This is an estimate, see nsDisplayNotation::Paint for the exact head size
   418     nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness;
   420     // We want that the arrow shaft strikes the menclose content and that the
   421     // arrow head does not overlap with that content. Hence we add some space
   422     // on the right. We don't add space on the top but only ensure that the
   423     // ascent is large enough.
   424     dx_right = std::max(dx_right, arrowHeadSize);
   425     mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize);
   426   }
   428   ///////////////
   429   // circle notation: we don't want the ellipse to overlap the enclosed
   430   // content. Hence, we need to increase the size of the bounding box by a
   431   // factor of at least sqrt(2).
   432   if (IsToDraw(NOTATION_CIRCLE)) {
   433     double ratio = (sqrt(2.0) - 1.0) / 2.0;
   434     nscoord padding2;
   436     // Update horizontal parameters
   437     padding2 = ratio * bmBase.width;
   439     dx_left = std::max(dx_left, padding2);
   440     dx_right = std::max(dx_right, padding2);
   442     // Update vertical parameters
   443     padding2 = ratio * (bmBase.ascent + bmBase.descent);
   445     mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
   446                                      bmBase.ascent + padding2);
   447     mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
   448                                       bmBase.descent + padding2);
   449   }
   451   ///////////////
   452   // longdiv notation:
   453   if (IsToDraw(NOTATION_LONGDIV)) {
   454     if (aWidthOnly) {
   455         nscoord longdiv_width = mMathMLChar[mLongDivCharIndex].
   456           GetMaxWidth(PresContext(), aRenderingContext);
   458         // Update horizontal parameters
   459         dx_left = std::max(dx_left, longdiv_width);
   460     } else {
   461       // Stretch the parenthesis to the appropriate height if it is not
   462       // big enough.
   463       nsBoundingMetrics contSize = bmBase;
   464       contSize.ascent = mRuleThickness;
   465       contSize.descent = bmBase.ascent + bmBase.descent + psi;
   467       // height(longdiv) should be >= height(base) + psi + mRuleThickness
   468       mMathMLChar[mLongDivCharIndex].Stretch(PresContext(), aRenderingContext,
   469                                              NS_STRETCH_DIRECTION_VERTICAL,
   470                                              contSize, bmLongdivChar,
   471                                              NS_STRETCH_LARGER, false);
   472       mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar);
   474       // Update horizontal parameters
   475       dx_left = std::max(dx_left, bmLongdivChar.width);
   477       // Update vertical parameters
   478       longdivAscent = bmBase.ascent + psi + mRuleThickness;
   479       longdivDescent = std::max(bmBase.descent,
   480                               (bmLongdivChar.ascent + bmLongdivChar.descent -
   481                                longdivAscent));
   483       mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
   484                                        longdivAscent);
   485       mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
   486                                         longdivDescent);
   487     }
   488   }
   490   ///////////////
   491   // radical notation:
   492   if (IsToDraw(NOTATION_RADICAL)) {
   493     nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left;
   495     if (aWidthOnly) {
   496       nscoord radical_width = mMathMLChar[mRadicalCharIndex].
   497         GetMaxWidth(PresContext(), aRenderingContext);
   499       // Update horizontal parameters
   500       *dx_leading = std::max(*dx_leading, radical_width);
   501     } else {
   502       // Stretch the radical symbol to the appropriate height if it is not
   503       // big enough.
   504       nsBoundingMetrics contSize = bmBase;
   505       contSize.ascent = mRuleThickness;
   506       contSize.descent = bmBase.ascent + bmBase.descent + psi;
   508       // height(radical) should be >= height(base) + psi + mRuleThickness
   509       mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aRenderingContext,
   510                                              NS_STRETCH_DIRECTION_VERTICAL,
   511                                              contSize, bmRadicalChar,
   512                                              NS_STRETCH_LARGER,
   513                                              StyleVisibility()->mDirection);
   514       mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar);
   516       // Update horizontal parameters
   517       *dx_leading = std::max(*dx_leading, bmRadicalChar.width);
   519       // Update vertical parameters
   520       radicalAscent = bmBase.ascent + psi + mRuleThickness;
   521       radicalDescent = std::max(bmBase.descent,
   522                               (bmRadicalChar.ascent + bmRadicalChar.descent -
   523                                radicalAscent));
   525       mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent,
   526                                        radicalAscent);
   527       mBoundingMetrics.descent = std::max(mBoundingMetrics.descent,
   528                                         radicalDescent);
   529     }
   530   }
   532   ///////////////
   533   //
   534   if (IsToDraw(NOTATION_CIRCLE) ||
   535       IsToDraw(NOTATION_ROUNDEDBOX) ||
   536       (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) {
   537     // center the menclose around the content (horizontally)
   538     dx_left = dx_right = std::max(dx_left, dx_right);
   539   }
   541   ///////////////
   542   // The maximum size is now computed: set the remaining parameters
   543   mBoundingMetrics.width = dx_left + bmBase.width + dx_right;
   545   mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing);
   546   mBoundingMetrics.rightBearing =
   547     std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing);
   549   aDesiredSize.Width() = mBoundingMetrics.width;
   551   aDesiredSize.SetTopAscent(std::max(mBoundingMetrics.ascent, baseSize.TopAscent()));
   552   aDesiredSize.Height() = aDesiredSize.TopAscent() +
   553     std::max(mBoundingMetrics.descent, baseSize.Height() - baseSize.TopAscent());
   555   if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) {
   556     // get the leading to be left at the top of the resulting frame
   557     // this seems more reliable than using fm->GetLeading() on suspicious
   558     // fonts
   559     nscoord leading = nscoord(0.2f * mEmHeight);
   560     nscoord desiredSizeAscent = aDesiredSize.TopAscent();
   561     nscoord desiredSizeDescent = aDesiredSize.Height() - aDesiredSize.TopAscent();
   563     if (IsToDraw(NOTATION_LONGDIV)) {
   564       desiredSizeAscent = std::max(desiredSizeAscent,
   565                                  longdivAscent + leading);
   566       desiredSizeDescent = std::max(desiredSizeDescent,
   567                                   longdivDescent + mRuleThickness);
   568     }
   570     if (IsToDraw(NOTATION_RADICAL)) {
   571       desiredSizeAscent = std::max(desiredSizeAscent,
   572                                  radicalAscent + leading);
   573       desiredSizeDescent = std::max(desiredSizeDescent,
   574                                   radicalDescent + mRuleThickness);
   575     }
   577     aDesiredSize.SetTopAscent(desiredSizeAscent);
   578     aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent;
   579   }
   581   if (IsToDraw(NOTATION_CIRCLE) ||
   582       IsToDraw(NOTATION_ROUNDEDBOX) ||
   583       (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) {
   584     // center the menclose around the content (vertically)
   585     nscoord dy = std::max(aDesiredSize.TopAscent() - bmBase.ascent,
   586                         aDesiredSize.Height() - aDesiredSize.TopAscent() -
   587                         bmBase.descent);
   589     aDesiredSize.SetTopAscent(bmBase.ascent + dy);
   590     aDesiredSize.Height() = aDesiredSize.TopAscent() + bmBase.descent + dy;
   591   }
   593   // Update mBoundingMetrics ascent/descent
   594   if (IsToDraw(NOTATION_TOP) ||
   595       IsToDraw(NOTATION_RIGHT) ||
   596       IsToDraw(NOTATION_LEFT) ||
   597       IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
   598       IsToDraw(NOTATION_UPDIAGONALARROW) ||
   599       IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
   600       IsToDraw(NOTATION_VERTICALSTRIKE) ||
   601       IsToDraw(NOTATION_CIRCLE) ||
   602       IsToDraw(NOTATION_ROUNDEDBOX))
   603     mBoundingMetrics.ascent = aDesiredSize.TopAscent();
   605   if (IsToDraw(NOTATION_BOTTOM) ||
   606       IsToDraw(NOTATION_RIGHT) ||
   607       IsToDraw(NOTATION_LEFT) ||
   608       IsToDraw(NOTATION_UPDIAGONALSTRIKE) ||
   609       IsToDraw(NOTATION_UPDIAGONALARROW) ||
   610       IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) ||
   611       IsToDraw(NOTATION_VERTICALSTRIKE) ||
   612       IsToDraw(NOTATION_CIRCLE) ||
   613       IsToDraw(NOTATION_ROUNDEDBOX))
   614     mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.TopAscent();
   616   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
   618   mReference.x = 0;
   619   mReference.y = aDesiredSize.TopAscent();
   621   if (aPlaceOrigin) {
   622     //////////////////
   623     // Set position and size of MathMLChars
   624     if (IsToDraw(NOTATION_LONGDIV))
   625       mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left -
   626                                                     bmLongdivChar.width,
   627                                                     aDesiredSize.TopAscent() -
   628                                                     longdivAscent,
   629                                                     bmLongdivChar.width,
   630                                                     bmLongdivChar.ascent +
   631                                                     bmLongdivChar.descent));
   633     if (IsToDraw(NOTATION_RADICAL)) {
   634       nscoord dx = (StyleVisibility()->mDirection ?
   635                     dx_left + bmBase.width : dx_left - bmRadicalChar.width);
   637       mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx,
   638                                                     aDesiredSize.TopAscent() -
   639                                                     radicalAscent,
   640                                                     bmRadicalChar.width,
   641                                                     bmRadicalChar.ascent +
   642                                                     bmRadicalChar.descent));
   643     }
   645     mContentWidth = bmBase.width;
   647     //////////////////
   648     // Finish reflowing child frames
   649     PositionRowChildFrames(dx_left, aDesiredSize.TopAscent());
   650   }
   652   return NS_OK;
   653 }
   655 nscoord
   656 nsMathMLmencloseFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize)
   657 {
   658   nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize);
   659   if (!gap)
   660     return 0;
   662   // Move the MathML characters
   663   nsRect rect;
   664   for (uint32_t i = 0; i < mMathMLChar.Length(); i++) {
   665     mMathMLChar[i].GetRect(rect);
   666     rect.MoveBy(gap, 0);
   667     mMathMLChar[i].SetRect(rect);
   668   }
   670   return gap;
   671 }
   673 nsresult
   674 nsMathMLmencloseFrame::AttributeChanged(int32_t         aNameSpaceID,
   675                                         nsIAtom*        aAttribute,
   676                                         int32_t         aModType)
   677 {
   678   if (aAttribute == nsGkAtoms::notation_) {
   679     InitNotations();
   680   }
   682   return nsMathMLContainerFrame::
   683     AttributeChanged(aNameSpaceID, aAttribute, aModType);
   684 }
   686 //////////////////
   687 // the Style System will use these to pass the proper style context to our
   688 // MathMLChar
   689 nsStyleContext*
   690 nsMathMLmencloseFrame::GetAdditionalStyleContext(int32_t aIndex) const
   691 {
   692   int32_t len = mMathMLChar.Length();
   693   if (aIndex >= 0 && aIndex < len)
   694     return mMathMLChar[aIndex].GetStyleContext();
   695   else
   696     return nullptr;
   697 }
   699 void
   700 nsMathMLmencloseFrame::SetAdditionalStyleContext(int32_t          aIndex, 
   701                                                  nsStyleContext*  aStyleContext)
   702 {
   703   int32_t len = mMathMLChar.Length();
   704   if (aIndex >= 0 && aIndex < len)
   705     mMathMLChar[aIndex].SetStyleContext(aStyleContext);
   706 }
   708 class nsDisplayNotation : public nsDisplayItem
   709 {
   710 public:
   711   nsDisplayNotation(nsDisplayListBuilder* aBuilder,
   712                     nsIFrame* aFrame, const nsRect& aRect,
   713                     nscoord aThickness, nsMencloseNotation aType)
   714     : nsDisplayItem(aBuilder, aFrame), mRect(aRect), 
   715       mThickness(aThickness), mType(aType) {
   716     MOZ_COUNT_CTOR(nsDisplayNotation);
   717   }
   718 #ifdef NS_BUILD_REFCNT_LOGGING
   719   virtual ~nsDisplayNotation() {
   720     MOZ_COUNT_DTOR(nsDisplayNotation);
   721   }
   722 #endif
   724   virtual void Paint(nsDisplayListBuilder* aBuilder,
   725                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
   726   NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION)
   728 private:
   729   nsRect             mRect;
   730   nscoord            mThickness;
   731   nsMencloseNotation mType;
   732 };
   734 void nsDisplayNotation::Paint(nsDisplayListBuilder* aBuilder,
   735                               nsRenderingContext* aCtx)
   736 {
   737   // get the gfxRect
   738   nsPresContext* presContext = mFrame->PresContext();
   739   gfxRect rect = presContext->AppUnitsToGfxUnits(mRect + ToReferenceFrame());
   741   // paint the frame with the current text color
   742   aCtx->SetColor(mFrame->GetVisitedDependentColor(eCSSProperty_color));
   744   // change line width to mThickness
   745   gfxContext *gfxCtx = aCtx->ThebesContext();
   746   gfxFloat e = presContext->AppUnitsToGfxUnits(mThickness);
   747   gfxCtx->Save();
   748   gfxCtx->SetLineWidth(e);
   750   rect.Deflate(e / 2.0);
   752   switch(mType)
   753     {
   754     case NOTATION_CIRCLE:
   755       gfxCtx->NewPath();
   756       gfxCtx->Ellipse(rect.Center(), rect.Size());
   757       gfxCtx->Stroke();
   758       break;
   760     case NOTATION_ROUNDEDBOX:
   761       gfxCtx->NewPath();
   762       gfxCtx->RoundedRectangle(rect, gfxCornerSizes(3 * e), true);
   763       gfxCtx->Stroke();
   764       break;
   766     case NOTATION_UPDIAGONALSTRIKE:
   767       gfxCtx->NewPath();
   768       gfxCtx->Line(rect.BottomLeft(), rect.TopRight());
   769       gfxCtx->Stroke();
   770       break;
   772     case NOTATION_DOWNDIAGONALSTRIKE:
   773       gfxCtx->NewPath();
   774       gfxCtx->Line(rect.TopLeft(), rect.BottomRight());
   775       gfxCtx->Stroke();
   776       break;
   778     case NOTATION_UPDIAGONALARROW: {
   779       // Compute some parameters to draw the updiagonalarrow. The values below
   780       // are taken from MathJax's HTML-CSS output.
   781       gfxFloat W = rect.Width(); gfxFloat H = rect.Height();
   782       gfxFloat l = sqrt(W*W + H*H);
   783       gfxFloat f = gfxFloat(kArrowHeadSize) * e / l;
   784       gfxFloat w = W * f; gfxFloat h = H * f;
   786       // Draw the arrow shaft
   787       gfxCtx->NewPath();
   788       gfxCtx->Line(rect.BottomLeft(), rect.TopRight() + gfxPoint(-.7*w, .7*h));
   789       gfxCtx->Stroke();
   791       // Draw the arrow head
   792       gfxCtx->NewPath();
   793       gfxPoint p[] = {
   794         rect.TopRight(),
   795         rect.TopRight() + gfxPoint(-w -.4*h, std::max(-e / 2.0, h - .4*w)),
   796         rect.TopRight() + gfxPoint(-.7*w, .7*h),
   797         rect.TopRight() + gfxPoint(std::min(e / 2.0, -w + .4*h), h + .4*w),
   798         rect.TopRight()
   799       };
   800       gfxCtx->Polygon(p, MOZ_ARRAY_LENGTH(p));
   801       gfxCtx->Fill();
   802     }
   803       break;
   805     default:
   806       NS_NOTREACHED("This notation can not be drawn using nsDisplayNotation");
   807       break;
   808     }
   810   gfxCtx->Restore();
   811 }
   813 void
   814 nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder* aBuilder,
   815                                        nsIFrame* aFrame, const nsRect& aRect,
   816                                        const nsDisplayListSet& aLists,
   817                                        nscoord aThickness,
   818                                        nsMencloseNotation aType)
   819 {
   820   if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty() ||
   821       aThickness <= 0)
   822     return;
   824   aLists.Content()->AppendNewToTop(new (aBuilder)
   825     nsDisplayNotation(aBuilder, aFrame, aRect, aThickness, aType));
   826 }

mercurial