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