diff options
author | Steve Block <steveblock@google.com> | 2011-05-13 06:44:40 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-05-13 06:44:40 -0700 |
commit | 08014c20784f3db5df3a89b73cce46037b77eb59 (patch) | |
tree | 47749210d31e19e6e2f64036fa8fae2ad693476f /Source/WebCore/mathml/RenderMathMLOperator.cpp | |
parent | 860220379e56aeb66424861ad602b07ee22b4055 (diff) | |
parent | 4c3661f7918f8b3f139f824efb7855bedccb4c94 (diff) | |
download | external_webkit-08014c20784f3db5df3a89b73cce46037b77eb59.zip external_webkit-08014c20784f3db5df3a89b73cce46037b77eb59.tar.gz external_webkit-08014c20784f3db5df3a89b73cce46037b77eb59.tar.bz2 |
Merge changes Ide388898,Ic49f367c,I1158a808,Iacb6ca5d,I2100dd3a,I5c1abe54,Ib0ef9902,I31dbc523,I570314b3
* changes:
Merge WebKit at r75315: Update WebKit version
Merge WebKit at r75315: Add FrameLoaderClient PageCache stubs
Merge WebKit at r75315: Stub out AXObjectCache::remove()
Merge WebKit at r75315: Fix ImageBuffer
Merge WebKit at r75315: Fix PluginData::initPlugins()
Merge WebKit at r75315: Fix conflicts
Merge WebKit at r75315: Fix Makefiles
Merge WebKit at r75315: Move Android-specific WebCore files to Source
Merge WebKit at r75315: Initial merge by git.
Diffstat (limited to 'Source/WebCore/mathml/RenderMathMLOperator.cpp')
-rw-r--r-- | Source/WebCore/mathml/RenderMathMLOperator.cpp | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/Source/WebCore/mathml/RenderMathMLOperator.cpp b/Source/WebCore/mathml/RenderMathMLOperator.cpp new file mode 100644 index 0000000..7f039b9 --- /dev/null +++ b/Source/WebCore/mathml/RenderMathMLOperator.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2010 Alex Milowski (alex@milowski.com). All rights reserved. + * Copyright (C) 2010 François Sausset (sausset@gmail.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 "RenderMathMLOperator.h" + +#include "FontSelector.h" +#include "MathMLNames.h" +#include "RenderText.h" + +namespace WebCore { + +using namespace MathMLNames; + +RenderMathMLOperator::RenderMathMLOperator(Node* container) + : RenderMathMLBlock(container) + , m_stretchHeight(0) + , m_operator(0) +{ +} + +RenderMathMLOperator::RenderMathMLOperator(Node* container, UChar operatorChar) + : RenderMathMLBlock(container) + , m_stretchHeight(0) + , m_operator(convertHyphenMinusToMinusSign(operatorChar)) +{ +} + +bool RenderMathMLOperator::isChildAllowed(RenderObject*, RenderStyle*) const +{ + return false; +} + +static const float gOperatorSpacer = 0.1f; +static const float gOperatorExpansion = 1.2f; + +void RenderMathMLOperator::stretchToHeight(int height) +{ + if (height == m_stretchHeight) + return; + m_stretchHeight = static_cast<int>(height * gOperatorExpansion); + + updateBoxModelInfoFromStyle(); + setNeedsLayout(true); +} + +void RenderMathMLOperator::layout() +{ + // FIXME: This probably shouldn't be called here but when the operator + // isn't stretched (e.g. outside of a mrow), it needs to be called somehow + updateFromElement(); + RenderBlock::layout(); +} + +// This is a table of stretchy characters. +// FIXME: Should this be read from the unicode characteristics somehow? +// table: stretchy operator, top char, extension char, bottom char, middle char +static struct StretchyCharacter { + UChar character; + UChar topGlyph; + UChar extensionGlyph; + UChar bottomGlyph; + UChar middleGlyph; +} stretchyCharacters[13] = { + { 0x28 , 0x239b, 0x239c, 0x239d, 0x0 }, // left parenthesis + { 0x29 , 0x239e, 0x239f, 0x23a0, 0x0 }, // right parenthesis + { 0x5b , 0x23a1, 0x23a2, 0x23a3, 0x0 }, // left square bracket + { 0x2308, 0x23a1, 0x23a2, 0x23a2, 0x0 }, // left ceiling + { 0x230a, 0x23a2, 0x23a2, 0x23a3, 0x0 }, // left floor + { 0x5d , 0x23a4, 0x23a5, 0x23a6, 0x0 }, // right square bracket + { 0x2309, 0x23a4, 0x23a5, 0x23a5, 0x0 }, // right ceiling + { 0x230b, 0x23a5, 0x23a5, 0x23a6, 0x0 }, // right floor + { 0x7b , 0x23a7, 0x23aa, 0x23a9, 0x23a8 }, // left curly bracket + { 0x7c , 0x23d0, 0x23d0, 0x23d0, 0x0 }, // vertical bar + { 0x2016, 0x2016, 0x2016, 0x2016, 0x0 }, // double vertical line + { 0x7d , 0x23ab, 0x23aa, 0x23ad, 0x23ac }, // right curly bracket + { 0x222b, 0x2320, 0x23ae, 0x2321, 0x0 } // integral sign +}; + +// We stack glyphs using a 14px height with a displayed glyph height +// of 10px. The line height is set to less than the 14px so that there +// are no blank spaces between the stacked glyphs. +// +// Certain glyphs (e.g. middle and bottom) need to be adjusted upwards +// in the stack so that there isn't a gap. +// +// All of these settings are represented in the constants below. + +// FIXME: use fractions of style()->fontSize() for proper zooming/resizing. +static const int gGlyphFontSize = 14; +static const int gGlyphLineHeight = 11; +static const int gMinimumStretchHeight = 24; +static const int gGlyphHeight = 10; +static const int gTopGlyphTopAdjust = 1; +static const int gMiddleGlyphTopAdjust = -1; +static const int gBottomGlyphTopAdjust = -3; +static const float gMinimumRatioForStretch = 0.10f; + +void RenderMathMLOperator::updateFromElement() +{ + // Destroy our current children + children()->destroyLeftoverChildren(); + + // Since we share a node with our children, destroying our children will set our node's + // renderer to 0, so we need to re-set it back to this. + node()->setRenderer(this); + + // If the operator is fixed, it will be contained in m_operator + UChar firstChar = m_operator; + + // This boolean indicates whether stretching is disabled via the markup. + bool stretchDisabled = false; + + // We made need the element later if we can't stretch. + if (node()->nodeType() == Node::ELEMENT_NODE) { + if (Element* mo = static_cast<Element*>(node())) { + AtomicString stretchyAttr = mo->getAttribute(MathMLNames::stretchyAttr); + stretchDisabled = equalIgnoringCase(stretchyAttr, "false"); + + // If stretching isn't disabled, get the character from the text content. + if (!stretchDisabled && !firstChar) { + String opText = mo->textContent(); + for (unsigned int i = 0; !firstChar && i < opText.length(); i++) { + if (!isSpaceOrNewline(opText[i])) + firstChar = opText[i]; + } + } + } + } + + // The 'index' holds the stretchable character's glyph information + int index = -1; + + // isStretchy indicates whether the character is streatchable via a number of factors. + bool isStretchy = false; + + // Check for a stretchable character. + if (!stretchDisabled && firstChar) { + const int maxIndex = WTF_ARRAY_LENGTH(stretchyCharacters); + for (index++; index < maxIndex; index++) { + if (stretchyCharacters[index].character == firstChar) { + isStretchy = true; + break; + } + } + } + + // We only stretch character if the stretch height is larger than a minimum size (e.g. 24px). + bool shouldStretch = isStretchy && m_stretchHeight>gMinimumStretchHeight; + + // Either stretch is disabled or we don't have a stretchable character over the minimum height + if (stretchDisabled || !shouldStretch) { + m_isStacked = false; + RenderBlock* container = new (renderArena()) RenderMathMLBlock(node()); + + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(INLINE_BLOCK); + newStyle->setVerticalAlign(BASELINE); + + // Check for a stretchable character that is under the minimum height and use the + // font size to adjust the glyph size. + int currentFontSize = style()->fontSize(); + if (!stretchDisabled && isStretchy && m_stretchHeight > 0 && m_stretchHeight <= gMinimumStretchHeight && m_stretchHeight > currentFontSize) { + FontDescription desc; + desc.setIsAbsoluteSize(true); + desc.setSpecifiedSize(m_stretchHeight); + desc.setComputedSize(m_stretchHeight); + newStyle->setFontDescription(desc); + newStyle->font().update(newStyle->font().fontSelector()); + } + + container->setStyle(newStyle.release()); + addChild(container); + + // Build the text of the operator. + RenderText* text = 0; + if (m_operator) + text = new (renderArena()) RenderText(node(), StringImpl::create(&m_operator, 1)); + else if (node()->nodeType() == Node::ELEMENT_NODE) + if (Element* mo = static_cast<Element*>(node())) + text = new (renderArena()) RenderText(node(), mo->textContent().replace(hyphenMinus, minusSign).impl()); + // If we can't figure out the text, leave it blank. + if (text) { + RefPtr<RenderStyle> textStyle = RenderStyle::create(); + textStyle->inheritFrom(container->style()); + text->setStyle(textStyle.release()); + container->addChild(text); + } + } else { + // Build stretchable characters as a stack of glyphs. + m_isStacked = true; + + if (stretchyCharacters[index].middleGlyph) { + // We have a middle glyph (e.g. a curly bracket) that requires special processing. + int half = (m_stretchHeight - gGlyphHeight) / 2; + if (half <= gGlyphHeight) { + // We only have enough space for a single middle glyph. + createGlyph(stretchyCharacters[index].topGlyph, half, gTopGlyphTopAdjust); + createGlyph(stretchyCharacters[index].middleGlyph, gGlyphHeight, gMiddleGlyphTopAdjust); + createGlyph(stretchyCharacters[index].bottomGlyph, 0, gBottomGlyphTopAdjust); + } else { + // We have to extend both the top and bottom to the middle. + createGlyph(stretchyCharacters[index].topGlyph, gGlyphHeight, gTopGlyphTopAdjust); + int remaining = half - gGlyphHeight; + while (remaining > 0) { + if (remaining < gGlyphHeight) { + createGlyph(stretchyCharacters[index].extensionGlyph, remaining); + remaining = 0; + } else { + createGlyph(stretchyCharacters[index].extensionGlyph, gGlyphHeight); + remaining -= gGlyphHeight; + } + } + + // The middle glyph in the stack. + createGlyph(stretchyCharacters[index].middleGlyph, gGlyphHeight, gMiddleGlyphTopAdjust); + + // The remaining is the top half minus the middle glyph height. + remaining = half - gGlyphHeight; + // We need to make sure we have the full height in case the height is odd. + if (m_stretchHeight % 2 == 1) + remaining++; + + // Extend to the bottom glyph. + while (remaining > 0) { + if (remaining < gGlyphHeight) { + createGlyph(stretchyCharacters[index].extensionGlyph, remaining); + remaining = 0; + } else { + createGlyph(stretchyCharacters[index].extensionGlyph, gGlyphHeight); + remaining -= gGlyphHeight; + } + } + + // The bottom glyph in the stack. + createGlyph(stretchyCharacters[index].bottomGlyph, 0, gBottomGlyphTopAdjust); + } + } else { + // We do not have a middle glyph and so we just extend from the top to the bottom glyph. + int remaining = m_stretchHeight - 2 * gGlyphHeight; + createGlyph(stretchyCharacters[index].topGlyph, gGlyphHeight, gTopGlyphTopAdjust); + while (remaining > 0) { + if (remaining < gGlyphHeight) { + createGlyph(stretchyCharacters[index].extensionGlyph, remaining); + remaining = 0; + } else { + createGlyph(stretchyCharacters[index].extensionGlyph, gGlyphHeight); + remaining -= gGlyphHeight; + } + } + createGlyph(stretchyCharacters[index].bottomGlyph, 0, gBottomGlyphTopAdjust); + } + } +} + +RefPtr<RenderStyle> RenderMathMLOperator::createStackableStyle(int size, int topRelative) +{ + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(BLOCK); + + FontDescription desc; + desc.setIsAbsoluteSize(true); + desc.setSpecifiedSize(gGlyphFontSize); + desc.setComputedSize(gGlyphFontSize); + newStyle->setFontDescription(desc); + newStyle->font().update(newStyle->font().fontSelector()); + newStyle->setLineHeight(Length(gGlyphLineHeight, Fixed)); + newStyle->setVerticalAlign(TOP); + + if (size > 0) + newStyle->setMaxHeight(Length(size, Fixed)); + + newStyle->setOverflowY(OHIDDEN); + newStyle->setOverflowX(OHIDDEN); + if (topRelative) { + newStyle->setTop(Length(topRelative, Fixed)); + newStyle->setPosition(RelativePosition); + } + + return newStyle; +} + +RenderBlock* RenderMathMLOperator::createGlyph(UChar glyph, int size, int charRelative, int topRelative) +{ + RenderBlock* container = new (renderArena()) RenderMathMLBlock(node()); + container->setStyle(createStackableStyle(size, topRelative).release()); + addChild(container); + RenderBlock* parent = container; + if (charRelative) { + RenderBlock* charBlock = new (renderArena()) RenderBlock(node()); + RefPtr<RenderStyle> charStyle = RenderStyle::create(); + charStyle->inheritFrom(container->style()); + charStyle->setDisplay(INLINE_BLOCK); + charStyle->setTop(Length(charRelative, Fixed)); + charStyle->setPosition(RelativePosition); + charBlock->setStyle(charStyle); + parent->addChild(charBlock); + parent = charBlock; + } + + RenderText* text = new (renderArena()) RenderText(node(), StringImpl::create(&glyph, 1)); + text->setStyle(container->style()); + parent->addChild(text); + return container; +} + +int RenderMathMLOperator::baselinePosition(FontBaseline, bool firstLine, LineDirectionMode lineDirection, LinePositionMode linePositionMode) const +{ + if (m_isStacked) + return m_stretchHeight * 2 / 3 - (m_stretchHeight - static_cast<int>(m_stretchHeight / gOperatorExpansion)) / 2; + return RenderBlock::baselinePosition(AlphabeticBaseline, firstLine, lineDirection, linePositionMode); +} + +} + +#endif |