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
41 namespace WebCore {
42
43 using namespace MathMLNames;
44
45 static const int gTopAdjustDivisor = 3;
46 static const int gSubsupScriptMargin = 1;
47 static const float gSubSupStretch = 1.2f;
48
RenderMathMLSubSup(Element * element)49 RenderMathMLSubSup::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
addChild(RenderObject * child,RenderObject * beforeChild)64 void 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
stretchToHeight(int height)106 void RenderMathMLSubSup::stretchToHeight(int height)
107 {
108 RenderObject* base = firstChild();
109 if (!base || !base->firstChild())
110 return;
111
112 if (base->firstChild() && 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
nonOperatorHeight() const138 int 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
layout()145 void 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
baselinePosition(FontBaseline,bool firstLine,LineDirectionMode direction,LinePositionMode linePositionMode) const175 int 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 (!base)
189 break;
190
191 if (m_scripts && base->isBoxModelObject()) {
192 RenderBoxModelObject* box = toRenderBoxModelObject(base);
193
194 int topAdjust = (m_scripts->offsetHeight() - box->offsetHeight()) / 2;
195
196 // FIXME: The last bit of this calculation should be more exact. Why is the 2-3px scaled for zoom necessary?
197 // The baseline is top spacing of the base + the baseline of the base + adjusted space for zoom
198 float zoomFactor = style()->effectiveZoom();
199 return topAdjust + box->baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode) + static_cast<int>((zoomFactor > 1.25 ? 2 : 3) * zoomFactor);
200 }
201 break;
202 case Sup:
203 case Sub:
204 RenderBoxModelObject* box = toRenderBoxModelObject(base);
205 baseline = box->baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode);
206 break;
207 }
208
209 return baseline;
210
211 }
212
213 }
214
215 #endif // ENABLE(MATHML)
216