Bumping manifests a=b2g-bump
[gecko.git] / layout / generic / MathMLTextRunFactory.cpp
blob19acb216423aa843701807cace73444750a7f95b
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "MathMLTextRunFactory.h"
8 #include "mozilla/ArrayUtils.h"
10 #include "nsStyleConsts.h"
11 #include "nsStyleContext.h"
12 #include "nsTextFrameUtils.h"
13 #include "nsFontMetrics.h"
14 #include "nsDeviceContext.h"
16 using namespace mozilla;
19 Entries for the mathvariant lookup tables. mKey represents the Unicode
20 character to be transformed and is used for searching the tables.
21 mReplacement represents the mapped mathvariant Unicode character.
23 typedef struct
25 uint32_t mKey;
26 uint32_t mReplacement;
27 } MathVarMapping;
30 Lookup tables for use with mathvariant mappings to transform a unicode
31 character point to another unicode character that indicates the proper output.
32 mKey represents one of two concepts.
33 1. In the Latin table it represents a hole in the mathematical alphanumeric
34 block, where the character that should occupy that position is located
35 elsewhere.
36 2. It represents an Arabic letter.
38 As a replacement, 0 is reserved to indicate no mapping was found.
40 static const MathVarMapping gArabicInitialMapTable[] = {
41 { 0x628, 0x1EE21 },
42 { 0x62A, 0x1EE35 },
43 { 0x62B, 0x1EE36 },
44 { 0x62C, 0x1EE22 },
45 { 0x62D, 0x1EE27 },
46 { 0x62E, 0x1EE37 },
47 { 0x633, 0x1EE2E },
48 { 0x634, 0x1EE34 },
49 { 0x635, 0x1EE31 },
50 { 0x636, 0x1EE39 },
51 { 0x639, 0x1EE2F },
52 { 0x63A, 0x1EE3B },
53 { 0x641, 0x1EE30 },
54 { 0x642, 0x1EE32 },
55 { 0x643, 0x1EE2A },
56 { 0x644, 0x1EE2B },
57 { 0x645, 0x1EE2C },
58 { 0x646, 0x1EE2D },
59 { 0x647, 0x1EE24 },
60 { 0x64A, 0x1EE29 }
63 static const MathVarMapping gArabicTailedMapTable[] = {
64 { 0x62C, 0x1EE42 },
65 { 0x62D, 0x1EE47 },
66 { 0x62E, 0x1EE57 },
67 { 0x633, 0x1EE4E },
68 { 0x634, 0x1EE54 },
69 { 0x635, 0x1EE51 },
70 { 0x636, 0x1EE59 },
71 { 0x639, 0x1EE4F },
72 { 0x63A, 0x1EE5B },
73 { 0x642, 0x1EE52 },
74 { 0x644, 0x1EE4B },
75 { 0x646, 0x1EE4D },
76 { 0x64A, 0x1EE49 },
77 { 0x66F, 0x1EE5F },
78 { 0x6BA, 0x1EE5D }
81 static const MathVarMapping gArabicStretchedMapTable[] = {
82 { 0x628, 0x1EE61 },
83 { 0x62A, 0x1EE75 },
84 { 0x62B, 0x1EE76 },
85 { 0x62C, 0x1EE62 },
86 { 0x62D, 0x1EE67 },
87 { 0x62E, 0x1EE77 },
88 { 0x633, 0x1EE6E },
89 { 0x634, 0x1EE74 },
90 { 0x635, 0x1EE71 },
91 { 0x636, 0x1EE79 },
92 { 0x637, 0x1EE68 },
93 { 0x638, 0x1EE7A },
94 { 0x639, 0x1EE6F },
95 { 0x63A, 0x1EE7B },
96 { 0x641, 0x1EE70 },
97 { 0x642, 0x1EE72 },
98 { 0x643, 0x1EE6A },
99 { 0x645, 0x1EE6C },
100 { 0x646, 0x1EE6D },
101 { 0x647, 0x1EE64 },
102 { 0x64A, 0x1EE69 },
103 { 0x66E, 0x1EE7C },
104 { 0x6A1, 0x1EE7E }
107 static const MathVarMapping gArabicLoopedMapTable[] = {
108 { 0x627, 0x1EE80 },
109 { 0x628, 0x1EE81 },
110 { 0x62A, 0x1EE95 },
111 { 0x62B, 0x1EE96 },
112 { 0x62C, 0x1EE82 },
113 { 0x62D, 0x1EE87 },
114 { 0x62E, 0x1EE97 },
115 { 0x62F, 0x1EE83 },
116 { 0x630, 0x1EE98 },
117 { 0x631, 0x1EE93 },
118 { 0x632, 0x1EE86 },
119 { 0x633, 0x1EE8E },
120 { 0x634, 0x1EE94 },
121 { 0x635, 0x1EE91 },
122 { 0x636, 0x1EE99 },
123 { 0x637, 0x1EE88 },
124 { 0x638, 0x1EE9A },
125 { 0x639, 0x1EE8F },
126 { 0x63A, 0x1EE9B },
127 { 0x641, 0x1EE90 },
128 { 0x642, 0x1EE92 },
129 { 0x644, 0x1EE8B },
130 { 0x645, 0x1EE8C },
131 { 0x646, 0x1EE8D },
132 { 0x647, 0x1EE84 },
133 { 0x648, 0x1EE85 },
134 { 0x64A, 0x1EE89 }
137 static const MathVarMapping gArabicDoubleMapTable[] = {
138 { 0x628, 0x1EEA1 },
139 { 0x62A, 0x1EEB5 },
140 { 0x62B, 0x1EEB6 },
141 { 0x62C, 0x1EEA2 },
142 { 0x62D, 0x1EEA7 },
143 { 0x62E, 0x1EEB7 },
144 { 0x62F, 0x1EEA3 },
145 { 0x630, 0x1EEB8 },
146 { 0x631, 0x1EEB3 },
147 { 0x632, 0x1EEA6 },
148 { 0x633, 0x1EEAE },
149 { 0x634, 0x1EEB4 },
150 { 0x635, 0x1EEB1 },
151 { 0x636, 0x1EEB9 },
152 { 0x637, 0x1EEA8 },
153 { 0x638, 0x1EEBA },
154 { 0x639, 0x1EEAF },
155 { 0x63A, 0x1EEBB },
156 { 0x641, 0x1EEB0 },
157 { 0x642, 0x1EEB2 },
158 { 0x644, 0x1EEAB },
159 { 0x645, 0x1EEAC },
160 { 0x646, 0x1EEAD },
161 { 0x648, 0x1EEA5 },
162 { 0x64A, 0x1EEA9 }
165 static const MathVarMapping gLatinExceptionMapTable[] = {
166 { 0x1D455, 0x210E },
167 { 0x1D49D, 0x212C },
168 { 0x1D4A0, 0x2130 },
169 { 0x1D4A1, 0x2131 },
170 { 0x1D4A3, 0x210B },
171 { 0x1D4A4, 0x2110 },
172 { 0x1D4A7, 0x2112 },
173 { 0x1D4A8, 0x2133 },
174 { 0x1D4AD, 0x211B },
175 { 0x1D4BA, 0x212F },
176 { 0x1D4BC, 0x210A },
177 { 0x1D4C4, 0x2134 },
178 { 0x1D506, 0x212D },
179 { 0x1D50B, 0x210C },
180 { 0x1D50C, 0x2111 },
181 { 0x1D515, 0x211C },
182 { 0x1D51D, 0x2128 },
183 { 0x1D53A, 0x2102 },
184 { 0x1D53F, 0x210D },
185 { 0x1D545, 0x2115 },
186 { 0x1D547, 0x2119 },
187 { 0x1D548, 0x211A },
188 { 0x1D549, 0x211D },
189 { 0x1D551, 0x2124 }
192 // Finds a MathVarMapping struct with the specified key (aKey) within aTable.
193 // aTable must be an array, whose length is specified by aNumElements
194 static uint32_t
195 MathvarMappingSearch(uint32_t aKey, const MathVarMapping* aTable, uint32_t aNumElements)
197 uint32_t low = 0;
198 uint32_t high = aNumElements;
199 while (high > low) {
200 uint32_t midPoint = (low+high) >> 1;
201 if (aKey == aTable[midPoint].mKey) {
202 return aTable[midPoint].mReplacement;
204 if (aKey > aTable[midPoint].mKey) {
205 low = midPoint + 1;
206 } else {
207 high = midPoint;
210 return 0;
213 #define GREEK_UPPER_THETA 0x03F4
214 #define HOLE_GREEK_UPPER_THETA 0x03A2
215 #define NABLA 0x2207
216 #define PARTIAL_DIFFERENTIAL 0x2202
217 #define GREEK_UPPER_ALPHA 0x0391
218 #define GREEK_UPPER_OMEGA 0x03A9
219 #define GREEK_LOWER_ALPHA 0x03B1
220 #define GREEK_LOWER_OMEGA 0x03C9
221 #define GREEK_LUNATE_EPSILON_SYMBOL 0x03F5
222 #define GREEK_THETA_SYMBOL 0x03D1
223 #define GREEK_KAPPA_SYMBOL 0x03F0
224 #define GREEK_PHI_SYMBOL 0x03D5
225 #define GREEK_RHO_SYMBOL 0x03F1
226 #define GREEK_PI_SYMBOL 0x03D6
227 #define GREEK_LETTER_DIGAMMA 0x03DC
228 #define GREEK_SMALL_LETTER_DIGAMMA 0x03DD
229 #define MATH_BOLD_CAPITAL_DIGAMMA 0x1D7CA
230 #define MATH_BOLD_SMALL_DIGAMMA 0x1D7CB
232 #define LATIN_SMALL_LETTER_DOTLESS_I 0x0131
233 #define LATIN_SMALL_LETTER_DOTLESS_J 0x0237
235 #define MATH_ITALIC_SMALL_DOTLESS_I 0x1D6A4
236 #define MATH_ITALIC_SMALL_DOTLESS_J 0x1D6A5
238 #define MATH_BOLD_UPPER_A 0x1D400
239 #define MATH_ITALIC_UPPER_A 0x1D434
240 #define MATH_BOLD_SMALL_A 0x1D41A
241 #define MATH_BOLD_UPPER_ALPHA 0x1D6A8
242 #define MATH_BOLD_SMALL_ALPHA 0x1D6C2
243 #define MATH_ITALIC_UPPER_ALPHA 0x1D6E2
244 #define MATH_BOLD_DIGIT_ZERO 0x1D7CE
245 #define MATH_DOUBLE_STRUCK_ZERO 0x1D7D8
247 #define MATH_BOLD_UPPER_THETA 0x1D6B9
248 #define MATH_BOLD_NABLA 0x1D6C1
249 #define MATH_BOLD_PARTIAL_DIFFERENTIAL 0x1D6DB
250 #define MATH_BOLD_EPSILON_SYMBOL 0x1D6DC
251 #define MATH_BOLD_THETA_SYMBOL 0x1D6DD
252 #define MATH_BOLD_KAPPA_SYMBOL 0x1D6DE
253 #define MATH_BOLD_PHI_SYMBOL 0x1D6DF
254 #define MATH_BOLD_RHO_SYMBOL 0x1D6E0
255 #define MATH_BOLD_PI_SYMBOL 0x1D6E1
258 Performs the character mapping needed to implement MathML's mathvariant
259 attribute. It takes a unicode character and maps it to its appropriate
260 mathvariant counterpart specified by aMathVar. The mapped character is
261 typically located within Unicode's mathematical blocks (0x1D***, 0x1EE**) but
262 there are exceptions which this function accounts for.
263 Characters without a valid mapping or valid aMathvar value are returned
264 unaltered. Characters already in the mathematical blocks (or are one of the
265 exceptions) are never transformed.
266 Acceptable values for aMathVar are specified in layout/style/nsStyleConsts.h.
267 The transformable characters can be found at:
268 http://lists.w3.org/Archives/Public/www-math/2013Sep/0012.html and
269 https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols
271 static uint32_t
272 MathVariant(uint32_t aCh, uint8_t aMathVar)
274 uint32_t baseChar;
275 enum CharacterType {
276 kIsLatin,
277 kIsGreekish,
278 kIsNumber,
279 kIsArabic,
281 CharacterType varType;
283 int8_t multiplier;
285 if (aMathVar <= NS_MATHML_MATHVARIANT_NORMAL) {
286 // nothing to do here
287 return aCh;
289 if (aMathVar > NS_MATHML_MATHVARIANT_STRETCHED) {
290 NS_ASSERTION(false, "Illegal mathvariant value");
291 return aCh;
294 // Exceptional characters with at most one possible transformation
295 if (aCh == HOLE_GREEK_UPPER_THETA) {
296 // Nothing at this code point is transformed
297 return aCh;
299 if (aCh == GREEK_LETTER_DIGAMMA) {
300 if (aMathVar == NS_MATHML_MATHVARIANT_BOLD) {
301 return MATH_BOLD_CAPITAL_DIGAMMA;
303 return aCh;
305 if (aCh == GREEK_SMALL_LETTER_DIGAMMA) {
306 if (aMathVar == NS_MATHML_MATHVARIANT_BOLD) {
307 return MATH_BOLD_SMALL_DIGAMMA;
309 return aCh;
311 if (aCh == LATIN_SMALL_LETTER_DOTLESS_I) {
312 if (aMathVar == NS_MATHML_MATHVARIANT_ITALIC) {
313 return MATH_ITALIC_SMALL_DOTLESS_I;
315 return aCh;
317 if (aCh == LATIN_SMALL_LETTER_DOTLESS_J) {
318 if (aMathVar == NS_MATHML_MATHVARIANT_ITALIC) {
319 return MATH_ITALIC_SMALL_DOTLESS_J;
321 return aCh;
324 // The Unicode mathematical blocks are divided into four segments: Latin,
325 // Greek, numbers and Arabic. In the case of the first three
326 // baseChar represents the relative order in which the characters are
327 // encoded in the Unicode mathematical block, normalised to the first
328 // character of that sequence.
330 if ('A' <= aCh && aCh <= 'Z') {
331 baseChar = aCh - 'A';
332 varType = kIsLatin;
333 } else if ('a' <= aCh && aCh <= 'z') {
334 // Lowercase characters are placed immediately after the uppercase
335 // characters in the Unicode mathematical block. The constant subtraction
336 // represents the number of characters between the start of the sequence
337 // (capital A) and the first lowercase letter.
338 baseChar = MATH_BOLD_SMALL_A-MATH_BOLD_UPPER_A + aCh - 'a';
339 varType = kIsLatin;
340 } else if ('0' <= aCh && aCh <= '9') {
341 baseChar = aCh - '0';
342 varType = kIsNumber;
343 } else if (GREEK_UPPER_ALPHA <= aCh && aCh <= GREEK_UPPER_OMEGA) {
344 baseChar = aCh-GREEK_UPPER_ALPHA;
345 varType = kIsGreekish;
346 } else if (GREEK_LOWER_ALPHA <= aCh && aCh <= GREEK_LOWER_OMEGA) {
347 // Lowercase Greek comes after uppercase Greek.
348 // Note in this instance the presence of an additional character (Nabla)
349 // between the end of the uppercase Greek characters and the lowercase
350 // ones.
351 baseChar = MATH_BOLD_SMALL_ALPHA - MATH_BOLD_UPPER_ALPHA
352 + aCh-GREEK_LOWER_ALPHA;
353 varType = kIsGreekish;
354 } else if (0x0600 <= aCh && aCh <= 0x06FF) {
355 // Arabic characters are defined within this range
356 varType = kIsArabic;
357 } else {
358 switch (aCh) {
359 case GREEK_UPPER_THETA:
360 baseChar = MATH_BOLD_UPPER_THETA-MATH_BOLD_UPPER_ALPHA;
361 break;
362 case NABLA:
363 baseChar = MATH_BOLD_NABLA-MATH_BOLD_UPPER_ALPHA;
364 break;
365 case PARTIAL_DIFFERENTIAL:
366 baseChar = MATH_BOLD_PARTIAL_DIFFERENTIAL - MATH_BOLD_UPPER_ALPHA;
367 break;
368 case GREEK_LUNATE_EPSILON_SYMBOL:
369 baseChar = MATH_BOLD_EPSILON_SYMBOL - MATH_BOLD_UPPER_ALPHA;
370 break;
371 case GREEK_THETA_SYMBOL:
372 baseChar = MATH_BOLD_THETA_SYMBOL - MATH_BOLD_UPPER_ALPHA;
373 break;
374 case GREEK_KAPPA_SYMBOL:
375 baseChar = MATH_BOLD_KAPPA_SYMBOL - MATH_BOLD_UPPER_ALPHA;
376 break;
377 case GREEK_PHI_SYMBOL:
378 baseChar = MATH_BOLD_PHI_SYMBOL - MATH_BOLD_UPPER_ALPHA;
379 break;
380 case GREEK_RHO_SYMBOL:
381 baseChar = MATH_BOLD_RHO_SYMBOL - MATH_BOLD_UPPER_ALPHA;
382 break;
383 case GREEK_PI_SYMBOL:
384 baseChar = MATH_BOLD_PI_SYMBOL - MATH_BOLD_UPPER_ALPHA;
385 break;
386 default:
387 return aCh;
390 varType = kIsGreekish;
393 if (varType == kIsNumber) {
394 switch (aMathVar) {
395 // Each possible number mathvariant is encoded in a single, contiguous
396 // block. For example the beginning of the double struck number range
397 // follows immediately after the end of the bold number range.
398 // multiplier represents the order of the sequences relative to the first
399 // one.
400 case NS_MATHML_MATHVARIANT_BOLD:
401 multiplier = 0;
402 break;
403 case NS_MATHML_MATHVARIANT_DOUBLE_STRUCK:
404 multiplier = 1;
405 break;
406 case NS_MATHML_MATHVARIANT_SANS_SERIF:
407 multiplier = 2;
408 break;
409 case NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF:
410 multiplier = 3;
411 break;
412 case NS_MATHML_MATHVARIANT_MONOSPACE:
413 multiplier = 4;
414 break;
415 default:
416 // This mathvariant isn't defined for numbers or is otherwise normal
417 return aCh;
419 // As the ranges are contiguous, to find the desired mathvariant range it
420 // is sufficient to multiply the position within the sequence order
421 // (multiplier) with the period of the sequence (which is constant for all
422 // number sequences) and to add the character point of the first character
423 // within the number mathvariant range.
424 // To this the baseChar calculated earlier is added to obtain the final
425 // code point.
426 return baseChar+multiplier*(MATH_DOUBLE_STRUCK_ZERO-MATH_BOLD_DIGIT_ZERO)
427 +MATH_BOLD_DIGIT_ZERO;
428 } else if (varType == kIsGreekish) {
429 switch (aMathVar) {
430 case NS_MATHML_MATHVARIANT_BOLD:
431 multiplier = 0;
432 break;
433 case NS_MATHML_MATHVARIANT_ITALIC:
434 multiplier = 1;
435 break;
436 case NS_MATHML_MATHVARIANT_BOLD_ITALIC:
437 multiplier = 2;
438 break;
439 case NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF:
440 multiplier = 3;
441 break;
442 case NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC:
443 multiplier = 4;
444 break;
445 default:
446 // This mathvariant isn't defined for Greek or is otherwise normal
447 return aCh;
449 // See the kIsNumber case for an explanation of the following calculation
450 return baseChar + MATH_BOLD_UPPER_ALPHA +
451 multiplier*(MATH_ITALIC_UPPER_ALPHA - MATH_BOLD_UPPER_ALPHA);
454 uint32_t tempChar;
455 uint32_t newChar;
456 if (varType == kIsArabic) {
457 const MathVarMapping* mapTable;
458 uint32_t tableLength;
459 switch (aMathVar) {
460 /* The Arabic mathematical block is not continuous, nor does it have a
461 * monotonic mapping to the unencoded characters, requiring the use of a
462 * lookup table.
464 case NS_MATHML_MATHVARIANT_INITIAL:
465 mapTable = gArabicInitialMapTable;
466 tableLength = ArrayLength(gArabicInitialMapTable);
467 break;
468 case NS_MATHML_MATHVARIANT_TAILED:
469 mapTable = gArabicTailedMapTable;
470 tableLength = ArrayLength(gArabicTailedMapTable);
471 break;
472 case NS_MATHML_MATHVARIANT_STRETCHED:
473 mapTable = gArabicStretchedMapTable;
474 tableLength = ArrayLength(gArabicStretchedMapTable);
475 break;
476 case NS_MATHML_MATHVARIANT_LOOPED:
477 mapTable = gArabicLoopedMapTable;
478 tableLength = ArrayLength(gArabicLoopedMapTable);
479 break;
480 case NS_MATHML_MATHVARIANT_DOUBLE_STRUCK:
481 mapTable = gArabicDoubleMapTable;
482 tableLength = ArrayLength(gArabicDoubleMapTable);
483 break;
484 default:
485 // No valid transformations exist
486 return aCh;
488 newChar = MathvarMappingSearch(aCh, mapTable, tableLength);
489 } else {
490 // Must be Latin
491 if (aMathVar > NS_MATHML_MATHVARIANT_MONOSPACE) {
492 // Latin doesn't support the Arabic mathvariants
493 return aCh;
495 multiplier = aMathVar - 2;
496 // This is possible because the values for NS_MATHML_MATHVARIANT_* are
497 // chosen to coincide with the order in which the encoded mathvariant
498 // characters are located within their unicode block (less an offset to
499 // avoid _NONE and _NORMAL variants)
500 // See the kIsNumber case for an explanation of the following calculation
501 tempChar = baseChar + MATH_BOLD_UPPER_A +
502 multiplier*(MATH_ITALIC_UPPER_A - MATH_BOLD_UPPER_A);
503 // There are roughly twenty characters that are located outside of the
504 // mathematical block, so the spaces where they ought to be are used
505 // as keys for a lookup table containing the correct character mappings.
506 newChar = MathvarMappingSearch(tempChar, gLatinExceptionMapTable,
507 ArrayLength(gLatinExceptionMapTable));
510 if (newChar) {
511 return newChar;
512 } else if (varType == kIsLatin) {
513 return tempChar;
514 } else {
515 // An Arabic character without a corresponding mapping
516 return aCh;
521 void
522 MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
523 gfxContext* aRefContext)
525 gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
527 nsAutoString convertedString;
528 nsAutoTArray<bool,50> charsToMergeArray;
529 nsAutoTArray<bool,50> deletedCharsArray;
530 nsAutoTArray<nsStyleContext*,50> styleArray;
531 nsAutoTArray<uint8_t,50> canBreakBeforeArray;
532 bool mergeNeeded = false;
534 bool singleCharMI =
535 aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SINGLE_CHAR_MI;
537 uint32_t length = aTextRun->GetLength();
538 const char16_t* str = aTextRun->mString.BeginReading();
539 nsRefPtr<nsStyleContext>* styles = aTextRun->mStyles.Elements();
540 nsFont font;
541 if (length) {
542 font = styles[0]->StyleFont()->mFont;
544 if (mSSTYScriptLevel) {
545 bool found = false;
546 // We respect ssty settings explicitly set by the user
547 for (uint32_t i = 0; i < font.fontFeatureSettings.Length(); i++) {
548 if (font.fontFeatureSettings[i].mTag == TRUETYPE_TAG('s', 's', 't', 'y')) {
549 found = true;
550 break;
553 if (!found) {
554 uint8_t sstyLevel = 0;
555 float scriptScaling = pow(styles[0]->StyleFont()->mScriptSizeMultiplier,
556 mSSTYScriptLevel);
557 static_assert(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER < 1,
558 "Shouldn't it make things smaller?");
560 An SSTY level of 2 is set if the scaling factor is less than or equal
561 to halfway between that for a scriptlevel of 1 (0.71) and that of a
562 scriptlevel of 2 (0.71^2), assuming the default script size multiplier.
563 An SSTY level of 1 is set if the script scaling factor is less than
564 or equal that for a scriptlevel of 1 assuming the default script size
565 multiplier.
567 User specified values of script size multiplier will change the scaling
568 factor which mSSTYScriptLevel values correspond to.
570 In the event that the script size multiplier actually makes things
571 larger, no change is made.
573 To opt out of this change, add the following to the stylesheet:
574 "font-feature-settings: 'ssty' 0"
576 if (scriptScaling <= (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER +
577 (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER *
578 NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER))/2) {
579 // Currently only the first two ssty settings are used, so two is large
580 // as we go
581 sstyLevel = 2;
582 } else if (scriptScaling <= NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER) {
583 sstyLevel = 1;
585 if (sstyLevel) {
586 gfxFontFeature settingSSTY;
587 settingSSTY.mTag = TRUETYPE_TAG('s','s','t','y');
588 settingSSTY.mValue = sstyLevel;
589 font.fontFeatureSettings.AppendElement(settingSSTY);
595 uint8_t mathVar = NS_MATHML_MATHVARIANT_NONE;
596 bool doMathvariantStyling = true;
598 for (uint32_t i = 0; i < length; ++i) {
599 int extraChars = 0;
600 nsStyleContext* styleContext = styles[i];
601 mathVar = styleContext->StyleFont()->mMathVariant;
603 if (singleCharMI && mathVar == NS_MATHML_MATHVARIANT_NONE) {
604 // If the user has explicitly set a non-default value for fontstyle or
605 // fontweight, the italic mathvariant behaviour of <mi> is disabled
606 // This overrides the initial values specified in fontStyle, to avoid
607 // inconsistencies in which attributes allow CSS changes and which do not.
608 if (mFlags & MATH_FONT_WEIGHT_BOLD) {
609 font.weight = NS_FONT_WEIGHT_BOLD;
610 if (mFlags & MATH_FONT_STYLING_NORMAL) {
611 font.style = NS_FONT_STYLE_NORMAL;
612 } else {
613 font.style = NS_FONT_STYLE_ITALIC;
615 } else if (mFlags & MATH_FONT_STYLING_NORMAL) {
616 font.style = NS_FONT_STYLE_NORMAL;
617 font.weight = NS_FONT_WEIGHT_NORMAL;
618 } else {
619 mathVar = NS_MATHML_MATHVARIANT_ITALIC;
623 uint32_t ch = str[i];
624 if (NS_IS_HIGH_SURROGATE(ch) && i < length - 1 &&
625 NS_IS_LOW_SURROGATE(str[i + 1])) {
626 ch = SURROGATE_TO_UCS4(ch, str[i + 1]);
628 uint32_t ch2 = MathVariant(ch, mathVar);
630 if (mathVar == NS_MATHML_MATHVARIANT_BOLD ||
631 mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC ||
632 mathVar == NS_MATHML_MATHVARIANT_ITALIC) {
633 if (ch == ch2 && ch != 0x20 && ch != 0xA0) {
634 // Don't apply the CSS style if a character cannot be
635 // transformed. There is an exception for whitespace as it is both
636 // common and innocuous.
637 doMathvariantStyling = false;
639 if (ch2 != ch) {
640 // Bug 930504. Some platforms do not have fonts for Mathematical
641 // Alphanumeric Symbols. Hence we check whether the transformed
642 // character is actually available.
643 uint8_t matchType;
644 nsRefPtr<gfxFont> mathFont = fontGroup->
645 FindFontForChar(ch2, 0, HB_SCRIPT_COMMON, nullptr, &matchType);
646 if (mathFont) {
647 // Don't apply the CSS style if there is a math font for at least one
648 // of the transformed character in this text run.
649 doMathvariantStyling = false;
650 } else {
651 // We fallback to the original character.
652 ch2 = ch;
657 deletedCharsArray.AppendElement(false);
658 charsToMergeArray.AppendElement(false);
659 styleArray.AppendElement(styleContext);
660 canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i));
662 if (IS_IN_BMP(ch2)) {
663 convertedString.Append(ch2);
664 } else {
665 convertedString.Append(H_SURROGATE(ch2));
666 convertedString.Append(L_SURROGATE(ch2));
667 ++extraChars;
668 if (!IS_IN_BMP(ch)) {
669 deletedCharsArray.AppendElement(true); // not exactly deleted, but
670 // the trailing surrogate is skipped
671 ++i;
675 while (extraChars-- > 0) {
676 mergeNeeded = true;
677 charsToMergeArray.AppendElement(true);
678 styleArray.AppendElement(styleContext);
679 canBreakBeforeArray.AppendElement(false);
683 uint32_t flags;
684 gfxTextRunFactory::Parameters innerParams =
685 GetParametersForInner(aTextRun, &flags, aRefContext);
687 nsAutoPtr<nsTransformedTextRun> transformedChild;
688 nsAutoPtr<gfxTextRun> cachedChild;
689 gfxTextRun* child;
691 if (mathVar == NS_MATHML_MATHVARIANT_BOLD && doMathvariantStyling) {
692 font.style = NS_FONT_STYLE_NORMAL;
693 font.weight = NS_FONT_WEIGHT_BOLD;
694 } else if (mathVar == NS_MATHML_MATHVARIANT_ITALIC && doMathvariantStyling) {
695 font.style = NS_FONT_STYLE_ITALIC;
696 font.weight = NS_FONT_WEIGHT_NORMAL;
697 } else if (mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC &&
698 doMathvariantStyling) {
699 font.style = NS_FONT_STYLE_ITALIC;
700 font.weight = NS_FONT_WEIGHT_BOLD;
701 } else if (mathVar != NS_MATHML_MATHVARIANT_NONE) {
702 // Mathvariant overrides fontstyle and fontweight
703 // Need to check to see if mathvariant is actually applied as this function
704 // is used for other purposes.
705 font.style = NS_FONT_STYLE_NORMAL;
706 font.weight = NS_FONT_WEIGHT_NORMAL;
708 gfxFontGroup* newFontGroup = nullptr;
710 // Get the correct gfxFontGroup that corresponds to the earlier font changes.
711 if (length) {
712 nsPresContext* pc = styles[0]->PresContext();
713 nsRefPtr<nsFontMetrics> metrics;
714 pc->DeviceContext()->GetMetricsFor(font,
715 styles[0]->StyleFont()->mLanguage,
716 pc->GetUserFontSet(),
717 pc->GetTextPerfMetrics(),
718 *getter_AddRefs(metrics));
719 if (metrics) {
720 newFontGroup = metrics->GetThebesFontGroup();
724 if (!newFontGroup) {
725 // If we can't get a new font group, fall back to the old one. Rendering
726 // will be incorrect, but not significantly so.
727 newFontGroup = fontGroup;
730 if (mInnerTransformingTextRunFactory) {
731 transformedChild = mInnerTransformingTextRunFactory->MakeTextRun(
732 convertedString.BeginReading(), convertedString.Length(),
733 &innerParams, newFontGroup, flags, styleArray.Elements(), false);
734 child = transformedChild.get();
735 } else {
736 cachedChild = newFontGroup->MakeTextRun(
737 convertedString.BeginReading(), convertedString.Length(),
738 &innerParams, flags);
739 child = cachedChild.get();
741 if (!child)
742 return;
743 // Copy potential linebreaks into child so they're preserved
744 // (and also child will be shaped appropriately)
745 NS_ASSERTION(convertedString.Length() == canBreakBeforeArray.Length(),
746 "Dropped characters or break-before values somewhere!");
747 child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(),
748 canBreakBeforeArray.Elements(), aRefContext);
749 if (transformedChild) {
750 transformedChild->FinishSettingProperties(aRefContext);
753 if (mergeNeeded) {
754 // Now merge multiple characters into one multi-glyph character as required
755 NS_ASSERTION(charsToMergeArray.Length() == child->GetLength(),
756 "source length mismatch");
757 NS_ASSERTION(deletedCharsArray.Length() == aTextRun->GetLength(),
758 "destination length mismatch");
759 MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements(),
760 deletedCharsArray.Elements());
761 } else {
762 // No merging to do, so just copy; this produces a more optimized textrun.
763 // We can't steal the data because the child may be cached and stealing
764 // the data would break the cache.
765 aTextRun->ResetGlyphRuns();
766 aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), 0);