initial import
[vuplus_webkit] / Source / WebCore / rendering / mathml / RenderMathMLSubSup.cpp
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
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
64 void RenderMathMLSubSup::addChild(RenderObject* child, RenderObject* beforeChild)
65 {
66     
67     // Note: The RenderMathMLBlock only allows element children to be added.
68     Element* childElement = toElement(child->node());
69
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);
80             
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);
92         }
93     } else {
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());
100
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);
106             else                 
107                 m_scripts->addChild(script, m_scripts->firstChild());
108             
109             script->addChild(child);
110         } else
111             RenderMathMLBlock::addChild(child, beforeChild);
112     }
113 }
114
115 void RenderMathMLSubSup::stretchToHeight(int height)
116 {
117     RenderObject* base = firstChild();
118     if (!base || !base->firstChild())
119         return;
120     
121     if (base->firstChild() && base->firstChild()->isRenderMathMLBlock()) {
122         RenderMathMLBlock* block = toRenderMathMLBlock(base->firstChild());
123         block->stretchToHeight(static_cast<int>(gSubSupStretch * height));
124         
125         // Adjust the script placement after we stretch
126         if (height > 0 && m_kind == SubSup && m_scripts) {
127             RenderObject* script = m_scripts->firstChild();
128             if (script) {
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();
138                 }
139                 m_scripts->setNeedsLayout(true);
140                 setNeedsLayout(true);
141             }
142         }
143         
144     }
145 }
146
147 int RenderMathMLSubSup::nonOperatorHeight() const 
148 {
149     if (m_kind == SubSup) 
150        return static_cast<int>(style()->fontSize()*gSubSupStretch);
151     return static_cast<int>(style()->fontSize());
152 }
153
154 void RenderMathMLSubSup::layout() 
155 {
156     if (firstChild())
157         firstChild()->setNeedsLayout(true);
158     if (m_scripts) 
159         m_scripts->setNeedsLayout(true);
160     
161     RenderBlock::layout();
162     
163     if (m_kind == SubSup) {
164         if (RenderObject* base = firstChild()) {
165             LayoutUnit maxHeight = 0;
166             RenderObject* current = base->firstChild();
167             while (current) {
168                 LayoutUnit height = getBoxModelObjectHeight(current);
169                 if (height > maxHeight)
170                     maxHeight = height;
171                 current = current->nextSibling();
172             }
173             LayoutUnit heightDiff = m_scripts ? (m_scripts->offsetHeight() - maxHeight) / 2 : 0;
174             if (heightDiff < 0) 
175                 heightDiff = 0;
176             base->style()->setPaddingTop(Length(heightDiff, Fixed));
177             base->setNeedsLayout(true);
178         }
179         setNeedsLayout(true);
180         RenderBlock::layout();
181     }    
182 }
183
184 int RenderMathMLSubSup::baselinePosition(FontBaseline, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
185 {
186     RenderObject* base = firstChild();
187     if (!base) 
188         return offsetHeight();
189     
190     int baseline = offsetHeight();
191     if (!base || !base->isBoxModelObject()) 
192         return baseline;
193
194     switch (m_kind) {
195     case SubSup:
196         base = base->firstChild();
197         if (m_scripts && base && base->isBoxModelObject()) {
198             RenderBoxModelObject* box = toRenderBoxModelObject(base);
199             
200             int topAdjust = (m_scripts->offsetHeight() - box->offsetHeight()) / 2;
201         
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);
206         }
207         break;
208     case Sup: 
209     case Sub:
210         RenderBoxModelObject* box = toRenderBoxModelObject(base);
211         baseline = box->baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode);
212         break;
213     }
214     
215     return baseline;
216     
217 }
218     
219 }    
220
221 #endif // ENABLE(MATHML)