WebCore/mathml/RenderMathMLUnderOver.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/mathml/RenderMathMLUnderOver.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2009 Alex Milowski (alex@milowski.com). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(MATHML)
+
+#include "RenderMathMLUnderOver.h"
+
+#include "FontSelector.h"
+#include "MathMLNames.h"
+
+namespace WebCore {
+
+using namespace MathMLNames;
+    
+static const double gOverSpacingAdjustment = 0.5;
+    
+RenderMathMLUnderOver::RenderMathMLUnderOver(Node* expression) 
+    : RenderMathMLBlock(expression) 
+{
+    Element* element = static_cast<Element*>(expression);
+    // Determine what kind of under/over expression we have by element name
+    
+    if (element->hasLocalName(MathMLNames::munderTag))
+        m_kind = Under;
+    else if (element->hasLocalName(MathMLNames::moverTag))
+        m_kind = Over;
+    else if (element->hasLocalName(MathMLNames::munderoverTag))
+        m_kind = UnderOver;
+    else 
+        m_kind = Under;
+    
+}
+
+void RenderMathMLUnderOver::addChild(RenderObject* child, RenderObject* beforeChild)
+{    
+    RenderMathMLBlock* row = new (renderArena()) RenderMathMLBlock(node());
+    RefPtr<RenderStyle> rowStyle = makeBlockStyle();
+    row->setStyle(rowStyle.release());
+    
+    // look through the children for rendered elements counting the blocks so we know what child
+    // we are adding
+    int blocks = 0;
+    RenderObject* current = this->firstChild();
+    while (current) {
+        blocks++;
+        current = current->nextSibling();
+    }
+    
+    switch (blocks) {
+    case 0:
+        // this is the base so just append it
+        RenderBlock::addChild(row, beforeChild);
+        break;
+    case 1:
+        // the under or over
+        // FIXME: text-align: center does not work
+        row->style()->setTextAlign(CENTER);
+        if (m_kind == Over) {
+            // add the over as first
+            RenderBlock::addChild(row, firstChild());
+        } else {
+            // add the under as last
+            RenderBlock::addChild(row, beforeChild);
+        }
+        break;
+    case 2:
+        // the under or over
+        // FIXME: text-align: center does not work
+        row->style()->setTextAlign(CENTER);
+        if (m_kind == UnderOver) {
+            // add the over as first
+            RenderBlock::addChild(row, firstChild());
+        } else {
+            // we really shouldn't get here as only munderover should have three children
+            RenderBlock::addChild(row, beforeChild);
+        }
+        break;
+    default:
+        // munderover shouldn't have more than three children.  In theory we shouldn't 
+        // get here if the MathML is correctly formed, but that isn't a guarantee.
+        // We will treat this as another under element and they'll get something funky.
+        RenderBlock::addChild(row, beforeChild);
+    }
+    row->addChild(child);    
+}
+
+inline int getOffsetHeight(RenderObject* obj) 
+{
+    if (obj->isBoxModelObject()) {
+        RenderBoxModelObject* box = toRenderBoxModelObject(obj);
+        return box->offsetHeight();
+    }
+   
+    return 0;
+}
+
+void  RenderMathMLUnderOver::stretchToHeight(int height)
+{
+
+    RenderObject* base = firstChild();
+    if (!base)
+        return;
+        
+    // For over or underover, the base is the sibling of the first child
+    if (m_kind != Under) 
+        base = base->nextSibling();
+        
+    if (!base)
+        return;
+        
+    // use the child of the row which is the actual base
+    base = base->firstChild();
+    
+    if (base && base->isRenderMathMLBlock()) {
+        RenderMathMLBlock* block = toRenderMathMLBlock(base);
+        block->stretchToHeight(height);
+        updateBoxModelInfoFromStyle();
+        setNeedsLayoutAndPrefWidthsRecalc();
+        markContainingBlocksForLayout();
+    }
+}
+
+void RenderMathMLUnderOver::layout() 
+{
+    RenderBlock::layout();
+    RenderObject* over = 0;
+    RenderObject* base = 0;
+    switch (m_kind) {
+    case Over:
+        // We need to calculate the baseline over the over versus the start of the base and 
+        // adjust the placement of the base.
+        over = firstChild();
+        if (over) {
+            // FIXME: descending glyphs intrude into base (e.g. lowercase y over base)
+            // FIXME: bases that ascend higher than the line box intrude into the over
+            int overSpacing = static_cast<int>(gOverSpacingAdjustment * (getOffsetHeight(over) - over->firstChild()->baselinePosition(true)));
+            
+            // base row wrapper
+            base = over->nextSibling();
+            if (base) {
+                if (overSpacing > 0) 
+                    base->style()->setMarginTop(Length(-overSpacing, Fixed));
+                else 
+                    base->style()->setMarginTop(Length(0, Fixed));
+            }
+            
+        }
+        break;
+    case Under:
+        // FIXME: Non-ascending glyphs in the under should be moved closer to the base
+
+        // We need to calculate the baseline of the base versus the start of the under block and
+        // adjust the placement of the under block.
+        
+        // base row wrapper
+        base = firstChild();
+        if (base) {
+            int baseHeight = getOffsetHeight(base);
+            // actual base
+            base = base->firstChild();
+            // FIXME: We need to look at the space between a single maximum height of
+            //        the line boxes and the baseline and squeeze them together
+            int underSpacing = baseHeight - base->baselinePosition(true);
+            
+            // adjust the base's intrusion into the under
+            RenderObject* under = lastChild();
+            if (under && underSpacing > 0)
+                under->style()->setMarginTop(Length(-underSpacing, Fixed));
+        }
+        break;
+    case UnderOver:
+        // FIXME: Non-descending glyphs in the over should be moved closer to the base
+        // FIXME: Non-ascending glyphs in the under should be moved closer to the base
+        
+        // We need to calculate the baseline of the over versus the start of the base and 
+        // adjust the placement of the base.
+        
+        over = firstChild();
+        if (over) {
+            // FIXME: descending glyphs intrude into base (e.g. lowercase y over base)
+            // FIXME: bases that ascend higher than the line box intrude into the over
+            int overSpacing = static_cast<int>(gOverSpacingAdjustment * (getOffsetHeight(over) - over->firstChild()->baselinePosition(true)));
+            
+            // base row wrapper
+            base = over->nextSibling();
+            
+            if (base) {
+                if (overSpacing > 0)
+                    base->style()->setMarginTop(Length(-overSpacing, Fixed));
+                
+                // We need to calculate the baseline of the base versus the start of the under block and
+                // adjust the placement of the under block.
+                
+                int baseHeight = getOffsetHeight(base);
+                // actual base
+                base = base->firstChild();
+                // FIXME: We need to look at the space between a single maximum height of
+                //        the line boxes and the baseline and squeeze them together
+                int underSpacing = baseHeight - base->baselinePosition(true);
+                
+                RenderObject* under = lastChild();
+                if (under && under->firstChild()->isRenderInline() && underSpacing > 0)
+                    under->style()->setMarginTop(Length(-underSpacing, Fixed));
+                
+            }
+        }
+        break;
+    }
+    setNeedsLayoutAndPrefWidthsRecalc();
+    RenderBlock::layout();
+}
+
+int RenderMathMLUnderOver::baselinePosition(bool firstLine, bool isRootLineBox) const
+{
+    int baseline = 0;
+    RenderObject* current = 0;
+    switch (m_kind) {
+    case UnderOver:
+    case Over:
+        current = firstChild();
+        baseline += getOffsetHeight(current);
+        current = current->nextSibling();
+        if (current) {
+            // actual base
+            RenderObject* base = current->firstChild();
+            baseline += base->baselinePosition(firstLine, isRootLineBox);
+            // added the negative top margin
+            baseline += current->style()->marginTop().value();
+            // FIXME: Where is the extra 2-3px adjusted for zoom coming from?
+            float zoomFactor = style()->effectiveZoom();
+            baseline += static_cast<int>((zoomFactor > 1.25 ? 2 : 3) * zoomFactor);
+        }
+        break;
+    case Under:
+        current = firstChild();
+        if (current) {
+            RenderObject* base = current->firstChild();
+            baseline += base->baselinePosition(true);
+            // FIXME: Where is the extra 2-3px adjusted for zoom coming from?
+            float zoomFactor = style()->effectiveZoom();
+            baseline += static_cast<int>((zoomFactor > 1.25 ? 2 : 3) * zoomFactor);
+        }
+    }
+    return baseline;
+}
+
+
+int RenderMathMLUnderOver::nonOperatorHeight() const 
+{
+    return 0;
+}
+
+}
+
+
+#endif // ENABLE(MATHML)