gecko-dev/layout/mathml/base/src/nsMathMLmrootFrame.cpp

400 lines
14 KiB
C++
Raw Normal View History

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* 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 Initial Developer are Copyright (C) 1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Roger B. Sidje <rbs@maths.uq.edu.au>
* David J. Fiddes <D.J.Fiddes@hw.ac.uk>
1999-11-22 12:35:36 +00:00
* Vilya Harvey <vilya@nag.co.uk>
2000-03-28 09:38:24 +00:00
* Shyjan Mahamud <mahamud@cs.cmu.edu>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsCOMPtr.h"
#include "nsFrame.h"
#include "nsPresContext.h"
#include "nsUnitConversion.h"
#include "nsStyleContext.h"
#include "nsStyleConsts.h"
#include "nsIRenderingContext.h"
#include "nsIFontMetrics.h"
#include "nsMathMLmrootFrame.h"
//
2000-03-28 09:38:24 +00:00
// <msqrt> and <mroot> -- 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.
2000-03-28 09:38:24 +00:00
// additional style context to be used by our MathMLChar.
#define NS_SQR_CHAR_STYLE_CONTEXT_INDEX 0
static const PRUnichar kSqrChar = PRUnichar(0x221A);
nsIFrame*
NS_NewMathMLmrootFrame(nsIPresShell* aPresShell)
{
return new (aPresShell) nsMathMLmrootFrame;
}
1999-11-22 12:35:36 +00:00
nsMathMLmrootFrame::nsMathMLmrootFrame() :
mSqrChar(),
2000-03-28 09:38:24 +00:00
mBarRect()
{
}
nsMathMLmrootFrame::~nsMathMLmrootFrame()
{
}
NS_IMETHODIMP
nsMathMLmrootFrame::Init(nsPresContext* aPresContext,
2000-03-28 09:38:24 +00:00
nsIContent* aContent,
nsIFrame* aParent,
nsStyleContext* aContext,
2000-03-28 09:38:24 +00:00
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);
2000-03-28 09:38:24 +00:00
return rv;
}
1999-11-22 12:35:36 +00:00
NS_IMETHODIMP
nsMathMLmrootFrame::TransmitAutomaticData()
{
// 1. The REC says:
// The <mroot> element increments scriptlevel by 2, and sets displaystyle to
// "false", within index, but leaves both attributes unchanged within base.
// 2. The TeXbook (Ch 17. p.141) says \sqrt is compressed
UpdatePresentationDataFromChildAt(1, 1, 2,
~NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED,
NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED);
UpdatePresentationDataFromChildAt(0, 0, 0,
NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED);
return NS_OK;
}
1999-11-22 12:35:36 +00:00
NS_IMETHODIMP
nsMathMLmrootFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
1999-11-22 12:35:36 +00:00
{
/////////////
// paint the content we are square-rooting
nsresult rv = nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
NS_ENSURE_SUCCESS(rv, rv);
2000-03-28 09:38:24 +00:00
/////////////
// paint the sqrt symbol
if (!NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
rv = mSqrChar.Display(aBuilder, this, aLists);
NS_ENSURE_SUCCESS(rv, rv);
rv = DisplayBar(aBuilder, this, mBarRect, aLists);
NS_ENSURE_SUCCESS(rv, rv);
2000-03-28 09:38:24 +00:00
#if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
// for visual debug
nsRect rect;
mSqrChar.GetRect(rect);
nsBoundingMetrics bm;
mSqrChar.GetBoundingMetrics(bm);
rv = DisplayBoundingMetrics(aBuilder, this, rect.TopLeft(), bm, aLists);
2000-03-28 09:38:24 +00:00
#endif
1999-11-22 12:35:36 +00:00
}
return rv;
}
NS_IMETHODIMP
nsMathMLmrootFrame::Reflow(nsPresContext* aPresContext,
1999-11-22 12:35:36 +00:00
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
nsresult rv = NS_OK;
// ask our children to compute their bounding metrics
nsHTMLReflowMetrics childDesiredSize(aDesiredSize.mComputeMEW,
aDesiredSize.mFlags | NS_REFLOW_CALC_BOUNDING_METRICS);
1999-11-22 12:35:36 +00:00
nsSize availSize(aReflowState.mComputedWidth, aReflowState.mComputedHeight);
2000-03-28 09:38:24 +00:00
nsReflowStatus childStatus;
aDesiredSize.width = aDesiredSize.height = 0;
aDesiredSize.ascent = aDesiredSize.descent = 0;
nsBoundingMetrics bmSqr, bmBase, bmIndex;
nsIRenderingContext& renderingContext = *aReflowState.rendContext;
1999-11-22 12:35:36 +00:00
//////////////////
// Reflow Children
PRInt32 count = 0;
2000-03-28 09:38:24 +00:00
nsIFrame* baseFrame = nsnull;
nsIFrame* indexFrame = nsnull;
nsHTMLReflowMetrics baseSize(nsnull);
nsHTMLReflowMetrics indexSize(nsnull);
1999-11-22 12:35:36 +00:00
nsIFrame* childFrame = mFrames.FirstChild();
while (childFrame) {
nsReflowReason reason = (childFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)
? eReflowReason_Initial : aReflowState.reason;
nsHTMLReflowState childReflowState(aPresContext, aReflowState,
childFrame, availSize, reason);
rv = ReflowChild(childFrame, aPresContext,
childDesiredSize, childReflowState, childStatus);
//NS_ASSERTION(NS_FRAME_IS_COMPLETE(childStatus), "bad status");
if (NS_FAILED(rv)) 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;
1999-11-22 12:35:36 +00:00
}
count++;
2003-07-08 11:00:00 +00:00
childFrame = childFrame->GetNextSibling();
1999-11-22 12:35:36 +00:00
}
if (aDesiredSize.mComputeMEW) {
aDesiredSize.mMaxElementWidth = childDesiredSize.mMaxElementWidth;
}
if (2 != count) {
2000-03-28 09:38:24 +00:00
// report an error, encourage people to get their markups in order
NS_WARNING("invalid markup");
return ReflowError(renderingContext, aDesiredSize);
2000-03-28 09:38:24 +00:00
}
1999-11-22 12:35:36 +00:00
////////////
// Prepare the radical symbol and the overline bar
renderingContext.SetFont(GetStyleFont()->mFont, nsnull);
1999-11-22 12:35:36 +00:00
nsCOMPtr<nsIFontMetrics> fm;
renderingContext.GetFontMetrics(*getter_AddRefs(fm));
2000-03-28 09:38:24 +00:00
2002-01-05 01:08:09 +00:00
nscoord ruleThickness, leading, em;
2000-03-28 09:38:24 +00:00
GetRuleThickness(renderingContext, fm, ruleThickness);
nsBoundingMetrics bmOne;
renderingContext.GetBoundingMetrics(NS_LITERAL_STRING("1").get(), 1, bmOne);
// get the leading to be left at the top of the resulting frame
// this seems more reliable than using fm->GetLeading() on suspicious fonts
2002-01-05 01:08:09 +00:00
GetEmHeight(fm, em);
leading = nscoord(0.2f * em);
2000-03-28 09:38:24 +00:00
// Rule 11, App. G, TeXbook
// psi = clearance between rule and content
nscoord phi = 0, psi = 0;
if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags))
fm->GetXHeight(phi);
else
phi = ruleThickness;
psi = ruleThickness + phi/4;
// built-in: adjust clearance psi to emulate \mathstrut using '1' (TexBook, p.131)
if (bmOne.ascent > bmBase.ascent)
psi += bmOne.ascent - bmBase.ascent;
2000-03-28 09:38:24 +00:00
// 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;
2000-03-28 09:38:24 +00:00
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 on screen
nscoord onePixel = aPresContext->IntScaledPixelsToTwips(1);
if (ruleThickness < onePixel) {
ruleThickness = onePixel;
}
2000-03-28 09:38:24 +00:00
// 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
2000-03-28 09:38:24 +00:00
// Update the desired size for the container (like msqrt, index is not yet included)
2000-03-28 09:38:24 +00:00
// 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;
2000-03-28 09:38:24 +00:00
aDesiredSize.descent =
PR_MAX(baseSize.descent, (mBoundingMetrics.descent + ruleThickness));
aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
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;
aDesiredSize.ascent = mBoundingMetrics.ascent + leading;
2000-03-28 09:38:24 +00:00
aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent;
1999-11-22 12:35:36 +00:00
}
2000-03-28 09:38:24 +00:00
// the index is tucked in closer to the radical while making sure
// that the kern does not make the index and radical collide
nscoord dxIndex, dxSqr, dx, dy;
2000-03-28 09:38:24 +00:00
nscoord xHeight = 0;
fm->GetXHeight(xHeight);
nscoord indexRadicalKern = NSToCoordRound(1.35f * xHeight);
if (indexRadicalKern > bmIndex.width) {
dxIndex = indexRadicalKern - bmIndex.width;
dxSqr = 0;
}
else {
dxIndex = 0;
dxSqr = bmIndex.width - indexRadicalKern;
}
// avoid collision by leaving a minimun space between index and radical
nscoord minimumClearance = bmSqr.width/2;
if (dxIndex + bmIndex.width + minimumClearance > dxSqr + bmSqr.width) {
if (bmIndex.width + minimumClearance < bmSqr.width) {
dxIndex = bmSqr.width - (bmIndex.width + minimumClearance);
dxSqr = 0;
}
else {
dxIndex = 0;
dxSqr = (bmIndex.width + minimumClearance) - bmSqr.width;
}
1999-11-22 12:35:36 +00:00
}
// place the index
dx = dxIndex;
2000-03-28 09:38:24 +00:00
dy = aDesiredSize.ascent - (indexRaisedAscent + indexSize.ascent - bmIndex.ascent);
FinishReflowChild(indexFrame, aPresContext, nsnull, indexSize, dx, dy, 0);
1999-11-22 12:35:36 +00:00
// place the radical symbol and the radical bar
dx = dxSqr;
dy = indexClearance + leading; // leave a leading at the top
2000-03-28 09:38:24 +00:00
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);
1999-11-22 12:35:36 +00:00
mReference.x = 0;
mReference.y = aDesiredSize.ascent;
mBoundingMetrics.width = dx + bmBase.width;
mBoundingMetrics.leftBearing =
PR_MIN(dxIndex + bmIndex.leftBearing, dxSqr + bmSqr.leftBearing);
mBoundingMetrics.rightBearing = dx +
PR_MAX(bmBase.width, bmBase.rightBearing);
aDesiredSize.width = mBoundingMetrics.width;
aDesiredSize.mBoundingMetrics = mBoundingMetrics;
if (aDesiredSize.mComputeMEW) {
aDesiredSize.mMaxElementWidth = aDesiredSize.width;
1999-11-22 12:35:36 +00:00
}
aStatus = NS_FRAME_COMPLETE;
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
2000-03-28 09:38:24 +00:00
return NS_OK;
}
2000-03-28 09:38:24 +00:00
// ----------------------
// the Style System will use these to pass the proper style context to our MathMLChar
nsStyleContext*
nsMathMLmrootFrame::GetAdditionalStyleContext(PRInt32 aIndex) const
2000-03-28 09:38:24 +00:00
{
switch (aIndex) {
case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
return mSqrChar.GetStyleContext();
2000-03-28 09:38:24 +00:00
break;
default:
return nsnull;
2000-03-28 09:38:24 +00:00
}
}
void
2000-03-28 09:38:24 +00:00
nsMathMLmrootFrame::SetAdditionalStyleContext(PRInt32 aIndex,
nsStyleContext* aStyleContext)
2000-03-28 09:38:24 +00:00
{
switch (aIndex) {
case NS_SQR_CHAR_STYLE_CONTEXT_INDEX:
mSqrChar.SetStyleContext(aStyleContext);
break;
}
1999-11-22 12:35:36 +00:00
}