1/*
2 * Copyright (C) 2014 Frédéric Wang (fred.wang@free.fr). All rights reserved.
3 * Copyright (C) 2016 Igalia S.L.
4 * Copyright (C) 2016 Apple Inc. All rights reserved.
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#include "RenderMathMLToken.h"
30
31#if ENABLE(MATHML)
32
33#include "MathMLElement.h"
34#include "MathMLNames.h"
35#include "MathMLTokenElement.h"
36#include "PaintInfo.h"
37#include "RenderElement.h"
38#include "RenderIterator.h"
39#include <wtf/IsoMallocInlines.h>
40
41namespace WebCore {
42
43using namespace MathMLNames;
44
45WTF_MAKE_ISO_ALLOCATED_IMPL(RenderMathMLToken);
46
47RenderMathMLToken::RenderMathMLToken(MathMLTokenElement& element, RenderStyle&& style)
48 : RenderMathMLBlock(element, WTFMove(style))
49{
50}
51
52RenderMathMLToken::RenderMathMLToken(Document& document, RenderStyle&& style)
53 : RenderMathMLBlock(document, WTFMove(style))
54{
55}
56
57MathMLTokenElement& RenderMathMLToken::element()
58{
59 return static_cast<MathMLTokenElement&>(nodeForNonAnonymous());
60}
61
62void RenderMathMLToken::updateTokenContent()
63{
64 RenderMathMLBlock::updateFromElement();
65 setMathVariantGlyphDirty();
66}
67
68// Entries for the mathvariant lookup tables.
69// 'key' represents the Unicode character to be transformed and is used for searching the tables.
70// 'replacement' represents the mapped mathvariant Unicode character.
71struct MathVariantMapping {
72 uint32_t key;
73 uint32_t replacement;
74};
75static inline UChar32 ExtractKey(const MathVariantMapping* entry) { return entry->key; }
76static UChar32 MathVariantMappingSearch(uint32_t key, const MathVariantMapping* table, size_t tableLength)
77{
78 if (const auto* entry = tryBinarySearch<const MathVariantMapping, UChar32>(table, tableLength, key, ExtractKey))
79 return entry->replacement;
80
81 return 0;
82}
83
84// Lookup tables for use with mathvariant mappings to transform a unicode character point to another unicode character that indicates the proper output.
85// key represents one of two concepts.
86// 1. In the Latin table it represents a hole in the mathematical alphanumeric block, where the character that should occupy that position is located elsewhere.
87// 2. It represents an Arabic letter.
88// As a replacement, 0 is reserved to indicate no mapping was found.
89static const MathVariantMapping arabicInitialMapTable[] = {
90 { 0x628, 0x1EE21 },
91 { 0x62A, 0x1EE35 },
92 { 0x62B, 0x1EE36 },
93 { 0x62C, 0x1EE22 },
94 { 0x62D, 0x1EE27 },
95 { 0x62E, 0x1EE37 },
96 { 0x633, 0x1EE2E },
97 { 0x634, 0x1EE34 },
98 { 0x635, 0x1EE31 },
99 { 0x636, 0x1EE39 },
100 { 0x639, 0x1EE2F },
101 { 0x63A, 0x1EE3B },
102 { 0x641, 0x1EE30 },
103 { 0x642, 0x1EE32 },
104 { 0x643, 0x1EE2A },
105 { 0x644, 0x1EE2B },
106 { 0x645, 0x1EE2C },
107 { 0x646, 0x1EE2D },
108 { 0x647, 0x1EE24 },
109 { 0x64A, 0x1EE29 }
110};
111
112static const MathVariantMapping arabicTailedMapTable[] = {
113 { 0x62C, 0x1EE42 },
114 { 0x62D, 0x1EE47 },
115 { 0x62E, 0x1EE57 },
116 { 0x633, 0x1EE4E },
117 { 0x634, 0x1EE54 },
118 { 0x635, 0x1EE51 },
119 { 0x636, 0x1EE59 },
120 { 0x639, 0x1EE4F },
121 { 0x63A, 0x1EE5B },
122 { 0x642, 0x1EE52 },
123 { 0x644, 0x1EE4B },
124 { 0x646, 0x1EE4D },
125 { 0x64A, 0x1EE49 },
126 { 0x66F, 0x1EE5F },
127 { 0x6BA, 0x1EE5D }
128};
129
130static const MathVariantMapping arabicStretchedMapTable[] = {
131 { 0x628, 0x1EE61 },
132 { 0x62A, 0x1EE75 },
133 { 0x62B, 0x1EE76 },
134 { 0x62C, 0x1EE62 },
135 { 0x62D, 0x1EE67 },
136 { 0x62E, 0x1EE77 },
137 { 0x633, 0x1EE6E },
138 { 0x634, 0x1EE74 },
139 { 0x635, 0x1EE71 },
140 { 0x636, 0x1EE79 },
141 { 0x637, 0x1EE68 },
142 { 0x638, 0x1EE7A },
143 { 0x639, 0x1EE6F },
144 { 0x63A, 0x1EE7B },
145 { 0x641, 0x1EE70 },
146 { 0x642, 0x1EE72 },
147 { 0x643, 0x1EE6A },
148 { 0x645, 0x1EE6C },
149 { 0x646, 0x1EE6D },
150 { 0x647, 0x1EE64 },
151 { 0x64A, 0x1EE69 },
152 { 0x66E, 0x1EE7C },
153 { 0x6A1, 0x1EE7E }
154};
155
156static const MathVariantMapping arabicLoopedMapTable[] = {
157 { 0x627, 0x1EE80 },
158 { 0x628, 0x1EE81 },
159 { 0x62A, 0x1EE95 },
160 { 0x62B, 0x1EE96 },
161 { 0x62C, 0x1EE82 },
162 { 0x62D, 0x1EE87 },
163 { 0x62E, 0x1EE97 },
164 { 0x62F, 0x1EE83 },
165 { 0x630, 0x1EE98 },
166 { 0x631, 0x1EE93 },
167 { 0x632, 0x1EE86 },
168 { 0x633, 0x1EE8E },
169 { 0x634, 0x1EE94 },
170 { 0x635, 0x1EE91 },
171 { 0x636, 0x1EE99 },
172 { 0x637, 0x1EE88 },
173 { 0x638, 0x1EE9A },
174 { 0x639, 0x1EE8F },
175 { 0x63A, 0x1EE9B },
176 { 0x641, 0x1EE90 },
177 { 0x642, 0x1EE92 },
178 { 0x644, 0x1EE8B },
179 { 0x645, 0x1EE8C },
180 { 0x646, 0x1EE8D },
181 { 0x647, 0x1EE84 },
182 { 0x648, 0x1EE85 },
183 { 0x64A, 0x1EE89 }
184};
185
186static const MathVariantMapping arabicDoubleMapTable[] = {
187 { 0x628, 0x1EEA1 },
188 { 0x62A, 0x1EEB5 },
189 { 0x62B, 0x1EEB6 },
190 { 0x62C, 0x1EEA2 },
191 { 0x62D, 0x1EEA7 },
192 { 0x62E, 0x1EEB7 },
193 { 0x62F, 0x1EEA3 },
194 { 0x630, 0x1EEB8 },
195 { 0x631, 0x1EEB3 },
196 { 0x632, 0x1EEA6 },
197 { 0x633, 0x1EEAE },
198 { 0x634, 0x1EEB4 },
199 { 0x635, 0x1EEB1 },
200 { 0x636, 0x1EEB9 },
201 { 0x637, 0x1EEA8 },
202 { 0x638, 0x1EEBA },
203 { 0x639, 0x1EEAF },
204 { 0x63A, 0x1EEBB },
205 { 0x641, 0x1EEB0 },
206 { 0x642, 0x1EEB2 },
207 { 0x644, 0x1EEAB },
208 { 0x645, 0x1EEAC },
209 { 0x646, 0x1EEAD },
210 { 0x648, 0x1EEA5 },
211 { 0x64A, 0x1EEA9 }
212};
213
214static const MathVariantMapping latinExceptionMapTable[] = {
215 { 0x1D455, 0x210E },
216 { 0x1D49D, 0x212C },
217 { 0x1D4A0, 0x2130 },
218 { 0x1D4A1, 0x2131 },
219 { 0x1D4A3, 0x210B },
220 { 0x1D4A4, 0x2110 },
221 { 0x1D4A7, 0x2112 },
222 { 0x1D4A8, 0x2133 },
223 { 0x1D4AD, 0x211B },
224 { 0x1D4BA, 0x212F },
225 { 0x1D4BC, 0x210A },
226 { 0x1D4C4, 0x2134 },
227 { 0x1D506, 0x212D },
228 { 0x1D50B, 0x210C },
229 { 0x1D50C, 0x2111 },
230 { 0x1D515, 0x211C },
231 { 0x1D51D, 0x2128 },
232 { 0x1D53A, 0x2102 },
233 { 0x1D53F, 0x210D },
234 { 0x1D545, 0x2115 },
235 { 0x1D547, 0x2119 },
236 { 0x1D548, 0x211A },
237 { 0x1D549, 0x211D },
238 { 0x1D551, 0x2124 }
239};
240
241const UChar32 greekUpperTheta = 0x03F4;
242const UChar32 holeGreekUpperTheta = 0x03A2;
243const UChar32 nabla = 0x2207;
244const UChar32 partialDifferential = 0x2202;
245const UChar32 greekUpperAlpha = 0x0391;
246const UChar32 greekUpperOmega = 0x03A9;
247const UChar32 greekLowerAlpha = 0x03B1;
248const UChar32 greekLowerOmega = 0x03C9;
249const UChar32 greekLunateEpsilonSymbol = 0x03F5;
250const UChar32 greekThetaSymbol = 0x03D1;
251const UChar32 greekKappaSymbol = 0x03F0;
252const UChar32 greekPhiSymbol = 0x03D5;
253const UChar32 greekRhoSymbol = 0x03F1;
254const UChar32 greekPiSymbol = 0x03D6;
255const UChar32 greekLetterDigamma = 0x03DC;
256const UChar32 greekSmallLetterDigamma = 0x03DD;
257const UChar32 mathBoldCapitalDigamma = 0x1D7CA;
258const UChar32 mathBoldSmallDigamma = 0x1D7CB;
259
260const UChar32 latinSmallLetterDotlessI = 0x0131;
261const UChar32 latinSmallLetterDotlessJ = 0x0237;
262
263const UChar32 mathItalicSmallDotlessI = 0x1D6A4;
264const UChar32 mathItalicSmallDotlessJ = 0x1D6A5;
265
266const UChar32 mathBoldUpperA = 0x1D400;
267const UChar32 mathItalicUpperA = 0x1D434;
268const UChar32 mathBoldSmallA = 0x1D41A;
269const UChar32 mathBoldUpperAlpha = 0x1D6A8;
270const UChar32 mathBoldSmallAlpha = 0x1D6C2;
271const UChar32 mathItalicUpperAlpha = 0x1D6E2;
272const UChar32 mathBoldDigitZero = 0x1D7CE;
273const UChar32 mathDoubleStruckZero = 0x1D7D8;
274
275const UChar32 mathBoldUpperTheta = 0x1D6B9;
276const UChar32 mathBoldNabla = 0x1D6C1;
277const UChar32 mathBoldPartialDifferential = 0x1D6DB;
278const UChar32 mathBoldEpsilonSymbol = 0x1D6DC;
279const UChar32 mathBoldThetaSymbol = 0x1D6DD;
280const UChar32 mathBoldKappaSymbol = 0x1D6DE;
281const UChar32 mathBoldPhiSymbol = 0x1D6DF;
282const UChar32 mathBoldRhoSymbol = 0x1D6E0;
283const UChar32 mathBoldPiSymbol = 0x1D6E1;
284
285// Performs the character mapping needed to implement MathML's mathvariant attribute.
286// It takes a unicode character and maps it to its appropriate mathvariant counterpart specified by mathvariant.
287// The mapped character is typically located within Unicode's mathematical blocks (0x1D***, 0x1EE**) but there are exceptions which this function accounts for.
288// Characters without a valid mapping or valid aMathvar value are returned
289// unaltered.
290// Characters already in the mathematical blocks (or are one of the exceptions) are never transformed.
291// Acceptable values for mathvariant are specified in MathMLElement.h
292// The transformable characters can be found at:
293// http://lists.w3.org/Archives/Public/www-math/2013Sep/0012.html and
294// https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols
295static UChar32 mathVariant(UChar32 codePoint, MathMLElement::MathVariant mathvariant)
296{
297 ASSERT(mathvariant >= MathMLElement::MathVariant::Normal && mathvariant <= MathMLElement::MathVariant::Stretched);
298
299 if (mathvariant == MathMLElement::MathVariant::Normal)
300 return codePoint; // Nothing to do here.
301
302 // Exceptional characters with at most one possible transformation.
303 if (codePoint == holeGreekUpperTheta)
304 return codePoint; // Nothing at this code point is transformed
305 if (codePoint == greekLetterDigamma) {
306 if (mathvariant == MathMLElement::MathVariant::Bold)
307 return mathBoldCapitalDigamma;
308 return codePoint;
309 }
310 if (codePoint == greekSmallLetterDigamma) {
311 if (mathvariant == MathMLElement::MathVariant::Bold)
312 return mathBoldSmallDigamma;
313 return codePoint;
314 }
315 if (codePoint == latinSmallLetterDotlessI) {
316 if (mathvariant == MathMLElement::MathVariant::Italic)
317 return mathItalicSmallDotlessI;
318 return codePoint;
319 }
320 if (codePoint == latinSmallLetterDotlessJ) {
321 if (mathvariant == MathMLElement::MathVariant::Italic)
322 return mathItalicSmallDotlessJ;
323 return codePoint;
324 }
325
326 // The Unicode mathematical blocks are divided into four segments: Latin, Greek, numbers and Arabic.
327 // In the case of the first three baseChar represents the relative order in which the characters are encoded in the Unicode mathematical block, normalised to the first character of that sequence.
328 UChar32 baseChar = 0;
329 enum CharacterType {
330 Latin,
331 Greekish,
332 Number,
333 Arabic
334 };
335 CharacterType varType;
336 if (isASCIIUpper(codePoint)) {
337 baseChar = codePoint - 'A';
338 varType = Latin;
339 } else if (isASCIILower(codePoint)) {
340 // Lowercase characters are placed immediately after the uppercase characters in the Unicode mathematical block.
341 // The constant subtraction represents the number of characters between the start of the sequence (capital A) and the first lowercase letter.
342 baseChar = mathBoldSmallA - mathBoldUpperA + codePoint - 'a';
343 varType = Latin;
344 } else if (isASCIIDigit(codePoint)) {
345 baseChar = codePoint - '0';
346 varType = Number;
347 } else if (greekUpperAlpha <= codePoint && codePoint <= greekUpperOmega) {
348 baseChar = codePoint - greekUpperAlpha;
349 varType = Greekish;
350 } else if (greekLowerAlpha <= codePoint && codePoint <= greekLowerOmega) {
351 // Lowercase Greek comes after uppercase Greek.
352 // Note in this instance the presence of an additional character (Nabla) between the end of the uppercase Greek characters and the lowercase ones.
353 baseChar = mathBoldSmallAlpha - mathBoldUpperAlpha + codePoint - greekLowerAlpha;
354 varType = Greekish;
355 } else if (0x0600 <= codePoint && codePoint <= 0x06FF) {
356 // Arabic characters are defined within this range
357 varType = Arabic;
358 } else {
359 switch (codePoint) {
360 case greekUpperTheta:
361 baseChar = mathBoldUpperTheta - mathBoldUpperAlpha;
362 break;
363 case nabla:
364 baseChar = mathBoldNabla - mathBoldUpperAlpha;
365 break;
366 case partialDifferential:
367 baseChar = mathBoldPartialDifferential - mathBoldUpperAlpha;
368 break;
369 case greekLunateEpsilonSymbol:
370 baseChar = mathBoldEpsilonSymbol - mathBoldUpperAlpha;
371 break;
372 case greekThetaSymbol:
373 baseChar = mathBoldThetaSymbol - mathBoldUpperAlpha;
374 break;
375 case greekKappaSymbol:
376 baseChar = mathBoldKappaSymbol - mathBoldUpperAlpha;
377 break;
378 case greekPhiSymbol:
379 baseChar = mathBoldPhiSymbol - mathBoldUpperAlpha;
380 break;
381 case greekRhoSymbol:
382 baseChar = mathBoldRhoSymbol - mathBoldUpperAlpha;
383 break;
384 case greekPiSymbol:
385 baseChar = mathBoldPiSymbol - mathBoldUpperAlpha;
386 break;
387 default:
388 return codePoint;
389 }
390 varType = Greekish;
391 }
392
393 int8_t multiplier;
394 if (varType == Number) {
395 // Each possible number mathvariant is encoded in a single, contiguous block.
396 // For example the beginning of the double struck number range follows immediately after the end of the bold number range.
397 // multiplier represents the order of the sequences relative to the first one.
398 switch (mathvariant) {
399 case MathMLElement::MathVariant::Bold:
400 multiplier = 0;
401 break;
402 case MathMLElement::MathVariant::DoubleStruck:
403 multiplier = 1;
404 break;
405 case MathMLElement::MathVariant::SansSerif:
406 multiplier = 2;
407 break;
408 case MathMLElement::MathVariant::BoldSansSerif:
409 multiplier = 3;
410 break;
411 case MathMLElement::MathVariant::Monospace:
412 multiplier = 4;
413 break;
414 default:
415 // This mathvariant isn't defined for numbers or is otherwise normal.
416 return codePoint;
417 }
418 // As the ranges are contiguous, to find the desired mathvariant range it is sufficient to
419 // multiply the position within the sequence order (multiplier) with the period of the sequence (which is constant for all number sequences)
420 // and to add the character point of the first character within the number mathvariant range.
421 // To this the baseChar calculated earlier is added to obtain the final code point.
422 return baseChar + multiplier * (mathDoubleStruckZero - mathBoldDigitZero) + mathBoldDigitZero;
423 }
424 if (varType == Greekish) {
425 switch (mathvariant) {
426 case MathMLElement::MathVariant::Bold:
427 multiplier = 0;
428 break;
429 case MathMLElement::MathVariant::Italic:
430 multiplier = 1;
431 break;
432 case MathMLElement::MathVariant::BoldItalic:
433 multiplier = 2;
434 break;
435 case MathMLElement::MathVariant::BoldSansSerif:
436 multiplier = 3;
437 break;
438 case MathMLElement::MathVariant::SansSerifBoldItalic:
439 multiplier = 4;
440 break;
441 default:
442 // This mathvariant isn't defined for Greek or is otherwise normal.
443 return codePoint;
444 }
445 // See the Number case for an explanation of the following calculation.
446 return baseChar + mathBoldUpperAlpha + multiplier * (mathItalicUpperAlpha - mathBoldUpperAlpha);
447 }
448
449 UChar32 tempChar = 0;
450 UChar32 newChar;
451 if (varType == Arabic) {
452 // The Arabic mathematical block is not continuous, nor does it have a monotonic mapping to the unencoded characters, requiring the use of a lookup table.
453 const MathVariantMapping* mapTable;
454 size_t tableLength;
455 switch (mathvariant) {
456 case MathMLElement::MathVariant::Initial:
457 mapTable = arabicInitialMapTable;
458 tableLength = WTF_ARRAY_LENGTH(arabicInitialMapTable);
459 break;
460 case MathMLElement::MathVariant::Tailed:
461 mapTable = arabicTailedMapTable;
462 tableLength = WTF_ARRAY_LENGTH(arabicTailedMapTable);
463 break;
464 case MathMLElement::MathVariant::Stretched:
465 mapTable = arabicStretchedMapTable;
466 tableLength = WTF_ARRAY_LENGTH(arabicStretchedMapTable);
467 break;
468 case MathMLElement::MathVariant::Looped:
469 mapTable = arabicLoopedMapTable;
470 tableLength = WTF_ARRAY_LENGTH(arabicLoopedMapTable);
471 break;
472 case MathMLElement::MathVariant::DoubleStruck:
473 mapTable = arabicDoubleMapTable;
474 tableLength = WTF_ARRAY_LENGTH(arabicDoubleMapTable);
475 break;
476 default:
477 return codePoint; // No valid transformations exist.
478 }
479 newChar = MathVariantMappingSearch(codePoint, mapTable, tableLength);
480 } else {
481 // Must be Latin
482 if (mathvariant > MathMLElement::MathVariant::Monospace)
483 return codePoint; // Latin doesn't support the Arabic mathvariants
484 multiplier = static_cast<int>(mathvariant) - 2;
485 // This is possible because the values for NS_MATHML_MATHVARIANT_* are chosen to coincide with the order in which the encoded mathvariant characters are located within their unicode block (less an offset to avoid None and Normal variants)
486 // See the Number case for an explanation of the following calculation
487 tempChar = baseChar + mathBoldUpperA + multiplier * (mathItalicUpperA - mathBoldUpperA);
488 // There are roughly twenty characters that are located outside of the mathematical block, so the spaces where they ought to be are used as keys for a lookup table containing the correct character mappings.
489 newChar = MathVariantMappingSearch(tempChar, latinExceptionMapTable, WTF_ARRAY_LENGTH(latinExceptionMapTable));
490 }
491
492 if (newChar)
493 return newChar;
494 if (varType == Latin)
495 return tempChar;
496 return codePoint; // This is an Arabic character without a corresponding mapping.
497}
498
499void RenderMathMLToken::computePreferredLogicalWidths()
500{
501 ASSERT(preferredLogicalWidthsDirty());
502
503 if (m_mathVariantGlyphDirty)
504 updateMathVariantGlyph();
505
506 if (m_mathVariantCodePoint) {
507 auto mathVariantGlyph = style().fontCascade().glyphDataForCharacter(m_mathVariantCodePoint.value(), m_mathVariantIsMirrored);
508 if (mathVariantGlyph.font) {
509 m_maxPreferredLogicalWidth = m_minPreferredLogicalWidth = mathVariantGlyph.font->widthForGlyph(mathVariantGlyph.glyph);
510 setPreferredLogicalWidthsDirty(false);
511 return;
512 }
513 }
514
515 RenderMathMLBlock::computePreferredLogicalWidths();
516}
517
518void RenderMathMLToken::updateMathVariantGlyph()
519{
520 ASSERT(m_mathVariantGlyphDirty);
521
522 m_mathVariantCodePoint = WTF::nullopt;
523 m_mathVariantGlyphDirty = false;
524
525 // Early return if the token element contains RenderElements.
526 // Note that the renderers corresponding to the children of the token element are wrapped inside an anonymous RenderBlock.
527 if (const auto& block = downcast<RenderElement>(firstChild())) {
528 if (childrenOfType<RenderElement>(*block).first())
529 return;
530 }
531
532 const auto& tokenElement = element();
533 if (auto codePoint = MathMLTokenElement::convertToSingleCodePoint(element().textContent())) {
534 MathMLElement::MathVariant mathvariant = mathMLStyle().mathVariant();
535 if (mathvariant == MathMLElement::MathVariant::None)
536 mathvariant = tokenElement.hasTagName(MathMLNames::miTag) ? MathMLElement::MathVariant::Italic : MathMLElement::MathVariant::Normal;
537 UChar32 transformedCodePoint = mathVariant(codePoint.value(), mathvariant);
538 if (transformedCodePoint != codePoint.value()) {
539 m_mathVariantCodePoint = mathVariant(codePoint.value(), mathvariant);
540 m_mathVariantIsMirrored = !style().isLeftToRightDirection();
541 }
542 }
543}
544
545void RenderMathMLToken::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
546{
547 RenderMathMLBlock::styleDidChange(diff, oldStyle);
548 setMathVariantGlyphDirty();
549}
550
551void RenderMathMLToken::updateFromElement()
552{
553 RenderMathMLBlock::updateFromElement();
554 setMathVariantGlyphDirty();
555}
556
557Optional<int> RenderMathMLToken::firstLineBaseline() const
558{
559 if (m_mathVariantCodePoint) {
560 auto mathVariantGlyph = style().fontCascade().glyphDataForCharacter(m_mathVariantCodePoint.value(), m_mathVariantIsMirrored);
561 if (mathVariantGlyph.font)
562 return Optional<int>(static_cast<int>(lroundf(-mathVariantGlyph.font->boundsForGlyph(mathVariantGlyph.glyph).y())));
563 }
564 return RenderMathMLBlock::firstLineBaseline();
565}
566
567void RenderMathMLToken::layoutBlock(bool relayoutChildren, LayoutUnit pageLogicalHeight)
568{
569 ASSERT(needsLayout());
570
571 if (!relayoutChildren && simplifiedLayout())
572 return;
573
574 GlyphData mathVariantGlyph;
575 if (m_mathVariantCodePoint)
576 mathVariantGlyph = style().fontCascade().glyphDataForCharacter(m_mathVariantCodePoint.value(), m_mathVariantIsMirrored);
577
578 if (!mathVariantGlyph.font) {
579 RenderMathMLBlock::layoutBlock(relayoutChildren, pageLogicalHeight);
580 return;
581 }
582
583 for (auto* child = firstChildBox(); child; child = child->nextSiblingBox())
584 child->layoutIfNeeded();
585
586 setLogicalWidth(mathVariantGlyph.font->widthForGlyph(mathVariantGlyph.glyph));
587 setLogicalHeight(mathVariantGlyph.font->boundsForGlyph(mathVariantGlyph.glyph).height());
588
589 updateScrollInfoAfterLayout();
590
591 clearNeedsLayout();
592}
593
594void RenderMathMLToken::paint(PaintInfo& info, const LayoutPoint& paintOffset)
595{
596 RenderMathMLBlock::paint(info, paintOffset);
597
598 // FIXME: Instead of using DrawGlyph, we may consider using the more general TextPainter so that we can apply mathvariant to strings with an arbitrary number of characters and preserve advanced CSS effects (text-shadow, etc).
599 if (info.context().paintingDisabled() || info.phase != PaintPhase::Foreground || style().visibility() != Visibility::Visible || !m_mathVariantCodePoint)
600 return;
601
602 auto mathVariantGlyph = style().fontCascade().glyphDataForCharacter(m_mathVariantCodePoint.value(), m_mathVariantIsMirrored);
603 if (!mathVariantGlyph.font)
604 return;
605
606 GraphicsContextStateSaver stateSaver(info.context());
607 info.context().setFillColor(style().visitedDependentColorWithColorFilter(CSSPropertyColor));
608
609 GlyphBuffer buffer;
610 buffer.add(mathVariantGlyph.glyph, mathVariantGlyph.font, mathVariantGlyph.font->widthForGlyph(mathVariantGlyph.glyph));
611 LayoutUnit glyphAscent = static_cast<int>(lroundf(-mathVariantGlyph.font->boundsForGlyph(mathVariantGlyph.glyph).y()));
612 info.context().drawGlyphs(*mathVariantGlyph.font, buffer, 0, 1, paintOffset + location() + LayoutPoint(0_lu, glyphAscent), style().fontCascade().fontDescription().fontSmoothing());
613}
614
615void RenderMathMLToken::paintChildren(PaintInfo& paintInfo, const LayoutPoint& paintOffset, PaintInfo& paintInfoForChild, bool usePrintRect)
616{
617 if (m_mathVariantCodePoint) {
618 auto mathVariantGlyph = style().fontCascade().glyphDataForCharacter(m_mathVariantCodePoint.value(), m_mathVariantIsMirrored);
619 if (mathVariantGlyph.font)
620 return;
621 }
622
623 RenderMathMLBlock::paintChildren(paintInfo, paintOffset, paintInfoForChild, usePrintRect);
624}
625
626}
627
628#endif // ENABLE(MATHML)
629