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