diff options
Diffstat (limited to 'Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp')
-rw-r--r-- | Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp | 461 |
1 files changed, 190 insertions, 271 deletions
diff --git a/Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp b/Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp index 4fe93a58d..9f97926f2 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp +++ b/Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2010 Alex Milowski (alex@milowski.com). All rights reserved. * Copyright (C) 2010 François Sausset (sausset@gmail.com). All rights reserved. - * Copyright (C) 2013 Igalia S.L. + * Copyright (C) 2013, 2016 Igalia S.L. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,379 +26,298 @@ */ #include "config.h" +#include "RenderMathMLOperator.h" #if ENABLE(MATHML) -#include "RenderMathMLOperator.h" - -#include "FontCache.h" #include "FontSelector.h" #include "MathMLNames.h" +#include "MathMLOperatorElement.h" #include "PaintInfo.h" #include "RenderBlockFlow.h" #include "RenderText.h" #include "ScaleTransformOperation.h" #include "TransformOperations.h" +#include <cmath> #include <wtf/MathExtras.h> +#include <wtf/unicode/CharacterNames.h> namespace WebCore { - + using namespace MathMLNames; -// FIXME: The OpenType MATH table contains information that should override this table (http://wkbug/122297). -static RenderMathMLOperator::StretchyCharacter stretchyCharacters[14] = { - { 0x28 , 0x239b, 0x239c, 0x239d, 0x0 }, // left parenthesis - { 0x29 , 0x239e, 0x239f, 0x23a0, 0x0 }, // right parenthesis - { 0x5b , 0x23a1, 0x23a2, 0x23a3, 0x0 }, // left square bracket - { 0x2308, 0x23a1, 0x23a2, 0x23a2, 0x0 }, // left ceiling - { 0x230a, 0x23a2, 0x23a2, 0x23a3, 0x0 }, // left floor - { 0x5d , 0x23a4, 0x23a5, 0x23a6, 0x0 }, // right square bracket - { 0x2309, 0x23a4, 0x23a5, 0x23a5, 0x0 }, // right ceiling - { 0x230b, 0x23a5, 0x23a5, 0x23a6, 0x0 }, // right floor - { 0x7b , 0x23a7, 0x23aa, 0x23a9, 0x23a8 }, // left curly bracket - { 0x7c , 0x7c, 0x7c, 0x7c, 0x0 }, // vertical bar - { 0x2016, 0x2016, 0x2016, 0x2016, 0x0 }, // double vertical line - { 0x2225, 0x2225, 0x2225, 0x2225, 0x0 }, // parallel to - { 0x7d , 0x23ab, 0x23aa, 0x23ad, 0x23ac }, // right curly bracket - { 0x222b, 0x2320, 0x23ae, 0x2321, 0x0 } // integral sign -}; - -RenderMathMLOperator::RenderMathMLOperator(MathMLElement& element, PassRef<RenderStyle> style) - : RenderMathMLBlock(element, std::move(style)) - , m_stretchHeight(0) - , m_operator(0) - , m_operatorType(Default) - , m_stretchyCharacter(nullptr) +RenderMathMLOperator::RenderMathMLOperator(MathMLOperatorElement& element, RenderStyle&& style) + : RenderMathMLToken(element, WTFMove(style)) { + updateTokenContent(); } -RenderMathMLOperator::RenderMathMLOperator(MathMLElement& element, PassRef<RenderStyle> style, UChar operatorChar) - : RenderMathMLBlock(element, std::move(style)) - , m_stretchHeight(0) - , m_operator(convertHyphenMinusToMinusSign(operatorChar)) - , m_operatorType(Default) - , m_stretchyCharacter(nullptr) +RenderMathMLOperator::RenderMathMLOperator(Document& document, RenderStyle&& style) + : RenderMathMLToken(document, WTFMove(style)) { } -bool RenderMathMLOperator::isChildAllowed(const RenderObject&, const RenderStyle&) const +MathMLOperatorElement& RenderMathMLOperator::element() const { - return false; + return static_cast<MathMLOperatorElement&>(nodeForNonAnonymous()); } -static const float gOperatorExpansion = 1.2f; +UChar32 RenderMathMLOperator::textContent() const +{ + return element().operatorChar().character; +} -float RenderMathMLOperator::expandedStretchHeight() const +bool RenderMathMLOperator::isInvisibleOperator() const { - return m_stretchHeight * gOperatorExpansion; + // The following operators are invisible: U+2061 FUNCTION APPLICATION, U+2062 INVISIBLE TIMES, U+2063 INVISIBLE SEPARATOR, U+2064 INVISIBLE PLUS. + UChar32 character = textContent(); + return 0x2061 <= character && character <= 0x2064; } -void RenderMathMLOperator::stretchToHeight(int height) +bool RenderMathMLOperator::hasOperatorFlag(MathMLOperatorDictionary::Flag flag) const { - if (m_stretchHeight == height) - return; + return element().hasProperty(flag); +} - m_stretchHeight = height; - updateStyle(); +LayoutUnit RenderMathMLOperator::leadingSpace() const +{ + // FIXME: Negative leading spaces must be implemented (https://webkit.org/b/124830). + LayoutUnit leadingSpace = toUserUnits(element().defaultLeadingSpace(), style(), 0); + leadingSpace = toUserUnits(element().leadingSpace(), style(), leadingSpace); + return std::max<LayoutUnit>(0, leadingSpace); } -void RenderMathMLOperator::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +LayoutUnit RenderMathMLOperator::trailingSpace() const { - RenderMathMLBlock::styleDidChange(diff, oldStyle); - updateFromElement(); + // FIXME: Negative trailing spaces must be implemented (https://webkit.org/b/124830). + LayoutUnit trailingSpace = toUserUnits(element().defaultTrailingSpace(), style(), 0); + trailingSpace = toUserUnits(element().trailingSpace(), style(), trailingSpace); + return std::max<LayoutUnit>(0, trailingSpace); } -FloatRect RenderMathMLOperator::glyphBoundsForCharacter(UChar character) +LayoutUnit RenderMathMLOperator::minSize() const { - GlyphData data = style().font().glyphDataForCharacter(character, false); - return data.fontData->boundsForGlyph(data.glyph); + LayoutUnit minSize = style().fontCascade().size(); // Default minsize is "1em". + minSize = toUserUnits(element().minSize(), style(), minSize); + return std::max<LayoutUnit>(0, minSize); } -float RenderMathMLOperator::glyphHeightForCharacter(UChar character) +LayoutUnit RenderMathMLOperator::maxSize() const { - return glyphBoundsForCharacter(character).height(); + LayoutUnit maxSize = intMaxForLayoutUnit; // Default maxsize is "infinity". + maxSize = toUserUnits(element().maxSize(), style(), maxSize); + return std::max<LayoutUnit>(0, maxSize); } -float RenderMathMLOperator::advanceForCharacter(UChar character) +bool RenderMathMLOperator::isVertical() const { - // Hyphen minus is always replaced by the minus sign in rendered text. - GlyphData data = style().font().glyphDataForCharacter(convertHyphenMinusToMinusSign(character), false); - return data.fontData->widthForGlyph(data.glyph); + return element().operatorChar().isVertical; } -void RenderMathMLOperator::computePreferredLogicalWidths() + +void RenderMathMLOperator::stretchTo(LayoutUnit heightAboveBaseline, LayoutUnit depthBelowBaseline) { - ASSERT(preferredLogicalWidthsDirty()); + ASSERT(isStretchy()); + ASSERT(isVertical()); - UChar stretchedCharacter; - bool allowStretching = shouldAllowStretching(stretchedCharacter); - if (!allowStretching) { - RenderMathMLBlock::computePreferredLogicalWidths(); + if (!isVertical() || (heightAboveBaseline == m_stretchHeightAboveBaseline && depthBelowBaseline == m_stretchDepthBelowBaseline)) return; - } - float maximumGlyphWidth = advanceForCharacter(stretchedCharacter); - for (unsigned index = 0; index < WTF_ARRAY_LENGTH(stretchyCharacters); ++index) { - if (stretchyCharacters[index].character != stretchedCharacter) - continue; - - StretchyCharacter& character = stretchyCharacters[index]; - if (character.topGlyph) - maximumGlyphWidth = std::max(maximumGlyphWidth, advanceForCharacter(character.topGlyph)); - if (character.extensionGlyph) - maximumGlyphWidth = std::max(maximumGlyphWidth, advanceForCharacter(character.extensionGlyph)); - if (character.bottomGlyph) - maximumGlyphWidth = std::max(maximumGlyphWidth, advanceForCharacter(character.bottomGlyph)); - if (character.middleGlyph) - maximumGlyphWidth = std::max(maximumGlyphWidth, advanceForCharacter(character.middleGlyph)); - m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth = maximumGlyphWidth; - return; + m_stretchHeightAboveBaseline = heightAboveBaseline; + m_stretchDepthBelowBaseline = depthBelowBaseline; + + if (hasOperatorFlag(MathMLOperatorDictionary::Symmetric)) { + // We make the operator stretch symmetrically above and below the axis. + LayoutUnit axis = mathAxisHeight(); + LayoutUnit halfStretchSize = std::max(m_stretchHeightAboveBaseline - axis, m_stretchDepthBelowBaseline + axis); + m_stretchHeightAboveBaseline = halfStretchSize + axis; + m_stretchDepthBelowBaseline = halfStretchSize - axis; + } + // We try to honor the minsize/maxsize condition by increasing or decreasing both height and depth proportionately. + // The MathML specification does not indicate what to do when maxsize < minsize, so we follow Gecko and make minsize take precedence. + LayoutUnit size = stretchSize(); + float aspect = 1.0; + if (size > 0) { + LayoutUnit minSizeValue = minSize(); + if (size < minSizeValue) + aspect = minSizeValue.toFloat() / size; + else { + LayoutUnit maxSizeValue = maxSize(); + if (maxSizeValue < size) + aspect = maxSizeValue.toFloat() / size; + } } + m_stretchHeightAboveBaseline *= aspect; + m_stretchDepthBelowBaseline *= aspect; + + m_mathOperator.stretchTo(style(), m_stretchHeightAboveBaseline + m_stretchDepthBelowBaseline); - m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth = maximumGlyphWidth; + setLogicalHeight(m_mathOperator.ascent() + m_mathOperator.descent()); } -// FIXME: It's cleaner to only call updateFromElement when an attribute has changed. The body of -// this method should probably be moved to a private stretchHeightChanged or checkStretchHeight -// method. Probably at the same time, addChild/removeChild methods should be made to work for -// dynamic DOM changes. -void RenderMathMLOperator::updateFromElement() +void RenderMathMLOperator::stretchTo(LayoutUnit width) { - RenderElement* savedRenderer = element().renderer(); - - // Destroy our current children - destroyLeftoverChildren(); - - // Since we share a node with our children, destroying our children may set our node's - // renderer to 0, so we need to restore it. - element().setRenderer(savedRenderer); + ASSERT(isStretchy()); + ASSERT(!isVertical()); - RenderPtr<RenderMathMLBlock> container = createRenderer<RenderMathMLBlock>(element(), RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX)); - // This container doesn't offer any useful information to accessibility. - container->setIgnoreInAccessibilityTree(true); - container->initializeStyle(); + if (isVertical() || m_stretchWidth == width) + return; - RenderPtr<RenderText> text; - if (m_operator) - text = createRenderer<RenderText>(document(), String(&m_operator, 1)); - else - text = createRenderer<RenderText>(document(), element().textContent().replace(hyphenMinus, minusSign).impl()); + m_stretchWidth = width; + m_mathOperator.stretchTo(style(), width); - container->addChild(text.leakPtr()); - addChild(container.leakPtr()); + setLogicalHeight(m_mathOperator.ascent() + m_mathOperator.descent()); +} - updateStyle(); - setNeedsLayoutAndPrefWidthsRecalc(); +void RenderMathMLOperator::resetStretchSize() +{ + if (isVertical()) { + m_stretchHeightAboveBaseline = 0; + m_stretchDepthBelowBaseline = 0; + } else + m_stretchWidth = 0; } -bool RenderMathMLOperator::shouldAllowStretching(UChar& stretchedCharacter) +void RenderMathMLOperator::computePreferredLogicalWidths() { - if (equalIgnoringCase(element().getAttribute(MathMLNames::stretchyAttr), "false")) - return false; + ASSERT(preferredLogicalWidthsDirty()); - if (m_operator) { - stretchedCharacter = m_operator; - return true; - } + LayoutUnit preferredWidth = 0; + + if (!useMathOperator()) { + RenderMathMLToken::computePreferredLogicalWidths(); + preferredWidth = m_maxPreferredLogicalWidth; + if (isInvisibleOperator()) { + // In some fonts, glyphs for invisible operators have nonzero width. Consequently, we subtract that width here to avoid wide gaps. + GlyphData data = style().fontCascade().glyphDataForCharacter(textContent(), false); + float glyphWidth = data.font ? data.font->widthForGlyph(data.glyph) : 0; + ASSERT(glyphWidth <= preferredWidth); + preferredWidth -= glyphWidth; + } + } else + preferredWidth = m_mathOperator.maxPreferredWidth(); - // FIXME: This does not handle surrogate pairs (http://wkbug.com/122296/). - String opText = element().textContent(); - stretchedCharacter = 0; - for (unsigned i = 0; i < opText.length(); ++i) { - // If there's more than one non-whitespace character in this node, then don't even try to stretch it. - if (stretchedCharacter && !isSpaceOrNewline(opText[i])) - return false; + // FIXME: The spacing should be added to the whole embellished operator (https://webkit.org/b/124831). + // FIXME: The spacing should only be added inside (perhaps inferred) mrow (http://www.w3.org/TR/MathML/chapter3.html#presm.opspacing). + preferredWidth = leadingSpace() + preferredWidth + trailingSpace(); - if (!isSpaceOrNewline(opText[i])) - stretchedCharacter = opText[i]; - } + m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth = preferredWidth; - return stretchedCharacter; + setPreferredLogicalWidthsDirty(false); } -// FIXME: We should also look at alternate characters defined in the OpenType MATH table (http://wkbug/122297). -RenderMathMLOperator::StretchyCharacter* RenderMathMLOperator::findAcceptableStretchyCharacter(UChar character) +void RenderMathMLOperator::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeight) { - StretchyCharacter* stretchyCharacter = 0; - const int maxIndex = WTF_ARRAY_LENGTH(stretchyCharacters); - for (int index = 0; index < maxIndex; ++index) { - if (stretchyCharacters[index].character == character) { - stretchyCharacter = &stretchyCharacters[index]; - break; - } - } + ASSERT(needsLayout()); - // If we didn't find a stretchy character set for this character, we don't know how to stretch it. - if (!stretchyCharacter) - return 0; - - float height = glyphHeightForCharacter(stretchyCharacter->topGlyph) + glyphHeightForCharacter(stretchyCharacter->bottomGlyph); - if (stretchyCharacter->middleGlyph) - height += glyphHeightForCharacter(stretchyCharacter->middleGlyph); + if (!relayoutChildren && simplifiedLayout()) + return; - if (height > expandedStretchHeight()) - return 0; + LayoutUnit leadingSpaceValue = leadingSpace(); + LayoutUnit trailingSpaceValue = trailingSpace(); + + if (useMathOperator()) { + for (auto child = firstChildBox(); child; child = child->nextSiblingBox()) + child->layoutIfNeeded(); + setLogicalWidth(leadingSpaceValue + m_mathOperator.width() + trailingSpaceValue); + setLogicalHeight(m_mathOperator.ascent() + m_mathOperator.descent()); + } else { + // We first do the normal layout without spacing. + recomputeLogicalWidth(); + LayoutUnit width = logicalWidth(); + setLogicalWidth(width - leadingSpaceValue - trailingSpaceValue); + RenderMathMLToken::layoutBlock(relayoutChildren, pageLogicalHeight); + setLogicalWidth(width); + + // We then move the children to take spacing into account. + LayoutPoint horizontalShift(style().direction() == LTR ? leadingSpaceValue : -leadingSpaceValue, 0); + for (auto* child = firstChildBox(); child; child = child->nextSiblingBox()) + child->setLocation(child->location() + horizontalShift); + } - return stretchyCharacter; + clearNeedsLayout(); } -void RenderMathMLOperator::updateStyle() +void RenderMathMLOperator::updateMathOperator() { - ASSERT(firstChild()); - if (!firstChild()) - return; - - UChar stretchedCharacter; - bool allowStretching = shouldAllowStretching(stretchedCharacter); - - float stretchedCharacterHeight = style().fontMetrics().floatHeight(); - m_isStretched = allowStretching && expandedStretchHeight() > stretchedCharacterHeight; + ASSERT(useMathOperator()); + MathOperator::Type type; + if (isStretchy()) + type = isVertical() ? MathOperator::Type::VerticalOperator : MathOperator::Type::HorizontalOperator; + else if (textContent() && isLargeOperatorInDisplayStyle()) + type = MathOperator::Type::DisplayOperator; + else + type = MathOperator::Type::NormalOperator; - // Sometimes we cannot stretch an operator properly, so in that case, we should just use the original size. - m_stretchyCharacter = m_isStretched ? findAcceptableStretchyCharacter(stretchedCharacter) : 0; - if (!m_stretchyCharacter) - m_isStretched = false; + m_mathOperator.setOperator(style(), textContent(), type); } -int RenderMathMLOperator::firstLineBaseline() const +void RenderMathMLOperator::updateTokenContent() { - if (m_isStretched) - return expandedStretchHeight() * 2 / 3 - (expandedStretchHeight() - m_stretchHeight) / 2; - return RenderMathMLBlock::firstLineBaseline(); + ASSERT(!isAnonymous()); + RenderMathMLToken::updateTokenContent(); + if (useMathOperator()) + updateMathOperator(); } -void RenderMathMLOperator::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const +void RenderMathMLOperator::updateFromElement() { - if (m_isStretched) - logicalHeight = expandedStretchHeight(); - RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues); + updateTokenContent(); } -LayoutRect RenderMathMLOperator::paintCharacter(PaintInfo& info, UChar character, const LayoutPoint& origin, CharacterPaintTrimming trim) +bool RenderMathMLOperator::useMathOperator() const { - GlyphData data = style().font().glyphDataForCharacter(character, false); - FloatRect glyphBounds = data.fontData->boundsForGlyph(data.glyph); - - LayoutRect glyphPaintRect(origin, LayoutSize(glyphBounds.x() + glyphBounds.width(), glyphBounds.height())); - glyphPaintRect.setY(origin.y() + glyphBounds.y()); - - // In order to have glyphs fit snugly with one another we snap the connecting edges to pixel boundaries - // and trim off one pixel. The pixel trim is to account for fonts that have edge pixels that have less - // than full coverage. These edge pixels can introduce small seams between connected glyphs - FloatRect clipBounds = info.rect; - switch (trim) { - case TrimTop: - glyphPaintRect.shiftYEdgeTo(glyphPaintRect.y().ceil() + 1); - clipBounds.shiftYEdgeTo(glyphPaintRect.y()); - break; - case TrimBottom: - glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1); - clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY()); - break; - case TrimTopAndBottom: - LayoutUnit temp = glyphPaintRect.y() + 1; - glyphPaintRect.shiftYEdgeTo(temp.ceil()); - glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1); - clipBounds.shiftYEdgeTo(glyphPaintRect.y()); - clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY()); - break; - } - - // Clipping the enclosing IntRect avoids any potential issues at joined edges. - GraphicsContextStateSaver stateSaver(*info.context); - info.context->clip(clipBounds); - - info.context->drawText(style().font(), TextRun(&character, 1), origin); - - return glyphPaintRect; + // We use the MathOperator class to handle the following cases: + // 1) Stretchy and large operators, since they require special painting. + // 2) The minus sign, since it can be obtained from a hyphen in the DOM. + return isStretchy() || (textContent() && isLargeOperatorInDisplayStyle()) || textContent() == minusSign; } -void RenderMathMLOperator::fillWithExtensionGlyph(PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to) +void RenderMathMLOperator::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { - ASSERT(m_stretchyCharacter); - ASSERT(m_stretchyCharacter->extensionGlyph); - ASSERT(from.y() <= to.y()); - - // If there is no space for the extension glyph, we don't need to do anything. - if (from.y() == to.y()) - return; - - GraphicsContextStateSaver stateSaver(*info.context); - - FloatRect glyphBounds = glyphBoundsForCharacter(m_stretchyCharacter->extensionGlyph); - - // Clipping the extender region here allows us to draw the bottom extender glyph into the - // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping. - LayoutRect clipBounds = info.rect; - clipBounds.shiftYEdgeTo(from.y()); - clipBounds.shiftMaxYEdgeTo(to.y()); - info.context->clip(clipBounds); + RenderMathMLBlock::styleDidChange(diff, oldStyle); + m_mathOperator.reset(style()); +} - // Trimming may remove up to two pixels from the top of the extender glyph, so we move it up by two pixels. - float offsetToGlyphTop = glyphBounds.y() + 2; - LayoutPoint glyphOrigin = LayoutPoint(from.x(), from.y() - offsetToGlyphTop); - FloatRect lastPaintedGlyphRect(from, FloatSize()); +LayoutUnit RenderMathMLOperator::verticalStretchedOperatorShift() const +{ + if (!isVertical() || !stretchSize()) + return 0; - while (lastPaintedGlyphRect.maxY() < to.y()) { - lastPaintedGlyphRect = paintCharacter(info, m_stretchyCharacter->extensionGlyph, glyphOrigin, TrimTopAndBottom); - glyphOrigin.setY(glyphOrigin.y() + lastPaintedGlyphRect.height()); + return (m_stretchDepthBelowBaseline - m_stretchHeightAboveBaseline - m_mathOperator.descent() + m_mathOperator.ascent()) / 2; +} - // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle - // with trimming. In that case we just draw nothing. - if (lastPaintedGlyphRect.isEmpty()) - break; - } +std::optional<int> RenderMathMLOperator::firstLineBaseline() const +{ + if (useMathOperator()) + return std::optional<int>(std::lround(static_cast<float>(m_mathOperator.ascent() - verticalStretchedOperatorShift()))); + return RenderMathMLToken::firstLineBaseline(); } void RenderMathMLOperator::paint(PaintInfo& info, const LayoutPoint& paintOffset) { - RenderMathMLBlock::paint(info, paintOffset); - - if (info.context->paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE) - return; - - if (!m_isStretched && !m_stretchyCharacter) { - RenderMathMLBlock::paint(info, paintOffset); + RenderMathMLToken::paint(info, paintOffset); + if (!useMathOperator()) return; - } - - GraphicsContextStateSaver stateSaver(*info.context); - info.context->setFillColor(style().visitedDependentColor(CSSPropertyColor), style().colorSpace()); - ASSERT(m_stretchyCharacter->topGlyph); - ASSERT(m_stretchyCharacter->bottomGlyph); + LayoutPoint operatorTopLeft = paintOffset + location(); + operatorTopLeft.move(style().isLeftToRightDirection() ? leadingSpace() : trailingSpace(), 0); - // We are positioning the glyphs so that the edge of the tight glyph bounds line up exactly with the edges of our paint box. - LayoutPoint operatorTopLeft = ceiledIntPoint(paintOffset + location()); - FloatRect topGlyphBounds = glyphBoundsForCharacter(m_stretchyCharacter->topGlyph); - LayoutPoint topGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() - topGlyphBounds.y()); - LayoutRect topGlyphPaintRect = paintCharacter(info, m_stretchyCharacter->topGlyph, topGlyphOrigin, TrimBottom); + // Center horizontal operators. + if (!isVertical()) + operatorTopLeft.move(-(m_mathOperator.width() - width()) / 2, 0); - FloatRect bottomGlyphBounds = glyphBoundsForCharacter(m_stretchyCharacter->bottomGlyph); - LayoutPoint bottomGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() + offsetHeight() - (bottomGlyphBounds.height() + bottomGlyphBounds.y())); - LayoutRect bottomGlyphPaintRect = paintCharacter(info, m_stretchyCharacter->bottomGlyph, bottomGlyphOrigin, TrimTop); - - if (m_stretchyCharacter->middleGlyph) { - // Center the glyph origin between the start and end glyph paint extents. Then shift it half the paint height toward the bottom glyph. - FloatRect middleGlyphBounds = glyphBoundsForCharacter(m_stretchyCharacter->middleGlyph); - LayoutPoint middleGlyphOrigin(operatorTopLeft.x(), topGlyphOrigin.y()); - middleGlyphOrigin.moveBy(LayoutPoint(0, (bottomGlyphPaintRect.y() - topGlyphPaintRect.maxY()) / 2.0)); - middleGlyphOrigin.moveBy(LayoutPoint(0, middleGlyphBounds.height() / 2.0)); - - LayoutRect middleGlyphPaintRect = paintCharacter(info, m_stretchyCharacter->middleGlyph, middleGlyphOrigin, TrimTopAndBottom); - fillWithExtensionGlyph(info, topGlyphPaintRect.minXMaxYCorner(), middleGlyphPaintRect.minXMinYCorner()); - fillWithExtensionGlyph(info, middleGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner()); - } else - fillWithExtensionGlyph(info, topGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner()); + m_mathOperator.paint(style(), info, operatorTopLeft); } void RenderMathMLOperator::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect) { - if (m_isStretched) + // We skip painting for invisible operators too to avoid some "missing character" glyph to appear if appropriate math fonts are not available. + if (useMathOperator() || isInvisibleOperator()) return; - RenderMathMLBlock::paintChildren(paintInfo, paintOffset, paintInfoForChild, usePrintRect); + RenderMathMLToken::paintChildren(paintInfo, paintOffset, paintInfoForChild, usePrintRect); } - + } #endif |