diff options
Diffstat (limited to 'layout/mathml/nsMathMLmencloseFrame.cpp')
-rw-r--r-- | layout/mathml/nsMathMLmencloseFrame.cpp | 868 |
1 files changed, 868 insertions, 0 deletions
diff --git a/layout/mathml/nsMathMLmencloseFrame.cpp b/layout/mathml/nsMathMLmencloseFrame.cpp new file mode 100644 index 0000000000..64277a92e9 --- /dev/null +++ b/layout/mathml/nsMathMLmencloseFrame.cpp @@ -0,0 +1,868 @@ +/* -*- 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 "nsMathMLmencloseFrame.h" + +#include "gfx2DGlue.h" +#include "gfxUtils.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/PathHelpers.h" +#include "nsPresContext.h" +#include "nsRenderingContext.h" +#include "nsWhitespaceTokenizer.h" + +#include "nsDisplayList.h" +#include "gfxContext.h" +#include "nsMathMLChar.h" +#include <algorithm> + +using namespace mozilla; +using namespace mozilla::gfx; + +// +// <menclose> -- enclose content with a stretching symbol such +// as a long division sign. - implementation + +// longdiv: +// Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis +// renders better with current font support. +static const char16_t kLongDivChar = ')'; + +// radical: 'SQUARE ROOT' +static const char16_t kRadicalChar = 0x221A; + +// updiagonalstrike +static const uint8_t kArrowHeadSize = 10; + +// phasorangle +static const uint8_t kPhasorangleWidth = 8; + +nsIFrame* +NS_NewMathMLmencloseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) +{ + return new (aPresShell) nsMathMLmencloseFrame(aContext); +} + +NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame) + +nsMathMLmencloseFrame::nsMathMLmencloseFrame(nsStyleContext* aContext) : + nsMathMLContainerFrame(aContext), mNotationsToDraw(0), + mLongDivCharIndex(-1), mRadicalCharIndex(-1), mContentWidth(0) +{ +} + +nsMathMLmencloseFrame::~nsMathMLmencloseFrame() +{ +} + +nsresult nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask) +{ + // Is the char already allocated? + if ((mask == NOTATION_LONGDIV && mLongDivCharIndex >= 0) || + (mask == NOTATION_RADICAL && mRadicalCharIndex >= 0)) + return NS_OK; + + // No need to track the style context given to our MathML chars. + // The Style System will use Get/SetAdditionalStyleContext() to keep it + // up-to-date if dynamic changes arise. + uint32_t i = mMathMLChar.Length(); + nsAutoString Char; + + if (!mMathMLChar.AppendElement()) + return NS_ERROR_OUT_OF_MEMORY; + + if (mask == NOTATION_LONGDIV) { + Char.Assign(kLongDivChar); + mLongDivCharIndex = i; + } else if (mask == NOTATION_RADICAL) { + Char.Assign(kRadicalChar); + mRadicalCharIndex = i; + } + + nsPresContext *presContext = PresContext(); + mMathMLChar[i].SetData(Char); + ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar[i]); + + return NS_OK; +} + +/* + * Add a notation to draw, if the argument is the name of a known notation. + * @param aNotation string name of a notation + */ +nsresult nsMathMLmencloseFrame::AddNotation(const nsAString& aNotation) +{ + nsresult rv; + + if (aNotation.EqualsLiteral("longdiv")) { + rv = AllocateMathMLChar(NOTATION_LONGDIV); + NS_ENSURE_SUCCESS(rv, rv); + mNotationsToDraw |= NOTATION_LONGDIV; + } else if (aNotation.EqualsLiteral("actuarial")) { + mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_TOP); + } else if (aNotation.EqualsLiteral("radical")) { + rv = AllocateMathMLChar(NOTATION_RADICAL); + NS_ENSURE_SUCCESS(rv, rv); + mNotationsToDraw |= NOTATION_RADICAL; + } else if (aNotation.EqualsLiteral("box")) { + mNotationsToDraw |= (NOTATION_LEFT | NOTATION_RIGHT | + NOTATION_TOP | NOTATION_BOTTOM); + } else if (aNotation.EqualsLiteral("roundedbox")) { + mNotationsToDraw |= NOTATION_ROUNDEDBOX; + } else if (aNotation.EqualsLiteral("circle")) { + mNotationsToDraw |= NOTATION_CIRCLE; + } else if (aNotation.EqualsLiteral("left")) { + mNotationsToDraw |= NOTATION_LEFT; + } else if (aNotation.EqualsLiteral("right")) { + mNotationsToDraw |= NOTATION_RIGHT; + } else if (aNotation.EqualsLiteral("top")) { + mNotationsToDraw |= NOTATION_TOP; + } else if (aNotation.EqualsLiteral("bottom")) { + mNotationsToDraw |= NOTATION_BOTTOM; + } else if (aNotation.EqualsLiteral("updiagonalstrike")) { + mNotationsToDraw |= NOTATION_UPDIAGONALSTRIKE; + } else if (aNotation.EqualsLiteral("updiagonalarrow")) { + mNotationsToDraw |= NOTATION_UPDIAGONALARROW; + } else if (aNotation.EqualsLiteral("downdiagonalstrike")) { + mNotationsToDraw |= NOTATION_DOWNDIAGONALSTRIKE; + } else if (aNotation.EqualsLiteral("verticalstrike")) { + mNotationsToDraw |= NOTATION_VERTICALSTRIKE; + } else if (aNotation.EqualsLiteral("horizontalstrike")) { + mNotationsToDraw |= NOTATION_HORIZONTALSTRIKE; + } else if (aNotation.EqualsLiteral("madruwb")) { + mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_BOTTOM); + } else if (aNotation.EqualsLiteral("phasorangle")) { + mNotationsToDraw |= (NOTATION_BOTTOM | NOTATION_PHASORANGLE); + } + + return NS_OK; +} + +/* + * Initialize the list of notations to draw + */ +void nsMathMLmencloseFrame::InitNotations() +{ + mNotationsToDraw = 0; + mLongDivCharIndex = mRadicalCharIndex = -1; + mMathMLChar.Clear(); + + nsAutoString value; + + if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::notation_, value)) { + // parse the notation attribute + nsWhitespaceTokenizer tokenizer(value); + + while (tokenizer.hasMoreTokens()) + AddNotation(tokenizer.nextToken()); + + if (IsToDraw(NOTATION_UPDIAGONALARROW)) { + // For <menclose notation="updiagonalstrike updiagonalarrow">, if + // the two notations are drawn then the strike line may cause the point of + // the arrow to be too wide. Hence we will only draw the updiagonalarrow + // and the arrow shaft may be thought to be the updiagonalstrike. + mNotationsToDraw &= ~NOTATION_UPDIAGONALSTRIKE; + } + } else { + // default: longdiv + if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV))) + return; + mNotationsToDraw = NOTATION_LONGDIV; + } +} + +NS_IMETHODIMP +nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame* aParent) +{ + // let the base class get the default from our parent + nsMathMLContainerFrame::InheritAutomaticData(aParent); + + mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY; + + InitNotations(); + + return NS_OK; +} + +NS_IMETHODIMP +nsMathMLmencloseFrame::TransmitAutomaticData() +{ + if (IsToDraw(NOTATION_RADICAL)) { + // The TeXBook (Ch 17. p.141) says that \sqrt is cramped + UpdatePresentationDataFromChildAt(0, -1, + NS_MATHML_COMPRESSED, + NS_MATHML_COMPRESSED); + } + + return NS_OK; +} + +void +nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, + const nsRect& aDirtyRect, + const nsDisplayListSet& aLists) +{ + ///////////// + // paint the menclosed content + nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); + + if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) + return; + + nsRect mencloseRect = nsIFrame::GetRect(); + mencloseRect.x = mencloseRect.y = 0; + + if (IsToDraw(NOTATION_RADICAL)) { + mMathMLChar[mRadicalCharIndex].Display(aBuilder, this, aLists, 0); + + nsRect rect; + mMathMLChar[mRadicalCharIndex].GetRect(rect); + rect.MoveBy(StyleVisibility()->mDirection ? -mContentWidth : rect.width, 0); + rect.SizeTo(mContentWidth, mRadicalRuleThickness); + DisplayBar(aBuilder, this, rect, aLists); + } + + if (IsToDraw(NOTATION_PHASORANGLE)) { + DisplayNotation(aBuilder, this, mencloseRect, aLists, + mRuleThickness, NOTATION_PHASORANGLE); + } + + if (IsToDraw(NOTATION_LONGDIV)) { + mMathMLChar[mLongDivCharIndex].Display(aBuilder, this, aLists, 1); + + nsRect rect; + mMathMLChar[mLongDivCharIndex].GetRect(rect); + rect.SizeTo(rect.width + mContentWidth, mRuleThickness); + DisplayBar(aBuilder, this, rect, aLists); + } + + if (IsToDraw(NOTATION_TOP)) { + nsRect rect(0, 0, mencloseRect.width, mRuleThickness); + DisplayBar(aBuilder, this, rect, aLists); + } + + if (IsToDraw(NOTATION_BOTTOM)) { + nsRect rect(0, mencloseRect.height - mRuleThickness, + mencloseRect.width, mRuleThickness); + DisplayBar(aBuilder, this, rect, aLists); + } + + if (IsToDraw(NOTATION_LEFT)) { + nsRect rect(0, 0, mRuleThickness, mencloseRect.height); + DisplayBar(aBuilder, this, rect, aLists); + } + + if (IsToDraw(NOTATION_RIGHT)) { + nsRect rect(mencloseRect.width - mRuleThickness, 0, + mRuleThickness, mencloseRect.height); + DisplayBar(aBuilder, this, rect, aLists); + } + + if (IsToDraw(NOTATION_ROUNDEDBOX)) { + DisplayNotation(aBuilder, this, mencloseRect, aLists, + mRuleThickness, NOTATION_ROUNDEDBOX); + } + + if (IsToDraw(NOTATION_CIRCLE)) { + DisplayNotation(aBuilder, this, mencloseRect, aLists, + mRuleThickness, NOTATION_CIRCLE); + } + + if (IsToDraw(NOTATION_UPDIAGONALSTRIKE)) { + DisplayNotation(aBuilder, this, mencloseRect, aLists, + mRuleThickness, NOTATION_UPDIAGONALSTRIKE); + } + + if (IsToDraw(NOTATION_UPDIAGONALARROW)) { + DisplayNotation(aBuilder, this, mencloseRect, aLists, + mRuleThickness, NOTATION_UPDIAGONALARROW); + } + + if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE)) { + DisplayNotation(aBuilder, this, mencloseRect, aLists, + mRuleThickness, NOTATION_DOWNDIAGONALSTRIKE); + } + + if (IsToDraw(NOTATION_HORIZONTALSTRIKE)) { + nsRect rect(0, mencloseRect.height / 2 - mRuleThickness / 2, + mencloseRect.width, mRuleThickness); + DisplayBar(aBuilder, this, rect, aLists); + } + + if (IsToDraw(NOTATION_VERTICALSTRIKE)) { + nsRect rect(mencloseRect.width / 2 - mRuleThickness / 2, 0, + mRuleThickness, mencloseRect.height); + DisplayBar(aBuilder, this, rect, aLists); + } +} + +/* virtual */ nsresult +nsMathMLmencloseFrame::MeasureForWidth(DrawTarget* aDrawTarget, + ReflowOutput& aDesiredSize) +{ + return PlaceInternal(aDrawTarget, false, aDesiredSize, true); +} + +/* virtual */ nsresult +nsMathMLmencloseFrame::Place(DrawTarget* aDrawTarget, + bool aPlaceOrigin, + ReflowOutput& aDesiredSize) +{ + return PlaceInternal(aDrawTarget, aPlaceOrigin, aDesiredSize, false); +} + +/* virtual */ nsresult +nsMathMLmencloseFrame::PlaceInternal(DrawTarget* aDrawTarget, + bool aPlaceOrigin, + ReflowOutput& aDesiredSize, + bool aWidthOnly) +{ + /////////////// + // Measure the size of our content using the base class to format like an + // inferred mrow. + ReflowOutput baseSize(aDesiredSize.GetWritingMode()); + nsresult rv = + nsMathMLContainerFrame::Place(aDrawTarget, false, baseSize); + + if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { + DidReflowChildren(PrincipalChildList().FirstChild()); + return rv; + } + + nsBoundingMetrics bmBase = baseSize.mBoundingMetrics; + nscoord dx_left = 0, dx_right = 0; + nsBoundingMetrics bmLongdivChar, bmRadicalChar; + nscoord radicalAscent = 0, radicalDescent = 0; + nscoord longdivAscent = 0, longdivDescent = 0; + nscoord psi = 0; + nscoord leading = 0; + + /////////////// + // Thickness of bars and font metrics + nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); + + float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); + RefPtr<nsFontMetrics> fm = + nsLayoutUtils::GetFontMetricsForFrame(this, fontSizeInflation); + GetRuleThickness(aDrawTarget, fm, mRuleThickness); + if (mRuleThickness < onePixel) { + mRuleThickness = onePixel; + } + + char16_t one = '1'; + nsBoundingMetrics bmOne = + nsLayoutUtils::AppUnitBoundsOfString(&one, 1, *fm, aDrawTarget); + + /////////////// + // General rules: the menclose element takes the size of the enclosed content. + // We add a padding when needed. + + // determine padding & psi + nscoord padding = 3 * mRuleThickness; + nscoord delta = padding % onePixel; + if (delta) + padding += onePixel - delta; // round up + + if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { + GetRadicalParameters(fm, StyleFont()->mMathDisplay == + NS_MATHML_DISPLAYSTYLE_BLOCK, + mRadicalRuleThickness, leading, psi); + + // make sure that the rule appears on on screen + if (mRadicalRuleThickness < onePixel) { + mRadicalRuleThickness = onePixel; + } + + // adjust clearance psi to get an exact number of pixels -- this + // gives a nicer & uniform look on stacked radicals (bug 130282) + delta = psi % onePixel; + if (delta) { + psi += onePixel - delta; // round up + } + } + + // Set horizontal parameters + if (IsToDraw(NOTATION_ROUNDEDBOX) || + IsToDraw(NOTATION_TOP) || + IsToDraw(NOTATION_LEFT) || + IsToDraw(NOTATION_BOTTOM) || + IsToDraw(NOTATION_CIRCLE)) + dx_left = padding; + + if (IsToDraw(NOTATION_ROUNDEDBOX) || + IsToDraw(NOTATION_TOP) || + IsToDraw(NOTATION_RIGHT) || + IsToDraw(NOTATION_BOTTOM) || + IsToDraw(NOTATION_CIRCLE)) + dx_right = padding; + + // Set vertical parameters + if (IsToDraw(NOTATION_RIGHT) || + IsToDraw(NOTATION_LEFT) || + IsToDraw(NOTATION_UPDIAGONALSTRIKE) || + IsToDraw(NOTATION_UPDIAGONALARROW) || + IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || + IsToDraw(NOTATION_VERTICALSTRIKE) || + IsToDraw(NOTATION_CIRCLE) || + IsToDraw(NOTATION_ROUNDEDBOX) || + IsToDraw(NOTATION_RADICAL) || + IsToDraw(NOTATION_LONGDIV) || + IsToDraw(NOTATION_PHASORANGLE)) { + // set a minimal value for the base height + bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent); + bmBase.descent = std::max(0, bmBase.descent); + } + + mBoundingMetrics.ascent = bmBase.ascent; + mBoundingMetrics.descent = bmBase.descent; + + if (IsToDraw(NOTATION_ROUNDEDBOX) || + IsToDraw(NOTATION_TOP) || + IsToDraw(NOTATION_LEFT) || + IsToDraw(NOTATION_RIGHT) || + IsToDraw(NOTATION_CIRCLE)) + mBoundingMetrics.ascent += padding; + + if (IsToDraw(NOTATION_ROUNDEDBOX) || + IsToDraw(NOTATION_LEFT) || + IsToDraw(NOTATION_RIGHT) || + IsToDraw(NOTATION_BOTTOM) || + IsToDraw(NOTATION_CIRCLE)) + mBoundingMetrics.descent += padding; + + /////////////// + // phasorangle notation + if (IsToDraw(NOTATION_PHASORANGLE)) { + nscoord phasorangleWidth = kPhasorangleWidth * mRuleThickness; + // Update horizontal parameters + dx_left = std::max(dx_left, phasorangleWidth); + } + + /////////////// + // updiagonal arrow notation. We need enough space at the top right corner to + // draw the arrow head. + if (IsToDraw(NOTATION_UPDIAGONALARROW)) { + // This is an estimate, see nsDisplayNotation::Paint for the exact head size + nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness; + + // We want that the arrow shaft strikes the menclose content and that the + // arrow head does not overlap with that content. Hence we add some space + // on the right. We don't add space on the top but only ensure that the + // ascent is large enough. + dx_right = std::max(dx_right, arrowHeadSize); + mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize); + } + + /////////////// + // circle notation: we don't want the ellipse to overlap the enclosed + // content. Hence, we need to increase the size of the bounding box by a + // factor of at least sqrt(2). + if (IsToDraw(NOTATION_CIRCLE)) { + double ratio = (sqrt(2.0) - 1.0) / 2.0; + nscoord padding2; + + // Update horizontal parameters + padding2 = ratio * bmBase.width; + + dx_left = std::max(dx_left, padding2); + dx_right = std::max(dx_right, padding2); + + // Update vertical parameters + padding2 = ratio * (bmBase.ascent + bmBase.descent); + + mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, + bmBase.ascent + padding2); + mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, + bmBase.descent + padding2); + } + + /////////////// + // longdiv notation: + if (IsToDraw(NOTATION_LONGDIV)) { + if (aWidthOnly) { + nscoord longdiv_width = mMathMLChar[mLongDivCharIndex]. + GetMaxWidth(PresContext(), aDrawTarget, fontSizeInflation); + + // Update horizontal parameters + dx_left = std::max(dx_left, longdiv_width); + } else { + // Stretch the parenthesis to the appropriate height if it is not + // big enough. + 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(), aDrawTarget, + fontSizeInflation, + NS_STRETCH_DIRECTION_VERTICAL, + contSize, bmLongdivChar, + NS_STRETCH_LARGER, false); + mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar); + + // Update horizontal parameters + dx_left = std::max(dx_left, bmLongdivChar.width); + + // Update vertical parameters + longdivAscent = bmBase.ascent + psi + mRuleThickness; + longdivDescent = std::max(bmBase.descent, + (bmLongdivChar.ascent + bmLongdivChar.descent - + longdivAscent)); + + mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, + longdivAscent); + mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, + longdivDescent); + } + } + + /////////////// + // radical notation: + if (IsToDraw(NOTATION_RADICAL)) { + nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left; + + if (aWidthOnly) { + nscoord radical_width = mMathMLChar[mRadicalCharIndex]. + GetMaxWidth(PresContext(), aDrawTarget, fontSizeInflation); + + // Update horizontal parameters + *dx_leading = std::max(*dx_leading, radical_width); + } else { + // Stretch the radical symbol to the appropriate height if it is not + // big enough. + nsBoundingMetrics contSize = bmBase; + contSize.ascent = mRadicalRuleThickness; + contSize.descent = bmBase.ascent + bmBase.descent + psi; + + // height(radical) should be >= height(base) + psi + mRadicalRuleThickness + mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aDrawTarget, + fontSizeInflation, + NS_STRETCH_DIRECTION_VERTICAL, + contSize, bmRadicalChar, + NS_STRETCH_LARGER, + StyleVisibility()->mDirection); + mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar); + + // Update horizontal parameters + *dx_leading = std::max(*dx_leading, bmRadicalChar.width); + + // Update vertical parameters + radicalAscent = bmBase.ascent + psi + mRadicalRuleThickness; + radicalDescent = std::max(bmBase.descent, + (bmRadicalChar.ascent + bmRadicalChar.descent - + radicalAscent)); + + mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, + radicalAscent); + mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, + radicalDescent); + } + } + + /////////////// + // + if (IsToDraw(NOTATION_CIRCLE) || + IsToDraw(NOTATION_ROUNDEDBOX) || + (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) { + // center the menclose around the content (horizontally) + dx_left = dx_right = std::max(dx_left, dx_right); + } + + /////////////// + // The maximum size is now computed: set the remaining parameters + mBoundingMetrics.width = dx_left + bmBase.width + dx_right; + + mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing); + mBoundingMetrics.rightBearing = + std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing); + + aDesiredSize.Width() = mBoundingMetrics.width; + + aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent, + baseSize.BlockStartAscent())); + aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + + std::max(mBoundingMetrics.descent, + baseSize.Height() - baseSize.BlockStartAscent()); + + if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { + nscoord desiredSizeAscent = aDesiredSize.BlockStartAscent(); + nscoord desiredSizeDescent = aDesiredSize.Height() - + aDesiredSize.BlockStartAscent(); + + if (IsToDraw(NOTATION_LONGDIV)) { + desiredSizeAscent = std::max(desiredSizeAscent, + longdivAscent + leading); + desiredSizeDescent = std::max(desiredSizeDescent, + longdivDescent + mRuleThickness); + } + + if (IsToDraw(NOTATION_RADICAL)) { + desiredSizeAscent = std::max(desiredSizeAscent, + radicalAscent + leading); + desiredSizeDescent = std::max(desiredSizeDescent, + radicalDescent + mRadicalRuleThickness); + } + + aDesiredSize.SetBlockStartAscent(desiredSizeAscent); + aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent; + } + + if (IsToDraw(NOTATION_CIRCLE) || + IsToDraw(NOTATION_ROUNDEDBOX) || + (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) { + // center the menclose around the content (vertically) + nscoord dy = std::max(aDesiredSize.BlockStartAscent() - bmBase.ascent, + aDesiredSize.Height() - + aDesiredSize.BlockStartAscent() - bmBase.descent); + + aDesiredSize.SetBlockStartAscent(bmBase.ascent + dy); + aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + bmBase.descent + dy; + } + + // Update mBoundingMetrics ascent/descent + if (IsToDraw(NOTATION_TOP) || + IsToDraw(NOTATION_RIGHT) || + IsToDraw(NOTATION_LEFT) || + IsToDraw(NOTATION_UPDIAGONALSTRIKE) || + IsToDraw(NOTATION_UPDIAGONALARROW) || + IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || + IsToDraw(NOTATION_VERTICALSTRIKE) || + IsToDraw(NOTATION_CIRCLE) || + IsToDraw(NOTATION_ROUNDEDBOX)) + mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent(); + + if (IsToDraw(NOTATION_BOTTOM) || + IsToDraw(NOTATION_RIGHT) || + IsToDraw(NOTATION_LEFT) || + IsToDraw(NOTATION_UPDIAGONALSTRIKE) || + IsToDraw(NOTATION_UPDIAGONALARROW) || + IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || + IsToDraw(NOTATION_VERTICALSTRIKE) || + IsToDraw(NOTATION_CIRCLE) || + IsToDraw(NOTATION_ROUNDEDBOX)) + mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.BlockStartAscent(); + + // phasorangle notation: + // move up from the bottom by the angled line height + if (IsToDraw(NOTATION_PHASORANGLE)) + mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, 2 * kPhasorangleWidth * mRuleThickness - mBoundingMetrics.descent); + + aDesiredSize.mBoundingMetrics = mBoundingMetrics; + + mReference.x = 0; + mReference.y = aDesiredSize.BlockStartAscent(); + + if (aPlaceOrigin) { + ////////////////// + // Set position and size of MathMLChars + if (IsToDraw(NOTATION_LONGDIV)) + mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left - + bmLongdivChar.width, + aDesiredSize.BlockStartAscent() - + longdivAscent, + bmLongdivChar.width, + bmLongdivChar.ascent + + bmLongdivChar.descent)); + + if (IsToDraw(NOTATION_RADICAL)) { + nscoord dx = (StyleVisibility()->mDirection ? + dx_left + bmBase.width : dx_left - bmRadicalChar.width); + + mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx, + aDesiredSize.BlockStartAscent() - + radicalAscent, + bmRadicalChar.width, + bmRadicalChar.ascent + + bmRadicalChar.descent)); + } + + mContentWidth = bmBase.width; + + ////////////////// + // Finish reflowing child frames + PositionRowChildFrames(dx_left, aDesiredSize.BlockStartAscent()); + } + + return NS_OK; +} + +nscoord +nsMathMLmencloseFrame::FixInterFrameSpacing(ReflowOutput& aDesiredSize) +{ + nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize); + if (!gap) + return 0; + + // Move the MathML characters + nsRect rect; + for (uint32_t i = 0; i < mMathMLChar.Length(); i++) { + mMathMLChar[i].GetRect(rect); + rect.MoveBy(gap, 0); + mMathMLChar[i].SetRect(rect); + } + + return gap; +} + +nsresult +nsMathMLmencloseFrame::AttributeChanged(int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType) +{ + if (aAttribute == nsGkAtoms::notation_) { + InitNotations(); + } + + return nsMathMLContainerFrame:: + AttributeChanged(aNameSpaceID, aAttribute, aModType); +} + +////////////////// +// the Style System will use these to pass the proper style context to our +// MathMLChar +nsStyleContext* +nsMathMLmencloseFrame::GetAdditionalStyleContext(int32_t aIndex) const +{ + int32_t len = mMathMLChar.Length(); + if (aIndex >= 0 && aIndex < len) + return mMathMLChar[aIndex].GetStyleContext(); + else + return nullptr; +} + +void +nsMathMLmencloseFrame::SetAdditionalStyleContext(int32_t aIndex, + nsStyleContext* aStyleContext) +{ + int32_t len = mMathMLChar.Length(); + if (aIndex >= 0 && aIndex < len) + mMathMLChar[aIndex].SetStyleContext(aStyleContext); +} + +class nsDisplayNotation : public nsDisplayItem +{ +public: + nsDisplayNotation(nsDisplayListBuilder* aBuilder, + nsIFrame* aFrame, const nsRect& aRect, + nscoord aThickness, nsMencloseNotation aType) + : nsDisplayItem(aBuilder, aFrame), mRect(aRect), + mThickness(aThickness), mType(aType) { + MOZ_COUNT_CTOR(nsDisplayNotation); + } +#ifdef NS_BUILD_REFCNT_LOGGING + virtual ~nsDisplayNotation() { + MOZ_COUNT_DTOR(nsDisplayNotation); + } +#endif + + virtual void Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx) override; + NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION) + +private: + nsRect mRect; + nscoord mThickness; + nsMencloseNotation mType; +}; + +void nsDisplayNotation::Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx) +{ + DrawTarget& aDrawTarget = *aCtx->GetDrawTarget(); + nsPresContext* presContext = mFrame->PresContext(); + + Float strokeWidth = presContext->AppUnitsToGfxUnits(mThickness); + + Rect rect = NSRectToRect(mRect + ToReferenceFrame(), + presContext->AppUnitsPerDevPixel()); + rect.Deflate(strokeWidth / 2.f); + + ColorPattern color(ToDeviceColor( + mFrame->GetVisitedDependentColor(eCSSProperty__webkit_text_fill_color))); + + StrokeOptions strokeOptions(strokeWidth); + + switch(mType) + { + case NOTATION_CIRCLE: { + RefPtr<Path> ellipse = + MakePathForEllipse(aDrawTarget, rect.Center(), rect.Size()); + aDrawTarget.Stroke(ellipse, color, strokeOptions); + return; + } + case NOTATION_ROUNDEDBOX: { + Float radius = 3 * strokeWidth; + RectCornerRadii radii(radius, radius); + RefPtr<Path> roundedRect = + MakePathForRoundedRect(aDrawTarget, rect, radii, true); + aDrawTarget.Stroke(roundedRect, color, strokeOptions); + return; + } + case NOTATION_UPDIAGONALSTRIKE: { + aDrawTarget.StrokeLine(rect.BottomLeft(), rect.TopRight(), + color, strokeOptions); + return; + } + case NOTATION_DOWNDIAGONALSTRIKE: { + aDrawTarget.StrokeLine(rect.TopLeft(), rect.BottomRight(), + color, strokeOptions); + return; + } + case NOTATION_UPDIAGONALARROW: { + // Compute some parameters to draw the updiagonalarrow. The values below + // are taken from MathJax's HTML-CSS output. + Float W = rect.Width(); gfxFloat H = rect.Height(); + Float l = sqrt(W*W + H*H); + Float f = Float(kArrowHeadSize) * strokeWidth / l; + Float w = W * f; gfxFloat h = H * f; + + // Draw the arrow shaft + aDrawTarget.StrokeLine(rect.BottomLeft(), + rect.TopRight() + Point(-.7*w, .7*h), + color, strokeOptions); + + // Draw the arrow head + RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder(); + builder->MoveTo(rect.TopRight()); + builder->LineTo(rect.TopRight() + Point(-w -.4*h, std::max(-strokeWidth / 2.0, h - .4*w))); + builder->LineTo(rect.TopRight() + Point(-.7*w, .7*h)); + builder->LineTo(rect.TopRight() + Point(std::min(strokeWidth / 2.0, -w + .4*h), h + .4*w)); + builder->Close(); + RefPtr<Path> path = builder->Finish(); + aDrawTarget.Fill(path, color); + return; + } + case NOTATION_PHASORANGLE: { + // Compute some parameters to draw the angled line, + // that uses a slope of 2 (angle = tan^-1(2)). + // H = w * tan(angle) = w * 2 + Float w = Float(kPhasorangleWidth) * strokeWidth; + Float H = 2 * w; + + // Draw the angled line + aDrawTarget.StrokeLine(rect.BottomLeft(), + rect.BottomLeft() + Point(w, -H), + color, strokeOptions); + return; + } + default: + NS_NOTREACHED("This notation can not be drawn using nsDisplayNotation"); + } +} + +void +nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder* aBuilder, + nsIFrame* aFrame, const nsRect& aRect, + const nsDisplayListSet& aLists, + nscoord aThickness, + nsMencloseNotation aType) +{ + if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty() || + aThickness <= 0) + return; + + aLists.Content()->AppendNewToTop(new (aBuilder) + nsDisplayNotation(aBuilder, aFrame, aRect, aThickness, aType)); +} |