Home | History | Annotate | Download | only in mathml
      1 /*
      2  * Copyright (C) 2010 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 "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     if (firstChild()) {
     67         // We already have a base, so this is the super/subscripts being added.
     68 
     69         if (m_kind == SubSup) {
     70             if (!m_scripts) {
     71                 m_scripts = new (renderArena()) RenderMathMLBlock(node());
     72                 RefPtr<RenderStyle> scriptsStyle = RenderStyle::create();
     73                 scriptsStyle->inheritFrom(style());
     74                 scriptsStyle->setDisplay(INLINE_BLOCK);
     75                 scriptsStyle->setVerticalAlign(TOP);
     76                 scriptsStyle->setMarginLeft(Length(gSubsupScriptMargin, Fixed));
     77                 scriptsStyle->setTextAlign(LEFT);
     78                 m_scripts->setStyle(scriptsStyle.release());
     79                 RenderMathMLBlock::addChild(m_scripts, beforeChild);
     80             }
     81 
     82             RenderBlock* script = new (renderArena()) RenderMathMLBlock(node());
     83             RefPtr<RenderStyle> scriptStyle = RenderStyle::create();
     84             scriptStyle->inheritFrom(m_scripts->style());
     85             scriptStyle->setDisplay(BLOCK);
     86             script->setStyle(scriptStyle.release());
     87 
     88             m_scripts->addChild(script, m_scripts->firstChild());
     89             script->addChild(child);
     90         } else
     91             RenderMathMLBlock::addChild(child, beforeChild);
     92 
     93     } else {
     94         RenderMathMLBlock* wrapper = new (renderArena()) RenderMathMLBlock(node());
     95         RefPtr<RenderStyle> wrapperStyle = RenderStyle::create();
     96         wrapperStyle->inheritFrom(style());
     97         wrapperStyle->setDisplay(INLINE_BLOCK);
     98         wrapperStyle->setVerticalAlign(BASELINE);
     99         wrapper->setStyle(wrapperStyle.release());
    100         RenderMathMLBlock::addChild(wrapper, beforeChild);
    101         wrapper->addChild(child);
    102 
    103     }
    104 }
    105 
    106 void RenderMathMLSubSup::stretchToHeight(int height)
    107 {
    108     RenderObject* base = firstChild();
    109     if (!base)
    110         return;
    111 
    112     if (base->firstChild()->isRenderMathMLBlock()) {
    113         RenderMathMLBlock* block = toRenderMathMLBlock(base->firstChild());
    114         block->stretchToHeight(static_cast<int>(gSubSupStretch * height));
    115 
    116         // Adjust the script placement after we stretch
    117         if (height > 0 && m_kind == SubSup && m_scripts) {
    118             RenderObject* script = m_scripts->firstChild();
    119             if (script) {
    120                 // Calculate the script height without the container margins.
    121                 RenderObject* top = script;
    122                 int topHeight = getBoxModelObjectHeight(top->firstChild());
    123                 int topAdjust = topHeight / gTopAdjustDivisor;
    124                 top->style()->setMarginTop(Length(-topAdjust, Fixed));
    125                 top->style()->setMarginBottom(Length(height - topHeight + topAdjust, Fixed));
    126                 if (top->isBoxModelObject()) {
    127                     RenderBoxModelObject* topBox = toRenderBoxModelObject(top);
    128                     topBox->updateBoxModelInfoFromStyle();
    129                 }
    130                 m_scripts->setNeedsLayout(true);
    131                 setNeedsLayout(true);
    132             }
    133         }
    134 
    135     }
    136 }
    137 
    138 int RenderMathMLSubSup::nonOperatorHeight() const
    139 {
    140     if (m_kind == SubSup)
    141        return static_cast<int>(style()->fontSize()*gSubSupStretch);
    142     return static_cast<int>(style()->fontSize());
    143 }
    144 
    145 void RenderMathMLSubSup::layout()
    146 {
    147     if (firstChild())
    148         firstChild()->setNeedsLayout(true);
    149     if (m_scripts)
    150         m_scripts->setNeedsLayout(true);
    151 
    152     RenderBlock::layout();
    153 
    154     if (m_kind == SubSup) {
    155         if (RenderObject* base = firstChild()) {
    156             int maxHeight = 0;
    157             RenderObject* current = base->firstChild();
    158             while (current) {
    159                 int height = getBoxModelObjectHeight(current);
    160                 if (height > maxHeight)
    161                     maxHeight = height;
    162                 current = current->nextSibling();
    163             }
    164             int heightDiff = m_scripts ? (m_scripts->offsetHeight() - maxHeight) / 2 : 0;
    165             if (heightDiff < 0)
    166                 heightDiff = 0;
    167             base->style()->setPaddingTop(Length(heightDiff, Fixed));
    168             base->setNeedsLayout(true);
    169         }
    170         setNeedsLayout(true);
    171         RenderBlock::layout();
    172     }
    173 }
    174 
    175 int RenderMathMLSubSup::baselinePosition(FontBaseline, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
    176 {
    177     RenderObject* base = firstChild();
    178     if (!base)
    179         return offsetHeight();
    180 
    181     int baseline = offsetHeight();
    182     if (!base || !base->isBoxModelObject())
    183         return baseline;
    184 
    185     switch (m_kind) {
    186     case SubSup:
    187         base = base->firstChild();
    188         if (m_scripts && base->isBoxModelObject()) {
    189             RenderBoxModelObject* box = toRenderBoxModelObject(base);
    190 
    191             int topAdjust = (m_scripts->offsetHeight() - box->offsetHeight()) / 2;
    192 
    193             // FIXME: The last bit of this calculation should be more exact.  Why is the 2-3px scaled for zoom necessary?
    194             // The baseline is top spacing of the base + the baseline of the base + adjusted space for zoom
    195             float zoomFactor = style()->effectiveZoom();
    196             return topAdjust + box->baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode) + static_cast<int>((zoomFactor > 1.25 ? 2 : 3) * zoomFactor);
    197         }
    198         break;
    199     case Sup:
    200     case Sub:
    201         RenderBoxModelObject* box = toRenderBoxModelObject(base);
    202         baseline = box->baselinePosition(AlphabeticBaseline, firstLine, direction, linePositionMode);
    203         break;
    204     }
    205 
    206     return baseline;
    207 
    208 }
    209 
    210 }
    211 
    212 #endif // ENABLE(MATHML)
    213