Replace WTF::move with WTFMove
[WebKit-https.git] / Source / WebCore / rendering / mathml / RenderMathMLOperator.cpp
1 /*
2  * Copyright (C) 2010 Alex Milowski (alex@milowski.com). All rights reserved.
3  * Copyright (C) 2010 Fran├žois Sausset (sausset@gmail.com). All rights reserved.
4  * Copyright (C) 2013 Igalia S.L.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29
30 #if ENABLE(MATHML)
31
32 #include "RenderMathMLOperator.h"
33
34 #include "FontSelector.h"
35 #include "MathMLNames.h"
36 #include "PaintInfo.h"
37 #include "RenderBlockFlow.h"
38 #include "RenderText.h"
39 #include "ScaleTransformOperation.h"
40 #include "TransformOperations.h"
41 #include <wtf/MathExtras.h>
42 #include <wtf/unicode/CharacterNames.h>
43
44 namespace WebCore {
45     
46 using namespace MathMLNames;
47
48 // FIXME: The OpenType MATH table contains information that should override this table (http://wkbug/122297).
49 struct StretchyCharacter {
50     UChar character;
51     UChar topChar;
52     UChar extensionChar;
53     UChar bottomChar;
54     UChar middleChar;
55 };
56 // The first leftRightPairsCount pairs correspond to left/right fences that can easily be mirrored in RTL.
57 static const short leftRightPairsCount = 5;
58 static const StretchyCharacter stretchyCharacters[14] = {
59     { 0x28  , 0x239b, 0x239c, 0x239d, 0x0    }, // left parenthesis
60     { 0x29  , 0x239e, 0x239f, 0x23a0, 0x0    }, // right parenthesis
61     { 0x5b  , 0x23a1, 0x23a2, 0x23a3, 0x0    }, // left square bracket
62     { 0x5d  , 0x23a4, 0x23a5, 0x23a6, 0x0    }, // right square bracket
63     { 0x7b  , 0x23a7, 0x23aa, 0x23a9, 0x23a8 }, // left curly bracket
64     { 0x7d  , 0x23ab, 0x23aa, 0x23ad, 0x23ac }, // right curly bracket
65     { 0x2308, 0x23a1, 0x23a2, 0x23a2, 0x0    }, // left ceiling
66     { 0x2309, 0x23a4, 0x23a5, 0x23a5, 0x0    }, // right ceiling
67     { 0x230a, 0x23a2, 0x23a2, 0x23a3, 0x0    }, // left floor
68     { 0x230b, 0x23a5, 0x23a5, 0x23a6, 0x0    }, // right floor
69     { 0x7c  , 0x7c,   0x7c,   0x7c,   0x0    }, // vertical bar
70     { 0x2016, 0x2016, 0x2016, 0x2016, 0x0    }, // double vertical line
71     { 0x2225, 0x2225, 0x2225, 0x2225, 0x0    }, // parallel to
72     { 0x222b, 0x2320, 0x23ae, 0x2321, 0x0    } // integral sign
73 };
74
75 RenderMathMLOperator::RenderMathMLOperator(MathMLElement& element, Ref<RenderStyle>&& style)
76     : RenderMathMLToken(element, WTFMove(style))
77     , m_stretchHeightAboveBaseline(0)
78     , m_stretchDepthBelowBaseline(0)
79     , m_textContent(0)
80     , m_isVertical(true)
81 {
82     updateTokenContent();
83 }
84
85 RenderMathMLOperator::RenderMathMLOperator(Document& document, Ref<RenderStyle>&& style, const String& operatorString, MathMLOperatorDictionary::Form form, unsigned short flags)
86     : RenderMathMLToken(document, WTFMove(style))
87     , m_stretchHeightAboveBaseline(0)
88     , m_stretchDepthBelowBaseline(0)
89     , m_textContent(0)
90     , m_isVertical(true)
91     , m_operatorForm(form)
92     , m_operatorFlags(flags)
93 {
94     updateTokenContent(operatorString);
95 }
96
97 void RenderMathMLOperator::setOperatorFlagAndScheduleLayoutIfNeeded(MathMLOperatorDictionary::Flag flag, const AtomicString& attributeValue)
98 {
99     unsigned short oldOperatorFlags = m_operatorFlags;
100
101     setOperatorFlagFromAttributeValue(flag, attributeValue);
102
103     if (oldOperatorFlags != m_operatorFlags)
104         setNeedsLayoutAndPrefWidthsRecalc();
105 }
106
107 void RenderMathMLOperator::setOperatorFlagFromAttribute(MathMLOperatorDictionary::Flag flag, const QualifiedName& name)
108 {
109     setOperatorFlagFromAttributeValue(flag, element().fastGetAttribute(name));
110 }
111
112 void RenderMathMLOperator::setOperatorFlagFromAttributeValue(MathMLOperatorDictionary::Flag flag, const AtomicString& attributeValue)
113 {
114     ASSERT(!isAnonymous());
115
116     if (attributeValue == "true")
117         m_operatorFlags |= flag;
118     else if (attributeValue == "false")
119         m_operatorFlags &= ~flag;
120     // We ignore absent or invalid attributes.
121 }
122
123 void RenderMathMLOperator::setOperatorPropertiesFromOpDictEntry(const MathMLOperatorDictionary::Entry* entry)
124 {
125     // If this operator is anonymous, we preserve the Fence and Separator properties. This is to handle the case of RenderMathMLFenced.
126     if (isAnonymous())
127         m_operatorFlags = (m_operatorFlags & (MathMLOperatorDictionary::Fence | MathMLOperatorDictionary::Separator)) | entry->flags;
128     else
129         m_operatorFlags = entry->flags;
130
131     // Leading and trailing space is specified as multiple of 1/18em.
132     m_leadingSpace = entry->lspace * style().fontCascade().size() / 18;
133     m_trailingSpace = entry->rspace * style().fontCascade().size() / 18;
134 }
135
136 void RenderMathMLOperator::setOperatorProperties()
137 {
138     // We determine the stretch direction (default is vertical).
139     m_isVertical = MathMLOperatorDictionary::isVertical(m_textContent);
140
141     // We determine the form of the operator.
142     bool explicitForm = true;
143     if (!isAnonymous()) {
144         const AtomicString& form = element().fastGetAttribute(MathMLNames::formAttr);
145         if (form == "prefix")
146             m_operatorForm = MathMLOperatorDictionary::Prefix;
147         else if (form == "infix")
148             m_operatorForm = MathMLOperatorDictionary::Infix;
149         else if (form == "postfix")
150             m_operatorForm = MathMLOperatorDictionary::Postfix;
151         else {
152             // FIXME: We should use more advanced heuristics indicated in the specification to determine the operator form (https://bugs.webkit.org/show_bug.cgi?id=124829).
153             explicitForm = false;
154             if (!element().previousSibling() && element().nextSibling())
155                 m_operatorForm = MathMLOperatorDictionary::Prefix;
156             else if (element().previousSibling() && !element().nextSibling())
157                 m_operatorForm = MathMLOperatorDictionary::Postfix;
158             else
159                 m_operatorForm = MathMLOperatorDictionary::Infix;
160         }
161     }
162
163     // We determine the default values of the operator properties.
164
165     // First we initialize with the default values for unknown operators.
166     if (isAnonymous())
167         m_operatorFlags &= MathMLOperatorDictionary::Fence | MathMLOperatorDictionary::Separator; // This resets all but the Fence and Separator properties.
168     else
169         m_operatorFlags = 0; // This resets all the operator properties.
170     m_leadingSpace = 5 * style().fontCascade().size() / 18; // This sets leading space to "thickmathspace".
171     m_trailingSpace = 5 * style().fontCascade().size() / 18; // This sets trailing space to "thickmathspace".
172     m_minSize = style().fontCascade().size(); // This sets minsize to "1em".
173     m_maxSize = intMaxForLayoutUnit; // This sets maxsize to "infinity".
174
175     if (m_textContent) {
176         // Then we try to find the default values from the operator dictionary.
177         if (const MathMLOperatorDictionary::Entry* entry = MathMLOperatorDictionary::getEntry(m_textContent, m_operatorForm))
178             setOperatorPropertiesFromOpDictEntry(entry);
179         else if (!explicitForm) {
180             // If we did not find the desired operator form and if it was not set explicitely, we use the first one in the following order: Infix, Prefix, Postfix.
181             // This is to handle bad MathML markup without explicit <mrow> delimiters like "<mo>(</mo><mi>a</mi><mo>)</mo><mo>(</mo><mi>b</mi><mo>)</mo>" where the inner parenthesis should not be considered infix.
182             if (const MathMLOperatorDictionary::Entry* entry = MathMLOperatorDictionary::getEntry(m_textContent)) {
183                 m_operatorForm = static_cast<MathMLOperatorDictionary::Form>(entry->form); // We override the form previously determined.
184                 setOperatorPropertiesFromOpDictEntry(entry);
185             }
186         }
187     }
188 #undef MATHML_OPDICT_SIZE
189
190     if (!isAnonymous()) {
191         // Finally, we make the attribute values override the default.
192
193         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Fence, MathMLNames::fenceAttr);
194         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Separator, MathMLNames::separatorAttr);
195         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Stretchy, MathMLNames::stretchyAttr);
196         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Symmetric, MathMLNames::symmetricAttr);
197         setOperatorFlagFromAttribute(MathMLOperatorDictionary::LargeOp, MathMLNames::largeopAttr);
198         setOperatorFlagFromAttribute(MathMLOperatorDictionary::MovableLimits, MathMLNames::movablelimitsAttr);
199         setOperatorFlagFromAttribute(MathMLOperatorDictionary::Accent, MathMLNames::accentAttr);
200
201         parseMathMLLength(element().fastGetAttribute(MathMLNames::lspaceAttr), m_leadingSpace, &style(), false); // FIXME: Negative leading space must be implemented (https://bugs.webkit.org/show_bug.cgi?id=124830).
202         parseMathMLLength(element().fastGetAttribute(MathMLNames::rspaceAttr), m_trailingSpace, &style(), false); // FIXME: Negative trailing space must be implemented (https://bugs.webkit.org/show_bug.cgi?id=124830).
203
204         parseMathMLLength(element().fastGetAttribute(MathMLNames::minsizeAttr), m_minSize, &style(), false);
205         const AtomicString& maxsize = element().fastGetAttribute(MathMLNames::maxsizeAttr);
206         if (maxsize != "infinity")
207             parseMathMLLength(maxsize, m_maxSize, &style(), false);
208     }
209 }
210
211 bool RenderMathMLOperator::isChildAllowed(const RenderObject&, const RenderStyle&) const
212 {
213     return false;
214 }
215
216 void RenderMathMLOperator::stretchTo(LayoutUnit heightAboveBaseline, LayoutUnit depthBelowBaseline)
217 {
218     if (!m_isVertical || (heightAboveBaseline == m_stretchHeightAboveBaseline && depthBelowBaseline == m_stretchDepthBelowBaseline))
219         return;
220
221     m_stretchHeightAboveBaseline = heightAboveBaseline;
222     m_stretchDepthBelowBaseline = depthBelowBaseline;
223
224     setOperatorProperties();
225     if (hasOperatorFlag(MathMLOperatorDictionary::Symmetric)) {
226         // We make the operator stretch symmetrically above and below the axis.
227         // FIXME: We should read the axis from the MATH table (https://bugs.webkit.org/show_bug.cgi?id=122297). For now, we use the same value as in RenderMathMLFraction::firstLineBaseline().
228         LayoutUnit axis = style().fontMetrics().xHeight() / 2;
229         LayoutUnit halfStretchSize = std::max(m_stretchHeightAboveBaseline - axis, m_stretchDepthBelowBaseline + axis);
230         m_stretchHeightAboveBaseline = halfStretchSize + axis;
231         m_stretchDepthBelowBaseline = halfStretchSize - axis;
232     }
233     // We try to honor the minsize/maxsize condition by increasing or decreasing both height and depth proportionately.
234     // The MathML specification does not indicate what to do when maxsize < minsize, so we follow Gecko and make minsize take precedence.
235     LayoutUnit size = stretchSize();
236     float aspect = 1.0;
237     if (size > 0) {
238         if (size < m_minSize)
239             aspect = float(m_minSize) / size;
240         else if (m_maxSize < size)
241             aspect = float(m_maxSize) / size;
242     }
243     m_stretchHeightAboveBaseline *= aspect;
244     m_stretchDepthBelowBaseline *= aspect;
245     updateStyle();
246 }
247
248 void RenderMathMLOperator::stretchTo(LayoutUnit width)
249 {
250     if (m_isVertical || m_stretchWidth == width)
251         return;
252
253     m_stretchWidth = width;
254
255     setOperatorProperties();
256
257     updateStyle();
258 }
259
260 void RenderMathMLOperator::resetStretchSize()
261 {
262     if (m_isVertical) {
263         m_stretchHeightAboveBaseline = 0;
264         m_stretchDepthBelowBaseline = 0;
265     } else
266         m_stretchWidth = 0;
267 }
268
269 FloatRect RenderMathMLOperator::boundsForGlyph(const GlyphData& data) const
270 {
271     return data.font && data.glyph ? data.font->boundsForGlyph(data.glyph) : FloatRect();
272 }
273
274 float RenderMathMLOperator::heightForGlyph(const GlyphData& data) const
275 {
276     return boundsForGlyph(data).height();
277 }
278
279 float RenderMathMLOperator::advanceForGlyph(const GlyphData& data) const
280 {
281     return data.font && data.glyph ? data.font->widthForGlyph(data.glyph) : 0;
282 }
283
284 void RenderMathMLOperator::computePreferredLogicalWidths()
285 {
286     ASSERT(preferredLogicalWidthsDirty());
287
288     setOperatorProperties();
289     if (!shouldAllowStretching()) {
290         RenderMathMLToken::computePreferredLogicalWidths();
291         if (isInvisibleOperator()) {
292             // In some fonts, glyphs for invisible operators have nonzero width. Consequently, we subtract that width here to avoid wide gaps.
293             GlyphData data = style().fontCascade().glyphDataForCharacter(m_textContent, false);
294             float glyphWidth = advanceForGlyph(data);
295             ASSERT(glyphWidth <= m_minPreferredLogicalWidth);
296             m_minPreferredLogicalWidth -= glyphWidth;
297             m_maxPreferredLogicalWidth -= glyphWidth;
298         }
299         return;
300     }
301
302     GlyphData data = style().fontCascade().glyphDataForCharacter(m_textContent, !style().isLeftToRightDirection());
303     float maximumGlyphWidth = advanceForGlyph(data);
304     if (!m_isVertical) {
305         if (maximumGlyphWidth < stretchSize())
306             maximumGlyphWidth = stretchSize();
307         m_maxPreferredLogicalWidth = m_leadingSpace + maximumGlyphWidth + m_trailingSpace;
308         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
309         return;
310     }
311     if (isLargeOperatorInDisplayStyle()) {
312         // 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/).
313         StretchyData largeOperator = getDisplayStyleLargeOperator(m_textContent);
314         if (largeOperator.mode() == DrawSizeVariant)
315             maximumGlyphWidth = boundsForGlyph(largeOperator.variant()).width();
316     } else {
317         // 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).
318         findStretchyData(m_textContent, &maximumGlyphWidth);
319     }
320     m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth = m_leadingSpace + maximumGlyphWidth + m_trailingSpace;
321 }
322
323 void RenderMathMLOperator::rebuildTokenContent(const String& operatorString)
324 {
325     // We collapse the whitespace and replace the hyphens by minus signs.
326     AtomicString textContent = operatorString.stripWhiteSpace().simplifyWhiteSpace().replace(hyphenMinus, minusSign).impl();
327
328     // We destroy the wrapper and rebuild it.
329     // FIXME: Using this RenderText make the text inaccessible to the dumpAsText/selection code (https://bugs.webkit.org/show_bug.cgi?id=125597).
330     if (firstChild())
331         downcast<RenderElement>(*firstChild()).destroy();
332     createWrapperIfNeeded();
333     RenderPtr<RenderText> text = createRenderer<RenderText>(document(), textContent);
334     downcast<RenderElement>(*firstChild()).addChild(text.leakPtr());
335
336     // We verify whether the operator text can be represented by a single UChar.
337     // FIXME: This does not handle surrogate pairs (https://bugs.webkit.org/show_bug.cgi?id=122296).
338     // FIXME: This does not handle <mo> operators with multiple characters (https://bugs.webkit.org/show_bug.cgi?id=124828).
339     m_textContent = textContent.length() == 1 ? textContent[0] : 0;
340     setOperatorProperties();
341     updateStyle();
342     setNeedsLayoutAndPrefWidthsRecalc();
343 }
344
345 void RenderMathMLOperator::updateTokenContent(const String& operatorString)
346 {
347     ASSERT(isAnonymous());
348     rebuildTokenContent(operatorString);
349 }
350
351 void RenderMathMLOperator::updateTokenContent()
352 {
353     ASSERT(!isAnonymous());
354     rebuildTokenContent(element().textContent());
355 }
356
357 void RenderMathMLOperator::updateFromElement()
358 {
359     setOperatorProperties();
360     RenderMathMLToken::updateFromElement();
361 }
362
363 void RenderMathMLOperator::updateOperatorProperties()
364 {
365     setOperatorProperties();
366     if (!isEmpty())
367         updateStyle();
368     setNeedsLayoutAndPrefWidthsRecalc();
369 }
370
371 bool RenderMathMLOperator::shouldAllowStretching() const
372 {
373     return m_textContent && (hasOperatorFlag(MathMLOperatorDictionary::Stretchy) || isLargeOperatorInDisplayStyle());
374 }
375
376 bool RenderMathMLOperator::getGlyphAssemblyFallBack(Vector<OpenTypeMathData::AssemblyPart> assemblyParts, StretchyData& stretchyData) const
377 {
378     GlyphData top;
379     GlyphData extension;
380     GlyphData bottom;
381     GlyphData middle;
382
383     // 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.
384     // FIXME: RenderMathMLOperator should support the most general format (https://bugs.webkit.org/show_bug.cgi?id=130327).
385     // We use the approach of the copyComponents function in github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py
386
387     // We count the number of non extender pieces.
388     int nonExtenderCount = 0;
389     for (auto& part : assemblyParts) {
390         if (!part.isExtender)
391             nonExtenderCount++;
392     }
393     if (nonExtenderCount > 3)
394         return false; // This is not supported: there are too many pieces.
395
396     // We now browse the list of pieces.
397     // 1 = look for a left/bottom glyph
398     // 2 = look for an extender between left/bottom and mid
399     // 4 = look for a middle glyph
400     // 5 = look for an extender between middle and right/top
401     // 5 = look for a right/top glyph
402     // 6 = no more piece expected
403     unsigned state = 1;
404
405     extension.glyph = 0;
406     middle.glyph = 0;
407     for (auto& part : assemblyParts) {
408         if ((state == 2 || state == 3) && nonExtenderCount < 3) {
409             // We do not try to find a middle glyph.
410             state += 2;
411         }
412         if (part.isExtender) {
413             if (!extension.glyph)
414                 extension.glyph = part.glyph;
415             else if (extension.glyph != part.glyph)
416                 return false; // This is not supported: the assembly has different extenders.
417
418             if (state == 1) {
419                 // We ignore left/bottom piece and multiple successive extenders.
420                 state = 2;
421             } else if (state == 3) {
422                 // We ignore middle piece and multiple successive extenders.
423                 state = 4;
424             } else if (state >= 5)
425                 return false; // This is not supported: we got an unexpected extender.
426             continue;
427         }
428
429         if (state == 1) {
430             // We copy the left/bottom part.
431             bottom.glyph = part.glyph;
432             state = 2;
433             continue;
434         }
435
436         if (state == 2 || state == 3) {
437             // We copy the middle part.
438             middle.glyph = part.glyph;
439             state = 4;
440             continue;
441         }
442
443         if (state == 4 || state == 5) {
444             // We copy the right/top part.
445             top.glyph = part.glyph;
446             state = 6;
447         }
448     }
449
450     if (!extension.glyph)
451         return false; // This is not supported: we always assume that we have an extension glyph.
452
453     // If we don't have top/bottom glyphs, we use the extension glyph.
454     if (!top.glyph)
455         top.glyph = extension.glyph;
456     if (!bottom.glyph)
457         bottom.glyph = extension.glyph;
458
459     top.font = &style().fontCascade().primaryFont();
460     extension.font = top.font;
461     bottom.font = top.font;
462     if (middle.glyph)
463         middle.font = top.font;
464
465     stretchyData.setGlyphAssemblyMode(top, extension, bottom, middle);
466
467     return true;
468 }
469
470 RenderMathMLOperator::StretchyData RenderMathMLOperator::getDisplayStyleLargeOperator(UChar character) const
471 {
472     StretchyData data;
473
474     ASSERT(m_isVertical && isLargeOperatorInDisplayStyle());
475
476     const auto& primaryFont = style().fontCascade().primaryFont();
477     GlyphData baseGlyph = style().fontCascade().glyphDataForCharacter(character, !style().isLeftToRightDirection());
478     if (!primaryFont.mathData() || baseGlyph.font != &primaryFont || !baseGlyph.font || !baseGlyph.glyph)
479         return data;
480
481     Vector<Glyph> sizeVariants;
482     Vector<OpenTypeMathData::AssemblyPart> assemblyParts;
483
484     // 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.
485     float displayOperatorMinHeight = std::max(baseGlyph.font->boundsForGlyph(baseGlyph.glyph).height() * sqrtOfTwoFloat, primaryFont.mathData()->getMathConstant(primaryFont, OpenTypeMathData::DisplayOperatorMinHeight));
486
487     primaryFont.mathData()->getMathVariants(baseGlyph.glyph, true, sizeVariants, assemblyParts);
488
489     // We choose the first size variant that is larger than the expected displayOperatorMinHeight and otherwise fallback to the largest variant.
490     for (auto& variant : sizeVariants) {
491         GlyphData sizeVariant;
492         sizeVariant.glyph = variant;
493         sizeVariant.font = &primaryFont;
494         data.setSizeVariantMode(sizeVariant);
495         if (boundsForGlyph(sizeVariant).height() >= displayOperatorMinHeight)
496             return data;
497     }
498     return data;
499 }
500
501 RenderMathMLOperator::StretchyData RenderMathMLOperator::findStretchyData(UChar character, float* maximumGlyphWidth)
502 {
503     ASSERT(!maximumGlyphWidth || m_isVertical);
504
505     StretchyData data;
506     StretchyData assemblyData;
507
508     const auto& primaryFont = style().fontCascade().primaryFont();
509     GlyphData baseGlyph = style().fontCascade().glyphDataForCharacter(character, !style().isLeftToRightDirection());
510     
511     if (primaryFont.mathData() && baseGlyph.font == &primaryFont) {
512         Vector<Glyph> sizeVariants;
513         Vector<OpenTypeMathData::AssemblyPart> assemblyParts;
514         primaryFont.mathData()->getMathVariants(baseGlyph.glyph, m_isVertical, sizeVariants, assemblyParts);
515         // We verify the size variants.
516         for (auto& variant : sizeVariants) {
517             GlyphData sizeVariant;
518             sizeVariant.glyph = variant;
519             sizeVariant.font = &primaryFont;
520             if (maximumGlyphWidth)
521                 *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(sizeVariant));
522             else {
523                 data.setSizeVariantMode(sizeVariant);
524                 float size = m_isVertical ? heightForGlyph(sizeVariant) : advanceForGlyph(sizeVariant);
525                 if (size >= stretchSize()) 
526                     return data;
527             }
528         }
529
530         // We verify if there is a construction.
531         if (!getGlyphAssemblyFallBack(assemblyParts, assemblyData))
532             return data;
533     } else {
534         if (!m_isVertical)
535             return data;
536
537         // If the font does not have a MATH table, we fallback to the Unicode-only constructions.
538         const StretchyCharacter* stretchyCharacter = nullptr;
539         const unsigned maxIndex = WTF_ARRAY_LENGTH(stretchyCharacters);
540         for (unsigned index = 0; index < maxIndex; ++index) {
541             if (stretchyCharacters[index].character == character) {
542                 stretchyCharacter = &stretchyCharacters[index];
543                 if (!style().isLeftToRightDirection() && index < leftRightPairsCount * 2) {
544                     // If we are in right-to-left direction we select the mirrored form by adding -1 or +1 according to the parity of index.
545                     index += index % 2 ? -1 : 1;
546                 }
547                 break;
548             }
549         }
550
551         // If we didn't find a stretchy character set for this character, we don't know how to stretch it.
552         if (!stretchyCharacter)
553             return data;
554
555         // We convert the list of Unicode characters into a list of glyph data.
556         GlyphData top = style().fontCascade().glyphDataForCharacter(stretchyCharacter->topChar, false);
557         GlyphData extension = style().fontCascade().glyphDataForCharacter(stretchyCharacter->extensionChar, false);
558         GlyphData bottom = style().fontCascade().glyphDataForCharacter(stretchyCharacter->bottomChar, false);
559         GlyphData middle;
560         if (stretchyCharacter->middleChar)
561             middle = style().fontCascade().glyphDataForCharacter(stretchyCharacter->middleChar, false);
562         assemblyData.setGlyphAssemblyMode(top, extension, bottom, middle);
563     }
564
565     ASSERT(assemblyData.mode() == DrawGlyphAssembly);
566
567     // If we are measuring the maximum width, verify each component.
568     if (maximumGlyphWidth) {
569         *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(assemblyData.top()));
570         *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(assemblyData.extension()));
571         if (assemblyData.middle().glyph)
572             *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(assemblyData.middle()));
573         *maximumGlyphWidth = std::max(*maximumGlyphWidth, advanceForGlyph(assemblyData.bottom()));
574         return assemblyData;
575     }
576
577     // We ensure that the size is large enough to avoid glyph overlaps.
578     float size;
579     if (m_isVertical) {
580         size = heightForGlyph(assemblyData.top()) + heightForGlyph(assemblyData.bottom());
581         if (assemblyData.middle().glyph)
582             size += heightForGlyph(assemblyData.middle());
583     } else {
584         size = advanceForGlyph(assemblyData.left()) + advanceForGlyph(assemblyData.right());
585         if (assemblyData.middle().glyph)
586             size += advanceForGlyph(assemblyData.middle());
587     }
588     if (size > stretchSize())
589         return data;
590
591     return assemblyData;
592 }
593
594 void RenderMathMLOperator::updateStyle()
595 {
596     ASSERT(firstChild());
597     if (!firstChild())
598         return;
599
600     m_stretchyData.setNormalMode();
601     // We add spacing around the operator.
602     // FIXME: The spacing should be added to the whole embellished operator (https://bugs.webkit.org/show_bug.cgi?id=124831).
603     // FIXME: The spacing should only be added inside (perhaps inferred) mrow (http://www.w3.org/TR/MathML/chapter3.html#presm.opspacing).
604     const auto& wrapper = downcast<RenderElement>(firstChild());
605     auto newStyle = RenderStyle::createAnonymousStyleWithDisplay(&style(), FLEX);
606     newStyle.get().setMarginStart(Length(m_leadingSpace, Fixed));
607     newStyle.get().setMarginEnd(Length(m_trailingSpace, Fixed));
608     wrapper->setStyle(WTFMove(newStyle));
609     wrapper->setNeedsLayoutAndPrefWidthsRecalc();
610
611     if (!shouldAllowStretching())
612         return;
613
614     if (m_isVertical && isLargeOperatorInDisplayStyle())
615         m_stretchyData = getDisplayStyleLargeOperator(m_textContent);
616     else {
617         // We do not stretch if the base glyph is large enough.
618         GlyphData baseGlyph = style().fontCascade().glyphDataForCharacter(m_textContent, !style().isLeftToRightDirection());
619         float baseSize = m_isVertical ? heightForGlyph(baseGlyph) : advanceForGlyph(baseGlyph);
620         if (stretchSize() <= baseSize)
621             return;
622         m_stretchyData = findStretchyData(m_textContent, nullptr);
623     }
624
625     if (m_isVertical && m_stretchyData.mode() == DrawSizeVariant) {
626         // We resize the operator to match the one of the size variant.
627         if (isLargeOperatorInDisplayStyle()) {
628             // The stretch size is actually not involved in the selection of the size variant in getDisplayStyleLargeOperator.
629             // We simply use the height and depth of the selected size variant glyph.
630             FloatRect glyphBounds = boundsForGlyph(m_stretchyData.variant());
631             m_stretchHeightAboveBaseline = -glyphBounds.y();
632             m_stretchDepthBelowBaseline = glyphBounds.maxY();
633         } else {
634             // We rescale the height and depth proportionately.
635             float variantSize = heightForGlyph(m_stretchyData.variant());
636             float size = stretchSize();
637             float aspect = size > 0 ? variantSize / size : 1.0;
638             m_stretchHeightAboveBaseline *= aspect;
639             m_stretchDepthBelowBaseline *= aspect;
640         }
641     }
642
643     if (!m_isVertical) {
644         if (m_stretchyData.mode() == DrawSizeVariant) {
645             FloatRect glyphBounds = boundsForGlyph(m_stretchyData.variant());
646             m_stretchHeightAboveBaseline = -glyphBounds.y();
647             m_stretchDepthBelowBaseline = glyphBounds.maxY();
648             m_stretchWidth = advanceForGlyph(m_stretchyData.variant());
649         } else if (m_stretchyData.mode() == DrawGlyphAssembly) {
650             FloatRect glyphBounds;
651             m_stretchHeightAboveBaseline = 0;
652             m_stretchDepthBelowBaseline = 0;
653
654             glyphBounds = boundsForGlyph(m_stretchyData.left());
655             m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
656             m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
657
658             glyphBounds = boundsForGlyph(m_stretchyData.right());
659             m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
660             m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
661
662             glyphBounds = boundsForGlyph(m_stretchyData.extension());
663             m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
664             m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
665
666             if (m_stretchyData.middle().glyph) {
667                 glyphBounds = boundsForGlyph(m_stretchyData.middle());
668                 m_stretchHeightAboveBaseline = std::max<LayoutUnit>(m_stretchHeightAboveBaseline, -glyphBounds.y());
669                 m_stretchDepthBelowBaseline = std::max<LayoutUnit>(m_stretchDepthBelowBaseline, glyphBounds.maxY());
670             }
671         }
672     }
673 }
674
675 Optional<int> RenderMathMLOperator::firstLineBaseline() const
676 {
677     if (m_stretchyData.mode() != DrawNormal)
678         return Optional<int>(m_stretchHeightAboveBaseline);
679     return RenderMathMLToken::firstLineBaseline();
680 }
681
682 void RenderMathMLOperator::computeLogicalHeight(LayoutUnit logicalHeight, LayoutUnit logicalTop, LogicalExtentComputedValues& computedValues) const
683 {
684     if (m_stretchyData.mode() != DrawNormal)
685         logicalHeight = m_stretchHeightAboveBaseline + m_stretchDepthBelowBaseline;
686     RenderBox::computeLogicalHeight(logicalHeight, logicalTop, computedValues);
687 }
688
689 LayoutRect RenderMathMLOperator::paintGlyph(PaintInfo& info, const GlyphData& data, const LayoutPoint& origin, GlyphPaintTrimming trim)
690 {
691     FloatRect glyphBounds = boundsForGlyph(data);
692
693     LayoutRect glyphPaintRect(origin, LayoutSize(glyphBounds.x() + glyphBounds.width(), glyphBounds.height()));
694     glyphPaintRect.setY(origin.y() + glyphBounds.y());
695
696     // In order to have glyphs fit snugly with one another we snap the connecting edges to pixel boundaries
697     // and trim off one pixel. The pixel trim is to account for fonts that have edge pixels that have less
698     // than full coverage. These edge pixels can introduce small seams between connected glyphs
699     FloatRect clipBounds = info.rect;
700     switch (trim) {
701     case TrimTop:
702         glyphPaintRect.shiftYEdgeTo(glyphPaintRect.y().ceil() + 1);
703         clipBounds.shiftYEdgeTo(glyphPaintRect.y());
704         break;
705     case TrimBottom:
706         glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1);
707         clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
708         break;
709     case TrimTopAndBottom: {
710         LayoutUnit temp = glyphPaintRect.y() + 1;
711         glyphPaintRect.shiftYEdgeTo(temp.ceil());
712         glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1);
713         clipBounds.shiftYEdgeTo(glyphPaintRect.y());
714         clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
715     }
716         break;
717     case TrimLeft:
718         glyphPaintRect.shiftXEdgeTo(glyphPaintRect.x().ceil() + 1);
719         clipBounds.shiftXEdgeTo(glyphPaintRect.x());
720         break;
721     case TrimRight:
722         glyphPaintRect.shiftMaxXEdgeTo(glyphPaintRect.maxX().floor() - 1);
723         clipBounds.shiftMaxXEdgeTo(glyphPaintRect.maxX());
724         break;
725     case TrimLeftAndRight: {
726         LayoutUnit temp = glyphPaintRect.x() + 1;
727         glyphPaintRect.shiftXEdgeTo(temp.ceil());
728         glyphPaintRect.shiftMaxXEdgeTo(glyphPaintRect.maxX().floor() - 1);
729         clipBounds.shiftXEdgeTo(glyphPaintRect.x());
730         clipBounds.shiftMaxXEdgeTo(glyphPaintRect.maxX());
731     }
732     }
733
734     // Clipping the enclosing IntRect avoids any potential issues at joined edges.
735     GraphicsContextStateSaver stateSaver(info.context());
736     info.context().clip(clipBounds);
737
738     GlyphBuffer buffer;
739     buffer.add(data.glyph, data.font, advanceForGlyph(data));
740     info.context().drawGlyphs(style().fontCascade(), *data.font, buffer, 0, 1, origin);
741
742     return glyphPaintRect;
743 }
744
745 void RenderMathMLOperator::fillWithVerticalExtensionGlyph(PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
746 {
747     ASSERT(m_isVertical);
748     ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
749     ASSERT(m_stretchyData.extension().glyph);
750     ASSERT(from.y() <= to.y());
751
752     // If there is no space for the extension glyph, we don't need to do anything.
753     if (from.y() == to.y())
754         return;
755
756     GraphicsContextStateSaver stateSaver(info.context());
757
758     FloatRect glyphBounds = boundsForGlyph(m_stretchyData.extension());
759
760     // Clipping the extender region here allows us to draw the bottom extender glyph into the
761     // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping.
762     LayoutRect clipBounds = info.rect;
763     clipBounds.shiftYEdgeTo(from.y());
764     clipBounds.shiftMaxYEdgeTo(to.y());
765     info.context().clip(clipBounds);
766
767     // Trimming may remove up to two pixels from the top of the extender glyph, so we move it up by two pixels.
768     float offsetToGlyphTop = glyphBounds.y() + 2;
769     LayoutPoint glyphOrigin = LayoutPoint(from.x(), from.y() - offsetToGlyphTop);
770     FloatRect lastPaintedGlyphRect(from, FloatSize());
771
772     while (lastPaintedGlyphRect.maxY() < to.y()) {
773         lastPaintedGlyphRect = paintGlyph(info, m_stretchyData.extension(), glyphOrigin, TrimTopAndBottom);
774         glyphOrigin.setY(glyphOrigin.y() + lastPaintedGlyphRect.height());
775
776         // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle
777         // with trimming. In that case we just draw nothing.
778         if (lastPaintedGlyphRect.isEmpty())
779             break;
780     }
781 }
782
783 void RenderMathMLOperator::fillWithHorizontalExtensionGlyph(PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
784 {
785     ASSERT(!m_isVertical);
786     ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
787     ASSERT(m_stretchyData.extension().glyph);
788     ASSERT(from.x() <= to.x());
789
790     // If there is no space for the extension glyph, we don't need to do anything.
791     if (from.x() == to.x())
792         return;
793
794     GraphicsContextStateSaver stateSaver(info.context());
795
796     // Clipping the extender region here allows us to draw the bottom extender glyph into the
797     // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping.
798     LayoutRect clipBounds = info.rect;
799     clipBounds.shiftXEdgeTo(from.x());
800     clipBounds.shiftMaxXEdgeTo(to.x());
801     info.context().clip(clipBounds);
802
803     // Trimming may remove up to two pixels from the left of the extender glyph, so we move it left by two pixels.
804     float offsetToGlyphLeft = -2;
805     LayoutPoint glyphOrigin = LayoutPoint(from.x() + offsetToGlyphLeft, std::min(from.y(), to.y()) + m_stretchHeightAboveBaseline);
806     FloatRect lastPaintedGlyphRect(from, FloatSize());
807
808     while (lastPaintedGlyphRect.maxX() < to.x()) {
809         lastPaintedGlyphRect = paintGlyph(info, m_stretchyData.extension(), glyphOrigin, TrimLeftAndRight);
810         glyphOrigin.setX(glyphOrigin.x() + lastPaintedGlyphRect.width());
811
812         // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle
813         // with trimming. In that case we just draw nothing.
814         if (lastPaintedGlyphRect.isEmpty())
815             break;
816     }
817 }
818
819 void RenderMathMLOperator::paint(PaintInfo& info, const LayoutPoint& paintOffset)
820 {
821     RenderMathMLToken::paint(info, paintOffset);
822
823     if (info.context().paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE || m_stretchyData.mode() == DrawNormal)
824         return;
825
826     GraphicsContextStateSaver stateSaver(info.context());
827     info.context().setFillColor(style().visitedDependentColor(CSSPropertyColor));
828
829     if (m_stretchyData.mode() == DrawSizeVariant) {
830         ASSERT(m_stretchyData.variant().glyph);
831         GlyphBuffer buffer;
832         buffer.add(m_stretchyData.variant().glyph, m_stretchyData.variant().font, advanceForGlyph(m_stretchyData.variant()));
833         LayoutPoint operatorTopLeft = ceiledIntPoint(paintOffset + location());
834         FloatRect glyphBounds = boundsForGlyph(m_stretchyData.variant());
835         LayoutPoint operatorOrigin(operatorTopLeft.x(), operatorTopLeft.y() - glyphBounds.y());
836         info.context().drawGlyphs(style().fontCascade(), *m_stretchyData.variant().font, buffer, 0, 1, operatorOrigin);
837         return;
838     }
839
840     if (m_isVertical)
841         paintVerticalGlyphAssembly(info, paintOffset);
842     else
843         paintHorizontalGlyphAssembly(info, paintOffset);
844 }
845
846 void RenderMathMLOperator::paintVerticalGlyphAssembly(PaintInfo& info, const LayoutPoint& paintOffset)
847 {
848     ASSERT(m_isVertical);
849     ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
850     ASSERT(m_stretchyData.top().glyph);
851     ASSERT(m_stretchyData.bottom().glyph);
852
853     // We are positioning the glyphs so that the edge of the tight glyph bounds line up exactly with the edges of our paint box.
854     LayoutPoint operatorTopLeft = paintOffset + location();
855     operatorTopLeft.move(style().isLeftToRightDirection() ? m_leadingSpace : m_trailingSpace, 0);
856     operatorTopLeft = ceiledIntPoint(operatorTopLeft);
857     FloatRect topGlyphBounds = boundsForGlyph(m_stretchyData.top());
858     LayoutPoint topGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() - topGlyphBounds.y());
859     LayoutRect topGlyphPaintRect = paintGlyph(info, m_stretchyData.top(), topGlyphOrigin, TrimBottom);
860
861     FloatRect bottomGlyphBounds = boundsForGlyph(m_stretchyData.bottom());
862     LayoutPoint bottomGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() + offsetHeight() - (bottomGlyphBounds.height() + bottomGlyphBounds.y()));
863     LayoutRect bottomGlyphPaintRect = paintGlyph(info, m_stretchyData.bottom(), bottomGlyphOrigin, TrimTop);
864
865     if (m_stretchyData.middle().glyph) {
866         // Center the glyph origin between the start and end glyph paint extents. Then shift it half the paint height toward the bottom glyph.
867         FloatRect middleGlyphBounds = boundsForGlyph(m_stretchyData.middle());
868         LayoutPoint middleGlyphOrigin(operatorTopLeft.x(), topGlyphOrigin.y());
869         middleGlyphOrigin.moveBy(LayoutPoint(0, (bottomGlyphPaintRect.y() - topGlyphPaintRect.maxY()) / 2.0));
870         middleGlyphOrigin.moveBy(LayoutPoint(0, middleGlyphBounds.height() / 2.0));
871
872         LayoutRect middleGlyphPaintRect = paintGlyph(info, m_stretchyData.middle(), middleGlyphOrigin, TrimTopAndBottom);
873         fillWithVerticalExtensionGlyph(info, topGlyphPaintRect.minXMaxYCorner(), middleGlyphPaintRect.minXMinYCorner());
874         fillWithVerticalExtensionGlyph(info, middleGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
875     } else
876         fillWithVerticalExtensionGlyph(info, topGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
877 }
878
879 void RenderMathMLOperator::paintHorizontalGlyphAssembly(PaintInfo& info, const LayoutPoint& paintOffset)
880 {
881     ASSERT(!m_isVertical);
882     ASSERT(m_stretchyData.mode() == DrawGlyphAssembly);
883     ASSERT(m_stretchyData.left().glyph);
884     ASSERT(m_stretchyData.right().glyph);
885
886     // We are positioning the glyphs so that the edge of the tight glyph bounds line up exactly with the edges of our paint box.
887     LayoutPoint operatorTopLeft = paintOffset + location();
888     operatorTopLeft.move(m_leadingSpace, 0);
889     operatorTopLeft = ceiledIntPoint(operatorTopLeft);
890     LayoutPoint leftGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() + m_stretchHeightAboveBaseline);
891     LayoutRect leftGlyphPaintRect = paintGlyph(info, m_stretchyData.left(), leftGlyphOrigin, TrimRight);
892
893     FloatRect rightGlyphBounds = boundsForGlyph(m_stretchyData.right());
894     LayoutPoint rightGlyphOrigin(operatorTopLeft.x() + offsetWidth() - rightGlyphBounds.width(), operatorTopLeft.y() + m_stretchHeightAboveBaseline);
895     LayoutRect rightGlyphPaintRect = paintGlyph(info, m_stretchyData.right(), rightGlyphOrigin, TrimLeft);
896
897     if (m_stretchyData.middle().glyph) {
898         // Center the glyph origin between the start and end glyph paint extents.
899         LayoutPoint middleGlyphOrigin(operatorTopLeft.x(), leftGlyphOrigin.y());
900         middleGlyphOrigin.moveBy(LayoutPoint((rightGlyphPaintRect.x() - leftGlyphPaintRect.maxX()) / 2.0, 0));
901         LayoutRect middleGlyphPaintRect = paintGlyph(info, m_stretchyData.middle(), middleGlyphOrigin, TrimLeftAndRight);
902         fillWithHorizontalExtensionGlyph(info, leftGlyphPaintRect.maxXMinYCorner(), middleGlyphPaintRect.minXMinYCorner());
903         fillWithHorizontalExtensionGlyph(info, middleGlyphPaintRect.maxXMinYCorner(), rightGlyphPaintRect.minXMinYCorner());
904     } else
905         fillWithHorizontalExtensionGlyph(info, leftGlyphPaintRect.maxXMinYCorner(), rightGlyphPaintRect.minXMinYCorner());
906 }
907
908 void RenderMathMLOperator::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
909 {
910     // We skip painting for invisible operators too to avoid some "missing character" glyph to appear if appropriate math fonts are not available.
911     if (m_stretchyData.mode() != DrawNormal || isInvisibleOperator())
912         return;
913     RenderMathMLToken::paintChildren(paintInfo, paintOffset, paintInfoForChild, usePrintRect);
914 }
915
916 LayoutUnit RenderMathMLOperator::trailingSpaceError()
917 {
918     const auto& primaryFont = style().fontCascade().primaryFont();
919     if (!primaryFont.mathData())
920         return 0;
921
922     // For OpenType MATH font, the layout is based on RenderMathOperator for which the preferred width is sometimes overestimated (bug https://bugs.webkit.org/show_bug.cgi?id=130326).
923     // Hence we determine the error in the logical width with respect to the actual width of the glyph(s) used to paint the operator.
924     LayoutUnit width = logicalWidth();
925
926     if (m_stretchyData.mode() == DrawNormal) {
927         GlyphData data = style().fontCascade().glyphDataForCharacter(textContent(), !style().isLeftToRightDirection());
928         return width - advanceForGlyph(data);
929     }
930
931     if (m_stretchyData.mode() == DrawSizeVariant)
932         return width - advanceForGlyph(m_stretchyData.variant());
933
934     float assemblyWidth = advanceForGlyph(m_stretchyData.top());
935     assemblyWidth = std::max(assemblyWidth, advanceForGlyph(m_stretchyData.bottom()));
936     assemblyWidth = std::max(assemblyWidth, advanceForGlyph(m_stretchyData.extension()));
937     if (m_stretchyData.middle().glyph)
938         assemblyWidth = std::max(assemblyWidth, advanceForGlyph(m_stretchyData.middle()));
939     return width - assemblyWidth;
940 }
941
942 }
943
944 #endif