/* * 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 "FontSelector.h" #include "MathMLNames.h" #include "RenderInline.h" #include "RenderTable.h" #include "RenderTableCell.h" #include "RenderTableRow.h" #include "RenderTableSection.h" #include "RenderText.h" namespace WebCore { using namespace MathMLNames; static const int gTopAdjustDivisor = 3; static const int gSubsupScriptMargin = 1; static const float gSubSupStretch = 1.2; RenderMathMLSubSup::RenderMathMLSubSup(Element* element) : RenderMathMLBlock(element) , m_scripts(0) { // Determine what kind of under/over expression we have by element name if (element->hasLocalName(MathMLNames::msubTag)) m_kind = Sub; else if (element->hasLocalName(MathMLNames::msupTag)) m_kind = Sup; else if (element->hasLocalName(MathMLNames::msubsupTag)) m_kind = SubSup; else m_kind = SubSup; } void RenderMathMLSubSup::addChild(RenderObject* child, RenderObject* beforeChild) { if (firstChild()) { // We already have a base, so this is the super/subscripts being added. if (m_kind == SubSup) { if (!m_scripts) { m_scripts = new (renderArena()) RenderMathMLBlock(node()); RefPtr scriptsStyle = RenderStyle::create(); scriptsStyle->inheritFrom(style()); scriptsStyle->setDisplay(INLINE_BLOCK); scriptsStyle->setVerticalAlign(MIDDLE); scriptsStyle->setMarginLeft(Length(gSubsupScriptMargin, Fixed)); scriptsStyle->setTextAlign(LEFT); m_scripts->setStyle(scriptsStyle.release()); RenderMathMLBlock::addChild(m_scripts, beforeChild); } RenderBlock* script = new (renderArena()) RenderMathMLBlock(node()); RefPtr scriptStyle = RenderStyle::create(); scriptStyle->inheritFrom(m_scripts->style()); scriptStyle->setDisplay(BLOCK); script->setStyle(scriptStyle.release()); m_scripts->addChild(script, m_scripts->firstChild()); script->addChild(child); } else RenderMathMLBlock::addChild(child, beforeChild); } else { RenderMathMLBlock* wrapper = new (renderArena()) RenderMathMLBlock(node()); RefPtr wrapperStyle = RenderStyle::create(); wrapperStyle->inheritFrom(style()); wrapperStyle->setDisplay(INLINE_BLOCK); wrapperStyle->setVerticalAlign(MIDDLE); wrapper->setStyle(wrapperStyle.release()); RenderMathMLBlock::addChild(wrapper, beforeChild); wrapper->addChild(child); } } void RenderMathMLSubSup::stretchToHeight(int height) { RenderObject* base = firstChild(); if (!base) return; if (base->isRenderMathMLBlock()) { RenderMathMLBlock* block = toRenderMathMLBlock(base); block->stretchToHeight(static_cast(gSubSupStretch * height)); } if (height > 0 && m_kind == SubSup && m_scripts) { RenderObject* script = m_scripts->firstChild(); if (script) { // Calculate the script height without the container margins. RenderObject* top = script; int topHeight = getBoxModelObjectHeight(top->firstChild()); int topAdjust = topHeight / gTopAdjustDivisor; top->style()->setMarginTop(Length(-topAdjust, Fixed)); top->style()->setMarginBottom(Length(height - topHeight + topAdjust, Fixed)); if (top->isBoxModelObject()) { RenderBoxModelObject* topBox = toRenderBoxModelObject(top); topBox->updateBoxModelInfoFromStyle(); } m_scripts->setNeedsLayoutAndPrefWidthsRecalc(); m_scripts->markContainingBlocksForLayout(); } } updateBoxModelInfoFromStyle(); setNeedsLayoutAndPrefWidthsRecalc(); markContainingBlocksForLayout(); } int RenderMathMLSubSup::nonOperatorHeight() const { return 0; } void RenderMathMLSubSup::layout() { RenderBlock::layout(); if (m_kind == SubSup) { int width = 0; RenderObject* current = firstChild(); while (current) { width += getBoxModelObjectWidth(current); current = current->nextSibling(); } width++; // 1 + margin of scripts if (m_scripts) width += gSubsupScriptMargin; style()->setWidth(Length(width, Fixed)); setNeedsLayoutAndPrefWidthsRecalc(); markContainingBlocksForLayout(); RenderBlock::layout(); } } int RenderMathMLSubSup::baselinePosition(bool firstLine, bool isRootLineBox) const { RenderObject* base = firstChild(); if (!base) return offsetHeight(); base = base->firstChild(); if (!base) return offsetHeight(); int baseline = offsetHeight(); switch (m_kind) { case SubSup: if (m_scripts) { int topAdjust = 0; if (base->isBoxModelObject()) { RenderBoxModelObject* box = toRenderBoxModelObject(base); topAdjust = (m_scripts->offsetHeight() - box->offsetHeight()) / 2; } return topAdjust + (base ? base->baselinePosition(firstLine, isRootLineBox) : 0) + 4; } break; case Sup: if (base) { baseline = base->baselinePosition(firstLine, isRootLineBox) + 4; // FIXME: The extra amount of the superscript ascending above the base's box // isn't taken into account. This should be calculated in a more reliable // way. RenderObject* sup = base->nextSibling(); if (sup && sup->isBoxModelObject()) { RenderBoxModelObject* box = toRenderBoxModelObject(sup); // we'll take half of the sup's box height into account in the baseline baseline += static_cast(box->offsetHeight() * 0.5); } baseline++; } break; case Sub: if (base) baseline = base->baselinePosition(true) + 4; } return baseline; } } #endif // ENABLE(MATHML)