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"
13 #include "mozilla/StaticPrefs_mathml.h"
14 #include "mozilla/intl/UnicodeScriptCodes.h"
16 #include "nsStyleConsts.h"
17 #include "nsTextFrameUtils.h"
18 #include "nsFontMetrics.h"
19 #include "nsDeviceContext.h"
21 using namespace mozilla
;
24 Entries for the mathvariant lookup tables. mKey represents the Unicode
25 character to be transformed and is used for searching the tables.
26 mReplacement represents the mapped mathvariant Unicode character.
30 uint32_t mReplacement
;
34 Lookup tables for use with mathvariant mappings to transform a unicode
35 character point to another unicode character that indicates the proper output.
36 mKey represents one of two concepts.
37 1. In the Latin table it represents a hole in the mathematical alphanumeric
38 block, where the character that should occupy that position is located
40 2. It represents an Arabic letter.
42 As a replacement, 0 is reserved to indicate no mapping was found.
44 static const MathVarMapping gArabicInitialMapTable
[] = {
45 {0x628, 0x1EE21}, {0x62A, 0x1EE35}, {0x62B, 0x1EE36}, {0x62C, 0x1EE22},
46 {0x62D, 0x1EE27}, {0x62E, 0x1EE37}, {0x633, 0x1EE2E}, {0x634, 0x1EE34},
47 {0x635, 0x1EE31}, {0x636, 0x1EE39}, {0x639, 0x1EE2F}, {0x63A, 0x1EE3B},
48 {0x641, 0x1EE30}, {0x642, 0x1EE32}, {0x643, 0x1EE2A}, {0x644, 0x1EE2B},
49 {0x645, 0x1EE2C}, {0x646, 0x1EE2D}, {0x647, 0x1EE24}, {0x64A, 0x1EE29}};
51 static const MathVarMapping gArabicTailedMapTable
[] = {
52 {0x62C, 0x1EE42}, {0x62D, 0x1EE47}, {0x62E, 0x1EE57}, {0x633, 0x1EE4E},
53 {0x634, 0x1EE54}, {0x635, 0x1EE51}, {0x636, 0x1EE59}, {0x639, 0x1EE4F},
54 {0x63A, 0x1EE5B}, {0x642, 0x1EE52}, {0x644, 0x1EE4B}, {0x646, 0x1EE4D},
55 {0x64A, 0x1EE49}, {0x66F, 0x1EE5F}, {0x6BA, 0x1EE5D}};
57 static const MathVarMapping gArabicStretchedMapTable
[] = {
58 {0x628, 0x1EE61}, {0x62A, 0x1EE75}, {0x62B, 0x1EE76}, {0x62C, 0x1EE62},
59 {0x62D, 0x1EE67}, {0x62E, 0x1EE77}, {0x633, 0x1EE6E}, {0x634, 0x1EE74},
60 {0x635, 0x1EE71}, {0x636, 0x1EE79}, {0x637, 0x1EE68}, {0x638, 0x1EE7A},
61 {0x639, 0x1EE6F}, {0x63A, 0x1EE7B}, {0x641, 0x1EE70}, {0x642, 0x1EE72},
62 {0x643, 0x1EE6A}, {0x645, 0x1EE6C}, {0x646, 0x1EE6D}, {0x647, 0x1EE64},
63 {0x64A, 0x1EE69}, {0x66E, 0x1EE7C}, {0x6A1, 0x1EE7E}};
65 static const MathVarMapping gArabicLoopedMapTable
[] = {
66 {0x627, 0x1EE80}, {0x628, 0x1EE81}, {0x62A, 0x1EE95}, {0x62B, 0x1EE96},
67 {0x62C, 0x1EE82}, {0x62D, 0x1EE87}, {0x62E, 0x1EE97}, {0x62F, 0x1EE83},
68 {0x630, 0x1EE98}, {0x631, 0x1EE93}, {0x632, 0x1EE86}, {0x633, 0x1EE8E},
69 {0x634, 0x1EE94}, {0x635, 0x1EE91}, {0x636, 0x1EE99}, {0x637, 0x1EE88},
70 {0x638, 0x1EE9A}, {0x639, 0x1EE8F}, {0x63A, 0x1EE9B}, {0x641, 0x1EE90},
71 {0x642, 0x1EE92}, {0x644, 0x1EE8B}, {0x645, 0x1EE8C}, {0x646, 0x1EE8D},
72 {0x647, 0x1EE84}, {0x648, 0x1EE85}, {0x64A, 0x1EE89}};
74 static const MathVarMapping gArabicDoubleMapTable
[] = {
75 {0x628, 0x1EEA1}, {0x62A, 0x1EEB5}, {0x62B, 0x1EEB6}, {0x62C, 0x1EEA2},
76 {0x62D, 0x1EEA7}, {0x62E, 0x1EEB7}, {0x62F, 0x1EEA3}, {0x630, 0x1EEB8},
77 {0x631, 0x1EEB3}, {0x632, 0x1EEA6}, {0x633, 0x1EEAE}, {0x634, 0x1EEB4},
78 {0x635, 0x1EEB1}, {0x636, 0x1EEB9}, {0x637, 0x1EEA8}, {0x638, 0x1EEBA},
79 {0x639, 0x1EEAF}, {0x63A, 0x1EEBB}, {0x641, 0x1EEB0}, {0x642, 0x1EEB2},
80 {0x644, 0x1EEAB}, {0x645, 0x1EEAC}, {0x646, 0x1EEAD}, {0x648, 0x1EEA5},
83 static const MathVarMapping gLatinExceptionMapTable
[] = {
84 {0x1D455, 0x210E}, {0x1D49D, 0x212C}, {0x1D4A0, 0x2130}, {0x1D4A1, 0x2131},
85 {0x1D4A3, 0x210B}, {0x1D4A4, 0x2110}, {0x1D4A7, 0x2112}, {0x1D4A8, 0x2133},
86 {0x1D4AD, 0x211B}, {0x1D4BA, 0x212F}, {0x1D4BC, 0x210A}, {0x1D4C4, 0x2134},
87 {0x1D506, 0x212D}, {0x1D50B, 0x210C}, {0x1D50C, 0x2111}, {0x1D515, 0x211C},
88 {0x1D51D, 0x2128}, {0x1D53A, 0x2102}, {0x1D53F, 0x210D}, {0x1D545, 0x2115},
89 {0x1D547, 0x2119}, {0x1D548, 0x211A}, {0x1D549, 0x211D}, {0x1D551, 0x2124}};
93 struct MathVarMappingWrapper
{
94 const MathVarMapping
* const mTable
;
95 explicit MathVarMappingWrapper(const MathVarMapping
* aTable
)
97 uint32_t operator[](size_t index
) const { return mTable
[index
].mKey
; }
102 // Finds a MathVarMapping struct with the specified key (aKey) within aTable.
103 // aTable must be an array, whose length is specified by aNumElements
104 static uint32_t MathvarMappingSearch(uint32_t aKey
,
105 const MathVarMapping
* aTable
,
106 uint32_t aNumElements
) {
108 if (BinarySearch(MathVarMappingWrapper(aTable
), 0, aNumElements
, aKey
,
110 return aTable
[index
].mReplacement
;
116 #define GREEK_UPPER_THETA 0x03F4
117 #define HOLE_GREEK_UPPER_THETA 0x03A2
119 #define PARTIAL_DIFFERENTIAL 0x2202
120 #define GREEK_UPPER_ALPHA 0x0391
121 #define GREEK_UPPER_OMEGA 0x03A9
122 #define GREEK_LOWER_ALPHA 0x03B1
123 #define GREEK_LOWER_OMEGA 0x03C9
124 #define GREEK_LUNATE_EPSILON_SYMBOL 0x03F5
125 #define GREEK_THETA_SYMBOL 0x03D1
126 #define GREEK_KAPPA_SYMBOL 0x03F0
127 #define GREEK_PHI_SYMBOL 0x03D5
128 #define GREEK_RHO_SYMBOL 0x03F1
129 #define GREEK_PI_SYMBOL 0x03D6
130 #define GREEK_LETTER_DIGAMMA 0x03DC
131 #define GREEK_SMALL_LETTER_DIGAMMA 0x03DD
132 #define MATH_BOLD_CAPITAL_DIGAMMA 0x1D7CA
133 #define MATH_BOLD_SMALL_DIGAMMA 0x1D7CB
135 #define LATIN_SMALL_LETTER_DOTLESS_I 0x0131
136 #define LATIN_SMALL_LETTER_DOTLESS_J 0x0237
138 #define MATH_ITALIC_SMALL_DOTLESS_I 0x1D6A4
139 #define MATH_ITALIC_SMALL_DOTLESS_J 0x1D6A5
141 #define MATH_BOLD_UPPER_A 0x1D400
142 #define MATH_ITALIC_UPPER_A 0x1D434
143 #define MATH_BOLD_SMALL_A 0x1D41A
144 #define MATH_BOLD_UPPER_ALPHA 0x1D6A8
145 #define MATH_BOLD_SMALL_ALPHA 0x1D6C2
146 #define MATH_ITALIC_UPPER_ALPHA 0x1D6E2
147 #define MATH_BOLD_DIGIT_ZERO 0x1D7CE
148 #define MATH_DOUBLE_STRUCK_ZERO 0x1D7D8
150 #define MATH_BOLD_UPPER_THETA 0x1D6B9
151 #define MATH_BOLD_NABLA 0x1D6C1
152 #define MATH_BOLD_PARTIAL_DIFFERENTIAL 0x1D6DB
153 #define MATH_BOLD_EPSILON_SYMBOL 0x1D6DC
154 #define MATH_BOLD_THETA_SYMBOL 0x1D6DD
155 #define MATH_BOLD_KAPPA_SYMBOL 0x1D6DE
156 #define MATH_BOLD_PHI_SYMBOL 0x1D6DF
157 #define MATH_BOLD_RHO_SYMBOL 0x1D6E0
158 #define MATH_BOLD_PI_SYMBOL 0x1D6E1
161 Performs the character mapping needed to implement MathML's mathvariant
162 attribute. It takes a unicode character and maps it to its appropriate
163 mathvariant counterpart specified by aMathVar. The mapped character is
164 typically located within Unicode's mathematical blocks (0x1D***, 0x1EE**) but
165 there are exceptions which this function accounts for.
166 Characters without a valid mapping or valid aMathvar value are returned
167 unaltered. Characters already in the mathematical blocks (or are one of the
168 exceptions) are never transformed.
169 Acceptable values for aMathVar are specified in layout/style/nsStyleConsts.h.
170 The transformable characters can be found at:
171 http://lists.w3.org/Archives/Public/www-math/2013Sep/0012.html and
172 https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols
174 /*static */ uint32_t MathMLTextRunFactory::MathVariant(
175 uint32_t aCh
, StyleMathVariant aMathVar
) {
183 CharacterType varType
;
187 if (aMathVar
<= StyleMathVariant::Normal
) {
188 // nothing to do here
191 if (aMathVar
> StyleMathVariant::Stretched
) {
192 NS_ASSERTION(false, "Illegal mathvariant value");
196 // Exceptional characters with at most one possible transformation
197 if (aCh
== HOLE_GREEK_UPPER_THETA
) {
198 // Nothing at this code point is transformed
201 if (aCh
== GREEK_LETTER_DIGAMMA
) {
202 if (aMathVar
== StyleMathVariant::Bold
) {
203 return MATH_BOLD_CAPITAL_DIGAMMA
;
207 if (aCh
== GREEK_SMALL_LETTER_DIGAMMA
) {
208 if (aMathVar
== StyleMathVariant::Bold
) {
209 return MATH_BOLD_SMALL_DIGAMMA
;
213 if (aCh
== LATIN_SMALL_LETTER_DOTLESS_I
) {
214 if (aMathVar
== StyleMathVariant::Italic
) {
215 return MATH_ITALIC_SMALL_DOTLESS_I
;
219 if (aCh
== LATIN_SMALL_LETTER_DOTLESS_J
) {
220 if (aMathVar
== StyleMathVariant::Italic
) {
221 return MATH_ITALIC_SMALL_DOTLESS_J
;
226 // The Unicode mathematical blocks are divided into four segments: Latin,
227 // Greek, numbers and Arabic. In the case of the first three
228 // baseChar represents the relative order in which the characters are
229 // encoded in the Unicode mathematical block, normalised to the first
230 // character of that sequence.
232 if ('A' <= aCh
&& aCh
<= 'Z') {
233 baseChar
= aCh
- 'A';
235 } else if ('a' <= aCh
&& aCh
<= 'z') {
236 // Lowercase characters are placed immediately after the uppercase
237 // characters in the Unicode mathematical block. The constant subtraction
238 // represents the number of characters between the start of the sequence
239 // (capital A) and the first lowercase letter.
240 baseChar
= MATH_BOLD_SMALL_A
- MATH_BOLD_UPPER_A
+ aCh
- 'a';
242 } else if ('0' <= aCh
&& aCh
<= '9') {
243 baseChar
= aCh
- '0';
245 } else if (GREEK_UPPER_ALPHA
<= aCh
&& aCh
<= GREEK_UPPER_OMEGA
) {
246 baseChar
= aCh
- GREEK_UPPER_ALPHA
;
247 varType
= kIsGreekish
;
248 } else if (GREEK_LOWER_ALPHA
<= aCh
&& aCh
<= GREEK_LOWER_OMEGA
) {
249 // Lowercase Greek comes after uppercase Greek.
250 // Note in this instance the presence of an additional character (Nabla)
251 // between the end of the uppercase Greek characters and the lowercase
254 MATH_BOLD_SMALL_ALPHA
- MATH_BOLD_UPPER_ALPHA
+ aCh
- GREEK_LOWER_ALPHA
;
255 varType
= kIsGreekish
;
256 } else if (0x0600 <= aCh
&& aCh
<= 0x06FF) {
257 // Arabic characters are defined within this range
261 case GREEK_UPPER_THETA
:
262 baseChar
= MATH_BOLD_UPPER_THETA
- MATH_BOLD_UPPER_ALPHA
;
265 baseChar
= MATH_BOLD_NABLA
- MATH_BOLD_UPPER_ALPHA
;
267 case PARTIAL_DIFFERENTIAL
:
268 baseChar
= MATH_BOLD_PARTIAL_DIFFERENTIAL
- MATH_BOLD_UPPER_ALPHA
;
270 case GREEK_LUNATE_EPSILON_SYMBOL
:
271 baseChar
= MATH_BOLD_EPSILON_SYMBOL
- MATH_BOLD_UPPER_ALPHA
;
273 case GREEK_THETA_SYMBOL
:
274 baseChar
= MATH_BOLD_THETA_SYMBOL
- MATH_BOLD_UPPER_ALPHA
;
276 case GREEK_KAPPA_SYMBOL
:
277 baseChar
= MATH_BOLD_KAPPA_SYMBOL
- MATH_BOLD_UPPER_ALPHA
;
279 case GREEK_PHI_SYMBOL
:
280 baseChar
= MATH_BOLD_PHI_SYMBOL
- MATH_BOLD_UPPER_ALPHA
;
282 case GREEK_RHO_SYMBOL
:
283 baseChar
= MATH_BOLD_RHO_SYMBOL
- MATH_BOLD_UPPER_ALPHA
;
285 case GREEK_PI_SYMBOL
:
286 baseChar
= MATH_BOLD_PI_SYMBOL
- MATH_BOLD_UPPER_ALPHA
;
292 varType
= kIsGreekish
;
295 if (varType
== kIsNumber
) {
297 // Each possible number mathvariant is encoded in a single, contiguous
298 // block. For example the beginning of the double struck number range
299 // follows immediately after the end of the bold number range.
300 // multiplier represents the order of the sequences relative to the first
302 case StyleMathVariant::Bold
:
305 case StyleMathVariant::DoubleStruck
:
308 case StyleMathVariant::SansSerif
:
311 case StyleMathVariant::BoldSansSerif
:
314 case StyleMathVariant::Monospace
:
318 // This mathvariant isn't defined for numbers or is otherwise normal
321 // As the ranges are contiguous, to find the desired mathvariant range it
322 // is sufficient to multiply the position within the sequence order
323 // (multiplier) with the period of the sequence (which is constant for all
324 // number sequences) and to add the character point of the first character
325 // within the number mathvariant range.
326 // To this the baseChar calculated earlier is added to obtain the final
329 multiplier
* (MATH_DOUBLE_STRUCK_ZERO
- MATH_BOLD_DIGIT_ZERO
) +
330 MATH_BOLD_DIGIT_ZERO
;
331 } else if (varType
== kIsGreekish
) {
333 case StyleMathVariant::Bold
:
336 case StyleMathVariant::Italic
:
339 case StyleMathVariant::BoldItalic
:
342 case StyleMathVariant::BoldSansSerif
:
345 case StyleMathVariant::SansSerifBoldItalic
:
349 // This mathvariant isn't defined for Greek or is otherwise normal
352 // See the kIsNumber case for an explanation of the following calculation
353 return baseChar
+ MATH_BOLD_UPPER_ALPHA
+
354 multiplier
* (MATH_ITALIC_UPPER_ALPHA
- MATH_BOLD_UPPER_ALPHA
);
359 if (varType
== kIsArabic
) {
360 const MathVarMapping
* mapTable
;
361 uint32_t tableLength
;
363 /* The Arabic mathematical block is not continuous, nor does it have a
364 * monotonic mapping to the unencoded characters, requiring the use of a
367 case StyleMathVariant::Initial
:
368 mapTable
= gArabicInitialMapTable
;
369 tableLength
= ArrayLength(gArabicInitialMapTable
);
371 case StyleMathVariant::Tailed
:
372 mapTable
= gArabicTailedMapTable
;
373 tableLength
= ArrayLength(gArabicTailedMapTable
);
375 case StyleMathVariant::Stretched
:
376 mapTable
= gArabicStretchedMapTable
;
377 tableLength
= ArrayLength(gArabicStretchedMapTable
);
379 case StyleMathVariant::Looped
:
380 mapTable
= gArabicLoopedMapTable
;
381 tableLength
= ArrayLength(gArabicLoopedMapTable
);
383 case StyleMathVariant::DoubleStruck
:
384 mapTable
= gArabicDoubleMapTable
;
385 tableLength
= ArrayLength(gArabicDoubleMapTable
);
388 // No valid transformations exist
391 newChar
= MathvarMappingSearch(aCh
, mapTable
, tableLength
);
394 if (aMathVar
> StyleMathVariant::Monospace
) {
395 // Latin doesn't support the Arabic mathvariants
398 multiplier
= uint8_t(aMathVar
) - 2;
399 // This is possible because the values for StyleMathVariant::* are
400 // chosen to coincide with the order in which the encoded mathvariant
401 // characters are located within their unicode block (less an offset to
402 // avoid _NONE and _NORMAL variants)
403 // See the kIsNumber case for an explanation of the following calculation
404 tempChar
= baseChar
+ MATH_BOLD_UPPER_A
+
405 multiplier
* (MATH_ITALIC_UPPER_A
- MATH_BOLD_UPPER_A
);
406 // There are roughly twenty characters that are located outside of the
407 // mathematical block, so the spaces where they ought to be are used
408 // as keys for a lookup table containing the correct character mappings.
409 newChar
= MathvarMappingSearch(tempChar
, gLatinExceptionMapTable
,
410 ArrayLength(gLatinExceptionMapTable
));
415 } else if (varType
== kIsLatin
) {
418 // An Arabic character without a corresponding mapping
423 #define TT_SSTY TRUETYPE_TAG('s', 's', 't', 'y')
424 #define TT_DTLS TRUETYPE_TAG('d', 't', 'l', 's')
426 void MathMLTextRunFactory::RebuildTextRun(
427 nsTransformedTextRun
* aTextRun
, mozilla::gfx::DrawTarget
* aRefDrawTarget
,
428 gfxMissingFontRecorder
* aMFR
) {
429 gfxFontGroup
* fontGroup
= aTextRun
->GetFontGroup();
431 nsAutoString convertedString
;
432 AutoTArray
<bool, 50> charsToMergeArray
;
433 AutoTArray
<bool, 50> deletedCharsArray
;
434 AutoTArray
<RefPtr
<nsTransformedCharStyle
>, 50> styleArray
;
435 AutoTArray
<uint8_t, 50> canBreakBeforeArray
;
436 bool mergeNeeded
= false;
439 !!(aTextRun
->GetFlags2() & nsTextFrameUtils::Flags::IsSingleCharMi
);
441 uint32_t length
= aTextRun
->GetLength();
442 const char16_t
* str
= aTextRun
->mString
.BeginReading();
443 const nsTArray
<RefPtr
<nsTransformedCharStyle
>>& styles
= aTextRun
->mStyles
;
446 font
= styles
[0]->mFont
;
448 if (mSSTYScriptLevel
|| (mFlags
& MATH_FONT_FEATURE_DTLS
)) {
449 bool foundSSTY
= false;
450 bool foundDTLS
= false;
451 // We respect ssty settings explicitly set by the user
452 for (uint32_t i
= 0; i
< font
.fontFeatureSettings
.Length(); i
++) {
453 if (font
.fontFeatureSettings
[i
].mTag
== TT_SSTY
) {
455 } else if (font
.fontFeatureSettings
[i
].mTag
== TT_DTLS
) {
459 if (mSSTYScriptLevel
&& !foundSSTY
) {
460 uint8_t sstyLevel
= 0;
461 // FIXME: Use the same logic as scale_factor_for_math_depth_change?
462 float scriptScaling
=
463 pow(kMathMLDefaultScriptSizeMultiplier
, mSSTYScriptLevel
);
464 static_assert(kMathMLDefaultScriptSizeMultiplier
< 1,
465 "Shouldn't it make things smaller?");
467 An SSTY level of 2 is set if the scaling factor is less than or equal
468 to halfway between that for a scriptlevel of 1 (0.71) and that of a
469 scriptlevel of 2 (0.71^2), assuming the default script size
470 multiplier. An SSTY level of 1 is set if the script scaling factor is
471 less than or equal that for a scriptlevel of 1 assuming the default
472 script size multiplier.
474 User specified values of script size multiplier will change the
475 scaling factor which mSSTYScriptLevel values correspond to.
477 In the event that the script size multiplier actually makes things
478 larger, no change is made.
480 To opt out of this change, add the following to the stylesheet:
481 "font-feature-settings: 'ssty' 0"
483 if (scriptScaling
<= (kMathMLDefaultScriptSizeMultiplier
+
484 (kMathMLDefaultScriptSizeMultiplier
*
485 kMathMLDefaultScriptSizeMultiplier
)) /
487 // Currently only the first two ssty settings are used, so two is
490 } else if (scriptScaling
<= kMathMLDefaultScriptSizeMultiplier
) {
494 gfxFontFeature settingSSTY
;
495 settingSSTY
.mTag
= TT_SSTY
;
496 settingSSTY
.mValue
= sstyLevel
;
497 font
.fontFeatureSettings
.AppendElement(settingSSTY
);
501 Apply the dtls font feature setting (dotless).
502 This gets applied to the base frame and all descendants of the base
503 frame of certain <mover> and <munderover> frames.
505 See nsMathMLmunderoverFrame.cpp for a full description.
507 To opt out of this change, add the following to the stylesheet:
508 "font-feature-settings: 'dtls' 0"
510 if ((mFlags
& MATH_FONT_FEATURE_DTLS
) && !foundDTLS
) {
511 gfxFontFeature settingDTLS
;
512 settingDTLS
.mTag
= TT_DTLS
;
513 settingDTLS
.mValue
= 1;
514 font
.fontFeatureSettings
.AppendElement(settingDTLS
);
519 StyleMathVariant mathVar
= StyleMathVariant::None
;
520 bool doMathvariantStyling
= true;
522 // Ensure it will be safe to call FindFontForChar in the loop below.
523 fontGroup
->CheckForUpdatedPlatformList();
525 for (uint32_t i
= 0; i
< length
; ++i
) {
527 mathVar
= styles
[i
]->mMathVariant
;
529 if (singleCharMI
&& mathVar
== StyleMathVariant::None
&&
530 (!StaticPrefs::mathml_legacy_mathvariant_attribute_disabled() ||
531 styles
[i
]->mTextTransform
.case_
== StyleTextTransformCase::MathAuto
)) {
532 mathVar
= StyleMathVariant::Italic
;
535 uint32_t ch
= str
[i
];
536 if (i
< length
- 1 && NS_IS_SURROGATE_PAIR(ch
, str
[i
+ 1])) {
537 ch
= SURROGATE_TO_UCS4(ch
, str
[i
+ 1]);
539 uint32_t ch2
= MathVariant(ch
, mathVar
);
541 if (!StaticPrefs::mathml_mathvariant_styling_fallback_disabled() &&
542 (mathVar
== StyleMathVariant::Bold
||
543 mathVar
== StyleMathVariant::BoldItalic
||
544 mathVar
== StyleMathVariant::Italic
)) {
545 if (ch
== ch2
&& ch
!= 0x20 && ch
!= 0xA0) {
546 // Don't apply the CSS style if a character cannot be
547 // transformed. There is an exception for whitespace as it is both
548 // common and innocuous.
549 doMathvariantStyling
= false;
552 // Bug 930504. Some platforms do not have fonts for Mathematical
553 // Alphanumeric Symbols. Hence we check whether the transformed
554 // character is actually available.
555 FontMatchType matchType
;
556 RefPtr
<gfxFont
> mathFont
= fontGroup
->FindFontForChar(
557 ch2
, 0, 0, intl::Script::COMMON
, nullptr, &matchType
);
559 // Don't apply the CSS style if there is a math font for at least one
560 // of the transformed character in this text run.
561 doMathvariantStyling
= false;
563 // We fallback to the original character.
566 aMFR
->RecordScript(intl::Script::MATHEMATICAL_NOTATION
);
572 deletedCharsArray
.AppendElement(false);
573 charsToMergeArray
.AppendElement(false);
574 styleArray
.AppendElement(styles
[i
]);
575 canBreakBeforeArray
.AppendElement(aTextRun
->CanBreakLineBefore(i
));
577 if (IS_IN_BMP(ch2
)) {
578 convertedString
.Append(ch2
);
580 convertedString
.Append(H_SURROGATE(ch2
));
581 convertedString
.Append(L_SURROGATE(ch2
));
583 if (!IS_IN_BMP(ch
)) {
584 deletedCharsArray
.AppendElement(
585 true); // not exactly deleted, but
586 // the trailing surrogate is skipped
591 while (extraChars
-- > 0) {
593 charsToMergeArray
.AppendElement(true);
594 styleArray
.AppendElement(styles
[i
]);
595 canBreakBeforeArray
.AppendElement(false);
599 gfx::ShapedTextFlags flags
;
600 gfxTextRunFactory::Parameters innerParams
=
601 GetParametersForInner(aTextRun
, &flags
, aRefDrawTarget
);
603 RefPtr
<nsTransformedTextRun
> transformedChild
;
604 RefPtr
<gfxTextRun
> cachedChild
;
607 if (!StaticPrefs::mathml_mathvariant_styling_fallback_disabled() &&
608 doMathvariantStyling
) {
609 if (mathVar
== StyleMathVariant::Bold
) {
610 font
.style
= FontSlantStyle::NORMAL
;
611 font
.weight
= FontWeight::BOLD
;
612 } else if (mathVar
== StyleMathVariant::Italic
) {
613 font
.style
= FontSlantStyle::ITALIC
;
614 font
.weight
= FontWeight::NORMAL
;
615 } else if (mathVar
== StyleMathVariant::BoldItalic
) {
616 font
.style
= FontSlantStyle::ITALIC
;
617 font
.weight
= FontWeight::BOLD
;
620 gfxFontGroup
* newFontGroup
= nullptr;
622 // Get the correct gfxFontGroup that corresponds to the earlier font changes.
624 font
.size
= font
.size
.ScaledBy(mFontInflation
);
625 nsPresContext
* pc
= styles
[0]->mPresContext
;
626 nsFontMetrics::Params params
;
627 params
.language
= styles
[0]->mLanguage
;
628 params
.explicitLanguage
= styles
[0]->mExplicitLanguage
;
629 params
.userFontSet
= pc
->GetUserFontSet();
630 params
.textPerf
= pc
->GetTextPerfMetrics();
631 params
.featureValueLookup
= pc
->GetFontFeatureValuesLookup();
632 RefPtr
<nsFontMetrics
> metrics
= pc
->GetMetricsFor(font
, params
);
633 newFontGroup
= metrics
->GetThebesFontGroup();
637 // If we can't get a new font group, fall back to the old one. Rendering
638 // will be incorrect, but not significantly so.
639 newFontGroup
= fontGroup
;
642 if (mInnerTransformingTextRunFactory
) {
643 transformedChild
= mInnerTransformingTextRunFactory
->MakeTextRun(
644 convertedString
.BeginReading(), convertedString
.Length(), &innerParams
,
645 newFontGroup
, flags
, nsTextFrameUtils::Flags(), std::move(styleArray
),
647 child
= transformedChild
.get();
649 cachedChild
= newFontGroup
->MakeTextRun(
650 convertedString
.BeginReading(), convertedString
.Length(), &innerParams
,
651 flags
, nsTextFrameUtils::Flags(), aMFR
);
652 child
= cachedChild
.get();
656 typedef gfxTextRun::Range Range
;
658 // Copy potential linebreaks into child so they're preserved
659 // (and also child will be shaped appropriately)
660 NS_ASSERTION(convertedString
.Length() == canBreakBeforeArray
.Length(),
661 "Dropped characters or break-before values somewhere!");
662 Range
range(0, uint32_t(canBreakBeforeArray
.Length()));
663 child
->SetPotentialLineBreaks(range
, canBreakBeforeArray
.Elements());
664 if (transformedChild
) {
665 transformedChild
->FinishSettingProperties(aRefDrawTarget
, aMFR
);
668 aTextRun
->ResetGlyphRuns();
670 // Now merge multiple characters into one multi-glyph character as required
671 NS_ASSERTION(charsToMergeArray
.Length() == child
->GetLength(),
672 "source length mismatch");
673 NS_ASSERTION(deletedCharsArray
.Length() == aTextRun
->GetLength(),
674 "destination length mismatch");
675 MergeCharactersInTextRun(aTextRun
, child
, charsToMergeArray
.Elements(),
676 deletedCharsArray
.Elements());
678 // No merging to do, so just copy; this produces a more optimized textrun.
679 // We can't steal the data because the child may be cached and stealing
680 // the data would break the cache.
681 aTextRun
->CopyGlyphDataFrom(child
, Range(child
), 0);