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