/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is Mozilla MathML Project. * * The Initial Developer of the Original Code is The University Of * Queensland. Portions created by The University Of Queensland are * Copyright (C) 1999 The University Of Queensland. All Rights Reserved. * * Contributor(s): * Roger B. Sidje * David J. Fiddes * Vilya Harvey * Shyjan Mahamud */ #include "nsCOMPtr.h" #include "nsFrame.h" #include "nsIPresContext.h" #include "nsUnitConversion.h" #include "nsStyleContext.h" #include "nsStyleConsts.h" #include "nsIRenderingContext.h" #include "nsIFontMetrics.h" #include "nsMathMLmsqrtFrame.h" // // and -- form a radical - implementation // //NOTE: // The code assumes that TeX fonts are picked. // There is no fall-back to draw the branches of the sqrt explicitly // in the case where TeX fonts are not there. In general, there are no // fall-back(s) in MathML when some (freely-downloadable) fonts are missing. // Otherwise, this will add much work and unnecessary complexity to the core // MathML engine. Assuming that authors have the free fonts is part of the // deal. We are not responsible for cases of misconfigurations out there. // additional style context to be used by our MathMLChar. #define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0 static const PRUnichar kSqrChar = PRUnichar(0x221A); nsresult NS_NewMathMLmsqrtFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame) { NS_PRECONDITION(aNewFrame, "null OUT ptr"); if (nsnull == aNewFrame) { return NS_ERROR_NULL_POINTER; } nsMathMLmsqrtFrame* it = new (aPresShell) nsMathMLmsqrtFrame; if (nsnull == it) { return NS_ERROR_OUT_OF_MEMORY; } *aNewFrame = it; return NS_OK; } nsMathMLmsqrtFrame::nsMathMLmsqrtFrame() : mSqrChar(), mBarRect() { } nsMathMLmsqrtFrame::~nsMathMLmsqrtFrame() { } NS_IMETHODIMP nsMathMLmsqrtFrame::Init(nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame* aParent, nsStyleContext* aContext, nsIFrame* aPrevInFlow) { nsresult rv = nsMathMLContainerFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow); // No need to tract the style context given to our MathML char. // The Style System will use Get/SetAdditionalStyleContext() to keep it // up-to-date if dynamic changes arise. nsAutoString sqrChar; sqrChar.Assign(kSqrChar); mSqrChar.SetData(aPresContext, sqrChar); ResolveMathMLCharStyle(aPresContext, mContent, mStyleContext, &mSqrChar, PR_TRUE); return rv; } NS_IMETHODIMP nsMathMLmsqrtFrame::InheritAutomaticData(nsIPresContext* aPresContext, nsIFrame* aParent) { // let the base class get the default from our parent nsMathMLContainerFrame::InheritAutomaticData(aPresContext, aParent); mPresentationData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY; return NS_OK; } NS_IMETHODIMP nsMathMLmsqrtFrame::TransmitAutomaticData(nsIPresContext* aPresContext) { // 1. The REC says: // The element leaves both attributes [displaystyle and scriptlevel] // unchanged within all its arguments. // 2. The TeXBook (Ch 17. p.141) says that \sqrt is cramped UpdatePresentationDataFromChildAt(aPresContext, 0, -1, 0, NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED); return NS_OK; } NS_IMETHODIMP nsMathMLmsqrtFrame::Paint(nsIPresContext* aPresContext, nsIRenderingContext& aRenderingContext, const nsRect& aDirtyRect, nsFramePaintLayer aWhichLayer, PRUint32 aFlags) { ///////////// // paint the content we are square-rooting nsresult rv = nsMathMLContainerFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer); ///////////// // paint the sqrt symbol if (!NS_MATHML_HAS_ERROR(mPresentationData.flags)) { mSqrChar.Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer, this); if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) { // paint the overline bar const nsStyleColor *color = NS_STATIC_CAST(const nsStyleColor*, mStyleContext->GetStyleData(eStyleStruct_Color)); aRenderingContext.SetColor(color->mColor); aRenderingContext.FillRect(mBarRect); } #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX) // for visual debug if (NS_MATHML_PAINT_BOUNDING_METRICS(mPresentationData.flags)) { nsRect rect; mSqrChar.GetRect(rect); nsBoundingMetrics bm; mSqrChar.GetBoundingMetrics(bm); aRenderingContext.SetColor(NS_RGB(255,0,0)); nscoord x = rect.x + bm.leftBearing; nscoord y = rect.y; nscoord w = bm.rightBearing - bm.leftBearing; nscoord h = bm.ascent + bm.descent; aRenderingContext.DrawRect(x,y,w,h); } #endif } return rv; } NS_IMETHODIMP nsMathMLmsqrtFrame::Reflow(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { /////////////// // Let the base class format our content like an inferred mrow nsHTMLReflowMetrics baseSize(aDesiredSize); nsresult rv = nsMathMLContainerFrame::Reflow(aPresContext, baseSize, aReflowState, aStatus); //NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); if (NS_FAILED(rv)) return rv; nsBoundingMetrics bmSqr, bmBase; bmBase = baseSize.mBoundingMetrics; //////////// // Prepare the radical symbol and the overline bar nsIRenderingContext& renderingContext = *aReflowState.rendContext; const nsStyleFont *font = NS_STATIC_CAST(const nsStyleFont*, mStyleContext->GetStyleData(eStyleStruct_Font)); renderingContext.SetFont(font->mFont, nsnull); nsCOMPtr fm; renderingContext.GetFontMetrics(*getter_AddRefs(fm)); nscoord ruleThickness, leading, em; GetRuleThickness(renderingContext, fm, ruleThickness); // get the leading to be left at the top of the resulting frame // this seems more reliable than using fm->GetLeading() on suspicious fonts GetEmHeight(fm, em); leading = nscoord(0.2f * em); // Rule 11, App. G, TeXbook // psi = clearance between rule and content nscoord phi = 0, psi = 0; if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) fm->GetXHeight(phi); else phi = ruleThickness; psi = ruleThickness + phi/4; // Stretch the radical symbol to the appropriate height if it is not big enough. nsBoundingMetrics contSize = bmBase; contSize.ascent = ruleThickness; contSize.descent = bmBase.ascent + bmBase.descent + psi; // height(radical) should be >= height(base) + psi + ruleThickness nsBoundingMetrics radicalSize; mSqrChar.Stretch(aPresContext, renderingContext, NS_STRETCH_DIRECTION_VERTICAL, contSize, radicalSize, NS_STRETCH_LARGER); // radicalSize have changed at this point, and should match with // the bounding metrics of the char mSqrChar.GetBoundingMetrics(bmSqr); // According to TeX, the ascent of the returned radical should be // the thickness of the overline ruleThickness = bmSqr.ascent; // make sure that the rule appears on the screen float p2t; aPresContext->GetScaledPixelsToTwips(&p2t); nscoord onePixel = NSIntPixelsToTwips(1, p2t); if (ruleThickness < onePixel) { ruleThickness = onePixel; } // adjust clearance psi to get an exact number of pixels -- this // gives a nicer & uniform look on stacked radicals (bug 130282) nscoord delta = psi % onePixel; if (delta) psi += onePixel - delta; // round up nscoord dx = 0, dy = 0; // place the radical symbol and the radical bar dy = leading; // leave a leading at the top mSqrChar.SetRect(nsRect(dx, dy, bmSqr.width, bmSqr.ascent + bmSqr.descent)); dx = bmSqr.width; mBarRect.SetRect(dx, dy, bmBase.width, ruleThickness); // Update the desired size for the container. // the baseline will be that of the base. mBoundingMetrics.ascent = bmBase.ascent + psi + ruleThickness; mBoundingMetrics.descent = PR_MAX(bmBase.descent, (bmSqr.descent - (bmBase.ascent + psi))); mBoundingMetrics.width = bmSqr.width + bmBase.width; mBoundingMetrics.leftBearing = bmSqr.leftBearing; mBoundingMetrics.rightBearing = bmSqr.width + PR_MAX(bmBase.width, bmBase.rightBearing); // take also care of the rule aDesiredSize.ascent = mBoundingMetrics.ascent + leading; aDesiredSize.descent = PR_MAX(baseSize.descent, (mBoundingMetrics.descent + ruleThickness)); aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent; aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.ascent; ////////////////// // Adjust the origins to leave room for the sqrt char and the overline bar nsPoint origin; dx = radicalSize.width; dy = aDesiredSize.ascent - baseSize.ascent; nsIFrame* childFrame = mFrames.FirstChild(); while (childFrame) { childFrame->GetOrigin(origin); childFrame->MoveTo(aPresContext, origin.x + dx, origin.y + dy); childFrame->GetNextSibling(&childFrame); } if (aDesiredSize.mComputeMEW) { aDesiredSize.mMaxElementWidth = aDesiredSize.width; } aDesiredSize.mBoundingMetrics = mBoundingMetrics; aStatus = NS_FRAME_COMPLETE; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); return NS_OK; } // ---------------------- // the Style System will use these to pass the proper style context to our MathMLChar nsStyleContext* nsMathMLmsqrtFrame::GetAdditionalStyleContext(PRInt32 aIndex) const { switch (aIndex) { case NS_SQR_CHAR_STYLE_CONTEXT_INDEX: return mSqrChar.GetStyleContext(); break; default: return nsnull; } } void nsMathMLmsqrtFrame::SetAdditionalStyleContext(PRInt32 aIndex, nsStyleContext* aStyleContext) { switch (aIndex) { case NS_SQR_CHAR_STYLE_CONTEXT_INDEX: mSqrChar.SetStyleContext(aStyleContext); break; } }