layout/generic/MathMLTextRunFactory.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial