RenderMathMLRoot.cpp [plain text]
#include "config.h"
#if ENABLE(MATHML)
#include "RenderMathMLRoot.h"
#include "FontCache.h"
#include "GraphicsContext.h"
#include "PaintInfo.h"
#include "RenderIterator.h"
#include "RenderMathMLRadicalOperator.h"
#include "RenderMathMLSquareRoot.h"
namespace WebCore {
RenderMathMLRoot::RenderMathMLRoot(Element& element, Ref<RenderStyle>&& style)
: RenderMathMLBlock(element, WTF::move(style))
{
}
RenderMathMLRoot::RenderMathMLRoot(Document& document, Ref<RenderStyle>&& style)
: RenderMathMLBlock(document, WTF::move(style))
{
}
RenderMathMLRootWrapper* RenderMathMLRoot::baseWrapper() const
{
ASSERT(!isEmpty());
return downcast<RenderMathMLRootWrapper>(lastChild());
}
RenderMathMLBlock* RenderMathMLRoot::radicalWrapper() const
{
ASSERT(!isEmpty());
return downcast<RenderMathMLBlock>(lastChild()->previousSibling());
}
RenderMathMLRootWrapper* RenderMathMLRoot::indexWrapper() const
{
ASSERT(!isEmpty());
return is<RenderMathMLSquareRoot>(*this) ? nullptr : downcast<RenderMathMLRootWrapper>(firstChild());
}
RenderMathMLRadicalOperator* RenderMathMLRoot::radicalOperator() const
{
ASSERT(!isEmpty());
return downcast<RenderMathMLRadicalOperator>(radicalWrapper()->firstChild());
}
void RenderMathMLRoot::restructureWrappers()
{
ASSERT(!isEmpty());
auto base = baseWrapper();
auto index = indexWrapper();
auto radical = radicalWrapper();
if (base->isEmpty() && (!index || index->isEmpty())) {
if (!radical->isEmpty()) {
auto child = radicalOperator();
radical->removeChild(*child);
child->destroy();
}
}
if (radical->isEmpty()) {
RenderPtr<RenderMathMLRadicalOperator> radicalOperator = createRenderer<RenderMathMLRadicalOperator>(document(), RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX));
radicalOperator->initializeStyle();
radical->addChild(radicalOperator.leakPtr());
}
if (isRenderMathMLSquareRoot())
return;
if (auto childToMove = base->lastChild()) {
if (childToMove->previousSibling() && index->isEmpty()) {
base->removeChildWithoutRestructuring(*childToMove);
index->addChild(childToMove);
}
}
if (auto childToMove = index->firstChild()) {
if (childToMove->nextSibling() || base->isEmpty()) {
index->removeChildWithoutRestructuring(*childToMove);
base->addChild(childToMove);
}
}
}
void RenderMathMLRoot::addChild(RenderObject* newChild, RenderObject* beforeChild)
{
if (isEmpty()) {
if (!isRenderMathMLSquareRoot()) {
RenderMathMLBlock::addChild(RenderMathMLRootWrapper::createAnonymousWrapper(this).leakPtr());
}
RenderMathMLBlock::addChild(RenderMathMLBlock::createAnonymousMathMLBlock().leakPtr());
RenderMathMLBlock::addChild(RenderMathMLRootWrapper::createAnonymousWrapper(this).leakPtr());
updateStyle();
}
auto base = baseWrapper();
auto index = indexWrapper();
RenderElement* actualParent;
RenderElement* actualBeforeChild;
if (is<RenderMathMLSquareRoot>(*this)) {
actualParent = base;
if (beforeChild && beforeChild->parent() == base)
actualBeforeChild = downcast<RenderElement>(beforeChild);
else
actualBeforeChild = nullptr;
} else {
actualParent = beforeChild ? beforeChild->parent() : nullptr;
if (actualParent == base || actualParent == index)
actualBeforeChild = downcast<RenderElement>(beforeChild);
else {
actualParent = index;
actualBeforeChild = nullptr;
}
}
actualParent->addChild(newChild, actualBeforeChild);
restructureWrappers();
}
void RenderMathMLRoot::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderMathMLBlock::styleDidChange(diff, oldStyle);
if (!isEmpty())
updateStyle();
}
void RenderMathMLRoot::updateFromElement()
{
RenderMathMLBlock::updateFromElement();
if (!isEmpty())
updateStyle();
}
void RenderMathMLRoot::updateStyle()
{
ASSERT(!isEmpty());
m_ruleThickness = 0.05f * style().fontCascade().size();
m_verticalGap = 11 * m_ruleThickness / 4;
m_extraAscender = m_ruleThickness;
LayoutUnit kernBeforeDegree = 5 * style().fontCascade().size() / 18;
LayoutUnit kernAfterDegree = -10 * style().fontCascade().size() / 18;
m_degreeBottomRaisePercent = 0.6f;
const auto& primaryFont = style().fontCascade().primaryFont();
if (auto* mathData = style().fontCascade().primaryFont().mathData()) {
m_verticalGap = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalVerticalGap);
m_ruleThickness = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalRuleThickness);
m_extraAscender = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalExtraAscender);
if (!isRenderMathMLSquareRoot()) {
kernBeforeDegree = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalKernBeforeDegree);
kernAfterDegree = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalKernAfterDegree);
m_degreeBottomRaisePercent = mathData->getMathConstant(primaryFont, OpenTypeMathData::RadicalDegreeBottomRaisePercent);
}
}
auto radical = radicalWrapper();
auto radicalStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX);
radicalStyle.get().setMarginTop(Length(0, Fixed)); radical->setStyle(WTF::move(radicalStyle));
radical->setNeedsLayoutAndPrefWidthsRecalc();
auto base = baseWrapper();
auto baseStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX);
baseStyle.get().setMarginTop(Length(0, Fixed)); baseStyle.get().setAlignItemsPosition(ItemPositionBaseline);
base->setStyle(WTF::move(baseStyle));
base->setNeedsLayoutAndPrefWidthsRecalc();
if (!isRenderMathMLSquareRoot()) {
auto index = indexWrapper();
auto indexStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX);
indexStyle.get().setMarginTop(Length(0, Fixed)); indexStyle.get().setMarginStart(Length(kernBeforeDegree, Fixed));
indexStyle.get().setMarginEnd(Length(kernAfterDegree, Fixed));
indexStyle.get().setAlignItemsPosition(ItemPositionBaseline);
index->setStyle(WTF::move(indexStyle));
index->setNeedsLayoutAndPrefWidthsRecalc();
}
}
Optional<int> RenderMathMLRoot::firstLineBaseline() const
{
if (!isEmpty()) {
auto base = baseWrapper();
return static_cast<int>(lroundf(base->firstLineBaseline().valueOr(-1) + base->marginTop()));
}
return RenderMathMLBlock::firstLineBaseline();
}
void RenderMathMLRoot::layout()
{
if (isEmpty()) {
RenderMathMLBlock::layout();
return;
}
auto base = baseWrapper();
if (base->marginTop() > 0) {
RenderMathMLBlock::layout();
return;
}
for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
if (child->needsLayout())
downcast<RenderElement>(*child).layout();
}
auto radical = radicalOperator();
if (radical) {
float baseHeight = base->logicalHeight();
float baseHeightAboveBaseline = base->firstLineBaseline().valueOr(baseHeight);
float baseDepthBelowBaseline = baseHeight - baseHeightAboveBaseline;
baseHeightAboveBaseline += m_verticalGap;
radical->stretchTo(baseHeightAboveBaseline, baseDepthBelowBaseline);
float radicalTopMargin = m_extraAscender;
float baseTopMargin = m_verticalGap + m_ruleThickness + m_extraAscender;
if (!isRenderMathMLSquareRoot()) {
auto index = indexWrapper();
float indexHeight = 0;
if (!index->isEmpty())
indexHeight = downcast<RenderBlock>(*index->firstChild()).logicalHeight();
float indexTopMargin = (1.0 - m_degreeBottomRaisePercent) * radical->stretchSize() + radicalTopMargin - indexHeight;
if (indexTopMargin < 0) {
radicalTopMargin -= indexTopMargin;
baseTopMargin -= indexTopMargin;
indexTopMargin = 0;
}
index->style().setMarginTop(Length(indexTopMargin, Fixed));
}
radical->style().setMarginTop(Length(radicalTopMargin, Fixed));
base->style().setMarginTop(Length(baseTopMargin, Fixed));
}
RenderMathMLBlock::layout();
}
void RenderMathMLRoot::paint(PaintInfo& info, const LayoutPoint& paintOffset)
{
RenderMathMLBlock::paint(info, paintOffset);
if (isEmpty() || info.context->paintingDisabled() || style().visibility() != VISIBLE)
return;
auto base = baseWrapper();
auto radical = radicalOperator();
if (!base || !radical || !m_ruleThickness)
return;
GraphicsContextStateSaver stateSaver(*info.context);
info.context->setStrokeThickness(m_ruleThickness);
info.context->setStrokeStyle(SolidStroke);
info.context->setStrokeColor(style().visitedDependentColor(CSSPropertyColor), ColorSpaceDeviceRGB);
LayoutUnit sizeError = radical->trailingSpaceError();
IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset + location() + base->location() + LayoutPoint(-sizeError, -(m_verticalGap + m_ruleThickness / 2)));
info.context->drawLine(adjustedPaintOffset, IntPoint(adjustedPaintOffset.x() + base->pixelSnappedOffsetWidth() + sizeError, adjustedPaintOffset.y()));
}
RenderPtr<RenderMathMLRootWrapper> RenderMathMLRootWrapper::createAnonymousWrapper(RenderMathMLRoot* renderObject)
{
RenderPtr<RenderMathMLRootWrapper> newBlock = createRenderer<RenderMathMLRootWrapper>(renderObject->document(), RenderStyle::createAnonymousStyleWithDisplay(&renderObject->style(), FLEX));
newBlock->initializeStyle();
return newBlock;
}
void RenderMathMLRootWrapper::removeChildWithoutRestructuring(RenderObject& child)
{
RenderMathMLBlock::removeChild(child);
}
void RenderMathMLRootWrapper::removeChild(RenderObject& child)
{
RenderMathMLBlock::removeChild(child);
if (!(beingDestroyed() || documentBeingDestroyed()))
downcast<RenderMathMLRoot>(*parent()).restructureWrappers();
}
}
#endif // ENABLE(MATHML)