RenderMathMLFraction.cpp [plain text]
#include "config.h"
#if ENABLE(MATHML)
#include "RenderMathMLFraction.h"
#include "GraphicsContext.h"
#include "MathMLNames.h"
#include "PaintInfo.h"
#include <cmath>
namespace WebCore {
using namespace MathMLNames;
RenderMathMLFraction::RenderMathMLFraction(MathMLInlineContainerElement& element, RenderStyle&& style)
: RenderMathMLBlock(element, WTFMove(style))
{
}
bool RenderMathMLFraction::isValid() const
{
auto* child = firstChildBox();
if (!child)
return false;
child = child->nextSiblingBox();
return child && !child->nextSiblingBox();
}
RenderBox& RenderMathMLFraction::numerator() const
{
ASSERT(isValid());
return *firstChildBox();
}
RenderBox& RenderMathMLFraction::denominator() const
{
ASSERT(isValid());
return *firstChildBox()->nextSiblingBox();
}
void RenderMathMLFraction::updateLayoutParameters()
{
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();
m_lineThickness = toUserUnits(element().lineThickness(), style(), m_defaultLineThickness);
if (m_lineThickness < 0)
m_lineThickness = 0;
bool display = mathMLStyle()->displayStyle();
if (isStack()) {
if (mathData) {
m_gapMin = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::StackDisplayStyleGapMin : OpenTypeMathData::StackGapMin);
m_topShiftUp = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::StackTopDisplayStyleShiftUp : OpenTypeMathData::StackTopShiftUp);
m_bottomShiftDown = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::StackBottomDisplayStyleShiftDown : OpenTypeMathData::StackBottomShiftDown);
} else {
m_gapMin = m_denominatorGapMin = display ? 7 * ruleThicknessFallback() : 3 * ruleThicknessFallback();
m_topShiftUp = m_bottomShiftDown = 0;
}
} else {
if (mathData) {
m_numeratorGapMin = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionNumDisplayStyleGapMin : OpenTypeMathData::FractionNumeratorGapMin);
m_denominatorGapMin = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionDenomDisplayStyleGapMin : OpenTypeMathData::FractionDenominatorGapMin);
m_numeratorMinShiftUp = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionNumeratorDisplayStyleShiftUp : OpenTypeMathData::FractionNumeratorShiftUp);
m_denominatorMinShiftDown = mathData->getMathConstant(primaryFont, display ? OpenTypeMathData::FractionDenominatorDisplayStyleShiftDown : OpenTypeMathData::FractionDenominatorShiftDown);
} else {
m_numeratorGapMin = m_denominatorGapMin = display ? 3 * ruleThicknessFallback() : ruleThicknessFallback();
m_numeratorMinShiftUp = m_denominatorMinShiftDown = 0;
}
}
}
RenderMathMLOperator* RenderMathMLFraction::unembellishedOperator()
{
if (!isValid() || !is<RenderMathMLBlock>(numerator()))
return nullptr;
return downcast<RenderMathMLBlock>(numerator()).unembellishedOperator();
}
void RenderMathMLFraction::computePreferredLogicalWidths()
{
ASSERT(preferredLogicalWidthsDirty());
m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0;
if (isValid()) {
LayoutUnit numeratorWidth = numerator().maxPreferredLogicalWidth();
LayoutUnit denominatorWidth = denominator().maxPreferredLogicalWidth();
m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = std::max(numeratorWidth, denominatorWidth);
}
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()) {
setLogicalWidth(0);
setLogicalHeight(0);
clearNeedsLayout();
return;
}
numerator().layoutIfNeeded();
denominator().layoutIfNeeded();
setLogicalWidth(std::max(numerator().logicalWidth(), denominator().logicalWidth()));
updateLayoutParameters();
LayoutUnit verticalOffset = 0; 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()) {
LayoutUnit topShiftUp = m_topShiftUp;
LayoutUnit bottomShiftDown = m_bottomShiftDown;
LayoutUnit gap = topShiftUp - numeratorDescent + bottomShiftDown - denominatorAscent;
if (gap < m_gapMin) {
LayoutUnit delta = (m_gapMin - gap) / 2;
topShiftUp += delta;
bottomShiftDown += delta;
}
verticalOffset += numeratorAscent + topShiftUp; m_ascent = verticalOffset + mathAxisHeight();
verticalOffset += bottomShiftDown - denominatorAscent;
} else {
verticalOffset += std::max(numerator().logicalHeight() + m_numeratorGapMin + m_lineThickness / 2, numeratorAscent + m_numeratorMinShiftUp); m_ascent = verticalOffset + mathAxisHeight();
verticalOffset += std::max(m_lineThickness / 2 + m_denominatorGapMin, m_denominatorMinShiftDown - denominatorAscent);
}
LayoutPoint denominatorLocation(horizontalOffset(denominator(), element().denominatorAlignment()), verticalOffset);
denominator().setLocation(denominatorLocation);
verticalOffset = std::max(verticalOffset + denominator().logicalHeight(), m_ascent + denominatorDescent); 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 || !isValid() || isStack())
return;
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())));
}
Optional<int> RenderMathMLFraction::firstLineBaseline() const
{
if (isValid())
return Optional<int>(std::lround(static_cast<float>(m_ascent)));
return RenderMathMLBlock::firstLineBaseline();
}
}
#endif // ENABLE(MATHML)