/* | |
* 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) |