1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/MathMLTextRunFactory.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,730 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "MathMLTextRunFactory.h" 1.10 + 1.11 +#include "mozilla/ArrayUtils.h" 1.12 + 1.13 +#include "nsStyleConsts.h" 1.14 +#include "nsStyleContext.h" 1.15 +#include "nsTextFrameUtils.h" 1.16 + 1.17 +using namespace mozilla; 1.18 + 1.19 +/* 1.20 + Entries for the mathvariant lookup tables. mKey represents the Unicode 1.21 + character to be transformed and is used for searching the tables. 1.22 + mReplacement represents the mapped mathvariant Unicode character. 1.23 +*/ 1.24 +typedef struct 1.25 +{ 1.26 + uint32_t mKey; 1.27 + uint32_t mReplacement; 1.28 +} MathVarMapping; 1.29 + 1.30 +/* 1.31 + Lookup tables for use with mathvariant mappings to transform a unicode 1.32 + character point to another unicode character that indicates the proper output. 1.33 + mKey represents one of two concepts. 1.34 + 1. In the Latin table it represents a hole in the mathematical alphanumeric 1.35 + block, where the character that should occupy that position is located 1.36 + elsewhere. 1.37 + 2. It represents an Arabic letter. 1.38 + 1.39 + As a replacement, 0 is reserved to indicate no mapping was found. 1.40 +*/ 1.41 +static const MathVarMapping gArabicInitialMapTable[] = { 1.42 + { 0x628, 0x1EE21 }, 1.43 + { 0x62A, 0x1EE35 }, 1.44 + { 0x62B, 0x1EE36 }, 1.45 + { 0x62C, 0x1EE22 }, 1.46 + { 0x62D, 0x1EE27 }, 1.47 + { 0x62E, 0x1EE37 }, 1.48 + { 0x633, 0x1EE2E }, 1.49 + { 0x634, 0x1EE34 }, 1.50 + { 0x635, 0x1EE31 }, 1.51 + { 0x636, 0x1EE39 }, 1.52 + { 0x639, 0x1EE2F }, 1.53 + { 0x63A, 0x1EE3B }, 1.54 + { 0x641, 0x1EE30 }, 1.55 + { 0x642, 0x1EE32 }, 1.56 + { 0x643, 0x1EE2A }, 1.57 + { 0x644, 0x1EE2B }, 1.58 + { 0x645, 0x1EE2C }, 1.59 + { 0x646, 0x1EE2D }, 1.60 + { 0x647, 0x1EE24 }, 1.61 + { 0x64A, 0x1EE29 } 1.62 +}; 1.63 + 1.64 +static const MathVarMapping gArabicTailedMapTable[] = { 1.65 + { 0x62C, 0x1EE42 }, 1.66 + { 0x62D, 0x1EE47 }, 1.67 + { 0x62E, 0x1EE57 }, 1.68 + { 0x633, 0x1EE4E }, 1.69 + { 0x634, 0x1EE54 }, 1.70 + { 0x635, 0x1EE51 }, 1.71 + { 0x636, 0x1EE59 }, 1.72 + { 0x639, 0x1EE4F }, 1.73 + { 0x63A, 0x1EE5B }, 1.74 + { 0x642, 0x1EE52 }, 1.75 + { 0x644, 0x1EE4B }, 1.76 + { 0x646, 0x1EE4D }, 1.77 + { 0x64A, 0x1EE49 }, 1.78 + { 0x66F, 0x1EE5F }, 1.79 + { 0x6BA, 0x1EE5D } 1.80 +}; 1.81 + 1.82 +static const MathVarMapping gArabicStretchedMapTable[] = { 1.83 + { 0x628, 0x1EE61 }, 1.84 + { 0x62A, 0x1EE75 }, 1.85 + { 0x62B, 0x1EE76 }, 1.86 + { 0x62C, 0x1EE62 }, 1.87 + { 0x62D, 0x1EE67 }, 1.88 + { 0x62E, 0x1EE77 }, 1.89 + { 0x633, 0x1EE6E }, 1.90 + { 0x634, 0x1EE74 }, 1.91 + { 0x635, 0x1EE71 }, 1.92 + { 0x636, 0x1EE79 }, 1.93 + { 0x637, 0x1EE68 }, 1.94 + { 0x638, 0x1EE7A }, 1.95 + { 0x639, 0x1EE6F }, 1.96 + { 0x63A, 0x1EE7B }, 1.97 + { 0x641, 0x1EE70 }, 1.98 + { 0x642, 0x1EE72 }, 1.99 + { 0x643, 0x1EE6A }, 1.100 + { 0x645, 0x1EE6C }, 1.101 + { 0x646, 0x1EE6D }, 1.102 + { 0x647, 0x1EE64 }, 1.103 + { 0x64A, 0x1EE69 }, 1.104 + { 0x66E, 0x1EE7C }, 1.105 + { 0x6A1, 0x1EE7E } 1.106 +}; 1.107 + 1.108 +static const MathVarMapping gArabicLoopedMapTable[] = { 1.109 + { 0x627, 0x1EE80 }, 1.110 + { 0x628, 0x1EE81 }, 1.111 + { 0x62A, 0x1EE95 }, 1.112 + { 0x62B, 0x1EE96 }, 1.113 + { 0x62C, 0x1EE82 }, 1.114 + { 0x62D, 0x1EE87 }, 1.115 + { 0x62E, 0x1EE97 }, 1.116 + { 0x62F, 0x1EE83 }, 1.117 + { 0x630, 0x1EE98 }, 1.118 + { 0x631, 0x1EE93 }, 1.119 + { 0x632, 0x1EE86 }, 1.120 + { 0x633, 0x1EE8E }, 1.121 + { 0x634, 0x1EE94 }, 1.122 + { 0x635, 0x1EE91 }, 1.123 + { 0x636, 0x1EE99 }, 1.124 + { 0x637, 0x1EE88 }, 1.125 + { 0x638, 0x1EE9A }, 1.126 + { 0x639, 0x1EE8F }, 1.127 + { 0x63A, 0x1EE9B }, 1.128 + { 0x641, 0x1EE90 }, 1.129 + { 0x642, 0x1EE92 }, 1.130 + { 0x644, 0x1EE8B }, 1.131 + { 0x645, 0x1EE8C }, 1.132 + { 0x646, 0x1EE8D }, 1.133 + { 0x647, 0x1EE84 }, 1.134 + { 0x648, 0x1EE85 }, 1.135 + { 0x64A, 0x1EE89 } 1.136 +}; 1.137 + 1.138 +static const MathVarMapping gArabicDoubleMapTable[] = { 1.139 + { 0x628, 0x1EEA1 }, 1.140 + { 0x62A, 0x1EEB5 }, 1.141 + { 0x62B, 0x1EEB6 }, 1.142 + { 0x62C, 0x1EEA2 }, 1.143 + { 0x62D, 0x1EEA7 }, 1.144 + { 0x62E, 0x1EEB7 }, 1.145 + { 0x62F, 0x1EEA3 }, 1.146 + { 0x630, 0x1EEB8 }, 1.147 + { 0x631, 0x1EEB3 }, 1.148 + { 0x632, 0x1EEA6 }, 1.149 + { 0x633, 0x1EEAE }, 1.150 + { 0x634, 0x1EEB4 }, 1.151 + { 0x635, 0x1EEB1 }, 1.152 + { 0x636, 0x1EEB9 }, 1.153 + { 0x637, 0x1EEA8 }, 1.154 + { 0x638, 0x1EEBA }, 1.155 + { 0x639, 0x1EEAF }, 1.156 + { 0x63A, 0x1EEBB }, 1.157 + { 0x641, 0x1EEB0 }, 1.158 + { 0x642, 0x1EEB2 }, 1.159 + { 0x644, 0x1EEAB }, 1.160 + { 0x645, 0x1EEAC }, 1.161 + { 0x646, 0x1EEAD }, 1.162 + { 0x648, 0x1EEA5 }, 1.163 + { 0x64A, 0x1EEA9 } 1.164 +}; 1.165 + 1.166 +static const MathVarMapping gLatinExceptionMapTable[] = { 1.167 + { 0x1D455, 0x210E }, 1.168 + { 0x1D49D, 0x212C }, 1.169 + { 0x1D4A0, 0x2130 }, 1.170 + { 0x1D4A1, 0x2131 }, 1.171 + { 0x1D4A3, 0x210B }, 1.172 + { 0x1D4A4, 0x2110 }, 1.173 + { 0x1D4A7, 0x2112 }, 1.174 + { 0x1D4A8, 0x2133 }, 1.175 + { 0x1D4AD, 0x211B }, 1.176 + { 0x1D4BA, 0x212F }, 1.177 + { 0x1D4BC, 0x210A }, 1.178 + { 0x1D4C4, 0x2134 }, 1.179 + { 0x1D506, 0x212D }, 1.180 + { 0x1D50B, 0x210C }, 1.181 + { 0x1D50C, 0x2111 }, 1.182 + { 0x1D515, 0x211C }, 1.183 + { 0x1D51D, 0x2128 }, 1.184 + { 0x1D53A, 0x2102 }, 1.185 + { 0x1D53F, 0x210D }, 1.186 + { 0x1D545, 0x2115 }, 1.187 + { 0x1D547, 0x2119 }, 1.188 + { 0x1D548, 0x211A }, 1.189 + { 0x1D549, 0x211D }, 1.190 + { 0x1D551, 0x2124 } 1.191 +}; 1.192 + 1.193 +// Finds a MathVarMapping struct with the specified key (aKey) within aTable. 1.194 +// aTable must be an array, whose length is specified by aNumElements 1.195 +static uint32_t 1.196 +MathvarMappingSearch(uint32_t aKey, const MathVarMapping* aTable, uint32_t aNumElements) 1.197 +{ 1.198 + uint32_t low = 0; 1.199 + uint32_t high = aNumElements; 1.200 + while (high > low) { 1.201 + uint32_t midPoint = (low+high) >> 1; 1.202 + if (aKey == aTable[midPoint].mKey) { 1.203 + return aTable[midPoint].mReplacement; 1.204 + } 1.205 + if (aKey > aTable[midPoint].mKey) { 1.206 + low = midPoint + 1; 1.207 + } else { 1.208 + high = midPoint; 1.209 + } 1.210 + } 1.211 + return 0; 1.212 +} 1.213 + 1.214 +#define GREEK_UPPER_THETA 0x03F4 1.215 +#define HOLE_GREEK_UPPER_THETA 0x03A2 1.216 +#define NABLA 0x2207 1.217 +#define PARTIAL_DIFFERENTIAL 0x2202 1.218 +#define GREEK_UPPER_ALPHA 0x0391 1.219 +#define GREEK_UPPER_OMEGA 0x03A9 1.220 +#define GREEK_LOWER_ALPHA 0x03B1 1.221 +#define GREEK_LOWER_OMEGA 0x03C9 1.222 +#define GREEK_LUNATE_EPSILON_SYMBOL 0x03F5 1.223 +#define GREEK_THETA_SYMBOL 0x03D1 1.224 +#define GREEK_KAPPA_SYMBOL 0x03F0 1.225 +#define GREEK_PHI_SYMBOL 0x03D5 1.226 +#define GREEK_RHO_SYMBOL 0x03F1 1.227 +#define GREEK_PI_SYMBOL 0x03D6 1.228 +#define GREEK_LETTER_DIGAMMA 0x03DC 1.229 +#define GREEK_SMALL_LETTER_DIGAMMA 0x03DD 1.230 +#define MATH_BOLD_CAPITAL_DIGAMMA 0x1D7CA 1.231 +#define MATH_BOLD_SMALL_DIGAMMA 0x1D7CB 1.232 + 1.233 +#define LATIN_SMALL_LETTER_DOTLESS_I 0x0131 1.234 +#define LATIN_SMALL_LETTER_DOTLESS_J 0x0237 1.235 + 1.236 +#define MATH_ITALIC_SMALL_DOTLESS_I 0x1D6A4 1.237 +#define MATH_ITALIC_SMALL_DOTLESS_J 0x1D6A5 1.238 + 1.239 +#define MATH_BOLD_UPPER_A 0x1D400 1.240 +#define MATH_ITALIC_UPPER_A 0x1D434 1.241 +#define MATH_BOLD_SMALL_A 0x1D41A 1.242 +#define MATH_BOLD_UPPER_ALPHA 0x1D6A8 1.243 +#define MATH_BOLD_SMALL_ALPHA 0x1D6C2 1.244 +#define MATH_ITALIC_UPPER_ALPHA 0x1D6E2 1.245 +#define MATH_BOLD_DIGIT_ZERO 0x1D7CE 1.246 +#define MATH_DOUBLE_STRUCK_ZERO 0x1D7D8 1.247 + 1.248 +#define MATH_BOLD_UPPER_THETA 0x1D6B9 1.249 +#define MATH_BOLD_NABLA 0x1D6C1 1.250 +#define MATH_BOLD_PARTIAL_DIFFERENTIAL 0x1D6DB 1.251 +#define MATH_BOLD_EPSILON_SYMBOL 0x1D6DC 1.252 +#define MATH_BOLD_THETA_SYMBOL 0x1D6DD 1.253 +#define MATH_BOLD_KAPPA_SYMBOL 0x1D6DE 1.254 +#define MATH_BOLD_PHI_SYMBOL 0x1D6DF 1.255 +#define MATH_BOLD_RHO_SYMBOL 0x1D6E0 1.256 +#define MATH_BOLD_PI_SYMBOL 0x1D6E1 1.257 + 1.258 +/* 1.259 + Performs the character mapping needed to implement MathML's mathvariant 1.260 + attribute. It takes a unicode character and maps it to its appropriate 1.261 + mathvariant counterpart specified by aMathVar. The mapped character is 1.262 + typically located within Unicode's mathematical blocks (0x1D***, 0x1EE**) but 1.263 + there are exceptions which this function accounts for. 1.264 + Characters without a valid mapping or valid aMathvar value are returned 1.265 + unaltered. Characters already in the mathematical blocks (or are one of the 1.266 + exceptions) are never transformed. 1.267 + Acceptable values for aMathVar are specified in layout/style/nsStyleConsts.h. 1.268 + The transformable characters can be found at: 1.269 + http://lists.w3.org/Archives/Public/www-math/2013Sep/0012.html and 1.270 + https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols 1.271 +*/ 1.272 +static uint32_t 1.273 +MathVariant(uint32_t aCh, uint8_t aMathVar) 1.274 +{ 1.275 + uint32_t baseChar; 1.276 + enum CharacterType { 1.277 + kIsLatin, 1.278 + kIsGreekish, 1.279 + kIsNumber, 1.280 + kIsArabic, 1.281 + }; 1.282 + CharacterType varType; 1.283 + 1.284 + int8_t multiplier; 1.285 + 1.286 + if (aMathVar <= NS_MATHML_MATHVARIANT_NORMAL) { 1.287 + // nothing to do here 1.288 + return aCh; 1.289 + } 1.290 + if (aMathVar > NS_MATHML_MATHVARIANT_STRETCHED) { 1.291 + NS_ASSERTION(false, "Illegal mathvariant value"); 1.292 + return aCh; 1.293 + } 1.294 + 1.295 + // Exceptional characters with at most one possible transformation 1.296 + if (aCh == HOLE_GREEK_UPPER_THETA) { 1.297 + // Nothing at this code point is transformed 1.298 + return aCh; 1.299 + } 1.300 + if (aCh == GREEK_LETTER_DIGAMMA) { 1.301 + if (aMathVar == NS_MATHML_MATHVARIANT_BOLD) { 1.302 + return MATH_BOLD_CAPITAL_DIGAMMA; 1.303 + } 1.304 + return aCh; 1.305 + } 1.306 + if (aCh == GREEK_SMALL_LETTER_DIGAMMA) { 1.307 + if (aMathVar == NS_MATHML_MATHVARIANT_BOLD) { 1.308 + return MATH_BOLD_SMALL_DIGAMMA; 1.309 + } 1.310 + return aCh; 1.311 + } 1.312 + if (aCh == LATIN_SMALL_LETTER_DOTLESS_I) { 1.313 + if (aMathVar == NS_MATHML_MATHVARIANT_ITALIC) { 1.314 + return MATH_ITALIC_SMALL_DOTLESS_I; 1.315 + } 1.316 + return aCh; 1.317 + } 1.318 + if (aCh == LATIN_SMALL_LETTER_DOTLESS_J) { 1.319 + if (aMathVar == NS_MATHML_MATHVARIANT_ITALIC) { 1.320 + return MATH_ITALIC_SMALL_DOTLESS_J; 1.321 + } 1.322 + return aCh; 1.323 + } 1.324 + 1.325 + // The Unicode mathematical blocks are divided into four segments: Latin, 1.326 + // Greek, numbers and Arabic. In the case of the first three 1.327 + // baseChar represents the relative order in which the characters are 1.328 + // encoded in the Unicode mathematical block, normalised to the first 1.329 + // character of that sequence. 1.330 + // 1.331 + if ('A' <= aCh && aCh <= 'Z') { 1.332 + baseChar = aCh - 'A'; 1.333 + varType = kIsLatin; 1.334 + } else if ('a' <= aCh && aCh <= 'z') { 1.335 + // Lowercase characters are placed immediately after the uppercase 1.336 + // characters in the Unicode mathematical block. The constant subtraction 1.337 + // represents the number of characters between the start of the sequence 1.338 + // (capital A) and the first lowercase letter. 1.339 + baseChar = MATH_BOLD_SMALL_A-MATH_BOLD_UPPER_A + aCh - 'a'; 1.340 + varType = kIsLatin; 1.341 + } else if ('0' <= aCh && aCh <= '9') { 1.342 + baseChar = aCh - '0'; 1.343 + varType = kIsNumber; 1.344 + } else if (GREEK_UPPER_ALPHA <= aCh && aCh <= GREEK_UPPER_OMEGA) { 1.345 + baseChar = aCh-GREEK_UPPER_ALPHA; 1.346 + varType = kIsGreekish; 1.347 + } else if (GREEK_LOWER_ALPHA <= aCh && aCh <= GREEK_LOWER_OMEGA) { 1.348 + // Lowercase Greek comes after uppercase Greek. 1.349 + // Note in this instance the presence of an additional character (Nabla) 1.350 + // between the end of the uppercase Greek characters and the lowercase 1.351 + // ones. 1.352 + baseChar = MATH_BOLD_SMALL_ALPHA - MATH_BOLD_UPPER_ALPHA 1.353 + + aCh-GREEK_LOWER_ALPHA; 1.354 + varType = kIsGreekish; 1.355 + } else if (0x0600 <= aCh && aCh <= 0x06FF) { 1.356 + // Arabic characters are defined within this range 1.357 + varType = kIsArabic; 1.358 + } else { 1.359 + switch (aCh) { 1.360 + case GREEK_UPPER_THETA: 1.361 + baseChar = MATH_BOLD_UPPER_THETA-MATH_BOLD_UPPER_ALPHA; 1.362 + break; 1.363 + case NABLA: 1.364 + baseChar = MATH_BOLD_NABLA-MATH_BOLD_UPPER_ALPHA; 1.365 + break; 1.366 + case PARTIAL_DIFFERENTIAL: 1.367 + baseChar = MATH_BOLD_PARTIAL_DIFFERENTIAL - MATH_BOLD_UPPER_ALPHA; 1.368 + break; 1.369 + case GREEK_LUNATE_EPSILON_SYMBOL: 1.370 + baseChar = MATH_BOLD_EPSILON_SYMBOL - MATH_BOLD_UPPER_ALPHA; 1.371 + break; 1.372 + case GREEK_THETA_SYMBOL: 1.373 + baseChar = MATH_BOLD_THETA_SYMBOL - MATH_BOLD_UPPER_ALPHA; 1.374 + break; 1.375 + case GREEK_KAPPA_SYMBOL: 1.376 + baseChar = MATH_BOLD_KAPPA_SYMBOL - MATH_BOLD_UPPER_ALPHA; 1.377 + break; 1.378 + case GREEK_PHI_SYMBOL: 1.379 + baseChar = MATH_BOLD_PHI_SYMBOL - MATH_BOLD_UPPER_ALPHA; 1.380 + break; 1.381 + case GREEK_RHO_SYMBOL: 1.382 + baseChar = MATH_BOLD_RHO_SYMBOL - MATH_BOLD_UPPER_ALPHA; 1.383 + break; 1.384 + case GREEK_PI_SYMBOL: 1.385 + baseChar = MATH_BOLD_PI_SYMBOL - MATH_BOLD_UPPER_ALPHA; 1.386 + break; 1.387 + default: 1.388 + return aCh; 1.389 + } 1.390 + 1.391 + varType = kIsGreekish; 1.392 + } 1.393 + 1.394 + if (varType == kIsNumber) { 1.395 + switch (aMathVar) { 1.396 + // Each possible number mathvariant is encoded in a single, contiguous 1.397 + // block. For example the beginning of the double struck number range 1.398 + // follows immediately after the end of the bold number range. 1.399 + // multiplier represents the order of the sequences relative to the first 1.400 + // one. 1.401 + case NS_MATHML_MATHVARIANT_BOLD: 1.402 + multiplier = 0; 1.403 + break; 1.404 + case NS_MATHML_MATHVARIANT_DOUBLE_STRUCK: 1.405 + multiplier = 1; 1.406 + break; 1.407 + case NS_MATHML_MATHVARIANT_SANS_SERIF: 1.408 + multiplier = 2; 1.409 + break; 1.410 + case NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF: 1.411 + multiplier = 3; 1.412 + break; 1.413 + case NS_MATHML_MATHVARIANT_MONOSPACE: 1.414 + multiplier = 4; 1.415 + break; 1.416 + default: 1.417 + // This mathvariant isn't defined for numbers or is otherwise normal 1.418 + return aCh; 1.419 + } 1.420 + // As the ranges are contiguous, to find the desired mathvariant range it 1.421 + // is sufficient to multiply the position within the sequence order 1.422 + // (multiplier) with the period of the sequence (which is constant for all 1.423 + // number sequences) and to add the character point of the first character 1.424 + // within the number mathvariant range. 1.425 + // To this the baseChar calculated earlier is added to obtain the final 1.426 + // code point. 1.427 + return baseChar+multiplier*(MATH_DOUBLE_STRUCK_ZERO-MATH_BOLD_DIGIT_ZERO) 1.428 + +MATH_BOLD_DIGIT_ZERO; 1.429 + } else if (varType == kIsGreekish) { 1.430 + switch (aMathVar) { 1.431 + case NS_MATHML_MATHVARIANT_BOLD: 1.432 + multiplier = 0; 1.433 + break; 1.434 + case NS_MATHML_MATHVARIANT_ITALIC: 1.435 + multiplier = 1; 1.436 + break; 1.437 + case NS_MATHML_MATHVARIANT_BOLD_ITALIC: 1.438 + multiplier = 2; 1.439 + break; 1.440 + case NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF: 1.441 + multiplier = 3; 1.442 + break; 1.443 + case NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC: 1.444 + multiplier = 4; 1.445 + break; 1.446 + default: 1.447 + // This mathvariant isn't defined for Greek or is otherwise normal 1.448 + return aCh; 1.449 + } 1.450 + // See the kIsNumber case for an explanation of the following calculation 1.451 + return baseChar + MATH_BOLD_UPPER_ALPHA + 1.452 + multiplier*(MATH_ITALIC_UPPER_ALPHA - MATH_BOLD_UPPER_ALPHA); 1.453 + } 1.454 + 1.455 + uint32_t tempChar; 1.456 + uint32_t newChar; 1.457 + if (varType == kIsArabic) { 1.458 + const MathVarMapping* mapTable; 1.459 + uint32_t tableLength; 1.460 + switch (aMathVar) { 1.461 + /* The Arabic mathematical block is not continuous, nor does it have a 1.462 + * monotonic mapping to the unencoded characters, requiring the use of a 1.463 + * lookup table. 1.464 + */ 1.465 + case NS_MATHML_MATHVARIANT_INITIAL: 1.466 + mapTable = gArabicInitialMapTable; 1.467 + tableLength = ArrayLength(gArabicInitialMapTable); 1.468 + break; 1.469 + case NS_MATHML_MATHVARIANT_TAILED: 1.470 + mapTable = gArabicTailedMapTable; 1.471 + tableLength = ArrayLength(gArabicTailedMapTable); 1.472 + break; 1.473 + case NS_MATHML_MATHVARIANT_STRETCHED: 1.474 + mapTable = gArabicStretchedMapTable; 1.475 + tableLength = ArrayLength(gArabicStretchedMapTable); 1.476 + break; 1.477 + case NS_MATHML_MATHVARIANT_LOOPED: 1.478 + mapTable = gArabicLoopedMapTable; 1.479 + tableLength = ArrayLength(gArabicLoopedMapTable); 1.480 + break; 1.481 + case NS_MATHML_MATHVARIANT_DOUBLE_STRUCK: 1.482 + mapTable = gArabicDoubleMapTable; 1.483 + tableLength = ArrayLength(gArabicDoubleMapTable); 1.484 + break; 1.485 + default: 1.486 + // No valid transformations exist 1.487 + return aCh; 1.488 + } 1.489 + newChar = MathvarMappingSearch(aCh, mapTable, tableLength); 1.490 + } else { 1.491 + // Must be Latin 1.492 + if (aMathVar > NS_MATHML_MATHVARIANT_MONOSPACE) { 1.493 + // Latin doesn't support the Arabic mathvariants 1.494 + return aCh; 1.495 + } 1.496 + multiplier = aMathVar - 2; 1.497 + // This is possible because the values for NS_MATHML_MATHVARIANT_* are 1.498 + // chosen to coincide with the order in which the encoded mathvariant 1.499 + // characters are located within their unicode block (less an offset to 1.500 + // avoid _NONE and _NORMAL variants) 1.501 + // See the kIsNumber case for an explanation of the following calculation 1.502 + tempChar = baseChar + MATH_BOLD_UPPER_A + 1.503 + multiplier*(MATH_ITALIC_UPPER_A - MATH_BOLD_UPPER_A); 1.504 + // There are roughly twenty characters that are located outside of the 1.505 + // mathematical block, so the spaces where they ought to be are used 1.506 + // as keys for a lookup table containing the correct character mappings. 1.507 + newChar = MathvarMappingSearch(tempChar, gLatinExceptionMapTable, 1.508 + ArrayLength(gLatinExceptionMapTable)); 1.509 + } 1.510 + 1.511 + if (newChar) { 1.512 + return newChar; 1.513 + } else if (varType == kIsLatin) { 1.514 + return tempChar; 1.515 + } else { 1.516 + // An Arabic character without a corresponding mapping 1.517 + return aCh; 1.518 + } 1.519 + 1.520 +} 1.521 + 1.522 +void 1.523 +MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, 1.524 + gfxContext* aRefContext) 1.525 +{ 1.526 + gfxFontGroup* fontGroup = aTextRun->GetFontGroup(); 1.527 + gfxFontStyle fontStyle = *fontGroup->GetStyle(); 1.528 + 1.529 + nsAutoString convertedString; 1.530 + nsAutoTArray<bool,50> charsToMergeArray; 1.531 + nsAutoTArray<bool,50> deletedCharsArray; 1.532 + nsAutoTArray<nsStyleContext*,50> styleArray; 1.533 + nsAutoTArray<uint8_t,50> canBreakBeforeArray; 1.534 + bool mergeNeeded = false; 1.535 + 1.536 + bool singleCharMI = 1.537 + aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SINGLE_CHAR_MI; 1.538 + 1.539 + uint32_t length = aTextRun->GetLength(); 1.540 + const char16_t* str = aTextRun->mString.BeginReading(); 1.541 + nsRefPtr<nsStyleContext>* styles = aTextRun->mStyles.Elements(); 1.542 + 1.543 + if (mSSTYScriptLevel && length) { 1.544 + bool found = false; 1.545 + // We respect ssty settings explicitly set by the user 1.546 + for (uint32_t i = 0; i < fontStyle.featureSettings.Length(); i++) { 1.547 + if (fontStyle.featureSettings[i].mTag == TRUETYPE_TAG('s','s','t','y')) { 1.548 + found = true; 1.549 + break; 1.550 + } 1.551 + } 1.552 + if (!found) { 1.553 + uint8_t sstyLevel = 0; 1.554 + float scriptScaling = pow(styles[0]->StyleFont()->mScriptSizeMultiplier, 1.555 + mSSTYScriptLevel); 1.556 + static_assert(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER < 1, 1.557 + "Shouldn't it make things smaller?"); 1.558 + /* 1.559 + An SSTY level of 2 is set if the scaling factor is less than or equal 1.560 + to halfway between that for a scriptlevel of 1 (0.71) and that of a 1.561 + scriptlevel of 2 (0.71^2), assuming the default script size multiplier. 1.562 + An SSTY level of 1 is set if the script scaling factor is less than 1.563 + or equal that for a scriptlevel of 1 assuming the default script size 1.564 + multiplier. 1.565 + 1.566 + User specified values of script size multiplier will change the scaling 1.567 + factor which mSSTYScriptLevel values correspond to. 1.568 + 1.569 + In the event that the script size multiplier actually makes things 1.570 + larger, no change is made. 1.571 + 1.572 + If the user doesn't want this to happen, all they need to do is set 1.573 + style="-moz-font-feature-settings: 'ssty' 0" 1.574 + */ 1.575 + if (scriptScaling <= (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER + 1.576 + (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER* 1.577 + NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER))/2) { 1.578 + // Currently only the first two ssty settings are used, so two is large 1.579 + // as we go 1.580 + sstyLevel = 2; 1.581 + } else if (scriptScaling <= NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER) { 1.582 + sstyLevel = 1; 1.583 + } 1.584 + if (sstyLevel) { 1.585 + gfxFontFeature settingSSTY; 1.586 + settingSSTY.mTag = TRUETYPE_TAG('s','s','t','y'); 1.587 + settingSSTY.mValue = sstyLevel; 1.588 + fontStyle.featureSettings.AppendElement(settingSSTY); 1.589 + } 1.590 + } 1.591 + } 1.592 + 1.593 + uint8_t mathVar; 1.594 + bool doMathvariantStyling = true; 1.595 + 1.596 + for (uint32_t i = 0; i < length; ++i) { 1.597 + int extraChars = 0; 1.598 + nsStyleContext* styleContext = styles[i]; 1.599 + mathVar = styleContext->StyleFont()->mMathVariant; 1.600 + 1.601 + if (singleCharMI && mathVar == NS_MATHML_MATHVARIANT_NONE) { 1.602 + mathVar = NS_MATHML_MATHVARIANT_ITALIC; 1.603 + } 1.604 + 1.605 + uint32_t ch = str[i]; 1.606 + if (NS_IS_HIGH_SURROGATE(ch) && i < length - 1 && 1.607 + NS_IS_LOW_SURROGATE(str[i + 1])) { 1.608 + ch = SURROGATE_TO_UCS4(ch, str[i + 1]); 1.609 + } 1.610 + uint32_t ch2 = MathVariant(ch, mathVar); 1.611 + 1.612 + if (mathVar == NS_MATHML_MATHVARIANT_BOLD || 1.613 + mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC || 1.614 + mathVar == NS_MATHML_MATHVARIANT_ITALIC) { 1.615 + if (ch == ch2 && ch != 0x20 && ch != 0xA0) { 1.616 + // Don't apply the CSS style if a character cannot be 1.617 + // transformed. There is an exception for whitespace as it is both 1.618 + // common and innocuous. 1.619 + doMathvariantStyling = false; 1.620 + } 1.621 + if (ch2 != ch) { 1.622 + // Bug 930504. Some platforms do not have fonts for Mathematical 1.623 + // Alphanumeric Symbols. Hence we check whether the transformed 1.624 + // character is actually available. 1.625 + uint8_t matchType; 1.626 + nsRefPtr<gfxFont> mathFont = fontGroup-> 1.627 + FindFontForChar(ch2, 0, HB_SCRIPT_COMMON, nullptr, &matchType); 1.628 + if (mathFont) { 1.629 + // Don't apply the CSS style if there is a math font for at least one 1.630 + // of the transformed character in this text run. 1.631 + doMathvariantStyling = false; 1.632 + } else { 1.633 + // We fallback to the original character. 1.634 + ch2 = ch; 1.635 + } 1.636 + } 1.637 + } 1.638 + 1.639 + deletedCharsArray.AppendElement(false); 1.640 + charsToMergeArray.AppendElement(false); 1.641 + styleArray.AppendElement(styleContext); 1.642 + canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i)); 1.643 + 1.644 + if (IS_IN_BMP(ch2)) { 1.645 + convertedString.Append(ch2); 1.646 + } else { 1.647 + convertedString.Append(H_SURROGATE(ch2)); 1.648 + convertedString.Append(L_SURROGATE(ch2)); 1.649 + ++extraChars; 1.650 + if (!IS_IN_BMP(ch)) { 1.651 + deletedCharsArray.AppendElement(true); // not exactly deleted, but 1.652 + // the trailing surrogate is skipped 1.653 + ++i; 1.654 + } 1.655 + } 1.656 + 1.657 + while (extraChars-- > 0) { 1.658 + mergeNeeded = true; 1.659 + charsToMergeArray.AppendElement(true); 1.660 + styleArray.AppendElement(styleContext); 1.661 + canBreakBeforeArray.AppendElement(false); 1.662 + } 1.663 + } 1.664 + 1.665 + uint32_t flags; 1.666 + gfxTextRunFactory::Parameters innerParams = 1.667 + GetParametersForInner(aTextRun, &flags, aRefContext); 1.668 + 1.669 + nsAutoPtr<nsTransformedTextRun> transformedChild; 1.670 + nsAutoPtr<gfxTextRun> cachedChild; 1.671 + gfxTextRun* child; 1.672 + 1.673 + if (mathVar == NS_MATHML_MATHVARIANT_BOLD && doMathvariantStyling) { 1.674 + fontStyle.style = NS_FONT_STYLE_NORMAL; 1.675 + fontStyle.weight = NS_FONT_WEIGHT_BOLD; 1.676 + } else if (mathVar == NS_MATHML_MATHVARIANT_ITALIC && doMathvariantStyling) { 1.677 + fontStyle.style = NS_FONT_STYLE_ITALIC; 1.678 + fontStyle.weight = NS_FONT_WEIGHT_NORMAL; 1.679 + } else if (mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC && 1.680 + doMathvariantStyling) { 1.681 + fontStyle.style = NS_FONT_STYLE_ITALIC; 1.682 + fontStyle.weight = NS_FONT_WEIGHT_BOLD; 1.683 + } else if (mathVar != NS_MATHML_MATHVARIANT_NONE) { 1.684 + // Mathvariant overrides fontstyle and fontweight 1.685 + // Need to check to see if mathvariant is actually applied as this function 1.686 + // is used for other purposes. 1.687 + fontStyle.style = NS_FONT_STYLE_NORMAL; 1.688 + fontStyle.weight = NS_FONT_WEIGHT_NORMAL; 1.689 + } 1.690 + nsRefPtr<gfxFontGroup> newFontGroup = fontGroup->Copy(&fontStyle); 1.691 + 1.692 + if (!newFontGroup) 1.693 + return; 1.694 + 1.695 + if (mInnerTransformingTextRunFactory) { 1.696 + transformedChild = mInnerTransformingTextRunFactory->MakeTextRun( 1.697 + convertedString.BeginReading(), convertedString.Length(), 1.698 + &innerParams, newFontGroup, flags, styleArray.Elements(), false); 1.699 + child = transformedChild.get(); 1.700 + } else { 1.701 + cachedChild = newFontGroup->MakeTextRun( 1.702 + convertedString.BeginReading(), convertedString.Length(), 1.703 + &innerParams, flags); 1.704 + child = cachedChild.get(); 1.705 + } 1.706 + if (!child) 1.707 + return; 1.708 + // Copy potential linebreaks into child so they're preserved 1.709 + // (and also child will be shaped appropriately) 1.710 + NS_ASSERTION(convertedString.Length() == canBreakBeforeArray.Length(), 1.711 + "Dropped characters or break-before values somewhere!"); 1.712 + child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(), 1.713 + canBreakBeforeArray.Elements(), aRefContext); 1.714 + if (transformedChild) { 1.715 + transformedChild->FinishSettingProperties(aRefContext); 1.716 + } 1.717 + 1.718 + if (mergeNeeded) { 1.719 + // Now merge multiple characters into one multi-glyph character as required 1.720 + NS_ASSERTION(charsToMergeArray.Length() == child->GetLength(), 1.721 + "source length mismatch"); 1.722 + NS_ASSERTION(deletedCharsArray.Length() == aTextRun->GetLength(), 1.723 + "destination length mismatch"); 1.724 + MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements(), 1.725 + deletedCharsArray.Elements()); 1.726 + } else { 1.727 + // No merging to do, so just copy; this produces a more optimized textrun. 1.728 + // We can't steal the data because the child may be cached and stealing 1.729 + // the data would break the cache. 1.730 + aTextRun->ResetGlyphRuns(); 1.731 + aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), 0); 1.732 + } 1.733 +}