2 * Copyright (C) 2010 Alex Milowski (alex@milowski.com). All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #include "RenderMathMLSubSup.h"
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"
43 using namespace MathMLNames;
45 static const int gTopAdjustDivisor = 3;
46 static const int gSubsupScriptMargin = 1;
47 static const float gSubSupStretch = 1.2f;
49 RenderMathMLSubSup::RenderMathMLSubSup(Element* element)
50 : RenderMathMLBlock(element)
53 // Determine what kind of under/over expression we have by element name
54 if (element->hasLocalName(MathMLNames::msubTag))
56 else if (element->hasLocalName(MathMLNames::msupTag))
58 else if (element->hasLocalName(MathMLNames::msubsupTag))
64 void RenderMathMLSubSup::addChild(RenderObject* child, RenderObject* beforeChild)
67 // Note: The RenderMathMLBlock only allows element children to be added.
68 Element* childElement = toElement(child->node());
70 if (!childElement->previousElementSibling()) {
71 // Position 1 is always the base of the msub/msup/msubsup.
72 RenderMathMLBlock* wrapper = new (renderArena()) RenderMathMLBlock(node());
73 RefPtr<RenderStyle> wrapperStyle = RenderStyle::create();
74 wrapperStyle->inheritFrom(style());
75 wrapperStyle->setDisplay(INLINE_BLOCK);
76 wrapperStyle->setVerticalAlign(BASELINE);
77 wrapper->setStyle(wrapperStyle.release());
78 RenderMathMLBlock::addChild(wrapper, firstChild());
79 wrapper->addChild(child);
81 // Make sure we have a script block for rendering.
82 if (m_kind == SubSup && !m_scripts) {
83 m_scripts = new (renderArena()) RenderMathMLBlock(node());
84 RefPtr<RenderStyle> scriptsStyle = RenderStyle::create();
85 scriptsStyle->inheritFrom(style());
86 scriptsStyle->setDisplay(INLINE_BLOCK);
87 scriptsStyle->setVerticalAlign(TOP);
88 scriptsStyle->setMarginLeft(Length(gSubsupScriptMargin, Fixed));
89 scriptsStyle->setTextAlign(LEFT);
90 m_scripts->setStyle(scriptsStyle.release());
91 RenderMathMLBlock::addChild(m_scripts, beforeChild);
94 if (m_kind == SubSup) {
95 RenderBlock* script = new (renderArena()) RenderMathMLBlock(node());
96 RefPtr<RenderStyle> scriptStyle = RenderStyle::create();
97 scriptStyle->inheritFrom(m_scripts->style());
98 scriptStyle->setDisplay(BLOCK);
99 script->setStyle(scriptStyle.release());
101 // The order is always backwards so the first script is the subscript and the superscript
102 // is last. That means the superscript is the first to render vertically.
103 Element* previousSibling = childElement->previousElementSibling();
104 if (previousSibling && !previousSibling->previousElementSibling())
105 m_scripts->addChild(script);
107 m_scripts->addChild(script, m_scripts->firstChild());
109 script->addChild(child);
111 RenderMathMLBlock::addChild(child, beforeChild);
115 void RenderMathMLSubSup::stretchToHeight(int height)
117 RenderObject* base = firstChild();
118 if (!base || !base->firstChild())
121 if (base->firstChild() && base->firstChild()->isRenderMathMLBlock()) {
122 RenderMathMLBlock* block = toRenderMathMLBlock(base->firstChild());
123 block->stretchToHeight(static_cast<int>(gSubSupStretch * height));
125 // Adjust the script placement after we stretch
126 if (height > 0 && m_kind == SubSup && m_scripts) {
127 RenderObject* script = m_scripts->firstChild();
129 // Calculate the script height without the container margins.
130 RenderObject* top = script;
131 int topHeight = getBoxModelObjectHeight(top->firstChild());
132 int topAdjust = topHeight / gTopAdjustDivisor;
133 top->style()->setMarginTop(Length(-topAdjust, Fixed));
134 top->style()->setMarginBottom(Length(height - topHeight + topAdjust, Fixed));
135 if (top->isBoxModelObject()) {
136 RenderBoxModelObject* topBox = toRenderBoxModelObject(top);
137 topBox->updateBoxModelInfoFromStyle();
139 m_scripts->setNeedsLayout(true);
140 setNeedsLayout(true);
147 int RenderMathMLSubSup::nonOperatorHeight() const
149 if (m_kind == SubSup)
150 return static_cast<int>(style()->fontSize()*gSubSupStretch);
151 return static_cast<int>(style()->fontSize());
154 void RenderMathMLSubSup::layout()
157 firstChild()->setNeedsLayout(true);
159 m_scripts->setNeedsLayout(true);
161 RenderBlock::layout();
163 if (m_kind == SubSup) {
164 if (RenderObject* base = firstChild()) {
165 LayoutUnit maxHeight = 0;
166 RenderObject* current = base->firstChild();
168 LayoutUnit height = getBoxModelObjectHeight(current);
169 if (height > maxHeight)
171 current = current->nextSibling();
173 LayoutUnit heightDiff = m_scripts ? (m_scripts->offsetHeight() - maxHeight) / 2 : 0;
176 base->style()->setPaddingTop(Length(heightDiff, Fixed));
177 base->setNeedsLayout(true);
179 setNeedsLayout(true);
180 RenderBlock::layout();
184 int RenderMathMLSubSup::baselinePosition(FontBaseline, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
186 RenderObject* base = firstChild();
188 return offsetHeight();
190 int baseline = offsetHeight();
191 if (!base || !base->isBoxModelObject())
196 base = base->firstChild();
197 if (m_scripts && base && base->isBoxModelObject()) {
198 RenderBoxModelObject* box = toRenderBoxModelObject(base);
200 int topAdjust = (m_scripts->offsetHeight() - box->offsetHeight()) / 2;
202 // FIXME: The last bit of this calculation should be more exact. Why is the 2-3px scaled for zoom necessary?
203 // The baseline is top spacing of the base + the baseline of the base + adjusted space for zoom
204 float zoomFactor = style()->effectiveZoom();
205 return topAdjust + box->baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode) + static_cast<int>((zoomFactor > 1.25 ? 2 : 3) * zoomFactor);
210 RenderBoxModelObject* box = toRenderBoxModelObject(base);
211 baseline = box->baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode);
221 #endif // ENABLE(MATHML)