2b1a3fddbd9efd74dbf17b4994b960d4df46eb3f
[WebKit-https.git] / Source / WebCore / rendering / mathml / MathOperator.cpp
1 /*
2  * Copyright (C) 2016 Igalia S.L. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
14  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
15  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
16  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
17  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
18  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #if ENABLE(MATHML)
29 #include "MathOperator.h"
30
31 #include "RenderStyle.h"
32 #include "StyleInheritedData.h"
33
34 namespace WebCore {
35
36 static inline FloatRect boundsForGlyph(const GlyphData& data)
37 {
38     return data.isValid() ? data.font->boundsForGlyph(data.glyph) : FloatRect();
39 }
40
41 static inline float heightForGlyph(const GlyphData& data)
42 {
43     return boundsForGlyph(data).height();
44 }
45
46 static inline void getAscentAndDescentForGlyph(const GlyphData& data, LayoutUnit& ascent, LayoutUnit& descent)
47 {
48     FloatRect bounds = boundsForGlyph(data);
49     ascent = -bounds.y();
50     descent = bounds.maxY();
51 }
52
53 static inline float advanceWidthForGlyph(const GlyphData& data)
54 {
55     return data.isValid() ? data.font->widthForGlyph(data.glyph) : 0;
56 }
57
58 // FIXME: This hardcoded data can be removed when OpenType MATH font are widely available (http://wkbug/156837).
59 struct StretchyCharacter {
60     UChar character;
61     UChar topChar;
62     UChar extensionChar;
63     UChar bottomChar;
64     UChar middleChar;
65 };
66 // The first leftRightPairsCount pairs correspond to left/right fences that can easily be mirrored in RTL.
67 static const short leftRightPairsCount = 5;
68 static const StretchyCharacter stretchyCharacters[14] = {
69     { 0x28  , 0x239b, 0x239c, 0x239d, 0x0    }, // left parenthesis
70     { 0x29  , 0x239e, 0x239f, 0x23a0, 0x0    }, // right parenthesis
71     { 0x5b  , 0x23a1, 0x23a2, 0x23a3, 0x0    }, // left square bracket
72     { 0x5d  , 0x23a4, 0x23a5, 0x23a6, 0x0    }, // right square bracket
73     { 0x7b  , 0x23a7, 0x23aa, 0x23a9, 0x23a8 }, // left curly bracket
74     { 0x7d  , 0x23ab, 0x23aa, 0x23ad, 0x23ac }, // right curly bracket
75     { 0x2308, 0x23a1, 0x23a2, 0x23a2, 0x0    }, // left ceiling
76     { 0x2309, 0x23a4, 0x23a5, 0x23a5, 0x0    }, // right ceiling
77     { 0x230a, 0x23a2, 0x23a2, 0x23a3, 0x0    }, // left floor
78     { 0x230b, 0x23a5, 0x23a5, 0x23a6, 0x0    }, // right floor
79     { 0x7c  , 0x7c,   0x7c,   0x7c,   0x0    }, // vertical bar
80     { 0x2016, 0x2016, 0x2016, 0x2016, 0x0    }, // double vertical line
81     { 0x2225, 0x2225, 0x2225, 0x2225, 0x0    }, // parallel to
82     { 0x222b, 0x2320, 0x23ae, 0x2321, 0x0    } // integral sign
83 };
84
85 void MathOperator::setOperator(const RenderStyle& style, UChar baseCharacter, Type operatorType)
86 {
87     m_baseCharacter = baseCharacter;
88     m_operatorType = operatorType;
89     reset(style);
90 }
91
92 void MathOperator::reset(const RenderStyle& style)
93 {
94     m_stretchType = StretchType::Unstretched;
95     m_maxPreferredWidth = 0;
96     m_width = 0;
97     m_ascent = 0;
98     m_descent = 0;
99     m_italicCorrection = 0;
100
101     // We use the base size for the calculation of the preferred width.
102     GlyphData baseGlyph;
103     if (!getBaseGlyph(style, baseGlyph))
104         return;
105     m_maxPreferredWidth = m_width = advanceWidthForGlyph(baseGlyph);
106     getAscentAndDescentForGlyph(baseGlyph, m_ascent, m_descent);
107
108     if (m_operatorType == Type::VerticalOperator)
109         calculateStretchyData(style, true); // We also take into account the width of larger sizes for the calculation of the preferred width.
110     else if (m_operatorType == Type::DisplayOperator)
111         calculateDisplayStyleLargeOperator(style); // We can directly select the size variant and determine the final metrics.
112 }
113
114 LayoutUnit MathOperator::stretchSize() const
115 {
116     ASSERT(m_operatorType == Type::VerticalOperator || m_operatorType == Type::HorizontalOperator);
117     return m_operatorType == Type::VerticalOperator ? m_ascent + m_descent : m_width;
118 }
119
120 bool MathOperator::getBaseGlyph(const RenderStyle& style, GlyphData& baseGlyph) const
121 {
122     baseGlyph = style.fontCascade().glyphDataForCharacter(m_baseCharacter, !style.isLeftToRightDirection());
123     return baseGlyph.isValid() && baseGlyph.font == &style.fontCascade().primaryFont();
124 }
125
126 void MathOperator::setSizeVariant(const GlyphData& sizeVariant)
127 {
128     ASSERT(sizeVariant.isValid());
129     ASSERT(sizeVariant.font->mathData());
130     m_stretchType = StretchType::SizeVariant;
131     m_variant = sizeVariant;
132     m_width = advanceWidthForGlyph(sizeVariant);
133     getAscentAndDescentForGlyph(sizeVariant, m_ascent, m_descent);
134 }
135
136 void MathOperator::setGlyphAssembly(const GlyphAssemblyData& assemblyData)
137 {
138     ASSERT(m_operatorType == Type::VerticalOperator || m_operatorType == Type::HorizontalOperator);
139     m_stretchType = StretchType::GlyphAssembly;
140     m_assembly = assemblyData;
141     if (m_operatorType == Type::VerticalOperator) {
142         m_width = 0;
143         m_width = std::max<LayoutUnit>(m_width, advanceWidthForGlyph(m_assembly.topOrRight));
144         m_width = std::max<LayoutUnit>(m_width, advanceWidthForGlyph(m_assembly.extension));
145         m_width = std::max<LayoutUnit>(m_width, advanceWidthForGlyph(m_assembly.bottomOrLeft));
146         m_width = std::max<LayoutUnit>(m_width, advanceWidthForGlyph(m_assembly.middle));
147     } else {
148         m_ascent = 0;
149         m_descent = 0;
150         LayoutUnit ascent, descent;
151         getAscentAndDescentForGlyph(m_assembly.bottomOrLeft, ascent, descent);
152         m_ascent = std::max(m_ascent, ascent);
153         m_descent = std::max(m_descent, descent);
154         getAscentAndDescentForGlyph(m_assembly.extension, ascent, descent);
155         m_ascent = std::max(m_ascent, ascent);
156         m_descent = std::max(m_descent, descent);
157         getAscentAndDescentForGlyph(m_assembly.topOrRight, ascent, descent);
158         m_ascent = std::max(m_ascent, ascent);
159         m_descent = std::max(m_descent, descent);
160         getAscentAndDescentForGlyph(m_assembly.middle, ascent, descent);
161         m_ascent = std::max(m_ascent, ascent);
162         m_descent = std::max(m_descent, descent);
163     }
164 }
165
166 void MathOperator::calculateDisplayStyleLargeOperator(const RenderStyle& style)
167 {
168     ASSERT(m_operatorType == Type::DisplayOperator);
169
170     GlyphData baseGlyph;
171     if (!getBaseGlyph(style, baseGlyph) || !baseGlyph.font->mathData())
172         return;
173
174     // 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.
175     float displayOperatorMinHeight = std::max(heightForGlyph(baseGlyph) * sqrtOfTwoFloat, baseGlyph.font->mathData()->getMathConstant(*baseGlyph.font, OpenTypeMathData::DisplayOperatorMinHeight));
176
177     Vector<Glyph> sizeVariants;
178     Vector<OpenTypeMathData::AssemblyPart> assemblyParts;
179     baseGlyph.font->mathData()->getMathVariants(baseGlyph.glyph, true, sizeVariants, assemblyParts);
180
181     // We choose the first size variant that is larger than the expected displayOperatorMinHeight and otherwise fallback to the largest variant.
182     for (auto& sizeVariant : sizeVariants) {
183         GlyphData glyphData(sizeVariant, baseGlyph.font);
184         setSizeVariant(glyphData);
185         m_maxPreferredWidth = m_width;
186         m_italicCorrection = glyphData.font->mathData()->getItalicCorrection(*glyphData.font, glyphData.glyph);
187         if (heightForGlyph(glyphData) >= displayOperatorMinHeight)
188             break;
189     }
190 }
191
192 bool MathOperator::calculateGlyphAssemblyFallback(const RenderStyle& style, const Vector<OpenTypeMathData::AssemblyPart>& assemblyParts, GlyphAssemblyData& assemblyData) const
193 {
194     // 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.
195     // FIXME: MathOperator should support the most general format (https://bugs.webkit.org/show_bug.cgi?id=130327).
196     // We use the approach of the copyComponents function in github.com/mathjax/MathJax-dev/blob/master/fonts/OpenTypeMath/fontUtil.py
197
198     // We count the number of non extender pieces.
199     int nonExtenderCount = 0;
200     for (auto& part : assemblyParts) {
201         if (!part.isExtender)
202             nonExtenderCount++;
203     }
204     if (nonExtenderCount > 3)
205         return false; // This is not supported: there are too many pieces.
206
207     // We now browse the list of pieces from left to right for horizontal operators and from bottom to top for vertical operators.
208     enum PartType {
209         Start,
210         ExtenderBetweenStartAndMiddle,
211         Middle,
212         ExtenderBetweenMiddleAndEnd,
213         End,
214         None
215     };
216     PartType expectedPartType = Start;
217     assemblyData.extension.glyph = 0;
218     assemblyData.middle.glyph = 0;
219     for (auto& part : assemblyParts) {
220         if (nonExtenderCount < 3) {
221             // If we only have at most two non-extenders then we skip the middle glyph.
222             if (expectedPartType == ExtenderBetweenStartAndMiddle)
223                 expectedPartType = ExtenderBetweenMiddleAndEnd;
224             else if (expectedPartType == Middle)
225                 expectedPartType = End;
226         }
227         if (part.isExtender) {
228             if (!assemblyData.extension.glyph)
229                 assemblyData.extension.glyph = part.glyph; // We copy the extender part.
230             else if (assemblyData.extension.glyph != part.glyph)
231                 return false; // This is not supported: the assembly has different extenders.
232
233             switch (expectedPartType) {
234             case Start:
235                 // We ignore the left/bottom part.
236                 expectedPartType = ExtenderBetweenStartAndMiddle;
237                 continue;
238             case Middle:
239                 // We ignore the middle part.
240                 expectedPartType = ExtenderBetweenMiddleAndEnd;
241                 continue;
242             case End:
243             case None:
244                 // This is not supported: we got an unexpected extender.
245                 return false;
246             case ExtenderBetweenStartAndMiddle:
247             case ExtenderBetweenMiddleAndEnd:
248                 // We ignore multiple consecutive extenders.
249                 continue;
250             }
251         }
252
253         switch (expectedPartType) {
254         case Start:
255             // We copy the left/bottom part.
256             assemblyData.bottomOrLeft.glyph = part.glyph;
257             expectedPartType = ExtenderBetweenStartAndMiddle;
258             continue;
259         case ExtenderBetweenStartAndMiddle:
260         case Middle:
261             // We copy the middle part.
262             assemblyData.middle.glyph = part.glyph;
263             expectedPartType = ExtenderBetweenMiddleAndEnd;
264             continue;
265         case ExtenderBetweenMiddleAndEnd:
266         case End:
267             // We copy the right/top part.
268             assemblyData.topOrRight.glyph = part.glyph;
269             expectedPartType = None;
270             continue;
271         case None:
272             // This is not supported: we got an unexpected non-extender part.
273             return false;
274         }
275     }
276
277     if (!assemblyData.extension.glyph)
278         return false; // This is not supported: we always assume that we have an extension glyph.
279
280     // If we don't have top/bottom glyphs, we use the extension glyph.
281     if (!assemblyData.topOrRight.glyph)
282         assemblyData.topOrRight.glyph = assemblyData.extension.glyph;
283     if (!assemblyData.bottomOrLeft.glyph)
284         assemblyData.bottomOrLeft.glyph = assemblyData.extension.glyph;
285
286     assemblyData.topOrRight.font = &style.fontCascade().primaryFont();
287     assemblyData.extension.font = assemblyData.topOrRight.font;
288     assemblyData.bottomOrLeft.font = assemblyData.topOrRight.font;
289     assemblyData.middle.font = assemblyData.middle.glyph ? assemblyData.topOrRight.font : nullptr;
290
291     return true;
292 }
293
294 void MathOperator::calculateStretchyData(const RenderStyle& style, bool calculateMaxPreferredWidth, LayoutUnit targetSize)
295 {
296     ASSERT(m_operatorType == Type::VerticalOperator || m_operatorType == Type::HorizontalOperator);
297     ASSERT(!calculateMaxPreferredWidth || m_operatorType == Type::VerticalOperator);
298     bool isVertical = m_operatorType == Type::VerticalOperator;
299
300     GlyphData baseGlyph;
301     if (!getBaseGlyph(style, baseGlyph))
302         return;
303
304     if (!calculateMaxPreferredWidth) {
305         // We do not stretch if the base glyph is large enough.
306         float baseSize = isVertical ? heightForGlyph(baseGlyph) : advanceWidthForGlyph(baseGlyph);
307         if (targetSize <= baseSize)
308             return;
309     }
310
311     GlyphAssemblyData assemblyData;
312     if (baseGlyph.font->mathData()) {
313         Vector<Glyph> sizeVariants;
314         Vector<OpenTypeMathData::AssemblyPart> assemblyParts;
315         baseGlyph.font->mathData()->getMathVariants(baseGlyph.glyph, isVertical, sizeVariants, assemblyParts);
316         // We verify the size variants.
317         for (auto& sizeVariant : sizeVariants) {
318             GlyphData glyphData(sizeVariant, baseGlyph.font);
319             if (calculateMaxPreferredWidth)
320                 m_maxPreferredWidth = std::max<LayoutUnit>(m_maxPreferredWidth, advanceWidthForGlyph(glyphData));
321             else {
322                 setSizeVariant(glyphData);
323                 LayoutUnit size = isVertical ? heightForGlyph(glyphData) : advanceWidthForGlyph(glyphData);
324                 if (size >= targetSize)
325                     return;
326             }
327         }
328
329         // We verify if there is a construction.
330         if (!calculateGlyphAssemblyFallback(style, assemblyParts, assemblyData))
331             return;
332     } else {
333         if (!isVertical)
334             return;
335
336         // If the font does not have a MATH table, we fallback to the Unicode-only constructions.
337         const StretchyCharacter* stretchyCharacter = nullptr;
338         const unsigned maxIndex = WTF_ARRAY_LENGTH(stretchyCharacters);
339         for (unsigned index = 0; index < maxIndex; ++index) {
340             if (stretchyCharacters[index].character == m_baseCharacter) {
341                 stretchyCharacter = &stretchyCharacters[index];
342                 if (!style.isLeftToRightDirection() && index < leftRightPairsCount * 2) {
343                     // If we are in right-to-left direction we select the mirrored form by adding -1 or +1 according to the parity of index.
344                     index += index % 2 ? -1 : 1;
345                 }
346                 break;
347             }
348         }
349
350         // If we didn't find a stretchy character set for this character, we don't know how to stretch it.
351         if (!stretchyCharacter)
352             return;
353
354         // We convert the list of Unicode characters into a list of glyph data.
355         assemblyData.topOrRight = style.fontCascade().glyphDataForCharacter(stretchyCharacter->topChar, false);
356         assemblyData.extension = style.fontCascade().glyphDataForCharacter(stretchyCharacter->extensionChar, false);
357         assemblyData.bottomOrLeft = style.fontCascade().glyphDataForCharacter(stretchyCharacter->bottomChar, false);
358         assemblyData.middle = stretchyCharacter->middleChar ? style.fontCascade().glyphDataForCharacter(stretchyCharacter->middleChar, false) : GlyphData();
359     }
360
361     // If we are measuring the maximum width, verify each component.
362     if (calculateMaxPreferredWidth) {
363         m_maxPreferredWidth = std::max<LayoutUnit>(m_maxPreferredWidth, advanceWidthForGlyph(assemblyData.topOrRight));
364         m_maxPreferredWidth = std::max<LayoutUnit>(m_maxPreferredWidth, advanceWidthForGlyph(assemblyData.extension));
365         m_maxPreferredWidth = std::max<LayoutUnit>(m_maxPreferredWidth, advanceWidthForGlyph(assemblyData.middle));
366         m_maxPreferredWidth = std::max<LayoutUnit>(m_maxPreferredWidth, advanceWidthForGlyph(assemblyData.bottomOrLeft));
367         return;
368     }
369
370     // We ensure that the size is large enough to avoid glyph overlaps.
371     float minSize = isVertical ?
372         heightForGlyph(assemblyData.topOrRight) + heightForGlyph(assemblyData.middle) + heightForGlyph(assemblyData.bottomOrLeft)
373         : advanceWidthForGlyph(assemblyData.bottomOrLeft) + advanceWidthForGlyph(assemblyData.middle) + advanceWidthForGlyph(assemblyData.topOrRight);
374     if (minSize > targetSize)
375         return;
376
377     setGlyphAssembly(assemblyData);
378 }
379
380 void MathOperator::stretchTo(const RenderStyle& style, LayoutUnit ascent, LayoutUnit descent)
381 {
382     ASSERT(m_operatorType == Type::VerticalOperator);
383     calculateStretchyData(style, false, ascent + descent);
384     if (m_stretchType == StretchType::GlyphAssembly) {
385         m_ascent = ascent;
386         m_descent = descent;
387     }
388 }
389
390 void MathOperator::stretchTo(const RenderStyle& style, LayoutUnit width)
391 {
392     ASSERT(m_operatorType == Type::HorizontalOperator);
393     calculateStretchyData(style, false, width);
394     if (m_stretchType == StretchType::GlyphAssembly)
395         m_width = width;
396 }
397
398 LayoutRect MathOperator::paintGlyph(const RenderStyle& style, PaintInfo& info, const GlyphData& data, const LayoutPoint& origin, GlyphPaintTrimming trim)
399 {
400     FloatRect glyphBounds = boundsForGlyph(data);
401
402     LayoutRect glyphPaintRect(origin, LayoutSize(glyphBounds.x() + glyphBounds.width(), glyphBounds.height()));
403     glyphPaintRect.setY(origin.y() + glyphBounds.y());
404
405     // In order to have glyphs fit snugly with one another we snap the connecting edges to pixel boundaries
406     // and trim off one pixel. The pixel trim is to account for fonts that have edge pixels that have less
407     // than full coverage. These edge pixels can introduce small seams between connected glyphs.
408     FloatRect clipBounds = info.rect;
409     switch (trim) {
410     case TrimTop:
411         glyphPaintRect.shiftYEdgeTo(glyphPaintRect.y().ceil() + 1);
412         clipBounds.shiftYEdgeTo(glyphPaintRect.y());
413         break;
414     case TrimBottom:
415         glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1);
416         clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
417         break;
418     case TrimTopAndBottom:
419         glyphPaintRect.shiftYEdgeTo(glyphPaintRect.y().ceil() + 1);
420         glyphPaintRect.shiftMaxYEdgeTo(glyphPaintRect.maxY().floor() - 1);
421         clipBounds.shiftYEdgeTo(glyphPaintRect.y());
422         clipBounds.shiftMaxYEdgeTo(glyphPaintRect.maxY());
423         break;
424     case TrimLeft:
425         glyphPaintRect.shiftXEdgeTo(glyphPaintRect.x().ceil() + 1);
426         clipBounds.shiftXEdgeTo(glyphPaintRect.x());
427         break;
428     case TrimRight:
429         glyphPaintRect.shiftMaxXEdgeTo(glyphPaintRect.maxX().floor() - 1);
430         clipBounds.shiftMaxXEdgeTo(glyphPaintRect.maxX());
431         break;
432     case TrimLeftAndRight:
433         glyphPaintRect.shiftXEdgeTo(glyphPaintRect.x().ceil() + 1);
434         glyphPaintRect.shiftMaxXEdgeTo(glyphPaintRect.maxX().floor() - 1);
435         clipBounds.shiftXEdgeTo(glyphPaintRect.x());
436         clipBounds.shiftMaxXEdgeTo(glyphPaintRect.maxX());
437     }
438
439     // Clipping the enclosing IntRect avoids any potential issues at joined edges.
440     GraphicsContextStateSaver stateSaver(info.context());
441     info.context().clip(clipBounds);
442
443     GlyphBuffer buffer;
444     buffer.add(data.glyph, data.font, advanceWidthForGlyph(data));
445     info.context().drawGlyphs(style.fontCascade(), *data.font, buffer, 0, 1, origin);
446
447     return glyphPaintRect;
448 }
449
450 void MathOperator::fillWithVerticalExtensionGlyph(const RenderStyle& style, PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
451 {
452     ASSERT(m_operatorType == Type::VerticalOperator);
453     ASSERT(m_stretchType == StretchType::GlyphAssembly);
454     ASSERT(m_assembly.extension.isValid());
455     ASSERT(from.y() <= to.y());
456
457     // If there is no space for the extension glyph, we don't need to do anything.
458     if (from.y() == to.y())
459         return;
460
461     GraphicsContextStateSaver stateSaver(info.context());
462
463     FloatRect glyphBounds = boundsForGlyph(m_assembly.extension);
464
465     // Clipping the extender region here allows us to draw the bottom extender glyph into the
466     // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping.
467     LayoutRect clipBounds = info.rect;
468     clipBounds.shiftYEdgeTo(from.y());
469     clipBounds.shiftMaxYEdgeTo(to.y());
470     info.context().clip(clipBounds);
471
472     // Trimming may remove up to two pixels from the top of the extender glyph, so we move it up by two pixels.
473     float offsetToGlyphTop = glyphBounds.y() + 2;
474     LayoutPoint glyphOrigin = LayoutPoint(from.x(), from.y() - offsetToGlyphTop);
475     FloatRect lastPaintedGlyphRect(from, FloatSize());
476
477     // FIXME: In practice, only small stretch sizes are requested but we may need to limit the number
478     // of pieces that can be repeated to avoid hangs. See http://webkit.org/b/155434
479     while (lastPaintedGlyphRect.maxY() < to.y()) {
480         lastPaintedGlyphRect = paintGlyph(style, info, m_assembly.extension, glyphOrigin, TrimTopAndBottom);
481         glyphOrigin.setY(glyphOrigin.y() + lastPaintedGlyphRect.height());
482
483         // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle
484         // with trimming. In that case we just draw nothing.
485         if (lastPaintedGlyphRect.isEmpty())
486             break;
487     }
488 }
489
490 void MathOperator::fillWithHorizontalExtensionGlyph(const RenderStyle& style, PaintInfo& info, const LayoutPoint& from, const LayoutPoint& to)
491 {
492     ASSERT(m_operatorType == Type::HorizontalOperator);
493     ASSERT(m_stretchType == StretchType::GlyphAssembly);
494     ASSERT(m_assembly.extension.isValid());
495     ASSERT(from.x() <= to.x());
496     ASSERT(from.y() == to.y());
497
498     // If there is no space for the extension glyph, we don't need to do anything.
499     if (from.x() == to.x())
500         return;
501
502     GraphicsContextStateSaver stateSaver(info.context());
503
504     // Clipping the extender region here allows us to draw the bottom extender glyph into the
505     // regions of the bottom glyph without worrying about overdraw (hairy pixels) and simplifies later clipping.
506     LayoutRect clipBounds = info.rect;
507     clipBounds.shiftXEdgeTo(from.x());
508     clipBounds.shiftMaxXEdgeTo(to.x());
509     info.context().clip(clipBounds);
510
511     // Trimming may remove up to two pixels from the left of the extender glyph, so we move it left by two pixels.
512     float offsetToGlyphLeft = -2;
513     LayoutPoint glyphOrigin = LayoutPoint(from.x() + offsetToGlyphLeft, from.y());
514     FloatRect lastPaintedGlyphRect(from, FloatSize());
515
516     // FIXME: In practice, only small stretch sizes are requested but we may need to limit the number
517     // of pieces that can be repeated to avoid hangs. See http://webkit.org/b/155434
518     while (lastPaintedGlyphRect.maxX() < to.x()) {
519         lastPaintedGlyphRect = paintGlyph(style, info, m_assembly.extension, glyphOrigin, TrimLeftAndRight);
520         glyphOrigin.setX(glyphOrigin.x() + lastPaintedGlyphRect.width());
521
522         // There's a chance that if the font size is small enough the glue glyph has been reduced to an empty rectangle
523         // with trimming. In that case we just draw nothing.
524         if (lastPaintedGlyphRect.isEmpty())
525             break;
526     }
527 }
528
529 void MathOperator::paintVerticalGlyphAssembly(const RenderStyle& style, PaintInfo& info, const LayoutPoint& paintOffset)
530 {
531     ASSERT(m_operatorType == Type::VerticalOperator);
532     ASSERT(m_stretchType == StretchType::GlyphAssembly);
533     ASSERT(m_assembly.topOrRight.isValid());
534     ASSERT(m_assembly.bottomOrLeft.isValid());
535
536     // We are positioning the glyphs so that the edge of the tight glyph bounds line up exactly with the edges of our paint box.
537     LayoutPoint operatorTopLeft = paintOffset;
538     FloatRect topGlyphBounds = boundsForGlyph(m_assembly.topOrRight);
539     LayoutPoint topGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() - topGlyphBounds.y());
540     LayoutRect topGlyphPaintRect = paintGlyph(style, info, m_assembly.topOrRight, topGlyphOrigin, TrimBottom);
541
542     FloatRect bottomGlyphBounds = boundsForGlyph(m_assembly.bottomOrLeft);
543     LayoutPoint bottomGlyphOrigin(operatorTopLeft.x(), operatorTopLeft.y() + stretchSize() - (bottomGlyphBounds.height() + bottomGlyphBounds.y()));
544     LayoutRect bottomGlyphPaintRect = paintGlyph(style, info, m_assembly.bottomOrLeft, bottomGlyphOrigin, TrimTop);
545
546     if (m_assembly.middle.isValid()) {
547         // Center the glyph origin between the start and end glyph paint extents. Then shift it half the paint height toward the bottom glyph.
548         FloatRect middleGlyphBounds = boundsForGlyph(m_assembly.middle);
549         LayoutPoint middleGlyphOrigin(operatorTopLeft.x(), topGlyphOrigin.y());
550         middleGlyphOrigin.moveBy(LayoutPoint(0, (bottomGlyphPaintRect.y() - topGlyphPaintRect.maxY()) / 2.0));
551         middleGlyphOrigin.moveBy(LayoutPoint(0, middleGlyphBounds.height() / 2.0));
552
553         LayoutRect middleGlyphPaintRect = paintGlyph(style, info, m_assembly.middle, middleGlyphOrigin, TrimTopAndBottom);
554         fillWithVerticalExtensionGlyph(style, info, topGlyphPaintRect.minXMaxYCorner(), middleGlyphPaintRect.minXMinYCorner());
555         fillWithVerticalExtensionGlyph(style, info, middleGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
556     } else
557         fillWithVerticalExtensionGlyph(style, info, topGlyphPaintRect.minXMaxYCorner(), bottomGlyphPaintRect.minXMinYCorner());
558 }
559
560 void MathOperator::paintHorizontalGlyphAssembly(const RenderStyle& style, PaintInfo& info, const LayoutPoint& paintOffset)
561 {
562     ASSERT(m_operatorType == Type::HorizontalOperator);
563     ASSERT(m_stretchType == StretchType::GlyphAssembly);
564     ASSERT(m_assembly.bottomOrLeft.isValid());
565     ASSERT(m_assembly.topOrRight.isValid());
566
567     // We are positioning the glyphs so that the edge of the tight glyph bounds line up exactly with the edges of our paint box.
568     LayoutPoint operatorTopLeft = paintOffset;
569     LayoutUnit baselineY = operatorTopLeft.y() + m_ascent;
570     LayoutPoint leftGlyphOrigin(operatorTopLeft.x(), baselineY);
571     LayoutRect leftGlyphPaintRect = paintGlyph(style, info, m_assembly.bottomOrLeft, leftGlyphOrigin, TrimRight);
572
573     FloatRect rightGlyphBounds = boundsForGlyph(m_assembly.topOrRight);
574     LayoutPoint rightGlyphOrigin(operatorTopLeft.x() + stretchSize() - rightGlyphBounds.width(), baselineY);
575     LayoutRect rightGlyphPaintRect = paintGlyph(style, info, m_assembly.topOrRight, rightGlyphOrigin, TrimLeft);
576
577     if (m_assembly.middle.isValid()) {
578         // Center the glyph origin between the start and end glyph paint extents.
579         LayoutPoint middleGlyphOrigin(operatorTopLeft.x(), baselineY);
580         middleGlyphOrigin.moveBy(LayoutPoint((rightGlyphPaintRect.x() - leftGlyphPaintRect.maxX()) / 2.0, 0));
581         LayoutRect middleGlyphPaintRect = paintGlyph(style, info, m_assembly.middle, middleGlyphOrigin, TrimLeftAndRight);
582         fillWithHorizontalExtensionGlyph(style, info, LayoutPoint(leftGlyphPaintRect.maxX(), baselineY), LayoutPoint(middleGlyphPaintRect.x(), baselineY));
583         fillWithHorizontalExtensionGlyph(style, info, LayoutPoint(middleGlyphPaintRect.maxX(), baselineY), LayoutPoint(rightGlyphPaintRect.x(), baselineY));
584     } else
585         fillWithHorizontalExtensionGlyph(style, info, LayoutPoint(leftGlyphPaintRect.maxX(), baselineY), LayoutPoint(rightGlyphPaintRect.x(), baselineY));
586 }
587
588 void MathOperator::paint(const RenderStyle& style, PaintInfo& info, const LayoutPoint& paintOffset)
589 {
590     if (info.context().paintingDisabled() || info.phase != PaintPhaseForeground || style.visibility() != VISIBLE)
591         return;
592
593     GraphicsContextStateSaver stateSaver(info.context());
594     info.context().setFillColor(style.visitedDependentColor(CSSPropertyColor));
595
596     if (m_stretchType == StretchType::GlyphAssembly) {
597         if (m_operatorType == Type::VerticalOperator)
598             paintVerticalGlyphAssembly(style, info, paintOffset);
599         else
600             paintHorizontalGlyphAssembly(style, info, paintOffset);
601         return;
602     }
603
604     GlyphData glyphData;
605     ASSERT(m_stretchType == StretchType::Unstretched || m_stretchType == StretchType::SizeVariant);
606     if (m_stretchType == StretchType::Unstretched) {
607         if (!getBaseGlyph(style, glyphData))
608             return;
609     } else if (m_stretchType == StretchType::SizeVariant) {
610         ASSERT(m_variant.isValid());
611         glyphData = m_variant;
612     }
613     GlyphBuffer buffer;
614     buffer.add(glyphData.glyph, glyphData.font, advanceWidthForGlyph(glyphData));
615     LayoutPoint operatorTopLeft = paintOffset;
616     FloatRect glyphBounds = boundsForGlyph(glyphData);
617     LayoutPoint operatorOrigin(operatorTopLeft.x(), operatorTopLeft.y() - glyphBounds.y());
618     info.context().drawGlyphs(style.fontCascade(), *glyphData.font, buffer, 0, 1, operatorOrigin);
619 }
620
621 }
622
623 #endif