LCOV - code coverage report
Current view: top level - layout/generic - MathMLTextRunFactory.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 266 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 5 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13