layout/generic/MathMLTextRunFactory.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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 }

mercurial