summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/mathml/RenderMathMLFraction.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/rendering/mathml/RenderMathMLFraction.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/rendering/mathml/RenderMathMLFraction.cpp')
-rw-r--r--Source/WebCore/rendering/mathml/RenderMathMLFraction.cpp269
1 files changed, 176 insertions, 93 deletions
diff --git a/Source/WebCore/rendering/mathml/RenderMathMLFraction.cpp b/Source/WebCore/rendering/mathml/RenderMathMLFraction.cpp
index 0655289df..a20bd122d 100644
--- a/Source/WebCore/rendering/mathml/RenderMathMLFraction.cpp
+++ b/Source/WebCore/rendering/mathml/RenderMathMLFraction.cpp
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2009 Alex Milowski (alex@milowski.com). All rights reserved.
* Copyright (C) 2010 François Sausset (sausset@gmail.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
@@ -25,144 +26,226 @@
*/
#include "config.h"
+#include "RenderMathMLFraction.h"
#if ENABLE(MATHML)
-#include "RenderMathMLFraction.h"
-
#include "GraphicsContext.h"
-#include "MathMLNames.h"
+#include "MathMLFractionElement.h"
#include "PaintInfo.h"
+#include <cmath>
namespace WebCore {
-
-using namespace MathMLNames;
-
-static const float gLineThin = 0.33f;
-static const float gLineMedium = 1.f;
-static const float gLineThick = 3.f;
-static const float gFractionBarWidth = 0.05f;
-RenderMathMLFraction::RenderMathMLFraction(MathMLInlineContainerElement& element, PassRef<RenderStyle> style)
- : RenderMathMLBlock(element, std::move(style))
- , m_lineThickness(gLineMedium)
+RenderMathMLFraction::RenderMathMLFraction(MathMLFractionElement& element, RenderStyle&& style)
+ : RenderMathMLBlock(element, WTFMove(style))
{
}
-void RenderMathMLFraction::fixChildStyle(RenderObject* child)
+bool RenderMathMLFraction::isValid() const
{
- ASSERT(child->isAnonymous() && child->style().refCount() == 1);
- child->style().setFlexDirection(FlowColumn);
+ // Verify whether the list of children is valid:
+ // <mfrac> numerator denominator </mfrac>
+ auto* child = firstChildBox();
+ if (!child)
+ return false;
+ child = child->nextSiblingBox();
+ return child && !child->nextSiblingBox();
}
-// FIXME: It's cleaner to only call updateFromElement when an attribute has changed. Move parts
-// of this to fixChildStyle or other methods, and call them when needed.
-void RenderMathMLFraction::updateFromElement()
+RenderBox& RenderMathMLFraction::numerator() const
{
- // FIXME: mfrac where bevelled=true will need to reorganize the descendants
- if (isEmpty())
- return;
+ ASSERT(isValid());
+ return *firstChildBox();
+}
- RenderObject* numeratorWrapper = firstChild();
- RenderObject* denominatorWrapper = numeratorWrapper->nextSibling();
- if (!denominatorWrapper)
- return;
+RenderBox& RenderMathMLFraction::denominator() const
+{
+ ASSERT(isValid());
+ return *firstChildBox()->nextSiblingBox();
+}
- String thickness = element().getAttribute(MathMLNames::linethicknessAttr);
- m_lineThickness = gLineMedium;
- if (equalIgnoringCase(thickness, "thin"))
- m_lineThickness = gLineThin;
- else if (equalIgnoringCase(thickness, "medium"))
- m_lineThickness = gLineMedium;
- else if (equalIgnoringCase(thickness, "thick"))
- m_lineThickness = gLineThick;
- else {
- // This function parses the thickness attribute using gLineMedium as
- // the default value. If the parsing fails, m_lineThickness will not be
- // modified i.e. the default value will be used.
- parseMathMLLength(thickness, m_lineThickness, &style(), false);
- }
+void RenderMathMLFraction::updateLineThickness()
+{
+ // We first determine the default line thickness.
+ const auto& primaryFont = style().fontCascade().primaryFont();
+ const auto* mathData = style().fontCascade().primaryFont().mathData();
+ if (mathData)
+ m_defaultLineThickness = mathData->getMathConstant(primaryFont, OpenTypeMathData::FractionRuleThickness);
+ else
+ m_defaultLineThickness = ruleThicknessFallback();
- // Update the style for the padding of the denominator for the line thickness
- lastChild()->style().setPaddingTop(Length(static_cast<int>(m_lineThickness), Fixed));
+ // Next we resolve the thickness using m_defaultLineThickness as the default value.
+ m_lineThickness = toUserUnits(element().lineThickness(), style(), m_defaultLineThickness);
+ if (m_lineThickness < 0)
+ m_lineThickness = 0;
}
-void RenderMathMLFraction::addChild(RenderObject* child, RenderObject* /* beforeChild */)
+RenderMathMLFraction::FractionParameters RenderMathMLFraction::fractionParameters()
{
- if (isEmpty()) {
- RenderPtr<RenderMathMLBlock> numeratorWrapper = createAnonymousMathMLBlock();
- fixChildStyle(numeratorWrapper.get());
- RenderMathMLBlock::addChild(numeratorWrapper.leakPtr());
-
- RenderPtr<RenderMathMLBlock> denominatorWrapper = createAnonymousMathMLBlock();
- fixChildStyle(denominatorWrapper.get());
- RenderMathMLBlock::addChild(denominatorWrapper.leakPtr());
+ ASSERT(!isStack());
+ FractionParameters parameters;
+
+ // We try and read constants to draw the fraction from the OpenType MATH and use fallback values otherwise.
+ const auto& primaryFont = style().fontCascade().primaryFont();
+ const auto* mathData = style().fontCascade().primaryFont().mathData();
+ bool display = mathMLStyle().displayStyle();
+ if (mathData) {
+ parameters.numeratorGapMin = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionNumDisplayStyleGapMin : OpenTypeMathData::FractionNumeratorGapMin);
+ parameters.denominatorGapMin = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionDenomDisplayStyleGapMin : OpenTypeMathData::FractionDenominatorGapMin);
+ parameters.numeratorMinShiftUp = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionNumeratorDisplayStyleShiftUp : OpenTypeMathData::FractionNumeratorShiftUp);
+ parameters.denominatorMinShiftDown = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionDenominatorDisplayStyleShiftDown : OpenTypeMathData::FractionDenominatorShiftDown);
+ } else {
+ // The MATH table specification suggests default rule thickness or (in displaystyle) 3 times default rule thickness for the gaps.
+ parameters.numeratorGapMin = display ? 3 * ruleThicknessFallback() : ruleThicknessFallback();
+ parameters.denominatorGapMin = parameters.numeratorGapMin;
+
+ // The MATH table specification does not suggest any values for shifts, so we leave them at zero.
+ parameters.numeratorMinShiftUp = 0;
+ parameters.denominatorMinShiftDown = 0;
}
-
- if (firstChild()->isEmpty())
- toRenderElement(firstChild())->addChild(child);
- else
- toRenderElement(lastChild())->addChild(child);
-
- updateFromElement();
+
+ return parameters;
}
-void RenderMathMLFraction::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+RenderMathMLFraction::StackParameters RenderMathMLFraction::stackParameters()
{
- RenderMathMLBlock::styleDidChange(diff, oldStyle);
+ ASSERT(isStack());
+ StackParameters parameters;
- for (RenderObject* child = firstChild(); child; child = child->nextSibling())
- fixChildStyle(child);
- updateFromElement();
+ // We try and read constants to draw the stack from the OpenType MATH and use fallback values otherwise.
+ const auto& primaryFont = style().fontCascade().primaryFont();
+ const auto* mathData = style().fontCascade().primaryFont().mathData();
+ bool display = mathMLStyle().displayStyle();
+ if (mathData) {
+ parameters.gapMin = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::StackDisplayStyleGapMin : OpenTypeMathData::StackGapMin);
+ parameters.topShiftUp = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::StackTopDisplayStyleShiftUp : OpenTypeMathData::StackTopShiftUp);
+ parameters.bottomShiftDown = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::StackBottomDisplayStyleShiftDown : OpenTypeMathData::StackBottomShiftDown);
+ } else {
+ // We use the values suggested in the MATH table specification.
+ parameters.gapMin = display ? 7 * ruleThicknessFallback() : 3 * ruleThicknessFallback();
+
+ // The MATH table specification does not suggest any values for shifts, so we leave them at zero.
+ parameters.topShiftUp = 0;
+ parameters.bottomShiftDown = 0;
+ }
+
+ return parameters;
}
RenderMathMLOperator* RenderMathMLFraction::unembellishedOperator()
{
- RenderObject* numeratorWrapper = firstChild();
- if (!numeratorWrapper)
- return 0;
- RenderObject* numerator = numeratorWrapper->firstChildSlow();
- if (!numerator || !numerator->isRenderMathMLBlock())
- return 0;
- return toRenderMathMLBlock(numerator)->unembellishedOperator();
+ if (!isValid() || !is<RenderMathMLBlock>(numerator()))
+ return nullptr;
+
+ return downcast<RenderMathMLBlock>(numerator()).unembellishedOperator();
}
-void RenderMathMLFraction::layout()
+void RenderMathMLFraction::computePreferredLogicalWidths()
{
- updateFromElement();
+ ASSERT(preferredLogicalWidthsDirty());
+
+ m_minPreferredLogicalWidth = 0;
+ m_maxPreferredLogicalWidth = 0;
- // Adjust the fraction line thickness for the zoom
- if (lastChild() && lastChild()->isRenderBlock())
- m_lineThickness *= ceilf(gFractionBarWidth * style().fontSize());
+ if (isValid()) {
+ LayoutUnit numeratorWidth = numerator().maxPreferredLogicalWidth();
+ LayoutUnit denominatorWidth = denominator().maxPreferredLogicalWidth();
+ m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = std::max(numeratorWidth, denominatorWidth);
+ }
- RenderMathMLBlock::layout();
+ setPreferredLogicalWidthsDirty(false);
+}
+
+LayoutUnit RenderMathMLFraction::horizontalOffset(RenderBox& child, MathMLFractionElement::FractionAlignment align)
+{
+ switch (align) {
+ case MathMLFractionElement::FractionAlignmentRight:
+ return LayoutUnit(logicalWidth() - child.logicalWidth());
+ case MathMLFractionElement::FractionAlignmentCenter:
+ return LayoutUnit((logicalWidth() - child.logicalWidth()) / 2);
+ case MathMLFractionElement::FractionAlignmentLeft:
+ return LayoutUnit(0);
+ }
+
+ ASSERT_NOT_REACHED();
+ return LayoutUnit(0);
+}
+
+void RenderMathMLFraction::layoutBlock(bool relayoutChildren, LayoutUnit)
+{
+ ASSERT(needsLayout());
+
+ if (!relayoutChildren && simplifiedLayout())
+ return;
+
+ if (!isValid()) {
+ layoutInvalidMarkup();
+ return;
+ }
+
+ numerator().layoutIfNeeded();
+ denominator().layoutIfNeeded();
+
+ setLogicalWidth(std::max(numerator().logicalWidth(), denominator().logicalWidth()));
+
+ updateLineThickness();
+ LayoutUnit verticalOffset = 0; // This is the top of the renderer.
+ LayoutPoint numeratorLocation(horizontalOffset(numerator(), element().numeratorAlignment()), verticalOffset);
+ numerator().setLocation(numeratorLocation);
+
+ LayoutUnit numeratorAscent = ascentForChild(numerator());
+ LayoutUnit numeratorDescent = numerator().logicalHeight() - numeratorAscent;
+ LayoutUnit denominatorAscent = ascentForChild(denominator());
+ LayoutUnit denominatorDescent = denominator().logicalHeight() - denominatorAscent;
+ if (isStack()) {
+ StackParameters parameters = stackParameters();
+ LayoutUnit gap = parameters.topShiftUp - numeratorDescent + parameters.bottomShiftDown - denominatorAscent;
+ if (gap < parameters.gapMin) {
+ // If the gap is not large enough, we increase the shifts by the same value.
+ LayoutUnit delta = (parameters.gapMin - gap) / 2;
+ parameters.topShiftUp += delta;
+ parameters.bottomShiftDown += delta;
+ }
+ verticalOffset += numeratorAscent + parameters.topShiftUp; // This is the middle of the stack gap.
+ m_ascent = verticalOffset + mathAxisHeight();
+ verticalOffset += parameters.bottomShiftDown - denominatorAscent;
+ } else {
+ FractionParameters parameters = fractionParameters();
+ verticalOffset += std::max(numerator().logicalHeight() + parameters.numeratorGapMin + m_lineThickness / 2, numeratorAscent + parameters.numeratorMinShiftUp); // This is the middle of the fraction bar.
+ m_ascent = verticalOffset + mathAxisHeight();
+ verticalOffset += std::max(m_lineThickness / 2 + parameters.denominatorGapMin, parameters.denominatorMinShiftDown - denominatorAscent);
+ }
+
+ LayoutPoint denominatorLocation(horizontalOffset(denominator(), element().denominatorAlignment()), verticalOffset);
+ denominator().setLocation(denominatorLocation);
+
+ verticalOffset = std::max(verticalOffset + denominator().logicalHeight(), m_ascent + denominatorDescent); // This is the bottom of our renderer.
+ setLogicalHeight(verticalOffset);
+
+ clearNeedsLayout();
}
void RenderMathMLFraction::paint(PaintInfo& info, const LayoutPoint& paintOffset)
{
RenderMathMLBlock::paint(info, paintOffset);
- if (info.context->paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE)
- return;
-
- RenderBox* denominatorWrapper = lastChildBox();
- if (!denominatorWrapper || !m_lineThickness)
+ if (info.context().paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE || !isValid() || isStack())
return;
- IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset + location() + denominatorWrapper->location() + LayoutPoint(0, m_lineThickness / 2));
-
- GraphicsContextStateSaver stateSaver(*info.context);
-
- info.context->setStrokeThickness(m_lineThickness);
- info.context->setStrokeStyle(SolidStroke);
- info.context->setStrokeColor(style().visitedDependentColor(CSSPropertyColor), ColorSpaceSRGB);
-
- info.context->drawLine(adjustedPaintOffset, IntPoint(adjustedPaintOffset.x() + denominatorWrapper->pixelSnappedOffsetWidth(), adjustedPaintOffset.y()));
+ IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset + location() + LayoutPoint(0, m_ascent - mathAxisHeight()));
+
+ GraphicsContextStateSaver stateSaver(info.context());
+
+ info.context().setStrokeThickness(m_lineThickness);
+ info.context().setStrokeStyle(SolidStroke);
+ info.context().setStrokeColor(style().visitedDependentColor(CSSPropertyColor));
+ info.context().drawLine(adjustedPaintOffset, roundedIntPoint(LayoutPoint(adjustedPaintOffset.x() + logicalWidth(), adjustedPaintOffset.y())));
}
-int RenderMathMLFraction::firstLineBaseline() const
+std::optional<int> RenderMathMLFraction::firstLineBaseline() const
{
- if (RenderBox* denominatorWrapper = lastChildBox())
- return denominatorWrapper->logicalTop() + static_cast<int>(lroundf((m_lineThickness + style().fontMetrics().xHeight()) / 2));
+ if (isValid())
+ return std::optional<int>(std::lround(static_cast<float>(m_ascent)));
return RenderMathMLBlock::firstLineBaseline();
}