RenderMathMLRoot.cpp [plain text]
#include "config.h"
#include "RenderMathMLRoot.h"
#if ENABLE(MATHML)
#include "FontCache.h"
#include "GraphicsContext.h"
#include "MathMLNames.h"
#include "MathMLRootElement.h"
#include "PaintInfo.h"
#include "RenderIterator.h"
#include "RenderMathMLMenclose.h"
#include "RenderMathMLOperator.h"
#include <wtf/IsoMallocInlines.h>
static const UChar gRadicalCharacter = 0x221A;
namespace WebCore {
WTF_MAKE_ISO_ALLOCATED_IMPL(RenderMathMLRoot);
RenderMathMLRoot::RenderMathMLRoot(MathMLRootElement& element, RenderStyle&& style)
: RenderMathMLRow(element, WTFMove(style))
{
m_radicalOperator.setOperator(RenderMathMLRoot::style(), gRadicalCharacter, MathOperator::Type::VerticalOperator);
}
MathMLRootElement& RenderMathMLRoot::element() const
{
return static_cast<MathMLRootElement&>(nodeForNonAnonymous());
}
RootType RenderMathMLRoot::rootType() const
{
return element().rootType();
}
bool RenderMathMLRoot::isValid() const
{
if (rootType() == RootType::SquareRoot)
return true;
ASSERT(rootType() == RootType::RootWithIndex);
auto* child = firstChildBox();
if (!child)
return false;
child = child->nextSiblingBox();
return child && !child->nextSiblingBox();
}
RenderBox& RenderMathMLRoot::getBase() const
{
ASSERT(isValid());
ASSERT(rootType() == RootType::RootWithIndex);
return *firstChildBox();
}
RenderBox& RenderMathMLRoot::getIndex() const
{
ASSERT(isValid());
ASSERT(rootType() == RootType::RootWithIndex);
return *firstChildBox()->nextSiblingBox();
}
void RenderMathMLRoot::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderMathMLRow::styleDidChange(diff, oldStyle);
m_radicalOperator.reset(style());
}
RenderMathMLRoot::HorizontalParameters RenderMathMLRoot::horizontalParameters()
{
HorizontalParameters parameters;
if (rootType() == RootType::SquareRoot)
return parameters;
const auto& primaryFont = style().fontCascade().primaryFont();
if (auto* mathData = style().fontCascade().primaryFont().mathData()) {
parameters.kernBeforeDegree = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalKernBeforeDegree);
parameters.kernAfterDegree = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalKernAfterDegree);
} else {
parameters.kernBeforeDegree = 5 * style().fontCascade().size() / 18;
parameters.kernAfterDegree = -10 * style().fontCascade().size() / 18;
}
return parameters;
}
RenderMathMLRoot::VerticalParameters RenderMathMLRoot::verticalParameters()
{
VerticalParameters parameters;
const auto& primaryFont = style().fontCascade().primaryFont();
if (auto* mathData = style().fontCascade().primaryFont().mathData()) {
parameters.ruleThickness = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalRuleThickness);
parameters.verticalGap = mathData->getMathConstant(primaryFont, mathMLStyle().displayStyle() ? OpenTypeMathData::RadicalDisplayStyleVerticalGap : OpenTypeMathData::RadicalVerticalGap);
parameters.extraAscender = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalExtraAscender);
if (rootType() == RootType::RootWithIndex)
parameters.degreeBottomRaisePercent = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalDegreeBottomRaisePercent);
} else {
parameters.ruleThickness = ruleThicknessFallback();
if (mathMLStyle().displayStyle())
parameters.verticalGap = parameters.ruleThickness + style().fontMetrics().xHeight() / 4;
else
parameters.verticalGap = 5 * parameters.ruleThickness / 4;
if (rootType() == RootType::RootWithIndex) {
parameters.extraAscender = parameters.ruleThickness;
parameters.degreeBottomRaisePercent = 0.6f;
}
}
return parameters;
}
void RenderMathMLRoot::computePreferredLogicalWidths()
{
ASSERT(preferredLogicalWidthsDirty());
if (!isValid()) {
m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0;
setPreferredLogicalWidthsDirty(false);
return;
}
LayoutUnit preferredWidth = 0;
if (rootType() == RootType::SquareRoot) {
preferredWidth += m_radicalOperator.maxPreferredWidth();
setPreferredLogicalWidthsDirty(true);
RenderMathMLRow::computePreferredLogicalWidths();
preferredWidth += m_maxPreferredLogicalWidth;
} else {
ASSERT(rootType() == RootType::RootWithIndex);
auto horizontal = horizontalParameters();
preferredWidth += horizontal.kernBeforeDegree;
preferredWidth += getIndex().maxPreferredLogicalWidth();
preferredWidth += horizontal.kernAfterDegree;
preferredWidth += m_radicalOperator.maxPreferredWidth();
preferredWidth += getBase().maxPreferredLogicalWidth();
}
m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = preferredWidth;
setPreferredLogicalWidthsDirty(false);
}
void RenderMathMLRoot::layoutBlock(bool relayoutChildren, LayoutUnit)
{
ASSERT(needsLayout());
if (!relayoutChildren && simplifiedLayout())
return;
m_radicalOperatorTop = 0;
m_baseWidth = 0;
if (!isValid()) {
layoutInvalidMarkup(relayoutChildren);
return;
}
LayoutUnit baseAscent, baseDescent;
recomputeLogicalWidth();
if (rootType() == RootType::SquareRoot) {
stretchVerticalOperatorsAndLayoutChildren();
getContentBoundingBox(m_baseWidth, baseAscent, baseDescent);
layoutRowItems(m_baseWidth, baseAscent);
} else {
getBase().layoutIfNeeded();
m_baseWidth = getBase().logicalWidth();
baseAscent = ascentForChild(getBase());
baseDescent = getBase().logicalHeight() - baseAscent;
getIndex().layoutIfNeeded();
}
auto horizontal = horizontalParameters();
auto vertical = verticalParameters();
m_radicalOperator.stretchTo(style(), baseAscent + baseDescent);
LayoutUnit radicalOperatorHeight = m_radicalOperator.ascent() + m_radicalOperator.descent();
LayoutUnit indexBottomRaise = vertical.degreeBottomRaisePercent * radicalOperatorHeight;
LayoutUnit radicalAscent = baseAscent + vertical.verticalGap + vertical.ruleThickness + vertical.extraAscender;
LayoutUnit radicalDescent = std::max<LayoutUnit>(baseDescent, radicalOperatorHeight + vertical.extraAscender - radicalAscent);
LayoutUnit descent = radicalDescent;
LayoutUnit ascent = radicalAscent;
if (rootType() == RootType::SquareRoot)
setLogicalWidth(m_radicalOperator.width() + m_baseWidth);
else {
ASSERT(rootType() == RootType::RootWithIndex);
setLogicalWidth(horizontal.kernBeforeDegree + getIndex().logicalWidth() + horizontal.kernAfterDegree + m_radicalOperator.width() + m_baseWidth);
}
LayoutUnit indexAscent, indexDescent;
if (rootType() == RootType::RootWithIndex) {
indexAscent = ascentForChild(getIndex());
indexDescent = getIndex().logicalHeight() - indexAscent;
ascent = std::max<LayoutUnit>(radicalAscent, indexBottomRaise + indexDescent + indexAscent - descent);
}
m_radicalOperatorTop = ascent - radicalAscent + vertical.extraAscender;
LayoutUnit horizontalOffset = m_radicalOperator.width();
if (rootType() == RootType::RootWithIndex)
horizontalOffset += horizontal.kernBeforeDegree + getIndex().logicalWidth() + horizontal.kernAfterDegree;
LayoutPoint baseLocation(mirrorIfNeeded(horizontalOffset, m_baseWidth), ascent - baseAscent);
if (rootType() == RootType::SquareRoot) {
for (auto* child = firstChildBox(); child; child = child->nextSiblingBox())
child->setLocation(child->location() + baseLocation);
} else {
ASSERT(rootType() == RootType::RootWithIndex);
getBase().setLocation(baseLocation);
LayoutPoint indexLocation(mirrorIfNeeded(horizontal.kernBeforeDegree, getIndex()), ascent + descent - indexBottomRaise - indexDescent - indexAscent);
getIndex().setLocation(indexLocation);
}
setLogicalHeight(ascent + descent);
layoutPositionedObjects(relayoutChildren);
clearNeedsLayout();
}
void RenderMathMLRoot::paint(PaintInfo& info, const LayoutPoint& paintOffset)
{
RenderMathMLRow::paint(info, paintOffset);
if (!firstChild() || info.context().paintingDisabled() || style().visibility() != VISIBLE || !isValid())
return;
LayoutPoint radicalOperatorTopLeft = paintOffset + location();
LayoutUnit horizontalOffset = 0;
if (rootType() == RootType::RootWithIndex) {
auto horizontal = horizontalParameters();
horizontalOffset = horizontal.kernBeforeDegree + getIndex().logicalWidth() + horizontal.kernAfterDegree;
}
radicalOperatorTopLeft.move(mirrorIfNeeded(horizontalOffset, m_radicalOperator.width()), m_radicalOperatorTop);
m_radicalOperator.paint(style(), info, radicalOperatorTopLeft);
LayoutUnit ruleThickness = verticalParameters().ruleThickness;
if (!ruleThickness)
return;
GraphicsContextStateSaver stateSaver(info.context());
info.context().setStrokeThickness(ruleThickness);
info.context().setStrokeStyle(SolidStroke);
info.context().setStrokeColor(style().visitedDependentColor(CSSPropertyColor));
LayoutPoint ruleOffsetFrom = paintOffset + location() + LayoutPoint(0, m_radicalOperatorTop + ruleThickness / 2);
LayoutPoint ruleOffsetTo = ruleOffsetFrom;
horizontalOffset += m_radicalOperator.width();
ruleOffsetFrom.move(mirrorIfNeeded(horizontalOffset), 0);
horizontalOffset += m_baseWidth;
ruleOffsetTo.move(mirrorIfNeeded(horizontalOffset), 0);
info.context().drawLine(ruleOffsetFrom, ruleOffsetTo);
}
}
#endif // ENABLE(MATHML)