123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- /* -*- 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 nsDisplayListSet& aLists)
- {
- /////////////
- // paint the content we are square-rooting
- nsMathMLContainerFrame::BuildDisplayList(aBuilder, 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;
- }
- }
|