/* static */ void nsMathMLFrame::GetRuleThickness(nsIRenderingContext& aRenderingContext, nsIFontMetrics* aFontMetrics, nscoord& aRuleThickness) { // get the bounding metrics of the overbar char, the rendering context // is assumed to have been set with the font of the current style context #ifdef NS_DEBUG nsCOMPtr<nsIFontMetrics> currFontMetrics; aRenderingContext.GetFontMetrics(*getter_AddRefs(currFontMetrics)); NS_ASSERTION(currFontMetrics->Font().Equals(aFontMetrics->Font()), "unexpected state"); #endif nscoord xHeight; aFontMetrics->GetXHeight(xHeight); PRUnichar overBar = 0x00AF; nsBoundingMetrics bm; nsresult rv = aRenderingContext.GetBoundingMetrics(&overBar, PRUint32(1), bm); if (NS_SUCCEEDED(rv)) { aRuleThickness = bm.ascent + bm.descent; } if (NS_FAILED(rv) || aRuleThickness <= 0 || aRuleThickness >= xHeight) { // fall-back to the other version GetRuleThickness(aFontMetrics, aRuleThickness); } #if 0 nscoord oldRuleThickness; GetRuleThickness(aFontMetrics, oldRuleThickness); PRUnichar sqrt = 0xE063; // a sqrt glyph from TeX's CMEX font rv = aRenderingContext.GetBoundingMetrics(&sqrt, PRUint32(1), bm); nscoord sqrtrule = bm.ascent; // according to TeX, the ascent should be the rule printf("xheight:%4d rule:%4d oldrule:%4d sqrtrule:%4d\n", xHeight, aRuleThickness, oldRuleThickness, sqrtrule); #endif }
/* static */ void nsMathMLFrame::GetRuleThickness(nsRenderingContext& aRenderingContext, nsFontMetrics* aFontMetrics, nscoord& aRuleThickness) { // get the bounding metrics of the overbar char, the rendering context // is assumed to have been set with the font of the current style context NS_ASSERTION(aRenderingContext.FontMetrics()->Font(). Equals(aFontMetrics->Font()), "unexpected state"); nscoord xHeight = aFontMetrics->XHeight(); PRUnichar overBar = 0x00AF; nsBoundingMetrics bm = aRenderingContext.GetBoundingMetrics(&overBar, 1); aRuleThickness = bm.ascent + bm.descent; if (aRuleThickness <= 0 || aRuleThickness >= xHeight) { // fall-back to the other version GetRuleThickness(aFontMetrics, aRuleThickness); } }
/* virtual */ nsresult nsMathMLmunderoverFrame::Place(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { nsIAtom* tag = mContent->Tag(); if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) && StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) { //place like sub sup or subsup if (tag == nsGkAtoms::munderover_) { return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), aRenderingContext, aPlaceOrigin, aDesiredSize, this, 0, 0); } else if (tag == nsGkAtoms::munder_) { return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), aRenderingContext, aPlaceOrigin, aDesiredSize, this, 0, 0); } else { NS_ASSERTION(tag == nsGkAtoms::mover_, "mContent->Tag() not recognized"); return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), aRenderingContext, aPlaceOrigin, aDesiredSize, this, 0, 0); } } //////////////////////////////////// // Get the children's desired sizes nsBoundingMetrics bmBase, bmUnder, bmOver; nsHTMLReflowMetrics baseSize(aDesiredSize.GetWritingMode()); nsHTMLReflowMetrics underSize(aDesiredSize.GetWritingMode()); nsHTMLReflowMetrics overSize(aDesiredSize.GetWritingMode()); nsIFrame* overFrame = nullptr; nsIFrame* underFrame = nullptr; nsIFrame* baseFrame = mFrames.FirstChild(); underSize.SetBlockStartAscent(0); overSize.SetBlockStartAscent(0); bool haveError = false; if (baseFrame) { if (tag == nsGkAtoms::munder_ || tag == nsGkAtoms::munderover_) { underFrame = baseFrame->GetNextSibling(); } else if (tag == nsGkAtoms::mover_) { overFrame = baseFrame->GetNextSibling(); } } if (underFrame && tag == nsGkAtoms::munderover_) { overFrame = underFrame->GetNextSibling(); } if (tag == nsGkAtoms::munder_) { if (!baseFrame || !underFrame || underFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order haveError = true; } } if (tag == nsGkAtoms::mover_) { if (!baseFrame || !overFrame || overFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order haveError = true; } } if (tag == nsGkAtoms::munderover_) { if (!baseFrame || !underFrame || !overFrame || overFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order haveError = true; } } if (haveError) { if (aPlaceOrigin) { ReportChildCountError(); } return ReflowError(aRenderingContext, aDesiredSize); } GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); if (underFrame) { GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder); } if (overFrame) { GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver); } nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); //////////////////// // Place Children nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); nscoord xHeight = fm->XHeight(); nscoord oneDevPixel = fm->AppUnitsPerDevPixel(); gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont(); nscoord ruleThickness; GetRuleThickness (aRenderingContext, fm, ruleThickness); nscoord correction = 0; GetItalicCorrection (bmBase, correction); // there are 2 different types of placement depending on // whether we want an accented under or not nscoord underDelta1 = 0; // gap between base and underscript nscoord underDelta2 = 0; // extra space beneath underscript if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) { // Rule 13a, App. G, TeXbook nscoord bigOpSpacing2, bigOpSpacing4, bigOpSpacing5, dummy; GetBigOpSpacings (fm, dummy, bigOpSpacing2, dummy, bigOpSpacing4, bigOpSpacing5); if (mathFont) { // XXXfredw The Open Type MATH table has some StretchStack* parameters // that we may use when the base is a stretchy horizontal operator. See // bug 963131. bigOpSpacing2 = mathFont->GetMathConstant(gfxFontEntry::LowerLimitGapMin, oneDevPixel); bigOpSpacing4 = mathFont->GetMathConstant(gfxFontEntry::LowerLimitBaselineDropMin, oneDevPixel); bigOpSpacing5 = 0; } underDelta1 = std::max(bigOpSpacing2, (bigOpSpacing4 - bmUnder.ascent)); underDelta2 = bigOpSpacing5; } else { // No corresponding rule in TeXbook - we are on our own here // XXX tune the gap delta between base and underscript // XXX Should we use Rule 10 like \underline does? // XXXfredw Perhaps use the Underbar* parameters of the MATH table. See // bug 963125. underDelta1 = ruleThickness + onePixel/2; underDelta2 = ruleThickness; } // empty under? if (!(bmUnder.ascent + bmUnder.descent)) { underDelta1 = 0; underDelta2 = 0; } nscoord overDelta1 = 0; // gap between base and overscript nscoord overDelta2 = 0; // extra space above overscript if (!NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) { // Rule 13a, App. G, TeXbook // XXXfredw The Open Type MATH table has some StretchStack* parameters // that we may use when the base is a stretchy horizontal operator. See // bug 963131. nscoord bigOpSpacing1, bigOpSpacing3, bigOpSpacing5, dummy; GetBigOpSpacings (fm, bigOpSpacing1, dummy, bigOpSpacing3, dummy, bigOpSpacing5); if (mathFont) { // XXXfredw The Open Type MATH table has some StretchStack* parameters // that we may use when the base is a stretchy horizontal operator. See // bug 963131. bigOpSpacing1 = mathFont->GetMathConstant(gfxFontEntry::UpperLimitGapMin, oneDevPixel); bigOpSpacing3 = mathFont->GetMathConstant(gfxFontEntry::UpperLimitBaselineRiseMin, oneDevPixel); bigOpSpacing5 = 0; } overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - bmOver.descent)); overDelta2 = bigOpSpacing5; // XXX This is not a TeX rule... // delta1 (as computed abvove) can become really big when bmOver.descent is // negative, e.g., if the content is &OverBar. In such case, we use the height if (bmOver.descent < 0) overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - (bmOver.ascent + bmOver.descent))); } else { // Rule 12, App. G, TeXbook // We are going to modify this rule to make it more general. // The idea behind Rule 12 in the TeXBook is to keep the accent // as close to the base as possible, while ensuring that the // distance between the *baseline* of the accent char and // the *baseline* of the base is atleast x-height. // The idea is that for normal use, we would like all the accents // on a line to line up atleast x-height above the baseline // if possible. // When the ascent of the base is >= x-height, // the baseline of the accent char is placed just above the base // (specifically, the baseline of the accent char is placed // above the baseline of the base by the ascent of the base). // For ease of implementation, // this assumes that the font-designer designs accents // in such a way that the bottom of the accent is atleast x-height // above its baseline, otherwise there will be collisions // with the base. Also there should be proper padding between // the bottom of the accent char and its baseline. // The above rule may not be obvious from a first // reading of rule 12 in the TeXBook !!! // The mathml <mover> tag can use accent chars that // do not follow this convention. So we modify TeX's rule // so that TeX's rule gets subsumed for accents that follow // TeX's convention, // while also allowing accents that do not follow the convention : // we try to keep the *bottom* of the accent char atleast x-height // from the baseline of the base char. we also slap on an extra // padding between the accent and base chars. overDelta1 = ruleThickness + onePixel/2; nscoord accentBaseHeight = xHeight; if (mathFont) { accentBaseHeight = mathFont->GetMathConstant(gfxFontEntry::AccentBaseHeight, oneDevPixel); } if (bmBase.ascent < accentBaseHeight) { // also ensure at least accentBaseHeight above the baseline of the base overDelta1 += accentBaseHeight - bmBase.ascent; } overDelta2 = ruleThickness; } // empty over? if (!(bmOver.ascent + bmOver.descent)) { overDelta1 = 0; overDelta2 = 0; } nscoord dxBase = 0, dxOver = 0, dxUnder = 0; nsAutoString valueAlign; enum { center, left, right } alignPosition = center; if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, valueAlign)) { if (valueAlign.EqualsLiteral("left")) { alignPosition = left; } else if (valueAlign.EqualsLiteral("right")) { alignPosition = right; } } ////////// // pass 1, do what <mover> does: attach the overscript on the base // Ad-hoc - This is to override fonts which have ready-made _accent_ // glyphs with negative lbearing and rbearing. We want to position // the overscript ourselves nscoord overWidth = bmOver.width; if (!overWidth && (bmOver.rightBearing - bmOver.leftBearing > 0)) { overWidth = bmOver.rightBearing - bmOver.leftBearing; dxOver = -bmOver.leftBearing; } if (NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) { mBoundingMetrics.width = bmBase.width; if (alignPosition == center) { dxOver += correction; } } else { mBoundingMetrics.width = std::max(bmBase.width, overWidth); if (alignPosition == center) { dxOver += correction/2; } } if (alignPosition == center) { dxOver += (mBoundingMetrics.width - overWidth)/2; dxBase = (mBoundingMetrics.width - bmBase.width)/2; } else if (alignPosition == right) { dxOver += mBoundingMetrics.width - overWidth; dxBase = mBoundingMetrics.width - bmBase.width; } mBoundingMetrics.ascent = bmBase.ascent + overDelta1 + bmOver.ascent + bmOver.descent; mBoundingMetrics.descent = bmBase.descent; mBoundingMetrics.leftBearing = std::min(dxBase + bmBase.leftBearing, dxOver + bmOver.leftBearing); mBoundingMetrics.rightBearing = std::max(dxBase + bmBase.rightBearing, dxOver + bmOver.rightBearing); ////////// // pass 2, do what <munder> does: attach the underscript on the previous // result. We conceptually view the previous result as an "anynomous base" // from where to attach the underscript. Hence if the underscript is empty, // we should end up like <mover>. If the overscript is empty, we should // end up like <munder>. nsBoundingMetrics bmAnonymousBase = mBoundingMetrics; nscoord ascentAnonymousBase = std::max(mBoundingMetrics.ascent + overDelta2, overSize.BlockStartAscent() + bmOver.descent + overDelta1 + bmBase.ascent); ascentAnonymousBase = std::max(ascentAnonymousBase, baseSize.BlockStartAscent()); // Width of non-spacing marks is zero so use left and right bearing. nscoord underWidth = bmUnder.width; if (!underWidth) { underWidth = bmUnder.rightBearing - bmUnder.leftBearing; dxUnder = -bmUnder.leftBearing; } nscoord maxWidth = std::max(bmAnonymousBase.width, underWidth); if (alignPosition == center && !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) { GetItalicCorrection(bmAnonymousBase, correction); dxUnder += -correction/2; } nscoord dxAnonymousBase = 0; if (alignPosition == center) { dxUnder += (maxWidth - underWidth)/2; dxAnonymousBase = (maxWidth - bmAnonymousBase.width)/2; } else if (alignPosition == right) { dxUnder += maxWidth - underWidth; dxAnonymousBase = maxWidth - bmAnonymousBase.width; } // adjust the offsets of the real base and overscript since their // final offsets should be relative to us... dxOver += dxAnonymousBase; dxBase += dxAnonymousBase; mBoundingMetrics.width = std::max(dxAnonymousBase + bmAnonymousBase.width, dxUnder + bmUnder.width); // At this point, mBoundingMetrics.ascent = bmAnonymousBase.ascent mBoundingMetrics.descent = bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + bmUnder.descent; mBoundingMetrics.leftBearing = std::min(dxAnonymousBase + bmAnonymousBase.leftBearing, dxUnder + bmUnder.leftBearing); mBoundingMetrics.rightBearing = std::max(dxAnonymousBase + bmAnonymousBase.rightBearing, dxUnder + bmUnder.rightBearing); aDesiredSize.SetBlockStartAscent(ascentAnonymousBase); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + std::max(mBoundingMetrics.descent + underDelta2, bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + underSize.Height() - underSize.BlockStartAscent()); aDesiredSize.Height() = std::max(aDesiredSize.Height(), aDesiredSize.BlockStartAscent() + baseSize.Height() - baseSize.BlockStartAscent()); aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); if (aPlaceOrigin) { nscoord dy; // place overscript if (overFrame) { dy = aDesiredSize.BlockStartAscent() - mBoundingMetrics.ascent + bmOver.ascent - overSize.BlockStartAscent(); FinishReflowChild (overFrame, PresContext(), overSize, nullptr, dxOver, dy, 0); } // place base dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent(); FinishReflowChild (baseFrame, PresContext(), baseSize, nullptr, dxBase, dy, 0); // place underscript if (underFrame) { dy = aDesiredSize.BlockStartAscent() + mBoundingMetrics.descent - bmUnder.descent - underSize.BlockStartAscent(); FinishReflowChild (underFrame, PresContext(), underSize, nullptr, dxUnder, dy, 0); } } return NS_OK; }
NS_IMETHODIMP nsMathMLmsqrtFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { /////////////// // Let the base class format our content like an inferred mrow nsHTMLReflowMetrics baseSize(aDesiredSize); nsresult rv = nsMathMLContainerFrame::Reflow(aPresContext, baseSize, aReflowState, aStatus); //NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); if (NS_FAILED(rv)) return rv; nsBoundingMetrics bmSqr, bmBase; bmBase = baseSize.mBoundingMetrics; //////////// // Prepare the radical symbol and the overline bar nsIRenderingContext& renderingContext = *aReflowState.rendContext; renderingContext.SetFont(GetStyleFont()->mFont, nsnull); nsCOMPtr<nsIFontMetrics> fm; renderingContext.GetFontMetrics(*getter_AddRefs(fm)); nscoord ruleThickness, leading, em; GetRuleThickness(renderingContext, fm, ruleThickness); nsBoundingMetrics bmOne; renderingContext.GetBoundingMetrics(NS_LITERAL_STRING("1").get(), 1, bmOne); // get the leading to be left at the top of the resulting frame // this seems more reliable than using fm->GetLeading() on suspicious fonts GetEmHeight(fm, em); leading = nscoord(0.2f * em); // Rule 11, App. G, TeXbook // psi = clearance between rule and content nscoord phi = 0, psi = 0; if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) fm->GetXHeight(phi); else phi = ruleThickness; psi = ruleThickness + phi/4; // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131) if (bmOne.ascent > bmBase.ascent) psi += bmOne.ascent - bmBase.ascent; // Stretch the radical symbol to the appropriate height if it is not big enough. nsBoundingMetrics contSize = bmBase; contSize.ascent = ruleThickness; contSize.descent = bmBase.ascent + bmBase.descent + psi; // height(radical) should be >= height(base) + psi + ruleThickness nsBoundingMetrics radicalSize; mSqrChar.Stretch(aPresContext, renderingContext, NS_STRETCH_DIRECTION_VERTICAL, contSize, radicalSize, NS_STRETCH_LARGER); // radicalSize have changed at this point, and should match with // the bounding metrics of the char mSqrChar.GetBoundingMetrics(bmSqr); // According to TeX, the ascent of the returned radical should be // the thickness of the overline ruleThickness = bmSqr.ascent; // make sure that the rule appears on the screen nscoord onePixel = aPresContext->IntScaledPixelsToTwips(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 nscoord dx = 0, dy = 0; // place the radical symbol and the radical bar dy = leading; // leave a leading at the top mSqrChar.SetRect(nsRect(dx, dy, bmSqr.width, bmSqr.ascent + bmSqr.descent)); dx = bmSqr.width; mBarRect.SetRect(dx, dy, bmBase.width, ruleThickness); // Update the desired size for the container. // the baseline will be that of the base. mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness; mBoundingMetrics.descent = PR_MAX(bmBase.descent, (bmSqr.descent - (bmBase.ascent + psi))); mBoundingMetrics.width = bmSqr.width + bmBase.width; mBoundingMetrics.leftBearing = bmSqr.leftBearing; mBoundingMetrics.rightBearing = bmSqr.width + PR_MAX(bmBase.width, bmBase.rightBearing); // take also care of the rule aDesiredSize.ascent = mBoundingMetrics.ascent + leading; aDesiredSize.descent = PR_MAX(baseSize.descent, (mBoundingMetrics.descent + ruleThickness)); aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent; aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.ascent; ////////////////// // Adjust the origins to leave room for the sqrt char and the overline bar dx = radicalSize.width; dy = aDesiredSize.ascent - baseSize.ascent; nsIFrame* childFrame = mFrames.FirstChild(); while (childFrame) { childFrame->SetPosition(childFrame->GetPosition() + nsPoint(dx, dy)); childFrame = childFrame->GetNextSibling(); } if (aDesiredSize.mComputeMEW) { aDesiredSize.mMaxElementWidth = aDesiredSize.width; } aDesiredSize.mBoundingMetrics = mBoundingMetrics; aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return NS_OK; }
// exported routine that both munderover and msubsup share. // munderover uses this when movablelimits is set. nsresult nsMathMLmsubsupFrame::PlaceSubSupScript(nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, PRBool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize, nsMathMLContainerFrame* aFrame, nscoord aUserSubScriptShift, nscoord aUserSupScriptShift, nscoord aScriptSpace) { // force the scriptSpace to be atleast 1 pixel nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); aScriptSpace = NS_MAX(onePixel, aScriptSpace); //////////////////////////////////// // Get the children's desired sizes nsHTMLReflowMetrics baseSize; nsHTMLReflowMetrics subScriptSize; nsHTMLReflowMetrics supScriptSize; nsBoundingMetrics bmBase, bmSubScript, bmSupScript; nsIFrame* subScriptFrame = nsnull; nsIFrame* supScriptFrame = nsnull; nsIFrame* baseFrame = aFrame->GetFirstPrincipalChild(); if (baseFrame) subScriptFrame = baseFrame->GetNextSibling(); if (subScriptFrame) supScriptFrame = subScriptFrame->GetNextSibling(); if (!baseFrame || !subScriptFrame || !supScriptFrame || supScriptFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order return aFrame->ReflowError(aRenderingContext, aDesiredSize); } GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript); GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript); // get the subdrop from the subscript font nscoord subDrop; GetSubDropFromChild(subScriptFrame, subDrop); // parameter v, Rule 18a, App. G, TeXbook nscoord minSubScriptShift = bmBase.descent + subDrop; // get the supdrop from the supscript font nscoord supDrop; GetSupDropFromChild(supScriptFrame, supDrop); // parameter u, Rule 18a, App. G, TeXbook nscoord minSupScriptShift = bmBase.ascent - supDrop; ////////////////// // Place Children ////////////////// ////////////////////////////////////////////////// // Get subscript shift // slightly different from nsMathMLmsubFrame.cpp ////////////////////////////////////////////////// // subScriptShift{1,2} // = minimum amount to shift the subscript down // = sub{1,2} in TeXbook // subScriptShift1 = subscriptshift attribute * x-height nscoord subScriptShift1, subScriptShift2; nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(baseFrame, getter_AddRefs(fm)); aRenderingContext.SetFont(fm); // get x-height (an ex) nscoord xHeight = fm->XHeight(); nscoord ruleSize; GetRuleThickness (aRenderingContext, fm, ruleSize); // Get subScriptShift{1,2} default from font GetSubScriptShifts (fm, subScriptShift1, subScriptShift2); if (0 < aUserSubScriptShift) { // the user has set the subscriptshift attribute float scaler = ((float) subScriptShift2) / subScriptShift1; subScriptShift1 = NS_MAX(subScriptShift1, aUserSubScriptShift); subScriptShift2 = NSToCoordRound(scaler * subScriptShift1); } // get a tentative value for subscriptshift // Rule 18d, App. G, TeXbook nscoord subScriptShift = NS_MAX(minSubScriptShift,NS_MAX(subScriptShift1,subScriptShift2)); ////////////////////////////////////////////////// // Get supscript shift // same code from nsMathMLmsupFrame.cpp ////////////////////////////////////////////////// // get min supscript shift limit from x-height // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook nscoord minShiftFromXHeight = (nscoord) (bmSupScript.descent + (1.0f/4.0f) * xHeight); // supScriptShift{1,2,3} // = minimum amount to shift the supscript up // = sup{1,2,3} in TeX // supScriptShift1 = superscriptshift attribute * x-height // Note that there are THREE values for supscript shifts depending // on the current style nscoord supScriptShift1, supScriptShift2, supScriptShift3; // Set supScriptShift{1,2,3} default from font GetSupScriptShifts (fm, supScriptShift1, supScriptShift2, supScriptShift3); if (0 < aUserSupScriptShift) { // the user has set the superscriptshift attribute float scaler2 = ((float) supScriptShift2) / supScriptShift1; float scaler3 = ((float) supScriptShift3) / supScriptShift1; supScriptShift1 = NS_MAX(supScriptShift1, aUserSupScriptShift); supScriptShift2 = NSToCoordRound(scaler2 * supScriptShift1); supScriptShift3 = NSToCoordRound(scaler3 * supScriptShift1); } // get sup script shift depending on current script level and display style // Rule 18c, App. G, TeXbook nscoord supScriptShift; nsPresentationData presentationData; aFrame->GetPresentationData(presentationData); if ( aFrame->GetStyleFont()->mScriptLevel == 0 && NS_MATHML_IS_DISPLAYSTYLE(presentationData.flags) && !NS_MATHML_IS_COMPRESSED(presentationData.flags)) { // Style D in TeXbook supScriptShift = supScriptShift1; } else if (NS_MATHML_IS_COMPRESSED(presentationData.flags)) { // Style C' in TeXbook = D',T',S',SS' supScriptShift = supScriptShift3; } else { // everything else = T,S,SS supScriptShift = supScriptShift2; } // get tentative value for superscriptshift // Rule 18c, App. G, TeXbook supScriptShift = NS_MAX(minSupScriptShift,NS_MAX(supScriptShift,minShiftFromXHeight)); ////////////////////////////////////////////////// // Negotiate between supScriptShift and subScriptShift // so that there will be enough gap between them // Rule 18e, App. G, TeXbook ////////////////////////////////////////////////// nscoord gap = (supScriptShift - bmSupScript.descent) - (bmSubScript.ascent - subScriptShift); if (gap < 4.0f * ruleSize) { // adjust subScriptShift to get a gap of (4.0 * ruleSize) subScriptShift += NSToCoordRound ((4.0f * ruleSize) - gap); } // next we want to ensure that the bottom of the superscript // will be > (4/5) * x-height above baseline gap = NSToCoordRound ((4.0f/5.0f) * xHeight - (supScriptShift - bmSupScript.descent)); if (gap > 0) { supScriptShift += gap; subScriptShift -= gap; } ////////////////////////////////////////////////// // Do the Placing ////////////////////////////////////////////////// // get bounding box for base + subscript + superscript nsBoundingMetrics boundingMetrics; boundingMetrics.ascent = NS_MAX(bmBase.ascent, (bmSupScript.ascent + supScriptShift)); boundingMetrics.descent = NS_MAX(bmBase.descent, (bmSubScript.descent + subScriptShift)); // leave aScriptSpace after both super/subscript // add italicCorrection between base and superscript // add "a little to spare" as well (see TeXbook Ch.11, p.64), as we // estimate the italic creation ourselves and it isn't the same as TeX nscoord italicCorrection; GetItalicCorrection(bmBase, italicCorrection); italicCorrection += onePixel; boundingMetrics.width = bmBase.width + aScriptSpace + NS_MAX((italicCorrection + bmSupScript.width), bmSubScript.width); boundingMetrics.leftBearing = bmBase.leftBearing; boundingMetrics.rightBearing = bmBase.width + NS_MAX((italicCorrection + bmSupScript.rightBearing), bmSubScript.rightBearing); aFrame->SetBoundingMetrics(boundingMetrics); // reflow metrics aDesiredSize.ascent = NS_MAX(baseSize.ascent, NS_MAX(subScriptSize.ascent - subScriptShift, supScriptSize.ascent + supScriptShift)); aDesiredSize.height = aDesiredSize.ascent + NS_MAX(baseSize.height - baseSize.ascent, NS_MAX(subScriptSize.height - subScriptSize.ascent + subScriptShift, supScriptSize.height - subScriptSize.ascent - supScriptShift)); aDesiredSize.width = boundingMetrics.width; aDesiredSize.mBoundingMetrics = boundingMetrics; aFrame->SetReference(nsPoint(0, aDesiredSize.ascent)); if (aPlaceOrigin) { nscoord dx, dy; // now place the base ... dx = 0; dy = aDesiredSize.ascent - baseSize.ascent; FinishReflowChild(baseFrame, aPresContext, nsnull, baseSize, dx, dy, 0); // ... and subscript dx = bmBase.width; dy = aDesiredSize.ascent - (subScriptSize.ascent - subScriptShift); FinishReflowChild(subScriptFrame, aPresContext, nsnull, subScriptSize, dx, dy, 0); // ... and the superscript dx = bmBase.width + italicCorrection; dy = aDesiredSize.ascent - (supScriptSize.ascent + supScriptShift); FinishReflowChild(supScriptFrame, aPresContext, nsnull, supScriptSize, dx, dy, 0); } return NS_OK; }
NS_IMETHODIMP nsMathMLmunderoverFrame::Place(nsIRenderingContext& aRenderingContext, PRBool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) && !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) { // place like sub-superscript pair return nsMathMLmsubsupFrame::PlaceSubSupScript(GetPresContext(), aRenderingContext, aPlaceOrigin, aDesiredSize, this); } //////////////////////////////////// // Get the children's desired sizes nsBoundingMetrics bmBase, bmUnder, bmOver; nsHTMLReflowMetrics baseSize (nsnull); nsHTMLReflowMetrics underSize (nsnull); nsHTMLReflowMetrics overSize (nsnull); nsIFrame* overFrame = nsnull; nsIFrame* underFrame = nsnull; nsIFrame* baseFrame = mFrames.FirstChild(); if (baseFrame) underFrame = baseFrame->GetNextSibling(); if (underFrame) overFrame = underFrame->GetNextSibling(); if (!baseFrame || !underFrame || !overFrame || overFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order NS_WARNING("invalid markup"); return ReflowError(aRenderingContext, aDesiredSize); } GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder); GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver); nscoord onePixel = GetPresContext()->IntScaledPixelsToTwips(1); //////////////////// // Place Children aRenderingContext.SetFont(GetStyleFont()->mFont, nsnull); nsCOMPtr<nsIFontMetrics> fm; aRenderingContext.GetFontMetrics(*getter_AddRefs(fm)); nscoord xHeight = 0; fm->GetXHeight (xHeight); nscoord ruleThickness; GetRuleThickness (aRenderingContext, fm, ruleThickness); nscoord correction = 0; GetItalicCorrection (bmBase, correction); // there are 2 different types of placement depending on // whether we want an accented under or not nscoord underDelta1 = 0; // gap between base and underscript nscoord underDelta2 = 0; // extra space beneath underscript if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) { // Rule 13a, App. G, TeXbook nscoord bigOpSpacing2, bigOpSpacing4, bigOpSpacing5, dummy; GetBigOpSpacings (fm, dummy, bigOpSpacing2, dummy, bigOpSpacing4, bigOpSpacing5); underDelta1 = PR_MAX(bigOpSpacing2, (bigOpSpacing4 - bmUnder.ascent)); underDelta2 = bigOpSpacing5; } else { // No corresponding rule in TeXbook - we are on our own here // XXX tune the gap delta between base and underscript // Should we use Rule 10 like \underline does? underDelta1 = ruleThickness + onePixel/2; underDelta2 = ruleThickness; } // empty under? if (!(bmUnder.ascent + bmUnder.descent)) underDelta1 = 0; nscoord overDelta1 = 0; // gap between base and overscript nscoord overDelta2 = 0; // extra space above overscript if (!NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) { // Rule 13a, App. G, TeXbook nscoord bigOpSpacing1, bigOpSpacing3, bigOpSpacing5, dummy; GetBigOpSpacings (fm, bigOpSpacing1, dummy, bigOpSpacing3, dummy, bigOpSpacing5); overDelta1 = PR_MAX(bigOpSpacing1, (bigOpSpacing3 - bmOver.descent)); overDelta2 = bigOpSpacing5; // XXX This is not a TeX rule... // delta1 (as computed abvove) can become really big when bmOver.descent is // negative, e.g., if the content is &OverBar. In such case, we use the height if (bmOver.descent < 0) overDelta1 = PR_MAX(bigOpSpacing1, (bigOpSpacing3 - (bmOver.ascent + bmOver.descent))); } else { // Rule 12, App. G, TeXbook overDelta1 = ruleThickness + onePixel/2; if (bmBase.ascent < xHeight) { overDelta1 += xHeight - bmBase.ascent; } overDelta2 = ruleThickness; } // empty over? if (!(bmOver.ascent + bmOver.descent)) overDelta1 = 0; nscoord dxBase, dxOver = 0, dxUnder = 0; ////////// // pass 1, do what <mover> does: attach the overscript on the base // Ad-hoc - This is to override fonts which have ready-made _accent_ // glyphs with negative lbearing and rbearing. We want to position // the overscript ourselves nscoord overWidth = bmOver.width; if (!overWidth && (bmOver.rightBearing - bmOver.leftBearing > 0)) { overWidth = bmOver.rightBearing - bmOver.leftBearing; dxOver = -bmOver.leftBearing; } if (NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) { mBoundingMetrics.width = bmBase.width; dxOver += correction + (mBoundingMetrics.width - overWidth)/2; } else { mBoundingMetrics.width = PR_MAX(bmBase.width, overWidth); dxOver += correction/2 + (mBoundingMetrics.width - overWidth)/2; } dxBase = (mBoundingMetrics.width - bmBase.width)/2; mBoundingMetrics.ascent = bmBase.ascent + overDelta1 + bmOver.ascent + bmOver.descent; mBoundingMetrics.descent = bmBase.descent + underDelta1 + bmUnder.ascent + bmUnder.descent; mBoundingMetrics.leftBearing = PR_MIN(dxBase + bmBase.leftBearing, dxOver + bmOver.leftBearing); mBoundingMetrics.rightBearing = PR_MAX(dxBase + bmBase.rightBearing, dxOver + bmOver.rightBearing); ////////// // pass 2, do what <munder> does: attach the underscript on the previous // result. We conceptually view the previous result as an "anynomous base" // from where to attach the underscript. Hence if the underscript is empty, // we should end up like <mover>. If the overscript is empty, we should // end up like <munder>. nsBoundingMetrics bmAnonymousBase = mBoundingMetrics; nscoord ascentAnonymousBase = PR_MAX(mBoundingMetrics.ascent + overDelta2, overSize.ascent + bmOver.descent + overDelta1 + bmBase.ascent); GetItalicCorrection(bmAnonymousBase, correction); nscoord maxWidth = PR_MAX(bmAnonymousBase.width, bmUnder.width); if (NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) { dxUnder = (maxWidth - bmUnder.width)/2;; } else { dxUnder = -correction/2 + (maxWidth - bmUnder.width)/2; } nscoord dxAnonymousBase = (maxWidth - bmAnonymousBase.width)/2; // adjust the offsets of the real base and overscript since their // final offsets should be relative to us... dxOver += dxAnonymousBase; dxBase += dxAnonymousBase; mBoundingMetrics.width = PR_MAX(dxAnonymousBase + bmAnonymousBase.width, dxUnder + bmUnder.width); mBoundingMetrics.leftBearing = PR_MIN(dxAnonymousBase + bmAnonymousBase.leftBearing, dxUnder + bmUnder.leftBearing); mBoundingMetrics.rightBearing = PR_MAX(dxAnonymousBase + bmAnonymousBase.rightBearing, dxUnder + bmUnder.rightBearing); aDesiredSize.ascent = ascentAnonymousBase; aDesiredSize.descent = PR_MAX(mBoundingMetrics.descent + underDelta2, bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + underSize.descent); aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent; aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.ascent; if (aPlaceOrigin) { nscoord dy; // place overscript dy = aDesiredSize.ascent - mBoundingMetrics.ascent + bmOver.ascent - overSize.ascent; FinishReflowChild (overFrame, GetPresContext(), nsnull, overSize, dxOver, dy, 0); // place base dy = aDesiredSize.ascent - baseSize.ascent; FinishReflowChild (baseFrame, GetPresContext(), nsnull, baseSize, dxBase, dy, 0); // place underscript dy = aDesiredSize.ascent + mBoundingMetrics.descent - bmUnder.descent - underSize.ascent; FinishReflowChild (underFrame, GetPresContext(), nsnull, underSize, dxUnder, dy, 0); } return NS_OK; }
/* virtual */ nsresult nsMathMLmoverFrame::Place(nsIRenderingContext& aRenderingContext, PRBool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) && !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) { // place like superscript return nsMathMLmsupFrame::PlaceSuperScript(PresContext(), aRenderingContext, aPlaceOrigin, aDesiredSize, this, 0, nsPresContext::CSSPointsToAppUnits(0.5f)); } //////////////////////////////////// // Get the children's desired sizes nsBoundingMetrics bmBase, bmOver; nsHTMLReflowMetrics baseSize; nsHTMLReflowMetrics overSize; nsIFrame* overFrame = nsnull; nsIFrame* baseFrame = mFrames.FirstChild(); if (baseFrame) overFrame = baseFrame->GetNextSibling(); if (!baseFrame || !overFrame || overFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order return ReflowError(aRenderingContext, aDesiredSize); } GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver); nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); //////////////////// // Place Children aRenderingContext.SetFont(GetStyleFont()->mFont, PresContext()->GetUserFontSet()); nsCOMPtr<nsIFontMetrics> fm; aRenderingContext.GetFontMetrics(*getter_AddRefs(fm)); nscoord xHeight = 0; fm->GetXHeight (xHeight); nscoord ruleThickness; GetRuleThickness (aRenderingContext, fm, ruleThickness); // there are 2 different types of placement depending on // whether we want an accented overscript or not nscoord correction = 0; GetItalicCorrection (bmBase, correction); nscoord delta1 = 0; // gap between base and overscript nscoord delta2 = 0; // extra space above overscript if (!NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) { // Rule 13a, App. G, TeXbook nscoord bigOpSpacing1, bigOpSpacing3, bigOpSpacing5, dummy; GetBigOpSpacings (fm, bigOpSpacing1, dummy, bigOpSpacing3, dummy, bigOpSpacing5); delta1 = NS_MAX(bigOpSpacing1, (bigOpSpacing3 - bmOver.descent)); delta2 = bigOpSpacing5; // XXX This is not a TeX rule... // delta1 (as computed above) can become really big when bmOver.descent is // negative, e.g., if the content is &OverBar. In such case, we use the height if (bmOver.descent < 0) delta1 = NS_MAX(bigOpSpacing1, (bigOpSpacing3 - (bmOver.ascent + bmOver.descent))); } else { // Rule 12, App. G, TeXbook // We are going to modify this rule to make it more general. // The idea behind Rule 12 in the TeXBook is to keep the accent // as close to the base as possible, while ensuring that the // distance between the *baseline* of the accent char and // the *baseline* of the base is atleast x-height. // The idea is that for normal use, we would like all the accents // on a line to line up atleast x-height above the baseline // if possible. // When the ascent of the base is >= x-height, // the baseline of the accent char is placed just above the base // (specifically, the baseline of the accent char is placed // above the baseline of the base by the ascent of the base). // For ease of implementation, // this assumes that the font-designer designs accents // in such a way that the bottom of the accent is atleast x-height // above its baseline, otherwise there will be collisions // with the base. Also there should be proper padding between // the bottom of the accent char and its baseline. // The above rule may not be obvious from a first // reading of rule 12 in the TeXBook !!! // The mathml <mover> tag can use accent chars that // do not follow this convention. So we modify TeX's rule // so that TeX's rule gets subsumed for accents that follow // TeX's convention, // while also allowing accents that do not follow the convention : // we try to keep the *bottom* of the accent char atleast x-height // from the baseline of the base char. we also slap on an extra // padding between the accent and base chars. delta1 = ruleThickness + onePixel/2; // we have at least the padding if (bmBase.ascent < xHeight) { // also ensure at least x-height above the baseline of the base delta1 += xHeight - bmBase.ascent; } delta2 = ruleThickness; } // empty over? if (!(bmOver.ascent + bmOver.descent)) delta1 = 0; nscoord dxBase, dxOver = 0; // Ad-hoc - This is to override fonts which have ready-made _accent_ // glyphs with negative lbearing and rbearing. We want to position // the overscript ourselves nscoord overWidth = bmOver.width; if (!overWidth && (bmOver.rightBearing - bmOver.leftBearing > 0)) { overWidth = bmOver.rightBearing - bmOver.leftBearing; dxOver = -bmOver.leftBearing; } if (NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) { mBoundingMetrics.width = bmBase.width; dxOver += correction + (mBoundingMetrics.width - overWidth)/2; } else { mBoundingMetrics.width = NS_MAX(bmBase.width, overWidth); dxOver += correction/2 + (mBoundingMetrics.width - overWidth)/2; } dxBase = (mBoundingMetrics.width - bmBase.width) / 2; mBoundingMetrics.ascent = bmOver.ascent + bmOver.descent + delta1 + bmBase.ascent; mBoundingMetrics.descent = bmBase.descent; mBoundingMetrics.leftBearing = NS_MIN(dxBase + bmBase.leftBearing, dxOver + bmOver.leftBearing); mBoundingMetrics.rightBearing = NS_MAX(dxBase + bmBase.rightBearing, dxOver + bmOver.rightBearing); aDesiredSize.ascent = NS_MAX(mBoundingMetrics.ascent + delta2, overSize.ascent + bmOver.descent + delta1 + bmBase.ascent); aDesiredSize.height = aDesiredSize.ascent + baseSize.height - baseSize.ascent; aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.ascent; if (aPlaceOrigin) { // place base nscoord dy = aDesiredSize.ascent - baseSize.ascent; FinishReflowChild (baseFrame, PresContext(), nsnull, baseSize, dxBase, dy, 0); // place overscript dy = aDesiredSize.ascent - mBoundingMetrics.ascent + bmOver.ascent - overSize.ascent; FinishReflowChild (overFrame, PresContext(), nsnull, overSize, dxOver, dy, 0); } return NS_OK; }
NS_IMETHODIMP nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { nsresult rv = NS_OK; nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE); nsReflowStatus childStatus; aDesiredSize.width = aDesiredSize.height = 0; aDesiredSize.ascent = 0; nsBoundingMetrics bmSqr, bmBase, bmIndex; nsRenderingContext& renderingContext = *aReflowState.rendContext; ////////////////// // Reflow Children PRInt32 count = 0; nsIFrame* baseFrame = nsnull; nsIFrame* indexFrame = nsnull; nsHTMLReflowMetrics baseSize; nsHTMLReflowMetrics indexSize; nsIFrame* childFrame = mFrames.FirstChild(); while (childFrame) { // ask our children to compute their bounding metrics nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS); nsHTMLReflowState childReflowState(aPresContext, aReflowState, childFrame, availSize); rv = ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowState, childStatus); //NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status"); if (NS_FAILED(rv)) { // Call DidReflow() for the child frames we successfully did reflow. DidReflowChildren(mFrames.FirstChild(), childFrame); return rv; } 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 rv = ReflowError(renderingContext, aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); // Call DidReflow() for the child frames we successfully did reflow. DidReflowChildren(mFrames.FirstChild(), childFrame); return rv; } //////////// // Prepare the radical symbol and the overline bar renderingContext.SetFont(GetStyleFont()->mFont, aPresContext->GetUserFontSet()); nsFontMetrics* fm = renderingContext.FontMetrics(); // For radical glyphs from TeX fonts and some of the radical glyphs from // Mathematica fonts, the thickness of the overline can be obtained from the // ascent of the glyph. Most fonts however have radical glyphs above the // baseline so no assumption can be made about the meaning of the ascent. nscoord ruleThickness, leading, em; GetRuleThickness(renderingContext, fm, ruleThickness); PRUnichar one = '1'; nsBoundingMetrics bmOne = renderingContext.GetBoundingMetrics(&one, 1); // get the leading to be left at the top of the resulting frame // this seems more reliable than using fm->GetLeading() on suspicious fonts GetEmHeight(fm, em); leading = nscoord(0.2f * em); // Rule 11, App. G, TeXbook // psi = clearance between rule and content nscoord phi = 0, psi = 0; if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) phi = fm->XHeight(); else phi = ruleThickness; psi = ruleThickness + phi/4; // built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131) 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, renderingContext, NS_STRETCH_DIRECTION_VERTICAL, contSize, radicalSize, NS_STRETCH_LARGER); // 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 = NS_MAX(bmBase.descent, (bmSqr.ascent + bmSqr.descent - mBoundingMetrics.ascent)); mBoundingMetrics.width = bmSqr.width + bmBase.width; mBoundingMetrics.leftBearing = bmSqr.leftBearing; mBoundingMetrics.rightBearing = bmSqr.width + NS_MAX(bmBase.width, bmBase.rightBearing); // take also care of the rule aDesiredSize.ascent = mBoundingMetrics.ascent + leading; aDesiredSize.height = aDesiredSize.ascent + NS_MAX(baseSize.height - baseSize.ascent, 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 nscoord raiseIndexDelta = NSToCoordRound(0.6f * (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.ascent; aDesiredSize.ascent = mBoundingMetrics.ascent + leading; aDesiredSize.height = aDesiredSize.ascent + descent; } nscoord dxIndex, dxSqr; GetRadicalXOffsets(bmIndex.width, bmSqr.width, fm, &dxIndex, &dxSqr); // place the index nscoord dx = dxIndex; nscoord dy = aDesiredSize.ascent - (indexRaisedAscent + indexSize.ascent - bmIndex.ascent); FinishReflowChild(indexFrame, aPresContext, nsnull, indexSize, 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(dx, dy, bmSqr.width, bmSqr.ascent + bmSqr.descent)); dx += bmSqr.width; mBarRect.SetRect(dx, dy, bmBase.width, ruleThickness); // place the base dy = aDesiredSize.ascent - baseSize.ascent; FinishReflowChild(baseFrame, aPresContext, nsnull, baseSize, dx, dy, 0); mReference.x = 0; mReference.y = aDesiredSize.ascent; mBoundingMetrics.width = dx + bmBase.width; mBoundingMetrics.leftBearing = NS_MIN(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing); mBoundingMetrics.rightBearing = dx + NS_MAX(bmBase.width, bmBase.rightBearing); aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; GatherAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return NS_OK; }
/* virtual */ nsresult nsMathMLmmultiscriptsFrame::Place(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { //////////////////////////////////// // Get the children's desired sizes nscoord minShiftFromXHeight, subDrop, supDrop; //////////////////////////////////////// // Initialize super/sub shifts that // depend only on the current font //////////////////////////////////////// ProcessAttributes(); // get x-height (an ex) const nsStyleFont* font = GetStyleFont(); nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); aRenderingContext.SetFont(fm); nscoord xHeight = fm->XHeight(); nscoord ruleSize; GetRuleThickness (aRenderingContext, fm, ruleSize); // scriptspace from TeX for extra spacing after sup/subscript (0.5pt in plain TeX) // forced to be at least 1 pixel here nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); nscoord scriptSpace = NS_MAX(nsPresContext::CSSPointsToAppUnits(0.5f), onePixel); ///////////////////////////////////// // first the shift for the subscript // subScriptShift{1,2} // = minimum amount to shift the subscript down // = sub{1,2} in TeXbook // subScriptShift1 = subscriptshift attribute * x-height nscoord subScriptShift1, subScriptShift2; // Get subScriptShift{1,2} default from font GetSubScriptShifts (fm, subScriptShift1, subScriptShift2); if (0 < mSubScriptShift) { // the user has set the subscriptshift attribute float scaler = ((float) subScriptShift2) / subScriptShift1; subScriptShift1 = NS_MAX(subScriptShift1, mSubScriptShift); subScriptShift2 = NSToCoordRound(scaler * subScriptShift1); } // the font dependent shift nscoord subScriptShift = NS_MAX(subScriptShift1,subScriptShift2); ///////////////////////////////////// // next the shift for the superscript // supScriptShift{1,2,3} // = minimum amount to shift the supscript up // = sup{1,2,3} in TeX // supScriptShift1 = superscriptshift attribute * x-height // Note that there are THREE values for supscript shifts depending // on the current style nscoord supScriptShift1, supScriptShift2, supScriptShift3; // Set supScriptShift{1,2,3} default from font GetSupScriptShifts (fm, supScriptShift1, supScriptShift2, supScriptShift3); if (0 < mSupScriptShift) { // the user has set the superscriptshift attribute float scaler2 = ((float) supScriptShift2) / supScriptShift1; float scaler3 = ((float) supScriptShift3) / supScriptShift1; supScriptShift1 = NS_MAX(supScriptShift1, mSupScriptShift); supScriptShift2 = NSToCoordRound(scaler2 * supScriptShift1); supScriptShift3 = NSToCoordRound(scaler3 * supScriptShift1); } // get sup script shift depending on current script level and display style // Rule 18c, App. G, TeXbook nscoord supScriptShift; if ( font->mScriptLevel == 0 && NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags) && !NS_MATHML_IS_COMPRESSED(mPresentationData.flags)) { // Style D in TeXbook supScriptShift = supScriptShift1; } else if (NS_MATHML_IS_COMPRESSED(mPresentationData.flags)) { // Style C' in TeXbook = D',T',S',SS' supScriptShift = supScriptShift3; } else { // everything else = T,S,SS supScriptShift = supScriptShift2; } //////////////////////////////////// // Get the children's sizes //////////////////////////////////// nscoord width = 0, prescriptsWidth = 0, rightBearing = 0; nsIFrame* mprescriptsFrame = nsnull; // frame of <mprescripts/>, if there. bool isSubScript = false; nscoord minSubScriptShift = 0, minSupScriptShift = 0; nscoord trySubScriptShift = subScriptShift; nscoord trySupScriptShift = supScriptShift; nscoord maxSubScriptShift = subScriptShift; nscoord maxSupScriptShift = supScriptShift; PRInt32 count = 0; nsHTMLReflowMetrics baseSize; nsHTMLReflowMetrics subScriptSize; nsHTMLReflowMetrics supScriptSize; nsIFrame* baseFrame = nsnull; nsIFrame* subScriptFrame = nsnull; nsIFrame* supScriptFrame = nsnull; bool firstPrescriptsPair = false; nsBoundingMetrics bmBase, bmSubScript, bmSupScript; nscoord italicCorrection = 0; mBoundingMetrics.width = 0; mBoundingMetrics.ascent = mBoundingMetrics.descent = -0x7FFFFFFF; nscoord ascent = -0x7FFFFFFF, descent = -0x7FFFFFFF; aDesiredSize.width = aDesiredSize.height = 0; nsIFrame* childFrame = mFrames.FirstChild(); while (childFrame) { if (childFrame->GetContent()->Tag() == nsGkAtoms::mprescripts_) { if (mprescriptsFrame) { // duplicate <mprescripts/> found // report an error, encourage people to get their markups in order return ReflowError(aRenderingContext, aDesiredSize); } mprescriptsFrame = childFrame; firstPrescriptsPair = true; } else { if (0 == count) { // base baseFrame = childFrame; GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); GetItalicCorrection(bmBase, italicCorrection); // for the superscript, we always add "a little to spare" italicCorrection += onePixel; // we update mBoundingMetrics.{ascent,descent} with that // of the baseFrame only after processing all the sup/sub pairs // XXX need italic correction only *if* there are postscripts ? mBoundingMetrics.width = bmBase.width + italicCorrection; mBoundingMetrics.rightBearing = bmBase.rightBearing; mBoundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten } else { // super/subscript block if (isSubScript) { // subscript subScriptFrame = childFrame; GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript); // get the subdrop from the subscript font GetSubDropFromChild (subScriptFrame, subDrop); // parameter v, Rule 18a, App. G, TeXbook minSubScriptShift = bmBase.descent + subDrop; trySubScriptShift = NS_MAX(minSubScriptShift,subScriptShift); mBoundingMetrics.descent = NS_MAX(mBoundingMetrics.descent,bmSubScript.descent); descent = NS_MAX(descent,subScriptSize.height - subScriptSize.ascent); width = bmSubScript.width + scriptSpace; rightBearing = bmSubScript.rightBearing; } else { // supscript supScriptFrame = childFrame; GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript); // get the supdrop from the supscript font GetSupDropFromChild (supScriptFrame, supDrop); // parameter u, Rule 18a, App. G, TeXbook minSupScriptShift = bmBase.ascent - supDrop; // get min supscript shift limit from x-height // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook minShiftFromXHeight = NSToCoordRound ((bmSupScript.descent + (1.0f/4.0f) * xHeight)); trySupScriptShift = NS_MAX(minSupScriptShift,NS_MAX(minShiftFromXHeight,supScriptShift)); mBoundingMetrics.ascent = NS_MAX(mBoundingMetrics.ascent,bmSupScript.ascent); ascent = NS_MAX(ascent,supScriptSize.ascent); width = NS_MAX(width, bmSupScript.width + scriptSpace); rightBearing = NS_MAX(rightBearing, bmSupScript.rightBearing); if (!mprescriptsFrame) { // we are still looping over base & postscripts mBoundingMetrics.rightBearing = mBoundingMetrics.width + rightBearing; mBoundingMetrics.width += width; } else { prescriptsWidth += width; if (firstPrescriptsPair) { firstPrescriptsPair = false; mBoundingMetrics.leftBearing = NS_MIN(bmSubScript.leftBearing, bmSupScript.leftBearing); } } width = rightBearing = 0; // negotiate between the various shifts so that // there is enough gap between the sup and subscripts // Rule 18e, App. G, TeXbook nscoord gap = (trySupScriptShift - bmSupScript.descent) - (bmSubScript.ascent - trySubScriptShift); if (gap < 4.0f * ruleSize) { // adjust trySubScriptShift to get a gap of (4.0 * ruleSize) trySubScriptShift += NSToCoordRound ((4.0f * ruleSize) - gap); } // next we want to ensure that the bottom of the superscript // will be > (4/5) * x-height above baseline gap = NSToCoordRound ((4.0f/5.0f) * xHeight - (trySupScriptShift - bmSupScript.descent)); if (gap > 0.0f) { trySupScriptShift += gap; trySubScriptShift -= gap; } maxSubScriptShift = NS_MAX(maxSubScriptShift, trySubScriptShift); maxSupScriptShift = NS_MAX(maxSupScriptShift, trySupScriptShift); trySubScriptShift = subScriptShift; trySupScriptShift = supScriptShift; } } isSubScript = !isSubScript; } count++; childFrame = childFrame->GetNextSibling(); } // note: width=0 if all sup-sub pairs match correctly if ((0 != width) || !baseFrame || !subScriptFrame || !supScriptFrame) { // report an error, encourage people to get their markups in order return ReflowError(aRenderingContext, aDesiredSize); } // we left out the width of prescripts, so ... mBoundingMetrics.rightBearing += prescriptsWidth; mBoundingMetrics.width += prescriptsWidth; // we left out the base during our bounding box updates, so ... mBoundingMetrics.ascent = NS_MAX(mBoundingMetrics.ascent+maxSupScriptShift,bmBase.ascent); mBoundingMetrics.descent = NS_MAX(mBoundingMetrics.descent+maxSubScriptShift,bmBase.descent); // get the reflow metrics ... aDesiredSize.ascent = NS_MAX(ascent+maxSupScriptShift,baseSize.ascent); aDesiredSize.height = aDesiredSize.ascent + NS_MAX(descent+maxSubScriptShift,baseSize.height - baseSize.ascent); aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.ascent; ////////////////// // Place Children // Place prescripts, followed by base, and then postscripts. // The list of frames is in the order: {base} {postscripts} {prescripts} // We go over the list in a circular manner, starting at <prescripts/> if (aPlaceOrigin) { nscoord dx = 0, dy = 0; count = 0; childFrame = mprescriptsFrame; do { if (!childFrame) { // end of prescripts, // place the base ... childFrame = baseFrame; dy = aDesiredSize.ascent - baseSize.ascent; FinishReflowChild (baseFrame, PresContext(), nsnull, baseSize, MirrorIfRTL(aDesiredSize.width, baseSize.width, dx), dy, 0); dx += bmBase.width + italicCorrection; } else if (mprescriptsFrame != childFrame) { // process each sup/sub pair if (0 == count) { subScriptFrame = childFrame; count = 1; } else if (1 == count) { supScriptFrame = childFrame; count = 0; // get the ascent/descent of sup/subscripts stored in their rects // rect.x = descent, rect.y = ascent GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript); GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript); // center w.r.t. largest width width = NS_MAX(subScriptSize.width, supScriptSize.width); dy = aDesiredSize.ascent - subScriptSize.ascent + maxSubScriptShift; FinishReflowChild (subScriptFrame, PresContext(), nsnull, subScriptSize, MirrorIfRTL(aDesiredSize.width, subScriptSize.width, dx + (width-subScriptSize.width)/2), dy, 0); dy = aDesiredSize.ascent - supScriptSize.ascent - maxSupScriptShift; FinishReflowChild (supScriptFrame, PresContext(), nsnull, supScriptSize, MirrorIfRTL(aDesiredSize.width, supScriptSize.width, dx + (width-supScriptSize.width)/2), dy, 0); dx += width + scriptSpace; } } childFrame = childFrame->GetNextSibling(); } while (mprescriptsFrame != childFrame); } return NS_OK; }
nsresult nsMathMLmfracFrame::PlaceInternal(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize, bool aWidthOnly) { //////////////////////////////////// // Get the children's desired sizes nsBoundingMetrics bmNum, bmDen; nsHTMLReflowMetrics sizeNum; nsHTMLReflowMetrics sizeDen; nsIFrame* frameDen = nsnull; nsIFrame* frameNum = mFrames.FirstChild(); if (frameNum) frameDen = frameNum->GetNextSibling(); if (!frameNum || !frameDen || frameDen->GetNextSibling()) { // report an error, encourage people to get their markups in order return ReflowError(aRenderingContext, aDesiredSize); } GetReflowAndBoundingMetricsFor(frameNum, sizeNum, bmNum); GetReflowAndBoundingMetricsFor(frameDen, sizeDen, bmDen); nsPresContext* presContext = PresContext(); nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); aRenderingContext.SetFont(fm); nscoord defaultRuleThickness, axisHeight; GetRuleThickness(aRenderingContext, fm, defaultRuleThickness); GetAxisHeight(aRenderingContext, fm, axisHeight); nsEmbellishData coreData; GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData); // see if the linethickness attribute is there nsAutoString value; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::linethickness_, value); mLineThickness = CalcLineThickness(presContext, mStyleContext, value, onePixel, defaultRuleThickness); // bevelled attribute GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::bevelled_, value); mIsBevelled = value.EqualsLiteral("true"); if (!mIsBevelled) { mLineRect.height = mLineThickness; // by default, leave at least one-pixel padding at either end, or use // lspace & rspace that may come from <mo> if we are an embellished // container (we fetch values from the core since they may use units that // depend on style data, and style changes could have occurred in the // core since our last visit there) nscoord leftSpace = NS_MAX(onePixel, coreData.leftSpace); nscoord rightSpace = NS_MAX(onePixel, coreData.rightSpace); ////////////////// // Get shifts nscoord numShift = 0; nscoord denShift = 0; // Rule 15b, App. G, TeXbook nscoord numShift1, numShift2, numShift3; nscoord denShift1, denShift2; GetNumeratorShifts(fm, numShift1, numShift2, numShift3); GetDenominatorShifts(fm, denShift1, denShift2); if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) { // C > T numShift = numShift1; denShift = denShift1; } else { numShift = (0 < mLineRect.height) ? numShift2 : numShift3; denShift = denShift2; } nscoord minClearance = 0; nscoord actualClearance = 0; nscoord actualRuleThickness = mLineThickness; if (0 == actualRuleThickness) { // Rule 15c, App. G, TeXbook // min clearance between numerator and denominator minClearance = (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) ? 7 * defaultRuleThickness : 3 * defaultRuleThickness; actualClearance = (numShift - bmNum.descent) - (bmDen.ascent - denShift); // actualClearance should be >= minClearance if (actualClearance < minClearance) { nscoord halfGap = (minClearance - actualClearance)/2; numShift += halfGap; denShift += halfGap; } } else { // Rule 15d, App. G, TeXbook // min clearance between numerator or denominator and middle of bar // TeX has a different interpretation of the thickness. // Try $a \above10pt b$ to see. Here is what TeX does: // minClearance = (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) ? // 3 * actualRuleThickness : actualRuleThickness; // we slightly depart from TeX here. We use the defaultRuleThickness instead // of the value coming from the linethickness attribute, i.e., we recover what // TeX does if the user hasn't set linethickness. But when the linethickness // is set, we avoid the wide gap problem. minClearance = (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) ? 3 * defaultRuleThickness : defaultRuleThickness + onePixel; // adjust numShift to maintain minClearance if needed actualClearance = (numShift - bmNum.descent) - (axisHeight + actualRuleThickness/2); if (actualClearance < minClearance) { numShift += (minClearance - actualClearance); } // adjust denShift to maintain minClearance if needed actualClearance = (axisHeight - actualRuleThickness/2) - (bmDen.ascent - denShift); if (actualClearance < minClearance) { denShift += (minClearance - actualClearance); } } ////////////////// // Place Children // XXX Need revisiting the width. TeX uses the exact width // e.g. in $$\huge\frac{\displaystyle\int}{i}$$ nscoord width = NS_MAX(bmNum.width, bmDen.width); nscoord dxNum = leftSpace + (width - sizeNum.width)/2; nscoord dxDen = leftSpace + (width - sizeDen.width)/2; width += leftSpace + rightSpace; // see if the numalign attribute is there GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::numalign_, value); if (value.EqualsLiteral("left")) dxNum = leftSpace; else if (value.EqualsLiteral("right")) dxNum = width - rightSpace - sizeNum.width; // see if the denomalign attribute is there GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::denomalign_, value); if (value.EqualsLiteral("left")) dxDen = leftSpace; else if (value.EqualsLiteral("right")) dxDen = width - rightSpace - sizeDen.width; mBoundingMetrics.rightBearing = NS_MAX(dxNum + bmNum.rightBearing, dxDen + bmDen.rightBearing); if (mBoundingMetrics.rightBearing < width - rightSpace) mBoundingMetrics.rightBearing = width - rightSpace; mBoundingMetrics.leftBearing = NS_MIN(dxNum + bmNum.leftBearing, dxDen + bmDen.leftBearing); if (mBoundingMetrics.leftBearing > leftSpace) mBoundingMetrics.leftBearing = leftSpace; mBoundingMetrics.ascent = bmNum.ascent + numShift; mBoundingMetrics.descent = bmDen.descent + denShift; mBoundingMetrics.width = width; aDesiredSize.ascent = sizeNum.ascent + numShift; aDesiredSize.height = aDesiredSize.ascent + sizeDen.height - sizeDen.ascent + denShift; aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.ascent; if (aPlaceOrigin) { nscoord dy; // place numerator dy = 0; FinishReflowChild(frameNum, presContext, nsnull, sizeNum, dxNum, dy, 0); // place denominator dy = aDesiredSize.height - sizeDen.height; FinishReflowChild(frameDen, presContext, nsnull, sizeDen, dxDen, dy, 0); // place the fraction bar - dy is top of bar dy = aDesiredSize.ascent - (axisHeight + actualRuleThickness/2); mLineRect.SetRect(leftSpace, dy, width - (leftSpace + rightSpace), actualRuleThickness); } } else { nscoord numShift = 0.0; nscoord denShift = 0.0; nscoord padding = 3 * defaultRuleThickness; nscoord slashRatio = 3; // Define the constant used in the expression of the maximum width nscoord em = fm->EmHeight(); nscoord slashMaxWidthConstant = 2 * em; // For large line thicknesses the minimum slash height is limited to the // largest expected height of a fraction nscoord slashMinHeight = slashRatio * NS_MIN(2 * mLineThickness, slashMaxWidthConstant); nscoord leftSpace = NS_MAX(padding, coreData.leftSpace); nscoord rightSpace = NS_MAX(padding, coreData.rightSpace); nscoord delta; // ___________ // | | / // {|-NUMERATOR-| / // {|___________| S // { L // numShift{ A // ------------------------------------------------------- baseline // S _____________ } denShift // H | |} // / |-DENOMINATOR-|} // / |_____________| // // first, ensure that the top of the numerator is at least as high as the // top of the denominator (and the reverse for the bottoms) delta = NS_MAX(bmDen.ascent - bmNum.ascent, bmNum.descent - bmDen.descent) / 2; if (delta > 0) { numShift += delta; denShift += delta; } if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) { delta = NS_MIN(bmDen.ascent + bmDen.descent, bmNum.ascent + bmNum.descent) / 2; numShift += delta; denShift += delta; } else { nscoord xHeight = fm->XHeight(); numShift += xHeight / 2; denShift += xHeight / 4; } // Set the ascent/descent of our BoundingMetrics. mBoundingMetrics.ascent = bmNum.ascent + numShift; mBoundingMetrics.descent = bmDen.descent + denShift; // At this point the height of the slash is // mBoundingMetrics.ascent + mBoundingMetrics.descent // Ensure that it is greater than slashMinHeight delta = (slashMinHeight - (mBoundingMetrics.ascent + mBoundingMetrics.descent)) / 2; if (delta > 0) { mBoundingMetrics.ascent += delta; mBoundingMetrics.descent += delta; } // Set the width of the slash if (aWidthOnly) { mLineRect.width = mLineThickness + slashMaxWidthConstant; } else { mLineRect.width = mLineThickness + NS_MIN(slashMaxWidthConstant, (mBoundingMetrics.ascent + mBoundingMetrics.descent) / slashRatio); } // Set horizontal bounding metrics mBoundingMetrics.leftBearing = leftSpace + bmNum.leftBearing; mBoundingMetrics.rightBearing = leftSpace + bmNum.width + mLineRect.width + bmDen.rightBearing; mBoundingMetrics.width = leftSpace + bmNum.width + mLineRect.width + bmDen.width + rightSpace; // Set aDesiredSize aDesiredSize.ascent = mBoundingMetrics.ascent + padding; aDesiredSize.height = mBoundingMetrics.ascent + mBoundingMetrics.descent + 2 * padding; aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.ascent; if (aPlaceOrigin) { FinishReflowChild(frameNum, presContext, nsnull, sizeNum, leftSpace, aDesiredSize.ascent - numShift - sizeNum.ascent, 0); mLineRect.SetRect(leftSpace + bmNum.width, aDesiredSize.ascent - mBoundingMetrics.ascent, mLineRect.width, aDesiredSize.height - 2 * padding); FinishReflowChild(frameDen, presContext, nsnull, sizeDen, leftSpace + bmNum.width + mLineRect.width, aDesiredSize.ascent + denShift - sizeDen.ascent, 0); } } return NS_OK; }
/* virtual */ nsresult nsMathMLmencloseFrame::PlaceInternal(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize, bool aWidthOnly) { /////////////// // Measure the size of our content using the base class to format like an // inferred mrow. nsHTMLReflowMetrics baseSize; nsresult rv = nsMathMLContainerFrame::Place(aRenderingContext, false, baseSize); if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { DidReflowChildren(GetFirstPrincipalChild()); 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; /////////////// // Thickness of bars and font metrics nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); nscoord mEmHeight; nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); aRenderingContext.SetFont(fm); GetRuleThickness(aRenderingContext, fm, mRuleThickness); GetEmHeight(fm, mEmHeight); PRUnichar one = '1'; nsBoundingMetrics bmOne = aRenderingContext.GetBoundingMetrics(&one, 1); /////////////// // 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)) { nscoord phi; // Rule 11, App. G, TeXbook // psi = clearance between rule and content if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) phi = fm->XHeight(); else phi = mRuleThickness; psi = mRuleThickness + phi / 4; delta = psi % onePixel; if (delta) psi += onePixel - delta; // round up } if (mRuleThickness < onePixel) mRuleThickness = onePixel; // 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_DOWNDIAGONALSTRIKE) || IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX) || IsToDraw(NOTATION_RADICAL) || IsToDraw(NOTATION_LONGDIV)) { // set a minimal value for the base height bmBase.ascent = NS_MAX(bmOne.ascent, bmBase.ascent); bmBase.descent = NS_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; /////////////// // 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 = NS_MAX(dx_left, padding2); dx_right = NS_MAX(dx_right, padding2); // Update vertical parameters padding2 = ratio * (bmBase.ascent + bmBase.descent); mBoundingMetrics.ascent = NS_MAX(mBoundingMetrics.ascent, bmBase.ascent + padding2); mBoundingMetrics.descent = NS_MAX(mBoundingMetrics.descent, bmBase.descent + padding2); } /////////////// // longdiv notation: if (IsToDraw(NOTATION_LONGDIV)) { if (aWidthOnly) { nscoord longdiv_width = mMathMLChar[mLongDivCharIndex]. GetMaxWidth(PresContext(), aRenderingContext); // Update horizontal parameters dx_left = NS_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(), aRenderingContext, NS_STRETCH_DIRECTION_VERTICAL, contSize, bmLongdivChar, NS_STRETCH_LARGER, false); mMathMLChar[mLongDivCharIndex].GetBoundingMetrics(bmLongdivChar); // Update horizontal parameters dx_left = NS_MAX(dx_left, bmLongdivChar.width); // Update vertical parameters longdivAscent = bmBase.ascent + psi + mRuleThickness; longdivDescent = NS_MAX(bmBase.descent, (bmLongdivChar.ascent + bmLongdivChar.descent - longdivAscent)); mBoundingMetrics.ascent = NS_MAX(mBoundingMetrics.ascent, longdivAscent); mBoundingMetrics.descent = NS_MAX(mBoundingMetrics.descent, longdivDescent); } } /////////////// // radical notation: if (IsToDraw(NOTATION_RADICAL)) { nscoord *dx_leading = NS_MATHML_IS_RTL(mPresentationData.flags) ? &dx_right : &dx_left; if (aWidthOnly) { nscoord radical_width = mMathMLChar[mRadicalCharIndex]. GetMaxWidth(PresContext(), aRenderingContext); // Update horizontal parameters *dx_leading = NS_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 = mRuleThickness; contSize.descent = bmBase.ascent + bmBase.descent + psi; // height(radical) should be >= height(base) + psi + mRuleThickness mMathMLChar[mRadicalCharIndex].Stretch(PresContext(), aRenderingContext, NS_STRETCH_DIRECTION_VERTICAL, contSize, bmRadicalChar, NS_STRETCH_LARGER, NS_MATHML_IS_RTL(mPresentationData.flags)); mMathMLChar[mRadicalCharIndex].GetBoundingMetrics(bmRadicalChar); // Update horizontal parameters *dx_leading = NS_MAX(*dx_leading, bmRadicalChar.width); // Update vertical parameters radicalAscent = bmBase.ascent + psi + mRuleThickness; radicalDescent = NS_MAX(bmBase.descent, (bmRadicalChar.ascent + bmRadicalChar.descent - radicalAscent)); mBoundingMetrics.ascent = NS_MAX(mBoundingMetrics.ascent, radicalAscent); mBoundingMetrics.descent = NS_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 = NS_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 = NS_MIN(0, dx_left + bmBase.leftBearing); mBoundingMetrics.rightBearing = NS_MAX(mBoundingMetrics.width, dx_left + bmBase.rightBearing); aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.ascent = NS_MAX(mBoundingMetrics.ascent, baseSize.ascent); aDesiredSize.height = aDesiredSize.ascent + NS_MAX(mBoundingMetrics.descent, baseSize.height - baseSize.ascent); if (IsToDraw(NOTATION_LONGDIV) || IsToDraw(NOTATION_RADICAL)) { // get the leading to be left at the top of the resulting frame // this seems more reliable than using fm->GetLeading() on suspicious // fonts nscoord leading = nscoord(0.2f * mEmHeight); nscoord desiredSizeAscent = aDesiredSize.ascent; nscoord desiredSizeDescent = aDesiredSize.height - aDesiredSize.ascent; if (IsToDraw(NOTATION_LONGDIV)) { desiredSizeAscent = NS_MAX(desiredSizeAscent, longdivAscent + leading); desiredSizeDescent = NS_MAX(desiredSizeDescent, longdivDescent + mRuleThickness); } if (IsToDraw(NOTATION_RADICAL)) { desiredSizeAscent = NS_MAX(desiredSizeAscent, radicalAscent + leading); desiredSizeDescent = NS_MAX(desiredSizeDescent, radicalDescent + mRuleThickness); } aDesiredSize.ascent = 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 = NS_MAX(aDesiredSize.ascent - bmBase.ascent, aDesiredSize.height - aDesiredSize.ascent - bmBase.descent); aDesiredSize.ascent = bmBase.ascent + dy; aDesiredSize.height = aDesiredSize.ascent + bmBase.descent + dy; } // Update mBoundingMetrics ascent/descent if (IsToDraw(NOTATION_TOP) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) || IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX)) mBoundingMetrics.ascent = aDesiredSize.ascent; if (IsToDraw(NOTATION_BOTTOM) || IsToDraw(NOTATION_RIGHT) || IsToDraw(NOTATION_LEFT) || IsToDraw(NOTATION_UPDIAGONALSTRIKE) || IsToDraw(NOTATION_DOWNDIAGONALSTRIKE) || IsToDraw(NOTATION_VERTICALSTRIKE) || IsToDraw(NOTATION_CIRCLE) || IsToDraw(NOTATION_ROUNDEDBOX)) mBoundingMetrics.descent = aDesiredSize.height - aDesiredSize.ascent; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.ascent; if (aPlaceOrigin) { ////////////////// // Set position and size of MathMLChars if (IsToDraw(NOTATION_LONGDIV)) mMathMLChar[mLongDivCharIndex].SetRect(nsRect(dx_left - bmLongdivChar.width, aDesiredSize.ascent - longdivAscent, bmLongdivChar.width, bmLongdivChar.ascent + bmLongdivChar.descent)); if (IsToDraw(NOTATION_RADICAL)) { nscoord dx = NS_MATHML_IS_RTL(mPresentationData.flags) ? dx_left + bmBase.width : dx_left - bmRadicalChar.width; mMathMLChar[mRadicalCharIndex].SetRect(nsRect(dx, aDesiredSize.ascent - radicalAscent, bmRadicalChar.width, bmRadicalChar.ascent + bmRadicalChar.descent)); } mContentWidth = bmBase.width; ////////////////// // Finish reflowing child frames PositionRowChildFrames(dx_left, aDesiredSize.ascent); } return NS_OK; }
/* virtual */ nsresult nsMathMLmfracFrame::Place(nsIRenderingContext& aRenderingContext, PRBool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { //////////////////////////////////// // Get the children's desired sizes nsBoundingMetrics bmNum, bmDen; nsHTMLReflowMetrics sizeNum; nsHTMLReflowMetrics sizeDen; nsIFrame* frameDen = nsnull; nsIFrame* frameNum = mFrames.FirstChild(); if (frameNum) frameDen = frameNum->GetNextSibling(); if (!frameNum || !frameDen || frameDen->GetNextSibling()) { // report an error, encourage people to get their markups in order NS_WARNING("invalid markup"); return ReflowError(aRenderingContext, aDesiredSize); } GetReflowAndBoundingMetricsFor(frameNum, sizeNum, bmNum); GetReflowAndBoundingMetricsFor(frameDen, sizeDen, bmDen); ////////////////// // Get shifts nsPresContext* presContext = PresContext(); nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); aRenderingContext.SetFont(GetStyleFont()->mFont, nsnull); nsCOMPtr<nsIFontMetrics> fm; aRenderingContext.GetFontMetrics(*getter_AddRefs(fm)); nscoord defaultRuleThickness, axisHeight; GetRuleThickness(aRenderingContext, fm, defaultRuleThickness); GetAxisHeight(aRenderingContext, fm, axisHeight); // by default, leave at least one-pixel padding at either end, or use // lspace & rspace that may come from <mo> if we are an embellished container // (we fetch values from the core since they may use units that depend // on style data, and style changes could have occurred in the core since // our last visit there) nsEmbellishData coreData; GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData); nscoord leftSpace = PR_MAX(onePixel, coreData.leftSpace); nscoord rightSpace = PR_MAX(onePixel, coreData.rightSpace); // see if the linethickness attribute is there nsAutoString value; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::linethickness_, value); mLineRect.height = CalcLineThickness(presContext, mStyleContext, value, onePixel, defaultRuleThickness); nscoord numShift = 0; nscoord denShift = 0; // Rule 15b, App. G, TeXbook nscoord numShift1, numShift2, numShift3; nscoord denShift1, denShift2; GetNumeratorShifts(fm, numShift1, numShift2, numShift3); GetDenominatorShifts(fm, denShift1, denShift2); if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) { // C > T numShift = numShift1; denShift = denShift1; } else { numShift = (0 < mLineRect.height) ? numShift2 : numShift3; denShift = denShift2; } nscoord minClearance = 0; nscoord actualClearance = 0; nscoord actualRuleThickness = mLineRect.height; if (0 == actualRuleThickness) { // Rule 15c, App. G, TeXbook // min clearance between numerator and denominator minClearance = (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) ? 7 * defaultRuleThickness : 3 * defaultRuleThickness; actualClearance = (numShift - bmNum.descent) - (bmDen.ascent - denShift); // actualClearance should be >= minClearance if (actualClearance < minClearance) { nscoord halfGap = (minClearance - actualClearance)/2; numShift += halfGap; denShift += halfGap; } } else { // Rule 15d, App. G, TeXbook // min clearance between numerator or denominator and middle of bar // TeX has a different interpretation of the thickness. // Try $a \above10pt b$ to see. Here is what TeX does: // minClearance = (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) ? // 3 * actualRuleThickness : actualRuleThickness; // we slightly depart from TeX here. We use the defaultRuleThickness instead // of the value coming from the linethickness attribute, i.e., we recover what // TeX does if the user hasn't set linethickness. But when the linethickness // is set, we avoid the wide gap problem. minClearance = (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) ? 3 * defaultRuleThickness : defaultRuleThickness + onePixel; // adjust numShift to maintain minClearance if needed actualClearance = (numShift - bmNum.descent) - (axisHeight + actualRuleThickness/2); if (actualClearance < minClearance) { numShift += (minClearance - actualClearance); } // adjust denShift to maintain minClearance if needed actualClearance = (axisHeight - actualRuleThickness/2) - (bmDen.ascent - denShift); if (actualClearance < minClearance) { denShift += (minClearance - actualClearance); } } ////////////////// // Place Children // XXX Need revisiting the width. TeX uses the exact width // e.g. in $$\huge\frac{\displaystyle\int}{i}$$ nscoord width = PR_MAX(bmNum.width, bmDen.width); nscoord dxNum = leftSpace + (width - sizeNum.width)/2; nscoord dxDen = leftSpace + (width - sizeDen.width)/2; width += leftSpace + rightSpace; // see if the numalign attribute is there GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::numalign_, value); if (value.EqualsLiteral("left")) dxNum = leftSpace; else if (value.EqualsLiteral("right")) dxNum = width - rightSpace - sizeNum.width; // see if the denomalign attribute is there GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::denomalign_, value); if (value.EqualsLiteral("left")) dxDen = leftSpace; else if (value.EqualsLiteral("right")) dxDen = width - rightSpace - sizeDen.width; mBoundingMetrics.rightBearing = PR_MAX(dxNum + bmNum.rightBearing, dxDen + bmDen.rightBearing); if (mBoundingMetrics.rightBearing < width - rightSpace) mBoundingMetrics.rightBearing = width - rightSpace; mBoundingMetrics.leftBearing = PR_MIN(dxNum + bmNum.leftBearing, dxDen + bmDen.leftBearing); if (mBoundingMetrics.leftBearing > leftSpace) mBoundingMetrics.leftBearing = leftSpace; mBoundingMetrics.ascent = bmNum.ascent + numShift; mBoundingMetrics.descent = bmDen.descent + denShift; mBoundingMetrics.width = width; aDesiredSize.ascent = sizeNum.ascent + numShift; aDesiredSize.height = aDesiredSize.ascent + sizeDen.height - sizeDen.ascent + denShift; aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.ascent; if (aPlaceOrigin) { nscoord dy; // place numerator dy = 0; FinishReflowChild(frameNum, presContext, nsnull, sizeNum, dxNum, dy, 0); // place denominator dy = aDesiredSize.height - sizeDen.height; FinishReflowChild(frameDen, presContext, nsnull, sizeDen, dxDen, dy, 0); // place the fraction bar - dy is top of bar dy = aDesiredSize.ascent - (axisHeight + actualRuleThickness/2); mLineRect.SetRect(leftSpace, dy, width - (leftSpace + rightSpace), actualRuleThickness); } return NS_OK; }
/* virtual */ nsresult nsMathMLmunderFrame::Place(nsIRenderingContext& aRenderingContext, PRBool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) && !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) { // place like subscript return nsMathMLmsubFrame::PlaceSubScript(PresContext(), aRenderingContext, aPlaceOrigin, aDesiredSize, this, 0, nsPresContext::CSSPointsToAppUnits(0.5f)); } //////////////////////////////////// // Get the children's desired sizes nsBoundingMetrics bmBase, bmUnder; nsHTMLReflowMetrics baseSize; nsHTMLReflowMetrics underSize; nsIFrame* underFrame = nsnull; nsIFrame* baseFrame = mFrames.FirstChild(); if (baseFrame) underFrame = baseFrame->GetNextSibling(); if (!baseFrame || !underFrame || underFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order return ReflowError(aRenderingContext, aDesiredSize); } GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder); nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); //////////////////// // Place Children aRenderingContext.SetFont(GetStyleFont()->mFont, PresContext()->GetUserFontSet()); nsCOMPtr<nsIFontMetrics> fm; aRenderingContext.GetFontMetrics(*getter_AddRefs(fm)); nscoord xHeight = 0; fm->GetXHeight (xHeight); nscoord ruleThickness; GetRuleThickness (aRenderingContext, fm, ruleThickness); // there are 2 different types of placement depending on // whether we want an accented under or not nscoord correction = 0; GetItalicCorrection (bmBase, correction); nscoord delta1 = 0; // gap between base and underscript nscoord delta2 = 0; // extra space beneath underscript if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) { // Rule 13a, App. G, TeXbook nscoord bigOpSpacing2, bigOpSpacing4, bigOpSpacing5, dummy; GetBigOpSpacings (fm, dummy, bigOpSpacing2, dummy, bigOpSpacing4, bigOpSpacing5); delta1 = NS_MAX(bigOpSpacing2, (bigOpSpacing4 - bmUnder.ascent)); delta2 = bigOpSpacing5; } else { // No corresponding rule in TeXbook - we are on our own here // XXX tune the gap delta between base and underscript // Should we use Rule 10 like \underline does? delta1 = ruleThickness + onePixel/2; delta2 = ruleThickness; } // empty under? if (!(bmUnder.ascent + bmUnder.descent)) delta1 = 0; nscoord dxBase, dxUnder = 0; // Width of non-spacing marks is zero so use left and right bearing. nscoord underWidth = bmUnder.width; if (!underWidth) { underWidth = bmUnder.rightBearing - bmUnder.leftBearing; dxUnder = -bmUnder.leftBearing; } nscoord maxWidth = NS_MAX(bmBase.width, underWidth); if (NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) { dxUnder += (maxWidth - underWidth)/2; } else { dxUnder += -correction/2 + (maxWidth - underWidth)/2; } dxBase = (maxWidth - bmBase.width)/2; mBoundingMetrics.width = NS_MAX(dxBase + bmBase.width, dxUnder + bmUnder.width); mBoundingMetrics.ascent = bmBase.ascent; mBoundingMetrics.descent = bmBase.descent + delta1 + bmUnder.ascent + bmUnder.descent; mBoundingMetrics.leftBearing = NS_MIN(dxBase + bmBase.leftBearing, dxUnder + bmUnder.leftBearing); mBoundingMetrics.rightBearing = NS_MAX(dxBase + bmBase.rightBearing, dxUnder + bmUnder.rightBearing); aDesiredSize.ascent = baseSize.ascent; aDesiredSize.height = aDesiredSize.ascent + NS_MAX(mBoundingMetrics.descent + delta2, bmBase.descent + delta1 + bmUnder.ascent + underSize.height - underSize.ascent); aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.ascent; if (aPlaceOrigin) { nscoord dy = 0; // place base FinishReflowChild(baseFrame, PresContext(), nsnull, baseSize, dxBase, dy, 0); // place underscript dy = aDesiredSize.ascent + mBoundingMetrics.descent - bmUnder.descent - underSize.ascent; FinishReflowChild(underFrame, PresContext(), nsnull, underSize, dxUnder, dy, 0); } return NS_OK; }