michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsMathMLmencloseFrame.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsWhitespaceTokenizer.h" michael@0: michael@0: #include "nsDisplayList.h" michael@0: #include "gfxContext.h" michael@0: #include "nsMathMLChar.h" michael@0: #include michael@0: michael@0: // michael@0: // -- enclose content with a stretching symbol such michael@0: // as a long division sign. - implementation michael@0: michael@0: // longdiv: michael@0: // Unicode 5.1 assigns U+27CC to LONG DIVISION, but a right parenthesis michael@0: // renders better with current font support. michael@0: static const char16_t kLongDivChar = ')'; michael@0: michael@0: // radical: 'SQUARE ROOT' michael@0: static const char16_t kRadicalChar = 0x221A; michael@0: michael@0: // updiagonalstrike michael@0: static const uint8_t kArrowHeadSize = 10; michael@0: michael@0: nsIFrame* michael@0: NS_NewMathMLmencloseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsMathMLmencloseFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmencloseFrame) michael@0: michael@0: nsMathMLmencloseFrame::nsMathMLmencloseFrame(nsStyleContext* aContext) : michael@0: nsMathMLContainerFrame(aContext), mNotationsToDraw(0), michael@0: mLongDivCharIndex(-1), mRadicalCharIndex(-1), mContentWidth(0) michael@0: { michael@0: } michael@0: michael@0: nsMathMLmencloseFrame::~nsMathMLmencloseFrame() michael@0: { michael@0: } michael@0: michael@0: nsresult nsMathMLmencloseFrame::AllocateMathMLChar(nsMencloseNotation mask) michael@0: { michael@0: // Is the char already allocated? michael@0: if ((mask == NOTATION_LONGDIV && mLongDivCharIndex >= 0) || michael@0: (mask == NOTATION_RADICAL && mRadicalCharIndex >= 0)) michael@0: return NS_OK; michael@0: michael@0: // No need to track the style context given to our MathML chars. michael@0: // The Style System will use Get/SetAdditionalStyleContext() to keep it michael@0: // up-to-date if dynamic changes arise. michael@0: uint32_t i = mMathMLChar.Length(); michael@0: nsAutoString Char; michael@0: michael@0: if (!mMathMLChar.AppendElement()) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: if (mask == NOTATION_LONGDIV) { michael@0: Char.Assign(kLongDivChar); michael@0: mLongDivCharIndex = i; michael@0: } else if (mask == NOTATION_RADICAL) { michael@0: Char.Assign(kRadicalChar); michael@0: mRadicalCharIndex = i; michael@0: } michael@0: michael@0: nsPresContext *presContext = PresContext(); michael@0: mMathMLChar[i].SetData(presContext, Char); michael@0: ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar[i]); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* michael@0: * Add a notation to draw, if the argument is the name of a known notation. michael@0: * @param aNotation string name of a notation michael@0: */ michael@0: nsresult nsMathMLmencloseFrame::AddNotation(const nsAString& aNotation) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (aNotation.EqualsLiteral("longdiv")) { michael@0: rv = AllocateMathMLChar(NOTATION_LONGDIV); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mNotationsToDraw |= NOTATION_LONGDIV; michael@0: } else if (aNotation.EqualsLiteral("actuarial")) { michael@0: mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_TOP); michael@0: } else if (aNotation.EqualsLiteral("radical")) { michael@0: rv = AllocateMathMLChar(NOTATION_RADICAL); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: mNotationsToDraw |= NOTATION_RADICAL; michael@0: } else if (aNotation.EqualsLiteral("box")) { michael@0: mNotationsToDraw |= (NOTATION_LEFT | NOTATION_RIGHT | michael@0: NOTATION_TOP | NOTATION_BOTTOM); michael@0: } else if (aNotation.EqualsLiteral("roundedbox")) { michael@0: mNotationsToDraw |= NOTATION_ROUNDEDBOX; michael@0: } else if (aNotation.EqualsLiteral("circle")) { michael@0: mNotationsToDraw |= NOTATION_CIRCLE; michael@0: } else if (aNotation.EqualsLiteral("left")) { michael@0: mNotationsToDraw |= NOTATION_LEFT; michael@0: } else if (aNotation.EqualsLiteral("right")) { michael@0: mNotationsToDraw |= NOTATION_RIGHT; michael@0: } else if (aNotation.EqualsLiteral("top")) { michael@0: mNotationsToDraw |= NOTATION_TOP; michael@0: } else if (aNotation.EqualsLiteral("bottom")) { michael@0: mNotationsToDraw |= NOTATION_BOTTOM; michael@0: } else if (aNotation.EqualsLiteral("updiagonalstrike")) { michael@0: mNotationsToDraw |= NOTATION_UPDIAGONALSTRIKE; michael@0: } else if (aNotation.EqualsLiteral("updiagonalarrow")) { michael@0: mNotationsToDraw |= NOTATION_UPDIAGONALARROW; michael@0: } else if (aNotation.EqualsLiteral("downdiagonalstrike")) { michael@0: mNotationsToDraw |= NOTATION_DOWNDIAGONALSTRIKE; michael@0: } else if (aNotation.EqualsLiteral("verticalstrike")) { michael@0: mNotationsToDraw |= NOTATION_VERTICALSTRIKE; michael@0: } else if (aNotation.EqualsLiteral("horizontalstrike")) { michael@0: mNotationsToDraw |= NOTATION_HORIZONTALSTRIKE; michael@0: } else if (aNotation.EqualsLiteral("madruwb")) { michael@0: mNotationsToDraw |= (NOTATION_RIGHT | NOTATION_BOTTOM); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* michael@0: * Initialize the list of notations to draw michael@0: */ michael@0: void nsMathMLmencloseFrame::InitNotations() michael@0: { michael@0: mNotationsToDraw = 0; michael@0: mLongDivCharIndex = mRadicalCharIndex = -1; michael@0: mMathMLChar.Clear(); michael@0: michael@0: nsAutoString value; michael@0: michael@0: if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::notation_, value)) { michael@0: // parse the notation attribute michael@0: nsWhitespaceTokenizer tokenizer(value); michael@0: michael@0: while (tokenizer.hasMoreTokens()) michael@0: AddNotation(tokenizer.nextToken()); michael@0: michael@0: if (IsToDraw(NOTATION_UPDIAGONALARROW)) { michael@0: // For , if michael@0: // the two notations are drawn then the strike line may cause the point of michael@0: // the arrow to be too wide. Hence we will only draw the updiagonalarrow michael@0: // and the arrow shaft may be thought to be the updiagonalstrike. michael@0: mNotationsToDraw &= ~NOTATION_UPDIAGONALSTRIKE; michael@0: } michael@0: } else { michael@0: // default: longdiv michael@0: if (NS_FAILED(AllocateMathMLChar(NOTATION_LONGDIV))) michael@0: return; michael@0: mNotationsToDraw = NOTATION_LONGDIV; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMathMLmencloseFrame::InheritAutomaticData(nsIFrame* aParent) michael@0: { michael@0: // let the base class get the default from our parent michael@0: nsMathMLContainerFrame::InheritAutomaticData(aParent); michael@0: michael@0: mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY; michael@0: michael@0: InitNotations(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMathMLmencloseFrame::TransmitAutomaticData() michael@0: { michael@0: if (IsToDraw(NOTATION_RADICAL)) { michael@0: // The TeXBook (Ch 17. p.141) says that \sqrt is cramped michael@0: UpdatePresentationDataFromChildAt(0, -1, michael@0: NS_MATHML_COMPRESSED, michael@0: NS_MATHML_COMPRESSED); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsMathMLmencloseFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: ///////////// michael@0: // paint the menclosed content michael@0: nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists); michael@0: michael@0: if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) michael@0: return; michael@0: michael@0: nsRect mencloseRect = nsIFrame::GetRect(); michael@0: mencloseRect.x = mencloseRect.y = 0; michael@0: michael@0: if (IsToDraw(NOTATION_RADICAL)) { michael@0: mMathMLChar[mRadicalCharIndex].Display(aBuilder, this, aLists, 0); michael@0: michael@0: nsRect rect; michael@0: mMathMLChar[mRadicalCharIndex].GetRect(rect); michael@0: rect.MoveBy(StyleVisibility()->mDirection ? -mContentWidth : rect.width, 0); michael@0: rect.SizeTo(mContentWidth, mRuleThickness); michael@0: DisplayBar(aBuilder, this, rect, aLists); michael@0: } michael@0: michael@0: if (IsToDraw(NOTATION_LONGDIV)) { michael@0: mMathMLChar[mLongDivCharIndex].Display(aBuilder, this, aLists, 1); michael@0: michael@0: nsRect rect; michael@0: mMathMLChar[mLongDivCharIndex].GetRect(rect); michael@0: rect.SizeTo(rect.width + mContentWidth, mRuleThickness); michael@0: DisplayBar(aBuilder, this, rect, aLists); michael@0: } michael@0: michael@0: if (IsToDraw(NOTATION_TOP)) { michael@0: nsRect rect(0, 0, mencloseRect.width, mRuleThickness); michael@0: DisplayBar(aBuilder, this, rect, aLists); michael@0: } michael@0: michael@0: if (IsToDraw(NOTATION_BOTTOM)) { michael@0: nsRect rect(0, mencloseRect.height - mRuleThickness, michael@0: mencloseRect.width, mRuleThickness); michael@0: DisplayBar(aBuilder, this, rect, aLists); michael@0: } michael@0: michael@0: if (IsToDraw(NOTATION_LEFT)) { michael@0: nsRect rect(0, 0, mRuleThickness, mencloseRect.height); michael@0: DisplayBar(aBuilder, this, rect, aLists); michael@0: } michael@0: michael@0: if (IsToDraw(NOTATION_RIGHT)) { michael@0: nsRect rect(mencloseRect.width - mRuleThickness, 0, michael@0: mRuleThickness, mencloseRect.height); michael@0: DisplayBar(aBuilder, this, rect, aLists); michael@0: } michael@0: michael@0: if (IsToDraw(NOTATION_ROUNDEDBOX)) { michael@0: DisplayNotation(aBuilder, this, mencloseRect, aLists, michael@0: mRuleThickness, NOTATION_ROUNDEDBOX); michael@0: } michael@0: michael@0: if (IsToDraw(NOTATION_CIRCLE)) { michael@0: DisplayNotation(aBuilder, this, mencloseRect, aLists, michael@0: mRuleThickness, NOTATION_CIRCLE); michael@0: } michael@0: michael@0: if (IsToDraw(NOTATION_UPDIAGONALSTRIKE)) { michael@0: DisplayNotation(aBuilder, this, mencloseRect, aLists, michael@0: mRuleThickness, NOTATION_UPDIAGONALSTRIKE); michael@0: } michael@0: michael@0: if (IsToDraw(NOTATION_UPDIAGONALARROW)) { michael@0: DisplayNotation(aBuilder, this, mencloseRect, aLists, michael@0: mRuleThickness, NOTATION_UPDIAGONALARROW); michael@0: } michael@0: michael@0: if (IsToDraw(NOTATION_DOWNDIAGONALSTRIKE)) { michael@0: DisplayNotation(aBuilder, this, mencloseRect, aLists, michael@0: mRuleThickness, NOTATION_DOWNDIAGONALSTRIKE); michael@0: } michael@0: michael@0: if (IsToDraw(NOTATION_HORIZONTALSTRIKE)) { michael@0: nsRect rect(0, mencloseRect.height / 2 - mRuleThickness / 2, michael@0: mencloseRect.width, mRuleThickness); michael@0: DisplayBar(aBuilder, this, rect, aLists); michael@0: } michael@0: michael@0: if (IsToDraw(NOTATION_VERTICALSTRIKE)) { michael@0: nsRect rect(mencloseRect.width / 2 - mRuleThickness / 2, 0, michael@0: mRuleThickness, mencloseRect.height); michael@0: DisplayBar(aBuilder, this, rect, aLists); michael@0: } michael@0: } michael@0: michael@0: /* virtual */ nsresult michael@0: nsMathMLmencloseFrame::MeasureForWidth(nsRenderingContext& aRenderingContext, michael@0: nsHTMLReflowMetrics& aDesiredSize) michael@0: { michael@0: return PlaceInternal(aRenderingContext, false, aDesiredSize, true); michael@0: } michael@0: michael@0: /* virtual */ nsresult michael@0: nsMathMLmencloseFrame::Place(nsRenderingContext& aRenderingContext, michael@0: bool aPlaceOrigin, michael@0: nsHTMLReflowMetrics& aDesiredSize) michael@0: { michael@0: return PlaceInternal(aRenderingContext, aPlaceOrigin, aDesiredSize, false); michael@0: } michael@0: michael@0: /* virtual */ nsresult michael@0: nsMathMLmencloseFrame::PlaceInternal(nsRenderingContext& aRenderingContext, michael@0: bool aPlaceOrigin, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: bool aWidthOnly) michael@0: { michael@0: /////////////// michael@0: // Measure the size of our content using the base class to format like an michael@0: // inferred mrow. michael@0: nsHTMLReflowMetrics baseSize(aDesiredSize.GetWritingMode()); michael@0: nsresult rv = michael@0: nsMathMLContainerFrame::Place(aRenderingContext, false, baseSize); michael@0: michael@0: if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { michael@0: DidReflowChildren(GetFirstPrincipalChild()); michael@0: return rv; michael@0: } michael@0: michael@0: nsBoundingMetrics bmBase = baseSize.mBoundingMetrics; michael@0: nscoord dx_left = 0, dx_right = 0; michael@0: nsBoundingMetrics bmLongdivChar, bmRadicalChar; michael@0: nscoord radicalAscent = 0, radicalDescent = 0; michael@0: nscoord longdivAscent = 0, longdivDescent = 0; michael@0: nscoord psi = 0; michael@0: michael@0: /////////////// michael@0: // Thickness of bars and font metrics michael@0: nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); michael@0: michael@0: nscoord mEmHeight; michael@0: nsRefPtr fm; michael@0: nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); michael@0: aRenderingContext.SetFont(fm); michael@0: GetRuleThickness(aRenderingContext, fm, mRuleThickness); michael@0: GetEmHeight(fm, mEmHeight); michael@0: michael@0: char16_t one = '1'; michael@0: nsBoundingMetrics bmOne = aRenderingContext.GetBoundingMetrics(&one, 1); michael@0: michael@0: /////////////// michael@0: // General rules: the menclose element takes the size of the enclosed content. michael@0: // We add a padding when needed. michael@0: michael@0: // determine padding & psi michael@0: nscoord padding = 3 * mRuleThickness; michael@0: nscoord delta = padding % onePixel; michael@0: if (delta) michael@0: padding += onePixel - delta; // round up michael@0: michael@0: if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { michael@0: nscoord phi; michael@0: // Rule 11, App. G, TeXbook michael@0: // psi = clearance between rule and content michael@0: if (StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK) michael@0: phi = fm->XHeight(); michael@0: else michael@0: phi = mRuleThickness; michael@0: psi = mRuleThickness + phi / 4; michael@0: michael@0: delta = psi % onePixel; michael@0: if (delta) michael@0: psi += onePixel - delta; // round up michael@0: } michael@0: michael@0: if (mRuleThickness < onePixel) michael@0: mRuleThickness = onePixel; michael@0: michael@0: // Set horizontal parameters michael@0: if (IsToDraw(NOTATION_ROUNDEDBOX) || michael@0: IsToDraw(NOTATION_TOP) || michael@0: IsToDraw(NOTATION_LEFT) || michael@0: IsToDraw(NOTATION_BOTTOM) || michael@0: IsToDraw(NOTATION_CIRCLE)) michael@0: dx_left = padding; michael@0: michael@0: if (IsToDraw(NOTATION_ROUNDEDBOX) || michael@0: IsToDraw(NOTATION_TOP) || michael@0: IsToDraw(NOTATION_RIGHT) || michael@0: IsToDraw(NOTATION_BOTTOM) || michael@0: IsToDraw(NOTATION_CIRCLE)) michael@0: dx_right = padding; michael@0: michael@0: // Set vertical parameters michael@0: if (IsToDraw(NOTATION_RIGHT) || michael@0: IsToDraw(NOTATION_LEFT) || michael@0: IsToDraw(NOTATION_UPDIAGONALSTRIKE) || michael@0: IsToDraw(NOTATION_UPDIAGONALARROW) || michael@0: IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || michael@0: IsToDraw(NOTATION_VERTICALSTRIKE) || michael@0: IsToDraw(NOTATION_CIRCLE) || michael@0: IsToDraw(NOTATION_ROUNDEDBOX) || michael@0: IsToDraw(NOTATION_RADICAL) || michael@0: IsToDraw(NOTATION_LONGDIV)) { michael@0: // set a minimal value for the base height michael@0: bmBase.ascent = std::max(bmOne.ascent, bmBase.ascent); michael@0: bmBase.descent = std::max(0, bmBase.descent); michael@0: } michael@0: michael@0: mBoundingMetrics.ascent = bmBase.ascent; michael@0: mBoundingMetrics.descent = bmBase.descent; michael@0: michael@0: if (IsToDraw(NOTATION_ROUNDEDBOX) || michael@0: IsToDraw(NOTATION_TOP) || michael@0: IsToDraw(NOTATION_LEFT) || michael@0: IsToDraw(NOTATION_RIGHT) || michael@0: IsToDraw(NOTATION_CIRCLE)) michael@0: mBoundingMetrics.ascent += padding; michael@0: michael@0: if (IsToDraw(NOTATION_ROUNDEDBOX) || michael@0: IsToDraw(NOTATION_LEFT) || michael@0: IsToDraw(NOTATION_RIGHT) || michael@0: IsToDraw(NOTATION_BOTTOM) || michael@0: IsToDraw(NOTATION_CIRCLE)) michael@0: mBoundingMetrics.descent += padding; michael@0: michael@0: /////////////// michael@0: // updiagonal arrow notation. We need enough space at the top right corner to michael@0: // draw the arrow head. michael@0: if (IsToDraw(NOTATION_UPDIAGONALARROW)) { michael@0: // This is an estimate, see nsDisplayNotation::Paint for the exact head size michael@0: nscoord arrowHeadSize = kArrowHeadSize * mRuleThickness; michael@0: michael@0: // We want that the arrow shaft strikes the menclose content and that the michael@0: // arrow head does not overlap with that content. Hence we add some space michael@0: // on the right. We don't add space on the top but only ensure that the michael@0: // ascent is large enough. michael@0: dx_right = std::max(dx_right, arrowHeadSize); michael@0: mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, arrowHeadSize); michael@0: } michael@0: michael@0: /////////////// michael@0: // circle notation: we don't want the ellipse to overlap the enclosed michael@0: // content. Hence, we need to increase the size of the bounding box by a michael@0: // factor of at least sqrt(2). michael@0: if (IsToDraw(NOTATION_CIRCLE)) { michael@0: double ratio = (sqrt(2.0) - 1.0) / 2.0; michael@0: nscoord padding2; michael@0: michael@0: // Update horizontal parameters michael@0: padding2 = ratio * bmBase.width; michael@0: michael@0: dx_left = std::max(dx_left, padding2); michael@0: dx_right = std::max(dx_right, padding2); michael@0: michael@0: // Update vertical parameters michael@0: padding2 = ratio * (bmBase.ascent + bmBase.descent); michael@0: michael@0: mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, michael@0: bmBase.ascent + padding2); michael@0: mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, michael@0: bmBase.descent + padding2); michael@0: } michael@0: michael@0: /////////////// michael@0: // longdiv notation: michael@0: if (IsToDraw(NOTATION_LONGDIV)) { michael@0: if (aWidthOnly) { michael@0: nscoord longdiv_width = mMathMLChar[mLongDivCharIndex]. michael@0: GetMaxWidth(PresContext(), aRenderingContext); michael@0: michael@0: // Update horizontal parameters michael@0: dx_left = std::max(dx_left, longdiv_width); michael@0: } else { michael@0: // Stretch the parenthesis to the appropriate height if it is not michael@0: // big enough. michael@0: nsBoundingMetrics contSize = bmBase; michael@0: contSize.ascent = mRuleThickness; michael@0: contSize.descent = bmBase.ascent + bmBase.descent + psi; michael@0: michael@0: // height(longdiv) should be >= height(base) + psi + mRuleThickness michael@0: mMathMLChar[mLongDivCharIndex].Stretch(PresContext(), aRenderingContext, michael@0: NS_STRETCH_DIRECTION_VERTICAL, michael@0: contSize, bmLongdivChar, michael@0: NS_STRETCH_LARGER, false); michael@0: mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar); michael@0: michael@0: // Update horizontal parameters michael@0: dx_left = std::max(dx_left, bmLongdivChar.width); michael@0: michael@0: // Update vertical parameters michael@0: longdivAscent = bmBase.ascent + psi + mRuleThickness; michael@0: longdivDescent = std::max(bmBase.descent, michael@0: (bmLongdivChar.ascent + bmLongdivChar.descent - michael@0: longdivAscent)); michael@0: michael@0: mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, michael@0: longdivAscent); michael@0: mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, michael@0: longdivDescent); michael@0: } michael@0: } michael@0: michael@0: /////////////// michael@0: // radical notation: michael@0: if (IsToDraw(NOTATION_RADICAL)) { michael@0: nscoord *dx_leading = StyleVisibility()->mDirection ? &dx_right : &dx_left; michael@0: michael@0: if (aWidthOnly) { michael@0: nscoord radical_width = mMathMLChar[mRadicalCharIndex]. michael@0: GetMaxWidth(PresContext(), aRenderingContext); michael@0: michael@0: // Update horizontal parameters michael@0: *dx_leading = std::max(*dx_leading, radical_width); michael@0: } else { michael@0: // Stretch the radical symbol to the appropriate height if it is not michael@0: // big enough. michael@0: nsBoundingMetrics contSize = bmBase; michael@0: contSize.ascent = mRuleThickness; michael@0: contSize.descent = bmBase.ascent + bmBase.descent + psi; michael@0: michael@0: // height(radical) should be >= height(base) + psi + mRuleThickness michael@0: mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aRenderingContext, michael@0: NS_STRETCH_DIRECTION_VERTICAL, michael@0: contSize, bmRadicalChar, michael@0: NS_STRETCH_LARGER, michael@0: StyleVisibility()->mDirection); michael@0: mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar); michael@0: michael@0: // Update horizontal parameters michael@0: *dx_leading = std::max(*dx_leading, bmRadicalChar.width); michael@0: michael@0: // Update vertical parameters michael@0: radicalAscent = bmBase.ascent + psi + mRuleThickness; michael@0: radicalDescent = std::max(bmBase.descent, michael@0: (bmRadicalChar.ascent + bmRadicalChar.descent - michael@0: radicalAscent)); michael@0: michael@0: mBoundingMetrics.ascent = std::max(mBoundingMetrics.ascent, michael@0: radicalAscent); michael@0: mBoundingMetrics.descent = std::max(mBoundingMetrics.descent, michael@0: radicalDescent); michael@0: } michael@0: } michael@0: michael@0: /////////////// michael@0: // michael@0: if (IsToDraw(NOTATION_CIRCLE) || michael@0: IsToDraw(NOTATION_ROUNDEDBOX) || michael@0: (IsToDraw(NOTATION_LEFT) && IsToDraw(NOTATION_RIGHT))) { michael@0: // center the menclose around the content (horizontally) michael@0: dx_left = dx_right = std::max(dx_left, dx_right); michael@0: } michael@0: michael@0: /////////////// michael@0: // The maximum size is now computed: set the remaining parameters michael@0: mBoundingMetrics.width = dx_left + bmBase.width + dx_right; michael@0: michael@0: mBoundingMetrics.leftBearing = std::min(0, dx_left + bmBase.leftBearing); michael@0: mBoundingMetrics.rightBearing = michael@0: std::max(mBoundingMetrics.width, dx_left + bmBase.rightBearing); michael@0: michael@0: aDesiredSize.Width() = mBoundingMetrics.width; michael@0: michael@0: aDesiredSize.SetTopAscent(std::max(mBoundingMetrics.ascent, baseSize.TopAscent())); michael@0: aDesiredSize.Height() = aDesiredSize.TopAscent() + michael@0: std::max(mBoundingMetrics.descent, baseSize.Height() - baseSize.TopAscent()); michael@0: michael@0: if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { michael@0: // get the leading to be left at the top of the resulting frame michael@0: // this seems more reliable than using fm->GetLeading() on suspicious michael@0: // fonts michael@0: nscoord leading = nscoord(0.2f * mEmHeight); michael@0: nscoord desiredSizeAscent = aDesiredSize.TopAscent(); michael@0: nscoord desiredSizeDescent = aDesiredSize.Height() - aDesiredSize.TopAscent(); michael@0: michael@0: if (IsToDraw(NOTATION_LONGDIV)) { michael@0: desiredSizeAscent = std::max(desiredSizeAscent, michael@0: longdivAscent + leading); michael@0: desiredSizeDescent = std::max(desiredSizeDescent, michael@0: longdivDescent + mRuleThickness); michael@0: } michael@0: michael@0: if (IsToDraw(NOTATION_RADICAL)) { michael@0: desiredSizeAscent = std::max(desiredSizeAscent, michael@0: radicalAscent + leading); michael@0: desiredSizeDescent = std::max(desiredSizeDescent, michael@0: radicalDescent + mRuleThickness); michael@0: } michael@0: michael@0: aDesiredSize.SetTopAscent(desiredSizeAscent); michael@0: aDesiredSize.Height() = desiredSizeAscent + desiredSizeDescent; michael@0: } michael@0: michael@0: if (IsToDraw(NOTATION_CIRCLE) || michael@0: IsToDraw(NOTATION_ROUNDEDBOX) || michael@0: (IsToDraw(NOTATION_TOP) && IsToDraw(NOTATION_BOTTOM))) { michael@0: // center the menclose around the content (vertically) michael@0: nscoord dy = std::max(aDesiredSize.TopAscent() - bmBase.ascent, michael@0: aDesiredSize.Height() - aDesiredSize.TopAscent() - michael@0: bmBase.descent); michael@0: michael@0: aDesiredSize.SetTopAscent(bmBase.ascent + dy); michael@0: aDesiredSize.Height() = aDesiredSize.TopAscent() + bmBase.descent + dy; michael@0: } michael@0: michael@0: // Update mBoundingMetrics ascent/descent michael@0: if (IsToDraw(NOTATION_TOP) || michael@0: IsToDraw(NOTATION_RIGHT) || michael@0: IsToDraw(NOTATION_LEFT) || michael@0: IsToDraw(NOTATION_UPDIAGONALSTRIKE) || michael@0: IsToDraw(NOTATION_UPDIAGONALARROW) || michael@0: IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || michael@0: IsToDraw(NOTATION_VERTICALSTRIKE) || michael@0: IsToDraw(NOTATION_CIRCLE) || michael@0: IsToDraw(NOTATION_ROUNDEDBOX)) michael@0: mBoundingMetrics.ascent = aDesiredSize.TopAscent(); michael@0: michael@0: if (IsToDraw(NOTATION_BOTTOM) || michael@0: IsToDraw(NOTATION_RIGHT) || michael@0: IsToDraw(NOTATION_LEFT) || michael@0: IsToDraw(NOTATION_UPDIAGONALSTRIKE) || michael@0: IsToDraw(NOTATION_UPDIAGONALARROW) || michael@0: IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || michael@0: IsToDraw(NOTATION_VERTICALSTRIKE) || michael@0: IsToDraw(NOTATION_CIRCLE) || michael@0: IsToDraw(NOTATION_ROUNDEDBOX)) michael@0: mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.TopAscent(); michael@0: michael@0: aDesiredSize.mBoundingMetrics = mBoundingMetrics; michael@0: michael@0: mReference.x = 0; michael@0: mReference.y = aDesiredSize.TopAscent(); michael@0: michael@0: if (aPlaceOrigin) { michael@0: ////////////////// michael@0: // Set position and size of MathMLChars michael@0: if (IsToDraw(NOTATION_LONGDIV)) michael@0: mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left - michael@0: bmLongdivChar.width, michael@0: aDesiredSize.TopAscent() - michael@0: longdivAscent, michael@0: bmLongdivChar.width, michael@0: bmLongdivChar.ascent + michael@0: bmLongdivChar.descent)); michael@0: michael@0: if (IsToDraw(NOTATION_RADICAL)) { michael@0: nscoord dx = (StyleVisibility()->mDirection ? michael@0: dx_left + bmBase.width : dx_left - bmRadicalChar.width); michael@0: michael@0: mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx, michael@0: aDesiredSize.TopAscent() - michael@0: radicalAscent, michael@0: bmRadicalChar.width, michael@0: bmRadicalChar.ascent + michael@0: bmRadicalChar.descent)); michael@0: } michael@0: michael@0: mContentWidth = bmBase.width; michael@0: michael@0: ////////////////// michael@0: // Finish reflowing child frames michael@0: PositionRowChildFrames(dx_left, aDesiredSize.TopAscent()); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nscoord michael@0: nsMathMLmencloseFrame::FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize) michael@0: { michael@0: nscoord gap = nsMathMLContainerFrame::FixInterFrameSpacing(aDesiredSize); michael@0: if (!gap) michael@0: return 0; michael@0: michael@0: // Move the MathML characters michael@0: nsRect rect; michael@0: for (uint32_t i = 0; i < mMathMLChar.Length(); i++) { michael@0: mMathMLChar[i].GetRect(rect); michael@0: rect.MoveBy(gap, 0); michael@0: mMathMLChar[i].SetRect(rect); michael@0: } michael@0: michael@0: return gap; michael@0: } michael@0: michael@0: nsresult michael@0: nsMathMLmencloseFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: if (aAttribute == nsGkAtoms::notation_) { michael@0: InitNotations(); michael@0: } michael@0: michael@0: return nsMathMLContainerFrame:: michael@0: AttributeChanged(aNameSpaceID, aAttribute, aModType); michael@0: } michael@0: michael@0: ////////////////// michael@0: // the Style System will use these to pass the proper style context to our michael@0: // MathMLChar michael@0: nsStyleContext* michael@0: nsMathMLmencloseFrame::GetAdditionalStyleContext(int32_t aIndex) const michael@0: { michael@0: int32_t len = mMathMLChar.Length(); michael@0: if (aIndex >= 0 && aIndex < len) michael@0: return mMathMLChar[aIndex].GetStyleContext(); michael@0: else michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: nsMathMLmencloseFrame::SetAdditionalStyleContext(int32_t aIndex, michael@0: nsStyleContext* aStyleContext) michael@0: { michael@0: int32_t len = mMathMLChar.Length(); michael@0: if (aIndex >= 0 && aIndex < len) michael@0: mMathMLChar[aIndex].SetStyleContext(aStyleContext); michael@0: } michael@0: michael@0: class nsDisplayNotation : public nsDisplayItem michael@0: { michael@0: public: michael@0: nsDisplayNotation(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, const nsRect& aRect, michael@0: nscoord aThickness, nsMencloseNotation aType) michael@0: : nsDisplayItem(aBuilder, aFrame), mRect(aRect), michael@0: mThickness(aThickness), mType(aType) { michael@0: MOZ_COUNT_CTOR(nsDisplayNotation); michael@0: } michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: virtual ~nsDisplayNotation() { michael@0: MOZ_COUNT_DTOR(nsDisplayNotation); michael@0: } michael@0: #endif michael@0: michael@0: virtual void Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) MOZ_OVERRIDE; michael@0: NS_DISPLAY_DECL_NAME("MathMLMencloseNotation", TYPE_MATHML_MENCLOSE_NOTATION) michael@0: michael@0: private: michael@0: nsRect mRect; michael@0: nscoord mThickness; michael@0: nsMencloseNotation mType; michael@0: }; michael@0: michael@0: void nsDisplayNotation::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: // get the gfxRect michael@0: nsPresContext* presContext = mFrame->PresContext(); michael@0: gfxRect rect = presContext->AppUnitsToGfxUnits(mRect + ToReferenceFrame()); michael@0: michael@0: // paint the frame with the current text color michael@0: aCtx->SetColor(mFrame->GetVisitedDependentColor(eCSSProperty_color)); michael@0: michael@0: // change line width to mThickness michael@0: gfxContext *gfxCtx = aCtx->ThebesContext(); michael@0: gfxFloat e = presContext->AppUnitsToGfxUnits(mThickness); michael@0: gfxCtx->Save(); michael@0: gfxCtx->SetLineWidth(e); michael@0: michael@0: rect.Deflate(e / 2.0); michael@0: michael@0: switch(mType) michael@0: { michael@0: case NOTATION_CIRCLE: michael@0: gfxCtx->NewPath(); michael@0: gfxCtx->Ellipse(rect.Center(), rect.Size()); michael@0: gfxCtx->Stroke(); michael@0: break; michael@0: michael@0: case NOTATION_ROUNDEDBOX: michael@0: gfxCtx->NewPath(); michael@0: gfxCtx->RoundedRectangle(rect, gfxCornerSizes(3 * e), true); michael@0: gfxCtx->Stroke(); michael@0: break; michael@0: michael@0: case NOTATION_UPDIAGONALSTRIKE: michael@0: gfxCtx->NewPath(); michael@0: gfxCtx->Line(rect.BottomLeft(), rect.TopRight()); michael@0: gfxCtx->Stroke(); michael@0: break; michael@0: michael@0: case NOTATION_DOWNDIAGONALSTRIKE: michael@0: gfxCtx->NewPath(); michael@0: gfxCtx->Line(rect.TopLeft(), rect.BottomRight()); michael@0: gfxCtx->Stroke(); michael@0: break; michael@0: michael@0: case NOTATION_UPDIAGONALARROW: { michael@0: // Compute some parameters to draw the updiagonalarrow. The values below michael@0: // are taken from MathJax's HTML-CSS output. michael@0: gfxFloat W = rect.Width(); gfxFloat H = rect.Height(); michael@0: gfxFloat l = sqrt(W*W + H*H); michael@0: gfxFloat f = gfxFloat(kArrowHeadSize) * e / l; michael@0: gfxFloat w = W * f; gfxFloat h = H * f; michael@0: michael@0: // Draw the arrow shaft michael@0: gfxCtx->NewPath(); michael@0: gfxCtx->Line(rect.BottomLeft(), rect.TopRight() + gfxPoint(-.7*w, .7*h)); michael@0: gfxCtx->Stroke(); michael@0: michael@0: // Draw the arrow head michael@0: gfxCtx->NewPath(); michael@0: gfxPoint p[] = { michael@0: rect.TopRight(), michael@0: rect.TopRight() + gfxPoint(-w -.4*h, std::max(-e / 2.0, h - .4*w)), michael@0: rect.TopRight() + gfxPoint(-.7*w, .7*h), michael@0: rect.TopRight() + gfxPoint(std::min(e / 2.0, -w + .4*h), h + .4*w), michael@0: rect.TopRight() michael@0: }; michael@0: gfxCtx->Polygon(p, MOZ_ARRAY_LENGTH(p)); michael@0: gfxCtx->Fill(); michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: NS_NOTREACHED("This notation can not be drawn using nsDisplayNotation"); michael@0: break; michael@0: } michael@0: michael@0: gfxCtx->Restore(); michael@0: } michael@0: michael@0: void michael@0: nsMathMLmencloseFrame::DisplayNotation(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, const nsRect& aRect, michael@0: const nsDisplayListSet& aLists, michael@0: nscoord aThickness, michael@0: nsMencloseNotation aType) michael@0: { michael@0: if (!aFrame->StyleVisibility()->IsVisible() || aRect.IsEmpty() || michael@0: aThickness <= 0) michael@0: return; michael@0: michael@0: aLists.Content()->AppendNewToTop(new (aBuilder) michael@0: nsDisplayNotation(aBuilder, aFrame, aRect, aThickness, aType)); michael@0: }