layout/mathml/nsMathMLmrootFrame.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

     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 "nsMathMLmrootFrame.h"
     7 #include "nsPresContext.h"
     8 #include "nsRenderingContext.h"
     9 #include <algorithm>
    11 //
    12 // <msqrt> and <mroot> -- form a radical - implementation
    13 //
    15 //NOTE:
    16 //  The code assumes that TeX fonts are picked.
    17 //  There is no fall-back to draw the branches of the sqrt explicitly
    18 //  in the case where TeX fonts are not there. In general, there are no
    19 //  fall-back(s) in MathML when some (freely-downloadable) fonts are missing.
    20 //  Otherwise, this will add much work and unnecessary complexity to the core
    21 //  MathML  engine. Assuming that authors have the free fonts is part of the
    22 //  deal. We are not responsible for cases of misconfigurations out there.
    24 // additional style context to be used by our MathMLChar.
    25 #define NS_SQR_CHAR_STYLE_CONTEXT_INDEX   0
    27 static const char16_t kSqrChar = char16_t(0x221A);
    29 nsIFrame*
    30 NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    31 {
    32   return new (aPresShell) nsMathMLmrootFrame(aContext);
    33 }
    35 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame)
    37 nsMathMLmrootFrame::nsMathMLmrootFrame(nsStyleContext* aContext) :
    38   nsMathMLContainerFrame(aContext),
    39   mSqrChar(),
    40   mBarRect()
    41 {
    42 }
    44 nsMathMLmrootFrame::~nsMathMLmrootFrame()
    45 {
    46 }
    48 void
    49 nsMathMLmrootFrame::Init(nsIContent*      aContent,
    50                          nsIFrame*        aParent,
    51                          nsIFrame*        aPrevInFlow)
    52 {
    53   nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
    55   nsPresContext *presContext = PresContext();
    57   // No need to track the style context given to our MathML char. 
    58   // The Style System will use Get/SetAdditionalStyleContext() to keep it
    59   // up-to-date if dynamic changes arise.
    60   nsAutoString sqrChar; sqrChar.Assign(kSqrChar);
    61   mSqrChar.SetData(presContext, sqrChar);
    62   ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mSqrChar);
    63 }
    65 NS_IMETHODIMP
    66 nsMathMLmrootFrame::TransmitAutomaticData()
    67 {
    68   // 1. The REC says:
    69   //    The <mroot> element increments scriptlevel by 2, and sets displaystyle to
    70   //    "false", within index, but leaves both attributes unchanged within base.
    71   // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
    72   UpdatePresentationDataFromChildAt(1, 1,
    73                                     NS_MATHML_COMPRESSED,
    74                                     NS_MATHML_COMPRESSED);
    75   UpdatePresentationDataFromChildAt(0, 0,
    76      NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
    78   PropagateFrameFlagFor(mFrames.LastChild(),
    79                         NS_FRAME_MATHML_SCRIPT_DESCENDANT);
    81   return NS_OK;
    82 }
    84 void
    85 nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
    86                                      const nsRect&           aDirtyRect,
    87                                      const nsDisplayListSet& aLists)
    88 {
    89   /////////////
    90   // paint the content we are square-rooting
    91   nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
    93   /////////////
    94   // paint the sqrt symbol
    95   if (!NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
    96     mSqrChar.Display(aBuilder, this, aLists, 0);
    98     DisplayBar(aBuilder, this, mBarRect, aLists);
   100 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
   101     // for visual debug
   102     nsRect rect;
   103     mSqrChar.GetRect(rect);
   104     nsBoundingMetrics bm;
   105     mSqrChar.GetBoundingMetrics(bm);
   106     DisplayBoundingMetrics(aBuilder, this, rect.TopLeft(), bm, aLists);
   107 #endif
   108   }
   109 }
   111 static void
   112 GetRadicalXOffsets(nscoord aIndexWidth, nscoord aSqrWidth,
   113                    nsFontMetrics* aFontMetrics,
   114                    nscoord* aIndexOffset, nscoord* aSqrOffset)
   115 {
   116   // The index is tucked in closer to the radical while making sure
   117   // that the kern does not make the index and radical collide
   118   nscoord dxIndex, dxSqr;
   119   nscoord xHeight = aFontMetrics->XHeight();
   120   nscoord indexRadicalKern = NSToCoordRound(1.35f * xHeight);
   121   if (indexRadicalKern > aIndexWidth) {
   122     dxIndex = indexRadicalKern - aIndexWidth;
   123     dxSqr = 0;
   124   }
   125   else {
   126     dxIndex = 0;
   127     dxSqr = aIndexWidth - indexRadicalKern;
   128   }
   129   // avoid collision by leaving a minimum space between index and radical
   130   nscoord minimumClearance = aSqrWidth/2;
   131   if (dxIndex + aIndexWidth + minimumClearance > dxSqr + aSqrWidth) {
   132     if (aIndexWidth + minimumClearance < aSqrWidth) {
   133       dxIndex = aSqrWidth - (aIndexWidth + minimumClearance);
   134       dxSqr = 0;
   135     }
   136     else {
   137       dxIndex = 0;
   138       dxSqr = (aIndexWidth + minimumClearance) - aSqrWidth;
   139     }
   140   }
   142   if (aIndexOffset)
   143     *aIndexOffset = dxIndex;
   144   if (aSqrOffset)
   145     *aSqrOffset = dxSqr;
   146 }
   148 nsresult
   149 nsMathMLmrootFrame::Reflow(nsPresContext*          aPresContext,
   150                            nsHTMLReflowMetrics&     aDesiredSize,
   151                            const nsHTMLReflowState& aReflowState,
   152                            nsReflowStatus&          aStatus)
   153 {
   154   nsresult rv = NS_OK;
   155   nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
   156   nsReflowStatus childStatus;
   158   aDesiredSize.Width() = aDesiredSize.Height() = 0;
   159   aDesiredSize.SetTopAscent(0);
   161   nsBoundingMetrics bmSqr, bmBase, bmIndex;
   162   nsRenderingContext& renderingContext = *aReflowState.rendContext;
   164   //////////////////
   165   // Reflow Children
   167   int32_t count = 0;
   168   nsIFrame* baseFrame = nullptr;
   169   nsIFrame* indexFrame = nullptr;
   170   nsHTMLReflowMetrics baseSize(aReflowState);
   171   nsHTMLReflowMetrics indexSize(aReflowState);
   172   nsIFrame* childFrame = mFrames.FirstChild();
   173   while (childFrame) {
   174     // ask our children to compute their bounding metrics 
   175     nsHTMLReflowMetrics childDesiredSize(aReflowState,
   176                                          aDesiredSize.mFlags
   177                                          | NS_REFLOW_CALC_BOUNDING_METRICS);
   178     nsHTMLReflowState childReflowState(aPresContext, aReflowState,
   179                                        childFrame, availSize);
   180     rv = ReflowChild(childFrame, aPresContext,
   181                      childDesiredSize, childReflowState, childStatus);
   182     //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
   183     if (NS_FAILED(rv)) {
   184       // Call DidReflow() for the child frames we successfully did reflow.
   185       DidReflowChildren(mFrames.FirstChild(), childFrame);
   186       return rv;
   187     }
   188     if (0 == count) {
   189       // base 
   190       baseFrame = childFrame;
   191       baseSize = childDesiredSize;
   192       bmBase = childDesiredSize.mBoundingMetrics;
   193     }
   194     else if (1 == count) {
   195       // index
   196       indexFrame = childFrame;
   197       indexSize = childDesiredSize;
   198       bmIndex = childDesiredSize.mBoundingMetrics;
   199     }
   200     count++;
   201     childFrame = childFrame->GetNextSibling();
   202   }
   203   if (2 != count) {
   204     // report an error, encourage people to get their markups in order
   205     ReportChildCountError();
   206     rv = ReflowError(renderingContext, aDesiredSize);
   207     aStatus = NS_FRAME_COMPLETE;
   208     NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   209     // Call DidReflow() for the child frames we successfully did reflow.
   210     DidReflowChildren(mFrames.FirstChild(), childFrame);
   211     return rv;
   212   }
   214   ////////////
   215   // Prepare the radical symbol and the overline bar
   217   nsRefPtr<nsFontMetrics> fm;
   218   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
   219   renderingContext.SetFont(fm);
   221   // For radical glyphs from TeX fonts and some of the radical glyphs from
   222   // Mathematica fonts, the thickness of the overline can be obtained from the
   223   // ascent of the glyph.  Most fonts however have radical glyphs above the
   224   // baseline so no assumption can be made about the meaning of the ascent.
   225   nscoord ruleThickness, leading, em;
   226   GetRuleThickness(renderingContext, fm, ruleThickness);
   228   char16_t one = '1';
   229   nsBoundingMetrics bmOne = renderingContext.GetBoundingMetrics(&one, 1);
   231   // get the leading to be left at the top of the resulting frame
   232   // this seems more reliable than using fm->GetLeading() on suspicious fonts
   233   GetEmHeight(fm, em);
   234   leading = nscoord(0.2f * em); 
   236   // Rule 11, App. G, TeXbook
   237   // psi = clearance between rule and content
   238   nscoord phi = 0, psi = 0;
   239   if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK)
   240     phi = fm->XHeight();
   241   else
   242     phi = ruleThickness;
   243   psi = ruleThickness + phi/4;
   245   // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
   246   if (bmOne.ascent > bmBase.ascent)
   247     psi += bmOne.ascent - bmBase.ascent;
   249   // make sure that the rule appears on on screen
   250   nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
   251   if (ruleThickness < onePixel) {
   252     ruleThickness = onePixel;
   253   }
   255   // adjust clearance psi to get an exact number of pixels -- this
   256   // gives a nicer & uniform look on stacked radicals (bug 130282)
   257   nscoord delta = psi % onePixel;
   258   if (delta)
   259     psi += onePixel - delta; // round up
   261   // Stretch the radical symbol to the appropriate height if it is not big enough.
   262   nsBoundingMetrics contSize = bmBase;
   263   contSize.descent = bmBase.ascent + bmBase.descent + psi;
   264   contSize.ascent = ruleThickness;
   266   // height(radical) should be >= height(base) + psi + ruleThickness
   267   nsBoundingMetrics radicalSize;
   268   mSqrChar.Stretch(aPresContext, renderingContext,
   269                    NS_STRETCH_DIRECTION_VERTICAL, 
   270                    contSize, radicalSize,
   271                    NS_STRETCH_LARGER,
   272                    StyleVisibility()->mDirection);
   273   // radicalSize have changed at this point, and should match with
   274   // the bounding metrics of the char
   275   mSqrChar.GetBoundingMetrics(bmSqr);
   277   // Update the desired size for the container (like msqrt, index is not yet included)
   278   // the baseline will be that of the base.
   279   mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness;
   280   mBoundingMetrics.descent = 
   281     std::max(bmBase.descent,
   282            (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent));
   283   mBoundingMetrics.width = bmSqr.width + bmBase.width;
   284   mBoundingMetrics.leftBearing = bmSqr.leftBearing;
   285   mBoundingMetrics.rightBearing = bmSqr.width + 
   286     std::max(bmBase.width, bmBase.rightBearing); // take also care of the rule
   288   aDesiredSize.SetTopAscent(mBoundingMetrics.ascent + leading);
   289   aDesiredSize.Height() = aDesiredSize.TopAscent() +
   290     std::max(baseSize.Height() - baseSize.TopAscent(),
   291            mBoundingMetrics.descent + ruleThickness);
   292   aDesiredSize.Width() = mBoundingMetrics.width;
   294   /////////////
   295   // Re-adjust the desired size to include the index.
   297   // the index is raised by some fraction of the height
   298   // of the radical, see \mroot macro in App. B, TexBook
   299   nscoord raiseIndexDelta = NSToCoordRound(0.6f * (bmSqr.ascent + bmSqr.descent));
   300   nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical 
   301     - (bmSqr.ascent + bmSqr.descent) // to bottom of radical
   302     + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index
   304   nscoord indexClearance = 0;
   305   if (mBoundingMetrics.ascent < indexRaisedAscent) {
   306     indexClearance = 
   307       indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index 
   308     mBoundingMetrics.ascent = indexRaisedAscent;
   309     nscoord descent = aDesiredSize.Height() - aDesiredSize.TopAscent();
   310     aDesiredSize.SetTopAscent(mBoundingMetrics.ascent + leading);
   311     aDesiredSize.Height() = aDesiredSize.TopAscent() + descent;
   312   }
   314   nscoord dxIndex, dxSqr;
   315   GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr);
   317   mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width;
   318   mBoundingMetrics.leftBearing = 
   319     std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
   320   mBoundingMetrics.rightBearing = dxSqr + bmSqr.width +
   321     std::max(bmBase.width, bmBase.rightBearing);
   323   aDesiredSize.Width() = mBoundingMetrics.width;
   324   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
   325   GatherAndStoreOverflow(&aDesiredSize);
   327   // place the index
   328   nscoord dx = dxIndex;
   329   nscoord dy = aDesiredSize.TopAscent() - (indexRaisedAscent + indexSize.TopAscent() - bmIndex.ascent);
   330   FinishReflowChild(indexFrame, aPresContext, indexSize, nullptr,
   331                     MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx),
   332                     dy, 0);
   334   // place the radical symbol and the radical bar
   335   dx = dxSqr;
   336   dy = indexClearance + leading; // leave a leading at the top
   337   mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx),
   338                           dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
   339   dx += bmSqr.width;
   340   mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(), bmBase.width, dx),
   341                    dy, bmBase.width, ruleThickness);
   343   // place the base
   344   dy = aDesiredSize.TopAscent() - baseSize.TopAscent();
   345   FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr,
   346                     MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx),
   347                     dy, 0);
   349   mReference.x = 0;
   350   mReference.y = aDesiredSize.TopAscent();
   352   aStatus = NS_FRAME_COMPLETE;
   353   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   354   return NS_OK;
   355 }
   357 /* virtual */ void
   358 nsMathMLmrootFrame::GetIntrinsicWidthMetrics(nsRenderingContext* aRenderingContext, nsHTMLReflowMetrics& aDesiredSize)
   359 {
   360   nsIFrame* baseFrame = mFrames.FirstChild();
   361   nsIFrame* indexFrame = nullptr;
   362   if (baseFrame)
   363     indexFrame = baseFrame->GetNextSibling();
   364   if (!indexFrame || indexFrame->GetNextSibling()) {
   365     ReflowError(*aRenderingContext, aDesiredSize);
   366     return;
   367   }
   369   nscoord baseWidth =
   370     nsLayoutUtils::IntrinsicForContainer(aRenderingContext, baseFrame,
   371                                          nsLayoutUtils::PREF_WIDTH);
   372   nscoord indexWidth =
   373     nsLayoutUtils::IntrinsicForContainer(aRenderingContext, indexFrame,
   374                                          nsLayoutUtils::PREF_WIDTH);
   375   nscoord sqrWidth = mSqrChar.GetMaxWidth(PresContext(), *aRenderingContext);
   377   nscoord dxSqr;
   378   nsRefPtr<nsFontMetrics> fm;
   379   nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
   380   GetRadicalXOffsets(indexWidth, sqrWidth, fm, nullptr, &dxSqr);
   382   nscoord width = dxSqr + sqrWidth + baseWidth;
   384   aDesiredSize.Width() = width;
   385   aDesiredSize.mBoundingMetrics.width = width;
   386   aDesiredSize.mBoundingMetrics.leftBearing = 0;
   387   aDesiredSize.mBoundingMetrics.rightBearing = width;
   388 }
   390 // ----------------------
   391 // the Style System will use these to pass the proper style context to our MathMLChar
   392 nsStyleContext*
   393 nsMathMLmrootFrame::GetAdditionalStyleContext(int32_t aIndex) const
   394 {
   395   switch (aIndex) {
   396   case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
   397     return mSqrChar.GetStyleContext();
   398     break;
   399   default:
   400     return nullptr;
   401   }
   402 }
   404 void
   405 nsMathMLmrootFrame::SetAdditionalStyleContext(int32_t          aIndex, 
   406                                               nsStyleContext*  aStyleContext)
   407 {
   408   switch (aIndex) {
   409   case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
   410     mSqrChar.SetStyleContext(aStyleContext);
   411     break;
   412   }
   413 }

mercurial