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 35namespace WebCore { 36 37using namespace MathMLNames; 38 39static const double gOverSpacingAdjustment = 0.5; 40 41RenderMathMLUnderOver::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 58void 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 111inline 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 121void 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 145void 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 246int 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 281int 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