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