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/RenderMathMLFraction.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/rendering/mathml/RenderMathMLFraction.cpp')
-rw-r--r-- | Source/WebCore/rendering/mathml/RenderMathMLFraction.cpp | 269 |
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(); } |