Home | History | Annotate | Download | only in mathml
      1 /*
      2  * Copyright (C) 2009 Alex Milowski (alex (at) 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 
     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 
    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 
    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 
    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 
    246 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 
    281 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