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/RenderMathMLUnderOver.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/rendering/mathml/RenderMathMLUnderOver.cpp')
-rw-r--r-- | Source/WebCore/rendering/mathml/RenderMathMLUnderOver.cpp | 299 |
1 files changed, 271 insertions, 28 deletions
diff --git a/Source/WebCore/rendering/mathml/RenderMathMLUnderOver.cpp b/Source/WebCore/rendering/mathml/RenderMathMLUnderOver.cpp index 34af852db..2ab8cc5c6 100644 --- a/Source/WebCore/rendering/mathml/RenderMathMLUnderOver.cpp +++ b/Source/WebCore/rendering/mathml/RenderMathMLUnderOver.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 Alex Milowski (alex@milowski.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 @@ -24,48 +25,290 @@ */ #include "config.h" +#include "RenderMathMLUnderOver.h" #if ENABLE(MATHML) -#include "RenderMathMLUnderOver.h" - -#include "MathMLNames.h" +#include "MathMLElement.h" +#include "MathMLOperatorDictionary.h" +#include "MathMLUnderOverElement.h" +#include "RenderIterator.h" +#include "RenderMathMLOperator.h" namespace WebCore { -using namespace MathMLNames; - -RenderMathMLUnderOver::RenderMathMLUnderOver(Element& element, PassRef<RenderStyle> style) - : RenderMathMLBlock(element, std::move(style)) +RenderMathMLUnderOver::RenderMathMLUnderOver(MathMLUnderOverElement& element, RenderStyle&& style) + : RenderMathMLScripts(element, WTFMove(style)) +{ +} + +MathMLUnderOverElement& RenderMathMLUnderOver::element() const +{ + return static_cast<MathMLUnderOverElement&>(nodeForNonAnonymous()); +} + +void RenderMathMLUnderOver::computeOperatorsHorizontalStretch() { - // Determine what kind of under/over expression we have by element name - if (element.hasLocalName(MathMLNames::munderTag)) - m_kind = Under; - else if (element.hasLocalName(MathMLNames::moverTag)) - m_kind = Over; - else { - ASSERT(element.hasLocalName(MathMLNames::munderoverTag)); - m_kind = UnderOver; + LayoutUnit stretchWidth = 0; + Vector<RenderMathMLOperator*, 2> renderOperators; + + for (auto* child = firstChildBox(); child; child = child->nextSiblingBox()) { + if (child->needsLayout()) { + if (is<RenderMathMLBlock>(child)) { + if (auto renderOperator = downcast<RenderMathMLBlock>(*child).unembellishedOperator()) { + if (renderOperator->isStretchy() && !renderOperator->isVertical()) { + renderOperator->resetStretchSize(); + renderOperators.append(renderOperator); + } + } + } + + child->layout(); + } + + // Skipping the embellished op does not work for nested structures like + // <munder><mover><mo>_</mo>...</mover> <mo>_</mo></munder>. + stretchWidth = std::max(stretchWidth, child->logicalWidth()); } + + // Set the sizes of (possibly embellished) stretchy operator children. + for (auto& renderOperator : renderOperators) + renderOperator->stretchTo(stretchWidth); } -RenderMathMLOperator* RenderMathMLUnderOver::unembellishedOperator() +bool RenderMathMLUnderOver::isValid() const { - RenderObject* base = firstChild(); - if (!base || !base->isRenderMathMLBlock()) - return 0; - return toRenderMathMLBlock(base)->unembellishedOperator(); + // Verify whether the list of children is valid: + // <munder> base under </munder> + // <mover> base over </mover> + // <munderover> base under over </munderover> + auto* child = firstChildBox(); + if (!child) + return false; + child = child->nextSiblingBox(); + if (!child) + return false; + child = child->nextSiblingBox(); + switch (m_scriptType) { + case Over: + case Under: + return !child; + case UnderOver: + return child && !child->nextSiblingBox(); + default: + ASSERT_NOT_REACHED(); + return false; + } } -int RenderMathMLUnderOver::firstLineBaseline() const +bool RenderMathMLUnderOver::shouldMoveLimits() { - RenderBox* base = firstChildBox(); - if (!base) - return -1; - LayoutUnit baseline = base->firstLineBaseline(); - if (baseline != -1) - baseline += base->logicalTop(); - return baseline; + if (auto* renderOperator = unembellishedOperator()) + return renderOperator->shouldMoveLimits(); + return false; +} + +RenderBox& RenderMathMLUnderOver::base() const +{ + ASSERT(isValid()); + return *firstChildBox(); +} + +RenderBox& RenderMathMLUnderOver::under() const +{ + ASSERT(isValid()); + ASSERT(m_scriptType == Under || m_scriptType == UnderOver); + return *firstChildBox()->nextSiblingBox(); +} + +RenderBox& RenderMathMLUnderOver::over() const +{ + ASSERT(isValid()); + ASSERT(m_scriptType == Over || m_scriptType == UnderOver); + auto* secondChild = firstChildBox()->nextSiblingBox(); + return m_scriptType == Over ? *secondChild : *secondChild->nextSiblingBox(); +} + + +void RenderMathMLUnderOver::computePreferredLogicalWidths() +{ + ASSERT(preferredLogicalWidthsDirty()); + + if (!isValid()) { + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0; + setPreferredLogicalWidthsDirty(false); + return; + } + + if (shouldMoveLimits()) { + RenderMathMLScripts::computePreferredLogicalWidths(); + return; + } + + LayoutUnit preferredWidth = base().maxPreferredLogicalWidth(); + + if (m_scriptType == Under || m_scriptType == UnderOver) + preferredWidth = std::max(preferredWidth, under().maxPreferredLogicalWidth()); + + if (m_scriptType == Over || m_scriptType == UnderOver) + preferredWidth = std::max(preferredWidth, over().maxPreferredLogicalWidth()); + + m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = preferredWidth; + + setPreferredLogicalWidthsDirty(false); +} + +LayoutUnit RenderMathMLUnderOver::horizontalOffset(const RenderBox& child) const +{ + return (logicalWidth() - child.logicalWidth()) / 2; +} + +bool RenderMathMLUnderOver::hasAccent(bool accentUnder) const +{ + ASSERT(m_scriptType == UnderOver || (accentUnder && m_scriptType == Under) || (!accentUnder && m_scriptType == Over)); + + const MathMLElement::BooleanValue& attributeValue = accentUnder ? element().accentUnder() : element().accent(); + if (attributeValue == MathMLElement::BooleanValue::True) + return true; + if (attributeValue == MathMLElement::BooleanValue::False) + return false; + RenderBox& script = accentUnder ? under() : over(); + if (!is<RenderMathMLBlock>(script)) + return false; + auto* scriptOperator = downcast<RenderMathMLBlock>(script).unembellishedOperator(); + return scriptOperator && scriptOperator->hasOperatorFlag(MathMLOperatorDictionary::Accent); +} + +RenderMathMLUnderOver::VerticalParameters RenderMathMLUnderOver::verticalParameters() const +{ + VerticalParameters parameters; + + // By default, we set all values to zero. + parameters.underGapMin = 0; + parameters.overGapMin = 0; + parameters.underShiftMin = 0; + parameters.overShiftMin = 0; + parameters.underExtraDescender = 0; + parameters.overExtraAscender = 0; + parameters.accentBaseHeight = 0; + + const auto& primaryFont = style().fontCascade().primaryFont(); + auto* mathData = primaryFont.mathData(); + if (!mathData) { + // The MATH table specification does not really provide any suggestions, except for some underbar/overbar values and AccentBaseHeight. + LayoutUnit defaultLineThickness = ruleThicknessFallback(); + parameters.underGapMin = 3 * defaultLineThickness; + parameters.overGapMin = 3 * defaultLineThickness; + parameters.underExtraDescender = defaultLineThickness; + parameters.overExtraAscender = defaultLineThickness; + parameters.accentBaseHeight = style().fontMetrics().xHeight(); + parameters.useUnderOverBarFallBack = true; + return parameters; + } + + if (is<RenderMathMLBlock>(base())) { + if (auto* baseOperator = downcast<RenderMathMLBlock>(base()).unembellishedOperator()) { + if (baseOperator->hasOperatorFlag(MathMLOperatorDictionary::LargeOp)) { + // The base is a large operator so we read UpperLimit/LowerLimit constants from the MATH table. + parameters.underGapMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::LowerLimitGapMin); + parameters.overGapMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::UpperLimitGapMin); + parameters.underShiftMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::LowerLimitBaselineDropMin); + parameters.overShiftMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::UpperLimitBaselineRiseMin); + parameters.useUnderOverBarFallBack = false; + return parameters; + } + if (baseOperator->isStretchy() && !baseOperator->isVertical()) { + // The base is a horizontal stretchy operator, so we read StretchStack constants from the MATH table. + parameters.underGapMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::StretchStackGapBelowMin); + parameters.overGapMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::StretchStackGapAboveMin); + parameters.underShiftMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::StretchStackBottomShiftDown); + parameters.overShiftMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::StretchStackTopShiftUp); + parameters.useUnderOverBarFallBack = false; + return parameters; + } + } + } + + // By default, we just use the underbar/overbar constants. + parameters.underGapMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::UnderbarVerticalGap); + parameters.overGapMin = mathData->getMathConstant(primaryFont, OpenTypeMathData::OverbarVerticalGap); + parameters.underExtraDescender = mathData->getMathConstant(primaryFont, OpenTypeMathData::UnderbarExtraDescender); + parameters.overExtraAscender = mathData->getMathConstant(primaryFont, OpenTypeMathData::OverbarExtraAscender); + parameters.accentBaseHeight = mathData->getMathConstant(primaryFont, OpenTypeMathData::AccentBaseHeight); + parameters.useUnderOverBarFallBack = true; + return parameters; +} + +void RenderMathMLUnderOver::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeight) +{ + ASSERT(needsLayout()); + + if (!relayoutChildren && simplifiedLayout()) + return; + + if (!isValid()) { + layoutInvalidMarkup(); + return; + } + + if (shouldMoveLimits()) { + RenderMathMLScripts::layoutBlock(relayoutChildren, pageLogicalHeight); + return; + } + + recomputeLogicalWidth(); + + computeOperatorsHorizontalStretch(); + + base().layoutIfNeeded(); + if (m_scriptType == Under || m_scriptType == UnderOver) + under().layoutIfNeeded(); + if (m_scriptType == Over || m_scriptType == UnderOver) + over().layoutIfNeeded(); + + LayoutUnit logicalWidth = base().logicalWidth(); + if (m_scriptType == Under || m_scriptType == UnderOver) + logicalWidth = std::max(logicalWidth, under().logicalWidth()); + if (m_scriptType == Over || m_scriptType == UnderOver) + logicalWidth = std::max(logicalWidth, over().logicalWidth()); + setLogicalWidth(logicalWidth); + + VerticalParameters parameters = verticalParameters(); + LayoutUnit verticalOffset = 0; + if (m_scriptType == Over || m_scriptType == UnderOver) { + verticalOffset += parameters.overExtraAscender; + over().setLocation(LayoutPoint(horizontalOffset(over()), verticalOffset)); + if (parameters.useUnderOverBarFallBack) { + verticalOffset += over().logicalHeight(); + if (hasAccent()) { + LayoutUnit baseAscent = ascentForChild(base()); + if (baseAscent < parameters.accentBaseHeight) + verticalOffset += parameters.accentBaseHeight - baseAscent; + } else + verticalOffset += parameters.overGapMin; + } else { + LayoutUnit overAscent = ascentForChild(over()); + verticalOffset += std::max(over().logicalHeight() + parameters.overGapMin, overAscent + parameters.overShiftMin); + } + } + base().setLocation(LayoutPoint(horizontalOffset(base()), verticalOffset)); + verticalOffset += base().logicalHeight(); + if (m_scriptType == Under || m_scriptType == UnderOver) { + if (parameters.useUnderOverBarFallBack) { + if (!hasAccentUnder()) + verticalOffset += parameters.underGapMin; + } else { + LayoutUnit underAscent = ascentForChild(under()); + verticalOffset += std::max(parameters.underGapMin, parameters.underShiftMin - underAscent); + } + under().setLocation(LayoutPoint(horizontalOffset(under()), verticalOffset)); + verticalOffset += under().logicalHeight(); + verticalOffset += parameters.underExtraDescender; + } + + setLogicalHeight(verticalOffset); + + clearNeedsLayout(); } } |