summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp')
-rw-r--r--Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp461
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