1/* 2 * Copyright (C) 2010 Alex Milowski (alex@milowski.com). All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 14 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 15 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 16 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 17 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 19 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27 28#if ENABLE(MATHML) 29 30#include "RenderMathMLSubSup.h" 31 32#include "FontSelector.h" 33#include "MathMLNames.h" 34#include "RenderInline.h" 35#include "RenderTable.h" 36#include "RenderTableCell.h" 37#include "RenderTableRow.h" 38#include "RenderTableSection.h" 39#include "RenderText.h" 40 41namespace WebCore { 42 43using namespace MathMLNames; 44 45static const int gTopAdjustDivisor = 3; 46static const int gSubsupScriptMargin = 1; 47static const float gSubSupStretch = 1.2f; 48 49RenderMathMLSubSup::RenderMathMLSubSup(Element* element) 50 : RenderMathMLBlock(element) 51 , m_scripts(0) 52{ 53 // Determine what kind of under/over expression we have by element name 54 if (element->hasLocalName(MathMLNames::msubTag)) 55 m_kind = Sub; 56 else if (element->hasLocalName(MathMLNames::msupTag)) 57 m_kind = Sup; 58 else if (element->hasLocalName(MathMLNames::msubsupTag)) 59 m_kind = SubSup; 60 else 61 m_kind = SubSup; 62} 63 64void RenderMathMLSubSup::addChild(RenderObject* child, RenderObject* beforeChild) 65{ 66 if (firstChild()) { 67 // We already have a base, so this is the super/subscripts being added. 68 69 if (m_kind == SubSup) { 70 if (!m_scripts) { 71 m_scripts = new (renderArena()) RenderMathMLBlock(node()); 72 RefPtr<RenderStyle> scriptsStyle = RenderStyle::create(); 73 scriptsStyle->inheritFrom(style()); 74 scriptsStyle->setDisplay(INLINE_BLOCK); 75 scriptsStyle->setVerticalAlign(TOP); 76 scriptsStyle->setMarginLeft(Length(gSubsupScriptMargin, Fixed)); 77 scriptsStyle->setTextAlign(LEFT); 78 m_scripts->setStyle(scriptsStyle.release()); 79 RenderMathMLBlock::addChild(m_scripts, beforeChild); 80 } 81 82 RenderBlock* script = new (renderArena()) RenderMathMLBlock(node()); 83 RefPtr<RenderStyle> scriptStyle = RenderStyle::create(); 84 scriptStyle->inheritFrom(m_scripts->style()); 85 scriptStyle->setDisplay(BLOCK); 86 script->setStyle(scriptStyle.release()); 87 88 m_scripts->addChild(script, m_scripts->firstChild()); 89 script->addChild(child); 90 } else 91 RenderMathMLBlock::addChild(child, beforeChild); 92 93 } else { 94 RenderMathMLBlock* wrapper = new (renderArena()) RenderMathMLBlock(node()); 95 RefPtr<RenderStyle> wrapperStyle = RenderStyle::create(); 96 wrapperStyle->inheritFrom(style()); 97 wrapperStyle->setDisplay(INLINE_BLOCK); 98 wrapperStyle->setVerticalAlign(BASELINE); 99 wrapper->setStyle(wrapperStyle.release()); 100 RenderMathMLBlock::addChild(wrapper, beforeChild); 101 wrapper->addChild(child); 102 103 } 104} 105 106void RenderMathMLSubSup::stretchToHeight(int height) 107{ 108 RenderObject* base = firstChild(); 109 if (!base) 110 return; 111 112 if (base->firstChild()->isRenderMathMLBlock()) { 113 RenderMathMLBlock* block = toRenderMathMLBlock(base->firstChild()); 114 block->stretchToHeight(static_cast<int>(gSubSupStretch * height)); 115 116 // Adjust the script placement after we stretch 117 if (height > 0 && m_kind == SubSup && m_scripts) { 118 RenderObject* script = m_scripts->firstChild(); 119 if (script) { 120 // Calculate the script height without the container margins. 121 RenderObject* top = script; 122 int topHeight = getBoxModelObjectHeight(top->firstChild()); 123 int topAdjust = topHeight / gTopAdjustDivisor; 124 top->style()->setMarginTop(Length(-topAdjust, Fixed)); 125 top->style()->setMarginBottom(Length(height - topHeight + topAdjust, Fixed)); 126 if (top->isBoxModelObject()) { 127 RenderBoxModelObject* topBox = toRenderBoxModelObject(top); 128 topBox->updateBoxModelInfoFromStyle(); 129 } 130 m_scripts->setNeedsLayout(true); 131 setNeedsLayout(true); 132 } 133 } 134 135 } 136} 137 138int RenderMathMLSubSup::nonOperatorHeight() const 139{ 140 if (m_kind == SubSup) 141 return static_cast<int>(style()->fontSize()*gSubSupStretch); 142 return static_cast<int>(style()->fontSize()); 143} 144 145void RenderMathMLSubSup::layout() 146{ 147 if (firstChild()) 148 firstChild()->setNeedsLayout(true); 149 if (m_scripts) 150 m_scripts->setNeedsLayout(true); 151 152 RenderBlock::layout(); 153 154 if (m_kind == SubSup) { 155 if (RenderObject* base = firstChild()) { 156 int maxHeight = 0; 157 RenderObject* current = base->firstChild(); 158 while (current) { 159 int height = getBoxModelObjectHeight(current); 160 if (height > maxHeight) 161 maxHeight = height; 162 current = current->nextSibling(); 163 } 164 int heightDiff = m_scripts ? (m_scripts->offsetHeight() - maxHeight) / 2 : 0; 165 if (heightDiff < 0) 166 heightDiff = 0; 167 base->style()->setPaddingTop(Length(heightDiff, Fixed)); 168 base->setNeedsLayout(true); 169 } 170 setNeedsLayout(true); 171 RenderBlock::layout(); 172 } 173} 174 175int RenderMathMLSubSup::baselinePosition(FontBaseline, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const 176{ 177 RenderObject* base = firstChild(); 178 if (!base) 179 return offsetHeight(); 180 181 int baseline = offsetHeight(); 182 if (!base || !base->isBoxModelObject()) 183 return baseline; 184 185 switch (m_kind) { 186 case SubSup: 187 base = base->firstChild(); 188 if (m_scripts && base->isBoxModelObject()) { 189 RenderBoxModelObject* box = toRenderBoxModelObject(base); 190 191 int topAdjust = (m_scripts->offsetHeight() - box->offsetHeight()) / 2; 192 193 // FIXME: The last bit of this calculation should be more exact. Why is the 2-3px scaled for zoom necessary? 194 // The baseline is top spacing of the base + the baseline of the base + adjusted space for zoom 195 float zoomFactor = style()->effectiveZoom(); 196 return topAdjust + box->baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode) + static_cast<int>((zoomFactor > 1.25 ? 2 : 3) * zoomFactor); 197 } 198 break; 199 case Sup: 200 case Sub: 201 RenderBoxModelObject* box = toRenderBoxModelObject(base); 202 baseline = box->baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode); 203 break; 204 } 205 206 return baseline; 207 208} 209 210} 211 212#endif // ENABLE(MATHML) 213