blob: e349862b49c7c056508cb06193694252edce5fb1 [file] [log] [blame]
/*
* Copyright (C) 2009 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 "RenderMathMLUnderOver.h"
#include "FontSelector.h"
#include "MathMLNames.h"
namespace WebCore {
using namespace MathMLNames;
static const double gOverSpacingAdjustment = 0.5;
RenderMathMLUnderOver::RenderMathMLUnderOver(Node* expression)
: RenderMathMLBlock(expression)
{
Element* element = static_cast<Element*>(expression);
// Determine what kind of under/over expression we have by element name
if (element->hasLocalName(MathMLNames::munderTag))
m_kind = Under;
else if (element->hasLocalName(MathMLNames::moverTag))
m_kind = Over;
else if (element->hasLocalName(MathMLNames::munderoverTag))
m_kind = UnderOver;
else
m_kind = Under;
}
void RenderMathMLUnderOver::addChild(RenderObject* child, RenderObject* beforeChild)
{
RenderMathMLBlock* row = new (renderArena()) RenderMathMLBlock(node());
RefPtr<RenderStyle> rowStyle = makeBlockStyle();
row->setStyle(rowStyle.release());
// look through the children for rendered elements counting the blocks so we know what child
// we are adding
int blocks = 0;
RenderObject* current = this->firstChild();
while (current) {
blocks++;
current = current->nextSibling();
}
switch (blocks) {
case 0:
// this is the base so just append it
RenderBlock::addChild(row, beforeChild);
break;
case 1:
// the under or over
// FIXME: text-align: center does not work
row->style()->setTextAlign(CENTER);
if (m_kind == Over) {
// add the over as first
RenderBlock::addChild(row, firstChild());
} else {
// add the under as last
RenderBlock::addChild(row, beforeChild);
}
break;
case 2:
// the under or over
// FIXME: text-align: center does not work
row->style()->setTextAlign(CENTER);
if (m_kind == UnderOver) {
// add the over as first
RenderBlock::addChild(row, firstChild());
} else {
// we really shouldn't get here as only munderover should have three children
RenderBlock::addChild(row, beforeChild);
}
break;
default:
// munderover shouldn't have more than three children. In theory we shouldn't
// get here if the MathML is correctly formed, but that isn't a guarantee.
// We will treat this as another under element and they'll get something funky.
RenderBlock::addChild(row, beforeChild);
}
row->addChild(child);
}
inline int getOffsetHeight(RenderObject* obj)
{
if (obj->isBoxModelObject()) {
RenderBoxModelObject* box = toRenderBoxModelObject(obj);
return box->offsetHeight();
}
return 0;
}
void RenderMathMLUnderOver::stretchToHeight(int height)
{
RenderObject* base = firstChild();
if (!base)
return;
// For over or underover, the base is the sibling of the first child
if (m_kind != Under)
base = base->nextSibling();
if (!base)
return;
// use the child of the row which is the actual base
base = base->firstChild();
if (base && base->isRenderMathMLBlock()) {
RenderMathMLBlock* block = toRenderMathMLBlock(base);
block->stretchToHeight(height);
updateBoxModelInfoFromStyle();
setNeedsLayoutAndPrefWidthsRecalc();
markContainingBlocksForLayout();
}
}
void RenderMathMLUnderOver::layout()
{
RenderBlock::layout();
RenderObject* over = 0;
RenderObject* base = 0;
switch (m_kind) {
case Over:
// We need to calculate the baseline over the over versus the start of the base and
// adjust the placement of the base.
over = firstChild();
if (over) {
// FIXME: descending glyphs intrude into base (e.g. lowercase y over base)
// FIXME: bases that ascend higher than the line box intrude into the over
int overSpacing = static_cast<int>(gOverSpacingAdjustment * (getOffsetHeight(over) - over->firstChild()->baselinePosition(true)));
// base row wrapper
base = over->nextSibling();
if (base) {
if (overSpacing > 0)
base->style()->setMarginTop(Length(-overSpacing, Fixed));
else
base->style()->setMarginTop(Length(0, Fixed));
}
}
break;
case Under:
// FIXME: Non-ascending glyphs in the under should be moved closer to the base
// We need to calculate the baseline of the base versus the start of the under block and
// adjust the placement of the under block.
// base row wrapper
base = firstChild();
if (base) {
int baseHeight = getOffsetHeight(base);
// actual base
base = base->firstChild();
// FIXME: We need to look at the space between a single maximum height of
// the line boxes and the baseline and squeeze them together
int underSpacing = baseHeight - base->baselinePosition(true);
// adjust the base's intrusion into the under
RenderObject* under = lastChild();
if (under && underSpacing > 0)
under->style()->setMarginTop(Length(-underSpacing, Fixed));
}
break;
case UnderOver:
// FIXME: Non-descending glyphs in the over should be moved closer to the base
// FIXME: Non-ascending glyphs in the under should be moved closer to the base
// We need to calculate the baseline of the over versus the start of the base and
// adjust the placement of the base.
over = firstChild();
if (over) {
// FIXME: descending glyphs intrude into base (e.g. lowercase y over base)
// FIXME: bases that ascend higher than the line box intrude into the over
int overSpacing = static_cast<int>(gOverSpacingAdjustment * (getOffsetHeight(over) - over->firstChild()->baselinePosition(true)));
// base row wrapper
base = over->nextSibling();
if (base) {
if (overSpacing > 0)
base->style()->setMarginTop(Length(-overSpacing, Fixed));
// We need to calculate the baseline of the base versus the start of the under block and
// adjust the placement of the under block.
int baseHeight = getOffsetHeight(base);
// actual base
base = base->firstChild();
// FIXME: We need to look at the space between a single maximum height of
// the line boxes and the baseline and squeeze them together
int underSpacing = baseHeight - base->baselinePosition(true);
RenderObject* under = lastChild();
if (under && under->firstChild()->isRenderInline() && underSpacing > 0)
under->style()->setMarginTop(Length(-underSpacing, Fixed));
}
}
break;
}
setNeedsLayoutAndPrefWidthsRecalc();
RenderBlock::layout();
}
int RenderMathMLUnderOver::baselinePosition(bool, bool) const
{
int baseline = 0;
RenderObject* current = 0;
switch (m_kind) {
case UnderOver:
case Over:
current = firstChild();
baseline += getOffsetHeight(current);
current = current->nextSibling();
if (current) {
// actual base
RenderObject* base = current->firstChild();
baseline += base->baselinePosition(true);
// added the negative top margin
baseline += current->style()->marginTop().value();
}
break;
case Under:
current = firstChild();
if (current) {
RenderObject* base = current->firstChild();
baseline += base->baselinePosition(true);
}
}
return baseline;
}
int RenderMathMLUnderOver::nonOperatorHeight() const
{
return 0;
}
}
#endif // ENABLE(MATHML)