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