1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "MathMLTextRunFactory.h"
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/BinarySearch.h"
11 #include "mozilla/ComputedStyle.h"
12 #include "mozilla/ComputedStyleInlines.h"
14 #include "nsStyleConsts.h"
15 #include "nsTextFrameUtils.h"
16 #include "nsFontMetrics.h"
17 #include "nsDeviceContext.h"
18 #include "nsUnicodeScriptCodes.h"
20 using namespace mozilla
;
23 Entries for the mathvariant lookup tables. mKey represents the Unicode
24 character to be transformed and is used for searching the tables.
25 mReplacement represents the mapped mathvariant Unicode character.
29 uint32_t mReplacement
;
33 Lookup tables for use with mathvariant mappings to transform a unicode
34 character point to another unicode character that indicates the proper output.
35 mKey represents one of two concepts.
36 1. In the Latin table it represents a hole in the mathematical alphanumeric
37 block, where the character that should occupy that position is located
39 2. It represents an Arabic letter.
41 As a replacement, 0 is reserved to indicate no mapping was found.
43 static const MathVarMapping gArabicInitialMapTable
[] = {
44 {0x628, 0x1EE21}, {0x62A, 0x1EE35}, {0x62B, 0x1EE36}, {0x62C, 0x1EE22},
45 {0x62D, 0x1EE27}, {0x62E, 0x1EE37}, {0x633, 0x1EE2E}, {0x634, 0x1EE34},
46 {0x635, 0x1EE31}, {0x636, 0x1EE39}, {0x639, 0x1EE2F}, {0x63A, 0x1EE3B},
47 {0x641, 0x1EE30}, {0x642, 0x1EE32}, {0x643, 0x1EE2A}, {0x644, 0x1EE2B},
48 {0x645, 0x1EE2C}, {0x646, 0x1EE2D}, {0x647, 0x1EE24}, {0x64A, 0x1EE29}};
50 static const MathVarMapping gArabicTailedMapTable
[] = {
51 {0x62C, 0x1EE42}, {0x62D, 0x1EE47}, {0x62E, 0x1EE57}, {0x633, 0x1EE4E},
52 {0x634, 0x1EE54}, {0x635, 0x1EE51}, {0x636, 0x1EE59}, {0x639, 0x1EE4F},
53 {0x63A, 0x1EE5B}, {0x642, 0x1EE52}, {0x644, 0x1EE4B}, {0x646, 0x1EE4D},
54 {0x64A, 0x1EE49}, {0x66F, 0x1EE5F}, {0x6BA, 0x1EE5D}};
56 static const MathVarMapping gArabicStretchedMapTable
[] = {
57 {0x628, 0x1EE61}, {0x62A, 0x1EE75}, {0x62B, 0x1EE76}, {0x62C, 0x1EE62},
58 {0x62D, 0x1EE67}, {0x62E, 0x1EE77}, {0x633, 0x1EE6E}, {0x634, 0x1EE74},
59 {0x635, 0x1EE71}, {0x636, 0x1EE79}, {0x637, 0x1EE68}, {0x638, 0x1EE7A},
60 {0x639, 0x1EE6F}, {0x63A, 0x1EE7B}, {0x641, 0x1EE70}, {0x642, 0x1EE72},
61 {0x643, 0x1EE6A}, {0x645, 0x1EE6C}, {0x646, 0x1EE6D}, {0x647, 0x1EE64},
62 {0x64A, 0x1EE69}, {0x66E, 0x1EE7C}, {0x6A1, 0x1EE7E}};
64 static const MathVarMapping gArabicLoopedMapTable
[] = {
65 {0x627, 0x1EE80}, {0x628, 0x1EE81}, {0x62A, 0x1EE95}, {0x62B, 0x1EE96},
66 {0x62C, 0x1EE82}, {0x62D, 0x1EE87}, {0x62E, 0x1EE97}, {0x62F, 0x1EE83},
67 {0x630, 0x1EE98}, {0x631, 0x1EE93}, {0x632, 0x1EE86}, {0x633, 0x1EE8E},
68 {0x634, 0x1EE94}, {0x635, 0x1EE91}, {0x636, 0x1EE99}, {0x637, 0x1EE88},
69 {0x638, 0x1EE9A}, {0x639, 0x1EE8F}, {0x63A, 0x1EE9B}, {0x641, 0x1EE90},
70 {0x642, 0x1EE92}, {0x644, 0x1EE8B}, {0x645, 0x1EE8C}, {0x646, 0x1EE8D},
71 {0x647, 0x1EE84}, {0x648, 0x1EE85}, {0x64A, 0x1EE89}};
73 static const MathVarMapping gArabicDoubleMapTable
[] = {
74 {0x628, 0x1EEA1}, {0x62A, 0x1EEB5}, {0x62B, 0x1EEB6}, {0x62C, 0x1EEA2},
75 {0x62D, 0x1EEA7}, {0x62E, 0x1EEB7}, {0x62F, 0x1EEA3}, {0x630, 0x1EEB8},
76 {0x631, 0x1EEB3}, {0x632, 0x1EEA6}, {0x633, 0x1EEAE}, {0x634, 0x1EEB4},
77 {0x635, 0x1EEB1}, {0x636, 0x1EEB9}, {0x637, 0x1EEA8}, {0x638, 0x1EEBA},
78 {0x639, 0x1EEAF}, {0x63A, 0x1EEBB}, {0x641, 0x1EEB0}, {0x642, 0x1EEB2},
79 {0x644, 0x1EEAB}, {0x645, 0x1EEAC}, {0x646, 0x1EEAD}, {0x648, 0x1EEA5},
82 static const MathVarMapping gLatinExceptionMapTable
[] = {
83 {0x1D455, 0x210E}, {0x1D49D, 0x212C}, {0x1D4A0, 0x2130}, {0x1D4A1, 0x2131},
84 {0x1D4A3, 0x210B}, {0x1D4A4, 0x2110}, {0x1D4A7, 0x2112}, {0x1D4A8, 0x2133},
85 {0x1D4AD, 0x211B}, {0x1D4BA, 0x212F}, {0x1D4BC, 0x210A}, {0x1D4C4, 0x2134},
86 {0x1D506, 0x212D}, {0x1D50B, 0x210C}, {0x1D50C, 0x2111}, {0x1D515, 0x211C},
87 {0x1D51D, 0x2128}, {0x1D53A, 0x2102}, {0x1D53F, 0x210D}, {0x1D545, 0x2115},
88 {0x1D547, 0x2119}, {0x1D548, 0x211A}, {0x1D549, 0x211D}, {0x1D551, 0x2124}};
92 struct MathVarMappingWrapper
{
93 const MathVarMapping
* const mTable
;
94 explicit MathVarMappingWrapper(const MathVarMapping
* aTable
)
96 uint32_t operator[](size_t index
) const { return mTable
[index
].mKey
; }
101 // Finds a MathVarMapping struct with the specified key (aKey) within aTable.
102 // aTable must be an array, whose length is specified by aNumElements
103 static uint32_t MathvarMappingSearch(uint32_t aKey
,
104 const MathVarMapping
* aTable
,
105 uint32_t aNumElements
) {
107 if (BinarySearch(MathVarMappingWrapper(aTable
), 0, aNumElements
, aKey
,
109 return aTable
[index
].mReplacement
;
115 #define GREEK_UPPER_THETA 0x03F4
116 #define HOLE_GREEK_UPPER_THETA 0x03A2
118 #define PARTIAL_DIFFERENTIAL 0x2202
119 #define GREEK_UPPER_ALPHA 0x0391
120 #define GREEK_UPPER_OMEGA 0x03A9
121 #define GREEK_LOWER_ALPHA 0x03B1
122 #define GREEK_LOWER_OMEGA 0x03C9
123 #define GREEK_LUNATE_EPSILON_SYMBOL 0x03F5
124 #define GREEK_THETA_SYMBOL 0x03D1
125 #define GREEK_KAPPA_SYMBOL 0x03F0
126 #define GREEK_PHI_SYMBOL 0x03D5
127 #define GREEK_RHO_SYMBOL 0x03F1
128 #define GREEK_PI_SYMBOL 0x03D6
129 #define GREEK_LETTER_DIGAMMA 0x03DC
130 #define GREEK_SMALL_LETTER_DIGAMMA 0x03DD
131 #define MATH_BOLD_CAPITAL_DIGAMMA 0x1D7CA
132 #define MATH_BOLD_SMALL_DIGAMMA 0x1D7CB
134 #define LATIN_SMALL_LETTER_DOTLESS_I 0x0131
135 #define LATIN_SMALL_LETTER_DOTLESS_J 0x0237
137 #define MATH_ITALIC_SMALL_DOTLESS_I 0x1D6A4
138 #define MATH_ITALIC_SMALL_DOTLESS_J 0x1D6A5
140 #define MATH_BOLD_UPPER_A 0x1D400
141 #define MATH_ITALIC_UPPER_A 0x1D434
142 #define MATH_BOLD_SMALL_A 0x1D41A
143 #define MATH_BOLD_UPPER_ALPHA 0x1D6A8
144 #define MATH_BOLD_SMALL_ALPHA 0x1D6C2
145 #define MATH_ITALIC_UPPER_ALPHA 0x1D6E2
146 #define MATH_BOLD_DIGIT_ZERO 0x1D7CE
147 #define MATH_DOUBLE_STRUCK_ZERO 0x1D7D8
149 #define MATH_BOLD_UPPER_THETA 0x1D6B9
150 #define MATH_BOLD_NABLA 0x1D6C1
151 #define MATH_BOLD_PARTIAL_DIFFERENTIAL 0x1D6DB
152 #define MATH_BOLD_EPSILON_SYMBOL 0x1D6DC
153 #define MATH_BOLD_THETA_SYMBOL 0x1D6DD
154 #define MATH_BOLD_KAPPA_SYMBOL 0x1D6DE
155 #define MATH_BOLD_PHI_SYMBOL 0x1D6DF
156 #define MATH_BOLD_RHO_SYMBOL 0x1D6E0
157 #define MATH_BOLD_PI_SYMBOL 0x1D6E1
160 Performs the character mapping needed to implement MathML's mathvariant
161 attribute. It takes a unicode character and maps it to its appropriate
162 mathvariant counterpart specified by aMathVar. The mapped character is
163 typically located within Unicode's mathematical blocks (0x1D***, 0x1EE**) but
164 there are exceptions which this function accounts for.
165 Characters without a valid mapping or valid aMathvar value are returned
166 unaltered. Characters already in the mathematical blocks (or are one of the
167 exceptions) are never transformed.
168 Acceptable values for aMathVar are specified in layout/style/nsStyleConsts.h.
169 The transformable characters can be found at:
170 http://lists.w3.org/Archives/Public/www-math/2013Sep/0012.html and
171 https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols
173 static uint32_t MathVariant(uint32_t aCh
, uint8_t aMathVar
) {
181 CharacterType varType
;
185 if (aMathVar
<= NS_MATHML_MATHVARIANT_NORMAL
) {
186 // nothing to do here
189 if (aMathVar
> NS_MATHML_MATHVARIANT_STRETCHED
) {
190 NS_ASSERTION(false, "Illegal mathvariant value");
194 // Exceptional characters with at most one possible transformation
195 if (aCh
== HOLE_GREEK_UPPER_THETA
) {
196 // Nothing at this code point is transformed
199 if (aCh
== GREEK_LETTER_DIGAMMA
) {
200 if (aMathVar
== NS_MATHML_MATHVARIANT_BOLD
) {
201 return MATH_BOLD_CAPITAL_DIGAMMA
;
205 if (aCh
== GREEK_SMALL_LETTER_DIGAMMA
) {
206 if (aMathVar
== NS_MATHML_MATHVARIANT_BOLD
) {
207 return MATH_BOLD_SMALL_DIGAMMA
;
211 if (aCh
== LATIN_SMALL_LETTER_DOTLESS_I
) {
212 if (aMathVar
== NS_MATHML_MATHVARIANT_ITALIC
) {
213 return MATH_ITALIC_SMALL_DOTLESS_I
;
217 if (aCh
== LATIN_SMALL_LETTER_DOTLESS_J
) {
218 if (aMathVar
== NS_MATHML_MATHVARIANT_ITALIC
) {
219 return MATH_ITALIC_SMALL_DOTLESS_J
;
224 // The Unicode mathematical blocks are divided into four segments: Latin,
225 // Greek, numbers and Arabic. In the case of the first three
226 // baseChar represents the relative order in which the characters are
227 // encoded in the Unicode mathematical block, normalised to the first
228 // character of that sequence.
230 if ('A' <= aCh
&& aCh
<= 'Z') {
231 baseChar
= aCh
- 'A';
233 } else if ('a' <= aCh
&& aCh
<= 'z') {
234 // Lowercase characters are placed immediately after the uppercase
235 // characters in the Unicode mathematical block. The constant subtraction
236 // represents the number of characters between the start of the sequence
237 // (capital A) and the first lowercase letter.
238 baseChar
= MATH_BOLD_SMALL_A
- MATH_BOLD_UPPER_A
+ aCh
- 'a';
240 } else if ('0' <= aCh
&& aCh
<= '9') {
241 baseChar
= aCh
- '0';
243 } else if (GREEK_UPPER_ALPHA
<= aCh
&& aCh
<= GREEK_UPPER_OMEGA
) {
244 baseChar
= aCh
- GREEK_UPPER_ALPHA
;
245 varType
= kIsGreekish
;
246 } else if (GREEK_LOWER_ALPHA
<= aCh
&& aCh
<= GREEK_LOWER_OMEGA
) {
247 // Lowercase Greek comes after uppercase Greek.
248 // Note in this instance the presence of an additional character (Nabla)
249 // between the end of the uppercase Greek characters and the lowercase
252 MATH_BOLD_SMALL_ALPHA
- MATH_BOLD_UPPER_ALPHA
+ aCh
- GREEK_LOWER_ALPHA
;
253 varType
= kIsGreekish
;
254 } else if (0x0600 <= aCh
&& aCh
<= 0x06FF) {
255 // Arabic characters are defined within this range
259 case GREEK_UPPER_THETA
:
260 baseChar
= MATH_BOLD_UPPER_THETA
- MATH_BOLD_UPPER_ALPHA
;
263 baseChar
= MATH_BOLD_NABLA
- MATH_BOLD_UPPER_ALPHA
;
265 case PARTIAL_DIFFERENTIAL
:
266 baseChar
= MATH_BOLD_PARTIAL_DIFFERENTIAL
- MATH_BOLD_UPPER_ALPHA
;
268 case GREEK_LUNATE_EPSILON_SYMBOL
:
269 baseChar
= MATH_BOLD_EPSILON_SYMBOL
- MATH_BOLD_UPPER_ALPHA
;
271 case GREEK_THETA_SYMBOL
:
272 baseChar
= MATH_BOLD_THETA_SYMBOL
- MATH_BOLD_UPPER_ALPHA
;
274 case GREEK_KAPPA_SYMBOL
:
275 baseChar
= MATH_BOLD_KAPPA_SYMBOL
- MATH_BOLD_UPPER_ALPHA
;
277 case GREEK_PHI_SYMBOL
:
278 baseChar
= MATH_BOLD_PHI_SYMBOL
- MATH_BOLD_UPPER_ALPHA
;
280 case GREEK_RHO_SYMBOL
:
281 baseChar
= MATH_BOLD_RHO_SYMBOL
- MATH_BOLD_UPPER_ALPHA
;
283 case GREEK_PI_SYMBOL
:
284 baseChar
= MATH_BOLD_PI_SYMBOL
- MATH_BOLD_UPPER_ALPHA
;
290 varType
= kIsGreekish
;
293 if (varType
== kIsNumber
) {
295 // Each possible number mathvariant is encoded in a single, contiguous
296 // block. For example the beginning of the double struck number range
297 // follows immediately after the end of the bold number range.
298 // multiplier represents the order of the sequences relative to the first
300 case NS_MATHML_MATHVARIANT_BOLD
:
303 case NS_MATHML_MATHVARIANT_DOUBLE_STRUCK
:
306 case NS_MATHML_MATHVARIANT_SANS_SERIF
:
309 case NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF
:
312 case NS_MATHML_MATHVARIANT_MONOSPACE
:
316 // This mathvariant isn't defined for numbers or is otherwise normal
319 // As the ranges are contiguous, to find the desired mathvariant range it
320 // is sufficient to multiply the position within the sequence order
321 // (multiplier) with the period of the sequence (which is constant for all
322 // number sequences) and to add the character point of the first character
323 // within the number mathvariant range.
324 // To this the baseChar calculated earlier is added to obtain the final
327 multiplier
* (MATH_DOUBLE_STRUCK_ZERO
- MATH_BOLD_DIGIT_ZERO
) +
328 MATH_BOLD_DIGIT_ZERO
;
329 } else if (varType
== kIsGreekish
) {
331 case NS_MATHML_MATHVARIANT_BOLD
:
334 case NS_MATHML_MATHVARIANT_ITALIC
:
337 case NS_MATHML_MATHVARIANT_BOLD_ITALIC
:
340 case NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF
:
343 case NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC
:
347 // This mathvariant isn't defined for Greek or is otherwise normal
350 // See the kIsNumber case for an explanation of the following calculation
351 return baseChar
+ MATH_BOLD_UPPER_ALPHA
+
352 multiplier
* (MATH_ITALIC_UPPER_ALPHA
- MATH_BOLD_UPPER_ALPHA
);
357 if (varType
== kIsArabic
) {
358 const MathVarMapping
* mapTable
;
359 uint32_t tableLength
;
361 /* The Arabic mathematical block is not continuous, nor does it have a
362 * monotonic mapping to the unencoded characters, requiring the use of a
365 case NS_MATHML_MATHVARIANT_INITIAL
:
366 mapTable
= gArabicInitialMapTable
;
367 tableLength
= ArrayLength(gArabicInitialMapTable
);
369 case NS_MATHML_MATHVARIANT_TAILED
:
370 mapTable
= gArabicTailedMapTable
;
371 tableLength
= ArrayLength(gArabicTailedMapTable
);
373 case NS_MATHML_MATHVARIANT_STRETCHED
:
374 mapTable
= gArabicStretchedMapTable
;
375 tableLength
= ArrayLength(gArabicStretchedMapTable
);
377 case NS_MATHML_MATHVARIANT_LOOPED
:
378 mapTable
= gArabicLoopedMapTable
;
379 tableLength
= ArrayLength(gArabicLoopedMapTable
);
381 case NS_MATHML_MATHVARIANT_DOUBLE_STRUCK
:
382 mapTable
= gArabicDoubleMapTable
;
383 tableLength
= ArrayLength(gArabicDoubleMapTable
);
386 // No valid transformations exist
389 newChar
= MathvarMappingSearch(aCh
, mapTable
, tableLength
);
392 if (aMathVar
> NS_MATHML_MATHVARIANT_MONOSPACE
) {
393 // Latin doesn't support the Arabic mathvariants
396 multiplier
= aMathVar
- 2;
397 // This is possible because the values for NS_MATHML_MATHVARIANT_* are
398 // chosen to coincide with the order in which the encoded mathvariant
399 // characters are located within their unicode block (less an offset to
400 // avoid _NONE and _NORMAL variants)
401 // See the kIsNumber case for an explanation of the following calculation
402 tempChar
= baseChar
+ MATH_BOLD_UPPER_A
+
403 multiplier
* (MATH_ITALIC_UPPER_A
- MATH_BOLD_UPPER_A
);
404 // There are roughly twenty characters that are located outside of the
405 // mathematical block, so the spaces where they ought to be are used
406 // as keys for a lookup table containing the correct character mappings.
407 newChar
= MathvarMappingSearch(tempChar
, gLatinExceptionMapTable
,
408 ArrayLength(gLatinExceptionMapTable
));
413 } else if (varType
== kIsLatin
) {
416 // An Arabic character without a corresponding mapping
421 #define TT_SSTY TRUETYPE_TAG('s', 's', 't', 'y')
422 #define TT_DTLS TRUETYPE_TAG('d', 't', 'l', 's')
424 void MathMLTextRunFactory::RebuildTextRun(
425 nsTransformedTextRun
* aTextRun
, mozilla::gfx::DrawTarget
* aRefDrawTarget
,
426 gfxMissingFontRecorder
* aMFR
) {
427 gfxFontGroup
* fontGroup
= aTextRun
->GetFontGroup();
429 nsAutoString convertedString
;
430 AutoTArray
<bool, 50> charsToMergeArray
;
431 AutoTArray
<bool, 50> deletedCharsArray
;
432 AutoTArray
<RefPtr
<nsTransformedCharStyle
>, 50> styleArray
;
433 AutoTArray
<uint8_t, 50> canBreakBeforeArray
;
434 bool mergeNeeded
= false;
437 !!(aTextRun
->GetFlags2() & nsTextFrameUtils::Flags::IsSingleCharMi
);
439 uint32_t length
= aTextRun
->GetLength();
440 const char16_t
* str
= aTextRun
->mString
.BeginReading();
441 const nsTArray
<RefPtr
<nsTransformedCharStyle
>>& styles
= aTextRun
->mStyles
;
444 font
= styles
[0]->mFont
;
446 if (mSSTYScriptLevel
|| (mFlags
& MATH_FONT_FEATURE_DTLS
)) {
447 bool foundSSTY
= false;
448 bool foundDTLS
= false;
449 // We respect ssty settings explicitly set by the user
450 for (uint32_t i
= 0; i
< font
.fontFeatureSettings
.Length(); i
++) {
451 if (font
.fontFeatureSettings
[i
].mTag
== TT_SSTY
) {
453 } else if (font
.fontFeatureSettings
[i
].mTag
== TT_DTLS
) {
457 if (mSSTYScriptLevel
&& !foundSSTY
) {
458 uint8_t sstyLevel
= 0;
459 float scriptScaling
=
460 pow(styles
[0]->mScriptSizeMultiplier
, mSSTYScriptLevel
);
461 static_assert(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER
< 1,
462 "Shouldn't it make things smaller?");
464 An SSTY level of 2 is set if the scaling factor is less than or equal
465 to halfway between that for a scriptlevel of 1 (0.71) and that of a
466 scriptlevel of 2 (0.71^2), assuming the default script size
467 multiplier. An SSTY level of 1 is set if the script scaling factor is
468 less than or equal that for a scriptlevel of 1 assuming the default
469 script size multiplier.
471 User specified values of script size multiplier will change the
472 scaling factor which mSSTYScriptLevel values correspond to.
474 In the event that the script size multiplier actually makes things
475 larger, no change is made.
477 To opt out of this change, add the following to the stylesheet:
478 "font-feature-settings: 'ssty' 0"
480 if (scriptScaling
<= (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER
+
481 (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER
*
482 NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER
)) /
484 // Currently only the first two ssty settings are used, so two is
487 } else if (scriptScaling
<= NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER
) {
491 gfxFontFeature settingSSTY
;
492 settingSSTY
.mTag
= TT_SSTY
;
493 settingSSTY
.mValue
= sstyLevel
;
494 font
.fontFeatureSettings
.AppendElement(settingSSTY
);
498 Apply the dtls font feature setting (dotless).
499 This gets applied to the base frame and all descendants of the base
500 frame of certain <mover> and <munderover> frames.
502 See nsMathMLmunderoverFrame.cpp for a full description.
504 To opt out of this change, add the following to the stylesheet:
505 "font-feature-settings: 'dtls' 0"
507 if ((mFlags
& MATH_FONT_FEATURE_DTLS
) && !foundDTLS
) {
508 gfxFontFeature settingDTLS
;
509 settingDTLS
.mTag
= TT_DTLS
;
510 settingDTLS
.mValue
= 1;
511 font
.fontFeatureSettings
.AppendElement(settingDTLS
);
516 uint8_t mathVar
= NS_MATHML_MATHVARIANT_NONE
;
517 bool doMathvariantStyling
= true;
519 for (uint32_t i
= 0; i
< length
; ++i
) {
521 mathVar
= styles
[i
]->mMathVariant
;
523 if (singleCharMI
&& mathVar
== NS_MATHML_MATHVARIANT_NONE
) {
524 // If the user has explicitly set a non-default value for fontstyle or
525 // fontweight, the italic mathvariant behaviour of <mi> is disabled
526 // This overrides the initial values specified in fontStyle, to avoid
527 // inconsistencies in which attributes allow CSS changes and which do not.
528 if (mFlags
& MATH_FONT_WEIGHT_BOLD
) {
529 font
.weight
= FontWeight::Bold();
530 if (mFlags
& MATH_FONT_STYLING_NORMAL
) {
531 font
.style
= FontSlantStyle::Normal();
533 font
.style
= FontSlantStyle::Italic();
535 } else if (mFlags
& MATH_FONT_STYLING_NORMAL
) {
536 font
.style
= FontSlantStyle::Normal();
537 font
.weight
= FontWeight::Normal();
539 mathVar
= NS_MATHML_MATHVARIANT_ITALIC
;
543 uint32_t ch
= str
[i
];
544 if (i
< length
- 1 && NS_IS_SURROGATE_PAIR(ch
, str
[i
+ 1])) {
545 ch
= SURROGATE_TO_UCS4(ch
, str
[i
+ 1]);
547 uint32_t ch2
= MathVariant(ch
, mathVar
);
549 if (mathVar
== NS_MATHML_MATHVARIANT_BOLD
||
550 mathVar
== NS_MATHML_MATHVARIANT_BOLD_ITALIC
||
551 mathVar
== NS_MATHML_MATHVARIANT_ITALIC
) {
552 if (ch
== ch2
&& ch
!= 0x20 && ch
!= 0xA0) {
553 // Don't apply the CSS style if a character cannot be
554 // transformed. There is an exception for whitespace as it is both
555 // common and innocuous.
556 doMathvariantStyling
= false;
559 // Bug 930504. Some platforms do not have fonts for Mathematical
560 // Alphanumeric Symbols. Hence we check whether the transformed
561 // character is actually available.
562 FontMatchType matchType
;
563 RefPtr
<gfxFont
> mathFont
= fontGroup
->FindFontForChar(
564 ch2
, 0, 0, unicode::Script::COMMON
, nullptr, &matchType
);
566 // Don't apply the CSS style if there is a math font for at least one
567 // of the transformed character in this text run.
568 doMathvariantStyling
= false;
570 // We fallback to the original character.
573 aMFR
->RecordScript(unicode::Script::MATHEMATICAL_NOTATION
);
579 deletedCharsArray
.AppendElement(false);
580 charsToMergeArray
.AppendElement(false);
581 styleArray
.AppendElement(styles
[i
]);
582 canBreakBeforeArray
.AppendElement(aTextRun
->CanBreakLineBefore(i
));
584 if (IS_IN_BMP(ch2
)) {
585 convertedString
.Append(ch2
);
587 convertedString
.Append(H_SURROGATE(ch2
));
588 convertedString
.Append(L_SURROGATE(ch2
));
590 if (!IS_IN_BMP(ch
)) {
591 deletedCharsArray
.AppendElement(
592 true); // not exactly deleted, but
593 // the trailing surrogate is skipped
598 while (extraChars
-- > 0) {
600 charsToMergeArray
.AppendElement(true);
601 styleArray
.AppendElement(styles
[i
]);
602 canBreakBeforeArray
.AppendElement(false);
606 gfx::ShapedTextFlags flags
;
607 gfxTextRunFactory::Parameters innerParams
=
608 GetParametersForInner(aTextRun
, &flags
, aRefDrawTarget
);
610 RefPtr
<nsTransformedTextRun
> transformedChild
;
611 RefPtr
<gfxTextRun
> cachedChild
;
614 if (mathVar
== NS_MATHML_MATHVARIANT_BOLD
&& doMathvariantStyling
) {
615 font
.style
= FontSlantStyle::Normal();
616 font
.weight
= FontWeight::Bold();
617 } else if (mathVar
== NS_MATHML_MATHVARIANT_ITALIC
&& doMathvariantStyling
) {
618 font
.style
= FontSlantStyle::Italic();
619 font
.weight
= FontWeight::Normal();
620 } else if (mathVar
== NS_MATHML_MATHVARIANT_BOLD_ITALIC
&&
621 doMathvariantStyling
) {
622 font
.style
= FontSlantStyle::Italic();
623 font
.weight
= FontWeight::Bold();
624 } else if (mathVar
!= NS_MATHML_MATHVARIANT_NONE
) {
625 // Mathvariant overrides fontstyle and fontweight
626 // Need to check to see if mathvariant is actually applied as this function
627 // is used for other purposes.
628 font
.style
= FontSlantStyle::Normal();
629 font
.weight
= FontWeight::Normal();
631 gfxFontGroup
* newFontGroup
= nullptr;
633 // Get the correct gfxFontGroup that corresponds to the earlier font changes.
635 font
.size
= font
.size
.ScaledBy(mFontInflation
);
636 nsPresContext
* pc
= styles
[0]->mPresContext
;
637 nsFontMetrics::Params params
;
638 params
.language
= styles
[0]->mLanguage
;
639 params
.explicitLanguage
= styles
[0]->mExplicitLanguage
;
640 params
.userFontSet
= pc
->GetUserFontSet();
641 params
.textPerf
= pc
->GetTextPerfMetrics();
642 params
.fontStats
= pc
->GetFontMatchingStats();
643 params
.featureValueLookup
= pc
->GetFontFeatureValuesLookup();
644 RefPtr
<nsFontMetrics
> metrics
=
645 pc
->DeviceContext()->GetMetricsFor(font
, params
);
646 newFontGroup
= metrics
->GetThebesFontGroup();
650 // If we can't get a new font group, fall back to the old one. Rendering
651 // will be incorrect, but not significantly so.
652 newFontGroup
= fontGroup
;
655 if (mInnerTransformingTextRunFactory
) {
656 transformedChild
= mInnerTransformingTextRunFactory
->MakeTextRun(
657 convertedString
.BeginReading(), convertedString
.Length(), &innerParams
,
658 newFontGroup
, flags
, nsTextFrameUtils::Flags(), std::move(styleArray
),
660 child
= transformedChild
.get();
662 cachedChild
= newFontGroup
->MakeTextRun(
663 convertedString
.BeginReading(), convertedString
.Length(), &innerParams
,
664 flags
, nsTextFrameUtils::Flags(), aMFR
);
665 child
= cachedChild
.get();
669 typedef gfxTextRun::Range Range
;
671 // Copy potential linebreaks into child so they're preserved
672 // (and also child will be shaped appropriately)
673 NS_ASSERTION(convertedString
.Length() == canBreakBeforeArray
.Length(),
674 "Dropped characters or break-before values somewhere!");
675 Range
range(0, uint32_t(canBreakBeforeArray
.Length()));
676 child
->SetPotentialLineBreaks(range
, canBreakBeforeArray
.Elements());
677 if (transformedChild
) {
678 transformedChild
->FinishSettingProperties(aRefDrawTarget
, aMFR
);
681 aTextRun
->ResetGlyphRuns();
683 // Now merge multiple characters into one multi-glyph character as required
684 NS_ASSERTION(charsToMergeArray
.Length() == child
->GetLength(),
685 "source length mismatch");
686 NS_ASSERTION(deletedCharsArray
.Length() == aTextRun
->GetLength(),
687 "destination length mismatch");
688 MergeCharactersInTextRun(aTextRun
, child
, charsToMergeArray
.Elements(),
689 deletedCharsArray
.Elements());
691 // No merging to do, so just copy; this produces a more optimized textrun.
692 // We can't steal the data because the child may be cached and stealing
693 // the data would break the cache.
694 aTextRun
->CopyGlyphDataFrom(child
, Range(child
), 0);