MathMLTextRunFactory.cpp (26290B)
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/. */ 6 7 #include "MathMLTextRunFactory.h" 8 9 #include "mozilla/BinarySearch.h" 10 #include "mozilla/ComputedStyle.h" 11 #include "mozilla/ComputedStyleInlines.h" 12 #include "mozilla/StaticPrefs_mathml.h" 13 #include "mozilla/intl/UnicodeScriptCodes.h" 14 #include "nsDeviceContext.h" 15 #include "nsFontMetrics.h" 16 #include "nsStyleConsts.h" 17 #include "nsTextFrameUtils.h" 18 19 using namespace mozilla; 20 21 /* 22 Entries for the mathvariant lookup tables. mKey represents the Unicode 23 character to be transformed and is used for searching the tables. 24 mReplacement represents the mapped mathvariant Unicode character. 25 */ 26 typedef struct { 27 uint32_t mKey; 28 uint32_t mReplacement; 29 } MathVarMapping; 30 31 /* 32 Lookup tables for use with mathvariant mappings to transform a unicode 33 character point to another unicode character that indicates the proper output. 34 mKey represents one of two concepts. 35 1. In the Latin table it represents a hole in the mathematical alphanumeric 36 block, where the character that should occupy that position is located 37 elsewhere. 38 2. It represents an Arabic letter. 39 40 As a replacement, 0 is reserved to indicate no mapping was found. 41 */ 42 static const MathVarMapping gArabicInitialMapTable[] = { 43 {0x628, 0x1EE21}, {0x62A, 0x1EE35}, {0x62B, 0x1EE36}, {0x62C, 0x1EE22}, 44 {0x62D, 0x1EE27}, {0x62E, 0x1EE37}, {0x633, 0x1EE2E}, {0x634, 0x1EE34}, 45 {0x635, 0x1EE31}, {0x636, 0x1EE39}, {0x639, 0x1EE2F}, {0x63A, 0x1EE3B}, 46 {0x641, 0x1EE30}, {0x642, 0x1EE32}, {0x643, 0x1EE2A}, {0x644, 0x1EE2B}, 47 {0x645, 0x1EE2C}, {0x646, 0x1EE2D}, {0x647, 0x1EE24}, {0x64A, 0x1EE29}}; 48 49 static const MathVarMapping gArabicTailedMapTable[] = { 50 {0x62C, 0x1EE42}, {0x62D, 0x1EE47}, {0x62E, 0x1EE57}, {0x633, 0x1EE4E}, 51 {0x634, 0x1EE54}, {0x635, 0x1EE51}, {0x636, 0x1EE59}, {0x639, 0x1EE4F}, 52 {0x63A, 0x1EE5B}, {0x642, 0x1EE52}, {0x644, 0x1EE4B}, {0x646, 0x1EE4D}, 53 {0x64A, 0x1EE49}, {0x66F, 0x1EE5F}, {0x6BA, 0x1EE5D}}; 54 55 static const MathVarMapping gArabicStretchedMapTable[] = { 56 {0x628, 0x1EE61}, {0x62A, 0x1EE75}, {0x62B, 0x1EE76}, {0x62C, 0x1EE62}, 57 {0x62D, 0x1EE67}, {0x62E, 0x1EE77}, {0x633, 0x1EE6E}, {0x634, 0x1EE74}, 58 {0x635, 0x1EE71}, {0x636, 0x1EE79}, {0x637, 0x1EE68}, {0x638, 0x1EE7A}, 59 {0x639, 0x1EE6F}, {0x63A, 0x1EE7B}, {0x641, 0x1EE70}, {0x642, 0x1EE72}, 60 {0x643, 0x1EE6A}, {0x645, 0x1EE6C}, {0x646, 0x1EE6D}, {0x647, 0x1EE64}, 61 {0x64A, 0x1EE69}, {0x66E, 0x1EE7C}, {0x6A1, 0x1EE7E}}; 62 63 static const MathVarMapping gArabicLoopedMapTable[] = { 64 {0x627, 0x1EE80}, {0x628, 0x1EE81}, {0x62A, 0x1EE95}, {0x62B, 0x1EE96}, 65 {0x62C, 0x1EE82}, {0x62D, 0x1EE87}, {0x62E, 0x1EE97}, {0x62F, 0x1EE83}, 66 {0x630, 0x1EE98}, {0x631, 0x1EE93}, {0x632, 0x1EE86}, {0x633, 0x1EE8E}, 67 {0x634, 0x1EE94}, {0x635, 0x1EE91}, {0x636, 0x1EE99}, {0x637, 0x1EE88}, 68 {0x638, 0x1EE9A}, {0x639, 0x1EE8F}, {0x63A, 0x1EE9B}, {0x641, 0x1EE90}, 69 {0x642, 0x1EE92}, {0x644, 0x1EE8B}, {0x645, 0x1EE8C}, {0x646, 0x1EE8D}, 70 {0x647, 0x1EE84}, {0x648, 0x1EE85}, {0x64A, 0x1EE89}}; 71 72 static const MathVarMapping gArabicDoubleMapTable[] = { 73 {0x628, 0x1EEA1}, {0x62A, 0x1EEB5}, {0x62B, 0x1EEB6}, {0x62C, 0x1EEA2}, 74 {0x62D, 0x1EEA7}, {0x62E, 0x1EEB7}, {0x62F, 0x1EEA3}, {0x630, 0x1EEB8}, 75 {0x631, 0x1EEB3}, {0x632, 0x1EEA6}, {0x633, 0x1EEAE}, {0x634, 0x1EEB4}, 76 {0x635, 0x1EEB1}, {0x636, 0x1EEB9}, {0x637, 0x1EEA8}, {0x638, 0x1EEBA}, 77 {0x639, 0x1EEAF}, {0x63A, 0x1EEBB}, {0x641, 0x1EEB0}, {0x642, 0x1EEB2}, 78 {0x644, 0x1EEAB}, {0x645, 0x1EEAC}, {0x646, 0x1EEAD}, {0x648, 0x1EEA5}, 79 {0x64A, 0x1EEA9}}; 80 81 static const MathVarMapping gLatinExceptionMapTable[] = { 82 {0x1D455, 0x210E}, {0x1D49D, 0x212C}, {0x1D4A0, 0x2130}, {0x1D4A1, 0x2131}, 83 {0x1D4A3, 0x210B}, {0x1D4A4, 0x2110}, {0x1D4A7, 0x2112}, {0x1D4A8, 0x2133}, 84 {0x1D4AD, 0x211B}, {0x1D4BA, 0x212F}, {0x1D4BC, 0x210A}, {0x1D4C4, 0x2134}, 85 {0x1D506, 0x212D}, {0x1D50B, 0x210C}, {0x1D50C, 0x2111}, {0x1D515, 0x211C}, 86 {0x1D51D, 0x2128}, {0x1D53A, 0x2102}, {0x1D53F, 0x210D}, {0x1D545, 0x2115}, 87 {0x1D547, 0x2119}, {0x1D548, 0x211A}, {0x1D549, 0x211D}, {0x1D551, 0x2124}}; 88 89 namespace { 90 91 struct MathVarMappingWrapper { 92 const MathVarMapping* const mTable; 93 explicit MathVarMappingWrapper(const MathVarMapping* aTable) 94 : mTable(aTable) {} 95 uint32_t operator[](size_t index) const { return mTable[index].mKey; } 96 }; 97 98 } // namespace 99 100 // Finds a MathVarMapping struct with the specified key (aKey) within aTable. 101 // aTable must be an array, whose length is specified by aNumElements 102 static uint32_t MathvarMappingSearch(uint32_t aKey, 103 const MathVarMapping* aTable, 104 uint32_t aNumElements) { 105 size_t index; 106 if (BinarySearch(MathVarMappingWrapper(aTable), 0, aNumElements, aKey, 107 &index)) { 108 return aTable[index].mReplacement; 109 } 110 111 return 0; 112 } 113 114 #define GREEK_UPPER_THETA 0x03F4 115 #define HOLE_GREEK_UPPER_THETA 0x03A2 116 #define NABLA 0x2207 117 #define PARTIAL_DIFFERENTIAL 0x2202 118 #define GREEK_UPPER_ALPHA 0x0391 119 #define GREEK_UPPER_OMEGA 0x03A9 120 #define GREEK_LOWER_ALPHA 0x03B1 121 #define GREEK_LOWER_OMEGA 0x03C9 122 #define GREEK_LUNATE_EPSILON_SYMBOL 0x03F5 123 #define GREEK_THETA_SYMBOL 0x03D1 124 #define GREEK_KAPPA_SYMBOL 0x03F0 125 #define GREEK_PHI_SYMBOL 0x03D5 126 #define GREEK_RHO_SYMBOL 0x03F1 127 #define GREEK_PI_SYMBOL 0x03D6 128 #define GREEK_LETTER_DIGAMMA 0x03DC 129 #define GREEK_SMALL_LETTER_DIGAMMA 0x03DD 130 #define MATH_BOLD_CAPITAL_DIGAMMA 0x1D7CA 131 #define MATH_BOLD_SMALL_DIGAMMA 0x1D7CB 132 133 #define LATIN_SMALL_LETTER_DOTLESS_I 0x0131 134 #define LATIN_SMALL_LETTER_DOTLESS_J 0x0237 135 136 #define MATH_ITALIC_SMALL_DOTLESS_I 0x1D6A4 137 #define MATH_ITALIC_SMALL_DOTLESS_J 0x1D6A5 138 139 #define MATH_BOLD_UPPER_A 0x1D400 140 #define MATH_ITALIC_UPPER_A 0x1D434 141 #define MATH_BOLD_SMALL_A 0x1D41A 142 #define MATH_BOLD_UPPER_ALPHA 0x1D6A8 143 #define MATH_BOLD_SMALL_ALPHA 0x1D6C2 144 #define MATH_ITALIC_UPPER_ALPHA 0x1D6E2 145 #define MATH_BOLD_DIGIT_ZERO 0x1D7CE 146 #define MATH_DOUBLE_STRUCK_ZERO 0x1D7D8 147 148 #define MATH_BOLD_UPPER_THETA 0x1D6B9 149 #define MATH_BOLD_NABLA 0x1D6C1 150 #define MATH_BOLD_PARTIAL_DIFFERENTIAL 0x1D6DB 151 #define MATH_BOLD_EPSILON_SYMBOL 0x1D6DC 152 #define MATH_BOLD_THETA_SYMBOL 0x1D6DD 153 #define MATH_BOLD_KAPPA_SYMBOL 0x1D6DE 154 #define MATH_BOLD_PHI_SYMBOL 0x1D6DF 155 #define MATH_BOLD_RHO_SYMBOL 0x1D6E0 156 #define MATH_BOLD_PI_SYMBOL 0x1D6E1 157 158 /* 159 Performs the character mapping needed to implement MathML's mathvariant 160 attribute. It takes a unicode character and maps it to its appropriate 161 mathvariant counterpart specified by aMathVar. The mapped character is 162 typically located within Unicode's mathematical blocks (0x1D***, 0x1EE**) but 163 there are exceptions which this function accounts for. 164 Characters without a valid mapping or valid aMathvar value are returned 165 unaltered. Characters already in the mathematical blocks (or are one of the 166 exceptions) are never transformed. 167 Acceptable values for aMathVar are specified in layout/style/nsStyleConsts.h. 168 The transformable characters can be found at: 169 http://lists.w3.org/Archives/Public/www-math/2013Sep/0012.html and 170 https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols 171 */ 172 /*static */ uint32_t MathMLTextRunFactory::MathVariant( 173 uint32_t aCh, StyleMathVariant aMathVar) { 174 uint32_t baseChar; 175 enum CharacterType { 176 kIsLatin, 177 kIsGreekish, 178 kIsNumber, 179 kIsArabic, 180 }; 181 CharacterType varType; 182 183 int8_t multiplier; 184 185 if (aMathVar <= StyleMathVariant::Normal) { 186 // nothing to do here 187 return aCh; 188 } 189 if (aMathVar > StyleMathVariant::Stretched) { 190 NS_ASSERTION(false, "Illegal mathvariant value"); 191 return aCh; 192 } 193 194 // Exceptional characters with at most one possible transformation 195 if (aCh == HOLE_GREEK_UPPER_THETA) { 196 // Nothing at this code point is transformed 197 return aCh; 198 } 199 if (aCh == GREEK_LETTER_DIGAMMA) { 200 if (aMathVar == StyleMathVariant::Bold) { 201 return MATH_BOLD_CAPITAL_DIGAMMA; 202 } 203 return aCh; 204 } 205 if (aCh == GREEK_SMALL_LETTER_DIGAMMA) { 206 if (aMathVar == StyleMathVariant::Bold) { 207 return MATH_BOLD_SMALL_DIGAMMA; 208 } 209 return aCh; 210 } 211 if (aCh == LATIN_SMALL_LETTER_DOTLESS_I) { 212 if (aMathVar == StyleMathVariant::Italic) { 213 return MATH_ITALIC_SMALL_DOTLESS_I; 214 } 215 return aCh; 216 } 217 if (aCh == LATIN_SMALL_LETTER_DOTLESS_J) { 218 if (aMathVar == StyleMathVariant::Italic) { 219 return MATH_ITALIC_SMALL_DOTLESS_J; 220 } 221 return aCh; 222 } 223 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. 229 // 230 if ('A' <= aCh && aCh <= 'Z') { 231 baseChar = aCh - 'A'; 232 varType = kIsLatin; 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'; 239 varType = kIsLatin; 240 } else if ('0' <= aCh && aCh <= '9') { 241 baseChar = aCh - '0'; 242 varType = kIsNumber; 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 250 // ones. 251 baseChar = 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 256 varType = kIsArabic; 257 } else { 258 switch (aCh) { 259 case GREEK_UPPER_THETA: 260 baseChar = MATH_BOLD_UPPER_THETA - MATH_BOLD_UPPER_ALPHA; 261 break; 262 case NABLA: 263 baseChar = MATH_BOLD_NABLA - MATH_BOLD_UPPER_ALPHA; 264 break; 265 case PARTIAL_DIFFERENTIAL: 266 baseChar = MATH_BOLD_PARTIAL_DIFFERENTIAL - MATH_BOLD_UPPER_ALPHA; 267 break; 268 case GREEK_LUNATE_EPSILON_SYMBOL: 269 baseChar = MATH_BOLD_EPSILON_SYMBOL - MATH_BOLD_UPPER_ALPHA; 270 break; 271 case GREEK_THETA_SYMBOL: 272 baseChar = MATH_BOLD_THETA_SYMBOL - MATH_BOLD_UPPER_ALPHA; 273 break; 274 case GREEK_KAPPA_SYMBOL: 275 baseChar = MATH_BOLD_KAPPA_SYMBOL - MATH_BOLD_UPPER_ALPHA; 276 break; 277 case GREEK_PHI_SYMBOL: 278 baseChar = MATH_BOLD_PHI_SYMBOL - MATH_BOLD_UPPER_ALPHA; 279 break; 280 case GREEK_RHO_SYMBOL: 281 baseChar = MATH_BOLD_RHO_SYMBOL - MATH_BOLD_UPPER_ALPHA; 282 break; 283 case GREEK_PI_SYMBOL: 284 baseChar = MATH_BOLD_PI_SYMBOL - MATH_BOLD_UPPER_ALPHA; 285 break; 286 default: 287 return aCh; 288 } 289 290 varType = kIsGreekish; 291 } 292 293 if (varType == kIsNumber) { 294 switch (aMathVar) { 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 299 // one. 300 case StyleMathVariant::Bold: 301 multiplier = 0; 302 break; 303 case StyleMathVariant::DoubleStruck: 304 multiplier = 1; 305 break; 306 case StyleMathVariant::SansSerif: 307 multiplier = 2; 308 break; 309 case StyleMathVariant::BoldSansSerif: 310 multiplier = 3; 311 break; 312 case StyleMathVariant::Monospace: 313 multiplier = 4; 314 break; 315 default: 316 // This mathvariant isn't defined for numbers or is otherwise normal 317 return aCh; 318 } 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 325 // code point. 326 return baseChar + 327 multiplier * (MATH_DOUBLE_STRUCK_ZERO - MATH_BOLD_DIGIT_ZERO) + 328 MATH_BOLD_DIGIT_ZERO; 329 } else if (varType == kIsGreekish) { 330 switch (aMathVar) { 331 case StyleMathVariant::Bold: 332 multiplier = 0; 333 break; 334 case StyleMathVariant::Italic: 335 multiplier = 1; 336 break; 337 case StyleMathVariant::BoldItalic: 338 multiplier = 2; 339 break; 340 case StyleMathVariant::BoldSansSerif: 341 multiplier = 3; 342 break; 343 case StyleMathVariant::SansSerifBoldItalic: 344 multiplier = 4; 345 break; 346 default: 347 // This mathvariant isn't defined for Greek or is otherwise normal 348 return aCh; 349 } 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); 353 } 354 355 uint32_t tempChar; 356 uint32_t newChar; 357 if (varType == kIsArabic) { 358 const MathVarMapping* mapTable; 359 uint32_t tableLength; 360 switch (aMathVar) { 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 363 * lookup table. 364 */ 365 case StyleMathVariant::Initial: 366 mapTable = gArabicInitialMapTable; 367 tableLength = std::size(gArabicInitialMapTable); 368 break; 369 case StyleMathVariant::Tailed: 370 mapTable = gArabicTailedMapTable; 371 tableLength = std::size(gArabicTailedMapTable); 372 break; 373 case StyleMathVariant::Stretched: 374 mapTable = gArabicStretchedMapTable; 375 tableLength = std::size(gArabicStretchedMapTable); 376 break; 377 case StyleMathVariant::Looped: 378 mapTable = gArabicLoopedMapTable; 379 tableLength = std::size(gArabicLoopedMapTable); 380 break; 381 case StyleMathVariant::DoubleStruck: 382 mapTable = gArabicDoubleMapTable; 383 tableLength = std::size(gArabicDoubleMapTable); 384 break; 385 default: 386 // No valid transformations exist 387 return aCh; 388 } 389 newChar = MathvarMappingSearch(aCh, mapTable, tableLength); 390 } else { 391 // Must be Latin 392 if (aMathVar > StyleMathVariant::Monospace) { 393 // Latin doesn't support the Arabic mathvariants 394 return aCh; 395 } 396 multiplier = uint8_t(aMathVar) - 2; 397 // This is possible because the values for StyleMathVariant::* 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 std::size(gLatinExceptionMapTable)); 409 } 410 411 if (newChar) { 412 return newChar; 413 } else if (varType == kIsLatin) { 414 return tempChar; 415 } else { 416 // An Arabic character without a corresponding mapping 417 return aCh; 418 } 419 } 420 421 #define TT_SSTY TRUETYPE_TAG('s', 's', 't', 'y') 422 #define TT_DTLS TRUETYPE_TAG('d', 't', 'l', 's') 423 424 void MathMLTextRunFactory::RebuildTextRun( 425 nsTransformedTextRun* aTextRun, mozilla::gfx::DrawTarget* aRefDrawTarget, 426 gfxMissingFontRecorder* aMFR) { 427 gfxFontGroup* fontGroup = aTextRun->GetFontGroup(); 428 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; 435 436 bool singleCharMI = 437 !!(aTextRun->GetFlags2() & nsTextFrameUtils::Flags::IsSingleCharMi); 438 439 uint32_t length = aTextRun->GetLength(); 440 const char16_t* str = aTextRun->mString.BeginReading(); 441 const nsTArray<RefPtr<nsTransformedCharStyle>>& styles = aTextRun->mStyles; 442 nsFont font; 443 if (length) { 444 font = styles[0]->mFont; 445 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) { 452 foundSSTY = true; 453 } else if (font.fontFeatureSettings[i].mTag == TT_DTLS) { 454 foundDTLS = true; 455 } 456 } 457 if (mSSTYScriptLevel && !foundSSTY) { 458 uint8_t sstyLevel = 0; 459 // FIXME: Use the same logic as scale_factor_for_math_depth_change? 460 float scriptScaling = 461 pow(kMathMLDefaultScriptSizeMultiplier, mSSTYScriptLevel); 462 static_assert(kMathMLDefaultScriptSizeMultiplier < 1, 463 "Shouldn't it make things smaller?"); 464 /* 465 An SSTY level of 2 is set if the scaling factor is less than or equal 466 to halfway between that for a scriptlevel of 1 (0.71) and that of a 467 scriptlevel of 2 (0.71^2), assuming the default script size 468 multiplier. An SSTY level of 1 is set if the script scaling factor is 469 less than or equal that for a scriptlevel of 1 assuming the default 470 script size multiplier. 471 472 User specified values of script size multiplier will change the 473 scaling factor which mSSTYScriptLevel values correspond to. 474 475 In the event that the script size multiplier actually makes things 476 larger, no change is made. 477 478 To opt out of this change, add the following to the stylesheet: 479 "font-feature-settings: 'ssty' 0" 480 */ 481 if (scriptScaling <= (kMathMLDefaultScriptSizeMultiplier + 482 (kMathMLDefaultScriptSizeMultiplier * 483 kMathMLDefaultScriptSizeMultiplier)) / 484 2) { 485 // Currently only the first two ssty settings are used, so two is 486 // large as we go 487 sstyLevel = 2; 488 } else if (scriptScaling <= kMathMLDefaultScriptSizeMultiplier) { 489 sstyLevel = 1; 490 } 491 if (sstyLevel) { 492 gfxFontFeature settingSSTY; 493 settingSSTY.mTag = TT_SSTY; 494 settingSSTY.mValue = sstyLevel; 495 font.fontFeatureSettings.AppendElement(settingSSTY); 496 } 497 } 498 /* 499 Apply the dtls font feature setting (dotless). 500 This gets applied to the base frame and all descendants of the base 501 frame of certain <mover> and <munderover> frames. 502 503 See nsMathMLmunderoverFrame.cpp for a full description. 504 505 To opt out of this change, add the following to the stylesheet: 506 "font-feature-settings: 'dtls' 0" 507 */ 508 if ((mFlags & MATH_FONT_FEATURE_DTLS) && !foundDTLS) { 509 gfxFontFeature settingDTLS; 510 settingDTLS.mTag = TT_DTLS; 511 settingDTLS.mValue = 1; 512 font.fontFeatureSettings.AppendElement(settingDTLS); 513 } 514 } 515 } 516 517 StyleMathVariant mathVar = StyleMathVariant::None; 518 bool doMathvariantStyling = true; 519 520 // Ensure the fontGroup is ready to be searched. 521 fontGroup->EnsureFontList(); 522 523 for (uint32_t i = 0; i < length; ++i) { 524 int extraChars = 0; 525 mathVar = styles[i]->mMathVariant; 526 527 if (singleCharMI && mathVar == StyleMathVariant::None && 528 (!StaticPrefs::mathml_legacy_mathvariant_attribute_disabled() || 529 styles[i]->mTextTransform & StyleTextTransform::MATH_AUTO)) { 530 mathVar = StyleMathVariant::Italic; 531 } 532 533 uint32_t ch = str[i]; 534 if (i < length - 1 && NS_IS_SURROGATE_PAIR(ch, str[i + 1])) { 535 ch = SURROGATE_TO_UCS4(ch, str[i + 1]); 536 } 537 uint32_t ch2 = MathVariant(ch, mathVar); 538 539 if (!StaticPrefs::mathml_mathvariant_styling_fallback_disabled() && 540 (mathVar == StyleMathVariant::Bold || 541 mathVar == StyleMathVariant::BoldItalic || 542 mathVar == StyleMathVariant::Italic)) { 543 if (ch == ch2 && ch != 0x20 && ch != 0xA0) { 544 // Don't apply the CSS style if a character cannot be 545 // transformed. There is an exception for whitespace as it is both 546 // common and innocuous. 547 doMathvariantStyling = false; 548 } 549 if (ch2 != ch) { 550 // Bug 930504. Some platforms do not have fonts for Mathematical 551 // Alphanumeric Symbols. Hence we check whether the transformed 552 // character is actually available. 553 FontMatchType matchType; 554 RefPtr<gfxFont> mathFont = fontGroup->FindFontForChar( 555 ch2, 0, 0, intl::Script::COMMON, nullptr, &matchType); 556 if (mathFont) { 557 // Don't apply the CSS style if there is a math font for at least one 558 // of the transformed character in this text run. 559 doMathvariantStyling = false; 560 } else { 561 // We fallback to the original character. 562 ch2 = ch; 563 if (aMFR) { 564 aMFR->RecordScript(intl::Script::MATHEMATICAL_NOTATION); 565 } 566 } 567 } 568 } 569 570 deletedCharsArray.AppendElement(false); 571 charsToMergeArray.AppendElement(false); 572 styleArray.AppendElement(styles[i]); 573 canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i)); 574 575 if (IS_IN_BMP(ch2)) { 576 convertedString.Append(ch2); 577 } else { 578 convertedString.Append(H_SURROGATE(ch2)); 579 convertedString.Append(L_SURROGATE(ch2)); 580 ++extraChars; 581 if (!IS_IN_BMP(ch)) { 582 deletedCharsArray.AppendElement( 583 true); // not exactly deleted, but 584 // the trailing surrogate is skipped 585 ++i; 586 } 587 } 588 589 while (extraChars-- > 0) { 590 mergeNeeded = true; 591 charsToMergeArray.AppendElement(true); 592 styleArray.AppendElement(styles[i]); 593 canBreakBeforeArray.AppendElement(false); 594 } 595 } 596 597 gfx::ShapedTextFlags flags; 598 gfxTextRunFactory::Parameters innerParams = 599 GetParametersForInner(aTextRun, &flags, aRefDrawTarget); 600 601 RefPtr<nsTransformedTextRun> transformedChild; 602 RefPtr<gfxTextRun> cachedChild; 603 gfxTextRun* child; 604 605 if (!StaticPrefs::mathml_mathvariant_styling_fallback_disabled() && 606 doMathvariantStyling) { 607 if (mathVar == StyleMathVariant::Bold) { 608 font.style = FontSlantStyle::NORMAL; 609 font.weight = FontWeight::BOLD; 610 } else if (mathVar == StyleMathVariant::Italic) { 611 font.style = FontSlantStyle::ITALIC; 612 font.weight = FontWeight::NORMAL; 613 } else if (mathVar == StyleMathVariant::BoldItalic) { 614 font.style = FontSlantStyle::ITALIC; 615 font.weight = FontWeight::BOLD; 616 } 617 } 618 gfxFontGroup* newFontGroup = nullptr; 619 620 // Get the correct gfxFontGroup that corresponds to the earlier font changes. 621 if (length) { 622 font.size = font.size.ScaledBy(mFontInflation); 623 nsPresContext* pc = styles[0]->mPresContext; 624 nsFontMetrics::Params params; 625 params.language = styles[0]->mLanguage; 626 params.explicitLanguage = styles[0]->mExplicitLanguage; 627 params.userFontSet = pc->GetUserFontSet(); 628 params.textPerf = pc->GetTextPerfMetrics(); 629 params.featureValueLookup = pc->GetFontFeatureValuesLookup(); 630 RefPtr<nsFontMetrics> metrics = pc->GetMetricsFor(font, params); 631 newFontGroup = metrics->GetThebesFontGroup(); 632 } 633 634 if (!newFontGroup) { 635 // If we can't get a new font group, fall back to the old one. Rendering 636 // will be incorrect, but not significantly so. 637 newFontGroup = fontGroup; 638 } 639 640 if (mInnerTransformingTextRunFactory) { 641 transformedChild = mInnerTransformingTextRunFactory->MakeTextRun( 642 convertedString.BeginReading(), convertedString.Length(), &innerParams, 643 newFontGroup, flags, nsTextFrameUtils::Flags(), std::move(styleArray), 644 false); 645 child = transformedChild.get(); 646 } else { 647 cachedChild = newFontGroup->MakeTextRun( 648 convertedString.BeginReading(), convertedString.Length(), &innerParams, 649 flags, nsTextFrameUtils::Flags(), aMFR); 650 child = cachedChild.get(); 651 } 652 if (!child) { 653 return; 654 } 655 656 typedef gfxTextRun::Range Range; 657 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); 666 } 667 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); 682 } 683 }