Diff
Modified: trunk/Source/WebCore/CMakeLists.txt (201853 => 201854)
--- trunk/Source/WebCore/CMakeLists.txt 2016-06-09 05:43:46 UTC (rev 201853)
+++ trunk/Source/WebCore/CMakeLists.txt 2016-06-09 06:17:40 UTC (rev 201854)
@@ -2517,6 +2517,7 @@
rendering/line/TrailingObjects.cpp
rendering/mathml/MathMLOperatorDictionary.cpp
+ rendering/mathml/MathOperator.cpp
rendering/mathml/RenderMathMLBlock.cpp
rendering/mathml/RenderMathMLFenced.cpp
rendering/mathml/RenderMathMLFraction.cpp
Modified: trunk/Source/WebCore/ChangeLog (201853 => 201854)
--- trunk/Source/WebCore/ChangeLog 2016-06-09 05:43:46 UTC (rev 201853)
+++ trunk/Source/WebCore/ChangeLog 2016-06-09 06:17:40 UTC (rev 201854)
@@ -1,3 +1,63 @@
+2016-06-08 Frederic Wang <[email protected]>
+
+ Move selection and drawing of stretchy operators into a separate MathOperator class
+ https://bugs.webkit.org/show_bug.cgi?id=156921
+
+ Reviewed by Martin Robinson.
+
+ No new tests, behavior is not changed.
+
+ * CMakeLists.txt: Add the MathOperator files.
+ * WebCore.xcodeproj/project.pbxproj: Ditto.
+ * rendering/mathml/MathOperator.cpp: Added.
+ (WebCore::boundsForGlyph): Moved from RenderMathMLOperator.
+ (WebCore::heightForGlyph): Moved from RenderMathMLOperator.
+ (WebCore::advanceWidthForGlyph): Moved from RenderMathMLOperator.
+ (WebCore::MathOperator::MathOperator):
+ (WebCore::MathOperator::setOperator):
+ (WebCore::MathOperator::getBaseGlyph): Moved from RenderMathMLOperator.
+ (WebCore::MathOperator::setSizeVariant): Moved from RenderMathMLOperator.
+ (WebCore::MathOperator::setGlyphAssembly): Moved from RenderMathMLOperator.
+ (WebCore::MathOperator::calculateDisplayStyleLargeOperator): Moved from RenderMathMLOperator with additional style parameter.
+ (WebCore::MathOperator::calculateGlyphAssemblyFallBack): Ditto.
+ (WebCore::MathOperator::calculateStretchyData): Ditto.
+ (WebCore::MathOperator::paintGlyph): Ditto.
+ (WebCore::MathOperator::fillWithVerticalExtensionGlyph): Ditto.
+ (WebCore::MathOperator::fillWithHorizontalExtensionGlyph): Ditto.
+ (WebCore::MathOperator::paintVerticalGlyphAssembly): Ditto.
+ (WebCore::MathOperator::paintHorizontalGlyphAssembly): Ditto.
+ * rendering/mathml/MathOperator.h: Added.
+ (WebCore::MathOperator::italicCorrection):
+ (WebCore::MathOperator::isStretched):
+ (WebCore::MathOperator::unstretch):
+ (WebCore::MathOperator::GlyphAssemblyData::GlyphAssemblyData): Moved from RenderMathMLOperator.
+ (WebCore::MathOperator::stretchSize):
+ * rendering/mathml/RenderMathMLOperator.cpp:
+ (WebCore::RenderMathMLOperator::computePreferredLogicalWidths): Updated to use MathOperator members.
+ For now we call setOperator to transmit information to m_mathOperator.
+ (WebCore::RenderMathMLOperator::updateStyle): Updated to use MathOperator members.
+ For now we set some m_mathOperator members to transmit it some information.
+ (WebCore::RenderMathMLOperator::firstLineBaseline): Updated to use MathOperator members.
+ (WebCore::RenderMathMLOperator::computeLogicalHeight): Ditto.
+ (WebCore::RenderMathMLOperator::paint): Ditto.
+ For now we set some m_mathOperator members to transmit it some information.
+ (WebCore::RenderMathMLOperator::paintChildren): Updated to use MathOperator members.
+ (WebCore::RenderMathMLOperator::trailingSpaceError): Ditto.
+ (WebCore::RenderMathMLOperator::getBaseGlyph): Deleted.
+ (WebCore::RenderMathMLOperator::setSizeVariant): Deleted.
+ (WebCore::RenderMathMLOperator::setGlyphAssembly): Deleted.
+ (WebCore::RenderMathMLOperator::calculateGlyphAssemblyFallBack): Deleted.
+ (WebCore::RenderMathMLOperator::calculateDisplayStyleLargeOperator): Deleted.
+ (WebCore::RenderMathMLOperator::calculateStretchyData): Deleted.
+ (WebCore::RenderMathMLOperator::paintGlyph): Deleted.
+ (WebCore::RenderMathMLOperator::fillWithVerticalExtensionGlyph): Deleted.
+ (WebCore::RenderMathMLOperator::fillWithHorizontalExtensionGlyph): Deleted.
+ (WebCore::RenderMathMLOperator::paintVerticalGlyphAssembly): Deleted.
+ (WebCore::RenderMathMLOperator::paintHorizontalGlyphAssembly): Deleted.
+ * rendering/mathml/RenderMathMLOperator.h:
+ (WebCore::RenderMathMLOperator::italicCorrection): Updated to use MathOperator members.
+ (WebCore::RenderMathMLOperator::GlyphAssemblyData::GlyphAssemblyData): Deleted.
+
2016-06-08 Chris Dumez <[email protected]>
DedicatedWorkerGlobalScope prototype chain is incorrect
Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (201853 => 201854)
--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2016-06-09 05:43:46 UTC (rev 201853)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj 2016-06-09 06:17:40 UTC (rev 201854)
@@ -1670,6 +1670,8 @@
439046E812DA25E800AF80A2 /* RenderMathMLScripts.h in Headers */ = {isa = PBXBuildFile; fileRef = 439046D412DA25E800AF80A2 /* RenderMathMLScripts.h */; };
439046E912DA25E800AF80A2 /* RenderMathMLUnderOver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 439046D512DA25E800AF80A2 /* RenderMathMLUnderOver.cpp */; };
439046EA12DA25E800AF80A2 /* RenderMathMLUnderOver.h in Headers */ = {isa = PBXBuildFile; fileRef = 439046D612DA25E800AF80A2 /* RenderMathMLUnderOver.h */; };
+ 439046E912DA25E812AF80AC /* MathOperator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 439046D512DA25E812AF80AC /* MathOperator.cpp */; };
+ 439046EA12DA25E812AF80AC /* MathOperator.h in Headers */ = {isa = PBXBuildFile; fileRef = 439046D612DA25E812AF80AC /* MathOperator.h */; };
439046EB12DA25E800AF80A9 /* RenderMathMLToken.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 439046D712DA25E800AF80A9 /* RenderMathMLToken.cpp */; };
439046EC12DA25E800AF80A9 /* RenderMathMLToken.h in Headers */ = {isa = PBXBuildFile; fileRef = 439046D812DA25E800AF80A9 /* RenderMathMLToken.h */; };
439D334313A6911C00C20F4F /* SVGAnimatedType.h in Headers */ = {isa = PBXBuildFile; fileRef = 439D334013A6911C00C20F4F /* SVGAnimatedType.h */; };
@@ -9233,6 +9235,8 @@
439046D412DA25E800AF80A2 /* RenderMathMLScripts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderMathMLScripts.h; sourceTree = "<group>"; };
439046D512DA25E800AF80A2 /* RenderMathMLUnderOver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderMathMLUnderOver.cpp; sourceTree = "<group>"; };
439046D612DA25E800AF80A2 /* RenderMathMLUnderOver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderMathMLUnderOver.h; sourceTree = "<group>"; };
+ 439046D512DA25E812AF80AC /* MathOperator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MathOperator.cpp; sourceTree = "<group>"; };
+ 439046D612DA25E812AF80AC /* MathOperator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MathOperator.h; sourceTree = "<group>"; };
439046D712DA25E800AF80A9 /* RenderMathMLToken.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderMathMLToken.cpp; sourceTree = "<group>"; };
439046D812DA25E800AF80A9 /* RenderMathMLToken.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderMathMLToken.h; sourceTree = "<group>"; };
439D334013A6911C00C20F4F /* SVGAnimatedType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVGAnimatedType.h; sourceTree = "<group>"; };
@@ -16983,6 +16987,8 @@
439046D812DA25E800AF80A9 /* RenderMathMLToken.h */,
439046D512DA25E800AF80A2 /* RenderMathMLUnderOver.cpp */,
439046D612DA25E800AF80A2 /* RenderMathMLUnderOver.h */,
+ 439046D512DA25E812AF80AC /* MathOperator.cpp */,
+ 439046D612DA25E812AF80AC /* MathOperator.h */,
);
path = mathml;
sourceTree = "<group>";
@@ -27701,6 +27707,7 @@
439046E612DA25E800AF80A2 /* RenderMathMLSquareRoot.h in Headers */,
439046EC12DA25E800AF80A9 /* RenderMathMLToken.h in Headers */,
439046EA12DA25E800AF80A2 /* RenderMathMLUnderOver.h in Headers */,
+ 439046EA12DA25E812AF80AC /* MathOperator.h in Headers */,
E4C279590CF9741900E97B98 /* RenderMedia.h in Headers */,
DEBCCDD216646E8200A452E1 /* RenderMediaControlElements.h in Headers */,
41FA303F1316C29C00C0BFC5 /* RenderMediaControls.h in Headers */,
@@ -31381,6 +31388,7 @@
439046E512DA25E800AF80A2 /* RenderMathMLSquareRoot.cpp in Sources */,
439046EB12DA25E800AF80A9 /* RenderMathMLToken.cpp in Sources */,
439046E912DA25E800AF80A2 /* RenderMathMLUnderOver.cpp in Sources */,
+ 439046E912DA25E812AF80AC /* MathOperator.cpp in Sources */,
E4C279580CF9741900E97B98 /* RenderMedia.cpp in Sources */,
DE49B309165F2FE10010338D /* RenderMediaControlElements.cpp in Sources */,
41FA303E1316C29C00C0BFC5 /* RenderMediaControls.cpp in Sources */,
Added: trunk/Source/WebCore/rendering/mathml/MathOperator.cpp (0 => 201854)
--- trunk/Source/WebCore/rendering/mathml/MathOperator.cpp (rev 0)
+++ trunk/Source/WebCore/rendering/mathml/MathOperator.cpp 2016-06-09 06:17:40 UTC (rev 201854)
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2016 Igalia S.L. 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 "MathOperator.h"
+
+#include "RenderStyle.h"
+#include "StyleInheritedData.h"
+
+namespace WebCore {
+
+static inline FloatRect boundsForGlyph(const GlyphData& data)
+{
+ return data.isValid() ? data.font->boundsForGlyph(data.glyph) : FloatRect();
+}
+
+static inline float heightForGlyph(const GlyphData& data)
+{
+ return boundsForGlyph(data).height();
+}
+
+static inline float advanceWidthForGlyph(const GlyphData& data)
+{
+ return data.isValid() ? data.font->widthForGlyph(data.glyph) : 0;
+}
+
+// FIXME: This hardcoded data can be removed when OpenType MATH font are widely available (http://wkbug/156837).
+struct StretchyCharacter {
+ UChar character;
+ UChar topChar;
+ UChar extensionChar;
+ UChar bottomChar;
+ UChar middleChar;
+};
+// The first leftRightPairsCount pairs correspond to left/right fences that can easily be mirrored in RTL.
+static const short leftRightPairsCount = 5;
+static const StretchyCharacter stretchyCharacters[14] = {
+ { 0x28 , 0x239b, 0x239c, 0x239d, 0x0 }, // left parenthesis
+ { 0x29 , 0x239e, 0x239f, 0x23a0, 0x0 }, // right parenthesis
+ { 0x5b , 0x23a1, 0x23a2, 0x23a3, 0x0 }, // left square bracket
+ { 0x5d , 0x23a4, 0x23a5, 0x23a6, 0x0 }, // right square bracket
+ { 0x7b , 0x23a7, 0x23aa, 0x23a9, 0x23a8 }, // left curly bracket
+ { 0x7d , 0x23ab, 0x23aa, 0x23ad, 0x23ac }, // right curly bracket
+ { 0x2308, 0x23a1, 0x23a2, 0x23a2, 0x0 }, // left ceiling
+ { 0x2309, 0x23a4, 0x23a5, 0x23a5, 0x0 }, // right ceiling
+ { 0x230a, 0x23a2, 0x23a2, 0x23a3, 0x0 }, // left floor
+ { 0x230b, 0x23a5, 0x23a5, 0x23a6, 0x0 }, // right floor
+ { 0x7c , 0x7c, 0x7c, 0x7c, 0x0 }, // vertical bar
+ { 0x2016, 0x2016, 0x2016, 0x2016, 0x0 }, // double vertical line
+ { 0x2225, 0x2225, 0x2225, 0x2225, 0x0 }, // parallel to
+ { 0x222b, 0x2320, 0x23ae, 0x2321, 0x0 } // integral sign
+};
+
+void MathOperator::setOperator(UChar baseCharacter, bool isVertical)
+{
+ m_baseCharacter = baseCharacter;
+ m_isVertical = isVertical;
+}
+
+bool MathOperator::getBaseGlyph(const RenderStyle& style, GlyphData& baseGlyph) const
+{
+ baseGlyph = style.fontCascade().glyphDataForCharacter(m_baseCharacter, !style.isLeftToRightDirection());
+ return baseGlyph.isValid() && baseGlyph.font == &style.fontCascade().primaryFont();
+}
+
+void MathOperator::setSizeVariant(const GlyphData& sizeVariant)
+{
+ ASSERT(sizeVariant.isValid());
+ ASSERT(sizeVariant.font->mathData());
+ m_stretchType = StretchType::SizeVariant;
+ m_variant = sizeVariant;
+}
+
+void MathOperator::setGlyphAssembly(const GlyphAssemblyData& assemblyData)
+{
+ m_stretchType = StretchType::GlyphAssembly;
+ m_assembly = assemblyData;
+}
+
+void MathOperator::calculateDisplayStyleLargeOperator(const RenderStyle& style)
+{
+ ASSERT(m_isVertical);
+
+ GlyphData baseGlyph;
+ if (!getBaseGlyph(style, baseGlyph) || !baseGlyph.font->mathData())
+ return;
+
+ // The value of displayOperatorMinHeight is sometimes too small, so we ensure that it is at least \sqrt{2} times the size of the base glyph.
+ float displayOperatorMinHeight = std::max(heightForGlyph(baseGlyph) * sqrtOfTwoFloat, baseGlyph.font->mathData()->getMathConstant(*baseGlyph.font, OpenTypeMathData::DisplayOperatorMinHeight));
+
+ Vector<Glyph> sizeVariants;
+ Vector<OpenTypeMathData::AssemblyPart> assemblyParts;
+ baseGlyph.font->mathData()->getMathVariants(baseGlyph.glyph, true, sizeVariants, assemblyParts);
+
+ // We choose the first size variant that is larger than the expected displayOperatorMinHeight and otherwise fallback to the largest variant.
+ for (auto& sizeVariant : sizeVariants) {
+ GlyphData glyphData(sizeVariant, baseGlyph.font);
+ setSizeVariant(glyphData);
+ m_italicCorrection = glyphData.font->mathData()->getItalicCorrection(*glyphData.font, glyphData.glyph);
+ if (heightForGlyph(glyphData) >= displayOperatorMinHeight)
+ break;
+ }
+}
+
+bool MathOperator::calculateGlyphAssemblyFallback(const RenderStyle& style, const Vector<OpenTypeMathData::AssemblyPart>& assemblyParts, GlyphAssemblyData& assemblyData) const
+{
+ // The structure of the Open Type Math table is a bit more general than the one currently used by the MathOperator code, so we try to fallback in a reasonable way.
+ // FIXME: MathOperator should support the most general format (https://bugs.webkit.org/show_bug.cgi?id=130327).
+ // We use the approach of the copyComponents function in github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py
+
+ // We count the number of non extender pieces.
+ int nonExtenderCount = 0;
+ for (auto& part : assemblyParts) {
+ if (!part.isExtender)
+ nonExtenderCount++;
+ }
+ if (nonExtenderCount > 3)
+ return false; // This is not supported: there are too many pieces.
+
+ // We now browse the list of pieces from left to right for horizontal operators and from bottom to top for vertical operators.
+ enum PartType {
+ Start,
+ ExtenderBetweenStartAndMiddle,
+ Middle,
+ ExtenderBetweenMiddleAndEnd,
+ End,
+ None
+ };
+ PartType expectedPartType = Start;
+ assemblyData.extension.glyph = 0;
+ assemblyData.middle.glyph = 0;
+ for (auto& part : assemblyParts) {
+ if (nonExtenderCount < 3) {
+ // If we only have at most two non-extenders then we skip the middle glyph.
+ if (expectedPartType == ExtenderBetweenStartAndMiddle)
+ expectedPartType = ExtenderBetweenMiddleAndEnd;
+ else if (expectedPartType == Middle)
+ expectedPartType = End;
+ }
+ if (part.isExtender) {
+ if (!assemblyData.extension.glyph)
+ assemblyData.extension.glyph = part.glyph; // We copy the extender part.
+ else if (assemblyData.extension.glyph != part.glyph)
+ return false; // This is not supported: the assembly has different extenders.
+
+ switch (expectedPartType) {
+ case Start:
+ // We ignore the left/bottom part.
+ expectedPartType = ExtenderBetweenStartAndMiddle;
+ continue;
+ case Middle:
+ // We ignore the middle part.
+ expectedPartType = ExtenderBetweenMiddleAndEnd;
+ continue;
+ case End:
+ case None:
+ // This is not supported: we got an unexpected extender.
+ return false;
+ case ExtenderBetweenStartAndMiddle:
+ case ExtenderBetweenMiddleAndEnd:
+ // We ignore multiple consecutive extenders.
+ continue;
+ }
+ }
+
+ switch (expectedPartType) {
+ case Start:
+ // We copy the left/bottom part.
+ assemblyData.bottomOrLeft.glyph = part.glyph;
+ expectedPartType = ExtenderBetweenStartAndMiddle;
+ continue;
+ case ExtenderBetweenStartAndMiddle:
+ case Middle:
+ // We copy the middle part.
+ assemblyData.middle.glyph = part.glyph;
+ expectedPartType = ExtenderBetweenMiddleAndEnd;
+ continue;
+ case ExtenderBetweenMiddleAndEnd:
+ case End:
+ // We copy the right/top part.
+ assemblyData.topOrRight.glyph = part.glyph;
+ expectedPartType = None;
+ continue;
+ case None:
+ // This is not supported: we got an unexpected non-extender part.
+ return false;
+ }
+ }
+
+ if (!assemblyData.extension.glyph)
+ return false; // This is not supported: we always assume that we have an extension glyph.
+
+ // If we don't have top/bottom glyphs, we use the extension glyph.
+ if (!assemblyData.topOrRight.glyph)
+ assemblyData.topOrRight.glyph = assemblyData.extension.glyph;
+ if (!assemblyData.bottomOrLeft.glyph)
+ assemblyData.bottomOrLeft.glyph = assemblyData.extension.glyph;
+
+ assemblyData.topOrRight.font = &style.fontCascade().primaryFont();
+ assemblyData.extension.font = assemblyData.topOrRight.font;
+ assemblyData.bottomOrLeft.font = assemblyData.topOrRight.font;
+ assemblyData.middle.font = assemblyData.middle.glyph ? assemblyData.topOrRight.font : nullptr;
+
+ return true;
+}
+
+void MathOperator::calculateStretchyData(const RenderStyle& style, float* maximumGlyphWidth, LayoutUnit targetSize)
+{
+ ASSERT(!maximumGlyphWidth || m_isVertical);
+
+ GlyphData baseGlyph;
+ if (!getBaseGlyph(style, baseGlyph))
+ return;
+
+ if (!maximumGlyphWidth) {
+ // We do not stretch if the base glyph is large enough.
+ float baseSize = m_isVertical ? heightForGlyph(baseGlyph) : advanceWidthForGlyph(baseGlyph);
+ if (targetSize <= baseSize)
+ return;
+ }
+
+ MathOperator::GlyphAssemblyData assemblyData;
+ if (baseGlyph.font->mathData()) {
+ Vector<Glyph> sizeVariants;
+ Vector<OpenTypeMathData::AssemblyPart> assemblyParts;
+ baseGlyph.font->mathData()->getMathVariants(baseGlyph.glyph, m_isVertical, sizeVariants, assemblyParts);
+ // We verify the size variants.
+ for (auto& sizeVariant : sizeVariants) {
+ GlyphData glyphData(sizeVariant, baseGlyph.font);
+ if (maximumGlyphWidth)
+ *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceWidthForGlyph(glyphData));
+ else {
+ setSizeVariant(glyphData);
+ float size = m_isVertical ? heightForGlyph(glyphData) : advanceWidthForGlyph(glyphData);
+ if (size >= targetSize)
+ return;
+ }
+ }
+
+ // We verify if there is a construction.
+ if (!calculateGlyphAssemblyFallback(style, assemblyParts, assemblyData))
+ return;
+ } else {
+ if (!m_isVertical)
+ return;
+
+ // If the font does not have a MATH table, we fallback to the Unicode-only constructions.
+ const StretchyCharacter* stretchyCharacter = nullptr;
+ const unsigned maxIndex = WTF_ARRAY_LENGTH(stretchyCharacters);
+ for (unsigned index = 0; index < maxIndex; ++index) {
+ if (stretchyCharacters[index].character == m_baseCharacter) {
+ stretchyCharacter = &stretchyCharacters[index];
+ if (!style.isLeftToRightDirection() && index < leftRightPairsCount * 2) {
+ // If we are in right-to-left direction we select the mirrored form by adding -1 or +1 according to the parity of index.
+ index += index % 2 ? -1 : 1;
+ }
+ break;
+ }
+ }
+
+ // If we didn't find a stretchy character set for this character, we don't know how to stretch it.
+ if (!stretchyCharacter)
+ return;
+
+ // We convert the list of Unicode characters into a list of glyph data.
+ assemblyData.topOrRight = style.fontCascade().glyphDataForCharacter(stretchyCharacter->topChar, false);
+ assemblyData.extension = style.fontCascade().glyphDataForCharacter(stretchyCharacter->extensionChar, false);
+ assemblyData.bottomOrLeft = style.fontCascade().glyphDataForCharacter(stretchyCharacter->bottomChar, false);
+ assemblyData.middle = stretchyCharacter->middleChar ? style.fontCascade().glyphDataForCharacter(stretchyCharacter->middleChar, false) : GlyphData();
+ }
+
+ // If we are measuring the maximum width, verify each component.
+ if (maximumGlyphWidth) {
+ *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceWidthForGlyph(assemblyData.topOrRight));
+ *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceWidthForGlyph(assemblyData.extension));
+ *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceWidthForGlyph(assemblyData.middle));
+ *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceWidthForGlyph(assemblyData.bottomOrLeft));
+ return;
+ }
+
+ // We ensure that the size is large enough to avoid glyph overlaps.
+ float minSize = m_isVertical ?
+ heightForGlyph(assemblyData.topOrRight) + heightForGlyph(assemblyData.middle) + heightForGlyph(assemblyData.bottomOrLeft)
+ : advanceWidthForGlyph(assemblyData.bottomOrLeft) + advanceWidthForGlyph(assemblyData.middle) + advanceWidthForGlyph(assemblyData.topOrRight);
+ if (minSize > targetSize)
+ return;
+
+ setGlyphAssembly(assemblyData);
+}
+
+LayoutRect MathOperator::paintGlyph(const RenderStyle& style, PaintInfo& info, const GlyphData& data, const LayoutPoint& origin, GlyphPaintTrimming trim)
+{
+ FloatRect glyphBounds = boundsForGlyph(data);
+
+ LayoutRect glyphPaintRect(origin, LayoutSize(glyphBounds.x() + glyphBounds.width(), glyphBounds.height()));
+ glyphPaintRect.setY(origin.y() + glyphBounds.y());
+
+ // In order to have glyphs fit snugly with one another we snap the connecting edges to pixel boundaries
+ // and trim off one pixel. The pixel trim is to account for fonts that have edge pixels that have less
+ // than full coverage. These edge pixels can introduce small seams between connected glyphs.
+ FloatRect clipBounds = info.rect;
+ switch (trim) {
+ case TrimTop:
+ glyphPaintRect.shiftYEdgeTo(glyphPaintRect.y().ceil() + 1);
+ clipBounds.shiftYEdgeTo(glyphPaintRect.y());
+ break;
+ case TrimBottom:
+ glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1);
+ clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
+ break;
+ case TrimTopAndBottom:
+ glyphPaintRect.shiftYEdgeTo(glyphPaintRect.y().ceil() + 1);
+ glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1);
+ clipBounds.shiftYEdgeTo(glyphPaintRect.y());
+ clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
+ break;
+ case TrimLeft:
+ glyphPaintRect.shiftXEdgeTo(glyphPaintRect.x().ceil() + 1);
+ clipBounds.shiftXEdgeTo(glyphPaintRect.x());
+ break;
+ case TrimRight:
+ glyphPaintRect.shiftMaxXEdgeTo(glyphPaintRect.maxX().floor() - 1);
+ clipBounds.shiftMaxXEdgeTo(glyphPaintRect.maxX());
+ break;
+ case TrimLeftAndRight:
+ glyphPaintRect.shiftXEdgeTo(glyphPaintRect.x().ceil() + 1);
+ glyphPaintRect.shiftMaxXEdgeTo(glyphPaintRect.maxX().floor() - 1);
+ clipBounds.shiftXEdgeTo(glyphPaintRect.x());
+ clipBounds.shiftMaxXEdgeTo(glyphPaintRect.maxX());
+ }
+
+ // Clipping the enclosing IntRect avoids any potential issues at joined edges.
+ GraphicsContextStateSaver stateSaver(info.context());
+ info.context().clip(clipBounds);
+
+ GlyphBuffer buffer;
+ buffer.add(data.glyph, data.font, advanceWidthForGlyph(data));
+ info.context().drawGlyphs(style.fontCascade(), *data.font, buffer, 0, 1, origin);
+
+ return glyphPaintRect;
+}
+
+void MathOperator::fillWithVerticalExtensionGlyph(const RenderStyle& style, PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
+{
+ ASSERT(m_isVertical);
+ ASSERT(m_stretchType == StretchType::GlyphAssembly);
+ ASSERT(m_assembly.extension.isValid());
+ ASSERT(from.y() <= to.y());
+
+ // If there is no space for the extension glyph, we don't need to do anything.
+ if (from.y() == to.y())
+ return;
+
+ GraphicsContextStateSaver stateSaver(info.context());
+
+ FloatRect glyphBounds = boundsForGlyph(m_assembly.extension);
+
+ // Clipping the extender region here allows us to draw the bottom extender glyph into the
+ // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping.
+ LayoutRect clipBounds = info.rect;
+ clipBounds.shiftYEdgeTo(from.y());
+ clipBounds.shiftMaxYEdgeTo(to.y());
+ info.context().clip(clipBounds);
+
+ // Trimming may remove up to two pixels from the top of the extender glyph, so we move it up by two pixels.
+ float offsetToGlyphTop = glyphBounds.y() + 2;
+ LayoutPoint glyphOrigin = LayoutPoint(from.x(), from.y() - offsetToGlyphTop);
+ FloatRect lastPaintedGlyphRect(from, FloatSize());
+
+ while (lastPaintedGlyphRect.maxY() < to.y()) {
+ lastPaintedGlyphRect = paintGlyph(style, info, m_assembly.extension, glyphOrigin, TrimTopAndBottom);
+ glyphOrigin.setY(glyphOrigin.y() + lastPaintedGlyphRect.height());
+
+ // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle
+ // with trimming. In that case we just draw nothing.
+ if (lastPaintedGlyphRect.isEmpty())
+ break;
+ }
+}
+
+void MathOperator::fillWithHorizontalExtensionGlyph(const RenderStyle& style, PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
+{
+ ASSERT(!m_isVertical);
+ ASSERT(m_stretchType == StretchType::GlyphAssembly);
+ ASSERT(m_assembly.extension.isValid());
+ ASSERT(from.x() <= to.x());
+
+ // If there is no space for the extension glyph, we don't need to do anything.
+ if (from.x() == to.x())
+ return;
+
+ GraphicsContextStateSaver stateSaver(info.context());
+
+ // Clipping the extender region here allows us to draw the bottom extender glyph into the
+ // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping.
+ LayoutRect clipBounds = info.rect;
+ clipBounds.shiftXEdgeTo(from.x());
+ clipBounds.shiftMaxXEdgeTo(to.x());
+ info.context().clip(clipBounds);
+
+ // Trimming may remove up to two pixels from the left of the extender glyph, so we move it left by two pixels.
+ float offsetToGlyphLeft = -2;
+ LayoutPoint glyphOrigin = LayoutPoint(from.x() + offsetToGlyphLeft, std::min(from.y(), to.y()));
+ FloatRect lastPaintedGlyphRect(from, FloatSize());
+
+ while (lastPaintedGlyphRect.maxX() < to.x()) {
+ lastPaintedGlyphRect = paintGlyph(style, info, m_assembly.extension, glyphOrigin, TrimLeftAndRight);
+ glyphOrigin.setX(glyphOrigin.x() + lastPaintedGlyphRect.width());
+
+ // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle
+ // with trimming. In that case we just draw nothing.
+ if (lastPaintedGlyphRect.isEmpty())
+ break;
+ }
+}
+
+void MathOperator::paintVerticalGlyphAssembly(const RenderStyle& style, PaintInfo& info, const LayoutPoint& paintOffset)
+{
+ ASSERT(m_isVertical);
+ ASSERT(m_stretchType == StretchType::GlyphAssembly);
+ ASSERT(m_assembly.topOrRight.isValid());
+ ASSERT(m_assembly.bottomOrLeft.isValid());
+
+ // We are positioning the glyphs so that the edge of the tight glyph bounds line up exactly with the edges of our paint box.
+ LayoutPoint operatorTopLeft = paintOffset;
+ operatorTopLeft = ceiledIntPoint(operatorTopLeft);
+ FloatRect topGlyphBounds = boundsForGlyph(m_assembly.topOrRight);
+ LayoutPoint topGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() - topGlyphBounds.y());
+ LayoutRect topGlyphPaintRect = paintGlyph(style, info, m_assembly.topOrRight, topGlyphOrigin, TrimBottom);
+
+ FloatRect bottomGlyphBounds = boundsForGlyph(m_assembly.bottomOrLeft);
+ LayoutPoint bottomGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() + stretchSize() - (bottomGlyphBounds.height() + bottomGlyphBounds.y()));
+ LayoutRect bottomGlyphPaintRect = paintGlyph(style, info, m_assembly.bottomOrLeft, bottomGlyphOrigin, TrimTop);
+
+ if (m_assembly.middle.isValid()) {
+ // Center the glyph origin between the start and end glyph paint extents. Then shift it half the paint height toward the bottom glyph.
+ FloatRect middleGlyphBounds = boundsForGlyph(m_assembly.middle);
+ LayoutPoint middleGlyphOrigin(operatorTopLeft.x(), topGlyphOrigin.y());
+ middleGlyphOrigin.moveBy(LayoutPoint(0, (bottomGlyphPaintRect.y() - topGlyphPaintRect.maxY()) / 2.0));
+ middleGlyphOrigin.moveBy(LayoutPoint(0, middleGlyphBounds.height() / 2.0));
+
+ LayoutRect middleGlyphPaintRect = paintGlyph(style, info, m_assembly.middle, middleGlyphOrigin, TrimTopAndBottom);
+ fillWithVerticalExtensionGlyph(style, info, topGlyphPaintRect.minXMaxYCorner(), middleGlyphPaintRect.minXMinYCorner());
+ fillWithVerticalExtensionGlyph(style, info, middleGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
+ } else
+ fillWithVerticalExtensionGlyph(style, info, topGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
+}
+
+void MathOperator::paintHorizontalGlyphAssembly(const RenderStyle& style, PaintInfo& info, const LayoutPoint& paintOffset)
+{
+ ASSERT(!m_isVertical);
+ ASSERT(m_stretchType == StretchType::GlyphAssembly);
+ ASSERT(m_assembly.bottomOrLeft.isValid());
+ ASSERT(m_assembly.topOrRight.isValid());
+
+ // We are positioning the glyphs so that the edge of the tight glyph bounds line up exactly with the edges of our paint box.
+ LayoutPoint operatorTopLeft = paintOffset;
+ operatorTopLeft = ceiledIntPoint(operatorTopLeft);
+ LayoutPoint leftGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() + m_ascent);
+ LayoutRect leftGlyphPaintRect = paintGlyph(style, info, m_assembly.bottomOrLeft, leftGlyphOrigin, TrimRight);
+
+ FloatRect rightGlyphBounds = boundsForGlyph(m_assembly.topOrRight);
+ LayoutPoint rightGlyphOrigin(operatorTopLeft.x() + m_width - rightGlyphBounds.width(), operatorTopLeft.y() + m_ascent);
+ LayoutRect rightGlyphPaintRect = paintGlyph(style, info, m_assembly.topOrRight, rightGlyphOrigin, TrimLeft);
+
+ LayoutPoint baselineShift(0, m_ascent);
+ if (m_assembly.middle.isValid()) {
+ // Center the glyph origin between the start and end glyph paint extents.
+ LayoutPoint middleGlyphOrigin(operatorTopLeft.x(), leftGlyphOrigin.y());
+ middleGlyphOrigin.moveBy(LayoutPoint((rightGlyphPaintRect.x() - leftGlyphPaintRect.maxX()) / 2.0, 0));
+ LayoutRect middleGlyphPaintRect = paintGlyph(style, info, m_assembly.middle, middleGlyphOrigin, TrimLeftAndRight);
+ fillWithHorizontalExtensionGlyph(style, info, leftGlyphPaintRect.maxXMinYCorner() + baselineShift, middleGlyphPaintRect.minXMinYCorner() + baselineShift);
+ fillWithHorizontalExtensionGlyph(style, info, middleGlyphPaintRect.maxXMinYCorner() + baselineShift, rightGlyphPaintRect.minXMinYCorner() + baselineShift);
+ } else
+ fillWithHorizontalExtensionGlyph(style, info, leftGlyphPaintRect.maxXMinYCorner() + baselineShift, rightGlyphPaintRect.minXMinYCorner() + baselineShift);
+}
+
+}
+
+#endif
Property changes on: trunk/Source/WebCore/rendering/mathml/MathOperator.cpp
___________________________________________________________________
Added: svn:eol-style
Added: trunk/Source/WebCore/rendering/mathml/MathOperator.h (0 => 201854)
--- trunk/Source/WebCore/rendering/mathml/MathOperator.h (rev 0)
+++ trunk/Source/WebCore/rendering/mathml/MathOperator.h 2016-06-09 06:17:40 UTC (rev 201854)
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 Igalia S.L. 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.
+ */
+
+#ifndef MathOperator_h
+#define MathOperator_h
+
+#if ENABLE(MATHML)
+#include "GlyphPage.h"
+#include "LayoutUnit.h"
+#include "OpenTypeMathData.h"
+#include "PaintInfo.h"
+#include <unicode/utypes.h>
+
+namespace WebCore {
+
+class RenderStyle;
+
+class MathOperator {
+public:
+ MathOperator() { }
+ void setOperator(UChar baseCharacter, bool isVertical);
+
+ LayoutUnit italicCorrection() const { return m_italicCorrection; }
+
+ bool isStretched() const { return m_stretchType != StretchType::Unstretched; }
+ void unstretch() { m_stretchType = StretchType::Unstretched; }
+
+ // FIXME: All the code below should be private when it is no longer needed by RenderMathMLOperator (http://webkit.org/b/152244).
+
+ struct GlyphAssemblyData {
+ GlyphData topOrRight;
+ GlyphData extension;
+ GlyphData bottomOrLeft;
+ GlyphData middle;
+ };
+ enum class StretchType { Unstretched, SizeVariant, GlyphAssembly };
+ enum GlyphPaintTrimming {
+ TrimTop,
+ TrimBottom,
+ TrimTopAndBottom,
+ TrimLeft,
+ TrimRight,
+ TrimLeftAndRight
+ };
+
+ LayoutUnit stretchSize() const { return m_isVertical ? m_ascent + m_descent : m_width; };
+ bool getBaseGlyph(const RenderStyle&, GlyphData&) const;
+ void setSizeVariant(const GlyphData&);
+ void setGlyphAssembly(const GlyphAssemblyData&);
+ void calculateDisplayStyleLargeOperator(const RenderStyle&);
+ void calculateStretchyData(const RenderStyle&, float* maximumGlyphWidth, LayoutUnit targetSize = 0);
+ bool calculateGlyphAssemblyFallback(const RenderStyle&, const Vector<OpenTypeMathData::AssemblyPart>&, GlyphAssemblyData&) const;
+
+ LayoutRect paintGlyph(const RenderStyle&, PaintInfo&, const GlyphData&, const LayoutPoint& origin, GlyphPaintTrimming);
+ void fillWithVerticalExtensionGlyph(const RenderStyle&, PaintInfo&, const LayoutPoint& from, const LayoutPoint& to);
+ void fillWithHorizontalExtensionGlyph(const RenderStyle&, PaintInfo&, const LayoutPoint& from, const LayoutPoint& to);
+ void paintVerticalGlyphAssembly(const RenderStyle&, PaintInfo&, const LayoutPoint&);
+ void paintHorizontalGlyphAssembly(const RenderStyle&, PaintInfo&, const LayoutPoint&);
+
+ UChar m_baseCharacter = 0;
+ bool m_isVertical = false;
+ StretchType m_stretchType = StretchType::Unstretched;
+ union {
+ GlyphData m_variant;
+ GlyphAssemblyData m_assembly;
+ };
+ LayoutUnit m_width = 0;
+ LayoutUnit m_ascent = 0;
+ LayoutUnit m_descent = 0;
+ LayoutUnit m_italicCorrection = 0;
+};
+
+}
+
+#endif // ENABLE(MATHML)
+#endif // MathOperator_h
Property changes on: trunk/Source/WebCore/rendering/mathml/MathOperator.h
___________________________________________________________________
Added: svn:eol-style
Modified: trunk/Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp (201853 => 201854)
--- trunk/Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp 2016-06-09 05:43:46 UTC (rev 201853)
+++ trunk/Source/WebCore/rendering/mathml/RenderMathMLOperator.cpp 2016-06-09 06:17:40 UTC (rev 201854)
@@ -45,33 +45,6 @@
using namespace MathMLNames;
-// FIXME: The OpenType MATH table contains information that should override this table (http://wkbug/122297).
-struct StretchyCharacter {
- UChar character;
- UChar topChar;
- UChar extensionChar;
- UChar bottomChar;
- UChar middleChar;
-};
-// The first leftRightPairsCount pairs correspond to left/right fences that can easily be mirrored in RTL.
-static const short leftRightPairsCount = 5;
-static const StretchyCharacter stretchyCharacters[14] = {
- { 0x28 , 0x239b, 0x239c, 0x239d, 0x0 }, // left parenthesis
- { 0x29 , 0x239e, 0x239f, 0x23a0, 0x0 }, // right parenthesis
- { 0x5b , 0x23a1, 0x23a2, 0x23a3, 0x0 }, // left square bracket
- { 0x5d , 0x23a4, 0x23a5, 0x23a6, 0x0 }, // right square bracket
- { 0x7b , 0x23a7, 0x23aa, 0x23a9, 0x23a8 }, // left curly bracket
- { 0x7d , 0x23ab, 0x23aa, 0x23ad, 0x23ac }, // right curly bracket
- { 0x2308, 0x23a1, 0x23a2, 0x23a2, 0x0 }, // left ceiling
- { 0x2309, 0x23a4, 0x23a5, 0x23a5, 0x0 }, // right ceiling
- { 0x230a, 0x23a2, 0x23a2, 0x23a3, 0x0 }, // left floor
- { 0x230b, 0x23a5, 0x23a5, 0x23a6, 0x0 }, // right floor
- { 0x7c , 0x7c, 0x7c, 0x7c, 0x0 }, // vertical bar
- { 0x2016, 0x2016, 0x2016, 0x2016, 0x0 }, // double vertical line
- { 0x2225, 0x2225, 0x2225, 0x2225, 0x0 }, // parallel to
- { 0x222b, 0x2320, 0x23ae, 0x2321, 0x0 } // integral sign
-};
-
RenderMathMLOperator::RenderMathMLOperator(MathMLElement& element, RenderStyle&& style)
: RenderMathMLToken(element, WTFMove(style))
, m_stretchHeightAboveBaseline(0)
@@ -294,8 +267,9 @@
return;
}
+ m_mathOperator.setOperator(m_textContent, m_isVertical);
GlyphData baseGlyph;
- float maximumGlyphWidth = getBaseGlyph(style(), baseGlyph) ? advanceWidthForGlyph(baseGlyph) : 0;
+ float maximumGlyphWidth = m_mathOperator.getBaseGlyph(style(), baseGlyph) ? advanceWidthForGlyph(baseGlyph) : 0;
if (!m_isVertical) {
if (maximumGlyphWidth < stretchSize())
maximumGlyphWidth = stretchSize();
@@ -305,12 +279,12 @@
}
if (isLargeOperatorInDisplayStyle()) {
// Large operators in STIX Word have incorrect advance width, causing misplacement of superscript, so we use the glyph bound instead (http://sourceforge.net/p/stixfonts/tracking/49/).
- calculateDisplayStyleLargeOperator();
- if (m_stretchType == StretchType::SizeVariant)
- maximumGlyphWidth = boundsForGlyph(m_variant).width();
+ m_mathOperator.calculateDisplayStyleLargeOperator(style());
+ if (m_mathOperator.m_stretchType == MathOperator::StretchType::SizeVariant)
+ maximumGlyphWidth = boundsForGlyph(m_mathOperator.m_variant).width();
} else {
// FIXME: some glyphs (e.g. the one for "FRACTION SLASH" in the STIX Math font or large operators) have a width that depends on the height, resulting in large gaps (https://bugs.webkit.org/show_bug.cgi?id=130326).
- calculateStretchyData(&maximumGlyphWidth);
+ m_mathOperator.calculateStretchyData(style(), &maximumGlyphWidth);
}
m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth = m_leadingSpace + maximumGlyphWidth + m_trailingSpace;
}
@@ -374,245 +348,14 @@
updateOperatorProperties();
}
-bool RenderMathMLOperator::getBaseGlyph(const RenderStyle& style, GlyphData& baseGlyph) const
-{
- baseGlyph = style.fontCascade().glyphDataForCharacter(m_textContent, !style.isLeftToRightDirection());
- return baseGlyph.isValid() && baseGlyph.font == &style.fontCascade().primaryFont();
-}
-
-void RenderMathMLOperator::setSizeVariant(const GlyphData& sizeVariant)
-{
- ASSERT(sizeVariant.isValid() && sizeVariant.font->mathData());
- m_stretchType = StretchType::SizeVariant;
- m_variant = sizeVariant;
-}
-
-void RenderMathMLOperator::setGlyphAssembly(const GlyphAssemblyData& assemblyData)
-{
- m_stretchType = StretchType::GlyphAssembly;
- m_assembly = assemblyData;
-}
-
-bool RenderMathMLOperator::calculateGlyphAssemblyFallBack(const Vector<OpenTypeMathData::AssemblyPart>& assemblyParts, GlyphAssemblyData& assemblyData) const
-{
- // The structure of the Open Type Math table is a bit more general than the one currently used by the RenderMathMLOperator code, so we try to fallback in a reasonable way.
- // FIXME: RenderMathMLOperator should support the most general format (https://bugs.webkit.org/show_bug.cgi?id=130327).
- // We use the approach of the copyComponents function in github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py
-
- // We count the number of non extender pieces.
- int nonExtenderCount = 0;
- for (auto& part : assemblyParts) {
- if (!part.isExtender)
- nonExtenderCount++;
- }
- if (nonExtenderCount > 3)
- return false; // This is not supported: there are too many pieces.
-
- // We now browse the list of pieces from left to right for horizontal operators and from bottom to top for vertical operators.
- enum PartType {
- Start,
- ExtenderBetweenStartAndMiddle,
- Middle,
- ExtenderBetweenMiddleAndEnd,
- End,
- None
- };
- PartType expectedPartType = Start;
- assemblyData.extension.glyph = 0;
- assemblyData.middle.glyph = 0;
- for (auto& part : assemblyParts) {
- if (nonExtenderCount < 3) {
- // If we only have at most two non-extenders then we skip the middle glyph.
- if (expectedPartType == ExtenderBetweenStartAndMiddle)
- expectedPartType = ExtenderBetweenMiddleAndEnd;
- else if (expectedPartType == Middle)
- expectedPartType = End;
- }
- if (part.isExtender) {
- if (!assemblyData.extension.glyph)
- assemblyData.extension.glyph = part.glyph; // We copy the extender part.
- else if (assemblyData.extension.glyph != part.glyph)
- return false; // This is not supported: the assembly has different extenders.
-
- switch (expectedPartType) {
- case Start:
- // We ignore the left/bottom part.
- expectedPartType = ExtenderBetweenStartAndMiddle;
- continue;
- case Middle:
- // We ignore the middle part.
- expectedPartType = ExtenderBetweenMiddleAndEnd;
- continue;
- case End:
- case None:
- // This is not supported: we got an unexpected extender.
- return false;
- case ExtenderBetweenStartAndMiddle:
- case ExtenderBetweenMiddleAndEnd:
- // We ignore multiple consecutive extenders.
- continue;
- }
- }
-
- switch (expectedPartType) {
- case Start:
- // We copy the left/bottom part.
- assemblyData.bottomOrLeft.glyph = part.glyph;
- expectedPartType = ExtenderBetweenStartAndMiddle;
- continue;
- case ExtenderBetweenStartAndMiddle:
- case Middle:
- // We copy the middle part.
- assemblyData.middle.glyph = part.glyph;
- expectedPartType = ExtenderBetweenMiddleAndEnd;
- continue;
- case ExtenderBetweenMiddleAndEnd:
- case End:
- // We copy the right/top part.
- assemblyData.topOrRight.glyph = part.glyph;
- expectedPartType = None;
- continue;
- case None:
- // This is not supported: we got an unexpected non-extender part.
- return false;
- }
- }
-
- if (!assemblyData.extension.glyph)
- return false; // This is not supported: we always assume that we have an extension glyph.
-
- // If we don't have top/right or left/bottom glyphs, we use the extension glyph.
- if (!assemblyData.topOrRight.glyph)
- assemblyData.topOrRight.glyph = assemblyData.extension.glyph;
- if (!assemblyData.bottomOrLeft.glyph)
- assemblyData.bottomOrLeft.glyph = assemblyData.extension.glyph;
-
- assemblyData.topOrRight.font = &style().fontCascade().primaryFont();
- assemblyData.extension.font = assemblyData.topOrRight.font;
- assemblyData.bottomOrLeft.font = assemblyData.topOrRight.font;
- assemblyData.middle.font = assemblyData.middle.glyph ? assemblyData.topOrRight.font : nullptr;
-
- return true;
-}
-
-void RenderMathMLOperator::calculateDisplayStyleLargeOperator()
-{
- ASSERT(m_isVertical && isLargeOperatorInDisplayStyle());
-
- GlyphData baseGlyph;
- if (!getBaseGlyph(style(), baseGlyph) || !baseGlyph.font->mathData())
- return;
-
- Vector<Glyph> sizeVariants;
- Vector<OpenTypeMathData::AssemblyPart> assemblyParts;
-
- // The value of displayOperatorMinHeight is sometimes too small, so we ensure that it is at least \sqrt{2} times the size of the base glyph.
- float displayOperatorMinHeight = std::max(baseGlyph.font->boundsForGlyph(baseGlyph.glyph).height() * sqrtOfTwoFloat, baseGlyph.font->mathData()->getMathConstant(*baseGlyph.font, OpenTypeMathData::DisplayOperatorMinHeight));
-
- baseGlyph.font->mathData()->getMathVariants(baseGlyph.glyph, true, sizeVariants, assemblyParts);
-
- // We choose the first size variant that is larger than the expected displayOperatorMinHeight and otherwise fallback to the largest variant.
- for (auto& sizeVariant : sizeVariants) {
- GlyphData glyphData(sizeVariant, baseGlyph.font);
- setSizeVariant(glyphData);
- m_italicCorrection = glyphData.font->mathData()->getItalicCorrection(*glyphData.font, glyphData.glyph);
- if (heightForGlyph(glyphData) >= displayOperatorMinHeight)
- break;
- }
-}
-
-void RenderMathMLOperator::calculateStretchyData(float* maximumGlyphWidth, LayoutUnit targetSize)
-{
- ASSERT(!maximumGlyphWidth || m_isVertical);
-
- GlyphData baseGlyph;
- if (!getBaseGlyph(style(), baseGlyph))
- return;
-
- if (!maximumGlyphWidth) {
- // We do not stretch if the base glyph is large enough.
- float baseSize = m_isVertical ? heightForGlyph(baseGlyph) : advanceWidthForGlyph(baseGlyph);
- if (targetSize <= baseSize)
- return;
- }
-
- GlyphAssemblyData assemblyData;
- if (baseGlyph.font->mathData()) {
- Vector<Glyph> sizeVariants;
- Vector<OpenTypeMathData::AssemblyPart> assemblyParts;
- baseGlyph.font->mathData()->getMathVariants(baseGlyph.glyph, m_isVertical, sizeVariants, assemblyParts);
- // We verify the size variants.
- for (auto& sizeVariant : sizeVariants) {
- GlyphData glyphData(sizeVariant, baseGlyph.font);
- if (maximumGlyphWidth)
- *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceWidthForGlyph(glyphData));
- else {
- setSizeVariant(glyphData);
- float size = m_isVertical ? heightForGlyph(glyphData) : advanceWidthForGlyph(glyphData);
- if (size >= targetSize)
- return;
- }
- }
-
- // We verify if there is a construction.
- if (!calculateGlyphAssemblyFallBack(assemblyParts, assemblyData))
- return;
- } else {
- if (!m_isVertical)
- return;
-
- // If the font does not have a MATH table, we fallback to the Unicode-only constructions.
- const StretchyCharacter* stretchyCharacter = nullptr;
- const unsigned maxIndex = WTF_ARRAY_LENGTH(stretchyCharacters);
- for (unsigned index = 0; index < maxIndex; ++index) {
- if (stretchyCharacters[index].character == m_textContent) {
- stretchyCharacter = &stretchyCharacters[index];
- if (!style().isLeftToRightDirection() && index < leftRightPairsCount * 2) {
- // If we are in right-to-left direction we select the mirrored form by adding -1 or +1 according to the parity of index.
- index += index % 2 ? -1 : 1;
- }
- break;
- }
- }
-
- // If we didn't find a stretchy character set for this character, we don't know how to stretch it.
- if (!stretchyCharacter)
- return;
-
- // We convert the list of Unicode characters into a list of glyph data.
- assemblyData.topOrRight = style().fontCascade().glyphDataForCharacter(stretchyCharacter->topChar, false);
- assemblyData.extension = style().fontCascade().glyphDataForCharacter(stretchyCharacter->extensionChar, false);
- assemblyData.bottomOrLeft = style().fontCascade().glyphDataForCharacter(stretchyCharacter->bottomChar, false);
- assemblyData.middle = stretchyCharacter->middleChar ? style().fontCascade().glyphDataForCharacter(stretchyCharacter->middleChar, false) : GlyphData();
- }
-
- // If we are measuring the maximum width, verify each component.
- if (maximumGlyphWidth) {
- *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceWidthForGlyph(assemblyData.topOrRight));
- *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceWidthForGlyph(assemblyData.extension));
- *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceWidthForGlyph(assemblyData.middle));
- *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceWidthForGlyph(assemblyData.bottomOrLeft));
- return;
- }
-
- // We ensure that the size is large enough to avoid glyph overlaps.
- float minSize = m_isVertical ?
- heightForGlyph(assemblyData.topOrRight) + heightForGlyph(assemblyData.middle) + heightForGlyph(assemblyData.bottomOrLeft)
- : advanceWidthForGlyph(assemblyData.bottomOrLeft) + advanceWidthForGlyph(assemblyData.middle) + advanceWidthForGlyph(assemblyData.topOrRight);
- if (minSize > targetSize)
- return;
-
- setGlyphAssembly(assemblyData);
-}
-
void RenderMathMLOperator::updateStyle()
{
ASSERT(firstChild());
if (!firstChild())
return;
- m_stretchType = StretchType::Unstretched;
- m_italicCorrection = 0;
+ m_mathOperator.unstretch();
+ m_mathOperator.m_italicCorrection = 0;
// We add spacing around the operator.
// FIXME: The spacing should be added to the whole embellished operator (https://bugs.webkit.org/show_bug.cgi?id=124831).
// FIXME: The spacing should only be added inside (perhaps inferred) mrow (http://www.w3.org/TR/MathML/chapter3.html#presm.opspacing).
@@ -626,25 +369,26 @@
if (!shouldAllowStretching())
return;
+ m_mathOperator.setOperator(m_textContent, m_isVertical);
if (m_isVertical && isLargeOperatorInDisplayStyle())
- calculateDisplayStyleLargeOperator();
+ m_mathOperator.calculateDisplayStyleLargeOperator(style());
else {
- calculateStretchyData(nullptr, stretchSize());
- if (m_stretchType == StretchType::Unstretched)
+ m_mathOperator.calculateStretchyData(style(), nullptr, stretchSize());
+ if (!m_mathOperator.isStretched())
return;
}
- if (m_isVertical && m_stretchType == StretchType::SizeVariant) {
+ if (m_isVertical && m_mathOperator.m_stretchType == MathOperator::StretchType::SizeVariant) {
// We resize the operator to match the one of the size variant.
if (isLargeOperatorInDisplayStyle()) {
// The stretch size is actually not involved in the selection of the size variant in findDisplayStyleLargeOperator.
// We simply use the height and depth of the selected size variant glyph.
- FloatRect glyphBounds = boundsForGlyph(m_variant);
+ FloatRect glyphBounds = boundsForGlyph(m_mathOperator.m_variant);
m_stretchHeightAboveBaseline = -glyphBounds.y();
m_stretchDepthBelowBaseline = glyphBounds.maxY();
} else {
// We rescale the height and depth proportionately.
- float variantSize = heightForGlyph(m_variant);
+ float variantSize = heightForGlyph(m_mathOperator.m_variant);
float size = stretchSize();
float aspect = size > 0 ? variantSize / size : 1.0;
m_stretchHeightAboveBaseline *= aspect;
@@ -653,30 +397,30 @@
}
if (!m_isVertical) {
- if (m_stretchType == StretchType::SizeVariant) {
- FloatRect glyphBounds = boundsForGlyph(m_variant);
+ if (m_mathOperator.m_stretchType == MathOperator::StretchType::SizeVariant) {
+ FloatRect glyphBounds = boundsForGlyph(m_mathOperator.m_variant);
m_stretchHeightAboveBaseline = -glyphBounds.y();
m_stretchDepthBelowBaseline = glyphBounds.maxY();
- m_stretchWidth = advanceWidthForGlyph(m_variant);
- } else if (m_stretchType == StretchType::GlyphAssembly) {
+ m_stretchWidth = advanceWidthForGlyph(m_mathOperator.m_variant);
+ } else if (m_mathOperator.m_stretchType == MathOperator::StretchType::GlyphAssembly) {
FloatRect glyphBounds;
m_stretchHeightAboveBaseline = 0;
m_stretchDepthBelowBaseline = 0;
- glyphBounds = boundsForGlyph(m_assembly.bottomOrLeft);
+ glyphBounds = boundsForGlyph(m_mathOperator.m_assembly.bottomOrLeft);
m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
- glyphBounds = boundsForGlyph(m_assembly.topOrRight);
+ glyphBounds = boundsForGlyph(m_mathOperator.m_assembly.topOrRight);
m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
- glyphBounds = boundsForGlyph(m_assembly.extension);
+ glyphBounds = boundsForGlyph(m_mathOperator.m_assembly.extension);
m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
- if (m_assembly.middle.isValid()) {
- glyphBounds = boundsForGlyph(m_assembly.middle);
+ if (m_mathOperator.m_assembly.middle.isValid()) {
+ glyphBounds = boundsForGlyph(m_mathOperator.m_assembly.middle);
m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
}
@@ -686,237 +430,54 @@
Optional<int> RenderMathMLOperator::firstLineBaseline() const
{
- if (m_stretchType != StretchType::Unstretched)
+ if (m_mathOperator.isStretched())
return Optional<int>(m_stretchHeightAboveBaseline);
return RenderMathMLToken::firstLineBaseline();
}
void RenderMathMLOperator::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
{
- if (m_stretchType != StretchType::Unstretched)
+ if (m_mathOperator.isStretched())
logicalHeight = m_stretchHeightAboveBaseline + m_stretchDepthBelowBaseline;
RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
}
-LayoutRect RenderMathMLOperator::paintGlyph(PaintInfo& info, const GlyphData& data, const LayoutPoint& origin, GlyphPaintTrimming trim)
-{
- FloatRect glyphBounds = boundsForGlyph(data);
-
- LayoutRect glyphPaintRect(origin, LayoutSize(glyphBounds.x() + glyphBounds.width(), glyphBounds.height()));
- glyphPaintRect.setY(origin.y() + glyphBounds.y());
-
- // In order to have glyphs fit snugly with one another we snap the connecting edges to pixel boundaries
- // and trim off one pixel. The pixel trim is to account for fonts that have edge pixels that have less
- // than full coverage. These edge pixels can introduce small seams between connected glyphs.
- FloatRect clipBounds = info.rect;
- switch (trim) {
- case TrimTop:
- glyphPaintRect.shiftYEdgeTo(glyphPaintRect.y().ceil() + 1);
- clipBounds.shiftYEdgeTo(glyphPaintRect.y());
- break;
- case TrimBottom:
- glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1);
- clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
- break;
- case TrimTopAndBottom:
- glyphPaintRect.shiftYEdgeTo(glyphPaintRect.y().ceil() + 1);
- glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1);
- clipBounds.shiftYEdgeTo(glyphPaintRect.y());
- clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
- break;
- case TrimLeft:
- glyphPaintRect.shiftXEdgeTo(glyphPaintRect.x().ceil() + 1);
- clipBounds.shiftXEdgeTo(glyphPaintRect.x());
- break;
- case TrimRight:
- glyphPaintRect.shiftMaxXEdgeTo(glyphPaintRect.maxX().floor() - 1);
- clipBounds.shiftMaxXEdgeTo(glyphPaintRect.maxX());
- break;
- case TrimLeftAndRight:
- glyphPaintRect.shiftXEdgeTo(glyphPaintRect.x().ceil() + 1);
- glyphPaintRect.shiftMaxXEdgeTo(glyphPaintRect.maxX().floor() - 1);
- clipBounds.shiftXEdgeTo(glyphPaintRect.x());
- clipBounds.shiftMaxXEdgeTo(glyphPaintRect.maxX());
- }
-
- // Clipping the enclosing IntRect avoids any potential issues at joined edges.
- GraphicsContextStateSaver stateSaver(info.context());
- info.context().clip(clipBounds);
-
- GlyphBuffer buffer;
- buffer.add(data.glyph, data.font, advanceWidthForGlyph(data));
- info.context().drawGlyphs(style().fontCascade(), *data.font, buffer, 0, 1, origin);
-
- return glyphPaintRect;
-}
-
-void RenderMathMLOperator::fillWithVerticalExtensionGlyph(PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
-{
- ASSERT(m_isVertical);
- ASSERT(m_stretchType == StretchType::GlyphAssembly);
- ASSERT(m_assembly.extension.isValid());
- ASSERT(from.y() <= to.y());
-
- // If there is no space for the extension glyph, we don't need to do anything.
- if (from.y() == to.y())
- return;
-
- GraphicsContextStateSaver stateSaver(info.context());
-
- FloatRect glyphBounds = boundsForGlyph(m_assembly.extension);
-
- // Clipping the extender region here allows us to draw the bottom extender glyph into the
- // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping.
- LayoutRect clipBounds = info.rect;
- clipBounds.shiftYEdgeTo(from.y());
- clipBounds.shiftMaxYEdgeTo(to.y());
- info.context().clip(clipBounds);
-
- // Trimming may remove up to two pixels from the top of the extender glyph, so we move it up by two pixels.
- float offsetToGlyphTop = glyphBounds.y() + 2;
- LayoutPoint glyphOrigin = LayoutPoint(from.x(), from.y() - offsetToGlyphTop);
- FloatRect lastPaintedGlyphRect(from, FloatSize());
-
- while (lastPaintedGlyphRect.maxY() < to.y()) {
- lastPaintedGlyphRect = paintGlyph(info, m_assembly.extension, glyphOrigin, TrimTopAndBottom);
- glyphOrigin.setY(glyphOrigin.y() + lastPaintedGlyphRect.height());
-
- // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle
- // with trimming. In that case we just draw nothing.
- if (lastPaintedGlyphRect.isEmpty())
- break;
- }
-}
-
-void RenderMathMLOperator::fillWithHorizontalExtensionGlyph(PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
-{
- ASSERT(!m_isVertical);
- ASSERT(m_stretchType == StretchType::GlyphAssembly);
- ASSERT(m_assembly.extension.isValid());
- ASSERT(from.x() <= to.x());
-
- // If there is no space for the extension glyph, we don't need to do anything.
- if (from.x() == to.x())
- return;
-
- GraphicsContextStateSaver stateSaver(info.context());
-
- // Clipping the extender region here allows us to draw the bottom extender glyph into the
- // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping.
- LayoutRect clipBounds = info.rect;
- clipBounds.shiftXEdgeTo(from.x());
- clipBounds.shiftMaxXEdgeTo(to.x());
- info.context().clip(clipBounds);
-
- // Trimming may remove up to two pixels from the left of the extender glyph, so we move it left by two pixels.
- float offsetToGlyphLeft = -2;
- LayoutPoint glyphOrigin = LayoutPoint(from.x() + offsetToGlyphLeft, std::min(from.y(), to.y()) + m_stretchHeightAboveBaseline);
- FloatRect lastPaintedGlyphRect(from, FloatSize());
-
- while (lastPaintedGlyphRect.maxX() < to.x()) {
- lastPaintedGlyphRect = paintGlyph(info, m_assembly.extension, glyphOrigin, TrimLeftAndRight);
- glyphOrigin.setX(glyphOrigin.x() + lastPaintedGlyphRect.width());
-
- // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle
- // with trimming. In that case we just draw nothing.
- if (lastPaintedGlyphRect.isEmpty())
- break;
- }
-}
-
void RenderMathMLOperator::paint(PaintInfo& info, const LayoutPoint& paintOffset)
{
RenderMathMLToken::paint(info, paintOffset);
- if (info.context().paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE || m_stretchType == StretchType::Unstretched)
+ if (info.context().paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE || !m_mathOperator.isStretched())
return;
GraphicsContextStateSaver stateSaver(info.context());
info.context().setFillColor(style().visitedDependentColor(CSSPropertyColor));
- if (m_stretchType == StretchType::SizeVariant) {
- ASSERT(m_variant.isValid());
+ if (m_mathOperator.m_stretchType == MathOperator::StretchType::SizeVariant) {
+ ASSERT(m_mathOperator.m_variant.isValid());
GlyphBuffer buffer;
- buffer.add(m_variant.glyph, m_variant.font, advanceWidthForGlyph(m_variant));
+ buffer.add(m_mathOperator.m_variant.glyph, m_mathOperator.m_variant.font, advanceWidthForGlyph(m_mathOperator.m_variant));
LayoutPoint operatorTopLeft = ceiledIntPoint(paintOffset + location());
- FloatRect glyphBounds = boundsForGlyph(m_variant);
+ FloatRect glyphBounds = boundsForGlyph(m_mathOperator.m_variant);
LayoutPoint operatorOrigin(operatorTopLeft.x(), operatorTopLeft.y() - glyphBounds.y());
- info.context().drawGlyphs(style().fontCascade(), *m_variant.font, buffer, 0, 1, operatorOrigin);
+ info.context().drawGlyphs(style().fontCascade(), *m_mathOperator.m_variant.font, buffer, 0, 1, operatorOrigin);
return;
}
+ m_mathOperator.m_ascent = m_stretchHeightAboveBaseline;
+ m_mathOperator.m_descent = m_stretchDepthBelowBaseline;
+ m_mathOperator.m_width = logicalWidth();
+ LayoutPoint operatorTopLeft = paintOffset + location();
+ operatorTopLeft.move(style().isLeftToRightDirection() ? m_leadingSpace : m_trailingSpace, 0);
if (m_isVertical)
- paintVerticalGlyphAssembly(info, paintOffset);
+ m_mathOperator.paintVerticalGlyphAssembly(style(), info, operatorTopLeft);
else
- paintHorizontalGlyphAssembly(info, paintOffset);
+ m_mathOperator.paintHorizontalGlyphAssembly(style(), info, operatorTopLeft);
}
-void RenderMathMLOperator::paintVerticalGlyphAssembly(PaintInfo& info, const LayoutPoint& paintOffset)
-{
- ASSERT(m_isVertical);
- ASSERT(m_stretchType == StretchType::GlyphAssembly);
- ASSERT(m_assembly.topOrRight.isValid());
- ASSERT(m_assembly.bottomOrLeft.isValid());
-
- // We are positioning the glyphs so that the edge of the tight glyph bounds line up exactly with the edges of our paint box.
- LayoutPoint operatorTopLeft = paintOffset + location();
- operatorTopLeft.move(style().isLeftToRightDirection() ? m_leadingSpace : m_trailingSpace, 0);
- operatorTopLeft = ceiledIntPoint(operatorTopLeft);
- FloatRect topGlyphBounds = boundsForGlyph(m_assembly.topOrRight);
- LayoutPoint topGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() - topGlyphBounds.y());
- LayoutRect topGlyphPaintRect = paintGlyph(info, m_assembly.topOrRight, topGlyphOrigin, TrimBottom);
-
- FloatRect bottomGlyphBounds = boundsForGlyph(m_assembly.bottomOrLeft);
- LayoutPoint bottomGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() + offsetHeight() - (bottomGlyphBounds.height() + bottomGlyphBounds.y()));
- LayoutRect bottomGlyphPaintRect = paintGlyph(info, m_assembly.bottomOrLeft, bottomGlyphOrigin, TrimTop);
-
- if (m_assembly.middle.isValid()) {
- // Center the glyph origin between the start and end glyph paint extents. Then shift it half the paint height toward the bottom glyph.
- FloatRect middleGlyphBounds = boundsForGlyph(m_assembly.middle);
- LayoutPoint middleGlyphOrigin(operatorTopLeft.x(), topGlyphOrigin.y());
- middleGlyphOrigin.moveBy(LayoutPoint(0, (bottomGlyphPaintRect.y() - topGlyphPaintRect.maxY()) / 2.0));
- middleGlyphOrigin.moveBy(LayoutPoint(0, middleGlyphBounds.height() / 2.0));
-
- LayoutRect middleGlyphPaintRect = paintGlyph(info, m_assembly.middle, middleGlyphOrigin, TrimTopAndBottom);
- fillWithVerticalExtensionGlyph(info, topGlyphPaintRect.minXMaxYCorner(), middleGlyphPaintRect.minXMinYCorner());
- fillWithVerticalExtensionGlyph(info, middleGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
- } else
- fillWithVerticalExtensionGlyph(info, topGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
-}
-
-void RenderMathMLOperator::paintHorizontalGlyphAssembly(PaintInfo& info, const LayoutPoint& paintOffset)
-{
- ASSERT(!m_isVertical);
- ASSERT(m_stretchType == StretchType::GlyphAssembly);
- ASSERT(m_assembly.bottomOrLeft.isValid());
- ASSERT(m_assembly.topOrRight.isValid());
-
- // We are positioning the glyphs so that the edge of the tight glyph bounds line up exactly with the edges of our paint box.
- LayoutPoint operatorTopLeft = paintOffset + location();
- operatorTopLeft.move(m_leadingSpace, 0);
- operatorTopLeft = ceiledIntPoint(operatorTopLeft);
- LayoutPoint leftGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() + m_stretchHeightAboveBaseline);
- LayoutRect leftGlyphPaintRect = paintGlyph(info, m_assembly.bottomOrLeft, leftGlyphOrigin, TrimRight);
-
- FloatRect rightGlyphBounds = boundsForGlyph(m_assembly.topOrRight);
- LayoutPoint rightGlyphOrigin(operatorTopLeft.x() + offsetWidth() - rightGlyphBounds.width(), operatorTopLeft.y() + m_stretchHeightAboveBaseline);
- LayoutRect rightGlyphPaintRect = paintGlyph(info, m_assembly.topOrRight, rightGlyphOrigin, TrimLeft);
-
- if (m_assembly.middle.isValid()) {
- // Center the glyph origin between the start and end glyph paint extents.
- LayoutPoint middleGlyphOrigin(operatorTopLeft.x(), leftGlyphOrigin.y());
- middleGlyphOrigin.moveBy(LayoutPoint((rightGlyphPaintRect.x() - leftGlyphPaintRect.maxX()) / 2.0, 0));
- LayoutRect middleGlyphPaintRect = paintGlyph(info, m_assembly.middle, middleGlyphOrigin, TrimLeftAndRight);
- fillWithHorizontalExtensionGlyph(info, leftGlyphPaintRect.maxXMinYCorner(), middleGlyphPaintRect.minXMinYCorner());
- fillWithHorizontalExtensionGlyph(info, middleGlyphPaintRect.maxXMinYCorner(), rightGlyphPaintRect.minXMinYCorner());
- } else
- fillWithHorizontalExtensionGlyph(info, leftGlyphPaintRect.maxXMinYCorner(), rightGlyphPaintRect.minXMinYCorner());
-}
-
void RenderMathMLOperator::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
{
// We skip painting for invisible operators too to avoid some "missing character" glyph to appear if appropriate math fonts are not available.
- if (m_stretchType != StretchType::Unstretched || isInvisibleOperator())
+ if (m_mathOperator.isStretched() || isInvisibleOperator())
return;
RenderMathMLToken::paintChildren(paintInfo, paintOffset, paintInfoForChild, usePrintRect);
}
@@ -931,19 +492,19 @@
// Hence we determine the error in the logical width with respect to the actual width of the glyph(s) used to paint the operator.
LayoutUnit width = logicalWidth();
- if (m_stretchType == StretchType::Unstretched) {
+ if (!m_mathOperator.isStretched()) {
GlyphData data = "" !style().isLeftToRightDirection());
return width - advanceWidthForGlyph(data);
}
- if (m_stretchType == StretchType::SizeVariant)
- return width - advanceWidthForGlyph(m_variant);
+ if (m_mathOperator.m_stretchType == MathOperator::StretchType::SizeVariant)
+ return width - advanceWidthForGlyph(m_mathOperator.m_variant);
- float assemblyWidth = advanceWidthForGlyph(m_assembly.topOrRight);
- assemblyWidth = std::max(assemblyWidth, advanceWidthForGlyph(m_assembly.bottomOrLeft));
- assemblyWidth = std::max(assemblyWidth, advanceWidthForGlyph(m_assembly.extension));
- if (m_assembly.middle.isValid())
- assemblyWidth = std::max(assemblyWidth, advanceWidthForGlyph(m_assembly.middle));
+ float assemblyWidth = advanceWidthForGlyph(m_mathOperator.m_assembly.topOrRight);
+ assemblyWidth = std::max(assemblyWidth, advanceWidthForGlyph(m_mathOperator.m_assembly.bottomOrLeft));
+ assemblyWidth = std::max(assemblyWidth, advanceWidthForGlyph(m_mathOperator.m_assembly.extension));
+ if (m_mathOperator.m_assembly.middle.isValid())
+ assemblyWidth = std::max(assemblyWidth, advanceWidthForGlyph(m_mathOperator.m_assembly.middle));
return width - assemblyWidth;
}
Modified: trunk/Source/WebCore/rendering/mathml/RenderMathMLOperator.h (201853 => 201854)
--- trunk/Source/WebCore/rendering/mathml/RenderMathMLOperator.h 2016-06-09 05:43:46 UTC (rev 201853)
+++ trunk/Source/WebCore/rendering/mathml/RenderMathMLOperator.h 2016-06-09 06:17:40 UTC (rev 201854)
@@ -32,6 +32,7 @@
#include "GlyphPage.h"
#include "MathMLElement.h"
#include "MathMLOperatorDictionary.h"
+#include "MathOperator.h"
#include "OpenTypeMathData.h"
#include "RenderMathMLToken.h"
@@ -51,7 +52,7 @@
// FIXME: The displaystyle property is not implemented (https://bugs.webkit.org/show_bug.cgi?id=118737).
bool isLargeOperatorInDisplayStyle() const { return !hasOperatorFlag(MathMLOperatorDictionary::Stretchy) && hasOperatorFlag(MathMLOperatorDictionary::LargeOp); }
bool isVertical() const { return m_isVertical; }
- LayoutUnit italicCorrection() const { return m_italicCorrection; }
+ LayoutUnit italicCorrection() const { return m_mathOperator.italicCorrection(); }
void styleDidChange(StyleDifference, const RenderStyle* oldStyle) final;
void updateStyle() final;
@@ -73,20 +74,6 @@
UChar textContent() const { return m_textContent; }
private:
- struct GlyphAssemblyData {
- GlyphData topOrRight;
- GlyphData extension;
- GlyphData bottomOrLeft;
- GlyphData middle;
- };
- enum class StretchType { Unstretched, SizeVariant, GlyphAssembly };
- StretchType m_stretchType;
- union {
- GlyphData m_variant;
- GlyphAssemblyData m_assembly;
- };
- LayoutUnit m_italicCorrection;
-
const char* renderName() const override { return isAnonymous() ? "RenderMathMLOperator (anonymous)" : "RenderMathMLOperator"; }
void paintChildren(PaintInfo& forSelf, const LayoutPoint&, PaintInfo& forChild, bool usePrintRect) final;
bool isRenderMathMLOperator() const final { return true; }
@@ -100,27 +87,6 @@
bool shouldAllowStretching() const;
- bool getBaseGlyph(const RenderStyle&, GlyphData&) const;
- void setSizeVariant(const GlyphData&);
- void setGlyphAssembly(const GlyphAssemblyData&);
- bool calculateGlyphAssemblyFallBack(const Vector<OpenTypeMathData::AssemblyPart>&, GlyphAssemblyData&) const;
- void calculateDisplayStyleLargeOperator();
- void calculateStretchyData(float* maximumGlyphWidth, LayoutUnit targetSize = 0);
-
- enum GlyphPaintTrimming {
- TrimTop,
- TrimBottom,
- TrimTopAndBottom,
- TrimLeft,
- TrimRight,
- TrimLeftAndRight
- };
-
- LayoutRect paintGlyph(PaintInfo&, const GlyphData&, const LayoutPoint& origin, GlyphPaintTrimming);
- void fillWithVerticalExtensionGlyph(PaintInfo&, const LayoutPoint& from, const LayoutPoint& to);
- void fillWithHorizontalExtensionGlyph(PaintInfo&, const LayoutPoint& from, const LayoutPoint& to);
- void paintVerticalGlyphAssembly(PaintInfo&, const LayoutPoint&);
- void paintHorizontalGlyphAssembly(PaintInfo&, const LayoutPoint&);
void setOperatorFlagFromAttribute(MathMLOperatorDictionary::Flag, const QualifiedName&);
void setOperatorFlagFromAttributeValue(MathMLOperatorDictionary::Flag, const AtomicString& attributeValue);
void setOperatorPropertiesFromOpDictEntry(const MathMLOperatorDictionary::Entry*);
@@ -137,6 +103,7 @@
LayoutUnit m_trailingSpace;
LayoutUnit m_minSize;
LayoutUnit m_maxSize;
+ MathOperator m_mathOperator;
};
} // namespace WebCore