RenderMathMLOperator.cpp [plain text]
#include "config.h"
#if ENABLE(MATHML)
#include "RenderMathMLOperator.h"
#include "FontSelector.h"
#include "MathMLNames.h"
#include "PaintInfo.h"
#include "RenderBlockFlow.h"
#include "RenderText.h"
#include "ScaleTransformOperation.h"
#include "TransformOperations.h"
#include <cmath>
#include <wtf/MathExtras.h>
#include <wtf/unicode/CharacterNames.h>
namespace WebCore {
using namespace MathMLNames;
RenderMathMLOperator::RenderMathMLOperator(MathMLElement& element, RenderStyle&& style)
: RenderMathMLToken(element, WTFMove(style))
, m_stretchHeightAboveBaseline(0)
, m_stretchDepthBelowBaseline(0)
, m_textContent(0)
, m_isVertical(true)
{
updateTokenContent();
}
RenderMathMLOperator::RenderMathMLOperator(Document& document, RenderStyle&& style, const String& operatorString, MathMLOperatorDictionary::Form form, unsigned short flags)
: RenderMathMLToken(document, WTFMove(style))
, m_stretchHeightAboveBaseline(0)
, m_stretchDepthBelowBaseline(0)
, m_textContent(0)
, m_isVertical(true)
, m_operatorForm(form)
, m_operatorFlags(flags)
{
updateTokenContent(operatorString);
}
void RenderMathMLOperator::setOperatorFlagFromAttribute(MathMLOperatorDictionary::Flag flag, const QualifiedName& name)
{
setOperatorFlagFromAttributeValue(flag, element().attributeWithoutSynchronization(name));
}
void RenderMathMLOperator::setOperatorFlagFromAttributeValue(MathMLOperatorDictionary::Flag flag, const AtomicString& attributeValue)
{
ASSERT(!isAnonymous());
if (attributeValue == "true")
m_operatorFlags |= flag;
else if (attributeValue == "false")
m_operatorFlags &= ~flag;
}
void RenderMathMLOperator::setOperatorPropertiesFromOpDictEntry(const MathMLOperatorDictionary::Entry* entry)
{
if (isAnonymous())
m_operatorFlags = (m_operatorFlags & (MathMLOperatorDictionary::Fence | MathMLOperatorDictionary::Separator)) | entry->flags;
else
m_operatorFlags = entry->flags;
m_leadingSpace = entry->lspace * style().fontCascade().size() / 18;
m_trailingSpace = entry->rspace * style().fontCascade().size() / 18;
}
void RenderMathMLOperator::setOperatorProperties()
{
m_isVertical = MathMLOperatorDictionary::isVertical(m_textContent);
bool explicitForm = true;
if (!isAnonymous()) {
const AtomicString& form = element().attributeWithoutSynchronization(MathMLNames::formAttr);
if (form == "prefix")
m_operatorForm = MathMLOperatorDictionary::Prefix;
else if (form == "infix")
m_operatorForm = MathMLOperatorDictionary::Infix;
else if (form == "postfix")
m_operatorForm = MathMLOperatorDictionary::Postfix;
else {
explicitForm = false;
if (!element().previousSibling() && element().nextSibling())
m_operatorForm = MathMLOperatorDictionary::Prefix;
else if (element().previousSibling() && !element().nextSibling())
m_operatorForm = MathMLOperatorDictionary::Postfix;
else
m_operatorForm = MathMLOperatorDictionary::Infix;
}
}
if (isAnonymous())
m_operatorFlags &= MathMLOperatorDictionary::Fence | MathMLOperatorDictionary::Separator; else
m_operatorFlags = 0; m_leadingSpace = 5 * style().fontCascade().size() / 18; m_trailingSpace = 5 * style().fontCascade().size() / 18; m_minSize = style().fontCascade().size(); m_maxSize = intMaxForLayoutUnit;
if (m_textContent) {
if (const MathMLOperatorDictionary::Entry* entry = MathMLOperatorDictionary::getEntry(m_textContent, m_operatorForm))
setOperatorPropertiesFromOpDictEntry(entry);
else if (!explicitForm) {
if (const MathMLOperatorDictionary::Entry* entry = MathMLOperatorDictionary::getEntry(m_textContent)) {
m_operatorForm = static_cast<MathMLOperatorDictionary::Form>(entry->form); setOperatorPropertiesFromOpDictEntry(entry);
}
}
}
if (!isAnonymous()) {
setOperatorFlagFromAttribute(MathMLOperatorDictionary::Fence, MathMLNames::fenceAttr);
setOperatorFlagFromAttribute(MathMLOperatorDictionary::Separator, MathMLNames::separatorAttr);
setOperatorFlagFromAttribute(MathMLOperatorDictionary::Stretchy, MathMLNames::stretchyAttr);
setOperatorFlagFromAttribute(MathMLOperatorDictionary::Symmetric, MathMLNames::symmetricAttr);
setOperatorFlagFromAttribute(MathMLOperatorDictionary::LargeOp, MathMLNames::largeopAttr);
setOperatorFlagFromAttribute(MathMLOperatorDictionary::MovableLimits, MathMLNames::movablelimitsAttr);
setOperatorFlagFromAttribute(MathMLOperatorDictionary::Accent, MathMLNames::accentAttr);
parseMathMLLength(element().attributeWithoutSynchronization(MathMLNames::lspaceAttr), m_leadingSpace, &style(), false); parseMathMLLength(element().attributeWithoutSynchronization(MathMLNames::rspaceAttr), m_trailingSpace, &style(), false);
parseMathMLLength(element().attributeWithoutSynchronization(MathMLNames::minsizeAttr), m_minSize, &style(), false);
const AtomicString& maxsize = element().attributeWithoutSynchronization(MathMLNames::maxsizeAttr);
if (maxsize != "infinity")
parseMathMLLength(maxsize, m_maxSize, &style(), false);
}
}
void RenderMathMLOperator::stretchTo(LayoutUnit heightAboveBaseline, LayoutUnit depthBelowBaseline)
{
ASSERT(hasOperatorFlag(MathMLOperatorDictionary::Stretchy));
ASSERT(m_isVertical);
if (!m_isVertical || (heightAboveBaseline == m_stretchHeightAboveBaseline && depthBelowBaseline == m_stretchDepthBelowBaseline))
return;
m_stretchHeightAboveBaseline = heightAboveBaseline;
m_stretchDepthBelowBaseline = depthBelowBaseline;
setOperatorProperties();
if (hasOperatorFlag(MathMLOperatorDictionary::Symmetric)) {
LayoutUnit axis = mathAxisHeight();
LayoutUnit halfStretchSize = std::max(m_stretchHeightAboveBaseline - axis, m_stretchDepthBelowBaseline + axis);
m_stretchHeightAboveBaseline = halfStretchSize + axis;
m_stretchDepthBelowBaseline = halfStretchSize - axis;
}
LayoutUnit size = stretchSize();
float aspect = 1.0;
if (size > 0) {
if (size < m_minSize)
aspect = float(m_minSize) / size;
else if (m_maxSize < size)
aspect = float(m_maxSize) / size;
}
m_stretchHeightAboveBaseline *= aspect;
m_stretchDepthBelowBaseline *= aspect;
m_mathOperator.stretchTo(style(), m_stretchHeightAboveBaseline + m_stretchDepthBelowBaseline);
setLogicalHeight(m_mathOperator.ascent() + m_mathOperator.descent());
}
void RenderMathMLOperator::stretchTo(LayoutUnit width)
{
ASSERT(hasOperatorFlag(MathMLOperatorDictionary::Stretchy));
ASSERT(!m_isVertical);
if (m_isVertical || m_stretchWidth == width)
return;
m_stretchWidth = width;
m_mathOperator.stretchTo(style(), width);
setOperatorProperties();
setLogicalHeight(m_mathOperator.ascent() + m_mathOperator.descent());
}
void RenderMathMLOperator::resetStretchSize()
{
if (m_isVertical) {
m_stretchHeightAboveBaseline = 0;
m_stretchDepthBelowBaseline = 0;
} else
m_stretchWidth = 0;
}
void RenderMathMLOperator::computePreferredLogicalWidths()
{
ASSERT(preferredLogicalWidthsDirty());
LayoutUnit preferredWidth = 0;
setOperatorProperties();
if (!useMathOperator()) {
RenderMathMLToken::computePreferredLogicalWidths();
preferredWidth = m_maxPreferredLogicalWidth;
if (isInvisibleOperator()) {
GlyphData data = style().fontCascade().glyphDataForCharacter(m_textContent, false);
float glyphWidth = data.font ? data.font->widthForGlyph(data.glyph) : 0;
ASSERT(glyphWidth <= preferredWidth);
preferredWidth -= glyphWidth;
}
} else
preferredWidth = m_mathOperator.maxPreferredWidth();
preferredWidth = m_leadingSpace + preferredWidth + m_trailingSpace;
m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth = preferredWidth;
setPreferredLogicalWidthsDirty(false);
}
void RenderMathMLOperator::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeight)
{
ASSERT(needsLayout());
if (!relayoutChildren && simplifiedLayout())
return;
if (useMathOperator()) {
for (auto child = firstChildBox(); child; child = child->nextSiblingBox())
child->layoutIfNeeded();
setLogicalWidth(m_leadingSpace + m_mathOperator.width() + m_trailingSpace);
setLogicalHeight(m_mathOperator.ascent() + m_mathOperator.descent());
} else {
recomputeLogicalWidth();
LayoutUnit width = logicalWidth();
setLogicalWidth(width - m_leadingSpace - m_trailingSpace);
RenderMathMLToken::layoutBlock(relayoutChildren, pageLogicalHeight);
setLogicalWidth(width);
LayoutPoint horizontalShift(style().direction() == LTR ? m_leadingSpace : -m_leadingSpace, 0);
for (auto* child = firstChildBox(); child; child = child->nextSiblingBox())
child->setLocation(child->location() + horizontalShift);
}
clearNeedsLayout();
}
void RenderMathMLOperator::rebuildTokenContent(const String& operatorString)
{
AtomicString textContent = operatorString.stripWhiteSpace().simplifyWhiteSpace().replace(hyphenMinus, minusSign).impl();
m_textContent = textContent.length() == 1 ? textContent[0] : 0;
setOperatorProperties();
if (useMathOperator()) {
MathOperator::Type type;
if (!shouldAllowStretching())
type = MathOperator::Type::NormalOperator;
else if (isLargeOperatorInDisplayStyle())
type = MathOperator::Type::DisplayOperator;
else
type = m_isVertical ? MathOperator::Type::VerticalOperator : MathOperator::Type::HorizontalOperator;
m_mathOperator.setOperator(style(), m_textContent, type);
}
setNeedsLayoutAndPrefWidthsRecalc();
}
void RenderMathMLOperator::updateTokenContent(const String& operatorString)
{
ASSERT(isAnonymous());
rebuildTokenContent(operatorString);
}
void RenderMathMLOperator::updateTokenContent()
{
ASSERT(!isAnonymous());
RenderMathMLToken::updateTokenContent();
rebuildTokenContent(element().textContent());
}
void RenderMathMLOperator::updateFromElement()
{
updateTokenContent();
}
void RenderMathMLOperator::updateOperatorProperties()
{
setOperatorProperties();
setNeedsLayoutAndPrefWidthsRecalc();
}
bool RenderMathMLOperator::shouldAllowStretching() const
{
return m_textContent && (hasOperatorFlag(MathMLOperatorDictionary::Stretchy) || isLargeOperatorInDisplayStyle());
}
bool RenderMathMLOperator::useMathOperator() const
{
return shouldAllowStretching() || m_textContent == minusSign || isAnonymous();
}
void RenderMathMLOperator::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderMathMLBlock::styleDidChange(diff, oldStyle);
m_mathOperator.reset(style());
updateOperatorProperties();
}
LayoutUnit RenderMathMLOperator::verticalStretchedOperatorShift() const
{
if (!m_isVertical || !stretchSize())
return 0;
return (m_stretchDepthBelowBaseline - m_stretchHeightAboveBaseline - m_mathOperator.descent() + m_mathOperator.ascent()) / 2;
}
Optional<int> RenderMathMLOperator::firstLineBaseline() const
{
if (useMathOperator())
return Optional<int>(std::lround(static_cast<float>(m_mathOperator.ascent() - verticalStretchedOperatorShift())));
return RenderMathMLToken::firstLineBaseline();
}
void RenderMathMLOperator::paint(PaintInfo& info, const LayoutPoint& paintOffset)
{
RenderMathMLToken::paint(info, paintOffset);
if (!useMathOperator())
return;
LayoutPoint operatorTopLeft = paintOffset + location();
operatorTopLeft.move(style().isLeftToRightDirection() ? m_leadingSpace : m_trailingSpace, 0);
if (!m_isVertical)
operatorTopLeft.move(-(m_mathOperator.width() - width()) / 2, 0);
m_mathOperator.paint(style(), info, operatorTopLeft);
}
void RenderMathMLOperator::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
{
if (useMathOperator() || isInvisibleOperator())
return;
RenderMathMLToken::paintChildren(paintInfo, paintOffset, paintInfoForChild, usePrintRect);
}
}
#endif