Backed out changeset 496886cb30a5 (bug 1867152) for bc failures on browser_user_input...
[gecko.git] / layout / generic / MathMLTextRunFactory.cpp
blobe8cb53833ec34dccfc68081d767d85d5bb2ddbcf
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.
28 typedef struct {
29 uint32_t mKey;
30 uint32_t mReplacement;
31 } MathVarMapping;
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
39 elsewhere.
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},
81 {0x64A, 0x1EEA9}};
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}};
91 namespace {
93 struct MathVarMappingWrapper {
94 const MathVarMapping* const mTable;
95 explicit MathVarMappingWrapper(const MathVarMapping* aTable)
96 : mTable(aTable) {}
97 uint32_t operator[](size_t index) const { return mTable[index].mKey; }
100 } // namespace
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) {
107 size_t index;
108 if (BinarySearch(MathVarMappingWrapper(aTable), 0, aNumElements, aKey,
109 &index)) {
110 return aTable[index].mReplacement;
113 return 0;
116 #define GREEK_UPPER_THETA 0x03F4
117 #define HOLE_GREEK_UPPER_THETA 0x03A2
118 #define NABLA 0x2207
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) {
176 uint32_t baseChar;
177 enum CharacterType {
178 kIsLatin,
179 kIsGreekish,
180 kIsNumber,
181 kIsArabic,
183 CharacterType varType;
185 int8_t multiplier;
187 if (aMathVar <= StyleMathVariant::Normal) {
188 // nothing to do here
189 return aCh;
191 if (aMathVar > StyleMathVariant::Stretched) {
192 NS_ASSERTION(false, "Illegal mathvariant value");
193 return aCh;
196 // Exceptional characters with at most one possible transformation
197 if (aCh == HOLE_GREEK_UPPER_THETA) {
198 // Nothing at this code point is transformed
199 return aCh;
201 if (aCh == GREEK_LETTER_DIGAMMA) {
202 if (aMathVar == StyleMathVariant::Bold) {
203 return MATH_BOLD_CAPITAL_DIGAMMA;
205 return aCh;
207 if (aCh == GREEK_SMALL_LETTER_DIGAMMA) {
208 if (aMathVar == StyleMathVariant::Bold) {
209 return MATH_BOLD_SMALL_DIGAMMA;
211 return aCh;
213 if (aCh == LATIN_SMALL_LETTER_DOTLESS_I) {
214 if (aMathVar == StyleMathVariant::Italic) {
215 return MATH_ITALIC_SMALL_DOTLESS_I;
217 return aCh;
219 if (aCh == LATIN_SMALL_LETTER_DOTLESS_J) {
220 if (aMathVar == StyleMathVariant::Italic) {
221 return MATH_ITALIC_SMALL_DOTLESS_J;
223 return aCh;
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';
234 varType = kIsLatin;
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';
241 varType = kIsLatin;
242 } else if ('0' <= aCh && aCh <= '9') {
243 baseChar = aCh - '0';
244 varType = kIsNumber;
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
252 // ones.
253 baseChar =
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
258 varType = kIsArabic;
259 } else {
260 switch (aCh) {
261 case GREEK_UPPER_THETA:
262 baseChar = MATH_BOLD_UPPER_THETA - MATH_BOLD_UPPER_ALPHA;
263 break;
264 case NABLA:
265 baseChar = MATH_BOLD_NABLA - MATH_BOLD_UPPER_ALPHA;
266 break;
267 case PARTIAL_DIFFERENTIAL:
268 baseChar = MATH_BOLD_PARTIAL_DIFFERENTIAL - MATH_BOLD_UPPER_ALPHA;
269 break;
270 case GREEK_LUNATE_EPSILON_SYMBOL:
271 baseChar = MATH_BOLD_EPSILON_SYMBOL - MATH_BOLD_UPPER_ALPHA;
272 break;
273 case GREEK_THETA_SYMBOL:
274 baseChar = MATH_BOLD_THETA_SYMBOL - MATH_BOLD_UPPER_ALPHA;
275 break;
276 case GREEK_KAPPA_SYMBOL:
277 baseChar = MATH_BOLD_KAPPA_SYMBOL - MATH_BOLD_UPPER_ALPHA;
278 break;
279 case GREEK_PHI_SYMBOL:
280 baseChar = MATH_BOLD_PHI_SYMBOL - MATH_BOLD_UPPER_ALPHA;
281 break;
282 case GREEK_RHO_SYMBOL:
283 baseChar = MATH_BOLD_RHO_SYMBOL - MATH_BOLD_UPPER_ALPHA;
284 break;
285 case GREEK_PI_SYMBOL:
286 baseChar = MATH_BOLD_PI_SYMBOL - MATH_BOLD_UPPER_ALPHA;
287 break;
288 default:
289 return aCh;
292 varType = kIsGreekish;
295 if (varType == kIsNumber) {
296 switch (aMathVar) {
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
301 // one.
302 case StyleMathVariant::Bold:
303 multiplier = 0;
304 break;
305 case StyleMathVariant::DoubleStruck:
306 multiplier = 1;
307 break;
308 case StyleMathVariant::SansSerif:
309 multiplier = 2;
310 break;
311 case StyleMathVariant::BoldSansSerif:
312 multiplier = 3;
313 break;
314 case StyleMathVariant::Monospace:
315 multiplier = 4;
316 break;
317 default:
318 // This mathvariant isn't defined for numbers or is otherwise normal
319 return aCh;
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
327 // code point.
328 return baseChar +
329 multiplier * (MATH_DOUBLE_STRUCK_ZERO - MATH_BOLD_DIGIT_ZERO) +
330 MATH_BOLD_DIGIT_ZERO;
331 } else if (varType == kIsGreekish) {
332 switch (aMathVar) {
333 case StyleMathVariant::Bold:
334 multiplier = 0;
335 break;
336 case StyleMathVariant::Italic:
337 multiplier = 1;
338 break;
339 case StyleMathVariant::BoldItalic:
340 multiplier = 2;
341 break;
342 case StyleMathVariant::BoldSansSerif:
343 multiplier = 3;
344 break;
345 case StyleMathVariant::SansSerifBoldItalic:
346 multiplier = 4;
347 break;
348 default:
349 // This mathvariant isn't defined for Greek or is otherwise normal
350 return aCh;
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);
357 uint32_t tempChar;
358 uint32_t newChar;
359 if (varType == kIsArabic) {
360 const MathVarMapping* mapTable;
361 uint32_t tableLength;
362 switch (aMathVar) {
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
365 * lookup table.
367 case StyleMathVariant::Initial:
368 mapTable = gArabicInitialMapTable;
369 tableLength = ArrayLength(gArabicInitialMapTable);
370 break;
371 case StyleMathVariant::Tailed:
372 mapTable = gArabicTailedMapTable;
373 tableLength = ArrayLength(gArabicTailedMapTable);
374 break;
375 case StyleMathVariant::Stretched:
376 mapTable = gArabicStretchedMapTable;
377 tableLength = ArrayLength(gArabicStretchedMapTable);
378 break;
379 case StyleMathVariant::Looped:
380 mapTable = gArabicLoopedMapTable;
381 tableLength = ArrayLength(gArabicLoopedMapTable);
382 break;
383 case StyleMathVariant::DoubleStruck:
384 mapTable = gArabicDoubleMapTable;
385 tableLength = ArrayLength(gArabicDoubleMapTable);
386 break;
387 default:
388 // No valid transformations exist
389 return aCh;
391 newChar = MathvarMappingSearch(aCh, mapTable, tableLength);
392 } else {
393 // Must be Latin
394 if (aMathVar > StyleMathVariant::Monospace) {
395 // Latin doesn't support the Arabic mathvariants
396 return aCh;
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));
413 if (newChar) {
414 return newChar;
415 } else if (varType == kIsLatin) {
416 return tempChar;
417 } else {
418 // An Arabic character without a corresponding mapping
419 return aCh;
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;
438 bool singleCharMI =
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;
444 nsFont font;
445 if (length) {
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) {
454 foundSSTY = true;
455 } else if (font.fontFeatureSettings[i].mTag == TT_DTLS) {
456 foundDTLS = true;
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)) /
486 2) {
487 // Currently only the first two ssty settings are used, so two is
488 // large as we go
489 sstyLevel = 2;
490 } else if (scriptScaling <= kMathMLDefaultScriptSizeMultiplier) {
491 sstyLevel = 1;
493 if (sstyLevel) {
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) {
526 int extraChars = 0;
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;
551 if (ch2 != ch) {
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);
558 if (mathFont) {
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;
562 } else {
563 // We fallback to the original character.
564 ch2 = ch;
565 if (aMFR) {
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);
579 } else {
580 convertedString.Append(H_SURROGATE(ch2));
581 convertedString.Append(L_SURROGATE(ch2));
582 ++extraChars;
583 if (!IS_IN_BMP(ch)) {
584 deletedCharsArray.AppendElement(
585 true); // not exactly deleted, but
586 // the trailing surrogate is skipped
587 ++i;
591 while (extraChars-- > 0) {
592 mergeNeeded = true;
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;
605 gfxTextRun* child;
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.
623 if (length) {
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();
636 if (!newFontGroup) {
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),
646 false);
647 child = transformedChild.get();
648 } else {
649 cachedChild = newFontGroup->MakeTextRun(
650 convertedString.BeginReading(), convertedString.Length(), &innerParams,
651 flags, nsTextFrameUtils::Flags(), aMFR);
652 child = cachedChild.get();
654 if (!child) return;
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();
669 if (mergeNeeded) {
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());
677 } else {
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);