Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
michael@0 | 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "MathMLTextRunFactory.h" |
michael@0 | 7 | |
michael@0 | 8 | #include "mozilla/ArrayUtils.h" |
michael@0 | 9 | |
michael@0 | 10 | #include "nsStyleConsts.h" |
michael@0 | 11 | #include "nsStyleContext.h" |
michael@0 | 12 | #include "nsTextFrameUtils.h" |
michael@0 | 13 | |
michael@0 | 14 | using namespace mozilla; |
michael@0 | 15 | |
michael@0 | 16 | /* |
michael@0 | 17 | Entries for the mathvariant lookup tables. mKey represents the Unicode |
michael@0 | 18 | character to be transformed and is used for searching the tables. |
michael@0 | 19 | mReplacement represents the mapped mathvariant Unicode character. |
michael@0 | 20 | */ |
michael@0 | 21 | typedef struct |
michael@0 | 22 | { |
michael@0 | 23 | uint32_t mKey; |
michael@0 | 24 | uint32_t mReplacement; |
michael@0 | 25 | } MathVarMapping; |
michael@0 | 26 | |
michael@0 | 27 | /* |
michael@0 | 28 | Lookup tables for use with mathvariant mappings to transform a unicode |
michael@0 | 29 | character point to another unicode character that indicates the proper output. |
michael@0 | 30 | mKey represents one of two concepts. |
michael@0 | 31 | 1. In the Latin table it represents a hole in the mathematical alphanumeric |
michael@0 | 32 | block, where the character that should occupy that position is located |
michael@0 | 33 | elsewhere. |
michael@0 | 34 | 2. It represents an Arabic letter. |
michael@0 | 35 | |
michael@0 | 36 | As a replacement, 0 is reserved to indicate no mapping was found. |
michael@0 | 37 | */ |
michael@0 | 38 | static const MathVarMapping gArabicInitialMapTable[] = { |
michael@0 | 39 | { 0x628, 0x1EE21 }, |
michael@0 | 40 | { 0x62A, 0x1EE35 }, |
michael@0 | 41 | { 0x62B, 0x1EE36 }, |
michael@0 | 42 | { 0x62C, 0x1EE22 }, |
michael@0 | 43 | { 0x62D, 0x1EE27 }, |
michael@0 | 44 | { 0x62E, 0x1EE37 }, |
michael@0 | 45 | { 0x633, 0x1EE2E }, |
michael@0 | 46 | { 0x634, 0x1EE34 }, |
michael@0 | 47 | { 0x635, 0x1EE31 }, |
michael@0 | 48 | { 0x636, 0x1EE39 }, |
michael@0 | 49 | { 0x639, 0x1EE2F }, |
michael@0 | 50 | { 0x63A, 0x1EE3B }, |
michael@0 | 51 | { 0x641, 0x1EE30 }, |
michael@0 | 52 | { 0x642, 0x1EE32 }, |
michael@0 | 53 | { 0x643, 0x1EE2A }, |
michael@0 | 54 | { 0x644, 0x1EE2B }, |
michael@0 | 55 | { 0x645, 0x1EE2C }, |
michael@0 | 56 | { 0x646, 0x1EE2D }, |
michael@0 | 57 | { 0x647, 0x1EE24 }, |
michael@0 | 58 | { 0x64A, 0x1EE29 } |
michael@0 | 59 | }; |
michael@0 | 60 | |
michael@0 | 61 | static const MathVarMapping gArabicTailedMapTable[] = { |
michael@0 | 62 | { 0x62C, 0x1EE42 }, |
michael@0 | 63 | { 0x62D, 0x1EE47 }, |
michael@0 | 64 | { 0x62E, 0x1EE57 }, |
michael@0 | 65 | { 0x633, 0x1EE4E }, |
michael@0 | 66 | { 0x634, 0x1EE54 }, |
michael@0 | 67 | { 0x635, 0x1EE51 }, |
michael@0 | 68 | { 0x636, 0x1EE59 }, |
michael@0 | 69 | { 0x639, 0x1EE4F }, |
michael@0 | 70 | { 0x63A, 0x1EE5B }, |
michael@0 | 71 | { 0x642, 0x1EE52 }, |
michael@0 | 72 | { 0x644, 0x1EE4B }, |
michael@0 | 73 | { 0x646, 0x1EE4D }, |
michael@0 | 74 | { 0x64A, 0x1EE49 }, |
michael@0 | 75 | { 0x66F, 0x1EE5F }, |
michael@0 | 76 | { 0x6BA, 0x1EE5D } |
michael@0 | 77 | }; |
michael@0 | 78 | |
michael@0 | 79 | static const MathVarMapping gArabicStretchedMapTable[] = { |
michael@0 | 80 | { 0x628, 0x1EE61 }, |
michael@0 | 81 | { 0x62A, 0x1EE75 }, |
michael@0 | 82 | { 0x62B, 0x1EE76 }, |
michael@0 | 83 | { 0x62C, 0x1EE62 }, |
michael@0 | 84 | { 0x62D, 0x1EE67 }, |
michael@0 | 85 | { 0x62E, 0x1EE77 }, |
michael@0 | 86 | { 0x633, 0x1EE6E }, |
michael@0 | 87 | { 0x634, 0x1EE74 }, |
michael@0 | 88 | { 0x635, 0x1EE71 }, |
michael@0 | 89 | { 0x636, 0x1EE79 }, |
michael@0 | 90 | { 0x637, 0x1EE68 }, |
michael@0 | 91 | { 0x638, 0x1EE7A }, |
michael@0 | 92 | { 0x639, 0x1EE6F }, |
michael@0 | 93 | { 0x63A, 0x1EE7B }, |
michael@0 | 94 | { 0x641, 0x1EE70 }, |
michael@0 | 95 | { 0x642, 0x1EE72 }, |
michael@0 | 96 | { 0x643, 0x1EE6A }, |
michael@0 | 97 | { 0x645, 0x1EE6C }, |
michael@0 | 98 | { 0x646, 0x1EE6D }, |
michael@0 | 99 | { 0x647, 0x1EE64 }, |
michael@0 | 100 | { 0x64A, 0x1EE69 }, |
michael@0 | 101 | { 0x66E, 0x1EE7C }, |
michael@0 | 102 | { 0x6A1, 0x1EE7E } |
michael@0 | 103 | }; |
michael@0 | 104 | |
michael@0 | 105 | static const MathVarMapping gArabicLoopedMapTable[] = { |
michael@0 | 106 | { 0x627, 0x1EE80 }, |
michael@0 | 107 | { 0x628, 0x1EE81 }, |
michael@0 | 108 | { 0x62A, 0x1EE95 }, |
michael@0 | 109 | { 0x62B, 0x1EE96 }, |
michael@0 | 110 | { 0x62C, 0x1EE82 }, |
michael@0 | 111 | { 0x62D, 0x1EE87 }, |
michael@0 | 112 | { 0x62E, 0x1EE97 }, |
michael@0 | 113 | { 0x62F, 0x1EE83 }, |
michael@0 | 114 | { 0x630, 0x1EE98 }, |
michael@0 | 115 | { 0x631, 0x1EE93 }, |
michael@0 | 116 | { 0x632, 0x1EE86 }, |
michael@0 | 117 | { 0x633, 0x1EE8E }, |
michael@0 | 118 | { 0x634, 0x1EE94 }, |
michael@0 | 119 | { 0x635, 0x1EE91 }, |
michael@0 | 120 | { 0x636, 0x1EE99 }, |
michael@0 | 121 | { 0x637, 0x1EE88 }, |
michael@0 | 122 | { 0x638, 0x1EE9A }, |
michael@0 | 123 | { 0x639, 0x1EE8F }, |
michael@0 | 124 | { 0x63A, 0x1EE9B }, |
michael@0 | 125 | { 0x641, 0x1EE90 }, |
michael@0 | 126 | { 0x642, 0x1EE92 }, |
michael@0 | 127 | { 0x644, 0x1EE8B }, |
michael@0 | 128 | { 0x645, 0x1EE8C }, |
michael@0 | 129 | { 0x646, 0x1EE8D }, |
michael@0 | 130 | { 0x647, 0x1EE84 }, |
michael@0 | 131 | { 0x648, 0x1EE85 }, |
michael@0 | 132 | { 0x64A, 0x1EE89 } |
michael@0 | 133 | }; |
michael@0 | 134 | |
michael@0 | 135 | static const MathVarMapping gArabicDoubleMapTable[] = { |
michael@0 | 136 | { 0x628, 0x1EEA1 }, |
michael@0 | 137 | { 0x62A, 0x1EEB5 }, |
michael@0 | 138 | { 0x62B, 0x1EEB6 }, |
michael@0 | 139 | { 0x62C, 0x1EEA2 }, |
michael@0 | 140 | { 0x62D, 0x1EEA7 }, |
michael@0 | 141 | { 0x62E, 0x1EEB7 }, |
michael@0 | 142 | { 0x62F, 0x1EEA3 }, |
michael@0 | 143 | { 0x630, 0x1EEB8 }, |
michael@0 | 144 | { 0x631, 0x1EEB3 }, |
michael@0 | 145 | { 0x632, 0x1EEA6 }, |
michael@0 | 146 | { 0x633, 0x1EEAE }, |
michael@0 | 147 | { 0x634, 0x1EEB4 }, |
michael@0 | 148 | { 0x635, 0x1EEB1 }, |
michael@0 | 149 | { 0x636, 0x1EEB9 }, |
michael@0 | 150 | { 0x637, 0x1EEA8 }, |
michael@0 | 151 | { 0x638, 0x1EEBA }, |
michael@0 | 152 | { 0x639, 0x1EEAF }, |
michael@0 | 153 | { 0x63A, 0x1EEBB }, |
michael@0 | 154 | { 0x641, 0x1EEB0 }, |
michael@0 | 155 | { 0x642, 0x1EEB2 }, |
michael@0 | 156 | { 0x644, 0x1EEAB }, |
michael@0 | 157 | { 0x645, 0x1EEAC }, |
michael@0 | 158 | { 0x646, 0x1EEAD }, |
michael@0 | 159 | { 0x648, 0x1EEA5 }, |
michael@0 | 160 | { 0x64A, 0x1EEA9 } |
michael@0 | 161 | }; |
michael@0 | 162 | |
michael@0 | 163 | static const MathVarMapping gLatinExceptionMapTable[] = { |
michael@0 | 164 | { 0x1D455, 0x210E }, |
michael@0 | 165 | { 0x1D49D, 0x212C }, |
michael@0 | 166 | { 0x1D4A0, 0x2130 }, |
michael@0 | 167 | { 0x1D4A1, 0x2131 }, |
michael@0 | 168 | { 0x1D4A3, 0x210B }, |
michael@0 | 169 | { 0x1D4A4, 0x2110 }, |
michael@0 | 170 | { 0x1D4A7, 0x2112 }, |
michael@0 | 171 | { 0x1D4A8, 0x2133 }, |
michael@0 | 172 | { 0x1D4AD, 0x211B }, |
michael@0 | 173 | { 0x1D4BA, 0x212F }, |
michael@0 | 174 | { 0x1D4BC, 0x210A }, |
michael@0 | 175 | { 0x1D4C4, 0x2134 }, |
michael@0 | 176 | { 0x1D506, 0x212D }, |
michael@0 | 177 | { 0x1D50B, 0x210C }, |
michael@0 | 178 | { 0x1D50C, 0x2111 }, |
michael@0 | 179 | { 0x1D515, 0x211C }, |
michael@0 | 180 | { 0x1D51D, 0x2128 }, |
michael@0 | 181 | { 0x1D53A, 0x2102 }, |
michael@0 | 182 | { 0x1D53F, 0x210D }, |
michael@0 | 183 | { 0x1D545, 0x2115 }, |
michael@0 | 184 | { 0x1D547, 0x2119 }, |
michael@0 | 185 | { 0x1D548, 0x211A }, |
michael@0 | 186 | { 0x1D549, 0x211D }, |
michael@0 | 187 | { 0x1D551, 0x2124 } |
michael@0 | 188 | }; |
michael@0 | 189 | |
michael@0 | 190 | // Finds a MathVarMapping struct with the specified key (aKey) within aTable. |
michael@0 | 191 | // aTable must be an array, whose length is specified by aNumElements |
michael@0 | 192 | static uint32_t |
michael@0 | 193 | MathvarMappingSearch(uint32_t aKey, const MathVarMapping* aTable, uint32_t aNumElements) |
michael@0 | 194 | { |
michael@0 | 195 | uint32_t low = 0; |
michael@0 | 196 | uint32_t high = aNumElements; |
michael@0 | 197 | while (high > low) { |
michael@0 | 198 | uint32_t midPoint = (low+high) >> 1; |
michael@0 | 199 | if (aKey == aTable[midPoint].mKey) { |
michael@0 | 200 | return aTable[midPoint].mReplacement; |
michael@0 | 201 | } |
michael@0 | 202 | if (aKey > aTable[midPoint].mKey) { |
michael@0 | 203 | low = midPoint + 1; |
michael@0 | 204 | } else { |
michael@0 | 205 | high = midPoint; |
michael@0 | 206 | } |
michael@0 | 207 | } |
michael@0 | 208 | return 0; |
michael@0 | 209 | } |
michael@0 | 210 | |
michael@0 | 211 | #define GREEK_UPPER_THETA 0x03F4 |
michael@0 | 212 | #define HOLE_GREEK_UPPER_THETA 0x03A2 |
michael@0 | 213 | #define NABLA 0x2207 |
michael@0 | 214 | #define PARTIAL_DIFFERENTIAL 0x2202 |
michael@0 | 215 | #define GREEK_UPPER_ALPHA 0x0391 |
michael@0 | 216 | #define GREEK_UPPER_OMEGA 0x03A9 |
michael@0 | 217 | #define GREEK_LOWER_ALPHA 0x03B1 |
michael@0 | 218 | #define GREEK_LOWER_OMEGA 0x03C9 |
michael@0 | 219 | #define GREEK_LUNATE_EPSILON_SYMBOL 0x03F5 |
michael@0 | 220 | #define GREEK_THETA_SYMBOL 0x03D1 |
michael@0 | 221 | #define GREEK_KAPPA_SYMBOL 0x03F0 |
michael@0 | 222 | #define GREEK_PHI_SYMBOL 0x03D5 |
michael@0 | 223 | #define GREEK_RHO_SYMBOL 0x03F1 |
michael@0 | 224 | #define GREEK_PI_SYMBOL 0x03D6 |
michael@0 | 225 | #define GREEK_LETTER_DIGAMMA 0x03DC |
michael@0 | 226 | #define GREEK_SMALL_LETTER_DIGAMMA 0x03DD |
michael@0 | 227 | #define MATH_BOLD_CAPITAL_DIGAMMA 0x1D7CA |
michael@0 | 228 | #define MATH_BOLD_SMALL_DIGAMMA 0x1D7CB |
michael@0 | 229 | |
michael@0 | 230 | #define LATIN_SMALL_LETTER_DOTLESS_I 0x0131 |
michael@0 | 231 | #define LATIN_SMALL_LETTER_DOTLESS_J 0x0237 |
michael@0 | 232 | |
michael@0 | 233 | #define MATH_ITALIC_SMALL_DOTLESS_I 0x1D6A4 |
michael@0 | 234 | #define MATH_ITALIC_SMALL_DOTLESS_J 0x1D6A5 |
michael@0 | 235 | |
michael@0 | 236 | #define MATH_BOLD_UPPER_A 0x1D400 |
michael@0 | 237 | #define MATH_ITALIC_UPPER_A 0x1D434 |
michael@0 | 238 | #define MATH_BOLD_SMALL_A 0x1D41A |
michael@0 | 239 | #define MATH_BOLD_UPPER_ALPHA 0x1D6A8 |
michael@0 | 240 | #define MATH_BOLD_SMALL_ALPHA 0x1D6C2 |
michael@0 | 241 | #define MATH_ITALIC_UPPER_ALPHA 0x1D6E2 |
michael@0 | 242 | #define MATH_BOLD_DIGIT_ZERO 0x1D7CE |
michael@0 | 243 | #define MATH_DOUBLE_STRUCK_ZERO 0x1D7D8 |
michael@0 | 244 | |
michael@0 | 245 | #define MATH_BOLD_UPPER_THETA 0x1D6B9 |
michael@0 | 246 | #define MATH_BOLD_NABLA 0x1D6C1 |
michael@0 | 247 | #define MATH_BOLD_PARTIAL_DIFFERENTIAL 0x1D6DB |
michael@0 | 248 | #define MATH_BOLD_EPSILON_SYMBOL 0x1D6DC |
michael@0 | 249 | #define MATH_BOLD_THETA_SYMBOL 0x1D6DD |
michael@0 | 250 | #define MATH_BOLD_KAPPA_SYMBOL 0x1D6DE |
michael@0 | 251 | #define MATH_BOLD_PHI_SYMBOL 0x1D6DF |
michael@0 | 252 | #define MATH_BOLD_RHO_SYMBOL 0x1D6E0 |
michael@0 | 253 | #define MATH_BOLD_PI_SYMBOL 0x1D6E1 |
michael@0 | 254 | |
michael@0 | 255 | /* |
michael@0 | 256 | Performs the character mapping needed to implement MathML's mathvariant |
michael@0 | 257 | attribute. It takes a unicode character and maps it to its appropriate |
michael@0 | 258 | mathvariant counterpart specified by aMathVar. The mapped character is |
michael@0 | 259 | typically located within Unicode's mathematical blocks (0x1D***, 0x1EE**) but |
michael@0 | 260 | there are exceptions which this function accounts for. |
michael@0 | 261 | Characters without a valid mapping or valid aMathvar value are returned |
michael@0 | 262 | unaltered. Characters already in the mathematical blocks (or are one of the |
michael@0 | 263 | exceptions) are never transformed. |
michael@0 | 264 | Acceptable values for aMathVar are specified in layout/style/nsStyleConsts.h. |
michael@0 | 265 | The transformable characters can be found at: |
michael@0 | 266 | http://lists.w3.org/Archives/Public/www-math/2013Sep/0012.html and |
michael@0 | 267 | https://en.wikipedia.org/wiki/Mathematical_Alphanumeric_Symbols |
michael@0 | 268 | */ |
michael@0 | 269 | static uint32_t |
michael@0 | 270 | MathVariant(uint32_t aCh, uint8_t aMathVar) |
michael@0 | 271 | { |
michael@0 | 272 | uint32_t baseChar; |
michael@0 | 273 | enum CharacterType { |
michael@0 | 274 | kIsLatin, |
michael@0 | 275 | kIsGreekish, |
michael@0 | 276 | kIsNumber, |
michael@0 | 277 | kIsArabic, |
michael@0 | 278 | }; |
michael@0 | 279 | CharacterType varType; |
michael@0 | 280 | |
michael@0 | 281 | int8_t multiplier; |
michael@0 | 282 | |
michael@0 | 283 | if (aMathVar <= NS_MATHML_MATHVARIANT_NORMAL) { |
michael@0 | 284 | // nothing to do here |
michael@0 | 285 | return aCh; |
michael@0 | 286 | } |
michael@0 | 287 | if (aMathVar > NS_MATHML_MATHVARIANT_STRETCHED) { |
michael@0 | 288 | NS_ASSERTION(false, "Illegal mathvariant value"); |
michael@0 | 289 | return aCh; |
michael@0 | 290 | } |
michael@0 | 291 | |
michael@0 | 292 | // Exceptional characters with at most one possible transformation |
michael@0 | 293 | if (aCh == HOLE_GREEK_UPPER_THETA) { |
michael@0 | 294 | // Nothing at this code point is transformed |
michael@0 | 295 | return aCh; |
michael@0 | 296 | } |
michael@0 | 297 | if (aCh == GREEK_LETTER_DIGAMMA) { |
michael@0 | 298 | if (aMathVar == NS_MATHML_MATHVARIANT_BOLD) { |
michael@0 | 299 | return MATH_BOLD_CAPITAL_DIGAMMA; |
michael@0 | 300 | } |
michael@0 | 301 | return aCh; |
michael@0 | 302 | } |
michael@0 | 303 | if (aCh == GREEK_SMALL_LETTER_DIGAMMA) { |
michael@0 | 304 | if (aMathVar == NS_MATHML_MATHVARIANT_BOLD) { |
michael@0 | 305 | return MATH_BOLD_SMALL_DIGAMMA; |
michael@0 | 306 | } |
michael@0 | 307 | return aCh; |
michael@0 | 308 | } |
michael@0 | 309 | if (aCh == LATIN_SMALL_LETTER_DOTLESS_I) { |
michael@0 | 310 | if (aMathVar == NS_MATHML_MATHVARIANT_ITALIC) { |
michael@0 | 311 | return MATH_ITALIC_SMALL_DOTLESS_I; |
michael@0 | 312 | } |
michael@0 | 313 | return aCh; |
michael@0 | 314 | } |
michael@0 | 315 | if (aCh == LATIN_SMALL_LETTER_DOTLESS_J) { |
michael@0 | 316 | if (aMathVar == NS_MATHML_MATHVARIANT_ITALIC) { |
michael@0 | 317 | return MATH_ITALIC_SMALL_DOTLESS_J; |
michael@0 | 318 | } |
michael@0 | 319 | return aCh; |
michael@0 | 320 | } |
michael@0 | 321 | |
michael@0 | 322 | // The Unicode mathematical blocks are divided into four segments: Latin, |
michael@0 | 323 | // Greek, numbers and Arabic. In the case of the first three |
michael@0 | 324 | // baseChar represents the relative order in which the characters are |
michael@0 | 325 | // encoded in the Unicode mathematical block, normalised to the first |
michael@0 | 326 | // character of that sequence. |
michael@0 | 327 | // |
michael@0 | 328 | if ('A' <= aCh && aCh <= 'Z') { |
michael@0 | 329 | baseChar = aCh - 'A'; |
michael@0 | 330 | varType = kIsLatin; |
michael@0 | 331 | } else if ('a' <= aCh && aCh <= 'z') { |
michael@0 | 332 | // Lowercase characters are placed immediately after the uppercase |
michael@0 | 333 | // characters in the Unicode mathematical block. The constant subtraction |
michael@0 | 334 | // represents the number of characters between the start of the sequence |
michael@0 | 335 | // (capital A) and the first lowercase letter. |
michael@0 | 336 | baseChar = MATH_BOLD_SMALL_A-MATH_BOLD_UPPER_A + aCh - 'a'; |
michael@0 | 337 | varType = kIsLatin; |
michael@0 | 338 | } else if ('0' <= aCh && aCh <= '9') { |
michael@0 | 339 | baseChar = aCh - '0'; |
michael@0 | 340 | varType = kIsNumber; |
michael@0 | 341 | } else if (GREEK_UPPER_ALPHA <= aCh && aCh <= GREEK_UPPER_OMEGA) { |
michael@0 | 342 | baseChar = aCh-GREEK_UPPER_ALPHA; |
michael@0 | 343 | varType = kIsGreekish; |
michael@0 | 344 | } else if (GREEK_LOWER_ALPHA <= aCh && aCh <= GREEK_LOWER_OMEGA) { |
michael@0 | 345 | // Lowercase Greek comes after uppercase Greek. |
michael@0 | 346 | // Note in this instance the presence of an additional character (Nabla) |
michael@0 | 347 | // between the end of the uppercase Greek characters and the lowercase |
michael@0 | 348 | // ones. |
michael@0 | 349 | baseChar = MATH_BOLD_SMALL_ALPHA - MATH_BOLD_UPPER_ALPHA |
michael@0 | 350 | + aCh-GREEK_LOWER_ALPHA; |
michael@0 | 351 | varType = kIsGreekish; |
michael@0 | 352 | } else if (0x0600 <= aCh && aCh <= 0x06FF) { |
michael@0 | 353 | // Arabic characters are defined within this range |
michael@0 | 354 | varType = kIsArabic; |
michael@0 | 355 | } else { |
michael@0 | 356 | switch (aCh) { |
michael@0 | 357 | case GREEK_UPPER_THETA: |
michael@0 | 358 | baseChar = MATH_BOLD_UPPER_THETA-MATH_BOLD_UPPER_ALPHA; |
michael@0 | 359 | break; |
michael@0 | 360 | case NABLA: |
michael@0 | 361 | baseChar = MATH_BOLD_NABLA-MATH_BOLD_UPPER_ALPHA; |
michael@0 | 362 | break; |
michael@0 | 363 | case PARTIAL_DIFFERENTIAL: |
michael@0 | 364 | baseChar = MATH_BOLD_PARTIAL_DIFFERENTIAL - MATH_BOLD_UPPER_ALPHA; |
michael@0 | 365 | break; |
michael@0 | 366 | case GREEK_LUNATE_EPSILON_SYMBOL: |
michael@0 | 367 | baseChar = MATH_BOLD_EPSILON_SYMBOL - MATH_BOLD_UPPER_ALPHA; |
michael@0 | 368 | break; |
michael@0 | 369 | case GREEK_THETA_SYMBOL: |
michael@0 | 370 | baseChar = MATH_BOLD_THETA_SYMBOL - MATH_BOLD_UPPER_ALPHA; |
michael@0 | 371 | break; |
michael@0 | 372 | case GREEK_KAPPA_SYMBOL: |
michael@0 | 373 | baseChar = MATH_BOLD_KAPPA_SYMBOL - MATH_BOLD_UPPER_ALPHA; |
michael@0 | 374 | break; |
michael@0 | 375 | case GREEK_PHI_SYMBOL: |
michael@0 | 376 | baseChar = MATH_BOLD_PHI_SYMBOL - MATH_BOLD_UPPER_ALPHA; |
michael@0 | 377 | break; |
michael@0 | 378 | case GREEK_RHO_SYMBOL: |
michael@0 | 379 | baseChar = MATH_BOLD_RHO_SYMBOL - MATH_BOLD_UPPER_ALPHA; |
michael@0 | 380 | break; |
michael@0 | 381 | case GREEK_PI_SYMBOL: |
michael@0 | 382 | baseChar = MATH_BOLD_PI_SYMBOL - MATH_BOLD_UPPER_ALPHA; |
michael@0 | 383 | break; |
michael@0 | 384 | default: |
michael@0 | 385 | return aCh; |
michael@0 | 386 | } |
michael@0 | 387 | |
michael@0 | 388 | varType = kIsGreekish; |
michael@0 | 389 | } |
michael@0 | 390 | |
michael@0 | 391 | if (varType == kIsNumber) { |
michael@0 | 392 | switch (aMathVar) { |
michael@0 | 393 | // Each possible number mathvariant is encoded in a single, contiguous |
michael@0 | 394 | // block. For example the beginning of the double struck number range |
michael@0 | 395 | // follows immediately after the end of the bold number range. |
michael@0 | 396 | // multiplier represents the order of the sequences relative to the first |
michael@0 | 397 | // one. |
michael@0 | 398 | case NS_MATHML_MATHVARIANT_BOLD: |
michael@0 | 399 | multiplier = 0; |
michael@0 | 400 | break; |
michael@0 | 401 | case NS_MATHML_MATHVARIANT_DOUBLE_STRUCK: |
michael@0 | 402 | multiplier = 1; |
michael@0 | 403 | break; |
michael@0 | 404 | case NS_MATHML_MATHVARIANT_SANS_SERIF: |
michael@0 | 405 | multiplier = 2; |
michael@0 | 406 | break; |
michael@0 | 407 | case NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF: |
michael@0 | 408 | multiplier = 3; |
michael@0 | 409 | break; |
michael@0 | 410 | case NS_MATHML_MATHVARIANT_MONOSPACE: |
michael@0 | 411 | multiplier = 4; |
michael@0 | 412 | break; |
michael@0 | 413 | default: |
michael@0 | 414 | // This mathvariant isn't defined for numbers or is otherwise normal |
michael@0 | 415 | return aCh; |
michael@0 | 416 | } |
michael@0 | 417 | // As the ranges are contiguous, to find the desired mathvariant range it |
michael@0 | 418 | // is sufficient to multiply the position within the sequence order |
michael@0 | 419 | // (multiplier) with the period of the sequence (which is constant for all |
michael@0 | 420 | // number sequences) and to add the character point of the first character |
michael@0 | 421 | // within the number mathvariant range. |
michael@0 | 422 | // To this the baseChar calculated earlier is added to obtain the final |
michael@0 | 423 | // code point. |
michael@0 | 424 | return baseChar+multiplier*(MATH_DOUBLE_STRUCK_ZERO-MATH_BOLD_DIGIT_ZERO) |
michael@0 | 425 | +MATH_BOLD_DIGIT_ZERO; |
michael@0 | 426 | } else if (varType == kIsGreekish) { |
michael@0 | 427 | switch (aMathVar) { |
michael@0 | 428 | case NS_MATHML_MATHVARIANT_BOLD: |
michael@0 | 429 | multiplier = 0; |
michael@0 | 430 | break; |
michael@0 | 431 | case NS_MATHML_MATHVARIANT_ITALIC: |
michael@0 | 432 | multiplier = 1; |
michael@0 | 433 | break; |
michael@0 | 434 | case NS_MATHML_MATHVARIANT_BOLD_ITALIC: |
michael@0 | 435 | multiplier = 2; |
michael@0 | 436 | break; |
michael@0 | 437 | case NS_MATHML_MATHVARIANT_BOLD_SANS_SERIF: |
michael@0 | 438 | multiplier = 3; |
michael@0 | 439 | break; |
michael@0 | 440 | case NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC: |
michael@0 | 441 | multiplier = 4; |
michael@0 | 442 | break; |
michael@0 | 443 | default: |
michael@0 | 444 | // This mathvariant isn't defined for Greek or is otherwise normal |
michael@0 | 445 | return aCh; |
michael@0 | 446 | } |
michael@0 | 447 | // See the kIsNumber case for an explanation of the following calculation |
michael@0 | 448 | return baseChar + MATH_BOLD_UPPER_ALPHA + |
michael@0 | 449 | multiplier*(MATH_ITALIC_UPPER_ALPHA - MATH_BOLD_UPPER_ALPHA); |
michael@0 | 450 | } |
michael@0 | 451 | |
michael@0 | 452 | uint32_t tempChar; |
michael@0 | 453 | uint32_t newChar; |
michael@0 | 454 | if (varType == kIsArabic) { |
michael@0 | 455 | const MathVarMapping* mapTable; |
michael@0 | 456 | uint32_t tableLength; |
michael@0 | 457 | switch (aMathVar) { |
michael@0 | 458 | /* The Arabic mathematical block is not continuous, nor does it have a |
michael@0 | 459 | * monotonic mapping to the unencoded characters, requiring the use of a |
michael@0 | 460 | * lookup table. |
michael@0 | 461 | */ |
michael@0 | 462 | case NS_MATHML_MATHVARIANT_INITIAL: |
michael@0 | 463 | mapTable = gArabicInitialMapTable; |
michael@0 | 464 | tableLength = ArrayLength(gArabicInitialMapTable); |
michael@0 | 465 | break; |
michael@0 | 466 | case NS_MATHML_MATHVARIANT_TAILED: |
michael@0 | 467 | mapTable = gArabicTailedMapTable; |
michael@0 | 468 | tableLength = ArrayLength(gArabicTailedMapTable); |
michael@0 | 469 | break; |
michael@0 | 470 | case NS_MATHML_MATHVARIANT_STRETCHED: |
michael@0 | 471 | mapTable = gArabicStretchedMapTable; |
michael@0 | 472 | tableLength = ArrayLength(gArabicStretchedMapTable); |
michael@0 | 473 | break; |
michael@0 | 474 | case NS_MATHML_MATHVARIANT_LOOPED: |
michael@0 | 475 | mapTable = gArabicLoopedMapTable; |
michael@0 | 476 | tableLength = ArrayLength(gArabicLoopedMapTable); |
michael@0 | 477 | break; |
michael@0 | 478 | case NS_MATHML_MATHVARIANT_DOUBLE_STRUCK: |
michael@0 | 479 | mapTable = gArabicDoubleMapTable; |
michael@0 | 480 | tableLength = ArrayLength(gArabicDoubleMapTable); |
michael@0 | 481 | break; |
michael@0 | 482 | default: |
michael@0 | 483 | // No valid transformations exist |
michael@0 | 484 | return aCh; |
michael@0 | 485 | } |
michael@0 | 486 | newChar = MathvarMappingSearch(aCh, mapTable, tableLength); |
michael@0 | 487 | } else { |
michael@0 | 488 | // Must be Latin |
michael@0 | 489 | if (aMathVar > NS_MATHML_MATHVARIANT_MONOSPACE) { |
michael@0 | 490 | // Latin doesn't support the Arabic mathvariants |
michael@0 | 491 | return aCh; |
michael@0 | 492 | } |
michael@0 | 493 | multiplier = aMathVar - 2; |
michael@0 | 494 | // This is possible because the values for NS_MATHML_MATHVARIANT_* are |
michael@0 | 495 | // chosen to coincide with the order in which the encoded mathvariant |
michael@0 | 496 | // characters are located within their unicode block (less an offset to |
michael@0 | 497 | // avoid _NONE and _NORMAL variants) |
michael@0 | 498 | // See the kIsNumber case for an explanation of the following calculation |
michael@0 | 499 | tempChar = baseChar + MATH_BOLD_UPPER_A + |
michael@0 | 500 | multiplier*(MATH_ITALIC_UPPER_A - MATH_BOLD_UPPER_A); |
michael@0 | 501 | // There are roughly twenty characters that are located outside of the |
michael@0 | 502 | // mathematical block, so the spaces where they ought to be are used |
michael@0 | 503 | // as keys for a lookup table containing the correct character mappings. |
michael@0 | 504 | newChar = MathvarMappingSearch(tempChar, gLatinExceptionMapTable, |
michael@0 | 505 | ArrayLength(gLatinExceptionMapTable)); |
michael@0 | 506 | } |
michael@0 | 507 | |
michael@0 | 508 | if (newChar) { |
michael@0 | 509 | return newChar; |
michael@0 | 510 | } else if (varType == kIsLatin) { |
michael@0 | 511 | return tempChar; |
michael@0 | 512 | } else { |
michael@0 | 513 | // An Arabic character without a corresponding mapping |
michael@0 | 514 | return aCh; |
michael@0 | 515 | } |
michael@0 | 516 | |
michael@0 | 517 | } |
michael@0 | 518 | |
michael@0 | 519 | void |
michael@0 | 520 | MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, |
michael@0 | 521 | gfxContext* aRefContext) |
michael@0 | 522 | { |
michael@0 | 523 | gfxFontGroup* fontGroup = aTextRun->GetFontGroup(); |
michael@0 | 524 | gfxFontStyle fontStyle = *fontGroup->GetStyle(); |
michael@0 | 525 | |
michael@0 | 526 | nsAutoString convertedString; |
michael@0 | 527 | nsAutoTArray<bool,50> charsToMergeArray; |
michael@0 | 528 | nsAutoTArray<bool,50> deletedCharsArray; |
michael@0 | 529 | nsAutoTArray<nsStyleContext*,50> styleArray; |
michael@0 | 530 | nsAutoTArray<uint8_t,50> canBreakBeforeArray; |
michael@0 | 531 | bool mergeNeeded = false; |
michael@0 | 532 | |
michael@0 | 533 | bool singleCharMI = |
michael@0 | 534 | aTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_SINGLE_CHAR_MI; |
michael@0 | 535 | |
michael@0 | 536 | uint32_t length = aTextRun->GetLength(); |
michael@0 | 537 | const char16_t* str = aTextRun->mString.BeginReading(); |
michael@0 | 538 | nsRefPtr<nsStyleContext>* styles = aTextRun->mStyles.Elements(); |
michael@0 | 539 | |
michael@0 | 540 | if (mSSTYScriptLevel && length) { |
michael@0 | 541 | bool found = false; |
michael@0 | 542 | // We respect ssty settings explicitly set by the user |
michael@0 | 543 | for (uint32_t i = 0; i < fontStyle.featureSettings.Length(); i++) { |
michael@0 | 544 | if (fontStyle.featureSettings[i].mTag == TRUETYPE_TAG('s','s','t','y')) { |
michael@0 | 545 | found = true; |
michael@0 | 546 | break; |
michael@0 | 547 | } |
michael@0 | 548 | } |
michael@0 | 549 | if (!found) { |
michael@0 | 550 | uint8_t sstyLevel = 0; |
michael@0 | 551 | float scriptScaling = pow(styles[0]->StyleFont()->mScriptSizeMultiplier, |
michael@0 | 552 | mSSTYScriptLevel); |
michael@0 | 553 | static_assert(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER < 1, |
michael@0 | 554 | "Shouldn't it make things smaller?"); |
michael@0 | 555 | /* |
michael@0 | 556 | An SSTY level of 2 is set if the scaling factor is less than or equal |
michael@0 | 557 | to halfway between that for a scriptlevel of 1 (0.71) and that of a |
michael@0 | 558 | scriptlevel of 2 (0.71^2), assuming the default script size multiplier. |
michael@0 | 559 | An SSTY level of 1 is set if the script scaling factor is less than |
michael@0 | 560 | or equal that for a scriptlevel of 1 assuming the default script size |
michael@0 | 561 | multiplier. |
michael@0 | 562 | |
michael@0 | 563 | User specified values of script size multiplier will change the scaling |
michael@0 | 564 | factor which mSSTYScriptLevel values correspond to. |
michael@0 | 565 | |
michael@0 | 566 | In the event that the script size multiplier actually makes things |
michael@0 | 567 | larger, no change is made. |
michael@0 | 568 | |
michael@0 | 569 | If the user doesn't want this to happen, all they need to do is set |
michael@0 | 570 | style="-moz-font-feature-settings: 'ssty' 0" |
michael@0 | 571 | */ |
michael@0 | 572 | if (scriptScaling <= (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER + |
michael@0 | 573 | (NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER* |
michael@0 | 574 | NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER))/2) { |
michael@0 | 575 | // Currently only the first two ssty settings are used, so two is large |
michael@0 | 576 | // as we go |
michael@0 | 577 | sstyLevel = 2; |
michael@0 | 578 | } else if (scriptScaling <= NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER) { |
michael@0 | 579 | sstyLevel = 1; |
michael@0 | 580 | } |
michael@0 | 581 | if (sstyLevel) { |
michael@0 | 582 | gfxFontFeature settingSSTY; |
michael@0 | 583 | settingSSTY.mTag = TRUETYPE_TAG('s','s','t','y'); |
michael@0 | 584 | settingSSTY.mValue = sstyLevel; |
michael@0 | 585 | fontStyle.featureSettings.AppendElement(settingSSTY); |
michael@0 | 586 | } |
michael@0 | 587 | } |
michael@0 | 588 | } |
michael@0 | 589 | |
michael@0 | 590 | uint8_t mathVar; |
michael@0 | 591 | bool doMathvariantStyling = true; |
michael@0 | 592 | |
michael@0 | 593 | for (uint32_t i = 0; i < length; ++i) { |
michael@0 | 594 | int extraChars = 0; |
michael@0 | 595 | nsStyleContext* styleContext = styles[i]; |
michael@0 | 596 | mathVar = styleContext->StyleFont()->mMathVariant; |
michael@0 | 597 | |
michael@0 | 598 | if (singleCharMI && mathVar == NS_MATHML_MATHVARIANT_NONE) { |
michael@0 | 599 | mathVar = NS_MATHML_MATHVARIANT_ITALIC; |
michael@0 | 600 | } |
michael@0 | 601 | |
michael@0 | 602 | uint32_t ch = str[i]; |
michael@0 | 603 | if (NS_IS_HIGH_SURROGATE(ch) && i < length - 1 && |
michael@0 | 604 | NS_IS_LOW_SURROGATE(str[i + 1])) { |
michael@0 | 605 | ch = SURROGATE_TO_UCS4(ch, str[i + 1]); |
michael@0 | 606 | } |
michael@0 | 607 | uint32_t ch2 = MathVariant(ch, mathVar); |
michael@0 | 608 | |
michael@0 | 609 | if (mathVar == NS_MATHML_MATHVARIANT_BOLD || |
michael@0 | 610 | mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC || |
michael@0 | 611 | mathVar == NS_MATHML_MATHVARIANT_ITALIC) { |
michael@0 | 612 | if (ch == ch2 && ch != 0x20 && ch != 0xA0) { |
michael@0 | 613 | // Don't apply the CSS style if a character cannot be |
michael@0 | 614 | // transformed. There is an exception for whitespace as it is both |
michael@0 | 615 | // common and innocuous. |
michael@0 | 616 | doMathvariantStyling = false; |
michael@0 | 617 | } |
michael@0 | 618 | if (ch2 != ch) { |
michael@0 | 619 | // Bug 930504. Some platforms do not have fonts for Mathematical |
michael@0 | 620 | // Alphanumeric Symbols. Hence we check whether the transformed |
michael@0 | 621 | // character is actually available. |
michael@0 | 622 | uint8_t matchType; |
michael@0 | 623 | nsRefPtr<gfxFont> mathFont = fontGroup-> |
michael@0 | 624 | FindFontForChar(ch2, 0, HB_SCRIPT_COMMON, nullptr, &matchType); |
michael@0 | 625 | if (mathFont) { |
michael@0 | 626 | // Don't apply the CSS style if there is a math font for at least one |
michael@0 | 627 | // of the transformed character in this text run. |
michael@0 | 628 | doMathvariantStyling = false; |
michael@0 | 629 | } else { |
michael@0 | 630 | // We fallback to the original character. |
michael@0 | 631 | ch2 = ch; |
michael@0 | 632 | } |
michael@0 | 633 | } |
michael@0 | 634 | } |
michael@0 | 635 | |
michael@0 | 636 | deletedCharsArray.AppendElement(false); |
michael@0 | 637 | charsToMergeArray.AppendElement(false); |
michael@0 | 638 | styleArray.AppendElement(styleContext); |
michael@0 | 639 | canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i)); |
michael@0 | 640 | |
michael@0 | 641 | if (IS_IN_BMP(ch2)) { |
michael@0 | 642 | convertedString.Append(ch2); |
michael@0 | 643 | } else { |
michael@0 | 644 | convertedString.Append(H_SURROGATE(ch2)); |
michael@0 | 645 | convertedString.Append(L_SURROGATE(ch2)); |
michael@0 | 646 | ++extraChars; |
michael@0 | 647 | if (!IS_IN_BMP(ch)) { |
michael@0 | 648 | deletedCharsArray.AppendElement(true); // not exactly deleted, but |
michael@0 | 649 | // the trailing surrogate is skipped |
michael@0 | 650 | ++i; |
michael@0 | 651 | } |
michael@0 | 652 | } |
michael@0 | 653 | |
michael@0 | 654 | while (extraChars-- > 0) { |
michael@0 | 655 | mergeNeeded = true; |
michael@0 | 656 | charsToMergeArray.AppendElement(true); |
michael@0 | 657 | styleArray.AppendElement(styleContext); |
michael@0 | 658 | canBreakBeforeArray.AppendElement(false); |
michael@0 | 659 | } |
michael@0 | 660 | } |
michael@0 | 661 | |
michael@0 | 662 | uint32_t flags; |
michael@0 | 663 | gfxTextRunFactory::Parameters innerParams = |
michael@0 | 664 | GetParametersForInner(aTextRun, &flags, aRefContext); |
michael@0 | 665 | |
michael@0 | 666 | nsAutoPtr<nsTransformedTextRun> transformedChild; |
michael@0 | 667 | nsAutoPtr<gfxTextRun> cachedChild; |
michael@0 | 668 | gfxTextRun* child; |
michael@0 | 669 | |
michael@0 | 670 | if (mathVar == NS_MATHML_MATHVARIANT_BOLD && doMathvariantStyling) { |
michael@0 | 671 | fontStyle.style = NS_FONT_STYLE_NORMAL; |
michael@0 | 672 | fontStyle.weight = NS_FONT_WEIGHT_BOLD; |
michael@0 | 673 | } else if (mathVar == NS_MATHML_MATHVARIANT_ITALIC && doMathvariantStyling) { |
michael@0 | 674 | fontStyle.style = NS_FONT_STYLE_ITALIC; |
michael@0 | 675 | fontStyle.weight = NS_FONT_WEIGHT_NORMAL; |
michael@0 | 676 | } else if (mathVar == NS_MATHML_MATHVARIANT_BOLD_ITALIC && |
michael@0 | 677 | doMathvariantStyling) { |
michael@0 | 678 | fontStyle.style = NS_FONT_STYLE_ITALIC; |
michael@0 | 679 | fontStyle.weight = NS_FONT_WEIGHT_BOLD; |
michael@0 | 680 | } else if (mathVar != NS_MATHML_MATHVARIANT_NONE) { |
michael@0 | 681 | // Mathvariant overrides fontstyle and fontweight |
michael@0 | 682 | // Need to check to see if mathvariant is actually applied as this function |
michael@0 | 683 | // is used for other purposes. |
michael@0 | 684 | fontStyle.style = NS_FONT_STYLE_NORMAL; |
michael@0 | 685 | fontStyle.weight = NS_FONT_WEIGHT_NORMAL; |
michael@0 | 686 | } |
michael@0 | 687 | nsRefPtr<gfxFontGroup> newFontGroup = fontGroup->Copy(&fontStyle); |
michael@0 | 688 | |
michael@0 | 689 | if (!newFontGroup) |
michael@0 | 690 | return; |
michael@0 | 691 | |
michael@0 | 692 | if (mInnerTransformingTextRunFactory) { |
michael@0 | 693 | transformedChild = mInnerTransformingTextRunFactory->MakeTextRun( |
michael@0 | 694 | convertedString.BeginReading(), convertedString.Length(), |
michael@0 | 695 | &innerParams, newFontGroup, flags, styleArray.Elements(), false); |
michael@0 | 696 | child = transformedChild.get(); |
michael@0 | 697 | } else { |
michael@0 | 698 | cachedChild = newFontGroup->MakeTextRun( |
michael@0 | 699 | convertedString.BeginReading(), convertedString.Length(), |
michael@0 | 700 | &innerParams, flags); |
michael@0 | 701 | child = cachedChild.get(); |
michael@0 | 702 | } |
michael@0 | 703 | if (!child) |
michael@0 | 704 | return; |
michael@0 | 705 | // Copy potential linebreaks into child so they're preserved |
michael@0 | 706 | // (and also child will be shaped appropriately) |
michael@0 | 707 | NS_ASSERTION(convertedString.Length() == canBreakBeforeArray.Length(), |
michael@0 | 708 | "Dropped characters or break-before values somewhere!"); |
michael@0 | 709 | child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(), |
michael@0 | 710 | canBreakBeforeArray.Elements(), aRefContext); |
michael@0 | 711 | if (transformedChild) { |
michael@0 | 712 | transformedChild->FinishSettingProperties(aRefContext); |
michael@0 | 713 | } |
michael@0 | 714 | |
michael@0 | 715 | if (mergeNeeded) { |
michael@0 | 716 | // Now merge multiple characters into one multi-glyph character as required |
michael@0 | 717 | NS_ASSERTION(charsToMergeArray.Length() == child->GetLength(), |
michael@0 | 718 | "source length mismatch"); |
michael@0 | 719 | NS_ASSERTION(deletedCharsArray.Length() == aTextRun->GetLength(), |
michael@0 | 720 | "destination length mismatch"); |
michael@0 | 721 | MergeCharactersInTextRun(aTextRun, child, charsToMergeArray.Elements(), |
michael@0 | 722 | deletedCharsArray.Elements()); |
michael@0 | 723 | } else { |
michael@0 | 724 | // No merging to do, so just copy; this produces a more optimized textrun. |
michael@0 | 725 | // We can't steal the data because the child may be cached and stealing |
michael@0 | 726 | // the data would break the cache. |
michael@0 | 727 | aTextRun->ResetGlyphRuns(); |
michael@0 | 728 | aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), 0); |
michael@0 | 729 | } |
michael@0 | 730 | } |