RenderMathMLOperator.cpp [plain text]
#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(Element* element)
: RenderMathMLBlock(element)
, m_stretchHeight(0)
, m_operator(0)
{
}
RenderMathMLOperator::RenderMathMLOperator(Node* node, UChar operatorChar)
: RenderMathMLBlock(node)
, 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)
{
height *= gOperatorExpansion;
if (m_stretchHeight == height)
return;
m_stretchHeight = height;
updateFromElement();
}
void RenderMathMLOperator::computePreferredLogicalWidths()
{
ASSERT(preferredLogicalWidthsDirty());
if (!firstChild())
updateFromElement();
RenderMathMLBlock::computePreferredLogicalWidths();
}
static struct StretchyCharacter {
UChar character;
UChar topGlyph;
UChar extensionGlyph;
UChar bottomGlyph;
UChar middleGlyph;
} stretchyCharacters[13] = {
{ 0x28 , 0x239b, 0x239c, 0x239d, 0x0 }, { 0x29 , 0x239e, 0x239f, 0x23a0, 0x0 }, { 0x5b , 0x23a1, 0x23a2, 0x23a3, 0x0 }, { 0x2308, 0x23a1, 0x23a2, 0x23a2, 0x0 }, { 0x230a, 0x23a2, 0x23a2, 0x23a3, 0x0 }, { 0x5d , 0x23a4, 0x23a5, 0x23a6, 0x0 }, { 0x2309, 0x23a4, 0x23a5, 0x23a5, 0x0 }, { 0x230b, 0x23a5, 0x23a5, 0x23a6, 0x0 }, { 0x7b , 0x23a7, 0x23aa, 0x23a9, 0x23a8 }, { 0x7c , 0x23d0, 0x23d0, 0x23d0, 0x0 }, { 0x2016, 0x2016, 0x2016, 0x2016, 0x0 }, { 0x7d , 0x23ab, 0x23aa, 0x23ad, 0x23ac }, { 0x222b, 0x2320, 0x23ae, 0x2321, 0x0 } };
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;
int RenderMathMLOperator::glyphHeightForCharacter(UChar character)
{
GlyphData data = style()->font().glyphDataForCharacter(character, false);
FloatRect glyphBounds = data.fontData->boundsForGlyph(data.glyph);
if (glyphBounds.height() && glyphBounds.height() < gGlyphHeight)
return glyphBounds.height();
return gGlyphHeight;
}
int RenderMathMLOperator::lineHeightForCharacter(UChar character)
{
int glyphHeight = glyphHeightForCharacter(character);
if (glyphHeight < gGlyphHeight)
return (glyphHeight - 1) > 0 ? glyphHeight - 1 : 1;
return gGlyphLineHeight;
}
void RenderMathMLOperator::updateFromElement()
{
RenderObject* savedRenderer = node()->renderer();
children()->destroyLeftoverChildren();
node()->setRenderer(savedRenderer);
UChar firstChar = m_operator;
bool stretchDisabled = false;
if (node()->nodeType() == Node::ELEMENT_NODE) {
if (Element* mo = static_cast<Element*>(node())) {
AtomicString stretchyAttr = mo->getAttribute(MathMLNames::stretchyAttr);
stretchDisabled = equalIgnoringCase(stretchyAttr, "false");
if (!stretchDisabled && !firstChar) {
String opText = mo->textContent();
for (unsigned int i = 0; !firstChar && i < opText.length(); i++) {
if (!isSpaceOrNewline(opText[i]))
firstChar = opText[i];
}
}
}
}
int index = -1;
bool isStretchy = false;
if (!stretchDisabled && firstChar) {
const int maxIndex = WTF_ARRAY_LENGTH(stretchyCharacters);
for (index++; index < maxIndex; index++) {
if (stretchyCharacters[index].character == firstChar) {
isStretchy = true;
break;
}
}
}
bool shouldStretch = isStretchy && m_stretchHeight>gMinimumStretchHeight;
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);
int currentFontSize = style()->fontSize();
if (!stretchDisabled && isStretchy && m_stretchHeight > 0 && m_stretchHeight <= gMinimumStretchHeight && m_stretchHeight > currentFontSize) {
FontDescription desc = style()->fontDescription();
desc.setIsAbsoluteSize(true);
desc.setSpecifiedSize(m_stretchHeight);
desc.setComputedSize(m_stretchHeight);
newStyle->setFontDescription(desc);
newStyle->font().update(style()->font().fontSelector());
}
container->setStyle(newStyle.release());
addChild(container);
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 (text) {
RefPtr<RenderStyle> textStyle = RenderStyle::create();
textStyle->inheritFrom(container->style());
text->setStyle(textStyle.release());
container->addChild(text);
}
} else {
m_isStacked = true;
int extensionGlyphLineHeight = lineHeightForCharacter(stretchyCharacters[index].extensionGlyph);
int topGlyphLineHeight = lineHeightForCharacter(stretchyCharacters[index].topGlyph);
int bottomGlyphLineHeight = lineHeightForCharacter(stretchyCharacters[index].bottomGlyph);
if (stretchyCharacters[index].middleGlyph) {
int glyphHeight = glyphHeightForCharacter(stretchyCharacters[index].middleGlyph);
int middleGlyphLineHeight = lineHeightForCharacter(stretchyCharacters[index].middleGlyph);
int half = (m_stretchHeight - glyphHeight) / 2;
if (half <= glyphHeight) {
createGlyph(stretchyCharacters[index].topGlyph, topGlyphLineHeight, half, gTopGlyphTopAdjust);
createGlyph(stretchyCharacters[index].middleGlyph, middleGlyphLineHeight, glyphHeight, gMiddleGlyphTopAdjust);
createGlyph(stretchyCharacters[index].bottomGlyph, bottomGlyphLineHeight, 0, gBottomGlyphTopAdjust);
} else {
createGlyph(stretchyCharacters[index].topGlyph, topGlyphLineHeight, glyphHeight, gTopGlyphTopAdjust);
int remaining = half - glyphHeight;
while (remaining > 0) {
if (remaining < glyphHeight) {
createGlyph(stretchyCharacters[index].extensionGlyph, extensionGlyphLineHeight, remaining);
remaining = 0;
} else {
createGlyph(stretchyCharacters[index].extensionGlyph, extensionGlyphLineHeight, glyphHeight);
remaining -= glyphHeight;
}
}
createGlyph(stretchyCharacters[index].middleGlyph, middleGlyphLineHeight, glyphHeight, gMiddleGlyphTopAdjust);
remaining = half - glyphHeight;
if (m_stretchHeight % 2 == 1)
remaining++;
while (remaining > 0) {
if (remaining < glyphHeight) {
createGlyph(stretchyCharacters[index].extensionGlyph, extensionGlyphLineHeight, remaining);
remaining = 0;
} else {
createGlyph(stretchyCharacters[index].extensionGlyph, extensionGlyphLineHeight, glyphHeight);
remaining -= glyphHeight;
}
}
createGlyph(stretchyCharacters[index].bottomGlyph, bottomGlyphLineHeight, 0, gBottomGlyphTopAdjust);
}
} else {
int glyphHeight = glyphHeightForCharacter(stretchyCharacters[index].extensionGlyph);
int remaining = m_stretchHeight - 2 * glyphHeight;
createGlyph(stretchyCharacters[index].topGlyph, topGlyphLineHeight, glyphHeight, gTopGlyphTopAdjust);
while (remaining > 0) {
if (remaining < glyphHeight) {
createGlyph(stretchyCharacters[index].extensionGlyph, extensionGlyphLineHeight, remaining);
remaining = 0;
} else {
createGlyph(stretchyCharacters[index].extensionGlyph, extensionGlyphLineHeight, glyphHeight);
remaining -= glyphHeight;
}
}
createGlyph(stretchyCharacters[index].bottomGlyph, bottomGlyphLineHeight, 0, gBottomGlyphTopAdjust);
}
}
setNeedsLayoutAndPrefWidthsRecalc();
}
PassRefPtr<RenderStyle> RenderMathMLOperator::createStackableStyle(int lineHeight, int maxHeightForRenderer, int topRelative)
{
RefPtr<RenderStyle> newStyle = RenderStyle::create();
newStyle->inheritFrom(style());
newStyle->setDisplay(BLOCK);
FontDescription desc = style()->fontDescription();
desc.setIsAbsoluteSize(true);
desc.setSpecifiedSize(gGlyphFontSize);
desc.setComputedSize(gGlyphFontSize);
newStyle->setFontDescription(desc);
newStyle->font().update(style()->font().fontSelector());
newStyle->setLineHeight(Length(lineHeight, Fixed));
newStyle->setVerticalAlign(TOP);
if (maxHeightForRenderer > 0)
newStyle->setMaxHeight(Length(maxHeightForRenderer, Fixed));
newStyle->setOverflowY(OHIDDEN);
newStyle->setOverflowX(OHIDDEN);
if (topRelative) {
newStyle->setTop(Length(topRelative, Fixed));
newStyle->setPosition(RelativePosition);
}
return newStyle.release();
}
RenderBlock* RenderMathMLOperator::createGlyph(UChar glyph, int lineHeight, int maxHeightForRenderer, int charRelative, int topRelative)
{
RenderBlock* container = new (renderArena()) RenderMathMLBlock(node());
container->setStyle(createStackableStyle(lineHeight, maxHeightForRenderer, topRelative));
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;
}
LayoutUnit 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