NS_IMETHODIMP nsMathMLmoFrame::AttributeChanged(int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType) { // check if this is an attribute that can affect the embellished hierarchy // in a significant way and re-layout the entire hierarchy. if (nsGkAtoms::accent_ == aAttribute || nsGkAtoms::movablelimits_ == aAttribute) { // set the target as the parent of our outermost embellished container // (we ensure that we are the core, not just a sibling of the core) nsIFrame* target = this; nsEmbellishData embellishData; do { target = target->GetParent(); GetEmbellishDataFrom(target, embellishData); } while (embellishData.coreFrame == this); // we have automatic data to update in the children of the target frame return ReLayoutChildren(target); } return nsMathMLTokenFrame:: AttributeChanged(aNameSpaceID, aAttribute, aModType); }
NS_IMETHODIMP nsMathMLmfracFrame::TransmitAutomaticData() { // 1. The REC says: // The <mfrac> element sets displaystyle to "false", or if it was already // false increments scriptlevel by 1, within numerator and denominator. // 2. The TeXbook (Ch 17. p.141) says the numerator inherits the compression // while the denominator is compressed bool increment = !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags); SetIncrementScriptLevel(0, increment); SetIncrementScriptLevel(1, increment); UpdatePresentationDataFromChildAt(0, -1, ~NS_MATHML_DISPLAYSTYLE, NS_MATHML_DISPLAYSTYLE); UpdatePresentationDataFromChildAt(1, 1, NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED); // if our numerator is an embellished operator, let its state bubble to us GetEmbellishDataFrom(mFrames.FirstChild(), mEmbellishData); if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) { // even when embellished, we need to record that <mfrac> won't fire // Stretch() on its embellished child mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED; } return NS_OK; }
NS_IMETHODIMP nsMathMLmfracFrame::TransmitAutomaticData() { // The TeXbook (Ch 17. p.141) says the numerator inherits the compression // while the denominator is compressed UpdatePresentationDataFromChildAt(1, 1, NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED); // If displaystyle is false, then scriptlevel is incremented, so notify the // children of this. if (!StyleFont()->mMathDisplay) { PropagateFrameFlagFor(mFrames.FirstChild(), NS_FRAME_MATHML_SCRIPT_DESCENDANT); PropagateFrameFlagFor(mFrames.LastChild(), NS_FRAME_MATHML_SCRIPT_DESCENDANT); } // if our numerator is an embellished operator, let its state bubble to us GetEmbellishDataFrom(mFrames.FirstChild(), mEmbellishData); if (NS_MATHML_IS_EMBELLISH_OPERATOR(mEmbellishData.flags)) { // even when embellished, we need to record that <mfrac> won't fire // Stretch() on its embellished child mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED; } return NS_OK; }
NS_IMETHODIMP nsMathMLmmultiscriptsFrame::TransmitAutomaticData() { // if our base is an embellished operator, let its state bubble to us mPresentationData.baseFrame = mFrames.FirstChild(); GetEmbellishDataFrom(mPresentationData.baseFrame, mEmbellishData); // The REC says: // The <mmultiscripts> element increments scriptlevel by 1, and sets // displaystyle to "false", within each of its arguments except base UpdatePresentationDataFromChildAt(1, -1, ~NS_MATHML_DISPLAYSTYLE, NS_MATHML_DISPLAYSTYLE); // The TeXbook (Ch 17. p.141) says the superscript inherits the compression // while the subscript is compressed. So here we collect subscripts and set // the compression flag in them. PRInt32 count = 0; bool isSubScript = false; nsAutoTArray<nsIFrame*, 8> subScriptFrames; nsIFrame* childFrame = mFrames.FirstChild(); while (childFrame) { if (childFrame->GetContent()->Tag() == nsGkAtoms::mprescripts_) { // mprescripts frame } else if (0 == count) { // base frame } else { // super/subscript block if (isSubScript) { // subscript subScriptFrames.AppendElement(childFrame); } else { // superscript } isSubScript = !isSubScript; } count++; childFrame = childFrame->GetNextSibling(); } for (PRInt32 i = subScriptFrames.Length() - 1; i >= 0; i--) { childFrame = subScriptFrames[i]; PropagatePresentationDataFor(childFrame, NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED); } return NS_OK; }
NS_IMETHODIMP nsMathMLmsubFrame::TransmitAutomaticData() { // if our base is an embellished operator, let its state bubble to us mPresentationData.baseFrame = mFrames.FirstChild(); GetEmbellishDataFrom(mPresentationData.baseFrame, mEmbellishData); // 1. The REC says: // The <msub> element increments scriptlevel by 1, and sets displaystyle to // "false", within subscript, but leaves both attributes unchanged within base. // 2. The TeXbook (Ch 17. p.141) says the subscript is compressed UpdatePresentationDataFromChildAt(1, -1, ~NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED, NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED); return NS_OK; }
NS_IMETHODIMP nsMathMLmsupFrame::TransmitAutomaticData() { // if our base is an embellished operator, its flags bubble to us mPresentationData.baseFrame = mFrames.FirstChild(); GetEmbellishDataFrom(mPresentationData.baseFrame, mEmbellishData); // 1. The REC says: // The <msup> element increments scriptlevel by 1, and sets displaystyle to // "false", within superscript, but leaves both attributes unchanged within base. // 2. The TeXbook (Ch 17. p.141) says the superscript *inherits* the compression, // so we don't set the compression flag. Our parent will propagate its own. UpdatePresentationDataFromChildAt(1, -1, ~NS_MATHML_DISPLAYSTYLE, NS_MATHML_DISPLAYSTYLE); return NS_OK; }
NS_IMETHODIMP nsMathMLmactionFrame::TransmitAutomaticData() { // The REC defines the following element to be space-like: // * an maction element whose selected sub-expression exists and is // space-like; nsIMathMLFrame* mathMLFrame = do_QueryFrame(mSelectedFrame); if (mathMLFrame && mathMLFrame->IsSpaceLike()) { mPresentationData.flags |= NS_MATHML_SPACE_LIKE; } else { mPresentationData.flags &= ~NS_MATHML_SPACE_LIKE; } // The REC defines the following element to be an embellished operator: // * an maction element whose selected sub-expression exists and is an // embellished operator; mPresentationData.baseFrame = mSelectedFrame; GetEmbellishDataFrom(mSelectedFrame, mEmbellishData); return NS_OK; }
/* virtual */ void nsMathMLmoFrame::MarkIntrinsicWidthsDirty() { // if we get this, it may mean that something changed in the text // content. So blow away everything an re-build the automatic data // from the parent of our outermost embellished container (we ensure // that we are the core, not just a sibling of the core) ProcessTextData(); nsIFrame* target = this; nsEmbellishData embellishData; do { target = target->GetParent(); GetEmbellishDataFrom(target, embellishData); } while (embellishData.coreFrame == this); // we have automatic data to update in the children of the target frame // XXXldb This should really be marking dirty rather than rebuilding // so that we don't rebuild multiple times for the same change. RebuildAutomaticDataForChildren(target); nsMathMLContainerFrame::MarkIntrinsicWidthsDirty(); }
NS_IMETHODIMP nsMathMLmunderoverFrame::TransmitAutomaticData() { // At this stage, all our children are in sync and we can fully // resolve our own mEmbellishData struct //--------------------------------------------------------------------- /* The REC says: As regards munder (respectively mover) : The default value of accentunder is false, unless underscript is an <mo> element or an embellished operator. If underscript is an <mo> element, the value of its accent attribute is used as the default value of accentunder. If underscript is an embellished operator, the accent attribute of the <mo> element at its core is used as the default value. As with all attributes, an explicitly given value overrides the default. XXX The winner is the outermost setting in conflicting settings like these: <munder accentunder='true'> <mi>...</mi> <mo accentunder='false'> ... </mo> </munder> As regards munderover: The accent and accentunder attributes have the same effect as the attributes with the same names on <mover> and <munder>, respectively. Their default values are also computed in the same manner as described for those elements, with the default value of accent depending on overscript and the default value of accentunder depending on underscript. */ nsIFrame* overscriptFrame = nullptr; nsIFrame* underscriptFrame = nullptr; nsIFrame* baseFrame = mFrames.FirstChild(); nsIAtom* tag = mContent->Tag(); if (baseFrame) { if (tag == nsGkAtoms::munder_ || tag == nsGkAtoms::munderover_) { underscriptFrame = baseFrame->GetNextSibling(); } else { NS_ASSERTION(tag == nsGkAtoms::mover_, "mContent->Tag() not recognized"); overscriptFrame = baseFrame->GetNextSibling(); } } if (underscriptFrame && tag == nsGkAtoms::munderover_) { overscriptFrame = underscriptFrame->GetNextSibling(); } // if our base is an embellished operator, let its state bubble to us (in particular, // this is where we get the flag for NS_MATHML_EMBELLISH_MOVABLELIMITS). Our flags // are reset to the default values of false if the base frame isn't embellished. mPresentationData.baseFrame = baseFrame; GetEmbellishDataFrom(baseFrame, mEmbellishData); // The default value of accentunder is false, unless the underscript is embellished // and its core <mo> is an accent nsEmbellishData embellishData; nsAutoString value; if (tag == nsGkAtoms::munder_ || tag == nsGkAtoms::munderover_) { GetEmbellishDataFrom(underscriptFrame, embellishData); if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) { mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER; } else { mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER; } // if we have an accentunder attribute, it overrides what the underscript said if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accentunder_, value)) { if (value.EqualsLiteral("true")) { mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER; } else if (value.EqualsLiteral("false")) { mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER; } } } // The default value of accent is false, unless the overscript is embellished // and its core <mo> is an accent if (tag == nsGkAtoms::mover_ || tag == nsGkAtoms::munderover_) { GetEmbellishDataFrom(overscriptFrame, embellishData); if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) { mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER; } else { mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER; } // if we have an accent attribute, it overrides what the overscript said if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accent_, value)) { if (value.EqualsLiteral("true")) { mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER; } else if (value.EqualsLiteral("false")) { mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER; } } } bool subsupDisplay = NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) && StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE; // disable the stretch-all flag if we are going to act like a superscript if (subsupDisplay) { mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY; } // Now transmit any change that we want to our children so that they // can update their mPresentationData structs //--------------------------------------------------------------------- /* The REC says: Within underscript, <munderover> always sets displaystyle to "false", but increments scriptlevel by 1 only when accentunder is "false". Within overscript, <munderover> always sets displaystyle to "false", but increments scriptlevel by 1 only when accent is "false". Within subscript and superscript it increments scriptlevel by 1, and sets displaystyle to "false", but leaves both attributes unchanged within base. The TeXBook treats 'over' like a superscript, so p.141 or Rule 13a say it shouldn't be compressed. However, The TeXBook says that math accents and \overline change uncramped styles to their cramped counterparts. */ if (tag == nsGkAtoms::mover_ || tag == nsGkAtoms::munderover_) { uint32_t compress = NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) ? NS_MATHML_COMPRESSED : 0; mIncrementOver = !NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) || subsupDisplay; SetIncrementScriptLevel(tag == nsGkAtoms::mover_ ? 1 : 2, mIncrementOver); if (mIncrementOver) { PropagateFrameFlagFor(overscriptFrame, NS_FRAME_MATHML_SCRIPT_DESCENDANT); } PropagatePresentationDataFor(overscriptFrame, compress, compress); } /* The TeXBook treats 'under' like a subscript, so p.141 or Rule 13a say it should be compressed */ if (tag == nsGkAtoms::munder_ || tag == nsGkAtoms::munderover_) { mIncrementUnder = !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags) || subsupDisplay; SetIncrementScriptLevel(1, mIncrementUnder); if (mIncrementUnder) { PropagateFrameFlagFor(underscriptFrame, NS_FRAME_MATHML_SCRIPT_DESCENDANT); } PropagatePresentationDataFor(underscriptFrame, NS_MATHML_COMPRESSED, NS_MATHML_COMPRESSED); } /* Set flags for dtls font feature settings. dtls Dotless Forms This feature provides dotless forms for Math Alphanumeric characters, such as U+1D422 MATHEMATICAL BOLD SMALL I, U+1D423 MATHEMATICAL BOLD SMALL J, U+1D456 U+MATHEMATICAL ITALIC SMALL I, U+1D457 MATHEMATICAL ITALIC SMALL J, and so on. The dotless forms are to be used as base forms for placing mathematical accents over them. To opt out of this change, add the following to the stylesheet: "font-feature-settings: 'dtls' 0" */ if (overscriptFrame && NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) && !NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags)) { PropagatePresentationDataFor(baseFrame, NS_MATHML_DTLS, NS_MATHML_DTLS); } return NS_OK; }
NS_IMETHODIMP nsMathMLmunderoverFrame::TransmitAutomaticData() { // At this stage, all our children are in sync and we can fully // resolve our own mEmbellishData struct //--------------------------------------------------------------------- /* The REC says: The accent and accentunder attributes have the same effect as the attributes with the same names on <mover> and <munder>, respectively. Their default values are also computed in the same manner as described for those elements, with the default value of accent depending on overscript and the default value of accentunder depending on underscript. */ nsIFrame* overscriptFrame = nsnull; nsIFrame* underscriptFrame = nsnull; nsIFrame* baseFrame = mFrames.FirstChild(); if (baseFrame) underscriptFrame = baseFrame->GetNextSibling(); if (underscriptFrame) overscriptFrame = underscriptFrame->GetNextSibling(); // if our base is an embellished operator, let its state bubble to us (in particular, // this is where we get the flag for NS_MATHML_EMBELLISH_MOVABLELIMITS). Our flags // are reset to the default values of false if the base frame isn't embellished. mPresentationData.baseFrame = baseFrame; GetEmbellishDataFrom(baseFrame, mEmbellishData); nsAutoString value; // The default value of accentunder is false, unless the underscript is embellished // and its core <mo> is an accent nsEmbellishData embellishData; GetEmbellishDataFrom(underscriptFrame, embellishData); if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER; else mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER; // if we have an accentunder attribute, it overrides what the underscript said if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttr(kNameSpaceID_None, nsMathMLAtoms::accentunder_, value)) { if (value.EqualsLiteral("true")) mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTUNDER; else if (value.EqualsLiteral("false")) mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTUNDER; } // The default value of accent is false, unless the overscript is embellished // and its core <mo> is an accent GetEmbellishDataFrom(overscriptFrame, embellishData); if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER; else mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER; // if we have an accent attribute, it overrides what the overscript said if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttr(kNameSpaceID_None, nsMathMLAtoms::accent_, value)) { if (value.EqualsLiteral("true")) mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER; else if (value.EqualsLiteral("false")) mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER; } // disable the stretch-all flag if we are going to act like a superscript if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) && !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY; // Now transmit any change that we want to our children so that they // can update their mPresentationData structs //--------------------------------------------------------------------- /* The REC says: Within underscript, <munderover> always sets displaystyle to "false", but increments scriptlevel by 1 only when accentunder is "false". Within overscript, <munderover> always sets displaystyle to "false", but increments scriptlevel by 1 only when accent is "false". The TeXBook treats 'over' like a superscript, so p.141 or Rule 13a say it shouldn't be compressed. However, The TeXBook says that math accents and \overline change uncramped styles to their cramped counterparts. */ PRInt32 increment = NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) ? 0 : 1; PRUint32 compress = NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) ? NS_MATHML_COMPRESSED : 0; PropagatePresentationDataFor(overscriptFrame, increment, ~NS_MATHML_DISPLAYSTYLE | compress, NS_MATHML_DISPLAYSTYLE | compress); /* The TeXBook treats 'under' like a subscript, so p.141 or Rule 13a say it should be compressed */ increment = NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags) ? 0 : 1; PropagatePresentationDataFor(underscriptFrame, increment, ~NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED, NS_MATHML_DISPLAYSTYLE | NS_MATHML_COMPRESSED); return NS_OK; }
NS_IMETHODIMP nsMathMLmoverFrame::TransmitAutomaticData() { // At this stage, all our children are in sync and we can fully // resolve our own mEmbellishData struct //--------------------------------------------------------------------- /* The REC says: The default value of accent is false, unless overscript is an <mo> element or an embellished operator. If overscript is an <mo> element, the value of its accent attribute is used as the default value of accent. If overscript is an embellished operator, the accent attribute of the <mo> element at its core is used as the default value. As with all attributes, an explicitly given value overrides the default. XXX The winner is the outermost in conflicting settings like these: <mover accent='true'> <mi>...</mi> <mo accent='false'> ... </mo> </mover> */ nsIFrame* overscriptFrame = nsnull; nsIFrame* baseFrame = mFrames.FirstChild(); if (baseFrame) overscriptFrame = baseFrame->GetNextSibling(); // if our base is an embellished operator, let its state bubble to us (in particular, // this is where we get the flag for NS_MATHML_EMBELLISH_MOVABLELIMITS). Our flags // are reset to the default values of false if the base frame isn't embellished. mPresentationData.baseFrame = baseFrame; GetEmbellishDataFrom(baseFrame, mEmbellishData); // The default value of accent is false, unless the overscript is embellished // and its core <mo> is an accent nsEmbellishData embellishData; GetEmbellishDataFrom(overscriptFrame, embellishData); if (NS_MATHML_EMBELLISH_IS_ACCENT(embellishData.flags)) mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER; else mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER; // if we have an accent attribute, it overrides what the overscript said static nsIContent::AttrValuesArray strings[] = {&nsGkAtoms::_true, &nsGkAtoms::_false, nsnull}; switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::accent_, strings, eCaseMatters)) { case 0: mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENTOVER; break; case 1: mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENTOVER; break; } // disable the stretch-all flag if we are going to act like a superscript if ( NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) && !NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) mPresentationData.flags &= ~NS_MATHML_STRETCH_ALL_CHILDREN_HORIZONTALLY; // Now transmit any change that we want to our children so that they // can update their mPresentationData structs //--------------------------------------------------------------------- /* The REC says: Within overscript, <mover> always sets displaystyle to "false", but increments scriptlevel by 1 only when accent is "false". The TeXBook treats 'over' like a superscript, so p.141 or Rule 13a say it shouldn't be compressed. However, The TeXBook says that math accents and \overline change uncramped styles to their cramped counterparts. */ SetIncrementScriptLevel(1, !NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)); PRUint32 compress = NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags) ? NS_MATHML_COMPRESSED : 0; PropagatePresentationDataFor(overscriptFrame, ~NS_MATHML_DISPLAYSTYLE | compress, NS_MATHML_DISPLAYSTYLE | compress); return NS_OK; }
// NOTE: aDesiredStretchSize is an IN/OUT parameter // On input - it contains our current size // On output - the same size or the new size that we want NS_IMETHODIMP nsMathMLmoFrame::Stretch(nsRenderingContext& aRenderingContext, nsStretchDirection aStretchDirection, nsBoundingMetrics& aContainerSize, nsHTMLReflowMetrics& aDesiredStretchSize) { if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) { NS_WARNING("it is wrong to fire stretch more than once on a frame"); return NS_OK; } mPresentationData.flags |= NS_MATHML_STRETCH_DONE; nsIFrame* firstChild = mFrames.FirstChild(); // get the axis height; nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); aRenderingContext.SetFont(fm); nscoord axisHeight, height; GetAxisHeight(aRenderingContext, fm, axisHeight); // get the leading to be left at the top and the bottom of the stretched char // this seems more reliable than using fm->GetLeading() on suspicious fonts nscoord em; GetEmHeight(fm, em); nscoord leading = NSToCoordRound(0.2f * em); // Operators that are stretchy, or those that are to be centered // to cater for fonts that are not math-aware, are handled by the MathMLChar // ('form' is reset if stretch fails -- i.e., we don't bother to stretch next time) bool useMathMLChar = UseMathMLChar(); nsBoundingMetrics charSize; nsBoundingMetrics container = aDesiredStretchSize.mBoundingMetrics; bool isVertical = false; if (((aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL) || (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT)) && (mEmbellishData.direction == NS_STRETCH_DIRECTION_VERTICAL)) { isVertical = true; } uint32_t stretchHint = GetStretchHint(mFlags, mPresentationData, isVertical); if (useMathMLChar) { nsBoundingMetrics initialSize = aDesiredStretchSize.mBoundingMetrics; if (stretchHint != NS_STRETCH_NONE) { container = aContainerSize; // some adjustments if the operator is symmetric and vertical if (isVertical && NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) { // we need to center about the axis nscoord delta = std::max(container.ascent - axisHeight, container.descent + axisHeight); container.ascent = delta + axisHeight; container.descent = delta - axisHeight; // get ready in case we encounter user-desired min-max size delta = std::max(initialSize.ascent - axisHeight, initialSize.descent + axisHeight); initialSize.ascent = delta + axisHeight; initialSize.descent = delta - axisHeight; } // check for user-desired min-max size if (mMaxSize != NS_MATHML_OPERATOR_SIZE_INFINITY && mMaxSize > 0.0f) { // if we are here, there is a user defined maxsize ... //XXX Set stretchHint = NS_STRETCH_NORMAL? to honor the maxsize as close as possible? if (NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags)) { // there is an explicit value like maxsize="20pt" // try to maintain the aspect ratio of the char float aspect = mMaxSize / float(initialSize.ascent + initialSize.descent); container.ascent = std::min(container.ascent, nscoord(initialSize.ascent * aspect)); container.descent = std::min(container.descent, nscoord(initialSize.descent * aspect)); // below we use a type cast instead of a conversion to avoid a VC++ bug // see http://support.microsoft.com/support/kb/articles/Q115/7/05.ASP container.width = std::min(container.width, (nscoord)mMaxSize); } else { // multiplicative value container.ascent = std::min(container.ascent, nscoord(initialSize.ascent * mMaxSize)); container.descent = std::min(container.descent, nscoord(initialSize.descent * mMaxSize)); container.width = std::min(container.width, nscoord(initialSize.width * mMaxSize)); } if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) { // re-adjust to align the char with the bottom of the initial container height = container.ascent + container.descent; container.descent = aContainerSize.descent; container.ascent = height - container.descent; } } if (mMinSize > 0.0f) { // if we are here, there is a user defined minsize ... // always allow the char to stretch in its natural direction, // even if it is different from the caller's direction if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT && aStretchDirection != mEmbellishData.direction) { aStretchDirection = NS_STRETCH_DIRECTION_DEFAULT; // but when we are not honoring the requested direction // we should not use the caller's container size either container = initialSize; } if (NS_MATHML_OPERATOR_MINSIZE_IS_ABSOLUTE(mFlags)) { // there is an explicit value like minsize="20pt" // try to maintain the aspect ratio of the char float aspect = mMinSize / float(initialSize.ascent + initialSize.descent); container.ascent = std::max(container.ascent, nscoord(initialSize.ascent * aspect)); container.descent = std::max(container.descent, nscoord(initialSize.descent * aspect)); container.width = std::max(container.width, (nscoord)mMinSize); } else { // multiplicative value container.ascent = std::max(container.ascent, nscoord(initialSize.ascent * mMinSize)); container.descent = std::max(container.descent, nscoord(initialSize.descent * mMinSize)); container.width = std::max(container.width, nscoord(initialSize.width * mMinSize)); } if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) { // re-adjust to align the char with the bottom of the initial container height = container.ascent + container.descent; container.descent = aContainerSize.descent; container.ascent = height - container.descent; } } } // let the MathMLChar stretch itself... nsresult res = mMathMLChar.Stretch(PresContext(), aRenderingContext, aStretchDirection, container, charSize, stretchHint, StyleVisibility()->mDirection); if (NS_FAILED(res)) { // gracefully handle cases where stretching the char failed (i.e., GetBoundingMetrics failed) // clear our 'form' to behave as if the operator wasn't in the dictionary mFlags &= ~NS_MATHML_OPERATOR_FORM; useMathMLChar = false; } } // Child frames of invisble operators are not reflowed if (!NS_MATHML_OPERATOR_IS_INVISIBLE(mFlags)) { // Place our children using the default method // This will allow our child text frame to get its DidReflow() nsresult rv = Place(aRenderingContext, true, aDesiredStretchSize); if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) { // Make sure the child frames get their DidReflow() calls. DidReflowChildren(mFrames.FirstChild()); } } if (useMathMLChar) { // update our bounding metrics... it becomes that of our MathML char mBoundingMetrics = charSize; // if the returned direction is 'unsupported', the char didn't actually change. // So we do the centering only if necessary if (mMathMLChar.GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) { bool largeopOnly = (NS_STRETCH_LARGEOP & stretchHint) != 0 && (NS_STRETCH_VARIABLE_MASK & stretchHint) == 0; if (isVertical || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) { // the desired size returned by mMathMLChar maybe different // from the size of the container. // the mMathMLChar.mRect.y calculation is subtle, watch out!!! height = mBoundingMetrics.ascent + mBoundingMetrics.descent; if (NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags) || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) { // For symmetric and vertical operators, or for operators that are always // centered ('+', '*', etc) we want to center about the axis of the container mBoundingMetrics.descent = height/2 - axisHeight; } else if (!largeopOnly) { // Align the center of the char with the center of the container mBoundingMetrics.descent = height/2 + (container.ascent + container.descent)/2 - container.ascent; } // else align the baselines mBoundingMetrics.ascent = height - mBoundingMetrics.descent; } } } // Fixup for the final height. // On one hand, our stretchy height can sometimes be shorter than surrounding // ASCII chars, e.g., arrow symbols have |mBoundingMetrics.ascent + leading| // that is smaller than the ASCII's ascent, hence when painting the background // later, it won't look uniform along the line. // On the other hand, sometimes we may leave too much gap when our glyph happens // to come from a font with tall glyphs. For example, since CMEX10 has very tall // glyphs, its natural font metrics are large, even if we pick a small glyph // whose size is comparable to the size of a normal ASCII glyph. // So to avoid uneven spacing in either of these two cases, we use the height // of the ASCII font as a reference and try to match it if possible. // special case for accents... keep them short to improve mouse operations... // an accent can only be the non-first child of <mover>, <munder>, <munderover> bool isAccent = NS_MATHML_EMBELLISH_IS_ACCENT(mEmbellishData.flags); if (isAccent) { nsEmbellishData parentData; GetEmbellishDataFrom(mParent, parentData); isAccent = (NS_MATHML_EMBELLISH_IS_ACCENTOVER(parentData.flags) || NS_MATHML_EMBELLISH_IS_ACCENTUNDER(parentData.flags)) && parentData.coreFrame != this; } if (isAccent && firstChild) { // see bug 188467 for what is going on here nscoord dy = aDesiredStretchSize.ascent - (mBoundingMetrics.ascent + leading); aDesiredStretchSize.ascent = mBoundingMetrics.ascent + leading; aDesiredStretchSize.height = aDesiredStretchSize.ascent + mBoundingMetrics.descent; firstChild->SetPosition(firstChild->GetPosition() - nsPoint(0, dy)); } else if (useMathMLChar) { nscoord ascent = fm->MaxAscent(); nscoord descent = fm->MaxDescent(); aDesiredStretchSize.ascent = std::max(mBoundingMetrics.ascent + leading, ascent); aDesiredStretchSize.height = aDesiredStretchSize.ascent + std::max(mBoundingMetrics.descent + leading, descent); } aDesiredStretchSize.width = mBoundingMetrics.width; aDesiredStretchSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredStretchSize.ascent; // Place our mMathMLChar, its origin is in our coordinate system if (useMathMLChar) { nscoord dy = aDesiredStretchSize.ascent - mBoundingMetrics.ascent; mMathMLChar.SetRect(nsRect(0, dy, charSize.width, charSize.ascent + charSize.descent)); } // Before we leave... there is a last item in the check-list: // If our parent is not embellished, it means we are the outermost embellished // container and so we put the spacing, otherwise we don't include the spacing, // the outermost embellished container will take care of it. if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) { // Account the spacing if we are not an accent with explicit attributes nscoord leadingSpace = mEmbellishData.leadingSpace; if (isAccent && !NS_MATHML_OPERATOR_HAS_LSPACE_ATTR(mFlags)) { leadingSpace = 0; } nscoord trailingSpace = mEmbellishData.trailingSpace; if (isAccent && !NS_MATHML_OPERATOR_HAS_RSPACE_ATTR(mFlags)) { trailingSpace = 0; } mBoundingMetrics.width += leadingSpace + trailingSpace; aDesiredStretchSize.width = mBoundingMetrics.width; aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width; nscoord dx = (StyleVisibility()->mDirection ? trailingSpace : leadingSpace); if (dx) { // adjust the offsets mBoundingMetrics.leftBearing += dx; mBoundingMetrics.rightBearing += dx; aDesiredStretchSize.mBoundingMetrics.leftBearing += dx; aDesiredStretchSize.mBoundingMetrics.rightBearing += dx; if (useMathMLChar) { nsRect rect; mMathMLChar.GetRect(rect); mMathMLChar.SetRect(nsRect(rect.x + dx, rect.y, rect.width, rect.height)); } else { nsIFrame* childFrame = firstChild; while (childFrame) { childFrame->SetPosition(childFrame->GetPosition() + nsPoint(dx, 0)); childFrame = childFrame->GetNextSibling(); } } } } // Finished with these: ClearSavedChildMetrics(); // Set our overflow area GatherAndStoreOverflow(&aDesiredStretchSize); // There used to be code here to change the height of the child frame to // change the caret height, but the text frame that manages the caret is now // not a direct child but wrapped in a block frame. See also bug 412033. return NS_OK; }
// get our 'form' and lookup in the Operator Dictionary to fetch // our default data that may come from there. Then complete our setup // using attributes that we may have. To stay in sync, this function is // called very often. We depend on many things that may change around us. // However, we re-use unchanged values. void nsMathMLmoFrame::ProcessOperatorData() { // if we have been here before, we will just use our cached form nsOperatorFlags form = NS_MATHML_OPERATOR_GET_FORM(mFlags); nsAutoString value; // special bits are always kept in mFlags. // remember the mutable bit from ProcessTextData(). // Some chars are listed under different forms in the dictionary, // and there could be a form under which the char is mutable. // If the char is the core of an embellished container, we will keep // it mutable irrespective of the form of the embellished container. // Also remember the other special bits that we want to carry forward. mFlags &= NS_MATHML_OPERATOR_MUTABLE | NS_MATHML_OPERATOR_ACCENT | NS_MATHML_OPERATOR_MOVABLELIMITS | NS_MATHML_OPERATOR_CENTERED | NS_MATHML_OPERATOR_INVISIBLE; if (!mEmbellishData.coreFrame) { // i.e., we haven't been here before, the default form is infix form = NS_MATHML_OPERATOR_FORM_INFIX; // reset everything so that we don't keep outdated values around // in case of dynamic changes mEmbellishData.flags = 0; mEmbellishData.coreFrame = nullptr; mEmbellishData.leadingSpace = 0; mEmbellishData.trailingSpace = 0; if (mMathMLChar.Length() != 1) mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED; // else... retain the native direction obtained in ProcessTextData() if (!mFrames.FirstChild()) { return; } mEmbellishData.flags |= NS_MATHML_EMBELLISH_OPERATOR; mEmbellishData.coreFrame = this; // there are two particular things that we also need to record so that if our // parent is <mover>, <munder>, or <munderover>, they will treat us properly: // 1) do we have accent="true" // 2) do we have movablelimits="true" // they need the extra information to decide how to treat their scripts/limits // (note: <mover>, <munder>, or <munderover> need not necessarily be our // direct parent -- case of embellished operators) // default values from the Operator Dictionary were obtained in ProcessTextData() // and these special bits are always kept in mFlags if (NS_MATHML_OPERATOR_IS_ACCENT(mFlags)) mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENT; if (NS_MATHML_OPERATOR_IS_MOVABLELIMITS(mFlags)) mEmbellishData.flags |= NS_MATHML_EMBELLISH_MOVABLELIMITS; // see if the accent attribute is there GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::accent_, value); if (value.EqualsLiteral("true")) mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENT; else if (value.EqualsLiteral("false")) mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENT; // see if the movablelimits attribute is there GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::movablelimits_, value); if (value.EqualsLiteral("true")) mEmbellishData.flags |= NS_MATHML_EMBELLISH_MOVABLELIMITS; else if (value.EqualsLiteral("false")) mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_MOVABLELIMITS; // --------------------------------------------------------------------- // we will be called again to re-sync the rest of our state next time... // (nobody needs the other values below at this stage) mFlags |= form; return; } nsPresContext* presContext = PresContext(); // beware of bug 133814 - there is a two-way dependency in the // embellished hierarchy: our embellished ancestors need to set // their flags based on some of our state (set above), and here we // need to re-sync our 'form' depending on our outermost embellished // container. A null form here means that an earlier attempt to stretch // our mMathMLChar failed, in which case we don't bother re-stretching again if (form) { // get our outermost embellished container and its parent. // (we ensure that we are the core, not just a sibling of the core) nsIFrame* embellishAncestor = this; nsEmbellishData embellishData; nsIFrame* parentAncestor = this; do { embellishAncestor = parentAncestor; parentAncestor = embellishAncestor->GetParent(); GetEmbellishDataFrom(parentAncestor, embellishData); } while (embellishData.coreFrame == this); // flag if we have an embellished ancestor if (embellishAncestor != this) mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR; else mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR; // find the position of our outermost embellished container w.r.t // its siblings. nsIFrame* nextSibling = embellishAncestor->GetNextSibling(); nsIFrame* prevSibling = embellishAncestor->GetPrevSibling(); // flag to distinguish from a real infix if (!prevSibling && !nextSibling) mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ISOLATED; else mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ISOLATED; // find our form form = NS_MATHML_OPERATOR_FORM_INFIX; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::form, value); if (!value.IsEmpty()) { if (value.EqualsLiteral("prefix")) form = NS_MATHML_OPERATOR_FORM_PREFIX; else if (value.EqualsLiteral("postfix")) form = NS_MATHML_OPERATOR_FORM_POSTFIX; } else { // set our form flag depending on the position if (!prevSibling && nextSibling) form = NS_MATHML_OPERATOR_FORM_PREFIX; else if (prevSibling && !nextSibling) form = NS_MATHML_OPERATOR_FORM_POSTFIX; } mFlags &= ~NS_MATHML_OPERATOR_FORM; // clear the old form bits mFlags |= form; // Use the default value suggested by the MathML REC. // http://www.w3.org/TR/MathML/chapter3.html#presm.mo.attrs // thickmathspace = 5/18em float lspace = 5.0f/18.0f; float rspace = 5.0f/18.0f; if (NS_MATHML_OPERATOR_IS_INVISIBLE(mFlags)) { // mMathMLChar has been reset in ProcessTextData so we can not find it // in the operator dictionary. The operator dictionary always uses // lspace = rspace = 0 for invisible operators. lspace = rspace = 0; } else { // lookup the operator dictionary nsAutoString data; mMathMLChar.GetData(data); nsMathMLOperators::LookupOperator(data, form, &mFlags, &lspace, &rspace); } if (lspace || rspace) { // Cache the default values of lspace and rspace. // since these values are relative to the 'em' unit, convert to twips now nscoord em; nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); GetEmHeight(fm, em); mEmbellishData.leadingSpace = NSToCoordRound(lspace * em); mEmbellishData.trailingSpace = NSToCoordRound(rspace * em); // tuning if we don't want too much extra space when we are a script. // (with its fonts, TeX sets lspace=0 & rspace=0 as soon as scriptlevel>0. // Our fonts can be anything, so...) if (StyleFont()->mScriptLevel > 0) { if (NS_MATHML_OPERATOR_EMBELLISH_IS_ISOLATED(mFlags)) { // could be an isolated accent or script, e.g., x^{+}, just zero out mEmbellishData.leadingSpace = 0; mEmbellishData.trailingSpace = 0; } else if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) { mEmbellishData.leadingSpace /= 2; mEmbellishData.trailingSpace /= 2; } } } } // If we are an accent without explicit lspace="." or rspace=".", // we will ignore our default leading/trailing space // lspace // // "Specifies the leading space appearing before the operator" // // values: length // default: set by dictionary (thickmathspace) // // XXXfredw Support for negative and relative values is not implemented // (bug 805926). // Relative values will give a multiple of the current leading space, // which is not necessarily the default one. // nscoord leadingSpace = mEmbellishData.leadingSpace; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::lspace_, value); if (!value.IsEmpty()) { nsCSSValue cssValue; if (nsMathMLElement::ParseNumericValue(value, cssValue, 0, mContent->OwnerDoc())) { if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue()) leadingSpace = 0; else if (cssValue.IsLengthUnit()) leadingSpace = CalcLength(presContext, mStyleContext, cssValue); mFlags |= NS_MATHML_OPERATOR_LSPACE_ATTR; } } // rspace // // "Specifies the trailing space appearing after the operator" // // values: length // default: set by dictionary (thickmathspace) // // XXXfredw Support for negative and relative values is not implemented // (bug 805926). // Relative values will give a multiple of the current leading space, // which is not necessarily the default one. // nscoord trailingSpace = mEmbellishData.trailingSpace; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::rspace_, value); if (!value.IsEmpty()) { nsCSSValue cssValue; if (nsMathMLElement::ParseNumericValue(value, cssValue, 0, mContent->OwnerDoc())) { if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue()) trailingSpace = 0; else if (cssValue.IsLengthUnit()) trailingSpace = CalcLength(presContext, mStyleContext, cssValue); mFlags |= NS_MATHML_OPERATOR_RSPACE_ATTR; } } // little extra tuning to round lspace & rspace to at least a pixel so that // operators don't look as if they are colliding with their operands if (leadingSpace || trailingSpace) { nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); if (leadingSpace && leadingSpace < onePixel) leadingSpace = onePixel; if (trailingSpace && trailingSpace < onePixel) trailingSpace = onePixel; } // the values that we get from our attributes override the dictionary mEmbellishData.leadingSpace = leadingSpace; mEmbellishData.trailingSpace = trailingSpace; // Now see if there are user-defined attributes that override the dictionary. // XXX If an attribute can be forced to be true when it is false in the // dictionary, then the following code has to change... // For each attribute overriden by the user, turn off its bit flag. // symmetric|movablelimits|separator|largeop|accent|fence|stretchy|form // special: accent and movablelimits are handled above, // don't process them here GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::stretchy_, value); if (value.EqualsLiteral("false")) { mFlags &= ~NS_MATHML_OPERATOR_STRETCHY; } else if (value.EqualsLiteral("true")) { mFlags |= NS_MATHML_OPERATOR_STRETCHY; } if (NS_MATHML_OPERATOR_IS_FENCE(mFlags)) { GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::fence_, value); if (value.EqualsLiteral("false")) mFlags &= ~NS_MATHML_OPERATOR_FENCE; } GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::largeop_, value); if (value.EqualsLiteral("false")) { mFlags &= ~NS_MATHML_OPERATOR_LARGEOP; } else if (value.EqualsLiteral("true")) { mFlags |= NS_MATHML_OPERATOR_LARGEOP; } if (NS_MATHML_OPERATOR_IS_SEPARATOR(mFlags)) { GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::separator_, value); if (value.EqualsLiteral("false")) mFlags &= ~NS_MATHML_OPERATOR_SEPARATOR; } GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::symmetric_, value); if (value.EqualsLiteral("false")) mFlags &= ~NS_MATHML_OPERATOR_SYMMETRIC; else if (value.EqualsLiteral("true")) mFlags |= NS_MATHML_OPERATOR_SYMMETRIC; // minsize // // "Specifies the minimum size of the operator when stretchy" // // values: length // default: set by dictionary (1em) // // We don't allow negative values. // Note: Contrary to other "length" values, unitless and percentage do not // give a multiple of the defaut value but a multiple of the operator at // normal size. // mMinSize = 0; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::minsize_, value); if (!value.IsEmpty()) { nsCSSValue cssValue; if (nsMathMLElement::ParseNumericValue(value, cssValue, nsMathMLElement:: PARSE_ALLOW_UNITLESS, mContent->OwnerDoc())) { nsCSSUnit unit = cssValue.GetUnit(); if (eCSSUnit_Number == unit) mMinSize = cssValue.GetFloatValue(); else if (eCSSUnit_Percent == unit) mMinSize = cssValue.GetPercentValue(); else if (eCSSUnit_Null != unit) { mMinSize = float(CalcLength(presContext, mStyleContext, cssValue)); mFlags |= NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE; } } } // maxsize // // "Specifies the maximum size of the operator when stretchy" // // values: length | "infinity" // default: set by dictionary (infinity) // // We don't allow negative values. // Note: Contrary to other "length" values, unitless and percentage do not // give a multiple of the defaut value but a multiple of the operator at // normal size. // mMaxSize = NS_MATHML_OPERATOR_SIZE_INFINITY; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::maxsize_, value); if (!value.IsEmpty()) { nsCSSValue cssValue; if (nsMathMLElement::ParseNumericValue(value, cssValue, nsMathMLElement:: PARSE_ALLOW_UNITLESS, mContent->OwnerDoc())) { nsCSSUnit unit = cssValue.GetUnit(); if (eCSSUnit_Number == unit) mMaxSize = cssValue.GetFloatValue(); else if (eCSSUnit_Percent == unit) mMaxSize = cssValue.GetPercentValue(); else if (eCSSUnit_Null != unit) { mMaxSize = float(CalcLength(presContext, mStyleContext, cssValue)); mFlags |= NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE; } } } }
nsresult nsMathMLmfracFrame::PlaceInternal(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize, bool aWidthOnly) { //////////////////////////////////// // Get the children's desired sizes nsBoundingMetrics bmNum, bmDen; nsHTMLReflowMetrics sizeNum; nsHTMLReflowMetrics sizeDen; nsIFrame* frameDen = nsnull; nsIFrame* frameNum = mFrames.FirstChild(); if (frameNum) frameDen = frameNum->GetNextSibling(); if (!frameNum || !frameDen || frameDen->GetNextSibling()) { // report an error, encourage people to get their markups in order return ReflowError(aRenderingContext, aDesiredSize); } GetReflowAndBoundingMetricsFor(frameNum, sizeNum, bmNum); GetReflowAndBoundingMetricsFor(frameDen, sizeDen, bmDen); nsPresContext* presContext = PresContext(); nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); aRenderingContext.SetFont(fm); nscoord defaultRuleThickness, axisHeight; GetRuleThickness(aRenderingContext, fm, defaultRuleThickness); GetAxisHeight(aRenderingContext, fm, axisHeight); nsEmbellishData coreData; GetEmbellishDataFrom(mEmbellishData.coreFrame, coreData); // see if the linethickness attribute is there nsAutoString value; GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::linethickness_, value); mLineThickness = CalcLineThickness(presContext, mStyleContext, value, onePixel, defaultRuleThickness); // bevelled attribute GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::bevelled_, value); mIsBevelled = value.EqualsLiteral("true"); if (!mIsBevelled) { mLineRect.height = mLineThickness; // by default, leave at least one-pixel padding at either end, or use // lspace & rspace that may come from <mo> if we are an embellished // container (we fetch values from the core since they may use units that // depend on style data, and style changes could have occurred in the // core since our last visit there) nscoord leftSpace = NS_MAX(onePixel, coreData.leftSpace); nscoord rightSpace = NS_MAX(onePixel, coreData.rightSpace); ////////////////// // Get shifts nscoord numShift = 0; nscoord denShift = 0; // Rule 15b, App. G, TeXbook nscoord numShift1, numShift2, numShift3; nscoord denShift1, denShift2; GetNumeratorShifts(fm, numShift1, numShift2, numShift3); GetDenominatorShifts(fm, denShift1, denShift2); if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) { // C > T numShift = numShift1; denShift = denShift1; } else { numShift = (0 < mLineRect.height) ? numShift2 : numShift3; denShift = denShift2; } nscoord minClearance = 0; nscoord actualClearance = 0; nscoord actualRuleThickness = mLineThickness; if (0 == actualRuleThickness) { // Rule 15c, App. G, TeXbook // min clearance between numerator and denominator minClearance = (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) ? 7 * defaultRuleThickness : 3 * defaultRuleThickness; actualClearance = (numShift - bmNum.descent) - (bmDen.ascent - denShift); // actualClearance should be >= minClearance if (actualClearance < minClearance) { nscoord halfGap = (minClearance - actualClearance)/2; numShift += halfGap; denShift += halfGap; } } else { // Rule 15d, App. G, TeXbook // min clearance between numerator or denominator and middle of bar // TeX has a different interpretation of the thickness. // Try $a \above10pt b$ to see. Here is what TeX does: // minClearance = (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) ? // 3 * actualRuleThickness : actualRuleThickness; // we slightly depart from TeX here. We use the defaultRuleThickness instead // of the value coming from the linethickness attribute, i.e., we recover what // TeX does if the user hasn't set linethickness. But when the linethickness // is set, we avoid the wide gap problem. minClearance = (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) ? 3 * defaultRuleThickness : defaultRuleThickness + onePixel; // adjust numShift to maintain minClearance if needed actualClearance = (numShift - bmNum.descent) - (axisHeight + actualRuleThickness/2); if (actualClearance < minClearance) { numShift += (minClearance - actualClearance); } // adjust denShift to maintain minClearance if needed actualClearance = (axisHeight - actualRuleThickness/2) - (bmDen.ascent - denShift); if (actualClearance < minClearance) { denShift += (minClearance - actualClearance); } } ////////////////// // Place Children // XXX Need revisiting the width. TeX uses the exact width // e.g. in $$\huge\frac{\displaystyle\int}{i}$$ nscoord width = NS_MAX(bmNum.width, bmDen.width); nscoord dxNum = leftSpace + (width - sizeNum.width)/2; nscoord dxDen = leftSpace + (width - sizeDen.width)/2; width += leftSpace + rightSpace; // see if the numalign attribute is there GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::numalign_, value); if (value.EqualsLiteral("left")) dxNum = leftSpace; else if (value.EqualsLiteral("right")) dxNum = width - rightSpace - sizeNum.width; // see if the denomalign attribute is there GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::denomalign_, value); if (value.EqualsLiteral("left")) dxDen = leftSpace; else if (value.EqualsLiteral("right")) dxDen = width - rightSpace - sizeDen.width; mBoundingMetrics.rightBearing = NS_MAX(dxNum + bmNum.rightBearing, dxDen + bmDen.rightBearing); if (mBoundingMetrics.rightBearing < width - rightSpace) mBoundingMetrics.rightBearing = width - rightSpace; mBoundingMetrics.leftBearing = NS_MIN(dxNum + bmNum.leftBearing, dxDen + bmDen.leftBearing); if (mBoundingMetrics.leftBearing > leftSpace) mBoundingMetrics.leftBearing = leftSpace; mBoundingMetrics.ascent = bmNum.ascent + numShift; mBoundingMetrics.descent = bmDen.descent + denShift; mBoundingMetrics.width = width; aDesiredSize.ascent = sizeNum.ascent + numShift; aDesiredSize.height = aDesiredSize.ascent + sizeDen.height - sizeDen.ascent + denShift; aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.ascent; if (aPlaceOrigin) { nscoord dy; // place numerator dy = 0; FinishReflowChild(frameNum, presContext, nsnull, sizeNum, dxNum, dy, 0); // place denominator dy = aDesiredSize.height - sizeDen.height; FinishReflowChild(frameDen, presContext, nsnull, sizeDen, dxDen, dy, 0); // place the fraction bar - dy is top of bar dy = aDesiredSize.ascent - (axisHeight + actualRuleThickness/2); mLineRect.SetRect(leftSpace, dy, width - (leftSpace + rightSpace), actualRuleThickness); } } else { nscoord numShift = 0.0; nscoord denShift = 0.0; nscoord padding = 3 * defaultRuleThickness; nscoord slashRatio = 3; // Define the constant used in the expression of the maximum width nscoord em = fm->EmHeight(); nscoord slashMaxWidthConstant = 2 * em; // For large line thicknesses the minimum slash height is limited to the // largest expected height of a fraction nscoord slashMinHeight = slashRatio * NS_MIN(2 * mLineThickness, slashMaxWidthConstant); nscoord leftSpace = NS_MAX(padding, coreData.leftSpace); nscoord rightSpace = NS_MAX(padding, coreData.rightSpace); nscoord delta; // ___________ // | | / // {|-NUMERATOR-| / // {|___________| S // { L // numShift{ A // ------------------------------------------------------- baseline // S _____________ } denShift // H | |} // / |-DENOMINATOR-|} // / |_____________| // // first, ensure that the top of the numerator is at least as high as the // top of the denominator (and the reverse for the bottoms) delta = NS_MAX(bmDen.ascent - bmNum.ascent, bmNum.descent - bmDen.descent) / 2; if (delta > 0) { numShift += delta; denShift += delta; } if (NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags)) { delta = NS_MIN(bmDen.ascent + bmDen.descent, bmNum.ascent + bmNum.descent) / 2; numShift += delta; denShift += delta; } else { nscoord xHeight = fm->XHeight(); numShift += xHeight / 2; denShift += xHeight / 4; } // Set the ascent/descent of our BoundingMetrics. mBoundingMetrics.ascent = bmNum.ascent + numShift; mBoundingMetrics.descent = bmDen.descent + denShift; // At this point the height of the slash is // mBoundingMetrics.ascent + mBoundingMetrics.descent // Ensure that it is greater than slashMinHeight delta = (slashMinHeight - (mBoundingMetrics.ascent + mBoundingMetrics.descent)) / 2; if (delta > 0) { mBoundingMetrics.ascent += delta; mBoundingMetrics.descent += delta; } // Set the width of the slash if (aWidthOnly) { mLineRect.width = mLineThickness + slashMaxWidthConstant; } else { mLineRect.width = mLineThickness + NS_MIN(slashMaxWidthConstant, (mBoundingMetrics.ascent + mBoundingMetrics.descent) / slashRatio); } // Set horizontal bounding metrics mBoundingMetrics.leftBearing = leftSpace + bmNum.leftBearing; mBoundingMetrics.rightBearing = leftSpace + bmNum.width + mLineRect.width + bmDen.rightBearing; mBoundingMetrics.width = leftSpace + bmNum.width + mLineRect.width + bmDen.width + rightSpace; // Set aDesiredSize aDesiredSize.ascent = mBoundingMetrics.ascent + padding; aDesiredSize.height = mBoundingMetrics.ascent + mBoundingMetrics.descent + 2 * padding; aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.ascent; if (aPlaceOrigin) { FinishReflowChild(frameNum, presContext, nsnull, sizeNum, leftSpace, aDesiredSize.ascent - numShift - sizeNum.ascent, 0); mLineRect.SetRect(leftSpace + bmNum.width, aDesiredSize.ascent - mBoundingMetrics.ascent, mLineRect.width, aDesiredSize.height - 2 * padding); FinishReflowChild(frameDen, presContext, nsnull, sizeDen, leftSpace + bmNum.width + mLineRect.width, aDesiredSize.ascent + denShift - sizeDen.ascent, 0); } } return NS_OK; }
/* virtual */ nsresult 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; }