/* * 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 */ #include "nsCOMPtr.h" #include "nsHTMLParts.h" #include "nsIHTMLContent.h" #include "nsFrame.h" #include "nsLineLayout.h" #include "nsHTMLIIDs.h" #include "nsIPresContext.h" #include "nsHTMLAtoms.h" #include "nsUnitConversion.h" #include "nsIStyleContext.h" #include "nsStyleConsts.h" #include "nsINameSpaceManager.h" #include "nsIRenderingContext.h" #include "nsIFontMetrics.h" #include "nsStyleUtil.h" #include "nsMathMLmpaddedFrame.h" #include "xp_mcom.h" // to get XP_IS_SPACE // // -- adjust space around content - implementation // #define NS_MATHML_SIGN_INVALID -1 // if the attribute is not there #define NS_MATHML_SIGN_UNSPECIFIED 0 #define NS_MATHML_SIGN_MINUS 1 #define NS_MATHML_SIGN_PLUS 2 #define NS_MATHML_PSEUDO_UNIT_UNSPECIFIED 0 #define NS_MATHML_PSEUDO_UNIT_ITSELF 1 // special #define NS_MATHML_PSEUDO_UNIT_WIDTH 2 #define NS_MATHML_PSEUDO_UNIT_HEIGHT 3 #define NS_MATHML_PSEUDO_UNIT_DEPTH 4 #define NS_MATHML_PSEUDO_UNIT_LSPACE 5 #define NS_MATHML_PSEUDO_UNIT_NAMEDSPACE 6 nsresult NS_NewMathMLmpaddedFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame) { NS_PRECONDITION(aNewFrame, "null OUT ptr"); if (nsnull == aNewFrame) { return NS_ERROR_NULL_POINTER; } nsMathMLmpaddedFrame* it = new (aPresShell) nsMathMLmpaddedFrame; if (nsnull == it) { return NS_ERROR_OUT_OF_MEMORY; } *aNewFrame = it; return NS_OK; } nsMathMLmpaddedFrame::nsMathMLmpaddedFrame() { } nsMathMLmpaddedFrame::~nsMathMLmpaddedFrame() { } NS_IMETHODIMP nsMathMLmpaddedFrame::Init(nsIPresContext* aPresContext, nsIContent* aContent, nsIFrame* aParent, nsIStyleContext* aContext, nsIFrame* aPrevInFlow) { nsresult rv = nsMathMLContainerFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow); mEmbellishData.flags |= NS_MATHML_STRETCH_ALL_CHILDREN_VERTICALLY; /* parse the attributes width = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | h-unit | namedspace) height= [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit) depth = [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | v-unit) lspace= [+|-] unsigned-number (% [pseudo-unit] | pseudo-unit | h-unit) */ nsAutoString value; /* The REC says: There is one exceptional element, , whose attributes cannot be set with . When the attributes width, height and depth are specified on an element, they apply only to the element. Similarly, when lspace is set with , it applies only to the element. */ // See if attributes are local, don't access mstyle ! // width mWidthSign = NS_MATHML_SIGN_INVALID; if (NS_CONTENT_ATTR_HAS_VALUE == GetAttribute(mContent, nsnull, nsMathMLAtoms::width_, value)) { ParseAttribute(value, mWidthSign, mWidth, mWidthPseudoUnit); } // height mHeightSign = NS_MATHML_SIGN_INVALID; if (NS_CONTENT_ATTR_HAS_VALUE == GetAttribute(mContent, nsnull, nsMathMLAtoms::height_, value)) { ParseAttribute(value, mHeightSign, mHeight, mHeightPseudoUnit); } // depth mDepthSign = NS_MATHML_SIGN_INVALID; if (NS_CONTENT_ATTR_HAS_VALUE == GetAttribute(mContent, nsnull, nsMathMLAtoms::depth_, value)) { ParseAttribute(value, mDepthSign, mDepth, mDepthPseudoUnit); } // lspace mLeftSpaceSign = NS_MATHML_SIGN_INVALID; if (NS_CONTENT_ATTR_HAS_VALUE == GetAttribute(mContent, nsnull, nsMathMLAtoms::lspace_, value)) { ParseAttribute(value, mLeftSpaceSign, mLeftSpace, mLeftSpacePseudoUnit); } #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX) mPresentationData.flags |= NS_MATHML_SHOW_BOUNDING_METRICS; #endif return rv; } PRBool nsMathMLmpaddedFrame::ParseAttribute(nsString& aString, PRInt32& aSign, nsCSSValue& aCSSValue, PRInt32& aPseudoUnit) { aCSSValue.Reset(); aString.CompressWhitespace(); // aString is not a const in this code PRInt32 stringLength = aString.Length(); if (!stringLength) return PR_FALSE; nsAutoString value; ////////////////////// // see if the sign is there PRInt32 i = 0; if (aString[0] == '+') { aSign = NS_MATHML_SIGN_PLUS; i++; } else if (aString[0] == '-') { aSign = NS_MATHML_SIGN_MINUS; i++; } else aSign = NS_MATHML_SIGN_UNSPECIFIED; // skip any space after the sign while (i < stringLength && XP_IS_SPACE(aString[i])) i++; /////////////////////// // get the string that represents the numeric value value.SetLength(0); while (i < stringLength && !XP_IS_SPACE(aString[i])) { value.Append(aString[i]); i++; } // convert to CSS units if (!ParseNumericValue(value, aCSSValue)) { #ifdef NS_DEBUG char str[50]; aString.ToCString(str, 50); printf("mpadded: attribute with bad numeric value: %s\n", str); #endif aSign = NS_MATHML_SIGN_INVALID; return PR_FALSE; } // skip any space in-between while (i < stringLength && XP_IS_SPACE(aString[i])) i++; ////////////////////////// // get the string that represents the pseudo unit aPseudoUnit = NS_MATHML_PSEUDO_UNIT_UNSPECIFIED; if (i == stringLength) { // no explicit pseudo-unit ... if (eCSSUnit_Number == aCSSValue.GetUnit() && 0.0f != aCSSValue.GetFloatValue()) { // ... and no explicit CSS unit either // In this case, the MathML REC suggests taking ems for // h-unit (width, lspace) or exs for v-unit (height, depth). // Here, however, we explicitly request authors to specify // the unit. This is more in line with the CSS REC (and // it allows keeping the code simpler...) #ifdef NS_DEBUG char str[50]; aString.ToCString(str, 50); printf("mpadded: attribute with bad numeric value: %s\n", str); #endif aCSSValue.Reset(); aSign = NS_MATHML_SIGN_INVALID; return PR_FALSE; } // else the only other valid possibility here is a percentage value, // otherwise ParseNumericValue() would have failed aPseudoUnit = NS_MATHML_PSEUDO_UNIT_ITSELF; return PR_TRUE; } if (value.EqualsWithConversion("width")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_WIDTH; else if (value.EqualsWithConversion("height")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_HEIGHT; else if (value.EqualsWithConversion("depth")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_DEPTH; else if (value.EqualsWithConversion("lspace")) aPseudoUnit = NS_MATHML_PSEUDO_UNIT_LSPACE; else { nsCSSValue aCSSNamedSpaceValue; if (!aCSSValue.IsLengthUnit() && ParseNamedSpaceValue(nsnull, value, aCSSNamedSpaceValue)) { // XXX nsnull in ParseNamedSpacedValue()? don't access mstyle? aPseudoUnit = NS_MATHML_PSEUDO_UNIT_NAMEDSPACE; float namedspace = aCSSNamedSpaceValue.GetFloatValue(); // combine aCSSNamedSpaceValue and aCSSValue since // we know that the unit of aCSSNamedSpaceValue is 'em' nsCSSUnit unit = aCSSValue.GetUnit(); if (eCSSUnit_Number == unit) { float scaler = aCSSValue.GetFloatValue(); aCSSValue.SetFloatValue(scaler*namedspace, eCSSUnit_EM); return PR_TRUE; } else if (eCSSUnit_Percent == unit) { float scaler = aCSSValue.GetPercentValue(); aCSSValue.SetFloatValue(scaler*namedspace, eCSSUnit_EM); return PR_TRUE; } } // if we reach here, it means we encounter an unexpected pseudo-unit aCSSValue.Reset(); aSign = NS_MATHML_SIGN_INVALID; return PR_FALSE; } if (aCSSValue.IsLengthUnit()) { // if we enter here, it means we have both a CSS unit *and* a pseudo-unit, // e.g., width="100em height", this an error/ambiguity that the author should fix #ifdef NS_DEBUG char str[50]; aString.ToCString(str, 50); printf("mpadded: attribute with bad numeric value: %s\n", str); #endif aCSSValue.Reset(); aSign = NS_MATHML_SIGN_INVALID; return PR_FALSE; } return PR_TRUE; } void nsMathMLmpaddedFrame::UpdateValue(nsIPresContext* aPresContext, nsIStyleContext* aStyleContext, PRInt32 aSign, PRInt32 aPseudoUnit, nsCSSValue& aCSSValue, nscoord aLeftSpace, nsBoundingMetrics& aBoundingMetrics, nscoord& aValueToUpdate) { nsCSSUnit unit = aCSSValue.GetUnit(); if (NS_MATHML_SIGN_INVALID != aSign && eCSSUnit_Null != unit) { nscoord scaler, amount; if (eCSSUnit_Percent == unit || eCSSUnit_Number == unit) { switch(aPseudoUnit) { case NS_MATHML_PSEUDO_UNIT_WIDTH: scaler = aBoundingMetrics.width; break; case NS_MATHML_PSEUDO_UNIT_HEIGHT: scaler = aBoundingMetrics.ascent; break; case NS_MATHML_PSEUDO_UNIT_DEPTH: scaler = aBoundingMetrics.descent; break; case NS_MATHML_PSEUDO_UNIT_LSPACE: scaler = aLeftSpace; break; default: // if we ever reach here, it would mean something is wrong // somewhere with the setup and/or the caller NS_ASSERTION(0, "Unexpected Pseudo Unit"); return; } } if (eCSSUnit_Number == unit) amount = NSToCoordRound(float(scaler) * aCSSValue.GetFloatValue()); else if (eCSSUnit_Percent == unit) amount = NSToCoordRound(float(scaler) * aCSSValue.GetPercentValue()); else amount = CalcLength(aPresContext, aStyleContext, aCSSValue); nscoord oldValue = aValueToUpdate; if (NS_MATHML_SIGN_PLUS == aSign) aValueToUpdate += amount; else if (NS_MATHML_SIGN_MINUS == aSign) aValueToUpdate -= amount; else aValueToUpdate = amount; /* The REC says: Dimensions that would be positive if the content was rendered normally cannot be made negative using ; a positive dimension is set to 0 if it would otherwise become negative. Dimensions which are initially 0 can be made negative */ if (0 < oldValue && 0 > aValueToUpdate) aValueToUpdate = 0; } } NS_IMETHODIMP nsMathMLmpaddedFrame::Reflow(nsIPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { nsresult rv = NS_OK; /////////////// // Let the base class format our content like an inferred mrow rv = nsMathMLContainerFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status"); if (NS_FAILED(rv)) { return rv; } nscoord height = mBoundingMetrics.ascent; nscoord depth = mBoundingMetrics.descent; nscoord width = mBoundingMetrics.width; nscoord lspace = 0; // it is unclear from the REC what is the default here PRInt32 pseudoUnit; // update width pseudoUnit = (mWidthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF) ? NS_MATHML_PSEUDO_UNIT_WIDTH : mWidthPseudoUnit; UpdateValue(aPresContext, mStyleContext, mWidthSign, pseudoUnit, mWidth, lspace, mBoundingMetrics, width); // update height pseudoUnit = (mHeightPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF) ? NS_MATHML_PSEUDO_UNIT_HEIGHT : mHeightPseudoUnit; UpdateValue(aPresContext, mStyleContext, mHeightSign, pseudoUnit, mHeight, lspace, mBoundingMetrics, height); // update depth pseudoUnit = (mDepthPseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF) ? NS_MATHML_PSEUDO_UNIT_DEPTH : mDepthPseudoUnit; UpdateValue(aPresContext, mStyleContext, mDepthSign, pseudoUnit, mDepth, lspace, mBoundingMetrics, depth); // update lspace -- should be *last* because lspace is overwritten!! pseudoUnit = (mLeftSpacePseudoUnit == NS_MATHML_PSEUDO_UNIT_ITSELF) ? NS_MATHML_PSEUDO_UNIT_LSPACE : mLeftSpacePseudoUnit; UpdateValue(aPresContext, mStyleContext, mLeftSpaceSign, pseudoUnit, mLeftSpace, lspace, mBoundingMetrics, lspace); // do the padding now that we have everything if (lspace != 0) { // there was padding on the left mBoundingMetrics.leftBearing = 0; } if (width != mBoundingMetrics.width) { // there was padding on the right mBoundingMetrics.rightBearing = lspace + width; } nscoord dy = height - mBoundingMetrics.ascent; nscoord dx = lspace; mBoundingMetrics.ascent = height; mBoundingMetrics.descent = depth; mBoundingMetrics.width = lspace + width; aDesiredSize.ascent += dy; aDesiredSize.descent += depth - mBoundingMetrics.descent; aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.height = aDesiredSize.ascent + aDesiredSize.descent; if (0 != dx || 0 != dy) { nsRect rect; nsIFrame* childFrame = mFrames.FirstChild(); while (childFrame) { childFrame->GetRect(rect); childFrame->MoveTo(aPresContext, rect.x + dx, rect.y + dy); childFrame->GetNextSibling(&childFrame); } } mReference.x = 0; mReference.y = aDesiredSize.ascent; return NS_OK; }