blob: 45642be525bba8cc70a5028d8c1c16cf60888c45 [file] [log] [blame]
/*
* Copyright (C) 2010 Alex Milowski (alex@milowski.com). All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#if ENABLE(MATHML)
#include "RenderMathMLSubSup.h"
#include "MathMLNames.h"
namespace WebCore {
using namespace MathMLNames;
RenderMathMLSubSup::RenderMathMLSubSup(Element* element)
: RenderMathMLBlock(element)
, m_scripts(0)
{
// Determine what kind of sub/sup expression we have by element name
if (element->hasLocalName(MathMLNames::msubTag))
m_kind = Sub;
else if (element->hasLocalName(MathMLNames::msupTag))
m_kind = Super;
else {
ASSERT(element->hasLocalName(MathMLNames::msubsupTag));
m_kind = SubSup;
}
}
RenderBoxModelObject* RenderMathMLSubSup::base() const
{
RenderObject* baseWrapper = firstChild();
if (!baseWrapper)
return 0;
RenderObject* base = baseWrapper->firstChild();
if (!base || !base->isBoxModelObject())
return 0;
return toRenderBoxModelObject(base);
}
void RenderMathMLSubSup::fixScriptsStyle()
{
ASSERT(m_scripts && m_scripts->style()->refCount() == 1);
RenderStyle* scriptsStyle = m_scripts->style();
scriptsStyle->setFlexDirection(FlowColumn);
scriptsStyle->setJustifyContent(m_kind == Sub ? JustifyFlexEnd : m_kind == Super ? JustifyFlexStart : JustifySpaceBetween);
// Set this wrapper's font-size for its line-height & baseline position, for its children.
scriptsStyle->setFontSize(static_cast<int>(0.75 * style()->fontSize()));
}
// FIXME: Handle arbitrary addChild/removeChild correctly throughout MathML, e.g. add/remove/add a base child here.
void RenderMathMLSubSup::addChild(RenderObject* child, RenderObject* beforeChild)
{
// Note: The RenderMathMLBlock only allows element children to be added.
Element* childElement = toElement(child->node());
if (childElement && !childElement->previousElementSibling()) {
// Position 1 is always the base of the msub/msup/msubsup.
RenderMathMLBlock* baseWrapper = createAnonymousMathMLBlock();
RenderMathMLBlock::addChild(baseWrapper, firstChild());
baseWrapper->addChild(child);
// Make sure we have a script block for rendering.
if (!m_scripts) {
m_scripts = createAnonymousMathMLBlock();
fixScriptsStyle();
RenderMathMLBlock::addChild(m_scripts, beforeChild);
}
} else
m_scripts->addChild(child, beforeChild ? m_scripts->firstChild() : 0);
}
void RenderMathMLSubSup::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
RenderMathMLBlock::styleDidChange(diff, oldStyle);
if (m_scripts)
fixScriptsStyle();
}
RenderMathMLOperator* RenderMathMLSubSup::unembellishedOperator()
{
RenderBoxModelObject* base = this->base();
if (!base || !base->isRenderMathMLBlock())
return 0;
return toRenderMathMLBlock(base)->unembellishedOperator();
}
void RenderMathMLSubSup::layout()
{
RenderMathMLBlock::layout();
RenderMathMLBlock* baseWrapper = toRenderMathMLBlock(firstChild());
if (!baseWrapper || !m_scripts)
return;
RenderBox* base = baseWrapper->firstChildBox();
if (!base)
return;
// Our layout rules include: Don't let the superscript go below the "axis" (half x-height above the
// baseline), or the subscript above the axis. Also, don't let the superscript's top edge be
// below the base's top edge, or the subscript's bottom edge above the base's bottom edge.
//
// FIXME: Check any subscriptshift or superscriptshift attributes, and maybe use more sophisticated
// heuristics from TeX or elsewhere. See https://bugs.webkit.org/show_bug.cgi?id=79274#c5.
LayoutUnit baseHeight = base->logicalHeight();
LayoutUnit baseBaseline = base->firstLineBoxBaseline();
if (baseBaseline == -1)
baseBaseline = baseHeight;
LayoutUnit axis = style()->fontMetrics().xHeight() / 2;
int fontSize = style()->fontSize();
if (RenderBox* superscript = m_kind == Sub ? 0 : m_scripts->lastChildBox()) {
LayoutUnit superscriptHeight = superscript->logicalHeight();
LayoutUnit superscriptBaseline = superscript->firstLineBoxBaseline();
if (superscriptBaseline == -1)
superscriptBaseline = superscriptHeight;
LayoutUnit minBaseline = max<LayoutUnit>(fontSize / 3 + 1 + superscriptBaseline, superscriptHeight + axis);
baseWrapper->style()->setPaddingTop(Length(max<LayoutUnit>(minBaseline - baseBaseline, 0), Fixed));
}
if (RenderBox* subscript = m_kind == Super ? 0 : m_scripts->firstChildBox()) {
LayoutUnit subscriptHeight = subscript->logicalHeight();
LayoutUnit subscriptBaseline = subscript->firstLineBoxBaseline();
if (subscriptBaseline == -1)
subscriptBaseline = subscriptHeight;
LayoutUnit baseExtendUnderBaseline = baseHeight - baseBaseline;
LayoutUnit subscriptUnderItsBaseline = subscriptHeight - subscriptBaseline;
LayoutUnit minExtendUnderBaseline = max<LayoutUnit>(fontSize / 5 + 1 + subscriptUnderItsBaseline, subscriptHeight - axis);
baseWrapper->style()->setPaddingBottom(Length(max<LayoutUnit>(minExtendUnderBaseline - baseExtendUnderBaseline, 0), Fixed));
}
setChildNeedsLayout(true, MarkOnlyThis);
baseWrapper->setNeedsLayout(true, MarkOnlyThis);
RenderMathMLBlock::layout();
}
}
#endif // ENABLE(MATHML)