summaryrefslogtreecommitdiffstats
path: root/layout/mathml/nsMathMLmrootFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/mathml/nsMathMLmrootFrame.cpp')
-rw-r--r--layout/mathml/nsMathMLmrootFrame.cpp419
1 files changed, 419 insertions, 0 deletions
diff --git a/layout/mathml/nsMathMLmrootFrame.cpp b/layout/mathml/nsMathMLmrootFrame.cpp
new file mode 100644
index 000000000..4c81bde3d
--- /dev/null
+++ b/layout/mathml/nsMathMLmrootFrame.cpp
@@ -0,0 +1,419 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMathMLmrootFrame.h"
+#include "nsPresContext.h"
+#include "nsRenderingContext.h"
+#include <algorithm>
+#include "gfxMathTable.h"
+
+using namespace mozilla;
+
+//
+// <mroot> -- form a radical - implementation
+//
+
+// additional style context to be used by our MathMLChar.
+#define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0
+
+static const char16_t kSqrChar = char16_t(0x221A);
+
+nsIFrame*
+NS_NewMathMLmrootFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
+{
+ return new (aPresShell) nsMathMLmrootFrame(aContext);
+}
+
+NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmrootFrame)
+
+nsMathMLmrootFrame::nsMathMLmrootFrame(nsStyleContext* aContext) :
+ nsMathMLContainerFrame(aContext),
+ mSqrChar(),
+ mBarRect()
+{
+}
+
+nsMathMLmrootFrame::~nsMathMLmrootFrame()
+{
+}
+
+void
+nsMathMLmrootFrame::Init(nsIContent* aContent,
+ nsContainerFrame* aParent,
+ nsIFrame* aPrevInFlow)
+{
+ nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
+
+ nsPresContext *presContext = PresContext();
+
+ // No need to track the style context given to our MathML char.
+ // The Style System will use Get/SetAdditionalStyleContext() to keep it
+ // up-to-date if dynamic changes arise.
+ nsAutoString sqrChar; sqrChar.Assign(kSqrChar);
+ mSqrChar.SetData(sqrChar);
+ ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mSqrChar);
+}
+
+NS_IMETHODIMP
+nsMathMLmrootFrame::TransmitAutomaticData()
+{
+ // 1. The REC says:
+ // The <mroot> element increments scriptlevel by 2, and sets displaystyle to
+ // "false", within index, but leaves both attributes unchanged within base.
+ // 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
+ UpdatePresentationDataFromChildAt(1, 1,
+ NS_MATHML_COMPRESSED,
+ NS_MATHML_COMPRESSED);
+ UpdatePresentationDataFromChildAt(0, 0,
+ NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
+
+ PropagateFrameFlagFor(mFrames.LastChild(),
+ NS_FRAME_MATHML_SCRIPT_DESCENDANT);
+
+ return NS_OK;
+}
+
+void
+nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
+ const nsRect& aDirtyRect,
+ const nsDisplayListSet& aLists)
+{
+ /////////////
+ // paint the content we are square-rooting
+ nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
+
+ /////////////
+ // paint the sqrt symbol
+ if (!NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
+ mSqrChar.Display(aBuilder, this, aLists, 0);
+
+ DisplayBar(aBuilder, this, mBarRect, aLists);
+
+#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
+ // for visual debug
+ nsRect rect;
+ mSqrChar.GetRect(rect);
+ nsBoundingMetrics bm;
+ mSqrChar.GetBoundingMetrics(bm);
+ DisplayBoundingMetrics(aBuilder, this, rect.TopLeft(), bm, aLists);
+#endif
+ }
+}
+
+void
+nsMathMLmrootFrame::GetRadicalXOffsets(nscoord aIndexWidth, nscoord aSqrWidth,
+ nsFontMetrics* aFontMetrics,
+ nscoord* aIndexOffset,
+ nscoord* aSqrOffset)
+{
+ // The index is tucked in closer to the radical while making sure
+ // that the kern does not make the index and radical collide
+ nscoord dxIndex, dxSqr;
+ nscoord xHeight = aFontMetrics->XHeight();
+ nscoord indexRadicalKern = NSToCoordRound(1.35f * xHeight);
+ nscoord oneDevPixel = aFontMetrics->AppUnitsPerDevPixel();
+ gfxFont* mathFont = aFontMetrics->GetThebesFontGroup()->GetFirstMathFont();
+ if (mathFont) {
+ indexRadicalKern =
+ mathFont->MathTable()->Constant(gfxMathTable::RadicalKernAfterDegree,
+ oneDevPixel);
+ indexRadicalKern = -indexRadicalKern;
+ }
+ if (indexRadicalKern > aIndexWidth) {
+ dxIndex = indexRadicalKern - aIndexWidth;
+ dxSqr = 0;
+ }
+ else {
+ dxIndex = 0;
+ dxSqr = aIndexWidth - indexRadicalKern;
+ }
+
+ if (mathFont) {
+ // add some kern before the radical index
+ nscoord indexRadicalKernBefore = 0;
+ indexRadicalKernBefore =
+ mathFont->MathTable()->Constant(gfxMathTable::RadicalKernBeforeDegree,
+ oneDevPixel);
+ dxIndex += indexRadicalKernBefore;
+ dxSqr += indexRadicalKernBefore;
+ } else {
+ // avoid collision by leaving a minimum space between index and radical
+ nscoord minimumClearance = aSqrWidth / 2;
+ if (dxIndex + aIndexWidth + minimumClearance > dxSqr + aSqrWidth) {
+ if (aIndexWidth + minimumClearance < aSqrWidth) {
+ dxIndex = aSqrWidth - (aIndexWidth + minimumClearance);
+ dxSqr = 0;
+ }
+ else {
+ dxIndex = 0;
+ dxSqr = (aIndexWidth + minimumClearance) - aSqrWidth;
+ }
+ }
+ }
+
+ if (aIndexOffset)
+ *aIndexOffset = dxIndex;
+ if (aSqrOffset)
+ *aSqrOffset = dxSqr;
+}
+
+void
+nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext,
+ ReflowOutput& aDesiredSize,
+ const ReflowInput& aReflowInput,
+ nsReflowStatus& aStatus)
+{
+ MarkInReflow();
+ nsReflowStatus childStatus;
+
+ mPresentationData.flags &= ~NS_MATHML_ERROR;
+ aDesiredSize.ClearSize();
+ aDesiredSize.SetBlockStartAscent(0);
+
+ nsBoundingMetrics bmSqr, bmBase, bmIndex;
+ DrawTarget* drawTarget = aReflowInput.mRenderingContext->GetDrawTarget();
+
+ //////////////////
+ // Reflow Children
+
+ int32_t count = 0;
+ nsIFrame* baseFrame = nullptr;
+ nsIFrame* indexFrame = nullptr;
+ ReflowOutput baseSize(aReflowInput);
+ ReflowOutput indexSize(aReflowInput);
+ nsIFrame* childFrame = mFrames.FirstChild();
+ while (childFrame) {
+ // ask our children to compute their bounding metrics
+ ReflowOutput childDesiredSize(aReflowInput,
+ aDesiredSize.mFlags
+ | NS_REFLOW_CALC_BOUNDING_METRICS);
+ WritingMode wm = childFrame->GetWritingMode();
+ LogicalSize availSize = aReflowInput.ComputedSize(wm);
+ availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
+ ReflowInput childReflowInput(aPresContext, aReflowInput,
+ childFrame, availSize);
+ ReflowChild(childFrame, aPresContext,
+ childDesiredSize, childReflowInput, childStatus);
+ //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
+ if (0 == count) {
+ // base
+ baseFrame = childFrame;
+ baseSize = childDesiredSize;
+ bmBase = childDesiredSize.mBoundingMetrics;
+ }
+ else if (1 == count) {
+ // index
+ indexFrame = childFrame;
+ indexSize = childDesiredSize;
+ bmIndex = childDesiredSize.mBoundingMetrics;
+ }
+ count++;
+ childFrame = childFrame->GetNextSibling();
+ }
+ if (2 != count) {
+ // report an error, encourage people to get their markups in order
+ ReportChildCountError();
+ ReflowError(drawTarget, aDesiredSize);
+ aStatus = NS_FRAME_COMPLETE;
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+ // Call DidReflow() for the child frames we successfully did reflow.
+ DidReflowChildren(mFrames.FirstChild(), childFrame);
+ return;
+ }
+
+ ////////////
+ // Prepare the radical symbol and the overline bar
+
+ float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
+
+ nscoord ruleThickness, leading, psi;
+ GetRadicalParameters(fm, StyleFont()->mMathDisplay ==
+ NS_MATHML_DISPLAYSTYLE_BLOCK,
+ ruleThickness, leading, psi);
+
+ // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
+ char16_t one = '1';
+ nsBoundingMetrics bmOne =
+ nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, drawTarget);
+ if (bmOne.ascent > bmBase.ascent)
+ psi += bmOne.ascent - bmBase.ascent;
+
+ // make sure that the rule appears on on screen
+ nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
+ if (ruleThickness < onePixel) {
+ ruleThickness = onePixel;
+ }
+
+ // adjust clearance psi to get an exact number of pixels -- this
+ // gives a nicer & uniform look on stacked radicals (bug 130282)
+ nscoord delta = psi % onePixel;
+ if (delta)
+ psi += onePixel - delta; // round up
+
+ // Stretch the radical symbol to the appropriate height if it is not big enough.
+ nsBoundingMetrics contSize = bmBase;
+ contSize.descent = bmBase.ascent + bmBase.descent + psi;
+ contSize.ascent = ruleThickness;
+
+ // height(radical) should be >= height(base) + psi + ruleThickness
+ nsBoundingMetrics radicalSize;
+ mSqrChar.Stretch(aPresContext, drawTarget,
+ fontSizeInflation,
+ NS_STRETCH_DIRECTION_VERTICAL,
+ contSize, radicalSize,
+ NS_STRETCH_LARGER,
+ StyleVisibility()->mDirection);
+ // 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 =
+ std::max(bmBase.descent,
+ (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent));
+ mBoundingMetrics.width = bmSqr.width + bmBase.width;
+ mBoundingMetrics.leftBearing = bmSqr.leftBearing;
+ mBoundingMetrics.rightBearing = bmSqr.width +
+ std::max(bmBase.width, bmBase.rightBearing); // take also care of the rule
+
+ aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
+ aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
+ std::max(baseSize.Height() - baseSize.BlockStartAscent(),
+ mBoundingMetrics.descent + ruleThickness);
+ aDesiredSize.Width() = mBoundingMetrics.width;
+
+ /////////////
+ // Re-adjust the desired size to include the index.
+
+ // the index is raised by some fraction of the height
+ // of the radical, see \mroot macro in App. B, TexBook
+ float raiseIndexPercent = 0.6f;
+ gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont();
+ if (mathFont) {
+ raiseIndexPercent = mathFont->MathTable()->
+ Constant(gfxMathTable::RadicalDegreeBottomRaisePercent);
+ }
+ nscoord raiseIndexDelta = NSToCoordRound(raiseIndexPercent *
+ (bmSqr.ascent + bmSqr.descent));
+ nscoord indexRaisedAscent = mBoundingMetrics.ascent // top of radical
+ - (bmSqr.ascent + bmSqr.descent) // to bottom of radical
+ + raiseIndexDelta + bmIndex.ascent + bmIndex.descent; // to top of raised index
+
+ nscoord indexClearance = 0;
+ if (mBoundingMetrics.ascent < indexRaisedAscent) {
+ indexClearance =
+ indexRaisedAscent - mBoundingMetrics.ascent; // excess gap introduced by a tall index
+ mBoundingMetrics.ascent = indexRaisedAscent;
+ nscoord descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent();
+ aDesiredSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading);
+ aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + descent;
+ }
+
+ nscoord dxIndex, dxSqr;
+ GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr);
+
+ mBoundingMetrics.width = dxSqr + bmSqr.width + bmBase.width;
+ mBoundingMetrics.leftBearing =
+ std::min(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
+ mBoundingMetrics.rightBearing = dxSqr + bmSqr.width +
+ std::max(bmBase.width, bmBase.rightBearing);
+
+ aDesiredSize.Width() = mBoundingMetrics.width;
+ aDesiredSize.mBoundingMetrics = mBoundingMetrics;
+ GatherAndStoreOverflow(&aDesiredSize);
+
+ // place the index
+ nscoord dx = dxIndex;
+ nscoord dy = aDesiredSize.BlockStartAscent() -
+ (indexRaisedAscent + indexSize.BlockStartAscent() - bmIndex.ascent);
+ FinishReflowChild(indexFrame, aPresContext, indexSize, nullptr,
+ MirrorIfRTL(aDesiredSize.Width(), indexSize.Width(), dx),
+ dy, 0);
+
+ // place the radical symbol and the radical bar
+ dx = dxSqr;
+ dy = indexClearance + leading; // leave a leading at the top
+ mSqrChar.SetRect(nsRect(MirrorIfRTL(aDesiredSize.Width(), bmSqr.width, dx),
+ dy, bmSqr.width, bmSqr.ascent + bmSqr.descent));
+ dx += bmSqr.width;
+ mBarRect.SetRect(MirrorIfRTL(aDesiredSize.Width(), bmBase.width, dx),
+ dy, bmBase.width, ruleThickness);
+
+ // place the base
+ dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent();
+ FinishReflowChild(baseFrame, aPresContext, baseSize, nullptr,
+ MirrorIfRTL(aDesiredSize.Width(), baseSize.Width(), dx),
+ dy, 0);
+
+ mReference.x = 0;
+ mReference.y = aDesiredSize.BlockStartAscent();
+
+ aStatus = NS_FRAME_COMPLETE;
+ NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
+}
+
+/* virtual */ void
+nsMathMLmrootFrame::GetIntrinsicISizeMetrics(nsRenderingContext* aRenderingContext, ReflowOutput& aDesiredSize)
+{
+ nsIFrame* baseFrame = mFrames.FirstChild();
+ nsIFrame* indexFrame = nullptr;
+ if (baseFrame)
+ indexFrame = baseFrame->GetNextSibling();
+ if (!indexFrame || indexFrame->GetNextSibling()) {
+ ReflowError(aRenderingContext->GetDrawTarget(), aDesiredSize);
+ return;
+ }
+
+ float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
+ nscoord baseWidth =
+ nsLayoutUtils::IntrinsicForContainer(aRenderingContext, baseFrame,
+ nsLayoutUtils::PREF_ISIZE);
+ nscoord indexWidth =
+ nsLayoutUtils::IntrinsicForContainer(aRenderingContext, indexFrame,
+ nsLayoutUtils::PREF_ISIZE);
+ nscoord sqrWidth = mSqrChar.GetMaxWidth(PresContext(),
+ aRenderingContext->GetDrawTarget(),
+ fontSizeInflation);
+
+ nscoord dxSqr;
+ RefPtr<nsFontMetrics> fm =
+ nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation);
+ GetRadicalXOffsets(indexWidth, sqrWidth, fm, nullptr, &dxSqr);
+
+ nscoord width = dxSqr + sqrWidth + baseWidth;
+
+ aDesiredSize.Width() = width;
+ aDesiredSize.mBoundingMetrics.width = width;
+ aDesiredSize.mBoundingMetrics.leftBearing = 0;
+ aDesiredSize.mBoundingMetrics.rightBearing = width;
+}
+
+// ----------------------
+// the Style System will use these to pass the proper style context to our MathMLChar
+nsStyleContext*
+nsMathMLmrootFrame::GetAdditionalStyleContext(int32_t aIndex) const
+{
+ switch (aIndex) {
+ case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
+ return mSqrChar.GetStyleContext();
+ default:
+ return nullptr;
+ }
+}
+
+void
+nsMathMLmrootFrame::SetAdditionalStyleContext(int32_t aIndex,
+ nsStyleContext* aStyleContext)
+{
+ switch (aIndex) {
+ case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
+ mSqrChar.SetStyleContext(aStyleContext);
+ break;
+ }
+}