WebCore/mathml/RenderMathMLOperator.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2010 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 "RenderMathMLOperator.h"
       
    31 
       
    32 #include "FontSelector.h"
       
    33 #include "MathMLNames.h"
       
    34 #include "RenderText.h"
       
    35 
       
    36 namespace WebCore {
       
    37     
       
    38 using namespace MathMLNames;
       
    39 
       
    40 RenderMathMLOperator::RenderMathMLOperator(Node* container)
       
    41     : RenderMathMLBlock(container),
       
    42       m_stretchHeight(0),
       
    43       m_operator(0)
       
    44 {
       
    45 }
       
    46     
       
    47 RenderMathMLOperator::RenderMathMLOperator(Node* container, UChar operatorChar)
       
    48     : RenderMathMLBlock(container),
       
    49       m_stretchHeight(0),
       
    50       m_operator(operatorChar)
       
    51 {
       
    52 }
       
    53     
       
    54 bool RenderMathMLOperator::isChildAllowed(RenderObject*, RenderStyle*) const
       
    55 {
       
    56     return false;
       
    57 }
       
    58 
       
    59 static const float gOperatorSpacer = 0.1;
       
    60 static const float gOperatorExpansion = 1.2;
       
    61 
       
    62 void  RenderMathMLOperator::stretchToHeight(int height)
       
    63 {
       
    64     if (height == m_stretchHeight)
       
    65         return;
       
    66     m_stretchHeight = static_cast<int>(height * gOperatorExpansion);
       
    67     
       
    68     updateBoxModelInfoFromStyle();
       
    69     setNeedsLayoutAndPrefWidthsRecalc();
       
    70     markContainingBlocksForLayout();
       
    71 }
       
    72 
       
    73 void RenderMathMLOperator::layout() 
       
    74 {
       
    75     // FIXME: This probably shouldn't be called here but when the operator
       
    76     // isn't stretched (e.g. outside of a mrow), it needs to be called somehow
       
    77     updateFromElement();
       
    78     RenderBlock::layout();
       
    79 }
       
    80 
       
    81 // This is a table of stretchy characters.
       
    82 // FIXME: Should this be read from the unicode characteristics somehow?
       
    83 // table:  stretchy operator, top char, extension char, bottom char, middle char
       
    84 static struct StretchyCharacter {
       
    85     UChar character;
       
    86     UChar topGlyph;
       
    87     UChar extensionGlyph;
       
    88     UChar bottomGlyph;
       
    89     UChar middleGlyph;
       
    90 } stretchyCharacters[9] = {
       
    91 { 0x28  , 0x239b, 0x239c, 0x239d, 0x0    }, // left parenthesis
       
    92 { 0x29  , 0x239e, 0x239f, 0x23a0, 0x0    }, // right parenthesis
       
    93 { 0x5b  , 0x23a1, 0x23a2, 0x23a3, 0x0    }, // left square bracket
       
    94 { 0x5d  , 0x23a4, 0x23a5, 0x23a6, 0x0    }, // right square bracket
       
    95 { 0x7b  , 0x23a7, 0x23aa, 0x23a9, 0x23a8 }, // left curly bracket
       
    96 { 0x7c  , 0x23d0, 0x23d0, 0x23d0, 0x0    }, // vertical bar
       
    97 { 0x7d  , 0x23ab, 0x23aa, 0x23ad, 0x23ac }, // right curly bracket
       
    98 { 0x222b, 0x2320, 0x23ae, 0x2321, 0x0    } // integral sign
       
    99 };
       
   100 
       
   101 // We stack glyphs using a 14px height with a displayed glyph height
       
   102 // of 10px.  The line height is set to less than the 14px so that there
       
   103 // are no blank spaces between the stacked glyphs.
       
   104 //
       
   105 // Certain glyphs (e.g. middle and bottom) need to be adjusted upwards
       
   106 // in the stack so that there isn't a gap.
       
   107 //
       
   108 // All of these settings are represented in the constants below.
       
   109 
       
   110 static const int gGlyphFontSize = 14;
       
   111 static const int gGlyphLineHeight = 12;
       
   112 static const int gMinimumStretchHeight = 24;
       
   113 static const int gGlyphHeight = 10;
       
   114 static const int gMiddleGlyphTopAdjust = -2;
       
   115 static const int gBottomGlyphTopAdjust = -4;
       
   116 static const float gMinimumRatioForStretch = 0.10;
       
   117 
       
   118 void RenderMathMLOperator::updateFromElement()
       
   119 {
       
   120     // clear our children
       
   121     while (firstChild()) {
       
   122        RenderObject* obj = firstChild();
       
   123        removeChild(obj);
       
   124     }
       
   125     
       
   126     // If the operator is fixed, it will be contained in m_operator
       
   127     UChar firstChar = m_operator;
       
   128     
       
   129     // This boolean indicates whether stretching is disabled via the markup.
       
   130     bool stretchDisabled = false;
       
   131     
       
   132     // We made need the element later if we can't stretch.
       
   133     if (node()->nodeType() == Node::ELEMENT_NODE) {
       
   134         if (Element* mo = static_cast<Element*>(node())) {
       
   135             AtomicString stretchyAttr = mo->getAttribute(MathMLNames::stretchyAttr);
       
   136             stretchDisabled = equalIgnoringCase(stretchyAttr, "false");
       
   137             
       
   138             // If stretching isn't disabled, get the character from the text content.
       
   139             if (!stretchDisabled && !firstChar) {
       
   140                 String opText = mo->textContent();
       
   141                 for (unsigned int i = 0; !firstChar && i < opText.length(); i++) {
       
   142                     if (!isSpaceOrNewline(opText[i]))
       
   143                        firstChar = opText[i];
       
   144                 }
       
   145             }
       
   146         }
       
   147     }
       
   148     
       
   149     // The 'index' holds the stretchable character's glyph information
       
   150     int index = -1;
       
   151     
       
   152     // isStretchy indicates whether the character is streatchable via a number of factors.
       
   153     bool isStretchy = false;
       
   154     
       
   155     // Check for a stretchable character.
       
   156     if (!stretchDisabled && firstChar) {
       
   157         const int maxIndex = sizeof(stretchyCharacters) / sizeof(stretchyCharacters[0]);
       
   158         for (index++; index < maxIndex; index++) {
       
   159             if (stretchyCharacters[index].character == firstChar) {
       
   160                 isStretchy = true;
       
   161                 break;
       
   162             }
       
   163         }
       
   164     }
       
   165     
       
   166     // We only stretch character if the stretch height is larger than a minimum size (e.g. 24px).
       
   167     bool shouldStretch = isStretchy && m_stretchHeight>gMinimumStretchHeight;
       
   168     m_isCentered = true;
       
   169     
       
   170     // Either stretch is disabled or we don't have a stretchable character over the minimum height
       
   171     if (stretchDisabled || !shouldStretch) {
       
   172         m_isStacked = false;
       
   173         RenderBlock* container = new (renderArena()) RenderMathMLBlock(node());
       
   174         
       
   175         RefPtr<RenderStyle> newStyle = RenderStyle::create();
       
   176         newStyle->inheritFrom(style());
       
   177         newStyle->setDisplay(INLINE_BLOCK);
       
   178 
       
   179         // Check for a stretchable character that is under the minimum height and use the
       
   180         // font size to adjust the glyph size.
       
   181         int currentFontSize = style()->fontSize();
       
   182         if (!stretchDisabled && isStretchy && m_stretchHeight > 0 && m_stretchHeight <= gMinimumStretchHeight  && m_stretchHeight > currentFontSize) {
       
   183             FontDescription* desc = new FontDescription();
       
   184             desc->setIsAbsoluteSize(true);
       
   185             desc->setSpecifiedSize(m_stretchHeight);
       
   186             desc->setComputedSize(m_stretchHeight);
       
   187             newStyle->setFontDescription(*desc);
       
   188             newStyle->font().update(newStyle->font().fontSelector());
       
   189             newStyle->setVerticalAlign(BASELINE);
       
   190             m_isCentered = false;
       
   191         } else {
       
   192             int topPad = (m_stretchHeight - currentFontSize) / 2;
       
   193             
       
   194             if (topPad / static_cast<float>(m_stretchHeight) > gMinimumRatioForStretch) {
       
   195                 newStyle->setVerticalAlign(TOP);
       
   196                 newStyle->setPaddingTop(Length(topPad, Fixed));
       
   197             } else {
       
   198                 m_isCentered = false;
       
   199                 newStyle->setVerticalAlign(BASELINE);
       
   200             }
       
   201         }
       
   202         
       
   203         container->setStyle(newStyle.release());
       
   204         addChild(container);
       
   205      
       
   206         // Build the text of the operator.  
       
   207         RenderText* text = 0;
       
   208         if (m_operator) 
       
   209             text = new (renderArena()) RenderText(node(), StringImpl::create(&m_operator, 1));
       
   210         else if (node()->nodeType() == Node::ELEMENT_NODE)
       
   211             if (Element* mo = static_cast<Element*>(node()))
       
   212                 text = new (renderArena()) RenderText(node(), StringImpl::create(mo->textContent().characters(), mo->textContent().length()));
       
   213         // If we can't figure out the text, leave it blank.
       
   214         if (text) {
       
   215             RefPtr<RenderStyle> textStyle = RenderStyle::create();
       
   216             textStyle->inheritFrom(container->style());
       
   217             text->setStyle(textStyle.release());
       
   218             container->addChild(text);
       
   219         }
       
   220     } else {
       
   221         // Build stretchable characters as a stack of glyphs.
       
   222         m_isStacked = true;
       
   223         
       
   224         if (stretchyCharacters[index].middleGlyph) {
       
   225             // We have a middle glyph (e.g. a curly bracket) that requires special processing.
       
   226             int half = (m_stretchHeight - gGlyphHeight) / 2;
       
   227             if (half <= gGlyphHeight) {
       
   228                 // We only have enough space for a single middle glyph.
       
   229                 createGlyph(stretchyCharacters[index].topGlyph, half);
       
   230                 createGlyph(stretchyCharacters[index].middleGlyph, gGlyphHeight, gMiddleGlyphTopAdjust);
       
   231                 createGlyph(stretchyCharacters[index].bottomGlyph, 0, gBottomGlyphTopAdjust);
       
   232             } else {
       
   233                 // We have to extend both the top and bottom to the middle.
       
   234                 createGlyph(stretchyCharacters[index].topGlyph, gGlyphHeight);
       
   235                 int remaining = half - gGlyphHeight;
       
   236                 while (remaining > 0) {
       
   237                     if (remaining < gGlyphHeight) {
       
   238                         createGlyph(stretchyCharacters[index].extensionGlyph, remaining);
       
   239                         remaining = 0;
       
   240                     } else {
       
   241                         createGlyph(stretchyCharacters[index].extensionGlyph, gGlyphHeight);
       
   242                         remaining -= gGlyphHeight;
       
   243                     }
       
   244                 }
       
   245                 
       
   246                 // The middle glyph in the stack.
       
   247                 createGlyph(stretchyCharacters[index].middleGlyph, gGlyphHeight, gMiddleGlyphTopAdjust);
       
   248                 
       
   249                 // The remaining is the top half minus the middle glyph height.
       
   250                 remaining = half - gGlyphHeight;
       
   251                 // We need to make sure we have the full height in case the height is odd.
       
   252                 if (m_stretchHeight % 2 == 1)
       
   253                     remaining++;
       
   254                 
       
   255                 // Extend to the bottom glyph.
       
   256                 while (remaining > 0) {
       
   257                     if (remaining < gGlyphHeight) {
       
   258                         createGlyph(stretchyCharacters[index].extensionGlyph, remaining);
       
   259                         remaining = 0;
       
   260                     } else {
       
   261                         createGlyph(stretchyCharacters[index].extensionGlyph, gGlyphHeight);
       
   262                         remaining -= gGlyphHeight;
       
   263                     }
       
   264                 }
       
   265                 
       
   266                 // The bottom glyph in the stack.
       
   267                 createGlyph(stretchyCharacters[index].bottomGlyph, 0, gBottomGlyphTopAdjust);
       
   268             }
       
   269         } else {
       
   270             // We do not have a middle glyph and so we just extend from the top to the bottom glyph.
       
   271             int remaining = m_stretchHeight - 2 * gGlyphHeight;
       
   272             createGlyph(stretchyCharacters[index].topGlyph, gGlyphHeight);
       
   273             while (remaining > 0) {
       
   274                 if (remaining < gGlyphHeight) {
       
   275                     createGlyph(stretchyCharacters[index].extensionGlyph, remaining);
       
   276                     remaining = 0;
       
   277                 } else {
       
   278                     createGlyph(stretchyCharacters[index].extensionGlyph, gGlyphHeight);
       
   279                     remaining -= gGlyphHeight;
       
   280                 }
       
   281             }
       
   282             createGlyph(stretchyCharacters[index].bottomGlyph, 0, gBottomGlyphTopAdjust);
       
   283         }
       
   284     }
       
   285 }
       
   286 
       
   287 RefPtr<RenderStyle> RenderMathMLOperator::createStackableStyle(int size, int topRelative)
       
   288 {
       
   289     RefPtr<RenderStyle> newStyle = RenderStyle::create();
       
   290     newStyle->inheritFrom(style());
       
   291     newStyle->setDisplay(BLOCK);
       
   292 
       
   293     FontDescription* desc = new FontDescription();
       
   294     desc->setIsAbsoluteSize(true);
       
   295     desc->setSpecifiedSize(gGlyphFontSize);
       
   296     desc->setComputedSize(gGlyphFontSize);
       
   297     newStyle->setFontDescription(*desc);
       
   298     newStyle->font().update(newStyle->font().fontSelector());
       
   299     newStyle->setLineHeight(Length(gGlyphLineHeight, Fixed));
       
   300     newStyle->setVerticalAlign(TOP);
       
   301     
       
   302     if (size > 0)
       
   303         newStyle->setMaxHeight(Length(size, Fixed));
       
   304         
       
   305     newStyle->setOverflowY(OHIDDEN);
       
   306     newStyle->setOverflowX(OHIDDEN);
       
   307     if (topRelative) {
       
   308         newStyle->setTop(Length(topRelative, Fixed));
       
   309         newStyle->setPosition(RelativePosition);
       
   310     }
       
   311     
       
   312     return newStyle;
       
   313 }
       
   314 
       
   315 RenderBlock* RenderMathMLOperator::createGlyph(UChar glyph, int size, int charRelative, int topRelative)
       
   316 {
       
   317     RenderBlock* container = new (renderArena()) RenderMathMLBlock(node());
       
   318     container->setStyle(createStackableStyle(size, topRelative).release());
       
   319     addChild(container);
       
   320     RenderBlock* parent = container;
       
   321     if (charRelative) {
       
   322         RenderBlock* charBlock = new (renderArena()) RenderBlock(node());
       
   323         RefPtr<RenderStyle> charStyle = RenderStyle::create();
       
   324         charStyle->inheritFrom(container->style());
       
   325         charStyle->setDisplay(INLINE_BLOCK);
       
   326         charStyle->setTop(Length(charRelative, Fixed));
       
   327         charStyle->setPosition(RelativePosition);
       
   328         charBlock->setStyle(charStyle);
       
   329         parent->addChild(charBlock);
       
   330         parent = charBlock;
       
   331     }
       
   332      
       
   333     RenderText* text = new (renderArena()) RenderText(node(), StringImpl::create(&glyph, 1));
       
   334     text->setStyle(container->style());
       
   335     parent->addChild(text);
       
   336     return container;
       
   337 }
       
   338 
       
   339 int RenderMathMLOperator::baselinePosition(bool firstLine, bool isRootLineBox) const
       
   340 {
       
   341     if (m_isStacked)
       
   342         return m_stretchHeight * 2 / 3 - (m_stretchHeight - static_cast<int>(m_stretchHeight / gOperatorExpansion)) / 2;
       
   343     if (m_isCentered && firstChild()) 
       
   344         return firstChild()->baselinePosition(firstLine, isRootLineBox);
       
   345     return RenderBlock::baselinePosition(firstLine, isRootLineBox);
       
   346 }
       
   347 
       
   348 }
       
   349 
       
   350 #endif