Change baselinePosition and lineHeight to LayoutUnit
[WebKit-https.git] / Source / WebCore / rendering / mathml / RenderMathMLUnderOver.cpp
1 /*
2  * Copyright (C) 2009 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 "RenderMathMLUnderOver.h"
31
32 #include "FontSelector.h"
33 #include "MathMLNames.h"
34
35 namespace WebCore {
36
37 using namespace MathMLNames;
38     
39 static const double gOverSpacingAdjustment = 0.5;
40     
41 RenderMathMLUnderOver::RenderMathMLUnderOver(Node* expression) 
42     : RenderMathMLBlock(expression) 
43 {
44     Element* element = static_cast<Element*>(expression);
45     // Determine what kind of under/over expression we have by element name
46     
47     if (element->hasLocalName(MathMLNames::munderTag))
48         m_kind = Under;
49     else if (element->hasLocalName(MathMLNames::moverTag))
50         m_kind = Over;
51     else if (element->hasLocalName(MathMLNames::munderoverTag))
52         m_kind = UnderOver;
53     else 
54         m_kind = Under;
55     
56 }
57
58 void RenderMathMLUnderOver::addChild(RenderObject* child, RenderObject* beforeChild)
59 {    
60     RenderMathMLBlock* row = new (renderArena()) RenderMathMLBlock(node());
61     RefPtr<RenderStyle> rowStyle = makeBlockStyle();
62     row->setStyle(rowStyle.release());
63     row->setIsAnonymous(true);
64     
65     // look through the children for rendered elements counting the blocks so we know what child
66     // we are adding
67     int blocks = 0;
68     RenderObject* current = this->firstChild();
69     while (current) {
70         blocks++;
71         current = current->nextSibling();
72     }
73     
74     switch (blocks) {
75     case 0:
76         // this is the base so just append it
77         RenderBlock::addChild(row, beforeChild);
78         break;
79     case 1:
80         // the under or over
81         // FIXME: text-align: center does not work
82         row->style()->setTextAlign(CENTER);
83         if (m_kind == Over) {
84             // add the over as first
85             RenderBlock::addChild(row, firstChild());
86         } else {
87             // add the under as last
88             RenderBlock::addChild(row, beforeChild);
89         }
90         break;
91     case 2:
92         // the under or over
93         // FIXME: text-align: center does not work
94         row->style()->setTextAlign(CENTER);
95         if (m_kind == UnderOver) {
96             // add the over as first
97             RenderBlock::addChild(row, firstChild());
98         } else {
99             // we really shouldn't get here as only munderover should have three children
100             RenderBlock::addChild(row, beforeChild);
101         }
102         break;
103     default:
104         // munderover shouldn't have more than three children. In theory we shouldn't 
105         // get here if the MathML is correctly formed, but that isn't a guarantee.
106         // We will treat this as another under element and they'll get something funky.
107         RenderBlock::addChild(row, beforeChild);
108     }
109     row->addChild(child);    
110 }
111
112 inline int getOffsetHeight(RenderObject* obj) 
113 {
114     if (obj->isBoxModelObject()) {
115         RenderBoxModelObject* box = toRenderBoxModelObject(obj);
116         return box->offsetHeight();
117     }
118    
119     return 0;
120 }
121
122 void RenderMathMLUnderOver::stretchToHeight(int height)
123 {
124
125     RenderObject* base = firstChild();
126     if (!base)
127         return;
128         
129     // For over or underover, the base is the sibling of the first child
130     if (m_kind != Under) 
131         base = base->nextSibling();
132         
133     if (!base)
134         return;
135         
136     // use the child of the row which is the actual base
137     base = base->firstChild();
138     
139     if (base && base->isRenderMathMLBlock()) {
140         RenderMathMLBlock* block = toRenderMathMLBlock(base);
141         block->stretchToHeight(height);
142         setNeedsLayout(true);
143     }
144 }
145
146 void RenderMathMLUnderOver::layout() 
147 {
148     RenderBlock::layout();
149     RenderObject* over = 0;
150     RenderObject* base = 0;
151     switch (m_kind) {
152     case Over:
153         // We need to calculate the baseline over the over versus the start of the base and 
154         // adjust the placement of the base.
155         over = firstChild();
156         if (over) {
157             // FIXME: descending glyphs intrude into base (e.g. lowercase y over base)
158             // FIXME: bases that ascend higher than the line box intrude into the over
159             if (!over->firstChild() || !over->firstChild()->isBoxModelObject())
160                 break;
161             
162             LayoutUnit overSpacing = static_cast<LayoutUnit>(gOverSpacingAdjustment * (getOffsetHeight(over) - toRenderBoxModelObject(over->firstChild())->baselinePosition(AlphabeticBaseline, true, HorizontalLine)));
163             
164             // base row wrapper
165             base = over->nextSibling();
166             if (base) {
167                 if (overSpacing > 0) 
168                     base->style()->setMarginTop(Length(-overSpacing, Fixed));
169                 else 
170                     base->style()->setMarginTop(Length(0, Fixed));
171             }
172             
173         }
174         break;
175     case Under:
176         // FIXME: Non-ascending glyphs in the under should be moved closer to the base
177
178         // We need to calculate the baseline of the base versus the start of the under block and
179         // adjust the placement of the under block.
180         
181         // base row wrapper
182         base = firstChild();
183         if (base) {
184             LayoutUnit baseHeight = getOffsetHeight(base);
185             // actual base
186             base = base->firstChild();
187             if (!base || !base->isBoxModelObject())
188                 break;
189             
190             // FIXME: We need to look at the space between a single maximum height of
191             //        the line boxes and the baseline and squeeze them together
192             LayoutUnit underSpacing = baseHeight - toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, true, HorizontalLine);
193             
194             // adjust the base's intrusion into the under
195             RenderObject* under = lastChild();
196             if (under && underSpacing > 0)
197                 under->style()->setMarginTop(Length(-underSpacing, Fixed));
198         }
199         break;
200     case UnderOver:
201         // FIXME: Non-descending glyphs in the over should be moved closer to the base
202         // FIXME: Non-ascending glyphs in the under should be moved closer to the base
203         
204         // We need to calculate the baseline of the over versus the start of the base and 
205         // adjust the placement of the base.
206         
207         over = firstChild();
208         if (over) {
209             // FIXME: descending glyphs intrude into base (e.g. lowercase y over base)
210             // FIXME: bases that ascend higher than the line box intrude into the over
211             if (!over->firstChild() || !over->firstChild()->isBoxModelObject())
212                 break;
213             LayoutUnit overSpacing = static_cast<LayoutUnit>(gOverSpacingAdjustment * (getOffsetHeight(over) - toRenderBoxModelObject(over->firstChild())->baselinePosition(AlphabeticBaseline, true, HorizontalLine)));
214             
215             // base row wrapper
216             base = over->nextSibling();
217             
218             if (base) {
219                 if (overSpacing > 0)
220                     base->style()->setMarginTop(Length(-overSpacing, Fixed));
221                 
222                 // We need to calculate the baseline of the base versus the start of the under block and
223                 // adjust the placement of the under block.
224                 
225                 LayoutUnit baseHeight = getOffsetHeight(base);
226                 // actual base
227                 base = base->firstChild();
228                 if (!base || !base->isBoxModelObject())
229                     break;
230
231                 // FIXME: We need to look at the space between a single maximum height of
232                 //        the line boxes and the baseline and squeeze them together
233                 LayoutUnit underSpacing = baseHeight - toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, true, HorizontalLine);
234                 
235                 RenderObject* under = lastChild();
236                 if (under && under->firstChild() && under->firstChild()->isRenderInline() && underSpacing > 0)
237                     under->style()->setMarginTop(Length(-underSpacing, Fixed));
238                 
239             }
240         }
241         break;
242     }
243     setNeedsLayout(true);
244     RenderBlock::layout();
245 }
246
247 LayoutUnit RenderMathMLUnderOver::baselinePosition(FontBaseline, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
248 {
249     RenderObject* current = firstChild();
250     if (!current || linePositionMode == PositionOfInteriorLineBoxes)
251         return RenderBlock::baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode);
252
253     LayoutUnit baseline = 0;
254     switch (m_kind) {
255     case UnderOver:
256     case Over:
257         baseline += getOffsetHeight(current);
258         current = current->nextSibling();
259         if (current) {
260             // actual base
261             RenderObject* base = current->firstChild();
262             if (!base || !base->isBoxModelObject())
263                 break;
264             baseline += toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, firstLine, HorizontalLine, linePositionMode);
265             // added the negative top margin
266             baseline += current->style()->marginTop().value();
267         }
268         break;
269     case Under:
270         RenderObject* base = current->firstChild();
271         if (base && base->isBoxModelObject())
272             baseline += toRenderBoxModelObject(base)->baselinePosition(AlphabeticBaseline, true, HorizontalLine);
273     }
274
275     return baseline;
276 }
277
278
279 int RenderMathMLUnderOver::nonOperatorHeight() const 
280 {
281     int nonOperators = 0;
282     for (RenderObject* current = firstChild(); current; current = current->nextSibling()) {
283         if (current->firstChild() && current->firstChild()->isRenderMathMLBlock()) {
284             RenderMathMLBlock* block = toRenderMathMLBlock(current->firstChild());
285             if (!block->isRenderMathMLOperator()) 
286                 nonOperators += getOffsetHeight(current);
287         } else
288             nonOperators += getOffsetHeight(current);
289     }
290     return nonOperators;
291 }
292
293 }
294
295
296 #endif // ENABLE(MATHML)