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