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 "nsMathMLmunderoverFrame.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsMathMLmmultiscriptsFrame.h" michael@0: #include michael@0: michael@0: // michael@0: // -- attach an underscript-overscript pair to a base - implementation michael@0: // -- attach an overscript to a base - implementation michael@0: // -- attach an underscript to a base - implementation michael@0: // michael@0: michael@0: nsIFrame* michael@0: NS_NewMathMLmunderoverFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsMathMLmunderoverFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmunderoverFrame) michael@0: michael@0: nsMathMLmunderoverFrame::~nsMathMLmunderoverFrame() michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: nsMathMLmunderoverFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: if (nsGkAtoms::accent_ == aAttribute || michael@0: nsGkAtoms::accentunder_ == aAttribute) { michael@0: // When we have automatic data to update within ourselves, we ask our michael@0: // parent to re-layout its children michael@0: return ReLayoutChildren(mParent); michael@0: } michael@0: michael@0: return nsMathMLContainerFrame:: michael@0: AttributeChanged(aNameSpaceID, aAttribute, aModType); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMathMLmunderoverFrame::UpdatePresentationData(uint32_t aFlagsValues, michael@0: uint32_t aFlagsToUpdate) michael@0: { michael@0: nsMathMLContainerFrame::UpdatePresentationData(aFlagsValues, aFlagsToUpdate); michael@0: // disable the stretch-all flag if we are going to act like a subscript-superscript pair michael@0: if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) && michael@0: StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) { michael@0: mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY; michael@0: } michael@0: else { michael@0: mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMathMLmunderoverFrame::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_HORIZONTALLY; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: uint8_t michael@0: nsMathMLmunderoverFrame::ScriptIncrement(nsIFrame* aFrame) michael@0: { michael@0: nsIFrame* child = mFrames.FirstChild(); michael@0: if (!aFrame || aFrame == child) { michael@0: return 0; michael@0: } michael@0: child = child->GetNextSibling(); michael@0: if (aFrame == child) { michael@0: if (mContent->Tag() == nsGkAtoms::mover_) { michael@0: return mIncrementOver ? 1 : 0; michael@0: } michael@0: return mIncrementUnder ? 1 : 0; michael@0: } michael@0: if (child && aFrame == child->GetNextSibling()) { michael@0: // must be a over frame of munderover michael@0: return mIncrementOver ? 1 : 0; michael@0: } michael@0: return 0; // frame not found michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMathMLmunderoverFrame::TransmitAutomaticData() michael@0: { michael@0: // At this stage, all our children are in sync and we can fully michael@0: // resolve our own mEmbellishData struct michael@0: //--------------------------------------------------------------------- michael@0: michael@0: /* michael@0: The REC says: michael@0: michael@0: As regards munder (respectively mover) : michael@0: The default value of accentunder is false, unless underscript michael@0: is an element or an embellished operator. If underscript is michael@0: an element, the value of its accent attribute is used as the michael@0: default value of accentunder. If underscript is an embellished michael@0: operator, the accent attribute of the element at its michael@0: core is used as the default value. As with all attributes, an michael@0: explicitly given value overrides the default. michael@0: michael@0: XXX The winner is the outermost setting in conflicting settings like these: michael@0: michael@0: ... michael@0: ... michael@0: michael@0: michael@0: As regards munderover: michael@0: The accent and accentunder attributes have the same effect as michael@0: the attributes with the same names on and , michael@0: respectively. Their default values are also computed in the michael@0: same manner as described for those elements, with the default michael@0: value of accent depending on overscript and the default value michael@0: of accentunder depending on underscript. michael@0: */ michael@0: michael@0: nsIFrame* overscriptFrame = nullptr; michael@0: nsIFrame* underscriptFrame = nullptr; michael@0: nsIFrame* baseFrame = mFrames.FirstChild(); michael@0: nsIAtom* tag = mContent->Tag(); michael@0: michael@0: if (baseFrame) { michael@0: if (tag == nsGkAtoms::munder_ || michael@0: tag == nsGkAtoms::munderover_) { michael@0: underscriptFrame = baseFrame->GetNextSibling(); michael@0: } else { michael@0: NS_ASSERTION(tag == nsGkAtoms::mover_, "mContent->Tag() not recognized"); michael@0: overscriptFrame = baseFrame->GetNextSibling(); michael@0: } michael@0: } michael@0: if (underscriptFrame && michael@0: tag == nsGkAtoms::munderover_) { michael@0: overscriptFrame = underscriptFrame->GetNextSibling(); michael@0: michael@0: } michael@0: michael@0: // if our base is an embellished operator, let its state bubble to us (in particular, michael@0: // this is where we get the flag for NS_MATHML_EMBELLISH_MOVABLELIMITS). Our flags michael@0: // are reset to the default values of false if the base frame isn't embellished. michael@0: mPresentationData.baseFrame = baseFrame; michael@0: GetEmbellishDataFrom(baseFrame, mEmbellishData); michael@0: michael@0: // The default value of accentunder is false, unless the underscript is embellished michael@0: // and its core is an accent michael@0: nsEmbellishData embellishData; michael@0: nsAutoString value; michael@0: if (tag == nsGkAtoms::munder_ || michael@0: tag == nsGkAtoms::munderover_) { michael@0: GetEmbellishDataFrom(underscriptFrame, embellishData); michael@0: if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) { michael@0: mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER; michael@0: } else { michael@0: mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER; michael@0: } michael@0: michael@0: // if we have an accentunder attribute, it overrides what the underscript said michael@0: if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accentunder_, value)) { michael@0: if (value.EqualsLiteral("true")) { michael@0: mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER; michael@0: } else if (value.EqualsLiteral("false")) { michael@0: mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // The default value of accent is false, unless the overscript is embellished michael@0: // and its core is an accent michael@0: if (tag == nsGkAtoms::mover_ || michael@0: tag == nsGkAtoms::munderover_) { michael@0: GetEmbellishDataFrom(overscriptFrame, embellishData); michael@0: if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) { michael@0: mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER; michael@0: } else { michael@0: mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER; michael@0: } michael@0: michael@0: // if we have an accent attribute, it overrides what the overscript said michael@0: if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accent_, value)) { michael@0: if (value.EqualsLiteral("true")) { michael@0: mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER; michael@0: } else if (value.EqualsLiteral("false")) { michael@0: mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER; michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool subsupDisplay = michael@0: NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) && michael@0: StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE; michael@0: michael@0: // disable the stretch-all flag if we are going to act like a superscript michael@0: if (subsupDisplay) { michael@0: mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY; michael@0: } michael@0: michael@0: // Now transmit any change that we want to our children so that they michael@0: // can update their mPresentationData structs michael@0: //--------------------------------------------------------------------- michael@0: michael@0: /* The REC says: michael@0: Within underscript, always sets displaystyle to "false", michael@0: but increments scriptlevel by 1 only when accentunder is "false". michael@0: michael@0: Within overscript, always sets displaystyle to "false", michael@0: but increments scriptlevel by 1 only when accent is "false". michael@0: michael@0: Within subscript and superscript it increments scriptlevel by 1, and michael@0: sets displaystyle to "false", but leaves both attributes unchanged within michael@0: base. michael@0: michael@0: The TeXBook treats 'over' like a superscript, so p.141 or Rule 13a michael@0: say it shouldn't be compressed. However, The TeXBook says michael@0: that math accents and \overline change uncramped styles to their michael@0: cramped counterparts. michael@0: */ michael@0: if (tag == nsGkAtoms::mover_ || michael@0: tag == nsGkAtoms::munderover_) { michael@0: uint32_t compress = NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) michael@0: ? NS_MATHML_COMPRESSED : 0; michael@0: mIncrementOver = michael@0: !NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) || michael@0: subsupDisplay; michael@0: SetIncrementScriptLevel(tag == nsGkAtoms::mover_ ? 1 : 2, mIncrementOver); michael@0: if (mIncrementOver) { michael@0: PropagateFrameFlagFor(overscriptFrame, michael@0: NS_FRAME_MATHML_SCRIPT_DESCENDANT); michael@0: } michael@0: PropagatePresentationDataFor(overscriptFrame, compress, compress); michael@0: } michael@0: /* michael@0: The TeXBook treats 'under' like a subscript, so p.141 or Rule 13a michael@0: say it should be compressed michael@0: */ michael@0: if (tag == nsGkAtoms::munder_ || michael@0: tag == nsGkAtoms::munderover_) { michael@0: mIncrementUnder = michael@0: !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags) || michael@0: subsupDisplay; michael@0: SetIncrementScriptLevel(1, mIncrementUnder); michael@0: if (mIncrementUnder) { michael@0: PropagateFrameFlagFor(underscriptFrame, michael@0: NS_FRAME_MATHML_SCRIPT_DESCENDANT); michael@0: } michael@0: PropagatePresentationDataFor(underscriptFrame, michael@0: NS_MATHML_COMPRESSED, michael@0: NS_MATHML_COMPRESSED); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* michael@0: The REC says: michael@0: * If the base is an operator with movablelimits="true" (or an embellished michael@0: operator whose element core has movablelimits="true"), and michael@0: displaystyle="false", then underscript and overscript are drawn in michael@0: a subscript and superscript position, respectively. In this case, michael@0: the accent and accentunder attributes are ignored. This is often michael@0: used for limits on symbols such as ∑. michael@0: michael@0: i.e.,: michael@0: if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishDataflags) && michael@0: StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) { michael@0: // place like subscript-superscript pair michael@0: } michael@0: else { michael@0: // place like underscript-overscript pair michael@0: } michael@0: */ michael@0: michael@0: /* virtual */ nsresult michael@0: nsMathMLmunderoverFrame::Place(nsRenderingContext& aRenderingContext, michael@0: bool aPlaceOrigin, michael@0: nsHTMLReflowMetrics& aDesiredSize) michael@0: { michael@0: nsIAtom* tag = mContent->Tag(); michael@0: if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) && michael@0: StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) { michael@0: //place like sub sup or subsup michael@0: nscoord scriptSpace = nsPresContext::CSSPointsToAppUnits(0.5f); michael@0: if (tag == nsGkAtoms::munderover_) { michael@0: return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), michael@0: aRenderingContext, michael@0: aPlaceOrigin, michael@0: aDesiredSize, michael@0: this, 0, 0, michael@0: scriptSpace); michael@0: } else if (tag == nsGkAtoms::munder_) { michael@0: return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), michael@0: aRenderingContext, michael@0: aPlaceOrigin, michael@0: aDesiredSize, michael@0: this, 0, 0, michael@0: scriptSpace); michael@0: } else { michael@0: NS_ASSERTION(tag == nsGkAtoms::mover_, "mContent->Tag() not recognized"); michael@0: return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), michael@0: aRenderingContext, michael@0: aPlaceOrigin, michael@0: aDesiredSize, michael@0: this, 0, 0, michael@0: scriptSpace); michael@0: } michael@0: michael@0: } michael@0: michael@0: //////////////////////////////////// michael@0: // Get the children's desired sizes michael@0: michael@0: nsBoundingMetrics bmBase, bmUnder, bmOver; michael@0: nsHTMLReflowMetrics baseSize(aDesiredSize.GetWritingMode()); michael@0: nsHTMLReflowMetrics underSize(aDesiredSize.GetWritingMode()); michael@0: nsHTMLReflowMetrics overSize(aDesiredSize.GetWritingMode()); michael@0: nsIFrame* overFrame = nullptr; michael@0: nsIFrame* underFrame = nullptr; michael@0: nsIFrame* baseFrame = mFrames.FirstChild(); michael@0: underSize.SetTopAscent(0); michael@0: overSize.SetTopAscent(0); michael@0: bool haveError = false; michael@0: if (baseFrame) { michael@0: if (tag == nsGkAtoms::munder_ || michael@0: tag == nsGkAtoms::munderover_) { michael@0: underFrame = baseFrame->GetNextSibling(); michael@0: } else if (tag == nsGkAtoms::mover_) { michael@0: overFrame = baseFrame->GetNextSibling(); michael@0: } michael@0: } michael@0: if (underFrame && tag == nsGkAtoms::munderover_) { michael@0: overFrame = underFrame->GetNextSibling(); michael@0: } michael@0: michael@0: if (tag == nsGkAtoms::munder_) { michael@0: if (!baseFrame || !underFrame || underFrame->GetNextSibling()) { michael@0: // report an error, encourage people to get their markups in order michael@0: haveError = true; michael@0: } michael@0: } michael@0: if (tag == nsGkAtoms::mover_) { michael@0: if (!baseFrame || !overFrame || overFrame->GetNextSibling()) { michael@0: // report an error, encourage people to get their markups in order michael@0: haveError = true; michael@0: } michael@0: } michael@0: if (tag == nsGkAtoms::munderover_) { michael@0: if (!baseFrame || !underFrame || !overFrame || overFrame->GetNextSibling()) { michael@0: // report an error, encourage people to get their markups in order michael@0: haveError = true; michael@0: } michael@0: } michael@0: if (haveError) { michael@0: if (aPlaceOrigin) { michael@0: ReportChildCountError(); michael@0: } michael@0: return ReflowError(aRenderingContext, aDesiredSize); michael@0: } michael@0: GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); michael@0: if (underFrame) { michael@0: GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder); michael@0: } michael@0: if (overFrame) { michael@0: GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver); michael@0: } michael@0: michael@0: nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); michael@0: michael@0: //////////////////// michael@0: // Place Children michael@0: michael@0: nsRefPtr fm; michael@0: nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); michael@0: aRenderingContext.SetFont(fm); michael@0: michael@0: nscoord xHeight = fm->XHeight(); michael@0: michael@0: nscoord ruleThickness; michael@0: GetRuleThickness (aRenderingContext, fm, ruleThickness); michael@0: michael@0: nscoord correction = 0; michael@0: GetItalicCorrection (bmBase, correction); michael@0: michael@0: // there are 2 different types of placement depending on michael@0: // whether we want an accented under or not michael@0: michael@0: nscoord underDelta1 = 0; // gap between base and underscript michael@0: nscoord underDelta2 = 0; // extra space beneath underscript michael@0: michael@0: if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) { michael@0: // Rule 13a, App. G, TeXbook michael@0: nscoord bigOpSpacing2, bigOpSpacing4, bigOpSpacing5, dummy; michael@0: GetBigOpSpacings (fm, michael@0: dummy, bigOpSpacing2, michael@0: dummy, bigOpSpacing4, michael@0: bigOpSpacing5); michael@0: underDelta1 = std::max(bigOpSpacing2, (bigOpSpacing4 - bmUnder.ascent)); michael@0: underDelta2 = bigOpSpacing5; michael@0: } michael@0: else { michael@0: // No corresponding rule in TeXbook - we are on our own here michael@0: // XXX tune the gap delta between base and underscript michael@0: michael@0: // Should we use Rule 10 like \underline does? michael@0: underDelta1 = ruleThickness + onePixel/2; michael@0: underDelta2 = ruleThickness; michael@0: } michael@0: // empty under? michael@0: if (!(bmUnder.ascent + bmUnder.descent)) { michael@0: underDelta1 = 0; michael@0: underDelta2 = 0; michael@0: } michael@0: michael@0: nscoord overDelta1 = 0; // gap between base and overscript michael@0: nscoord overDelta2 = 0; // extra space above overscript michael@0: michael@0: if (!NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) { michael@0: // Rule 13a, App. G, TeXbook michael@0: nscoord bigOpSpacing1, bigOpSpacing3, bigOpSpacing5, dummy; michael@0: GetBigOpSpacings (fm, michael@0: bigOpSpacing1, dummy, michael@0: bigOpSpacing3, dummy, michael@0: bigOpSpacing5); michael@0: overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - bmOver.descent)); michael@0: overDelta2 = bigOpSpacing5; michael@0: michael@0: // XXX This is not a TeX rule... michael@0: // delta1 (as computed abvove) can become really big when bmOver.descent is michael@0: // negative, e.g., if the content is &OverBar. In such case, we use the height michael@0: if (bmOver.descent < 0) michael@0: overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - (bmOver.ascent + bmOver.descent))); michael@0: } michael@0: else { michael@0: // Rule 12, App. G, TeXbook michael@0: // We are going to modify this rule to make it more general. michael@0: // The idea behind Rule 12 in the TeXBook is to keep the accent michael@0: // as close to the base as possible, while ensuring that the michael@0: // distance between the *baseline* of the accent char and michael@0: // the *baseline* of the base is atleast x-height. michael@0: // The idea is that for normal use, we would like all the accents michael@0: // on a line to line up atleast x-height above the baseline michael@0: // if possible. michael@0: // When the ascent of the base is >= x-height, michael@0: // the baseline of the accent char is placed just above the base michael@0: // (specifically, the baseline of the accent char is placed michael@0: // above the baseline of the base by the ascent of the base). michael@0: // For ease of implementation, michael@0: // this assumes that the font-designer designs accents michael@0: // in such a way that the bottom of the accent is atleast x-height michael@0: // above its baseline, otherwise there will be collisions michael@0: // with the base. Also there should be proper padding between michael@0: // the bottom of the accent char and its baseline. michael@0: // The above rule may not be obvious from a first michael@0: // reading of rule 12 in the TeXBook !!! michael@0: // The mathml tag can use accent chars that michael@0: // do not follow this convention. So we modify TeX's rule michael@0: // so that TeX's rule gets subsumed for accents that follow michael@0: // TeX's convention, michael@0: // while also allowing accents that do not follow the convention : michael@0: // we try to keep the *bottom* of the accent char atleast x-height michael@0: // from the baseline of the base char. we also slap on an extra michael@0: // padding between the accent and base chars. michael@0: overDelta1 = ruleThickness + onePixel/2; michael@0: if (bmBase.ascent < xHeight) { michael@0: // also ensure at least x-height above the baseline of the base michael@0: overDelta1 += xHeight - bmBase.ascent; michael@0: } michael@0: overDelta2 = ruleThickness; michael@0: } michael@0: // empty over? michael@0: if (!(bmOver.ascent + bmOver.descent)) { michael@0: overDelta1 = 0; michael@0: overDelta2 = 0; michael@0: } michael@0: michael@0: nscoord dxBase = 0, dxOver = 0, dxUnder = 0; michael@0: nsAutoString valueAlign; michael@0: enum { michael@0: center, michael@0: left, michael@0: right michael@0: } alignPosition = center; michael@0: michael@0: if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, valueAlign)) { michael@0: if (valueAlign.EqualsLiteral("left")) { michael@0: alignPosition = left; michael@0: } else if (valueAlign.EqualsLiteral("right")) { michael@0: alignPosition = right; michael@0: } michael@0: } michael@0: michael@0: ////////// michael@0: // pass 1, do what does: attach the overscript on the base michael@0: michael@0: // Ad-hoc - This is to override fonts which have ready-made _accent_ michael@0: // glyphs with negative lbearing and rbearing. We want to position michael@0: // the overscript ourselves michael@0: nscoord overWidth = bmOver.width; michael@0: if (!overWidth && (bmOver.rightBearing - bmOver.leftBearing > 0)) { michael@0: overWidth = bmOver.rightBearing - bmOver.leftBearing; michael@0: dxOver = -bmOver.leftBearing; michael@0: } michael@0: michael@0: if (NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) { michael@0: mBoundingMetrics.width = bmBase.width; michael@0: if (alignPosition == center) { michael@0: dxOver += correction; michael@0: } michael@0: } michael@0: else { michael@0: mBoundingMetrics.width = std::max(bmBase.width, overWidth); michael@0: if (alignPosition == center) { michael@0: dxOver += correction/2; michael@0: } michael@0: } michael@0: michael@0: if (alignPosition == center) { michael@0: dxOver += (mBoundingMetrics.width - overWidth)/2; michael@0: dxBase = (mBoundingMetrics.width - bmBase.width)/2; michael@0: } else if (alignPosition == right) { michael@0: dxOver += mBoundingMetrics.width - overWidth; michael@0: dxBase = mBoundingMetrics.width - bmBase.width; michael@0: } michael@0: michael@0: mBoundingMetrics.ascent = michael@0: bmBase.ascent + overDelta1 + bmOver.ascent + bmOver.descent; michael@0: mBoundingMetrics.descent = bmBase.descent; michael@0: mBoundingMetrics.leftBearing = michael@0: std::min(dxBase + bmBase.leftBearing, dxOver + bmOver.leftBearing); michael@0: mBoundingMetrics.rightBearing = michael@0: std::max(dxBase + bmBase.rightBearing, dxOver + bmOver.rightBearing); michael@0: michael@0: ////////// michael@0: // pass 2, do what does: attach the underscript on the previous michael@0: // result. We conceptually view the previous result as an "anynomous base" michael@0: // from where to attach the underscript. Hence if the underscript is empty, michael@0: // we should end up like . If the overscript is empty, we should michael@0: // end up like . michael@0: michael@0: nsBoundingMetrics bmAnonymousBase = mBoundingMetrics; michael@0: nscoord ascentAnonymousBase = michael@0: std::max(mBoundingMetrics.ascent + overDelta2, michael@0: overSize.TopAscent() + bmOver.descent + overDelta1 + bmBase.ascent); michael@0: ascentAnonymousBase = std::max(ascentAnonymousBase, baseSize.TopAscent()); michael@0: michael@0: // Width of non-spacing marks is zero so use left and right bearing. michael@0: nscoord underWidth = bmUnder.width; michael@0: if (!underWidth) { michael@0: underWidth = bmUnder.rightBearing - bmUnder.leftBearing; michael@0: dxUnder = -bmUnder.leftBearing; michael@0: } michael@0: michael@0: nscoord maxWidth = std::max(bmAnonymousBase.width, underWidth); michael@0: if (alignPosition == center && michael@0: !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) { michael@0: GetItalicCorrection(bmAnonymousBase, correction); michael@0: dxUnder += -correction/2; michael@0: } michael@0: nscoord dxAnonymousBase = 0; michael@0: if (alignPosition == center) { michael@0: dxUnder += (maxWidth - underWidth)/2; michael@0: dxAnonymousBase = (maxWidth - bmAnonymousBase.width)/2; michael@0: } else if (alignPosition == right) { michael@0: dxUnder += maxWidth - underWidth; michael@0: dxAnonymousBase = maxWidth - bmAnonymousBase.width; michael@0: } michael@0: michael@0: // adjust the offsets of the real base and overscript since their michael@0: // final offsets should be relative to us... michael@0: dxOver += dxAnonymousBase; michael@0: dxBase += dxAnonymousBase; michael@0: michael@0: mBoundingMetrics.width = michael@0: std::max(dxAnonymousBase + bmAnonymousBase.width, dxUnder + bmUnder.width); michael@0: // At this point, mBoundingMetrics.ascent = bmAnonymousBase.ascent michael@0: mBoundingMetrics.descent = michael@0: bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + bmUnder.descent; michael@0: mBoundingMetrics.leftBearing = michael@0: std::min(dxAnonymousBase + bmAnonymousBase.leftBearing, dxUnder + bmUnder.leftBearing); michael@0: mBoundingMetrics.rightBearing = michael@0: std::max(dxAnonymousBase + bmAnonymousBase.rightBearing, dxUnder + bmUnder.rightBearing); michael@0: michael@0: aDesiredSize.SetTopAscent(ascentAnonymousBase); michael@0: aDesiredSize.Height() = aDesiredSize.TopAscent() + michael@0: std::max(mBoundingMetrics.descent + underDelta2, michael@0: bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + michael@0: underSize.Height() - underSize.TopAscent()); michael@0: aDesiredSize.Height() = std::max(aDesiredSize.Height(), michael@0: aDesiredSize.TopAscent() + michael@0: baseSize.Height() - baseSize.TopAscent()); michael@0: aDesiredSize.Width() = mBoundingMetrics.width; 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: nscoord dy; michael@0: // place overscript michael@0: if (overFrame) { michael@0: dy = aDesiredSize.TopAscent() - mBoundingMetrics.ascent + bmOver.ascent michael@0: - overSize.TopAscent(); michael@0: FinishReflowChild (overFrame, PresContext(), overSize, nullptr, dxOver, dy, 0); michael@0: } michael@0: // place base michael@0: dy = aDesiredSize.TopAscent() - baseSize.TopAscent(); michael@0: FinishReflowChild (baseFrame, PresContext(), baseSize, nullptr, dxBase, dy, 0); michael@0: // place underscript michael@0: if (underFrame) { michael@0: dy = aDesiredSize.TopAscent() + mBoundingMetrics.descent - bmUnder.descent michael@0: - underSize.TopAscent(); michael@0: FinishReflowChild (underFrame, PresContext(), underSize, nullptr, michael@0: dxUnder, dy, 0); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: }