diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/rendering/mathml/RenderMathMLMenclose.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/rendering/mathml/RenderMathMLMenclose.cpp')
-rw-r--r--[-rwxr-xr-x] | Source/WebCore/rendering/mathml/RenderMathMLMenclose.cpp | 369 |
1 files changed, 277 insertions, 92 deletions
diff --git a/Source/WebCore/rendering/mathml/RenderMathMLMenclose.cpp b/Source/WebCore/rendering/mathml/RenderMathMLMenclose.cpp index 5cb924f03..7dde7a97b 100755..100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLMenclose.cpp +++ b/Source/WebCore/rendering/mathml/RenderMathMLMenclose.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2014 Gurpreet Kaur (k.gurpreet@samsung.com). All rights reserved. + * Copyright (C) 2016 Igalia S.L. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -23,136 +24,320 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define _USE_MATH_DEFINES 1 #include "config.h" +#include "RenderMathMLMenclose.h" #if ENABLE(MATHML) -#include "RenderMathMLMenclose.h" #include "GraphicsContext.h" -#include "MathMLMencloseElement.h" +#include "MathMLNames.h" #include "PaintInfo.h" -#include "RenderMathMLSquareRoot.h" #include <wtf/MathExtras.h> namespace WebCore { using namespace MathMLNames; -RenderMathMLMenclose::RenderMathMLMenclose(Element& element, PassRef<RenderStyle> style) - : RenderMathMLRow(element, std::move(style)) +// The MathML in HTML5 implementation note suggests drawing the left part of longdiv with a parenthesis. +// For now, we use a Bezier curve and this somewhat arbitrary value. +const unsigned short longDivLeftSpace = 10; + +RenderMathMLMenclose::RenderMathMLMenclose(MathMLMencloseElement& element, RenderStyle&& style) + : RenderMathMLRow(element, WTFMove(style)) { } -void RenderMathMLMenclose::addChild(RenderObject* newChild, RenderObject* beforeChild) +// This arbitrary thickness value is used for the parameter \xi_8 from the MathML in HTML5 implementation note. +// For now, we take: +// - OverbarVerticalGap = UnderbarVerticalGap = 3\xi_8 +// - OverbarRuleThickness = UnderbarRuleThickness = \xi_8 +// - OverbarExtraAscender = UnderbarExtraAscender = \xi_8 +// FIXME: OverBar and UnderBar parameters should be read from the MATH tables. +// See https://bugs.webkit.org/show_bug.cgi?id=122297 +LayoutUnit RenderMathMLMenclose::ruleThickness() const { - MathMLMencloseElement* menclose = toMathMLMencloseElement(element()); - // Allow an anonymous RenderMathMLSquareRoot to handle drawing the radical - // notation, rather than duplicating the code needed to paint a root. - if (!firstChild() && menclose->isRadical()) - RenderMathMLBlock::addChild(RenderMathMLSquareRoot::createAnonymousWithParentRenderer(*this).leakPtr()); - - if (newChild) { - if (firstChild() && menclose->isRadical()) - toRenderElement(firstChild())->addChild(newChild, beforeChild && beforeChild->parent() == firstChild() ? beforeChild : nullptr); - else - RenderMathMLBlock::addChild(newChild, beforeChild); + return 0.05f * style().fontCascade().size(); +} + +RenderMathMLMenclose::SpaceAroundContent RenderMathMLMenclose::spaceAroundContent(LayoutUnit contentWidth, LayoutUnit contentHeight) const +{ + SpaceAroundContent space; + space.right = 0; + space.top = 0; + space.bottom = 0; + space.left = 0; + + LayoutUnit thickness = ruleThickness(); + // In the MathML in HTML5 implementation note, the "left" notation is described as follows: + // - left side is 3\xi_8 padding + \xi_8 border + \xi_8 margin = 5\xi_8 + // - top space is Overbar Vertical Gap + Overbar Rule Thickness = 3\xi_8 + \xi_8 = 4\xi_8 + // - bottom space is Underbar Vertical Gap + Underbar Rule Thickness = 3\xi_8 + \xi_8 = 4\xi_8 + // The "right" notation is symmetric. + if (hasNotation(MathMLMencloseElement::Left)) + space.left = std::max(space.left, 5 * thickness); + if (hasNotation(MathMLMencloseElement::Right)) + space.right = std::max(space.right, 5 * thickness); + if (hasNotation(MathMLMencloseElement::Left) || hasNotation(MathMLMencloseElement::Right)) { + LayoutUnit extraSpace = 4 * thickness; + space.top = std::max(space.top, extraSpace); + space.bottom = std::max(space.bottom, extraSpace); + } + + // In the MathML in HTML5 implementation note, the "top" notation is described as follows: + // - left and right space are 4\xi_8 + // - top side is Vertical Gap + Rule Thickness + Extra Ascender = 3\xi_8 + \xi_8 + \xi_8 = 5\xi_8 + // The "bottom" notation is symmetric. + if (hasNotation(MathMLMencloseElement::Top)) + space.top = std::max(space.top, 5 * thickness); + if (hasNotation(MathMLMencloseElement::Bottom)) + space.bottom = std::max(space.bottom, 5 * thickness); + if (hasNotation(MathMLMencloseElement::Top) || hasNotation(MathMLMencloseElement::Bottom)) { + LayoutUnit extraSpace = 4 * thickness; + space.left = std::max(space.left, extraSpace); + space.right = std::max(space.right, extraSpace); + } + + // For longdiv, we use our own rules for now: + // - top space is like "top" notation + // - bottom space is like "bottom" notation + // - right space is like "right" notation + // - left space is longDivLeftSpace * \xi_8 + if (hasNotation(MathMLMencloseElement::LongDiv)) { + space.top = std::max(space.top, 5 * thickness); + space.bottom = std::max(space.bottom, 5 * thickness); + space.left = std::max(space.left, longDivLeftSpace * thickness); + space.right = std::max(space.right, 4 * thickness); + } + + // In the MathML in HTML5 implementation note, the "rounded" notation is described as follows: + // - top/bottom/left/right side have 3\xi_8 padding + \xi_8 border + \xi_8 margin = 5\xi_8 + if (hasNotation(MathMLMencloseElement::RoundedBox)) { + LayoutUnit extraSpace = 5 * thickness; + space.left = std::max(space.left, extraSpace); + space.right = std::max(space.right, extraSpace); + space.top = std::max(space.top, extraSpace); + space.bottom = std::max(space.bottom, extraSpace); } + + // In the MathML in HTML5 implementation note, the "rounded" notation is described as follows: + // - top/bottom/left/right spaces are \xi_8/2 + if (hasNotation(MathMLMencloseElement::UpDiagonalStrike) || hasNotation(MathMLMencloseElement::DownDiagonalStrike)) { + LayoutUnit extraSpace = thickness / 2; + space.left = std::max(space.left, extraSpace); + space.right = std::max(space.right, extraSpace); + space.top = std::max(space.top, extraSpace); + space.bottom = std::max(space.bottom, extraSpace); + } + + // In the MathML in HTML5 implementation note, the "circle" notation is described as follows: + // - We draw the ellipse of axes the axes of symmetry of this ink box + // - The radii of the ellipse are \sqrt{2}contentWidth/2 and \sqrt{2}contentHeight/2 + // - The thickness of the ellipse is \xi_8 + // - We add extra margin of \xi_8 + // Then for example the top space is \sqrt{2}contentHeight/2 - contentHeight/2 + \xi_8/2 + \xi_8. + if (hasNotation(MathMLMencloseElement::Circle)) { + LayoutUnit extraSpace = (contentWidth * (sqrtOfTwoFloat - 1) + 3 * thickness) / 2; + space.left = std::max(space.left, extraSpace); + space.right = std::max(space.right, extraSpace); + extraSpace = (contentHeight * (sqrtOfTwoFloat - 1) + 3 * thickness) / 2; + space.top = std::max(space.top, extraSpace); + space.bottom = std::max(space.bottom, extraSpace); + } + + // In the MathML in HTML5 implementation note, the "vertical" and "horizontal" notations do not add space around the content. + + return space; } void RenderMathMLMenclose::computePreferredLogicalWidths() { ASSERT(preferredLogicalWidthsDirty()); - RenderMathMLBlock::computePreferredLogicalWidths(); + RenderMathMLRow::computePreferredLogicalWidths(); - MathMLMencloseElement* menclose = toMathMLMencloseElement(element()); - const Vector<String>& notationValues = menclose->notationValues(); - size_t notationalValueSize = notationValues.size(); - for (size_t i = 0; i < notationalValueSize; i++) { - if (notationValues[i] == "circle") { - m_minPreferredLogicalWidth = minPreferredLogicalWidth() * float(sqrtOfTwoDouble); - m_maxPreferredLogicalWidth = maxPreferredLogicalWidth() * float(sqrtOfTwoDouble); - } - } + LayoutUnit preferredWidth = m_maxPreferredLogicalWidth; + SpaceAroundContent space = spaceAroundContent(preferredWidth, 0); + m_maxPreferredLogicalWidth = space.left + preferredWidth + space.right; + m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth; setPreferredLogicalWidthsDirty(false); } -void RenderMathMLMenclose::updateLogicalHeight() +void RenderMathMLMenclose::layoutBlock(bool relayoutChildren, LayoutUnit) +{ + ASSERT(needsLayout()); + + if (!relayoutChildren && simplifiedLayout()) + return; + + LayoutUnit contentAscent = 0; + LayoutUnit contentDescent = 0; + RenderMathMLRow::computeLineVerticalStretch(contentAscent, contentDescent); + RenderMathMLRow::layoutRowItems(contentAscent, contentDescent); + LayoutUnit contentWidth = logicalWidth(); + + SpaceAroundContent space = spaceAroundContent(contentWidth, contentAscent + contentDescent); + setLogicalWidth(space.left + contentWidth + space.right); + setLogicalHeight(space.top + contentAscent + contentDescent + space.bottom); + + LayoutPoint contentLocation(space.left, space.top); + for (auto* child = firstChildBox(); child; child = child->nextSiblingBox()) + child->setLocation(child->location() + contentLocation); + + m_contentRect = LayoutRect(space.left, space.top, contentWidth, contentAscent + contentDescent); + + clearNeedsLayout(); +} + +// GraphicsContext::drawLine does not seem appropriate to draw menclose lines. +// To avoid unexpected behaviors and inconsistency with other notations, we just use strokePath. +static void drawLine(PaintInfo& info, const LayoutUnit& xStart, const LayoutUnit& yStart, const LayoutUnit& xEnd, const LayoutUnit& yEnd) { - MathMLMencloseElement* menclose = toMathMLMencloseElement(element()); - const Vector<String>& notationValues = menclose->notationValues(); - size_t notationalValueSize = notationValues.size(); - for (size_t i = 0; i < notationalValueSize; i++) - if (notationValues[i] == "circle") - setLogicalHeight(logicalHeight() * float(sqrtOfTwoDouble)); + Path line; + line.moveTo(LayoutPoint(xStart, yStart)); + line.addLineTo(LayoutPoint(xEnd, yEnd)); + info.context().strokePath(line); } void RenderMathMLMenclose::paint(PaintInfo& info, const LayoutPoint& paintOffset) { - RenderMathMLBlock::paint(info, paintOffset); + RenderMathMLRow::paint(info, paintOffset); - if (info.context->paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE) + if (info.context().paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE) return; - - MathMLMencloseElement* menclose = toMathMLMencloseElement(element()); - const Vector<String>& notationValues = menclose->notationValues(); - size_t notationalValueSize = notationValues.size(); - bool isDefaultLongDiv = !notationalValueSize; - if (notationalValueSize && checkNotationalValuesValidity(notationValues)) { - IntRect rect = absoluteBoundingBoxRect(); - int left = rect.x(); - int top = rect.y(); - int boxWidth = rect.width(); - int boxHeight = rect.height(); - int halfboxWidth = rect.width() / 2; - int halfboxHeight = rect.height() / 2; - - GraphicsContextStateSaver stateSaver(*info.context); - info.context->setStrokeThickness(1); - info.context->setStrokeStyle(SolidStroke); - info.context->setStrokeColor(style().visitedDependentColor(CSSPropertyColor), ColorSpaceDeviceRGB); - // TODO add support for notation value updiagonalarrow https://bugs.webkit.org/show_bug.cgi?id=127466 - for (size_t i = 0; i < notationalValueSize; i++) { - if (notationValues[i] == "updiagonalstrike") - info.context->drawLine(IntPoint(left, top + boxHeight), IntPoint(left + boxWidth, top)); - else if (notationValues[i] == "downdiagonalstrike") - info.context->drawLine(IntPoint(left, top), IntPoint(left + boxWidth, top + boxHeight)); - else if (notationValues[i] == "verticalstrike") - info.context->drawLine(IntPoint(left + halfboxWidth, top), IntPoint(left + halfboxWidth, top + boxHeight)); - else if (notationValues[i] == "horizontalstrike") - info.context->drawLine(IntPoint(left, top + halfboxHeight), IntPoint(left + boxWidth, top + halfboxHeight)); - else if (notationValues[i] == "circle") { - info.context->setFillColor(Color::transparent, ColorSpaceDeviceRGB); - info.context->drawEllipse(rect); - } else if (notationValues[i] == "longdiv") - isDefaultLongDiv = true; - } - if (isDefaultLongDiv) { - Path root; - int midxPoint = 0; - root.moveTo(FloatPoint(left, top)); - int childLeft = firstChild() ? firstChild()->absoluteBoundingBoxRect().x() : 0; - if (childLeft) - midxPoint= childLeft - left; - else - midxPoint = style().paddingLeft().value(); - root.addBezierCurveTo(FloatPoint(left, top), FloatPoint(left + midxPoint, top + halfboxHeight), FloatPoint(left, top + boxHeight)); - info.context->strokePath(root); - } + + LayoutUnit thickness = ruleThickness(); + + // Make a copy of the PaintInfo because applyTransform will modify its rect. + PaintInfo paintInfo(info); + GraphicsContextStateSaver stateSaver(paintInfo.context()); + + paintInfo.context().setStrokeThickness(thickness); + paintInfo.context().setStrokeStyle(SolidStroke); + paintInfo.context().setStrokeColor(style().visitedDependentColor(CSSPropertyColor)); + paintInfo.context().setFillColor(Color::transparent); + paintInfo.applyTransform(AffineTransform().translate(paintOffset + location())); + + // In the MathML in HTML5 implementation note, the "left" notation is described as follows: + // - center of the left vertical bar is at 3\xi_8 padding + \xi_8 border/2 = 7\xi_8/2 + // - top space is Overbar Vertical Gap + Overbar Rule Thickness = 3\xi_8 + \xi_8 = 4\xi_8 + // - bottom space is Underbar Vertical Gap + Underbar Rule Thickness = 3\xi_8 + \xi_8 = 4\xi_8 + if (hasNotation(MathMLMencloseElement::Left)) { + LayoutUnit x = m_contentRect.x() - 7 * thickness / 2; + LayoutUnit yStart = m_contentRect.y() - 4 * thickness; + LayoutUnit yEnd = m_contentRect.maxY() + 4 * thickness; + drawLine(info, x, yStart, x, yEnd); } -} -bool RenderMathMLMenclose::checkNotationalValuesValidity(const Vector<String>& attr) const -{ - size_t attrSize = attr.size(); - for (size_t i = 0; i < attrSize; i++) { - if (attr[i] == "updiagonalstrike" || attr[i] == "downdiagonalstrike" || attr[i] == "horizontalstrike" || attr[i] == "verticalstrike" - || attr[i] == "circle" || attr[i] == "longdiv") - return true; + // In the MathML in HTML5 implementation note, the "right" notation is described as follows: + // - center of the right vertical bar is at 3\xi_8 padding + \xi_8 border/2 = 7\xi_8/2 + // - top space is Overbar Vertical Gap + Overbar Rule Thickness = 3\xi_8 + \xi_8 = 4\xi_8 + // - bottom space is Underbar Vertical Gap + Underbar Rule Thickness = 3\xi_8 + \xi_8 = 4\xi_8 + if (hasNotation(MathMLMencloseElement::Right)) { + LayoutUnit x = m_contentRect.maxX() + 7 * thickness / 2; + LayoutUnit yStart = m_contentRect.y() - 4 * thickness; + LayoutUnit yEnd = m_contentRect.maxY() + 4 * thickness; + drawLine(info, x, yStart, x, yEnd); + } + + // In the MathML in HTML5 implementation note, the "vertical" notation is horizontally centered. + if (hasNotation(MathMLMencloseElement::VerticalStrike)) { + LayoutUnit x = m_contentRect.x() + (m_contentRect.width() - thickness) / 2; + LayoutUnit yStart = m_contentRect.y(); + LayoutUnit yEnd = m_contentRect.maxY(); + drawLine(info, x, yStart, x, yEnd); + } + + // In the MathML in HTML5 implementation note, the "top" notation is described as follows: + // - middle of the top horizontal bar is at Vertical Gap + Rule Thickness / 2 = 7\xi_8/2 + // - left and right spaces have size 4\xi_8 + if (hasNotation(MathMLMencloseElement::Top)) { + LayoutUnit y = m_contentRect.y() - 7 * thickness / 2; + LayoutUnit xStart = m_contentRect.x() - 4 * thickness; + LayoutUnit xEnd = m_contentRect.maxX() + 4 * thickness; + drawLine(info, xStart, y, xEnd, y); + } + + // In the MathML in HTML5 implementation note, the "bottom" notation is described as follows: + // - middle of the bottom horizontal bar is at Vertical Gap + Rule Thickness / 2 = 7\xi_8/2 + // - left and right spaces have size 4\xi_8 + if (hasNotation(MathMLMencloseElement::Bottom)) { + LayoutUnit y = m_contentRect.maxY() + 7 * thickness / 2; + LayoutUnit xStart = m_contentRect.x() - 4 * thickness; + LayoutUnit xEnd = m_contentRect.maxX() + 4 * thickness; + drawLine(info, xStart, y, xEnd, y); + } + + // In the MathML in HTML5 implementation note, the "vertical" notation is vertically centered. + if (hasNotation(MathMLMencloseElement::HorizontalStrike)) { + LayoutUnit y = m_contentRect.y() + (m_contentRect.height() - thickness) / 2; + LayoutUnit xStart = m_contentRect.x(); + LayoutUnit xEnd = m_contentRect.maxX(); + drawLine(info, xStart, y, xEnd, y); + } + + // In the MathML in HTML5 implementation note, the "updiagonalstrike" goes from the bottom left corner + // to the top right corner. + if (hasNotation(MathMLMencloseElement::UpDiagonalStrike)) + drawLine(info, m_contentRect.x(), m_contentRect.maxY(), m_contentRect.maxX(), m_contentRect.y()); + + // In the MathML in HTML5 implementation note, the "downdiagonalstrike" goes from the top left corner + // to the bottom right corner. + if (hasNotation(MathMLMencloseElement::DownDiagonalStrike)) + drawLine(info, m_contentRect.x(), m_contentRect.y(), m_contentRect.maxX(), m_contentRect.maxY()); + + // In the MathML in HTML5 implementation note, the "roundedbox" has radii size 3\xi_8 and is obtained + // by inflating the content box by 3\xi_8 + \xi_8/2 = 7\xi_8/2 + if (hasNotation(MathMLMencloseElement::RoundedBox)) { + LayoutSize radiiSize(3 * thickness, 3 * thickness); + RoundedRect::Radii radii(radiiSize, radiiSize, radiiSize, radiiSize); + RoundedRect roundedRect(m_contentRect, radii); + roundedRect.inflate(7 * thickness / 2); + Path path; + path.addRoundedRect(roundedRect); + paintInfo.context().strokePath(path); + } + + // For longdiv, we use our own rules for now: + // - top space is like "top" notation + // - bottom space is like "bottom" notation + // - right space is like "right" notation + // - left space is longDivLeftSpace * \xi_8 + // - We subtract half of the thickness from these spaces to obtain "top", "bottom", "left" + // and "right" coordinates. + // - The top bar is drawn from "right" to "left" and positioned at vertical offset "top". + // - The left part is draw as a quadratic Bezier curve with end points going from "top" to + // "bottom" and positioned at horizontal offset "left". + // - In order to force the curvature of the left part, we use a middle point that is vertically + // centered and shifted towards the right by longDivLeftSpace * \xi_8 + if (hasNotation(MathMLMencloseElement::LongDiv)) { + LayoutUnit top = m_contentRect.y() - 7 * thickness / 2; + LayoutUnit bottom = m_contentRect.maxY() + 7 * thickness / 2; + LayoutUnit left = m_contentRect.x() - longDivLeftSpace * thickness + thickness / 2; + LayoutUnit right = m_contentRect.maxX() + 4 * thickness; + LayoutUnit midX = left + longDivLeftSpace * thickness; + LayoutUnit midY = (top + bottom) / 2; + Path path; + path.moveTo(LayoutPoint(right, top)); + path.addLineTo(LayoutPoint(left, top)); + path.addQuadCurveTo(LayoutPoint(midX, midY), FloatPoint(left, bottom)); + paintInfo.context().strokePath(path); + } + + // In the MathML in HTML5 implementation note, the "circle" notation is described as follows: + // - The center and axes are the same as the content bounding box. + // - The width of the bounding box is \xi_8/2 + contentWidth * \sqrt{2} + \xi_8/2 + // - The height is \xi_8/2 + contentHeight * \sqrt{2} + \xi_8/2 + if (hasNotation(MathMLMencloseElement::Circle)) { + LayoutRect ellipseRect; + ellipseRect.setWidth(m_contentRect.width() * sqrtOfTwoFloat + thickness); + ellipseRect.setHeight(m_contentRect.height() * sqrtOfTwoFloat + thickness); + ellipseRect.setX(m_contentRect.x() - (ellipseRect.width() - m_contentRect.width()) / 2); + ellipseRect.setY(m_contentRect.y() - (ellipseRect.height() - m_contentRect.height()) / 2); + Path path; + path.addEllipse(ellipseRect); + paintInfo.context().strokePath(path); } - return false; } } |