// Only place the selected child ... /* virtual */ nsresult nsMathMLmactionFrame::Place(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { nsIFrame* childFrame = GetSelectedFrame(); if (mSelection == -1) { return ReflowError(aRenderingContext, aDesiredSize); } aDesiredSize.width = aDesiredSize.height = 0; aDesiredSize.ascent = 0; mBoundingMetrics = nsBoundingMetrics(); if (childFrame) { GetReflowAndBoundingMetricsFor(childFrame, aDesiredSize, mBoundingMetrics); if (aPlaceOrigin) { FinishReflowChild(childFrame, PresContext(), nullptr, aDesiredSize, 0, 0, 0); } mReference.x = 0; mReference.y = aDesiredSize.ascent; } aDesiredSize.mBoundingMetrics = mBoundingMetrics; return NS_OK; }
void nsMeterFrame::ReflowBarFrame(nsIFrame* aBarFrame, nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { bool vertical = StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL; nsHTMLReflowState reflowState(aPresContext, aReflowState, aBarFrame, nsSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE)); nscoord size = vertical ? aReflowState.ComputedHeight() : aReflowState.ComputedWidth(); nscoord xoffset = aReflowState.mComputedBorderPadding.left; nscoord yoffset = aReflowState.mComputedBorderPadding.top; // NOTE: Introduce a new function getPosition in the content part ? double position, max, min, value; nsCOMPtr<nsIDOMHTMLMeterElement> meterElement = do_QueryInterface(mContent); meterElement->GetMax(&max); meterElement->GetMin(&min); meterElement->GetValue(&value); position = max - min; position = position != 0 ? (value - min) / position : 1; size = NSToCoordRound(size * position); if (!vertical && StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { xoffset += aReflowState.ComputedWidth() - size; } // The bar position is *always* constrained. if (vertical) { // We want the bar to begin at the bottom. yoffset += aReflowState.ComputedHeight() - size; size -= reflowState.mComputedMargin.TopBottom() + reflowState.mComputedBorderPadding.TopBottom(); size = std::max(size, 0); reflowState.SetComputedHeight(size); } else { size -= reflowState.mComputedMargin.LeftRight() + reflowState.mComputedBorderPadding.LeftRight(); size = std::max(size, 0); reflowState.SetComputedWidth(size); } xoffset += reflowState.mComputedMargin.left; yoffset += reflowState.mComputedMargin.top; nsHTMLReflowMetrics barDesiredSize; ReflowChild(aBarFrame, aPresContext, barDesiredSize, reflowState, xoffset, yoffset, 0, aStatus); FinishReflowChild(aBarFrame, aPresContext, &reflowState, barDesiredSize, xoffset, yoffset, 0); }
nsresult nsComboboxControlFrame::ReflowDropdown(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState) { // All we want out of it later on, really, is the height of a row, so we // don't even need to cache mDropdownFrame's ascent or anything. If we don't // need to reflow it, just bail out here. if (!aReflowState.ShouldReflowAllKids() && !NS_SUBTREE_DIRTY(mDropdownFrame)) { return NS_OK; } // XXXbz this will, for small-height dropdowns, have extra space on the right // edge for the scrollbar we don't show... but that's the best we can do here // for now. nsSize availSize(aReflowState.availableWidth, NS_UNCONSTRAINEDSIZE); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, mDropdownFrame, availSize); // If the dropdown's intrinsic width is narrower than our specified width, // then expand it out. We want our border-box width to end up the same as // the dropdown's so account for both sets of mComputedBorderPadding. nscoord forcedWidth = aReflowState.ComputedWidth() + aReflowState.mComputedBorderPadding.LeftRight() - kidReflowState.mComputedBorderPadding.LeftRight(); kidReflowState.SetComputedWidth(PR_MAX(kidReflowState.ComputedWidth(), forcedWidth)); // ensure we start off hidden if (GetStateBits() & NS_FRAME_FIRST_REFLOW) { nsIView* view = mDropdownFrame->GetView(); nsIViewManager* viewManager = view->GetViewManager(); viewManager->SetViewVisibility(view, nsViewVisibility_kHide); nsRect emptyRect(0, 0, 0, 0); viewManager->ResizeView(view, emptyRect); } // Allow the child to move/size/change-visibility its view if it's currently // dropped down PRInt32 flags = NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_VISIBILITY | NS_FRAME_NO_SIZE_VIEW; if (mDroppedDown) { flags = 0; } nsRect rect = mDropdownFrame->GetRect(); nsHTMLReflowMetrics desiredSize; nsReflowStatus ignoredStatus; nsresult rv = ReflowChild(mDropdownFrame, aPresContext, desiredSize, kidReflowState, rect.x, rect.y, flags, ignoredStatus); // Set the child's width and height to it's desired size FinishReflowChild(mDropdownFrame, aPresContext, &kidReflowState, desiredSize, rect.x, rect.y, flags); return rv; }
void nsRubyTextContainerFrame::ReflowRubyTextFrame( nsRubyTextFrame* rtFrame, nsIFrame* rbFrame, nscoord baseStart, nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState) { nsReflowStatus frameReflowStatus; nsHTMLReflowMetrics metrics(aReflowState, aDesiredSize.mFlags); mozilla::WritingMode lineWM = mLineLayout->GetWritingMode(); mozilla::LogicalSize availSize(lineWM, aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); nsHTMLReflowState childReflowState(aPresContext, aReflowState, rtFrame, availSize); // Determine the inline coordinate for the text frame by centering over // the corresponding base frame int baseWidth; if (rbFrame) { baseWidth = rbFrame->ISize(); // If this is the last ruby annotation, it gets paired with ALL remaining // ruby bases if (!rtFrame->GetNextSibling()) { rbFrame = rbFrame->GetNextSibling(); while (rbFrame) { baseWidth += rbFrame->ISize(); rbFrame = rbFrame->GetNextSibling(); } } } else { baseWidth = 0; } int baseCenter = baseStart + baseWidth / 2; // FIXME: Find a way to avoid using GetPrefISize here, potentially by moving // the frame after it has reflowed. nscoord ICoord = baseCenter - rtFrame->GetPrefISize(aReflowState.rendContext) / 2; if (ICoord > mLineLayout->GetCurrentICoord()) { mLineLayout->AdvanceICoord(ICoord - mLineLayout->GetCurrentICoord()); } bool pushedFrame; mLineLayout->ReflowFrame(rtFrame, frameReflowStatus, &metrics, pushedFrame); NS_ASSERTION(!pushedFrame, "Ruby line breaking is not yet implemented"); mISize += metrics.ISize(lineWM); rtFrame->SetSize(nsSize(metrics.ISize(lineWM), metrics.BSize(lineWM))); FinishReflowChild(rtFrame, aPresContext, metrics, &childReflowState, 0, 0, NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_MOVE_VIEW); }
void nsSVGForeignObjectFrame::DoReflow() { // Skip reflow if we're zero-sized, unless this is our first reflow. if (IsDisabled() && !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) return; nsPresContext *presContext = PresContext(); nsIFrame* kid = GetFirstPrincipalChild(); if (!kid) return; // initiate a synchronous reflow here and now: nsIPresShell* presShell = presContext->PresShell(); NS_ASSERTION(presShell, "null presShell"); nsRefPtr<nsRenderingContext> renderingContext = presShell->GetReferenceRenderingContext(); if (!renderingContext) return; mInReflow = true; nsHTMLReflowState reflowState(presContext, kid, renderingContext, nsSize(mRect.width, NS_UNCONSTRAINEDSIZE)); nsHTMLReflowMetrics desiredSize; nsReflowStatus status; // We don't use mRect.height above because that tells the child to do // page/column breaking at that height. NS_ASSERTION(reflowState.mComputedBorderPadding == nsMargin(0, 0, 0, 0) && reflowState.mComputedMargin == nsMargin(0, 0, 0, 0), "style system should ensure that :-moz-svg-foreign-content " "does not get styled"); NS_ASSERTION(reflowState.ComputedWidth() == mRect.width, "reflow state made child wrong size"); reflowState.SetComputedHeight(mRect.height); ReflowChild(kid, presContext, desiredSize, reflowState, 0, 0, NS_FRAME_NO_MOVE_FRAME, status); NS_ASSERTION(mRect.width == desiredSize.width && mRect.height == desiredSize.height, "unexpected size"); FinishReflowChild(kid, presContext, &reflowState, desiredSize, 0, 0, NS_FRAME_NO_MOVE_FRAME); mInReflow = false; if (!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) { FlushDirtyRegion(0); } }
void nsProgressFrame::ReflowBarFrame(nsIFrame* aBarFrame, nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { nsHTMLReflowState reflowState(aPresContext, aReflowState, aBarFrame, nsSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE)); nscoord width = aReflowState.ComputedWidth(); nscoord xoffset = aReflowState.mComputedBorderPadding.left; nscoord yoffset = aReflowState.mComputedBorderPadding.top; double position; nsCOMPtr<nsIDOMHTMLProgressElement> progressElement = do_QueryInterface(mContent); progressElement->GetPosition(&position); // Force the bar's width to match the current progress. // When indeterminate, the progress' width will be 100%. if (position >= 0.0) { width *= position; } if (GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { xoffset += aReflowState.ComputedWidth() - width; } // The bar width is fixed in these cases: // - the progress position is determined: the bar width is fixed according // to it's value. // - the progress position is indeterminate and the bar appearance should be // shown as native: the bar width is forced to 100%. // Otherwise (when the progress is indeterminate and the bar appearance isn't // native), the bar width isn't fixed and can be set by the author. if (position != -1 || ShouldUseNativeStyle()) { width -= reflowState.mComputedMargin.LeftRight() + reflowState.mComputedBorderPadding.LeftRight(); width = NS_MAX(width, 0); reflowState.SetComputedWidth(width); } xoffset += reflowState.mComputedMargin.left; yoffset += reflowState.mComputedMargin.top; nsHTMLReflowMetrics barDesiredSize; ReflowChild(aBarFrame, aPresContext, barDesiredSize, reflowState, xoffset, yoffset, 0, aStatus); FinishReflowChild(aBarFrame, aPresContext, &reflowState, barDesiredSize, xoffset, yoffset, 0); }
// For token elements, mBoundingMetrics is computed at the ReflowToken // pass, it is not computed here because our children may be text frames // that do not implement the GetBoundingMetrics() interface. /* virtual */ nsresult nsMathMLTokenFrame::Place(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { mBoundingMetrics = nsBoundingMetrics(); for (nsIFrame* childFrame = GetFirstPrincipalChild(); childFrame; childFrame = childFrame->GetNextSibling()) { nsHTMLReflowMetrics childSize(aDesiredSize.GetWritingMode()); GetReflowAndBoundingMetricsFor(childFrame, childSize, childSize.mBoundingMetrics, nullptr); // compute and cache the bounding metrics mBoundingMetrics += childSize.mBoundingMetrics; } nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); nscoord ascent = fm->MaxAscent(); nscoord descent = fm->MaxDescent(); aDesiredSize.mBoundingMetrics = mBoundingMetrics; aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.SetTopAscent(std::max(mBoundingMetrics.ascent, ascent)); aDesiredSize.Height() = aDesiredSize.TopAscent() + std::max(mBoundingMetrics.descent, descent); if (aPlaceOrigin) { nscoord dy, dx = 0; for (nsIFrame* childFrame = GetFirstPrincipalChild(); childFrame; childFrame = childFrame->GetNextSibling()) { nsHTMLReflowMetrics childSize(aDesiredSize.GetWritingMode()); GetReflowAndBoundingMetricsFor(childFrame, childSize, childSize.mBoundingMetrics); // place and size the child; (dx,0) makes the caret happy - bug 188146 dy = childSize.Height() == 0 ? 0 : aDesiredSize.TopAscent() - childSize.TopAscent(); FinishReflowChild(childFrame, PresContext(), childSize, nullptr, dx, dy, 0); dx += childSize.Width(); } } SetReference(nsPoint(0, aDesiredSize.TopAscent())); return NS_OK; }
NS_METHOD nsTableColGroupFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsTableColGroupFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_ASSERTION(nsnull!=mContent, "bad state -- null content for frame"); nsresult rv=NS_OK; const nsStyleVisibility* groupVis = GetStyleVisibility(); bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible); if (collapseGroup) { nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); tableFrame->SetNeedToCollapse(true); } // for every content child that (is a column thingy and does not already have a frame) // create a frame and adjust it's style for (nsIFrame *kidFrame = mFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) { // Give the child frame a chance to reflow, even though we know it'll have 0 size nsHTMLReflowMetrics kidSize; nsHTMLReflowState kidReflowState(aPresContext, aReflowState, kidFrame, nsSize(0,0)); nsReflowStatus status; ReflowChild(kidFrame, aPresContext, kidSize, kidReflowState, 0, 0, 0, status); FinishReflowChild(kidFrame, aPresContext, nsnull, kidSize, 0, 0, 0); } aDesiredSize.width=0; aDesiredSize.height=0; aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return rv; }
NS_IMETHODIMP nsSimplePageSequenceFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { NS_PRECONDITION(aPresContext->IsRootPaginatedDocument(), "A Page Sequence is only for real pages"); DO_GLOBAL_REFLOW_COUNT("nsSimplePageSequenceFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_FRAME_TRACE_REFLOW_IN("nsSimplePageSequenceFrame::Reflow"); aStatus = NS_FRAME_COMPLETE; // we're always complete // Don't do incremental reflow until we've taught tables how to do // it right in paginated mode. if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { // Return our desired size aDesiredSize.height = mSize.height * PresContext()->GetPrintPreviewScale(); aDesiredSize.width = mSize.width * PresContext()->GetPrintPreviewScale(); aDesiredSize.SetOverflowAreasToDesiredBounds(); FinishAndStoreOverflow(&aDesiredSize); return NS_OK; } PRBool isPrintPreview = aPresContext->Type() == nsPresContext::eContext_PrintPreview; // See if we can get a Print Settings from the Context if (!mPageData->mPrintSettings && aPresContext->Medium() == nsGkAtoms::print) { mPageData->mPrintSettings = aPresContext->GetPrintSettings(); } // now get out margins & edges if (mPageData->mPrintSettings) { nsIntMargin unwriteableTwips; mPageData->mPrintSettings->GetUnwriteableMarginInTwips(unwriteableTwips); NS_ASSERTION(unwriteableTwips.left >= 0 && unwriteableTwips.top >= 0 && unwriteableTwips.right >= 0 && unwriteableTwips.bottom >= 0, "Unwriteable twips should be non-negative"); nsIntMargin marginTwips; mPageData->mPrintSettings->GetMarginInTwips(marginTwips); mMargin = aPresContext->CSSTwipsToAppUnits(marginTwips + unwriteableTwips); PRInt16 printType; mPageData->mPrintSettings->GetPrintRange(&printType); mPrintRangeType = printType; nsIntMargin edgeTwips; mPageData->mPrintSettings->GetEdgeInTwips(edgeTwips); // sanity check the values. three inches are sometimes needed PRInt32 inchInTwips = NS_INCHES_TO_INT_TWIPS(3.0); edgeTwips.top = NS_MIN(NS_MAX(edgeTwips.top, 0), inchInTwips); edgeTwips.bottom = NS_MIN(NS_MAX(edgeTwips.bottom, 0), inchInTwips); edgeTwips.left = NS_MIN(NS_MAX(edgeTwips.left, 0), inchInTwips); edgeTwips.right = NS_MIN(NS_MAX(edgeTwips.right, 0), inchInTwips); mPageData->mEdgePaperMargin = aPresContext->CSSTwipsToAppUnits(edgeTwips + unwriteableTwips); } // *** Special Override *** // If this is a sub-sdoc (meaning it doesn't take the whole page) // and if this Document is in the upper left hand corner // we need to suppress the top margin or it will reflow too small nsSize pageSize = aPresContext->GetPageSize(); mPageData->mReflowSize = pageSize; // If we're printing a selection, we need to reflow with // unconstrained height, to make sure we'll get to the selection // even if it's beyond the first page of content. if (nsIPrintSettings::kRangeSelection == mPrintRangeType) { mPageData->mReflowSize.height = NS_UNCONSTRAINEDSIZE; } mPageData->mReflowMargin = mMargin; // Compute the size of each page and the x coordinate that each page will // be placed at nscoord extraThreshold = NS_MAX(pageSize.width, pageSize.height)/10; PRInt32 gapInTwips = nsContentUtils::GetIntPref("print.print_extra_margin"); gapInTwips = NS_MAX(0, gapInTwips); nscoord extraGap = aPresContext->CSSTwipsToAppUnits(gapInTwips); extraGap = NS_MIN(extraGap, extraThreshold); // clamp to 1/10 of the largest dim of the page nscoord deadSpaceGap = 0; if (isPrintPreview) { GetDeadSpaceValue(&gapInTwips); deadSpaceGap = aPresContext->CSSTwipsToAppUnits(gapInTwips); } nsMargin extraMargin(0,0,0,0); nsSize shadowSize(0,0); if (aPresContext->IsScreen()) { extraMargin.SizeTo(extraGap, extraGap, extraGap, extraGap); nscoord fourPixels = nsPresContext::CSSPixelsToAppUnits(4); shadowSize.SizeTo(fourPixels, fourPixels); } mPageData->mShadowSize = shadowSize; mPageData->mExtraMargin = extraMargin; const nscoord x = deadSpaceGap; nscoord y = deadSpaceGap;// Running y-offset for each page nsSize availSize(pageSize.width + shadowSize.width + extraMargin.LeftRight(), pageSize.height + shadowSize.height + extraMargin.TopBottom()); // Tile the pages vertically nsHTMLReflowMetrics kidSize; for (nsIFrame* kidFrame = mFrames.FirstChild(); nsnull != kidFrame; ) { // Set the shared data into the page frame before reflow nsPageFrame * pf = static_cast<nsPageFrame*>(kidFrame); pf->SetSharedPageData(mPageData); // Reflow the page nsHTMLReflowState kidReflowState(aPresContext, aReflowState, kidFrame, availSize); nsReflowStatus status; kidReflowState.SetComputedWidth(kidReflowState.availableWidth); //kidReflowState.SetComputedHeight(kidReflowState.availableHeight); PR_PL(("AV W: %d H: %d\n", kidReflowState.availableWidth, kidReflowState.availableHeight)); // Place and size the page. If the page is narrower than our // max width then center it horizontally ReflowChild(kidFrame, aPresContext, kidSize, kidReflowState, x, y, 0, status); FinishReflowChild(kidFrame, aPresContext, nsnull, kidSize, x, y, 0); y += kidSize.height; // Leave a slight gap between the pages y += deadSpaceGap; // Is the page complete? nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow(); if (NS_FRAME_IS_FULLY_COMPLETE(status)) { NS_ASSERTION(!kidNextInFlow, "bad child flow list"); } else if (!kidNextInFlow) { // The page isn't complete and it doesn't have a next-in-flow, so // create a continuing page. nsIFrame* continuingPage; nsresult rv = aPresContext->PresShell()->FrameConstructor()-> CreateContinuingFrame(aPresContext, kidFrame, this, &continuingPage); if (NS_FAILED(rv)) { break; } // Add it to our child list mFrames.InsertFrame(nsnull, kidFrame, continuingPage); } // Get the next page kidFrame = kidFrame->GetNextSibling(); } // Get Total Page Count nsIFrame* page; PRInt32 pageTot = 0; for (page = mFrames.FirstChild(); page; page = page->GetNextSibling()) { pageTot++; } // Set Page Number Info PRInt32 pageNum = 1; for (page = mFrames.FirstChild(); page; page = page->GetNextSibling()) { nsPageFrame * pf = static_cast<nsPageFrame*>(page); if (pf != nsnull) { pf->SetPageNumInfo(pageNum, pageTot); } pageNum++; } // Create current Date/Time String if (!mDateFormatter) mDateFormatter = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID); NS_ENSURE_TRUE(mDateFormatter, NS_ERROR_FAILURE); nsAutoString formattedDateString; time_t ltime; time( <ime ); if (NS_SUCCEEDED(mDateFormatter->FormatTime(nsnull /* nsILocale* locale */, kDateFormatShort, kTimeFormatNoSeconds, ltime, formattedDateString))) { PRUnichar * uStr = ToNewUnicode(formattedDateString); SetDateTimeStr(uStr); // memory will be freed } // Return our desired size // Adjustr the reflow size by PrintPreviewScale so the scrollbars end up the // correct size nscoord w = (x + availSize.width + deadSpaceGap); aDesiredSize.height = y * PresContext()->GetPrintPreviewScale(); // includes page heights and dead space aDesiredSize.width = w * PresContext()->GetPrintPreviewScale(); aDesiredSize.SetOverflowAreasToDesiredBounds(); FinishAndStoreOverflow(&aDesiredSize); // cache the size so we can set the desired size // for the other reflows that happen mSize.width = w; mSize.height = y; NS_FRAME_TRACE_REFLOW_OUT("nsSimplePageSequeceFrame::Reflow", aStatus); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 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 nsCanvasFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow"); // Initialize OUT parameter aStatus = NS_FRAME_COMPLETE; nsCanvasFrame* prevCanvasFrame = static_cast<nsCanvasFrame*> (GetPrevInFlow()); if (prevCanvasFrame) { nsAutoPtr<nsFrameList> overflow(prevCanvasFrame->StealOverflowFrames()); if (overflow) { NS_ASSERTION(overflow->OnlyChild(), "must have doc root as canvas frame's only child"); nsContainerFrame::ReparentFrameViewList(aPresContext, *overflow, prevCanvasFrame, this); // Prepend overflow to the our child list. There may already be // children placeholders for fixed-pos elements, which don't get // reflowed but must not be lost until the canvas frame is destroyed. mFrames.InsertFrames(this, nullptr, *overflow); } } // Set our size up front, since some parts of reflow depend on it // being already set. Note that the computed height may be // unconstrained; that's ok. Consumers should watch out for that. SetSize(nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); // Reflow our one and only normal child frame. It's either the root // element's frame or a placeholder for that frame, if the root element // is abs-pos or fixed-pos. We may have additional children which // are placeholders for continuations of fixed-pos content, but those // don't need to be reflowed. The normal child is always comes before // the fixed-pos placeholders, because we insert it at the start // of the child list, above. nsHTMLReflowMetrics kidDesiredSize; if (mFrames.IsEmpty()) { // We have no child frame, so return an empty size aDesiredSize.width = aDesiredSize.height = 0; } else { nsIFrame* kidFrame = mFrames.FirstChild(); bool kidDirty = (kidFrame->GetStateBits() & NS_FRAME_IS_DIRTY) != 0; nsHTMLReflowState kidReflowState(aPresContext, aReflowState, kidFrame, nsSize(aReflowState.availableWidth, aReflowState.availableHeight)); if (aReflowState.mFlags.mVResize && (kidFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) { // Tell our kid it's being vertically resized too. Bit of a // hack for framesets. kidReflowState.mFlags.mVResize = true; } nsPoint kidPt(kidReflowState.mComputedMargin.left, kidReflowState.mComputedMargin.top); // Apply CSS relative positioning const nsStyleDisplay* styleDisp = kidFrame->GetStyleDisplay(); if (NS_STYLE_POSITION_RELATIVE == styleDisp->mPosition) { kidPt += nsPoint(kidReflowState.mComputedOffsets.left, kidReflowState.mComputedOffsets.top); } // Reflow the frame ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState, kidPt.x, kidPt.y, 0, aStatus); // Complete the reflow and position and size the child frame FinishReflowChild(kidFrame, aPresContext, &kidReflowState, kidDesiredSize, kidPt.x, kidPt.y, 0); if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) { nsIFrame* nextFrame = kidFrame->GetNextInFlow(); NS_ASSERTION(nextFrame || aStatus & NS_FRAME_REFLOW_NEXTINFLOW, "If it's incomplete and has no nif yet, it must flag a nif reflow."); if (!nextFrame) { nsresult rv = aPresContext->PresShell()->FrameConstructor()-> CreateContinuingFrame(aPresContext, kidFrame, this, &nextFrame); NS_ENSURE_SUCCESS(rv, rv); SetOverflowFrames(aPresContext, nsFrameList(nextFrame, nextFrame)); // Root overflow containers will be normal children of // the canvas frame, but that's ok because there // aren't any other frames we need to isolate them from // during reflow. } if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { nextFrame->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); } } // If the child frame was just inserted, then we're responsible for making sure // it repaints if (kidDirty) { // But we have a new child, which will affect our background, so // invalidate our whole rect. // Note: Even though we request to be sized to our child's size, our // scroll frame ensures that we are always the size of the viewport. // Also note: GetPosition() on a CanvasFrame is always going to return // (0, 0). We only want to invalidate GetRect() since Get*OverflowRect() // could also include overflow to our top and left (out of the viewport) // which doesn't need to be painted. nsIFrame* viewport = PresContext()->GetPresShell()->GetRootFrame(); viewport->InvalidateFrame(); } // Return our desired size. Normally it's what we're told, but // sometimes we can be given an unconstrained height (when a window // is sizing-to-content), and we should compute our desired height. aDesiredSize.width = aReflowState.ComputedWidth(); if (aReflowState.ComputedHeight() == NS_UNCONSTRAINEDSIZE) { aDesiredSize.height = kidFrame->GetRect().height + kidReflowState.mComputedMargin.TopBottom(); } else { aDesiredSize.height = aReflowState.ComputedHeight(); } aDesiredSize.SetOverflowAreasToDesiredBounds(); aDesiredSize.mOverflowAreas.UnionWith( kidDesiredSize.mOverflowAreas + kidPt); } if (prevCanvasFrame) { ReflowOverflowContainerChildren(aPresContext, aReflowState, aDesiredSize.mOverflowAreas, 0, aStatus); } FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); NS_FRAME_TRACE_REFLOW_OUT("nsCanvasFrame::Reflow", aStatus); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return NS_OK; }
bool nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus, const ReflowConfig& aConfig, bool aUnboundedLastColumn, nsCollapsingMargin* aBottomMarginCarriedOut, ColumnBalanceData& aColData) { aColData.Reset(); bool allFit = true; bool RTL = GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL; bool shrinkingHeightOnly = !NS_SUBTREE_DIRTY(this) && mLastBalanceHeight > aConfig.mColMaxHeight; #ifdef DEBUG_roc printf("*** Doing column reflow pass: mLastBalanceHeight=%d, mColMaxHeight=%d, RTL=%d\n, mBalanceColCount=%d, mColWidth=%d, mColGap=%d\n", mLastBalanceHeight, aConfig.mColMaxHeight, RTL, aConfig.mBalanceColCount, aConfig.mColWidth, aConfig.mColGap); #endif DrainOverflowColumns(); if (mLastBalanceHeight != aConfig.mColMaxHeight) { mLastBalanceHeight = aConfig.mColMaxHeight; // XXX Seems like this could fire if incremental reflow pushed the column set // down so we reflow incrementally with a different available height. // We need a way to do an incremental reflow and be sure availableHeight // changes are taken account of! Right now I think block frames with absolute // children might exit early. //NS_ASSERTION(aKidReason != eReflowReason_Incremental, // "incremental reflow should not have changed the balance height"); } // get our border and padding const nsMargin &borderPadding = aReflowState.mComputedBorderPadding; nsRect contentRect(0, 0, 0, 0); nsOverflowAreas overflowRects; nsIFrame* child = mFrames.FirstChild(); nsPoint childOrigin = nsPoint(borderPadding.left, borderPadding.top); // For RTL, figure out where the last column's left edge should be. Since the // columns might not fill the frame exactly, we need to account for the // slop. Otherwise we'll waste time moving the columns by some tiny // amount unnecessarily. nscoord targetX = borderPadding.left; if (RTL) { nscoord availWidth = aReflowState.availableWidth; if (aReflowState.ComputedWidth() != NS_INTRINSICSIZE) { availWidth = aReflowState.ComputedWidth(); } if (availWidth != NS_INTRINSICSIZE) { childOrigin.x += availWidth - aConfig.mColWidth; targetX += aConfig.mExpectedWidthLeftOver; #ifdef DEBUG_roc printf("*** childOrigin.x = %d\n", childOrigin.x); #endif } } int columnCount = 0; int contentBottom = 0; bool reflowNext = false; while (child) { // Try to skip reflowing the child. We can't skip if the child is dirty. We also can't // skip if the next column is dirty, because the next column's first line(s) // might be pullable back to this column. We can't skip if it's the last child // because we need to obtain the bottom margin. We can't skip // if this is the last column and we're supposed to assign unbounded // height to it, because that could change the available height from // the last time we reflowed it and we should try to pull all the // content from its next sibling. (Note that it might be the last // column, but not be the last child because the desired number of columns // has changed.) bool skipIncremental = !aReflowState.ShouldReflowAllKids() && !NS_SUBTREE_DIRTY(child) && child->GetNextSibling() && !(aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) && !NS_SUBTREE_DIRTY(child->GetNextSibling()); // If we need to pull up content from the prev-in-flow then this is not just // a height shrink. The prev in flow will have set the dirty bit. // Check the overflow rect YMost instead of just the child's content height. The child // may have overflowing content that cares about the available height boundary. // (It may also have overflowing content that doesn't care about the available height // boundary, but if so, too bad, this optimization is defeated.) // We want scrollable overflow here since this is a calculation that // affects layout. bool skipResizeHeightShrink = shrinkingHeightOnly && child->GetScrollableOverflowRect().YMost() <= aConfig.mColMaxHeight; nscoord childContentBottom = 0; if (!reflowNext && (skipIncremental || skipResizeHeightShrink)) { // This child does not need to be reflowed, but we may need to move it MoveChildTo(this, child, childOrigin); // If this is the last frame then make sure we get the right status nsIFrame* kidNext = child->GetNextSibling(); if (kidNext) { aStatus = (kidNext->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) ? NS_FRAME_OVERFLOW_INCOMPLETE : NS_FRAME_NOT_COMPLETE; } else { aStatus = mLastFrameStatus; } childContentBottom = nsLayoutUtils::CalculateContentBottom(child); #ifdef DEBUG_roc printf("*** Skipping child #%d %p (incremental %d, resize height shrink %d): status = %d\n", columnCount, (void*)child, skipIncremental, skipResizeHeightShrink, aStatus); #endif } else { nsSize availSize(aConfig.mColWidth, aConfig.mColMaxHeight); if (aUnboundedLastColumn && columnCount == aConfig.mBalanceColCount - 1) { availSize.height = GetAvailableContentHeight(aReflowState); } if (reflowNext) child->AddStateBits(NS_FRAME_IS_DIRTY); nsHTMLReflowState kidReflowState(PresContext(), aReflowState, child, availSize, availSize.width, aReflowState.ComputedHeight()); kidReflowState.mFlags.mIsTopOfPage = PR_TRUE; kidReflowState.mFlags.mTableIsSplittable = PR_FALSE; #ifdef DEBUG_roc printf("*** Reflowing child #%d %p: availHeight=%d\n", columnCount, (void*)child,availSize.height); #endif // Note if the column's next in flow is not being changed by this incremental reflow. // This may allow the current column to avoid trying to pull lines from the next column. if (child->GetNextSibling() && !(GetStateBits() & NS_FRAME_IS_DIRTY) && !(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY)) { kidReflowState.mFlags.mNextInFlowUntouched = PR_TRUE; } nsHTMLReflowMetrics kidDesiredSize(aDesiredSize.mFlags); // XXX it would be cool to consult the float manager for the // previous block to figure out the region of floats from the // previous column that extend into this column, and subtract // that region from the new float manager. So you could stick a // really big float in the first column and text in following // columns would flow around it. // Reflow the frame ReflowChild(child, PresContext(), kidDesiredSize, kidReflowState, childOrigin.x + kidReflowState.mComputedMargin.left, childOrigin.y + kidReflowState.mComputedMargin.top, 0, aStatus); reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0; #ifdef DEBUG_roc printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d\n", columnCount, (void*)child, aStatus, kidDesiredSize.width, kidDesiredSize.height); #endif NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus); *aBottomMarginCarriedOut = kidDesiredSize.mCarriedOutBottomMargin; FinishReflowChild(child, PresContext(), &kidReflowState, kidDesiredSize, childOrigin.x, childOrigin.y, 0); childContentBottom = nsLayoutUtils::CalculateContentBottom(child); if (childContentBottom > aConfig.mColMaxHeight) { allFit = PR_FALSE; } if (childContentBottom > availSize.height) { aColData.mMaxOverflowingHeight = NS_MAX(childContentBottom, aColData.mMaxOverflowingHeight); } } contentRect.UnionRect(contentRect, child->GetRect()); ConsiderChildOverflow(overflowRects, child); contentBottom = NS_MAX(contentBottom, childContentBottom); aColData.mLastHeight = childContentBottom; aColData.mSumHeight += childContentBottom; // Build a continuation column if necessary nsIFrame* kidNextInFlow = child->GetNextInFlow(); if (NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)) { NS_ASSERTION(!kidNextInFlow, "next in flow should have been deleted"); child = nsnull; break; } else { ++columnCount; // Make sure that the column has a next-in-flow. If not, we must // create one to hold the overflowing stuff, even if we're just // going to put it on our overflow list and let *our* // next in flow handle it. if (!kidNextInFlow) { NS_ASSERTION(aStatus & NS_FRAME_REFLOW_NEXTINFLOW, "We have to create a continuation, but the block doesn't want us to reflow it?"); // We need to create a continuing column nsresult rv = CreateNextInFlow(PresContext(), child, kidNextInFlow); if (NS_FAILED(rv)) { NS_NOTREACHED("Couldn't create continuation"); child = nsnull; break; } } // Make sure we reflow a next-in-flow when it switches between being // normal or overflow container if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) { if (!(kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) { aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; reflowNext = PR_TRUE; kidNextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); } } else if (kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; reflowNext = PR_TRUE; kidNextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); } if (columnCount >= aConfig.mBalanceColCount) { // No more columns allowed here. Stop. aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; kidNextInFlow->AddStateBits(NS_FRAME_IS_DIRTY); // Move any of our leftover columns to our overflow list. Our // next-in-flow will eventually pick them up. const nsFrameList& continuationColumns = mFrames.RemoveFramesAfter(child); if (continuationColumns.NotEmpty()) { SetOverflowFrames(PresContext(), continuationColumns); } child = nsnull; break; } } if (PresContext()->HasPendingInterrupt()) { // Stop the loop now while |child| still points to the frame that bailed // out. We could keep going here and condition a bunch of the code in // this loop on whether there's an interrupt, or even just keep going and // trying to reflow the blocks (even though we know they'll interrupt // right after their first line), but stopping now is conceptually the // simplest (and probably fastest) thing. break; } // Advance to the next column child = child->GetNextSibling(); if (child) { if (!RTL) { childOrigin.x += aConfig.mColWidth + aConfig.mColGap; } else { childOrigin.x -= aConfig.mColWidth + aConfig.mColGap; } #ifdef DEBUG_roc printf("*** NEXT CHILD ORIGIN.x = %d\n", childOrigin.x); #endif } } if (PresContext()->CheckForInterrupt(this) && (GetStateBits() & NS_FRAME_IS_DIRTY)) { // Mark all our kids starting with |child| dirty // Note that this is a CheckForInterrupt call, not a HasPendingInterrupt, // because we might have interrupted while reflowing |child|, and since // we're about to add a dirty bit to |child| we need to make sure that // |this| is scheduled to have dirty bits marked on it and its ancestors. // Otherwise, when we go to mark dirty bits on |child|'s ancestors we'll // bail out immediately, since it'll already have a dirty bit. for (; child; child = child->GetNextSibling()) { child->AddStateBits(NS_FRAME_IS_DIRTY); } } // If we're doing RTL, we need to make sure our last column is at the left-hand side of the frame. if (RTL && childOrigin.x != targetX) { overflowRects.Clear(); contentRect = nsRect(0, 0, 0, 0); PRInt32 deltaX = targetX - childOrigin.x; #ifdef DEBUG_roc printf("*** CHILDORIGIN.x = %d, targetX = %d, DELTAX = %d\n", childOrigin.x, targetX, deltaX); #endif for (child = mFrames.FirstChild(); child; child = child->GetNextSibling()) { MoveChildTo(this, child, child->GetPosition() + nsPoint(deltaX, 0)); ConsiderChildOverflow(overflowRects, child); contentRect.UnionRect(contentRect, child->GetRect()); } } aColData.mMaxHeight = contentBottom; contentRect.height = NS_MAX(contentRect.height, contentBottom); mLastFrameStatus = aStatus; // contentRect included the borderPadding.left,borderPadding.top of the child rects contentRect -= nsPoint(borderPadding.left, borderPadding.top); nsSize contentSize = nsSize(contentRect.XMost(), contentRect.YMost()); // Apply computed and min/max values if (aReflowState.ComputedHeight() != NS_INTRINSICSIZE) { contentSize.height = aReflowState.ComputedHeight(); } else { if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxHeight) { contentSize.height = NS_MIN(aReflowState.mComputedMaxHeight, contentSize.height); } if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinHeight) { contentSize.height = NS_MAX(aReflowState.mComputedMinHeight, contentSize.height); } } if (aReflowState.ComputedWidth() != NS_INTRINSICSIZE) { contentSize.width = aReflowState.ComputedWidth(); } else { if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) { contentSize.width = NS_MIN(aReflowState.mComputedMaxWidth, contentSize.width); } if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) { contentSize.width = NS_MAX(aReflowState.mComputedMinWidth, contentSize.width); } } aDesiredSize.height = borderPadding.top + contentSize.height + borderPadding.bottom; aDesiredSize.width = contentSize.width + borderPadding.left + borderPadding.right; aDesiredSize.mOverflowAreas = overflowRects; aDesiredSize.UnionOverflowAreasWithDesiredBounds(); #ifdef DEBUG_roc printf("*** DONE PASS feasible=%d\n", allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)); #endif return allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus); }
// 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; }
// exported routine that both munder and msub share. // munder uses this when movablelimits is set. nsresult nsMathMLmsubFrame::PlaceSubScript (nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize, nsMathMLContainerFrame* aFrame, nscoord aUserSubScriptShift, nscoord aScriptSpace) { // force the scriptSpace to be atleast 1 pixel aScriptSpace = NS_MAX(nsPresContext::CSSPixelsToAppUnits(1), aScriptSpace); //////////////////////////////////// // Get the children's desired sizes nsBoundingMetrics bmBase, bmSubScript; nsHTMLReflowMetrics baseSize; nsHTMLReflowMetrics subScriptSize; nsIFrame* baseFrame = aFrame->GetFirstPrincipalChild(); nsIFrame* subScriptFrame = nullptr; if (baseFrame) subScriptFrame = baseFrame->GetNextSibling(); if (!baseFrame || !subScriptFrame || subScriptFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order if (aPlaceOrigin) { aFrame->ReportChildCountError(); } return aFrame->ReflowError(aRenderingContext, aDesiredSize); } GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript); // get the subdrop from the subscript font nscoord subDrop; GetSubDropFromChild(subScriptFrame, subDrop); // parameter v, Rule 18a, App. G, TeXbook nscoord minSubScriptShift = bmBase.descent + subDrop; ////////////////// // Place Children // get min subscript shift limit from x-height // = h(x) - 4/5 * sigma_5, Rule 18b, App. G, TeXbook nscoord xHeight = 0; nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(baseFrame, getter_AddRefs(fm)); xHeight = fm->XHeight(); nscoord minShiftFromXHeight = (nscoord) (bmSubScript.ascent - (4.0f/5.0f) * xHeight); // subScriptShift // = minimum amount to shift the subscript down set by user or from the font // = sub1 in TeX // = subscriptshift attribute * x-height nscoord subScriptShift, dummy; // get subScriptShift default from font GetSubScriptShifts (fm, subScriptShift, dummy); subScriptShift = NS_MAX(subScriptShift, aUserSubScriptShift); // get actual subscriptshift to be used // Rule 18b, App. G, TeXbook nscoord actualSubScriptShift = NS_MAX(minSubScriptShift,NS_MAX(subScriptShift,minShiftFromXHeight)); // get bounding box for base + subscript nsBoundingMetrics boundingMetrics; boundingMetrics.ascent = NS_MAX(bmBase.ascent, bmSubScript.ascent - actualSubScriptShift); boundingMetrics.descent = NS_MAX(bmBase.descent, bmSubScript.descent + actualSubScriptShift); // add aScriptSpace to the subscript's width boundingMetrics.width = bmBase.width + bmSubScript.width + aScriptSpace; boundingMetrics.leftBearing = bmBase.leftBearing; boundingMetrics.rightBearing = NS_MAX(bmBase.rightBearing, bmBase.width + NS_MAX(bmSubScript.width + aScriptSpace, bmSubScript.rightBearing)); aFrame->SetBoundingMetrics (boundingMetrics); // reflow metrics aDesiredSize.ascent = NS_MAX(baseSize.ascent, subScriptSize.ascent - actualSubScriptShift); aDesiredSize.height = aDesiredSize.ascent + NS_MAX(baseSize.height - baseSize.ascent, subScriptSize.height - subScriptSize.ascent + actualSubScriptShift); aDesiredSize.width = boundingMetrics.width; aDesiredSize.mBoundingMetrics = boundingMetrics; aFrame->SetReference(nsPoint(0, aDesiredSize.ascent)); if (aPlaceOrigin) { nscoord dx, dy; // now place the base ... dx = aFrame->MirrorIfRTL(aDesiredSize.width, baseSize.width, 0); dy = aDesiredSize.ascent - baseSize.ascent; FinishReflowChild (baseFrame, aPresContext, nullptr, baseSize, dx, dy, 0); // ... and subscript dx = aFrame->MirrorIfRTL(aDesiredSize.width, subScriptSize.width, bmBase.width); dy = aDesiredSize.ascent - (subScriptSize.ascent - actualSubScriptShift); FinishReflowChild (subScriptFrame, aPresContext, nullptr, subScriptSize, dx, dy, 0); } return NS_OK; }
void nsProgressFrame::ReflowBarFrame(nsIFrame* aBarFrame, nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { bool vertical = GetStyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL; nsHTMLReflowState reflowState(aPresContext, aReflowState, aBarFrame, nsSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE)); nscoord size = vertical ? aReflowState.ComputedHeight() : aReflowState.ComputedWidth(); nscoord xoffset = aReflowState.mComputedBorderPadding.left; nscoord yoffset = aReflowState.mComputedBorderPadding.top; double position; nsCOMPtr<nsIDOMHTMLProgressElement> progressElement = do_QueryInterface(mContent); progressElement->GetPosition(&position); // Force the bar's size to match the current progress. // When indeterminate, the progress' size will be 100%. if (position >= 0.0) { size *= position; } if (!vertical && GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { xoffset += aReflowState.ComputedWidth() - size; } // The bar size is fixed in these cases: // - the progress position is determined: the bar size is fixed according // to it's value. // - the progress position is indeterminate and the bar appearance should be // shown as native: the bar size is forced to 100%. // Otherwise (when the progress is indeterminate and the bar appearance isn't // native), the bar size isn't fixed and can be set by the author. if (position != -1 || ShouldUseNativeStyle()) { if (vertical) { // We want the bar to begin at the bottom. yoffset += aReflowState.ComputedHeight() - size; size -= reflowState.mComputedMargin.TopBottom() + reflowState.mComputedBorderPadding.TopBottom(); size = NS_MAX(size, 0); reflowState.SetComputedHeight(size); } else { size -= reflowState.mComputedMargin.LeftRight() + reflowState.mComputedBorderPadding.LeftRight(); size = NS_MAX(size, 0); reflowState.SetComputedWidth(size); } } else if (vertical) { // For vertical progress bars, we need to position the bar specificly when // the width isn't constrained (position == -1 and !ShouldUseNativeStyle()) // because aReflowState.ComputedHeight() - size == 0. yoffset += aReflowState.ComputedHeight() - reflowState.ComputedHeight(); } xoffset += reflowState.mComputedMargin.left; yoffset += reflowState.mComputedMargin.top; nsHTMLReflowMetrics barDesiredSize; ReflowChild(aBarFrame, aPresContext, barDesiredSize, reflowState, xoffset, yoffset, 0, aStatus); FinishReflowChild(aBarFrame, aPresContext, &reflowState, barDesiredSize, xoffset, yoffset, 0); }
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; }
NS_METHOD nsTableCellFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame", aReflowState.reason); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); #if defined DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState); #endif float p2t = aPresContext->ScaledPixelsToTwips(); // work around pixel rounding errors, round down to ensure we don't exceed the avail height in nscoord availHeight = aReflowState.availableHeight; if (NS_UNCONSTRAINEDSIZE != availHeight) { availHeight = nsTableFrame::RoundToPixel(availHeight, p2t, eAlwaysRoundDown); } nsresult rv = NS_OK; // see if a special height reflow needs to occur due to having a pct height if (!NeedSpecialReflow()) nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState); // this should probably be cached somewhere nsCompatibility compatMode = aPresContext->CompatibilityMode(); // Initialize out parameter if (aDesiredSize.mComputeMEW) { aDesiredSize.mMaxElementWidth = 0; } aStatus = NS_FRAME_COMPLETE; nsSize availSize(aReflowState.availableWidth, availHeight); PRBool noBorderBeforeReflow = GetContentEmpty() && GetStyleTableBorder()->mEmptyCells != NS_STYLE_TABLE_EMPTY_CELLS_SHOW; /* XXX: remove tableFrame when border-collapse inherits */ nsTableFrame* tableFrame = nsnull; rv = nsTableFrame::GetTableFrame(this, tableFrame); if (!tableFrame) ABORT1(NS_ERROR_NULL_POINTER); nsMargin borderPadding = aReflowState.mComputedPadding; nsMargin border; GetBorderWidth(p2t, border); if ((NS_UNCONSTRAINEDSIZE == availSize.width) || !noBorderBeforeReflow) { borderPadding += border; } nscoord topInset = borderPadding.top; nscoord rightInset = borderPadding.right; nscoord bottomInset = borderPadding.bottom; nscoord leftInset = borderPadding.left; // reduce available space by insets, if we're in a constrained situation if (NS_UNCONSTRAINEDSIZE!=availSize.width) availSize.width -= leftInset+rightInset; if (NS_UNCONSTRAINEDSIZE!=availSize.height) availSize.height -= topInset+bottomInset; PRBool isStyleChanged = PR_FALSE; if (eReflowReason_Incremental == aReflowState.reason) { // if the path has a reflow command then the cell must be the target of a style change nsHTMLReflowCommand* command = aReflowState.path->mReflowCommand; if (command) { // if there are other reflow commands targeted at the cell's block, these will // be subsumed by the style change reflow nsReflowType type; command->GetType(type); if (eReflowType_StyleChanged == type) { isStyleChanged = PR_TRUE; } else NS_ASSERTION(PR_FALSE, "table cell target of illegal incremental reflow type"); } // else the reflow command will be passed down to the child } // Try to reflow the child into the available space. It might not // fit or might need continuing. if (availSize.height < 0) availSize.height = 1; nsHTMLReflowMetrics kidSize(NS_UNCONSTRAINEDSIZE == aReflowState.availableWidth || aDesiredSize.mComputeMEW, aDesiredSize.mFlags); kidSize.width=kidSize.height=kidSize.ascent=kidSize.descent=0; SetPriorAvailWidth(aReflowState.availableWidth); nsIFrame* firstKid = mFrames.FirstChild(); NS_ASSERTION(firstKid, "Frame construction error, a table cell always has an inner cell frame"); nscoord computedPaginatedHeight = 0; if (aReflowState.mFlags.mSpecialHeightReflow || (HadSpecialReflow() && (eReflowReason_Incremental == aReflowState.reason))) { ((nsHTMLReflowState&)aReflowState).mComputedHeight = mRect.height - topInset - bottomInset; DISPLAY_REFLOW_CHANGE(); } else if (aPresContext->IsPaginated()) { computedPaginatedHeight = CalcUnpaginagedHeight(aPresContext, (nsTableCellFrame&)*this, *tableFrame, topInset + bottomInset); if (computedPaginatedHeight > 0) { ((nsHTMLReflowState&)aReflowState).mComputedHeight = computedPaginatedHeight; DISPLAY_REFLOW_CHANGE(); } } else { SetHasPctOverHeight(PR_FALSE); } // If it was a style change targeted at us, then reflow the child with a style change reason nsReflowReason reason = aReflowState.reason; if (isStyleChanged) { reason = eReflowReason_StyleChange; // the following could be optimized with a fair amount of effort tableFrame->SetNeedStrategyInit(PR_TRUE); } nsHTMLReflowState kidReflowState(aPresContext, aReflowState, firstKid, availSize, reason); // mIPercentHeightObserver is for non table related frames inside cells in quirks mode kidReflowState.mPercentHeightObserver = (eCompatibility_NavQuirks == compatMode) ? (nsIPercentHeightObserver *)this : nsnull; // Assume the inner child will stay positioned exactly where it is. Later in // VerticallyAlignChild() we'll move it if it turns out to be wrong. This // avoids excessive movement and is more stable nsPoint kidOrigin; if (isStyleChanged || (eReflowReason_Initial == aReflowState.reason) || (eReflowReason_StyleChange == aReflowState.reason)) { kidOrigin.MoveTo(leftInset, topInset); } else { // handle percent padding-left which was 0 during initial reflow if (eStyleUnit_Percent == aReflowState.mStylePadding->mPadding.GetLeftUnit()) { nsRect kidRect = firstKid->GetRect(); // only move in the x direction for the same reason as above kidOrigin.MoveTo(leftInset, kidRect.y); firstKid->SetPosition(nsPoint(leftInset, kidRect.y)); } kidOrigin = firstKid->GetPosition(); } #if defined DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugReflow(firstKid, (nsHTMLReflowState&)kidReflowState); #endif nscoord priorBlockHeight = GetLastBlockHeight(); ReflowChild(firstKid, aPresContext, kidSize, kidReflowState, kidOrigin.x, kidOrigin.y, 0, aStatus); SetLastBlockHeight(kidSize.height); if (isStyleChanged) { Invalidate(GetOverflowRect(), PR_FALSE); } #if defined DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugReflow(firstKid, (nsHTMLReflowState&)kidReflowState, &kidSize, aStatus); #endif #ifdef NS_DEBUG DebugCheckChildSize(firstKid, kidSize, availSize, (NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth)); #endif // 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode // see testcase "emptyCells.html" if ((0 == kidSize.width) || (0 == kidSize.height)) { // XXX why was this && SetContentEmpty(PR_TRUE); if (NS_UNCONSTRAINEDSIZE == kidReflowState.availableWidth && GetStyleTableBorder()->mEmptyCells != NS_STYLE_TABLE_EMPTY_CELLS_SHOW) { // need to reduce the insets by border if the cell is empty leftInset -= border.left; rightInset -= border.right; topInset -= border.top; bottomInset -= border.bottom; } } else { SetContentEmpty(PR_FALSE); if ((eReflowReason_Incremental == aReflowState.reason) && noBorderBeforeReflow) { // need to consider borders, since they were factored out above leftInset += border.left; rightInset += border.right; topInset += border.top; bottomInset += border.bottom; kidOrigin.MoveTo(leftInset, topInset); } } const nsStylePosition* pos = GetStylePosition(); // calculate the min cell width nscoord onePixel = NSIntPixelsToTwips(1, p2t); nscoord smallestMinWidth = 0; if (eCompatibility_NavQuirks == compatMode) { if ((pos->mWidth.GetUnit() != eStyleUnit_Coord) && (pos->mWidth.GetUnit() != eStyleUnit_Percent)) { if (PR_TRUE == GetContentEmpty()) { if (border.left > 0) smallestMinWidth += onePixel; if (border.right > 0) smallestMinWidth += onePixel; } } } PRInt32 colspan = tableFrame->GetEffectiveColSpan(*this); if (colspan > 1) { smallestMinWidth = PR_MAX(smallestMinWidth, colspan * onePixel); nscoord spacingX = tableFrame->GetCellSpacingX(); nscoord spacingExtra = spacingX * (colspan - 1); smallestMinWidth += spacingExtra; if (aReflowState.mComputedPadding.left > 0) { smallestMinWidth -= onePixel; } } if ((0 == kidSize.width) && (NS_UNCONSTRAINEDSIZE != kidReflowState.availableWidth)) { // empty content has to be forced to the assigned width for resize or incremental reflow kidSize.width = kidReflowState.availableWidth; } if (0 == kidSize.height) { if ((pos->mHeight.GetUnit() != eStyleUnit_Coord) && (pos->mHeight.GetUnit() != eStyleUnit_Percent)) { PRInt32 pixHeight = (eCompatibility_NavQuirks == compatMode) ? 1 : 0; kidSize.height = NSIntPixelsToTwips(pixHeight, p2t); } } // end 0 dimensioned cells kidSize.width = PR_MAX(kidSize.width, smallestMinWidth); if (!tableFrame->IsAutoLayout()) { // a cell in a fixed layout table is constrained to the avail width // if we need to shorten the cell the previous non overflowing block // will get some overflow area if (kidSize.width > availSize.width) { kidSize.width = availSize.width; firstKid->FinishAndStoreOverflow(&kidSize); } } //if (eReflowReason_Resize == aReflowState.reason) { // NS_ASSERTION(kidSize.width <= availSize.width, "child needed more space during resize reflow"); //} // Place the child FinishReflowChild(firstKid, aPresContext, &kidReflowState, kidSize, kidOrigin.x, kidOrigin.y, 0); // first, compute the height which can be set w/o being restricted by aMaxSize.height nscoord cellHeight = kidSize.height; if (NS_UNCONSTRAINEDSIZE != cellHeight) { cellHeight += topInset + bottomInset; // work around block rounding errors, round down to ensure we don't exceed the avail height in nsPixelRound roundMethod = (NS_UNCONSTRAINEDSIZE == availHeight) ? eAlwaysRoundUp : eAlwaysRoundDown; cellHeight = nsTableFrame::RoundToPixel(cellHeight, p2t, roundMethod); } // next determine the cell's width nscoord cellWidth = kidSize.width; // at this point, we've factored in the cell's style attributes // factor in border and padding if (NS_UNCONSTRAINEDSIZE != cellWidth) { cellWidth += leftInset + rightInset; } cellWidth = nsTableFrame::RoundToPixel(cellWidth, p2t); // work around block rounding errors // set the cell's desired size and max element size aDesiredSize.width = cellWidth; aDesiredSize.height = cellHeight; aDesiredSize.ascent = topInset; aDesiredSize.descent = bottomInset; aDesiredSize.ascent += kidSize.ascent; aDesiredSize.descent += kidSize.descent; // the overflow area will be computed when the child will be vertically aligned if (aDesiredSize.mComputeMEW) { aDesiredSize.mMaxElementWidth = PR_MAX(smallestMinWidth, kidSize.mMaxElementWidth); if (NS_UNCONSTRAINEDSIZE != aDesiredSize.mMaxElementWidth) { aDesiredSize.mMaxElementWidth = nsTableFrame::RoundToPixel( aDesiredSize.mMaxElementWidth + leftInset + rightInset, p2t); } } if (aDesiredSize.mFlags & NS_REFLOW_CALC_MAX_WIDTH) { aDesiredSize.mMaximumWidth = kidSize.mMaximumWidth; if (NS_UNCONSTRAINEDSIZE != aDesiredSize.mMaximumWidth) { aDesiredSize.mMaximumWidth += leftInset + rightInset; aDesiredSize.mMaximumWidth = nsTableFrame::RoundToPixel(aDesiredSize.mMaximumWidth, p2t); } // make sure the preferred width is at least as big as the max element width if (aDesiredSize.mComputeMEW) { aDesiredSize.mMaximumWidth = PR_MAX(aDesiredSize.mMaximumWidth, aDesiredSize.mMaxElementWidth); } } if (aReflowState.mFlags.mSpecialHeightReflow) { if (aDesiredSize.height > mRect.height) { // set a bit indicating that the pct height contents exceeded // the height that they could honor in the pass 2 reflow SetHasPctOverHeight(PR_TRUE); } if (NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) { aDesiredSize.height = mRect.height; } SetNeedSpecialReflow(PR_FALSE); SetHadSpecialReflow(PR_TRUE); } else if (HadSpecialReflow()) { if (eReflowReason_Incremental == aReflowState.reason) { // with an unconstrained height, if the block height value hasn't changed, // use the last height of the cell. if ((NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) && (GetLastBlockHeight() == priorBlockHeight)) { aDesiredSize.height = mRect.height; } } // XXX should probably call SetHadSpecialReflow(PR_FALSE) when things change so that // nothing inside the cell has a percent height, but it is not easy determining this } // remember the desired size for this reflow SetDesiredSize(aDesiredSize); #if defined DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState, &aDesiredSize, aStatus); #endif if (NS_UNCONSTRAINEDSIZE == aReflowState.availableWidth) { SetNeedPass2Reflow(PR_TRUE); } else if ((eReflowReason_Initial == aReflowState.reason) || (eReflowReason_Resize == aReflowState.reason)) { SetNeedPass2Reflow(PR_FALSE); } NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return NS_OK; }
NS_IMETHODIMP nsPageFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsPageFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); aStatus = NS_FRAME_COMPLETE; // initialize out parameter NS_ASSERTION(mFrames.FirstChild() && nsGkAtoms::pageContentFrame == mFrames.FirstChild()->GetType(), "pageFrame must have a pageContentFrame child"); // Resize our frame allowing it only to be as big as we are // XXX Pay attention to the page's border and padding... if (mFrames.NotEmpty()) { nsIFrame* frame = mFrames.FirstChild(); // When the reflow size is NS_UNCONSTRAINEDSIZE it means we are reflowing // a single page to print selection. So this means we want to use // NS_UNCONSTRAINEDSIZE without altering it nscoord avHeight; if (mPD->mReflowSize.height == NS_UNCONSTRAINEDSIZE) { avHeight = NS_UNCONSTRAINEDSIZE; } else { avHeight = mPD->mReflowSize.height - mPD->mReflowMargin.TopBottom(); } nsSize maxSize(mPD->mReflowSize.width - mPD->mReflowMargin.LeftRight(), avHeight); float scale = aPresContext->GetPageScale(); maxSize.width = NSToCoordCeil(maxSize.width / scale); if (maxSize.height != NS_UNCONSTRAINEDSIZE) { maxSize.height = NSToCoordCeil(maxSize.height / scale); } // Get the number of Twips per pixel from the PresContext nscoord onePixelInTwips = nsPresContext::CSSPixelsToAppUnits(1); // insurance against infinite reflow, when reflowing less than a pixel // XXX Shouldn't we do something more friendly when invalid margins // are set? if (maxSize.width < onePixelInTwips || maxSize.height < onePixelInTwips) { aDesiredSize.width = 0; aDesiredSize.height = 0; NS_WARNING("Reflow aborted; no space for content"); return NS_OK; } nsHTMLReflowState kidReflowState(aPresContext, aReflowState, frame, maxSize); kidReflowState.mFlags.mIsTopOfPage = PR_TRUE; kidReflowState.mFlags.mTableIsSplittable = PR_TRUE; // calc location of frame nscoord xc = mPD->mReflowMargin.left + mPD->mExtraMargin.left; nscoord yc = mPD->mReflowMargin.top + mPD->mExtraMargin.top; // Get the child's desired size ReflowChild(frame, aPresContext, aDesiredSize, kidReflowState, xc, yc, 0, aStatus); // Place and size the child FinishReflowChild(frame, aPresContext, &kidReflowState, aDesiredSize, xc, yc, 0); NS_ASSERTION(!NS_FRAME_IS_FULLY_COMPLETE(aStatus) || !frame->GetNextInFlow(), "bad child flow list"); } PR_PL(("PageFrame::Reflow %p ", this)); PR_PL(("[%d,%d][%d,%d]\n", aDesiredSize.width, aDesiredSize.height, aReflowState.availableWidth, aReflowState.availableHeight)); // Return our desired size aDesiredSize.width = aReflowState.availableWidth; if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) { aDesiredSize.height = aReflowState.availableHeight; } PR_PL(("PageFrame::Reflow %p ", this)); PR_PL(("[%d,%d]\n", aReflowState.availableWidth, aReflowState.availableHeight)); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return NS_OK; }
void nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsIFrame* aFirstKid, nsMargin aFocusPadding, nsReflowStatus& aStatus) { nsSize availSize(aReflowState.ComputedWidth(), NS_INTRINSICSIZE); // Indent the child inside us by the focus border. We must do this separate // from the regular border. availSize.width -= aFocusPadding.LeftRight(); // See whether out availSize's width is big enough. If it's smaller than our // intrinsic min width, that means that the kid wouldn't really fit; for a // better look in such cases we adjust the available width and our left // offset to allow the kid to spill left into our padding. nscoord xoffset = aFocusPadding.left + aReflowState.mComputedBorderPadding.left; nscoord extrawidth = GetMinWidth(aReflowState.rendContext) - aReflowState.ComputedWidth(); if (extrawidth > 0) { nscoord extraleft = extrawidth / 2; nscoord extraright = extrawidth - extraleft; NS_ASSERTION(extraright >=0, "How'd that happen?"); // Do not allow the extras to be bigger than the relevant padding extraleft = NS_MIN(extraleft, aReflowState.mComputedPadding.left); extraright = NS_MIN(extraright, aReflowState.mComputedPadding.right); xoffset -= extraleft; availSize.width += extraleft + extraright; } availSize.width = NS_MAX(availSize.width,0); nsHTMLReflowState reflowState(aPresContext, aReflowState, aFirstKid, availSize); ReflowChild(aFirstKid, aPresContext, aDesiredSize, reflowState, xoffset, aFocusPadding.top + aReflowState.mComputedBorderPadding.top, 0, aStatus); // calculate the min internal height so the contents gets centered correctly. // XXXbz this assumes border-box sizing. nscoord minInternalHeight = aReflowState.mComputedMinHeight - aReflowState.mComputedBorderPadding.TopBottom(); minInternalHeight = NS_MAX(minInternalHeight, 0); // center child vertically nscoord yoff = 0; if (aReflowState.ComputedHeight() != NS_INTRINSICSIZE) { yoff = (aReflowState.ComputedHeight() - aDesiredSize.height)/2; if (yoff < 0) { yoff = 0; } } else if (aDesiredSize.height < minInternalHeight) { yoff = (minInternalHeight - aDesiredSize.height) / 2; } // Place the child FinishReflowChild(aFirstKid, aPresContext, &reflowState, aDesiredSize, xoffset, yoff + aFocusPadding.top + aReflowState.mComputedBorderPadding.top, 0); if (aDesiredSize.ascent == nsHTMLReflowMetrics::ASK_FOR_BASELINE) aDesiredSize.ascent = aFirstKid->GetBaseline(); // Adjust the baseline by our offset (since we moved the child's // baseline by that much). aDesiredSize.ascent += yoff; }
NS_IMETHODIMP nsVideoFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsVideoFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsVideoFrame::Reflow: availSize=%d,%d", aReflowState.availableWidth, aReflowState.availableHeight)); NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); aStatus = NS_FRAME_COMPLETE; aMetrics.width = aReflowState.ComputedWidth(); aMetrics.height = aReflowState.ComputedHeight(); // stash this away so we can compute our inner area later mBorderPadding = aReflowState.mComputedBorderPadding; aMetrics.width += mBorderPadding.left + mBorderPadding.right; aMetrics.height += mBorderPadding.top + mBorderPadding.bottom; // Reflow the child frames. We may have up to two, an image frame // which is the poster, and a box frame, which is the video controls. for (nsIFrame *child = mFrames.FirstChild(); child; child = child->GetNextSibling()) { if (child->GetType() == nsGkAtoms::imageFrame) { // Reflow the poster frame. nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child); nsHTMLReflowMetrics kidDesiredSize; nsSize availableSize = nsSize(aReflowState.availableWidth, aReflowState.availableHeight); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, imageFrame, availableSize, aMetrics.width, aMetrics.height); if (ShouldDisplayPoster()) { kidReflowState.SetComputedWidth(aReflowState.ComputedWidth()); kidReflowState.SetComputedHeight(aReflowState.ComputedHeight()); } else { kidReflowState.SetComputedWidth(0); kidReflowState.SetComputedHeight(0); } ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowState, mBorderPadding.left, mBorderPadding.top, 0, aStatus); FinishReflowChild(imageFrame, aPresContext, &kidReflowState, kidDesiredSize, mBorderPadding.left, mBorderPadding.top, 0); } else if (child->GetType() == nsGkAtoms::boxFrame) { // Reflow the video controls frame. nsBoxLayoutState boxState(PresContext(), aReflowState.rendContext); nsBoxFrame::LayoutChildAt(boxState, child, nsRect(mBorderPadding.left, mBorderPadding.top, aReflowState.ComputedWidth(), aReflowState.ComputedHeight())); } } aMetrics.mOverflowArea.SetRect(0, 0, aMetrics.width, aMetrics.height); FinishAndStoreOverflow(&aMetrics); if (mRect.width != aMetrics.width || mRect.height != aMetrics.height) { Invalidate(nsRect(0, 0, mRect.width, mRect.height)); } NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsVideoFrame::Reflow: size=%d,%d", aMetrics.width, aMetrics.height)); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); return NS_OK; }
NS_METHOD nsTableOuterFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aOuterRS, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsTableOuterFrame"); DISPLAY_REFLOW(aPresContext, this, aOuterRS, aDesiredSize, aStatus); nsresult rv = NS_OK; PRUint8 captionSide = GetCaptionSide(); // Initialize out parameters aDesiredSize.width = aDesiredSize.height = 0; aStatus = NS_FRAME_COMPLETE; if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { // Set up our kids. They're already present, on an overflow list, // or there are none so we'll create them now MoveOverflowToChildList(aPresContext); } // Use longs to get more-aligned space. #define LONGS_IN_HTMLRS \ ((sizeof(nsHTMLReflowState) + sizeof(long) - 1) / sizeof(long)) long captionRSSpace[LONGS_IN_HTMLRS]; nsHTMLReflowState *captionRS = static_cast<nsHTMLReflowState*>((void*)captionRSSpace); long innerRSSpace[LONGS_IN_HTMLRS]; nsHTMLReflowState *innerRS = static_cast<nsHTMLReflowState*>((void*) innerRSSpace); nsRect origInnerRect = InnerTableFrame()->GetRect(); nsRect origInnerVisualOverflow = InnerTableFrame()->GetVisualOverflowRect(); PRBool innerFirstReflow = (InnerTableFrame()->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; nsRect origCaptionRect; nsRect origCaptionVisualOverflow; PRBool captionFirstReflow; if (mCaptionFrames.NotEmpty()) { origCaptionRect = mCaptionFrames.FirstChild()->GetRect(); origCaptionVisualOverflow = mCaptionFrames.FirstChild()->GetVisualOverflowRect(); captionFirstReflow = (mCaptionFrames.FirstChild()->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; } // ComputeAutoSize has to match this logic. if (captionSide == NO_SIDE) { // We don't have a caption. OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS, innerRSSpace, aOuterRS.ComputedWidth()); } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT || captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) { // nsTableCaptionFrame::ComputeAutoSize takes care of making side // captions small. Compute the caption's size first, and tell the // table to fit in what's left. OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS, captionRSSpace, aOuterRS.ComputedWidth()); nscoord innerAvailWidth = aOuterRS.ComputedWidth() - (captionRS->ComputedWidth() + captionRS->mComputedMargin.LeftRight() + captionRS->mComputedBorderPadding.LeftRight()); OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS, innerRSSpace, innerAvailWidth); } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { // Compute the table's size first, and then prevent the caption from // being wider unless it has to be. // // Note that CSS 2.1 (but not 2.0) says: // The width of the anonymous box is the border-edge width of the // table box inside it // We don't actually make our anonymous box that width (if we did, // it would break 'auto' margins), but this effectively does that. OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS, innerRSSpace, aOuterRS.ComputedWidth()); // It's good that CSS 2.1 says not to include margins, since we // can't, since they already been converted so they exactly // fill the available width (ignoring the margin on one side if // neither are auto). (We take advantage of that later when we call // GetCaptionOrigin, though.) nscoord innerBorderWidth = innerRS->ComputedWidth() + innerRS->mComputedBorderPadding.LeftRight(); OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS, captionRSSpace, innerBorderWidth); } else { NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE, "unexpected caption-side"); // Size the table and the caption independently. OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS, captionRSSpace, aOuterRS.ComputedWidth()); OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS, innerRSSpace, aOuterRS.ComputedWidth()); } // First reflow the caption. nsHTMLReflowMetrics captionMet; nsSize captionSize; nsMargin captionMargin; if (mCaptionFrames.NotEmpty()) { nsReflowStatus capStatus; // don't let the caption cause incomplete rv = OuterDoReflowChild(aPresContext, mCaptionFrames.FirstChild(), *captionRS, captionMet, capStatus); if (NS_FAILED(rv)) return rv; captionSize.width = captionMet.width; captionSize.height = captionMet.height; captionMargin = captionRS->mComputedMargin; // Now that we know the height of the caption, reduce the available height // for the table frame if we are height constrained and the caption is above // or below the inner table. if (NS_UNCONSTRAINEDSIZE != aOuterRS.availableHeight) { nscoord captionHeight = 0; switch (captionSide) { case NS_STYLE_CAPTION_SIDE_TOP: case NS_STYLE_CAPTION_SIDE_BOTTOM: { captionHeight = captionSize.height + captionMargin.TopBottom(); break; } case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE: { nsCollapsingMargin belowCaptionMargin; belowCaptionMargin.Include(captionMargin.bottom); belowCaptionMargin.Include(innerRS->mComputedMargin.top); captionHeight = captionSize.height + captionMargin.top + belowCaptionMargin.get(); break; } case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: { nsCollapsingMargin aboveCaptionMargin; aboveCaptionMargin.Include(captionMargin.top); aboveCaptionMargin.Include(innerRS->mComputedMargin.bottom); captionHeight = captionSize.height + captionMargin.bottom + aboveCaptionMargin.get(); break; } } innerRS->availableHeight = NS_MAX(0, innerRS->availableHeight - captionHeight); } } else { captionSize.SizeTo(0,0); captionMargin.SizeTo(0,0,0,0); } // Then, now that we know how much to reduce the width of the inner // table to account for side captions, reflow the inner table. nsHTMLReflowMetrics innerMet; rv = OuterDoReflowChild(aPresContext, InnerTableFrame(), *innerRS, innerMet, aStatus); if (NS_FAILED(rv)) return rv; nsSize innerSize; innerSize.width = innerMet.width; innerSize.height = innerMet.height; nsMargin innerMargin = innerRS->mComputedMargin; nsSize containSize = GetContainingBlockSize(aOuterRS); // Now that we've reflowed both we can place them. // XXXldb Most of the input variables here are now uninitialized! // XXX Need to recompute inner table's auto margins for the case of side // captions. (Caption's are broken too, but that should be fixed earlier.) if (mCaptionFrames.NotEmpty()) { nsPoint captionOrigin; GetCaptionOrigin(captionSide, containSize, innerSize, innerMargin, captionSize, captionMargin, captionOrigin); FinishReflowChild(mCaptionFrames.FirstChild(), aPresContext, captionRS, captionMet, captionOrigin.x, captionOrigin.y, 0); captionRS->~nsHTMLReflowState(); } // XXX If the height is constrained then we need to check whether // everything still fits... nsPoint innerOrigin; GetInnerOrigin(captionSide, containSize, captionSize, captionMargin, innerSize, innerMargin, innerOrigin); FinishReflowChild(InnerTableFrame(), aPresContext, innerRS, innerMet, innerOrigin.x, innerOrigin.y, 0); innerRS->~nsHTMLReflowState(); nsTableFrame::InvalidateFrame(InnerTableFrame(), origInnerRect, origInnerVisualOverflow, innerFirstReflow); if (mCaptionFrames.NotEmpty()) { nsTableFrame::InvalidateFrame(mCaptionFrames.FirstChild(), origCaptionRect, origCaptionVisualOverflow, captionFirstReflow); } UpdateReflowMetrics(captionSide, aDesiredSize, innerMargin, captionMargin); // Return our desired rect NS_FRAME_SET_TRUNCATION(aStatus, aOuterRS, aDesiredSize); return rv; }
// exported routine that both mover and msup share. // mover uses this when movablelimits is set. nsresult nsMathMLmsupFrame::PlaceSuperScript(nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize, nsMathMLContainerFrame* aFrame, nscoord aUserSupScriptShift, nscoord aScriptSpace) { // force the scriptSpace to be at least 1 pixel nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); aScriptSpace = std::max(onePixel, aScriptSpace); //////////////////////////////////// // Get the children's desired sizes nsHTMLReflowMetrics baseSize; nsHTMLReflowMetrics supScriptSize; nsBoundingMetrics bmBase, bmSupScript; nsIFrame* supScriptFrame = nullptr; nsIFrame* baseFrame = aFrame->GetFirstPrincipalChild(); if (baseFrame) supScriptFrame = baseFrame->GetNextSibling(); if (!baseFrame || !supScriptFrame || supScriptFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order if (aPlaceOrigin) { aFrame->ReportChildCountError(); } return aFrame->ReflowError(aRenderingContext, aDesiredSize); } GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript); // 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 min supscript shift limit from x-height // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook nscoord xHeight = 0; nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(baseFrame, getter_AddRefs(fm)); xHeight = fm->XHeight(); nscoord minShiftFromXHeight = (nscoord) (bmSupScript.descent + (1.0f/4.0f) * xHeight); nscoord italicCorrection; GetItalicCorrection(bmBase, italicCorrection); // 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 = std::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 actual supscriptshift to be used // Rule 18c, App. G, TeXbook nscoord actualSupScriptShift = std::max(minSupScriptShift,std::max(supScriptShift,minShiftFromXHeight)); // bounding box nsBoundingMetrics boundingMetrics; boundingMetrics.ascent = std::max(bmBase.ascent, (bmSupScript.ascent + actualSupScriptShift)); boundingMetrics.descent = std::max(bmBase.descent, (bmSupScript.descent - actualSupScriptShift)); // leave aScriptSpace after superscript // 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 italicCorrection += onePixel; boundingMetrics.width = bmBase.width + italicCorrection + bmSupScript.width + aScriptSpace; boundingMetrics.leftBearing = bmBase.leftBearing; boundingMetrics.rightBearing = bmBase.width + italicCorrection + bmSupScript.rightBearing; aFrame->SetBoundingMetrics(boundingMetrics); // reflow metrics aDesiredSize.ascent = std::max(baseSize.ascent, (supScriptSize.ascent + actualSupScriptShift)); aDesiredSize.height = aDesiredSize.ascent + std::max(baseSize.height - baseSize.ascent, (supScriptSize.height - supScriptSize.ascent - actualSupScriptShift)); aDesiredSize.width = boundingMetrics.width; aDesiredSize.mBoundingMetrics = boundingMetrics; aFrame->SetReference(nsPoint(0, aDesiredSize.ascent)); if (aPlaceOrigin) { nscoord dx, dy; // now place the base ... dx = aFrame->MirrorIfRTL(aDesiredSize.width, baseSize.width, 0); dy = aDesiredSize.ascent - baseSize.ascent; FinishReflowChild (baseFrame, aPresContext, nullptr, baseSize, dx, dy, 0); // ... and supscript dx = aFrame->MirrorIfRTL(aDesiredSize.width, supScriptSize.width, bmBase.width + italicCorrection); dy = aDesiredSize.ascent - (supScriptSize.ascent + actualSupScriptShift); FinishReflowChild (supScriptFrame, aPresContext, nullptr, supScriptSize, dx, dy, 0); } return NS_OK; }
NS_IMETHODIMP nsPageContentFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsPageContentFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); aStatus = NS_FRAME_COMPLETE; // initialize out parameter nsresult rv = NS_OK; if (GetPrevInFlow() && (GetStateBits() & NS_FRAME_FIRST_REFLOW)) { nsresult rv = aPresContext->PresShell()->FrameConstructor() ->ReplicateFixedFrames(this); NS_ENSURE_SUCCESS(rv, rv); } // Set our size up front, since some parts of reflow depend on it // being already set. Note that the computed height may be // unconstrained; that's ok. Consumers should watch out for that. SetSize(nsSize(aReflowState.availableWidth, aReflowState.availableHeight)); // A PageContentFrame must always have one child: the canvas frame. // Resize our frame allowing it only to be as big as we are // XXX Pay attention to the page's border and padding... if (mFrames.NotEmpty()) { nsIFrame* frame = mFrames.FirstChild(); nsSize maxSize(aReflowState.availableWidth, aReflowState.availableHeight); nsHTMLReflowState kidReflowState(aPresContext, aReflowState, frame, maxSize); kidReflowState.SetComputedHeight(aReflowState.availableHeight); mPD->mPageContentSize = aReflowState.availableWidth; // Reflow the page content area rv = ReflowChild(frame, aPresContext, aDesiredSize, kidReflowState, 0, 0, 0, aStatus); NS_ENSURE_SUCCESS(rv, rv); // The document element's background should cover the entire canvas, so // take into account the combined area and any space taken up by // absolutely positioned elements nsMargin padding(0,0,0,0); // XXXbz this screws up percentage padding (sets padding to zero // in the percentage padding case) kidReflowState.mStylePadding->GetPadding(padding); // This is for shrink-to-fit, and therefore we want to use the // scrollable overflow, since the purpose of shrink to fit is to // make the content that ought to be reachable (represented by the // scrollable overflow) fit in the page. if (frame->HasOverflowAreas()) { // The background covers the content area and padding area, so check // for children sticking outside the child frame's padding edge nscoord xmost = aDesiredSize.ScrollableOverflow().XMost(); if (xmost > aDesiredSize.width) { mPD->mPageContentXMost = xmost + kidReflowState.mStyleBorder->GetActualBorderWidth(NS_SIDE_RIGHT) + padding.right; } } // Place and size the child FinishReflowChild(frame, aPresContext, &kidReflowState, aDesiredSize, 0, 0, 0); NS_ASSERTION(aPresContext->IsDynamic() || !NS_FRAME_IS_FULLY_COMPLETE(aStatus) || !frame->GetNextInFlow(), "bad child flow list"); } // Reflow our fixed frames nsReflowStatus fixedStatus = NS_FRAME_COMPLETE; mFixedContainer.Reflow(this, aPresContext, aReflowState, fixedStatus, aReflowState.availableWidth, aReflowState.availableHeight, PR_FALSE, PR_TRUE, PR_TRUE, // XXX could be optimized nsnull /* ignore overflow */); NS_ASSERTION(NS_FRAME_IS_COMPLETE(fixedStatus), "fixed frames can be truncated, but not incomplete"); // Return our desired size aDesiredSize.width = aReflowState.availableWidth; if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE) { aDesiredSize.height = aReflowState.availableHeight; } FinishAndStoreOverflow(&aDesiredSize); NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 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; }
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; }
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; }
NS_IMETHODIMP nsMathMLmfencedFrame::Reflow(nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { nsresult rv; aDesiredSize.width = aDesiredSize.height = 0; aDesiredSize.ascent = 0; aDesiredSize.mBoundingMetrics = nsBoundingMetrics(); PRInt32 i; const nsStyleFont* font = GetStyleFont(); nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); aReflowState.rendContext->SetFont(fm); nscoord axisHeight, em; GetAxisHeight(*aReflowState.rendContext, fm, axisHeight); GetEmHeight(fm, em); // leading to be left at the top and the bottom of stretched chars nscoord leading = NSToCoordRound(0.2f * em); ///////////// // Reflow children // Asking each child to cache its bounding metrics // Note that we don't use the base method nsMathMLContainerFrame::Reflow() // because we want to stretch our fences, separators and stretchy frames using // the *same* initial aDesiredSize.mBoundingMetrics. If we were to use the base // method here, our stretchy frames will be stretched and placed, and we may // end up stretching our fences/separators with a different aDesiredSize. // XXX The above decision was revisited in bug 121748 and this code can be // refactored to use nsMathMLContainerFrame::Reflow() at some stage. nsReflowStatus childStatus; nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE); nsIFrame* firstChild = GetFirstChild(nsnull); nsIFrame* childFrame = firstChild; nscoord ascent = 0, descent = 0; if (firstChild || mOpenChar || mCloseChar || mSeparatorsCount > 0) { // We use the ASCII metrics to get our minimum height. This way, // if we have borders or a background, they will fit better with // other elements on the line. ascent = fm->MaxAscent(); descent = fm->MaxDescent(); } while (childFrame) { 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(firstChild, childFrame); return rv; } SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize, childDesiredSize.mBoundingMetrics); nscoord childDescent = childDesiredSize.height - childDesiredSize.ascent; if (descent < childDescent) descent = childDescent; if (ascent < childDesiredSize.ascent) ascent = childDesiredSize.ascent; childFrame = childFrame->GetNextSibling(); } ///////////// // Ask stretchy children to stretch themselves nsBoundingMetrics containerSize; nsStretchDirection stretchDir = NS_STRETCH_DIRECTION_VERTICAL; GetPreferredStretchSize(*aReflowState.rendContext, 0, /* i.e., without embellishments */ stretchDir, containerSize); childFrame = firstChild; while (childFrame) { nsIMathMLFrame* mathmlChild = do_QueryFrame(childFrame); if (mathmlChild) { nsHTMLReflowMetrics childDesiredSize; // retrieve the metrics that was stored at the previous pass GetReflowAndBoundingMetricsFor(childFrame, childDesiredSize, childDesiredSize.mBoundingMetrics); mathmlChild->Stretch(*aReflowState.rendContext, stretchDir, containerSize, childDesiredSize); // store the updated metrics SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize, childDesiredSize.mBoundingMetrics); nscoord childDescent = childDesiredSize.height - childDesiredSize.ascent; if (descent < childDescent) descent = childDescent; if (ascent < childDesiredSize.ascent) ascent = childDesiredSize.ascent; } childFrame = childFrame->GetNextSibling(); } // bug 121748: for surrounding fences & separators, use a size that covers everything GetPreferredStretchSize(*aReflowState.rendContext, STRETCH_CONSIDER_EMBELLISHMENTS, stretchDir, containerSize); ////////////////////////////////////////// // Prepare the opening fence, separators, and closing fence, and // adjust the origin of children. // we need to center around the axis nscoord delta = NS_MAX(containerSize.ascent - axisHeight, containerSize.descent + axisHeight); containerSize.ascent = delta + axisHeight; containerSize.descent = delta - axisHeight; ///////////////// // opening fence ... ReflowChar(aPresContext, *aReflowState.rendContext, mOpenChar, NS_MATHML_OPERATOR_FORM_PREFIX, font->mScriptLevel, axisHeight, leading, em, containerSize, ascent, descent); ///////////////// // separators ... for (i = 0; i < mSeparatorsCount; i++) { ReflowChar(aPresContext, *aReflowState.rendContext, &mSeparatorsChar[i], NS_MATHML_OPERATOR_FORM_INFIX, font->mScriptLevel, axisHeight, leading, em, containerSize, ascent, descent); } ///////////////// // closing fence ... ReflowChar(aPresContext, *aReflowState.rendContext, mCloseChar, NS_MATHML_OPERATOR_FORM_POSTFIX, font->mScriptLevel, axisHeight, leading, em, containerSize, ascent, descent); ////////////////// // Adjust the origins of each child. // and update our bounding metrics i = 0; nscoord dx = 0; nsBoundingMetrics bm; PRBool firstTime = PR_TRUE; if (mOpenChar) { PlaceChar(mOpenChar, ascent, bm, dx); aDesiredSize.mBoundingMetrics = bm; firstTime = PR_FALSE; } childFrame = firstChild; while (childFrame) { nsHTMLReflowMetrics childSize; GetReflowAndBoundingMetricsFor(childFrame, childSize, bm); if (firstTime) { firstTime = PR_FALSE; aDesiredSize.mBoundingMetrics = bm; } else aDesiredSize.mBoundingMetrics += bm; FinishReflowChild(childFrame, aPresContext, nsnull, childSize, dx, ascent - childSize.ascent, 0); dx += childSize.width; if (i < mSeparatorsCount) { PlaceChar(&mSeparatorsChar[i], ascent, bm, dx); aDesiredSize.mBoundingMetrics += bm; } i++; childFrame = childFrame->GetNextSibling(); } if (mCloseChar) { PlaceChar(mCloseChar, ascent, bm, dx); if (firstTime) aDesiredSize.mBoundingMetrics = bm; else aDesiredSize.mBoundingMetrics += bm; } aDesiredSize.width = aDesiredSize.mBoundingMetrics.width; aDesiredSize.height = ascent + descent; aDesiredSize.ascent = ascent; SetBoundingMetrics(aDesiredSize.mBoundingMetrics); SetReference(nsPoint(0, aDesiredSize.ascent)); // see if we should fix the spacing FixInterFrameSpacing(aDesiredSize); // Finished with these: ClearSavedChildMetrics(); // Set our overflow area GatherAndStoreOverflow(&aDesiredSize); aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 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 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; }
/* 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; }