// 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; float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); // 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 mContent->GetAttr(kNameSpaceID_None, 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 mContent->GetAttr(kNameSpaceID_None, 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. Set for (embellished) operators // that live in (inferred) mrows. nsIMathMLFrame* mathAncestor = do_QueryFrame(parentAncestor); bool zeroSpacing = false; if (mathAncestor) { zeroSpacing = !mathAncestor->IsMrowLike(); } else { nsMathMLmathBlockFrame* blockFrame = do_QueryFrame(parentAncestor); if (blockFrame) { zeroSpacing = !blockFrame->IsMrowLike(); } } if (zeroSpacing) { mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ISOLATED; } else { mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ISOLATED; } // find our form form = NS_MATHML_OPERATOR_FORM_INFIX; mContent->GetAttr(kNameSpaceID_None, 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; // lookup the operator dictionary nsAutoString data; mMathMLChar.GetData(data); nsMathMLOperators::LookupOperator(data, form, &mFlags, &lspace, &rspace); // Spacing is zero if our outermost embellished operator is not in an // inferred mrow. if (!NS_MATHML_OPERATOR_EMBELLISH_IS_ISOLATED(mFlags) && (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), fontSizeInflation); 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 && !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; mContent->GetAttr(kNameSpaceID_None, 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, fontSizeInflation); 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; mContent->GetAttr(kNameSpaceID_None, 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, fontSizeInflation); 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 Bug 1197771 - forcing an attribute to true when it is false in the // dictionary can cause conflicts in the rest of the stretching algorithms // (e.g. all largeops are assumed to have a vertical direction) // 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 mContent->GetAttr(kNameSpaceID_None, 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)) { mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::fence_, value); if (value.EqualsLiteral("false")) mFlags &= ~NS_MATHML_OPERATOR_FENCE; else mEmbellishData.flags |= NS_MATHML_EMBELLISH_FENCE; } mContent->GetAttr(kNameSpaceID_None, 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)) { mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::separator_, value); if (value.EqualsLiteral("false")) mFlags &= ~NS_MATHML_OPERATOR_SEPARATOR; else mEmbellishData.flags |= NS_MATHML_EMBELLISH_SEPARATOR; } mContent->GetAttr(kNameSpaceID_None, 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; mContent->GetAttr(kNameSpaceID_None, 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, fontSizeInflation)); 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; mContent->GetAttr(kNameSpaceID_None, 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, fontSizeInflation)); mFlags |= NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE; } } } }
// 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; float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), fontSizeInflation); 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, StyleFont()); 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, fontSizeInflation, 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; } } // 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(GetParent(), 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.BlockStartAscent() - (mBoundingMetrics.ascent + leading); aDesiredStretchSize.SetBlockStartAscent(mBoundingMetrics.ascent + leading); aDesiredStretchSize.Height() = aDesiredStretchSize.BlockStartAscent() + mBoundingMetrics.descent; firstChild->SetPosition(firstChild->GetPosition() - nsPoint(0, dy)); } else if (useMathMLChar) { nscoord ascent = fm->MaxAscent(); nscoord descent = fm->MaxDescent(); aDesiredStretchSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent + leading, ascent)); aDesiredStretchSize.Height() = aDesiredStretchSize.BlockStartAscent() + std::max(mBoundingMetrics.descent + leading, descent); } aDesiredStretchSize.Width() = mBoundingMetrics.width; aDesiredStretchSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredStretchSize.BlockStartAscent(); // Place our mMathMLChar, its origin is in our coordinate system if (useMathMLChar) { nscoord dy = aDesiredStretchSize.BlockStartAscent() - 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 the text that we enclose and setup our nsMathMLChar void nsMathMLmoFrame::ProcessTextData() { mFlags = 0; nsAutoString data; nsContentUtils::GetNodeTextContent(mContent, false, data); data.CompressWhitespace(); int32_t length = data.Length(); char16_t ch = (length == 0) ? char16_t('\0') : data[0]; if ((length == 1) && (ch == kApplyFunction || ch == kInvisibleSeparator || ch == kInvisiblePlus || ch == kInvisibleTimes)) { mFlags |= NS_MATHML_OPERATOR_INVISIBLE; } // don't bother doing anything special if we don't have a single child nsPresContext* presContext = PresContext(); if (mFrames.GetLength() != 1) { data.Truncate(); // empty data to reset the char mMathMLChar.SetData(presContext, data); ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar); return; } // special... in math mode, the usual minus sign '-' looks too short, so // what we do here is to remap <mo>-</mo> to the official Unicode minus // sign (U+2212) which looks much better. For background on this, see // http://groups.google.com/groups?hl=en&th=66488daf1ade7635&rnum=1 if (1 == length && ch == '-') { ch = 0x2212; data = ch; } // cache the special bits: mutable, accent, movablelimits, centered. // we need to do this in anticipation of other requirements, and these // bits don't change. Do not reset these bits unless the text gets changed. // lookup all the forms under which the operator is listed in the dictionary, // and record whether the operator has accent="true" or movablelimits="true" nsOperatorFlags flags[4]; float lspace[4], rspace[4]; nsMathMLOperators::LookupOperators(data, flags, lspace, rspace); nsOperatorFlags allFlags = flags[NS_MATHML_OPERATOR_FORM_INFIX] | flags[NS_MATHML_OPERATOR_FORM_POSTFIX] | flags[NS_MATHML_OPERATOR_FORM_PREFIX]; mFlags |= allFlags & NS_MATHML_OPERATOR_ACCENT; mFlags |= allFlags & NS_MATHML_OPERATOR_MOVABLELIMITS; // see if this is an operator that should be centered to cater for // fonts that are not math-aware if (1 == length) { if ((ch == '+') || (ch == '=') || (ch == '*') || (ch == 0x2212) || // − (ch == 0x2264) || // ≤ (ch == 0x2265) || // ≥ (ch == 0x00D7)) { // × mFlags |= NS_MATHML_OPERATOR_CENTERED; } } // cache the operator mMathMLChar.SetData(presContext, data); // cache the native direction -- beware of bug 133429... // mEmbellishData.direction must always retain our native direction, whereas // mMathMLChar.GetStretchDirection() may change later, when Stretch() is called mEmbellishData.direction = mMathMLChar.GetStretchDirection(); bool isMutable = NS_MATHML_OPERATOR_IS_LARGEOP(allFlags) || (mEmbellishData.direction != NS_STRETCH_DIRECTION_UNSUPPORTED); if (isMutable) mFlags |= NS_MATHML_OPERATOR_MUTABLE; ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar); }
/* virtual */ nsresult nsMathMLmmultiscriptsFrame::Place(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { //////////////////////////////////// // Get the children's desired sizes nscoord minShiftFromXHeight, subDrop, supDrop; //////////////////////////////////////// // Initialize super/sub shifts that // depend only on the current font //////////////////////////////////////// ProcessAttributes(); // get x-height (an ex) const nsStyleFont* font = GetStyleFont(); nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm)); aRenderingContext.SetFont(fm); nscoord xHeight = fm->XHeight(); nscoord ruleSize; GetRuleThickness (aRenderingContext, fm, ruleSize); // scriptspace from TeX for extra spacing after sup/subscript (0.5pt in plain TeX) // forced to be at least 1 pixel here nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); nscoord scriptSpace = NS_MAX(nsPresContext::CSSPointsToAppUnits(0.5f), onePixel); ///////////////////////////////////// // first the shift for the subscript // subScriptShift{1,2} // = minimum amount to shift the subscript down // = sub{1,2} in TeXbook // subScriptShift1 = subscriptshift attribute * x-height nscoord subScriptShift1, subScriptShift2; // Get subScriptShift{1,2} default from font GetSubScriptShifts (fm, subScriptShift1, subScriptShift2); if (0 < mSubScriptShift) { // the user has set the subscriptshift attribute float scaler = ((float) subScriptShift2) / subScriptShift1; subScriptShift1 = NS_MAX(subScriptShift1, mSubScriptShift); subScriptShift2 = NSToCoordRound(scaler * subScriptShift1); } // the font dependent shift nscoord subScriptShift = NS_MAX(subScriptShift1,subScriptShift2); ///////////////////////////////////// // next the shift for the superscript // supScriptShift{1,2,3} // = minimum amount to shift the supscript up // = sup{1,2,3} in TeX // supScriptShift1 = superscriptshift attribute * x-height // Note that there are THREE values for supscript shifts depending // on the current style nscoord supScriptShift1, supScriptShift2, supScriptShift3; // Set supScriptShift{1,2,3} default from font GetSupScriptShifts (fm, supScriptShift1, supScriptShift2, supScriptShift3); if (0 < mSupScriptShift) { // the user has set the superscriptshift attribute float scaler2 = ((float) supScriptShift2) / supScriptShift1; float scaler3 = ((float) supScriptShift3) / supScriptShift1; supScriptShift1 = NS_MAX(supScriptShift1, mSupScriptShift); supScriptShift2 = NSToCoordRound(scaler2 * supScriptShift1); supScriptShift3 = NSToCoordRound(scaler3 * supScriptShift1); } // get sup script shift depending on current script level and display style // Rule 18c, App. G, TeXbook nscoord supScriptShift; if ( font->mScriptLevel == 0 && NS_MATHML_IS_DISPLAYSTYLE(mPresentationData.flags) && !NS_MATHML_IS_COMPRESSED(mPresentationData.flags)) { // Style D in TeXbook supScriptShift = supScriptShift1; } else if (NS_MATHML_IS_COMPRESSED(mPresentationData.flags)) { // Style C' in TeXbook = D',T',S',SS' supScriptShift = supScriptShift3; } else { // everything else = T,S,SS supScriptShift = supScriptShift2; } //////////////////////////////////// // Get the children's sizes //////////////////////////////////// nscoord width = 0, prescriptsWidth = 0, rightBearing = 0; nsIFrame* mprescriptsFrame = nsnull; // frame of <mprescripts/>, if there. bool isSubScript = false; nscoord minSubScriptShift = 0, minSupScriptShift = 0; nscoord trySubScriptShift = subScriptShift; nscoord trySupScriptShift = supScriptShift; nscoord maxSubScriptShift = subScriptShift; nscoord maxSupScriptShift = supScriptShift; PRInt32 count = 0; nsHTMLReflowMetrics baseSize; nsHTMLReflowMetrics subScriptSize; nsHTMLReflowMetrics supScriptSize; nsIFrame* baseFrame = nsnull; nsIFrame* subScriptFrame = nsnull; nsIFrame* supScriptFrame = nsnull; bool firstPrescriptsPair = false; nsBoundingMetrics bmBase, bmSubScript, bmSupScript; nscoord italicCorrection = 0; mBoundingMetrics.width = 0; mBoundingMetrics.ascent = mBoundingMetrics.descent = -0x7FFFFFFF; nscoord ascent = -0x7FFFFFFF, descent = -0x7FFFFFFF; aDesiredSize.width = aDesiredSize.height = 0; nsIFrame* childFrame = mFrames.FirstChild(); while (childFrame) { if (childFrame->GetContent()->Tag() == nsGkAtoms::mprescripts_) { if (mprescriptsFrame) { // duplicate <mprescripts/> found // report an error, encourage people to get their markups in order return ReflowError(aRenderingContext, aDesiredSize); } mprescriptsFrame = childFrame; firstPrescriptsPair = true; } else { if (0 == count) { // base baseFrame = childFrame; GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); GetItalicCorrection(bmBase, italicCorrection); // for the superscript, we always add "a little to spare" italicCorrection += onePixel; // we update mBoundingMetrics.{ascent,descent} with that // of the baseFrame only after processing all the sup/sub pairs // XXX need italic correction only *if* there are postscripts ? mBoundingMetrics.width = bmBase.width + italicCorrection; mBoundingMetrics.rightBearing = bmBase.rightBearing; mBoundingMetrics.leftBearing = bmBase.leftBearing; // until overwritten } else { // super/subscript block if (isSubScript) { // subscript subScriptFrame = childFrame; GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript); // get the subdrop from the subscript font GetSubDropFromChild (subScriptFrame, subDrop); // parameter v, Rule 18a, App. G, TeXbook minSubScriptShift = bmBase.descent + subDrop; trySubScriptShift = NS_MAX(minSubScriptShift,subScriptShift); mBoundingMetrics.descent = NS_MAX(mBoundingMetrics.descent,bmSubScript.descent); descent = NS_MAX(descent,subScriptSize.height - subScriptSize.ascent); width = bmSubScript.width + scriptSpace; rightBearing = bmSubScript.rightBearing; } else { // supscript supScriptFrame = childFrame; GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript); // get the supdrop from the supscript font GetSupDropFromChild (supScriptFrame, supDrop); // parameter u, Rule 18a, App. G, TeXbook minSupScriptShift = bmBase.ascent - supDrop; // get min supscript shift limit from x-height // = d(x) + 1/4 * sigma_5, Rule 18c, App. G, TeXbook minShiftFromXHeight = NSToCoordRound ((bmSupScript.descent + (1.0f/4.0f) * xHeight)); trySupScriptShift = NS_MAX(minSupScriptShift,NS_MAX(minShiftFromXHeight,supScriptShift)); mBoundingMetrics.ascent = NS_MAX(mBoundingMetrics.ascent,bmSupScript.ascent); ascent = NS_MAX(ascent,supScriptSize.ascent); width = NS_MAX(width, bmSupScript.width + scriptSpace); rightBearing = NS_MAX(rightBearing, bmSupScript.rightBearing); if (!mprescriptsFrame) { // we are still looping over base & postscripts mBoundingMetrics.rightBearing = mBoundingMetrics.width + rightBearing; mBoundingMetrics.width += width; } else { prescriptsWidth += width; if (firstPrescriptsPair) { firstPrescriptsPair = false; mBoundingMetrics.leftBearing = NS_MIN(bmSubScript.leftBearing, bmSupScript.leftBearing); } } width = rightBearing = 0; // negotiate between the various shifts so that // there is enough gap between the sup and subscripts // Rule 18e, App. G, TeXbook nscoord gap = (trySupScriptShift - bmSupScript.descent) - (bmSubScript.ascent - trySubScriptShift); if (gap < 4.0f * ruleSize) { // adjust trySubScriptShift to get a gap of (4.0 * ruleSize) trySubScriptShift += NSToCoordRound ((4.0f * ruleSize) - gap); } // next we want to ensure that the bottom of the superscript // will be > (4/5) * x-height above baseline gap = NSToCoordRound ((4.0f/5.0f) * xHeight - (trySupScriptShift - bmSupScript.descent)); if (gap > 0.0f) { trySupScriptShift += gap; trySubScriptShift -= gap; } maxSubScriptShift = NS_MAX(maxSubScriptShift, trySubScriptShift); maxSupScriptShift = NS_MAX(maxSupScriptShift, trySupScriptShift); trySubScriptShift = subScriptShift; trySupScriptShift = supScriptShift; } } isSubScript = !isSubScript; } count++; childFrame = childFrame->GetNextSibling(); } // note: width=0 if all sup-sub pairs match correctly if ((0 != width) || !baseFrame || !subScriptFrame || !supScriptFrame) { // report an error, encourage people to get their markups in order return ReflowError(aRenderingContext, aDesiredSize); } // we left out the width of prescripts, so ... mBoundingMetrics.rightBearing += prescriptsWidth; mBoundingMetrics.width += prescriptsWidth; // we left out the base during our bounding box updates, so ... mBoundingMetrics.ascent = NS_MAX(mBoundingMetrics.ascent+maxSupScriptShift,bmBase.ascent); mBoundingMetrics.descent = NS_MAX(mBoundingMetrics.descent+maxSubScriptShift,bmBase.descent); // get the reflow metrics ... aDesiredSize.ascent = NS_MAX(ascent+maxSupScriptShift,baseSize.ascent); aDesiredSize.height = aDesiredSize.ascent + NS_MAX(descent+maxSubScriptShift,baseSize.height - baseSize.ascent); aDesiredSize.width = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.ascent; ////////////////// // Place Children // Place prescripts, followed by base, and then postscripts. // The list of frames is in the order: {base} {postscripts} {prescripts} // We go over the list in a circular manner, starting at <prescripts/> if (aPlaceOrigin) { nscoord dx = 0, dy = 0; count = 0; childFrame = mprescriptsFrame; do { if (!childFrame) { // end of prescripts, // place the base ... childFrame = baseFrame; dy = aDesiredSize.ascent - baseSize.ascent; FinishReflowChild (baseFrame, PresContext(), nsnull, baseSize, dx, dy, 0); dx += bmBase.width + italicCorrection; } else if (mprescriptsFrame != childFrame) { // process each sup/sub pair if (0 == count) { subScriptFrame = childFrame; count = 1; } else if (1 == count) { supScriptFrame = childFrame; count = 0; // get the ascent/descent of sup/subscripts stored in their rects // rect.x = descent, rect.y = ascent GetReflowAndBoundingMetricsFor(subScriptFrame, subScriptSize, bmSubScript); GetReflowAndBoundingMetricsFor(supScriptFrame, supScriptSize, bmSupScript); // center w.r.t. largest width width = NS_MAX(subScriptSize.width, supScriptSize.width); dy = aDesiredSize.ascent - subScriptSize.ascent + maxSubScriptShift; FinishReflowChild (subScriptFrame, PresContext(), nsnull, subScriptSize, dx + (width-subScriptSize.width)/2, dy, 0); dy = aDesiredSize.ascent - supScriptSize.ascent - maxSupScriptShift; FinishReflowChild (supScriptFrame, PresContext(), nsnull, supScriptSize, dx + (width-supScriptSize.width)/2, dy, 0); dx += width + scriptSpace; } } childFrame = childFrame->GetNextSibling(); } while (mprescriptsFrame != childFrame); } return NS_OK; }
/* virtual */ nsresult nsMathMLmunderoverFrame::Place(nsRenderingContext& aRenderingContext, bool aPlaceOrigin, nsHTMLReflowMetrics& aDesiredSize) { float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this); if (NS_MATHML_EMBELLISH_IS_MOVABLELIMITS(mEmbellishData.flags) && StyleFont()->mMathDisplay == NS_MATHML_DISPLAYSTYLE_INLINE) { //place like sub sup or subsup if (mContent->IsMathMLElement(nsGkAtoms::munderover_)) { return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), aRenderingContext, aPlaceOrigin, aDesiredSize, this, 0, 0, fontSizeInflation); } else if (mContent->IsMathMLElement( nsGkAtoms::munder_)) { return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), aRenderingContext, aPlaceOrigin, aDesiredSize, this, 0, 0, fontSizeInflation); } else { NS_ASSERTION(mContent->IsMathMLElement(nsGkAtoms::mover_), "mContent->NodeInfo()->NameAtom() not recognized"); return nsMathMLmmultiscriptsFrame::PlaceMultiScript(PresContext(), aRenderingContext, aPlaceOrigin, aDesiredSize, this, 0, 0, fontSizeInflation); } } //////////////////////////////////// // Get the children's desired sizes nsBoundingMetrics bmBase, bmUnder, bmOver; nsHTMLReflowMetrics baseSize(aDesiredSize.GetWritingMode()); nsHTMLReflowMetrics underSize(aDesiredSize.GetWritingMode()); nsHTMLReflowMetrics overSize(aDesiredSize.GetWritingMode()); nsIFrame* overFrame = nullptr; nsIFrame* underFrame = nullptr; nsIFrame* baseFrame = mFrames.FirstChild(); underSize.SetBlockStartAscent(0); overSize.SetBlockStartAscent(0); bool haveError = false; if (baseFrame) { if (mContent->IsAnyOfMathMLElements(nsGkAtoms::munder_, nsGkAtoms::munderover_)) { underFrame = baseFrame->GetNextSibling(); } else if (mContent->IsMathMLElement(nsGkAtoms::mover_)) { overFrame = baseFrame->GetNextSibling(); } } if (underFrame && mContent->IsMathMLElement(nsGkAtoms::munderover_)) { overFrame = underFrame->GetNextSibling(); } if (mContent->IsMathMLElement(nsGkAtoms::munder_)) { if (!baseFrame || !underFrame || underFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order haveError = true; } } if (mContent->IsMathMLElement(nsGkAtoms::mover_)) { if (!baseFrame || !overFrame || overFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order haveError = true; } } if (mContent->IsMathMLElement(nsGkAtoms::munderover_)) { if (!baseFrame || !underFrame || !overFrame || overFrame->GetNextSibling()) { // report an error, encourage people to get their markups in order haveError = true; } } if (haveError) { if (aPlaceOrigin) { ReportChildCountError(); } return ReflowError(aRenderingContext, aDesiredSize); } GetReflowAndBoundingMetricsFor(baseFrame, baseSize, bmBase); if (underFrame) { GetReflowAndBoundingMetricsFor(underFrame, underSize, bmUnder); } if (overFrame) { GetReflowAndBoundingMetricsFor(overFrame, overSize, bmOver); } nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); //////////////////// // Place Children nsRefPtr<nsFontMetrics> fm; nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), fontSizeInflation); nscoord xHeight = fm->XHeight(); nscoord oneDevPixel = fm->AppUnitsPerDevPixel(); gfxFont* mathFont = fm->GetThebesFontGroup()->GetFirstMathFont(); nscoord ruleThickness; GetRuleThickness (aRenderingContext, fm, ruleThickness); nscoord correction = 0; GetItalicCorrection (bmBase, correction); // there are 2 different types of placement depending on // whether we want an accented under or not nscoord underDelta1 = 0; // gap between base and underscript nscoord underDelta2 = 0; // extra space beneath underscript if (!NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) { // Rule 13a, App. G, TeXbook nscoord bigOpSpacing2, bigOpSpacing4, bigOpSpacing5, dummy; GetBigOpSpacings (fm, dummy, bigOpSpacing2, dummy, bigOpSpacing4, bigOpSpacing5); if (mathFont) { // XXXfredw The Open Type MATH table has some StretchStack* parameters // that we may use when the base is a stretchy horizontal operator. See // bug 963131. bigOpSpacing2 = mathFont->GetMathConstant(gfxFontEntry::LowerLimitGapMin, oneDevPixel); bigOpSpacing4 = mathFont->GetMathConstant(gfxFontEntry::LowerLimitBaselineDropMin, oneDevPixel); bigOpSpacing5 = 0; } underDelta1 = std::max(bigOpSpacing2, (bigOpSpacing4 - bmUnder.ascent)); underDelta2 = bigOpSpacing5; } else { // No corresponding rule in TeXbook - we are on our own here // XXX tune the gap delta between base and underscript // XXX Should we use Rule 10 like \underline does? // XXXfredw Perhaps use the Underbar* parameters of the MATH table. See // bug 963125. underDelta1 = ruleThickness + onePixel/2; underDelta2 = ruleThickness; } // empty under? if (!(bmUnder.ascent + bmUnder.descent)) { underDelta1 = 0; underDelta2 = 0; } nscoord overDelta1 = 0; // gap between base and overscript nscoord overDelta2 = 0; // extra space above overscript if (!NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) { // Rule 13a, App. G, TeXbook // XXXfredw The Open Type MATH table has some StretchStack* parameters // that we may use when the base is a stretchy horizontal operator. See // bug 963131. nscoord bigOpSpacing1, bigOpSpacing3, bigOpSpacing5, dummy; GetBigOpSpacings (fm, bigOpSpacing1, dummy, bigOpSpacing3, dummy, bigOpSpacing5); if (mathFont) { // XXXfredw The Open Type MATH table has some StretchStack* parameters // that we may use when the base is a stretchy horizontal operator. See // bug 963131. bigOpSpacing1 = mathFont->GetMathConstant(gfxFontEntry::UpperLimitGapMin, oneDevPixel); bigOpSpacing3 = mathFont->GetMathConstant(gfxFontEntry::UpperLimitBaselineRiseMin, oneDevPixel); bigOpSpacing5 = 0; } overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - bmOver.descent)); overDelta2 = bigOpSpacing5; // XXX This is not a TeX rule... // delta1 (as computed abvove) can become really big when bmOver.descent is // negative, e.g., if the content is &OverBar. In such case, we use the height if (bmOver.descent < 0) overDelta1 = std::max(bigOpSpacing1, (bigOpSpacing3 - (bmOver.ascent + bmOver.descent))); } else { // Rule 12, App. G, TeXbook // We are going to modify this rule to make it more general. // The idea behind Rule 12 in the TeXBook is to keep the accent // as close to the base as possible, while ensuring that the // distance between the *baseline* of the accent char and // the *baseline* of the base is atleast x-height. // The idea is that for normal use, we would like all the accents // on a line to line up atleast x-height above the baseline // if possible. // When the ascent of the base is >= x-height, // the baseline of the accent char is placed just above the base // (specifically, the baseline of the accent char is placed // above the baseline of the base by the ascent of the base). // For ease of implementation, // this assumes that the font-designer designs accents // in such a way that the bottom of the accent is atleast x-height // above its baseline, otherwise there will be collisions // with the base. Also there should be proper padding between // the bottom of the accent char and its baseline. // The above rule may not be obvious from a first // reading of rule 12 in the TeXBook !!! // The mathml <mover> tag can use accent chars that // do not follow this convention. So we modify TeX's rule // so that TeX's rule gets subsumed for accents that follow // TeX's convention, // while also allowing accents that do not follow the convention : // we try to keep the *bottom* of the accent char atleast x-height // from the baseline of the base char. we also slap on an extra // padding between the accent and base chars. overDelta1 = ruleThickness + onePixel/2; nscoord accentBaseHeight = xHeight; if (mathFont) { accentBaseHeight = mathFont->GetMathConstant(gfxFontEntry::AccentBaseHeight, oneDevPixel); } if (bmBase.ascent < accentBaseHeight) { // also ensure at least accentBaseHeight above the baseline of the base overDelta1 += accentBaseHeight - bmBase.ascent; } overDelta2 = ruleThickness; } // empty over? if (!(bmOver.ascent + bmOver.descent)) { overDelta1 = 0; overDelta2 = 0; } nscoord dxBase = 0, dxOver = 0, dxUnder = 0; nsAutoString valueAlign; enum { center, left, right } alignPosition = center; if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, valueAlign)) { if (valueAlign.EqualsLiteral("left")) { alignPosition = left; } else if (valueAlign.EqualsLiteral("right")) { alignPosition = right; } } ////////// // pass 1, do what <mover> does: attach the overscript on the base // Ad-hoc - This is to override fonts which have ready-made _accent_ // glyphs with negative lbearing and rbearing. We want to position // the overscript ourselves nscoord overWidth = bmOver.width; if (!overWidth && (bmOver.rightBearing - bmOver.leftBearing > 0)) { overWidth = bmOver.rightBearing - bmOver.leftBearing; dxOver = -bmOver.leftBearing; } if (NS_MATHML_EMBELLISH_IS_ACCENTOVER(mEmbellishData.flags)) { mBoundingMetrics.width = bmBase.width; if (alignPosition == center) { dxOver += correction; } } else { mBoundingMetrics.width = std::max(bmBase.width, overWidth); if (alignPosition == center) { dxOver += correction/2; } } if (alignPosition == center) { dxOver += (mBoundingMetrics.width - overWidth)/2; dxBase = (mBoundingMetrics.width - bmBase.width)/2; } else if (alignPosition == right) { dxOver += mBoundingMetrics.width - overWidth; dxBase = mBoundingMetrics.width - bmBase.width; } mBoundingMetrics.ascent = bmBase.ascent + overDelta1 + bmOver.ascent + bmOver.descent; mBoundingMetrics.descent = bmBase.descent; mBoundingMetrics.leftBearing = std::min(dxBase + bmBase.leftBearing, dxOver + bmOver.leftBearing); mBoundingMetrics.rightBearing = std::max(dxBase + bmBase.rightBearing, dxOver + bmOver.rightBearing); ////////// // pass 2, do what <munder> does: attach the underscript on the previous // result. We conceptually view the previous result as an "anynomous base" // from where to attach the underscript. Hence if the underscript is empty, // we should end up like <mover>. If the overscript is empty, we should // end up like <munder>. nsBoundingMetrics bmAnonymousBase = mBoundingMetrics; nscoord ascentAnonymousBase = std::max(mBoundingMetrics.ascent + overDelta2, overSize.BlockStartAscent() + bmOver.descent + overDelta1 + bmBase.ascent); ascentAnonymousBase = std::max(ascentAnonymousBase, baseSize.BlockStartAscent()); // Width of non-spacing marks is zero so use left and right bearing. nscoord underWidth = bmUnder.width; if (!underWidth) { underWidth = bmUnder.rightBearing - bmUnder.leftBearing; dxUnder = -bmUnder.leftBearing; } nscoord maxWidth = std::max(bmAnonymousBase.width, underWidth); if (alignPosition == center && !NS_MATHML_EMBELLISH_IS_ACCENTUNDER(mEmbellishData.flags)) { GetItalicCorrection(bmAnonymousBase, correction); dxUnder += -correction/2; } nscoord dxAnonymousBase = 0; if (alignPosition == center) { dxUnder += (maxWidth - underWidth)/2; dxAnonymousBase = (maxWidth - bmAnonymousBase.width)/2; } else if (alignPosition == right) { dxUnder += maxWidth - underWidth; dxAnonymousBase = maxWidth - bmAnonymousBase.width; } // adjust the offsets of the real base and overscript since their // final offsets should be relative to us... dxOver += dxAnonymousBase; dxBase += dxAnonymousBase; mBoundingMetrics.width = std::max(dxAnonymousBase + bmAnonymousBase.width, dxUnder + bmUnder.width); // At this point, mBoundingMetrics.ascent = bmAnonymousBase.ascent mBoundingMetrics.descent = bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + bmUnder.descent; mBoundingMetrics.leftBearing = std::min(dxAnonymousBase + bmAnonymousBase.leftBearing, dxUnder + bmUnder.leftBearing); mBoundingMetrics.rightBearing = std::max(dxAnonymousBase + bmAnonymousBase.rightBearing, dxUnder + bmUnder.rightBearing); aDesiredSize.SetBlockStartAscent(ascentAnonymousBase); aDesiredSize.Height() = aDesiredSize.BlockStartAscent() + std::max(mBoundingMetrics.descent + underDelta2, bmAnonymousBase.descent + underDelta1 + bmUnder.ascent + underSize.Height() - underSize.BlockStartAscent()); aDesiredSize.Height() = std::max(aDesiredSize.Height(), aDesiredSize.BlockStartAscent() + baseSize.Height() - baseSize.BlockStartAscent()); aDesiredSize.Width() = mBoundingMetrics.width; aDesiredSize.mBoundingMetrics = mBoundingMetrics; mReference.x = 0; mReference.y = aDesiredSize.BlockStartAscent(); if (aPlaceOrigin) { nscoord dy; // place overscript if (overFrame) { dy = aDesiredSize.BlockStartAscent() - mBoundingMetrics.ascent + bmOver.ascent - overSize.BlockStartAscent(); FinishReflowChild (overFrame, PresContext(), overSize, nullptr, dxOver, dy, 0); } // place base dy = aDesiredSize.BlockStartAscent() - baseSize.BlockStartAscent(); FinishReflowChild (baseFrame, PresContext(), baseSize, nullptr, dxBase, dy, 0); // place underscript if (underFrame) { dy = aDesiredSize.BlockStartAscent() + mBoundingMetrics.descent - bmUnder.descent - underSize.BlockStartAscent(); FinishReflowChild (underFrame, PresContext(), underSize, nullptr, dxUnder, dy, 0); } } return NS_OK; }
nsMenuFrame* nsMenuBarFrame::FindMenuWithShortcut(nsIDOMKeyEvent* aKeyEvent) { PRUint32 charCode; aKeyEvent->GetCharCode(&charCode); nsAutoTArray<PRUint32, 10> accessKeys; nsEvent* nativeEvent = nsContentUtils::GetNativeEvent(aKeyEvent); nsKeyEvent* nativeKeyEvent = static_cast<nsKeyEvent*>(nativeEvent); if (nativeKeyEvent) nsContentUtils::GetAccessKeyCandidates(nativeKeyEvent, accessKeys); if (accessKeys.IsEmpty() && charCode) accessKeys.AppendElement(charCode); if (accessKeys.IsEmpty()) return nsnull; // no character was pressed so just return // Enumerate over our list of frames. nsIFrame* immediateParent = nsnull; GetInsertionPoint(PresContext()->PresShell(), this, nsnull, &immediateParent); if (!immediateParent) immediateParent = this; // Find a most preferred accesskey which should be returned. nsIFrame* foundMenu = nsnull; PRUint32 foundIndex = accessKeys.NoIndex; nsIFrame* currFrame = immediateParent->GetFirstPrincipalChild(); while (currFrame) { nsIContent* current = currFrame->GetContent(); // See if it's a menu item. if (nsXULPopupManager::IsValidMenuItem(PresContext(), current, false)) { // Get the shortcut attribute. nsAutoString shortcutKey; current->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, shortcutKey); if (!shortcutKey.IsEmpty()) { ToLowerCase(shortcutKey); const PRUnichar* start = shortcutKey.BeginReading(); const PRUnichar* end = shortcutKey.EndReading(); PRUint32 ch = UTF16CharEnumerator::NextChar(&start, end); PRUint32 index = accessKeys.IndexOf(ch); if (index != accessKeys.NoIndex && (foundIndex == accessKeys.NoIndex || index < foundIndex)) { foundMenu = currFrame; foundIndex = index; } } } currFrame = currFrame->GetNextSibling(); } if (foundMenu) { return (foundMenu->GetType() == nsGkAtoms::menuFrame) ? static_cast<nsMenuFrame *>(foundMenu) : nsnull; } // didn't find a matching menu item #ifdef XP_WIN // behavior on Windows - this item is on the menu bar, beep and deactivate the menu bar if (mIsActive) { nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1"); if (soundInterface) soundInterface->Beep(); } nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); if (pm) { nsIFrame* popup = pm->GetTopPopup(ePopupTypeAny); if (popup) pm->HidePopup(popup->GetContent(), true, true, true); } SetCurrentMenuItem(nsnull); SetActive(false); #endif // #ifdef XP_WIN return nsnull; }
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 = nullptr; 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 if (aPlaceOrigin) { ReportChildCountError(); } 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 = std::max(onePixel, NS_MATHML_IS_RTL(mPresentationData.flags) ? coreData.trailingSpace : coreData.leadingSpace); nscoord rightSpace = std::max(onePixel, NS_MATHML_IS_RTL(mPresentationData.flags) ? coreData.leadingSpace : coreData.trailingSpace); ////////////////// // 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 = std::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 = std::max(dxNum + bmNum.rightBearing, dxDen + bmDen.rightBearing); if (mBoundingMetrics.rightBearing < width - rightSpace) mBoundingMetrics.rightBearing = width - rightSpace; mBoundingMetrics.leftBearing = std::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, nullptr, sizeNum, dxNum, dy, 0); // place denominator dy = aDesiredSize.height - sizeDen.height; FinishReflowChild(frameDen, presContext, nullptr, 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 * std::min(2 * mLineThickness, slashMaxWidthConstant); nscoord leadingSpace = std::max(padding, coreData.leadingSpace); nscoord trailingSpace = std::max(padding, coreData.trailingSpace); 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 = std::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 = std::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 + std::min(slashMaxWidthConstant, (mBoundingMetrics.ascent + mBoundingMetrics.descent) / slashRatio); } // Set horizontal bounding metrics if (NS_MATHML_IS_RTL(mPresentationData.flags)) { mBoundingMetrics.leftBearing = trailingSpace + bmDen.leftBearing; mBoundingMetrics.rightBearing = trailingSpace + bmDen.width + mLineRect.width + bmNum.rightBearing; } else { mBoundingMetrics.leftBearing = leadingSpace + bmNum.leftBearing; mBoundingMetrics.rightBearing = leadingSpace + bmNum.width + mLineRect.width + bmDen.rightBearing; } mBoundingMetrics.width = leadingSpace + bmNum.width + mLineRect.width + bmDen.width + trailingSpace; // 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) { nscoord dx, dy; // place numerator dx = MirrorIfRTL(aDesiredSize.width, sizeNum.width, leadingSpace); dy = aDesiredSize.ascent - numShift - sizeNum.ascent; FinishReflowChild(frameNum, presContext, nullptr, sizeNum, dx, dy, 0); // place the fraction bar dx = MirrorIfRTL(aDesiredSize.width, mLineRect.width, leadingSpace + bmNum.width); dy = aDesiredSize.ascent - mBoundingMetrics.ascent; mLineRect.SetRect(dx, dy, mLineRect.width, aDesiredSize.height - 2 * padding); // place denominator dx = MirrorIfRTL(aDesiredSize.width, sizeDen.width, leadingSpace + bmNum.width + mLineRect.width); dy = aDesiredSize.ascent + denShift - sizeDen.ascent; FinishReflowChild(frameDen, presContext, nullptr, sizeDen, dx, dy, 0); } } return NS_OK; }