layout/generic/MathMLTextRunFactory.cpp

changeset 0
6474c204b198
     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 +}

mercurial