LCOV - code coverage report
Current view: directory - layout/mathml - nsMathMLChar.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1004 0 0.0 %
Date: 2012-06-02 Functions: 87 0 0.0 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Mozilla MathML Project.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * The University Of Queensland.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1999
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Roger B. Sidje <rbs@maths.uq.edu.au>
      24                 :  *   Shyjan Mahamud <mahamud@cs.cmu.edu>
      25                 :  *   Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
      26                 :  *   Frederic Wang <fred.wang@free.fr>
      27                 :  *
      28                 :  * Alternatively, the contents of this file may be used under the terms of
      29                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      30                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      31                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      32                 :  * of those above. If you wish to allow use of your version of this file only
      33                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      34                 :  * use your version of this file under the terms of the MPL, indicate your
      35                 :  * decision by deleting the provisions above and replace them with the notice
      36                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      37                 :  * the provisions above, a recipient may use your version of this file under
      38                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      39                 :  *
      40                 :  * ***** END LICENSE BLOCK ***** */
      41                 : 
      42                 : #include "nsCOMPtr.h"
      43                 : #include "nsFrame.h"
      44                 : #include "nsPresContext.h"
      45                 : #include "nsStyleContext.h"
      46                 : #include "nsStyleConsts.h"
      47                 : #include "nsString.h"
      48                 : #include "nsUnicharUtils.h"
      49                 : #include "nsRenderingContext.h"
      50                 : #include "gfxPlatform.h"
      51                 : 
      52                 : #include "mozilla/Preferences.h"
      53                 : #include "nsISupportsPrimitives.h"
      54                 : #include "nsIComponentManager.h"
      55                 : #include "nsIPersistentProperties2.h"
      56                 : #include "nsIServiceManager.h"
      57                 : #include "nsIObserverService.h"
      58                 : #include "nsIObserver.h"
      59                 : #include "nsNetUtil.h"
      60                 : 
      61                 : #include "mozilla/LookAndFeel.h"
      62                 : #include "nsCSSRendering.h"
      63                 : #include "prprf.h"         // For PR_snprintf()
      64                 : 
      65                 : #include "nsDisplayList.h"
      66                 : 
      67                 : #include "nsMathMLOperators.h"
      68                 : #include "nsMathMLChar.h"
      69                 : 
      70                 : using namespace mozilla;
      71                 : 
      72                 : //#define SHOW_BORDERS 1
      73                 : //#define NOISY_SEARCH 1
      74                 : 
      75                 : // -----------------------------------------------------------------------------------
      76                 : static const PRUnichar   kSpaceCh   = PRUnichar(' ');
      77                 : static const nsGlyphCode kNullGlyph = {{0, 0}, 0};
      78                 : typedef enum {eExtension_base, eExtension_variants, eExtension_parts}
      79                 :   nsMathfontPrefExtension;
      80                 : 
      81                 : // -----------------------------------------------------------------------------------
      82                 : // nsGlyphTable is a class that provides an interface for accessing glyphs
      83                 : // of stretchy chars. It acts like a table that stores the variants of bigger
      84                 : // sizes (if any) and the partial glyphs needed to build extensible symbols.
      85                 : // An instance of nsGlyphTable is associated to one primary font. Extra glyphs
      86                 : // can be taken in other additional fonts when stretching certain characters.
      87                 : // These supplementary fonts are referred to as "external" fonts to the table.
      88                 : //
      89                 : // A char for which nsGlyphTable::Has(aChar) is true means that the table
      90                 : // contains some glyphs (bigger and/or partial) that can be used to render
      91                 : // the char. Bigger sizes (if any) of the char can then be retrieved with
      92                 : // BigOf(aSize). Partial glyphs can be retrieved with TopOf(), GlueOf(), etc.
      93                 : //
      94                 : // A table consists of "nsGlyphCode"s which are viewed either as Unicode
      95                 : // points or as direct glyph indices, depending on the type of the table.
      96                 : // XXX The latter is not yet supported.
      97                 : 
      98                 : // General format of MathFont Property Files from which glyph data are retrieved:
      99                 : // -----------------------------------------------------------------------------------
     100                 : // Each font should have its set of glyph data. For example, the glyph data for
     101                 : // the "Symbol" font and the "MT Extra" font are in "mathfontSymbol.properties"
     102                 : // and "mathfontMTExtra.properties", respectively. The mathfont property file is a
     103                 : // set of all the stretchy MathML characters that can be rendered with that font
     104                 : // using larger and/or partial glyphs. The entry of each stretchy character in the
     105                 : // mathfont property file gives, in that order, the 4 partial glyphs: Top (or Left),
     106                 : // Middle, Bottom (or Right), Glue; and the variants of bigger sizes (if any).
     107                 : // A position that is not relevant to a particular character is indicated there
     108                 : // with the UNICODE REPLACEMENT CHARACTER 0xFFFD.
     109                 : // Characters that need to be built recursively from other characters are said
     110                 : // to be composite. For example, chars like over/underbrace in CMEX10 have to
     111                 : // be built from two half stretchy chars and joined in the middle (TeXbook, p.225).
     112                 : // Such chars are handled in a special manner by the nsMathMLChar class, which allows
     113                 : // several (2 or more) child chars to be composed in order to render another char.
     114                 : // To specify such chars, their list of glyphs in the property file should be given
     115                 : // as space-separated segments of glyphs. Each segment gives the 4 partial
     116                 : // glyphs with which to build the child char that will be joined with its other
     117                 : // siblings. In this code, when this situation happens (see the detailed description
     118                 : // of Stretch() below), the original char (referred to as "parent") creates a
     119                 : // singly-linked list of child chars, asking them to stretch in an equally divided
     120                 : // space. The nsGlyphTable embeds the necessary logic to guarantee correctness in a
     121                 : // recursive stretch (and in the use of TopOf(), GlueOf(), etc) on these child chars.
     122                 : // -----------------------------------------------------------------------------------
     123                 : 
     124                 : #define NS_TABLE_TYPE_UNICODE       0
     125                 : #define NS_TABLE_TYPE_GLYPH_INDEX   1
     126                 : 
     127                 : #define NS_TABLE_STATE_ERROR       -1
     128                 : #define NS_TABLE_STATE_EMPTY        0
     129                 : #define NS_TABLE_STATE_READY        1
     130                 : 
     131                 : // helper to trim off comments from data in a MathFont Property File
     132                 : static void
     133               0 : Clean(nsString& aValue)
     134                 : {
     135                 :   // chop the trailing # comment portion if any ...
     136               0 :   PRInt32 comment = aValue.RFindChar('#');
     137               0 :   if (comment > 0) aValue.Truncate(comment);
     138               0 :   aValue.CompressWhitespace();
     139               0 : }
     140                 : 
     141                 : // helper to load a MathFont Property File
     142                 : static nsresult
     143               0 : LoadProperties(const nsString& aName,
     144                 :                nsCOMPtr<nsIPersistentProperties>& aProperties)
     145                 : {
     146               0 :   nsAutoString uriStr;
     147               0 :   uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
     148               0 :   uriStr.Append(aName);
     149               0 :   uriStr.StripWhitespace(); // that may come from aName
     150               0 :   uriStr.AppendLiteral(".properties");
     151               0 :   return NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(aProperties), 
     152               0 :                                                 NS_ConvertUTF16toUTF8(uriStr));
     153                 : }
     154                 : 
     155                 : // -----------------------------------------------------------------------------------
     156                 : 
     157                 : class nsGlyphTable {
     158                 : public:
     159               0 :   explicit nsGlyphTable(const nsString& aPrimaryFontName)
     160                 :     : mType(NS_TABLE_TYPE_UNICODE),
     161                 :       mFontName(1), // ensure space for primary font name.
     162                 :       mState(NS_TABLE_STATE_EMPTY),
     163               0 :       mCharCache(0)
     164                 :   {
     165               0 :     MOZ_COUNT_CTOR(nsGlyphTable);
     166               0 :     mFontName.AppendElement(aPrimaryFontName);
     167               0 :   }
     168                 : 
     169               0 :   ~nsGlyphTable() // not a virtual destructor: this class is not intended to be subclassed
     170               0 :   {
     171               0 :     MOZ_COUNT_DTOR(nsGlyphTable);
     172               0 :   }
     173                 : 
     174               0 :   const nsAString& PrimaryFontName() const
     175                 :   {
     176               0 :     return mFontName[0];
     177                 :   }
     178                 : 
     179               0 :   const nsAString& FontNameFor(const nsGlyphCode& aGlyphCode) const
     180                 :   {
     181               0 :     return mFontName[aGlyphCode.font];
     182                 :   }
     183                 : 
     184                 :   // True if this table contains some glyphs (variants and/or parts)
     185                 :   // or contains child chars that can be used to render this char
     186                 :   bool Has(nsPresContext* aPresContext, nsMathMLChar* aChar);
     187                 : 
     188                 :   // True if this table contains variants of larger sizes to render this char
     189                 :   bool HasVariantsOf(nsPresContext* aPresContext, nsMathMLChar* aChar);
     190                 : 
     191                 :   // True if this table contains parts (or composite parts) to render this char
     192                 :   bool HasPartsOf(nsPresContext* aPresContext, nsMathMLChar* aChar);
     193                 : 
     194                 :   // True if aChar is to be assembled from other child chars in this table
     195                 :   bool IsComposite(nsPresContext* aPresContext, nsMathMLChar* aChar);
     196                 : 
     197                 :   // The number of child chars to assemble in order to render aChar
     198                 :   PRInt32 ChildCountOf(nsPresContext* aPresContext, nsMathMLChar* aChar);
     199                 : 
     200                 :   // Getters for the parts
     201               0 :   nsGlyphCode TopOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
     202               0 :     return ElementAt(aPresContext, aChar, 0);
     203                 :   }
     204               0 :   nsGlyphCode MiddleOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
     205               0 :     return ElementAt(aPresContext, aChar, 1);
     206                 :   }
     207               0 :   nsGlyphCode BottomOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
     208               0 :     return ElementAt(aPresContext, aChar, 2);
     209                 :   }
     210               0 :   nsGlyphCode GlueOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
     211               0 :     return ElementAt(aPresContext, aChar, 3);
     212                 :   }
     213               0 :   nsGlyphCode BigOf(nsPresContext* aPresContext, nsMathMLChar* aChar, PRInt32 aSize) {
     214               0 :     return ElementAt(aPresContext, aChar, 4 + aSize);
     215                 :   }
     216               0 :   nsGlyphCode LeftOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
     217               0 :     return ElementAt(aPresContext, aChar, 0);
     218                 :   }
     219               0 :   nsGlyphCode RightOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
     220               0 :     return ElementAt(aPresContext, aChar, 2);
     221                 :   }
     222                 : 
     223                 : private:
     224                 :   nsGlyphCode ElementAt(nsPresContext* aPresContext, nsMathMLChar* aChar, PRUint32 aPosition);
     225                 : 
     226                 :   // The type is either NS_TABLE_TYPE_UNICODE or NS_TABLE_TYPE_GLYPH_INDEX
     227                 :   PRInt32 mType;    
     228                 :                            
     229                 :   // mFontName[0] is the primary font associated to this table. The others 
     230                 :   // are possible "external" fonts for glyphs not in the primary font
     231                 :   // but which are needed to stretch certain characters in the table
     232                 :   nsTArray<nsString> mFontName; 
     233                 :                                
     234                 :   // Tri-state variable for error/empty/ready
     235                 :   PRInt32 mState;
     236                 : 
     237                 :   // The set of glyph data in this table, as provided by the MathFont Property File
     238                 :   nsCOMPtr<nsIPersistentProperties> mGlyphProperties;
     239                 : 
     240                 :   // For speedy re-use, we always cache the last data used in the table.
     241                 :   // mCharCache is the Unicode point of the last char that was queried in this
     242                 :   // table. mGlyphCache is a buffer containing the glyph data associated to
     243                 :   // that char. For a property line 'key = value' in the MathFont Property File,
     244                 :   // mCharCache will retain the 'key' -- which is a Unicode point, while mGlyphCache
     245                 :   // will retain the 'value', which is a consecutive list of nsGlyphCodes, i.e.,
     246                 :   // the pairs of 'code@font' needed by the char -- in which 'code@0' can be specified
     247                 :   // without the optional '@0'. However, to ease subsequent processing, mGlyphCache
     248                 :   // excludes the '@' symbol and explicitly inserts all optional '0' that indicates
     249                 :   // the primary font identifier. Specifically therefore, the k-th glyph is
     250                 :   // characterized by :
     251                 :   // 1) mGlyphCache[3*k],mGlyphCache[3*k+1] : its Unicode point (or glyph index -- depending on mType),
     252                 :   // 2) mGlyphCache[3*k+2] : the numeric identifier of the font where it comes from.
     253                 :   // A font identifier of '0' means the default primary font associated to this
     254                 :   // table. Other digits map to the "external" fonts that may have been specified
     255                 :   // in the MathFont Property File.
     256                 :   nsString  mGlyphCache;
     257                 :   PRUnichar mCharCache;
     258                 : };
     259                 : 
     260                 : nsGlyphCode
     261               0 : nsGlyphTable::ElementAt(nsPresContext* aPresContext, nsMathMLChar* aChar, PRUint32 aPosition)
     262                 : {
     263               0 :   if (mState == NS_TABLE_STATE_ERROR) return kNullGlyph;
     264                 :   // Load glyph properties if this is the first time we have been here
     265               0 :   if (mState == NS_TABLE_STATE_EMPTY) {
     266               0 :     nsresult rv = LoadProperties(mFontName[0], mGlyphProperties);
     267                 : #ifdef NS_DEBUG
     268               0 :     nsCAutoString uriStr;
     269               0 :     uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
     270               0 :     LossyAppendUTF16toASCII(mFontName[0], uriStr);
     271               0 :     uriStr.StripWhitespace(); // that may come from mFontName
     272               0 :     uriStr.AppendLiteral(".properties");
     273                 :     printf("Loading %s ... %s\n",
     274                 :             uriStr.get(),
     275               0 :             (NS_FAILED(rv)) ? "Failed" : "Done");
     276                 : #endif
     277               0 :     if (NS_FAILED(rv)) {
     278               0 :       mState = NS_TABLE_STATE_ERROR; // never waste time with this table again
     279               0 :       return kNullGlyph;
     280                 :     }
     281               0 :     mState = NS_TABLE_STATE_READY;
     282                 : 
     283                 :     // see if there are external fonts needed for certain chars in this table
     284               0 :     nsCAutoString key;
     285               0 :     nsAutoString value;
     286               0 :     for (PRInt32 i = 1; ; i++) {
     287               0 :       key.AssignLiteral("external.");
     288               0 :       key.AppendInt(i, 10);
     289               0 :       rv = mGlyphProperties->GetStringProperty(key, value);
     290               0 :       if (NS_FAILED(rv)) break;
     291               0 :       Clean(value);
     292               0 :       mFontName.AppendElement(value); // i.e., mFontName[i] holds this font name
     293                 :     }
     294                 :   }
     295                 : 
     296                 :   // If aChar is a child char to be used by a parent composite char, make
     297                 :   // sure that it is really attached to this table
     298               0 :   if (aChar->mParent && (aChar->mGlyphTable != this)) return kNullGlyph;
     299                 : 
     300                 :   // Update our cache if it is not associated to this character
     301               0 :   PRUnichar uchar = aChar->mData[0];
     302               0 :   if (mCharCache != uchar) {
     303                 :     // The key in the property file is interpreted as ASCII and kept
     304                 :     // as such ...
     305               0 :     char key[10]; PR_snprintf(key, sizeof(key), "\\u%04X", uchar);
     306               0 :     nsAutoString value;
     307               0 :     nsresult rv = mGlyphProperties->GetStringProperty(nsDependentCString(key), value);
     308               0 :     if (NS_FAILED(rv)) return kNullGlyph;
     309               0 :     Clean(value);
     310                 :     // See if this char uses external fonts; e.g., if the 2nd glyph is taken from the
     311                 :     // external font '1', the property line looks like \uNNNN = \uNNNN\uNNNN@1\uNNNN.
     312                 :     // This is where mGlyphCache is pre-processed to explicitly store all glyph codes
     313                 :     // as combined pairs of 'code@font', excluding the '@' separator. This means that
     314                 :     // mGlyphCache[3*k],mGlyphCache[3*k+1] will later be rendered with mFontName[mGlyphCache[3*k+2]]
     315                 :     // Note: font identifier is internally an ASCII digit to avoid the null char issue
     316               0 :     nsAutoString buffer;
     317               0 :     PRInt32 length = value.Length();
     318               0 :     PRInt32 i = 0; // index in value
     319               0 :     PRInt32 j = 0; // part/variant index
     320               0 :     while (i < length) {
     321               0 :       PRUnichar code = value[i];
     322               0 :       ++i;
     323               0 :       buffer.Append(code);
     324                 :       // see if we are at the beginning of a child char
     325               0 :       if (code == kSpaceCh) {
     326                 :         // reset the annotation indicator to be 0 for the next code point
     327               0 :         j = -1;
     328                 :       }
     329                 : #if 0 // If we want this then the nsGlyphTableList must be declared
     330                 :       // or the UnicodeTable could be made a global.
     331                 :       // See if this code point is an *indirect reference* to the Unicode
     332                 :       // table and lookup the code there.
     333                 :       else if (code == PRUnichar(0xF8FF) && gGlyphTableList &&
     334                 :                this != &gGlyphTableList->mUnicodeTable) {
     335                 :         code = gGlyphTableList->mUnicodeTable.
     336                 :           ElementAt(aPresContext, aChar, aPosition).code;
     337                 :       }
     338                 :       // see if this code point is a *direct reference* to
     339                 :       // the Unicode table, and lookup the [TLMBRG1-9] position for code.
     340                 :       else if ((i+1 < length) && (value[i] == PRUnichar('.'))) {
     341                 :         ++i;
     342                 :         // Need to implement this if we want it:
     343                 :         // Set (new) code from the value[i] position for (current) code.
     344                 :         if (1)
     345                 :           return kNullGlyph;
     346                 :         ++i;
     347                 :       }
     348                 : #endif
     349                 :       // Read the next word if we have a non-BMP character.
     350               0 :       if (i < length && NS_IS_HIGH_SURROGATE(code)) {
     351               0 :         code = value[i];
     352               0 :         ++i;
     353                 :       } else {
     354               0 :         code = PRUnichar('\0');
     355                 :       }
     356               0 :       buffer.Append(code);
     357                 : 
     358                 :       // See if an external font is needed for the code point.
     359                 :       // Limit of 9 external fonts
     360               0 :       PRUnichar font = 0;
     361               0 :       if (i+1 < length && value[i] == PRUnichar('@') &&
     362               0 :           value[i+1] >= PRUnichar('0') && value[i+1] <= PRUnichar('9')) {
     363               0 :         ++i;
     364               0 :         font = value[i] - '0';
     365               0 :         ++i;
     366               0 :         if (font >= mFontName.Length()) {
     367               0 :           NS_ERROR("Nonexistent font referenced in glyph table");
     368               0 :           return kNullGlyph;
     369                 :         }
     370                 :         // The char cannot be handled if this font is not installed
     371               0 :         if (!mFontName[font].Length()) {
     372               0 :           return kNullGlyph;
     373                 :         }
     374                 :       }
     375               0 :       buffer.Append(font);
     376               0 :       ++j;
     377                 :     }
     378                 :     // update our cache with the new settings
     379               0 :     mGlyphCache.Assign(buffer);
     380               0 :     mCharCache = uchar;
     381                 :   }
     382                 : 
     383                 :   // If aChar is a composite char, only its children are allowed
     384                 :   // to use its glyphs in this table, i.e., the parent char itself
     385                 :   // is disabled and cannot be stretched directly with these glyphs.
     386                 :   // This guarantees a coherent behavior in Stretch().
     387               0 :   if (!aChar->mParent && (kNotFound != mGlyphCache.FindChar(kSpaceCh))) {
     388               0 :     return kNullGlyph;
     389                 :   }
     390                 : 
     391                 :   // If aChar is a child char, the index of the glyph is relative to
     392                 :   // the offset of the list of glyphs corresponding to the child char.
     393               0 :   PRUint32 offset = 0;
     394               0 :   PRUint32 length = mGlyphCache.Length();
     395               0 :   if (aChar->mParent) {
     396               0 :     nsMathMLChar* child = aChar->mParent->mSibling;
     397                 :     // XXXkt composite chars can't have size variants
     398               0 :     while (child && (child != aChar)) {
     399               0 :       offset += 5; // skip the 4 partial glyphs + the whitespace separator
     400               0 :       child = child->mSibling;
     401                 :     }
     402               0 :     length = 3*(offset + 4); // stay confined in the 4 partial glyphs of this child
     403                 :   }
     404               0 :   PRUint32 index = 3*(offset + aPosition); // 3* is to account for the code@font pairs
     405               0 :   if (index+2 >= length) return kNullGlyph;
     406                 :   nsGlyphCode ch;
     407               0 :   ch.code[0] = mGlyphCache.CharAt(index);
     408               0 :   ch.code[1] = mGlyphCache.CharAt(index + 1);
     409               0 :   ch.font = mGlyphCache.CharAt(index + 2);
     410               0 :   return ch.code[0] == PRUnichar(0xFFFD) ? kNullGlyph : ch;
     411                 : }
     412                 : 
     413                 : bool
     414               0 : nsGlyphTable::IsComposite(nsPresContext* aPresContext, nsMathMLChar* aChar)
     415                 : {
     416                 :   // there is only one level of recursion in our model. a child
     417                 :   // cannot be composite because it cannot have its own children
     418               0 :   if (aChar->mParent) return false;
     419                 :   // shortcut to sync the cache with this char...
     420               0 :   mCharCache = 0; mGlyphCache.Truncate(); ElementAt(aPresContext, aChar, 0);
     421                 :   // the cache remained empty if the char wasn't found in this table
     422               0 :   if (4*3 >= mGlyphCache.Length()) return false;
     423                 :   // the lists of glyphs of a composite char are space-separated
     424               0 :   return (kSpaceCh == mGlyphCache.CharAt(4*3));
     425                 : }
     426                 : 
     427                 : PRInt32
     428               0 : nsGlyphTable::ChildCountOf(nsPresContext* aPresContext, nsMathMLChar* aChar)
     429                 : {
     430                 :   // this will sync the cache as well ...
     431               0 :   if (!IsComposite(aPresContext, aChar)) return 0;
     432                 :   // the lists of glyphs of a composite char are space-separated
     433               0 :   return 1 + mGlyphCache.CountChar(kSpaceCh);
     434                 : }
     435                 : 
     436                 : bool
     437               0 : nsGlyphTable::Has(nsPresContext* aPresContext, nsMathMLChar* aChar)
     438                 : {
     439               0 :   return HasVariantsOf(aPresContext, aChar) || HasPartsOf(aPresContext, aChar);
     440                 : }
     441                 : 
     442                 : bool
     443               0 : nsGlyphTable::HasVariantsOf(nsPresContext* aPresContext, nsMathMLChar* aChar)
     444                 : {
     445                 :   //XXXkt all variants must be in the same file as size 1
     446               0 :   return BigOf(aPresContext, aChar, 1).Exists();
     447                 : }
     448                 : 
     449                 : bool
     450               0 : nsGlyphTable::HasPartsOf(nsPresContext* aPresContext, nsMathMLChar* aChar)
     451                 : {
     452               0 :   return GlueOf(aPresContext, aChar).Exists() ||
     453               0 :     TopOf(aPresContext, aChar).Exists() ||
     454               0 :     BottomOf(aPresContext, aChar).Exists() ||
     455               0 :     MiddleOf(aPresContext, aChar).Exists() ||
     456               0 :     IsComposite(aPresContext, aChar);
     457                 : }
     458                 : 
     459                 : // -----------------------------------------------------------------------------------
     460                 : // This is the list of all the applicable glyph tables.
     461                 : // We will maintain a single global instance that will only reveal those
     462                 : // glyph tables that are associated to fonts currently installed on the
     463                 : // user' system. The class is an XPCOM shutdown observer to allow us to
     464                 : // free its allocated data at shutdown
     465                 : 
     466                 : class nsGlyphTableList : public nsIObserver
     467                 : {
     468                 : public:
     469                 :   NS_DECL_ISUPPORTS
     470                 :   NS_DECL_NSIOBSERVER
     471                 : 
     472                 :   nsGlyphTable mUnicodeTable;
     473                 : 
     474               0 :   nsGlyphTableList()
     475               0 :     : mUnicodeTable(NS_LITERAL_STRING("Unicode"))
     476                 :   {
     477               0 :     MOZ_COUNT_CTOR(nsGlyphTableList);
     478               0 :   }
     479                 : 
     480               0 :   virtual ~nsGlyphTableList()
     481               0 :   {
     482               0 :     MOZ_COUNT_DTOR(nsGlyphTableList);
     483               0 :   }
     484                 : 
     485                 :   nsresult Initialize();
     486                 :   nsresult Finalize();
     487                 : 
     488                 :   // Add a glyph table in the list, return the new table that was added
     489                 :   nsGlyphTable*
     490                 :   AddGlyphTable(const nsString& aPrimaryFontName);
     491                 : 
     492                 :   // Find a glyph table in the list that has a glyph for the given char
     493                 :   nsGlyphTable*
     494                 :   GetGlyphTableFor(nsPresContext* aPresContext,
     495                 :                    nsMathMLChar*  aChar);
     496                 : 
     497                 :   // Find the glyph table in the list corresponding to the given font family.
     498                 :   nsGlyphTable*
     499                 :   GetGlyphTableFor(const nsAString& aFamily);
     500                 : 
     501                 : private:
     502               0 :   nsGlyphTable* TableAt(PRInt32 aIndex) {
     503               0 :     return &mTableList.ElementAt(aIndex);
     504                 :   }
     505               0 :   PRInt32 Count() {
     506               0 :     return mTableList.Length();
     507                 :   }
     508                 : 
     509                 :   // List of glyph tables;
     510                 :   nsTArray<nsGlyphTable> mTableList;
     511                 : };
     512                 : 
     513               0 : NS_IMPL_ISUPPORTS1(nsGlyphTableList, nsIObserver)
     514                 : 
     515                 : // -----------------------------------------------------------------------------------
     516                 : // Here is the global list of applicable glyph tables that we will be using
     517                 : static nsGlyphTableList* gGlyphTableList = nsnull;
     518                 : 
     519                 : static bool gInitialized = false;
     520                 : 
     521                 : // XPCOM shutdown observer
     522                 : NS_IMETHODIMP
     523               0 : nsGlyphTableList::Observe(nsISupports*     aSubject,
     524                 :                           const char* aTopic,
     525                 :                           const PRUnichar* someData)
     526                 : {
     527               0 :   Finalize();
     528               0 :   return NS_OK;
     529                 : }
     530                 : 
     531                 : // Add an observer to XPCOM shutdown so that we can free our data at shutdown
     532                 : nsresult
     533               0 : nsGlyphTableList::Initialize()
     534                 : {
     535               0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     536               0 :   if (!obs)
     537               0 :     return NS_ERROR_FAILURE;
     538                 : 
     539               0 :   nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
     540               0 :   NS_ENSURE_SUCCESS(rv, rv);
     541                 : 
     542               0 :   return NS_OK;
     543                 : }
     544                 : 
     545                 : // Remove our observer and free the memory that were allocated for us
     546                 : nsresult
     547               0 : nsGlyphTableList::Finalize()
     548                 : {
     549                 :   // Remove our observer from the observer service
     550               0 :   nsresult rv = NS_OK;
     551               0 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     552               0 :   if (obs)
     553               0 :     rv = obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
     554                 :   else
     555               0 :     rv = NS_ERROR_FAILURE;
     556                 : 
     557               0 :   gInitialized = false;
     558                 :   // our oneself will be destroyed when our |Release| is called by the observer
     559               0 :   return rv;
     560                 : }
     561                 : 
     562                 : nsGlyphTable*
     563               0 : nsGlyphTableList::AddGlyphTable(const nsString& aPrimaryFontName)
     564                 : {
     565                 :   // See if there is already a special table for this family.
     566               0 :   nsGlyphTable* glyphTable = GetGlyphTableFor(aPrimaryFontName);
     567               0 :   if (glyphTable != &mUnicodeTable)
     568               0 :     return glyphTable;
     569                 : 
     570                 :   // allocate a table
     571               0 :   glyphTable = mTableList.AppendElement(aPrimaryFontName);
     572               0 :   return glyphTable;
     573                 : }
     574                 : 
     575                 : nsGlyphTable*
     576               0 : nsGlyphTableList::GetGlyphTableFor(nsPresContext* aPresContext, 
     577                 :                                    nsMathMLChar*   aChar)
     578                 : {
     579               0 :   if (mUnicodeTable.Has(aPresContext, aChar))
     580               0 :     return &mUnicodeTable;
     581                 : 
     582                 :   PRInt32 i;
     583               0 :   for (i = 0; i < Count(); i++) {
     584               0 :     nsGlyphTable* glyphTable = TableAt(i);
     585               0 :     if (glyphTable->Has(aPresContext, aChar)) {
     586               0 :       return glyphTable;
     587                 :     }
     588                 :   }
     589               0 :   return nsnull;
     590                 : }
     591                 : 
     592                 : nsGlyphTable*
     593               0 : nsGlyphTableList::GetGlyphTableFor(const nsAString& aFamily)
     594                 : {
     595               0 :   for (PRInt32 i = 0; i < Count(); i++) {
     596               0 :     nsGlyphTable* glyphTable = TableAt(i);
     597               0 :     const nsAString& fontName = glyphTable->PrimaryFontName();
     598                 :     // TODO: would be nice to consider StripWhitespace and other aliasing
     599               0 :     if (fontName.Equals(aFamily, nsCaseInsensitiveStringComparator())) {
     600               0 :       return glyphTable;
     601                 :     }
     602                 :   }
     603                 :   // Fall back to default Unicode table
     604               0 :   return &mUnicodeTable;
     605                 : }
     606                 : 
     607                 : // -----------------------------------------------------------------------------------
     608                 : 
     609                 : // Lookup the preferences:
     610                 : // "font.mathfont-family.\uNNNN.base"     -- fonts for the base size
     611                 : // "font.mathfont-family.\uNNNN.variants" -- fonts for larger glyphs
     612                 : // "font.mathfont-family.\uNNNN.parts"    -- fonts for partial glyphs
     613                 : // Given the char code and mode of stretch, retrieve the preferred extension
     614                 : // font families.
     615                 : static bool
     616               0 : GetFontExtensionPref(PRUnichar aChar,
     617                 :                      nsMathfontPrefExtension aExtension, nsString& aValue)
     618                 : {
     619                 :   // initialize OUT param
     620               0 :   aValue.Truncate();
     621                 : 
     622                 :   // We are going to try two keys because some users specify their pref as 
     623                 :   // user_pref("font.mathfont-family.\uNNNN.base", "...") rather than
     624                 :   // user_pref("font.mathfont-family.\\uNNNN.base", "...").
     625                 :   // The \uNNNN in the former is interpreted as an UTF16 escape sequence by
     626                 :   // JavaScript and is converted to the internal UTF8 string that JavaScript uses. 
     627                 :   // But clueless users who are not savvy of JavaScript have no idea as to what 
     628                 :   // is going on and are baffled as to why their pref setting is not working.
     629                 :   // So to save countless explanations, we are going to support both keys.
     630                 : 
     631                 :   static const char* kMathFontPrefix = "font.mathfont-family.";
     632                 : 
     633               0 :   nsCAutoString extension;
     634               0 :   switch (aExtension)
     635                 :   {
     636                 :     case eExtension_base:
     637               0 :       extension.AssignLiteral(".base");
     638               0 :       break;
     639                 :     case eExtension_variants:
     640               0 :       extension.AssignLiteral(".variants");
     641               0 :       break;
     642                 :     case eExtension_parts:
     643               0 :       extension.AssignLiteral(".parts");
     644               0 :       break;
     645                 :     default:
     646               0 :       return false;
     647                 :   }
     648                 : 
     649                 :   // .\\uNNNN key
     650               0 :   nsCAutoString key;
     651               0 :   key.AssignASCII(kMathFontPrefix);
     652                 :   char ustr[10];
     653               0 :   PR_snprintf(ustr, sizeof(ustr), "\\u%04X", aChar);
     654               0 :   key.Append(ustr);
     655               0 :   key.Append(extension);
     656                 :   // .\uNNNN key
     657               0 :   nsCAutoString alternateKey;
     658               0 :   alternateKey.AssignASCII(kMathFontPrefix);
     659               0 :   NS_ConvertUTF16toUTF8 tmp(&aChar, 1);
     660               0 :   alternateKey.Append(tmp);
     661               0 :   alternateKey.Append(extension);
     662                 : 
     663               0 :   aValue = Preferences::GetString(key.get());
     664               0 :   if (aValue.IsEmpty()) {
     665               0 :     aValue = Preferences::GetString(alternateKey.get());
     666                 :   }
     667               0 :   return !aValue.IsEmpty();
     668                 : }
     669                 : 
     670                 : 
     671                 : static bool
     672               0 : MathFontEnumCallback(const nsString& aFamily, bool aGeneric, void *aData)
     673                 : {
     674               0 :   if (!gGlyphTableList->AddGlyphTable(aFamily))
     675               0 :     return false; // stop in low-memory situations
     676               0 :   return true; // don't stop
     677                 : }
     678                 : 
     679                 : static nsresult
     680               0 : InitGlobals(nsPresContext* aPresContext)
     681                 : {
     682               0 :   NS_ASSERTION(!gInitialized, "Error -- already initialized");
     683               0 :   gInitialized = true;
     684                 : 
     685                 :   // Allocate the placeholders for the preferred parts and variants
     686               0 :   nsresult rv = NS_ERROR_OUT_OF_MEMORY;
     687               0 :   gGlyphTableList = new nsGlyphTableList();
     688               0 :   if (gGlyphTableList) {
     689               0 :     rv = gGlyphTableList->Initialize();
     690                 :   }
     691               0 :   if (NS_FAILED(rv)) {
     692               0 :     delete gGlyphTableList;
     693               0 :     gGlyphTableList = nsnull;
     694               0 :     return rv;
     695                 :   }
     696                 :   /*
     697                 :   else
     698                 :     The gGlyphTableList has been successfully registered as a shutdown observer.
     699                 :     It will be deleted at shutdown, even if a failure happens below.
     700                 :   */
     701                 : 
     702               0 :   nsCAutoString key;
     703               0 :   nsAutoString value;
     704               0 :   nsCOMPtr<nsIPersistentProperties> mathfontProp;
     705                 : 
     706                 :   // Add the math fonts in the gGlyphTableList in order of preference ...
     707                 :   // Note: we only load font-names at this stage. The actual glyph tables will
     708                 :   // be loaded lazily (see nsGlyphTable::ElementAt()).
     709                 : 
     710                 :   // Load the "mathfont.properties" file
     711               0 :   value.Truncate();
     712               0 :   rv = LoadProperties(value, mathfontProp);
     713               0 :   if (NS_FAILED(rv)) return rv;
     714                 : 
     715                 :   // Get the list of mathfonts having special glyph tables to be used for
     716                 :   // stretchy characters.
     717                 :   // We just want to iterate over the font-family list using the
     718                 :   // callback mechanism that nsFont has...
     719               0 :   nsFont font("", 0, 0, 0, 0, 0, 0);
     720               0 :   NS_NAMED_LITERAL_CSTRING(defaultKey, "font.mathfont-glyph-tables");
     721               0 :   rv = mathfontProp->GetStringProperty(defaultKey, font.name);
     722               0 :   if (NS_FAILED(rv)) return rv;
     723                 : 
     724                 :   // Parse the font list and append an entry for each family to gGlyphTableList
     725               0 :   nsAutoString missingFamilyList;
     726                 : 
     727               0 :   font.EnumerateFamilies(MathFontEnumCallback, nsnull);
     728               0 :   return rv;
     729                 : }
     730                 : 
     731                 : // -----------------------------------------------------------------------------------
     732                 : // And now the implementation of nsMathMLChar
     733                 : 
     734                 : nsStyleContext*
     735               0 : nsMathMLChar::GetStyleContext() const
     736                 : {
     737               0 :   NS_ASSERTION(!mParent, "invalid call - not allowed for child chars");
     738               0 :   NS_ASSERTION(mStyleContext, "chars should always have style context");
     739               0 :   return mStyleContext;
     740                 :   return NS_OK;
     741                 : }
     742                 : 
     743                 : void
     744               0 : nsMathMLChar::SetStyleContext(nsStyleContext* aStyleContext)
     745                 : {
     746               0 :   NS_ASSERTION(!mParent, "invalid call - not allowed for child chars");
     747               0 :   NS_PRECONDITION(aStyleContext, "null ptr");
     748               0 :   if (aStyleContext != mStyleContext) {
     749               0 :     if (mStyleContext)
     750               0 :       mStyleContext->Release();
     751               0 :     if (aStyleContext) {
     752               0 :       mStyleContext = aStyleContext;
     753               0 :       aStyleContext->AddRef();
     754                 : 
     755                 :       // Sync the pointers of child chars.
     756               0 :       nsMathMLChar* child = mSibling;
     757               0 :       while (child) {
     758               0 :         child->mStyleContext = mStyleContext;
     759               0 :         child = child->mSibling;
     760                 :       }
     761                 :     }
     762                 :   }
     763               0 : }
     764                 : 
     765                 : void
     766               0 : nsMathMLChar::SetData(nsPresContext* aPresContext,
     767                 :                       nsString&       aData)
     768                 : {
     769               0 :   NS_ASSERTION(!mParent, "invalid call - not allowed for child chars");
     770               0 :   if (!gInitialized) {
     771               0 :     InitGlobals(aPresContext);
     772                 :   }
     773               0 :   mData = aData;
     774                 :   // some assumptions until proven otherwise
     775                 :   // note that mGlyph is not initialized
     776               0 :   mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
     777               0 :   mBoundingMetrics = nsBoundingMetrics();
     778               0 :   mGlyphTable = nsnull;
     779                 :   // check if stretching is applicable ...
     780               0 :   if (gGlyphTableList && (1 == mData.Length())) {
     781               0 :     mDirection = nsMathMLOperators::GetStretchyDirection(mData);
     782                 :     // default tentative table (not the one that is necessarily going
     783                 :     // to be used)
     784               0 :     mGlyphTable = gGlyphTableList->GetGlyphTableFor(aPresContext, this);
     785                 :   }
     786               0 : }
     787                 : 
     788                 : // -----------------------------------------------------------------------------------
     789                 : /*
     790                 :  The Stretch:
     791                 :  @param aContainerSize - suggested size for the stretched char
     792                 :  @param aDesiredStretchSize - OUT parameter. The desired size
     793                 :  after stretching. If no stretching is done, the output will
     794                 :  simply give the base size.
     795                 : 
     796                 :  How it works?
     797                 :  Summary:-
     798                 :  The Stretch() method first looks for a glyph of appropriate
     799                 :  size; If a glyph is found, it is cached by this object and
     800                 :  its size is returned in aDesiredStretchSize. The cached
     801                 :  glyph will then be used at the painting stage.
     802                 :  If no glyph of appropriate size is found, a search is made
     803                 :  to see if the char can be built by parts.
     804                 : 
     805                 :  Details:-
     806                 :  A character gets stretched through the following pipeline :
     807                 : 
     808                 :  1) If the base size of the char is sufficient to cover the
     809                 :     container' size, we use that. If not, it will still be
     810                 :     used as a fallback if the other stages in the pipeline fail.
     811                 :     Issues :
     812                 :     a) The base size, the parts and the variants of a char can
     813                 :        be in different fonts. For eg., the base size for '(' should
     814                 :        come from a normal ascii font if CMEX10 is used, since CMEX10
     815                 :        only contains the stretched versions. Hence, there are two
     816                 :        style contexts in use throughout the process. The leaf style
     817                 :        context of the char holds fonts with which to try to stretch
     818                 :        the char. The parent style context of the char contains fonts
     819                 :        for normal rendering. So the parent context is the one used
     820                 :        to get the initial base size at the start of the pipeline.
     821                 :     b) For operators that can be largeop's in display mode,
     822                 :        we will skip the base size even if it fits, so that
     823                 :        the next stage in the pipeline is given a chance to find
     824                 :        a largeop variant. If the next stage fails, we fallback
     825                 :        to the base size.
     826                 : 
     827                 :  2) We search for the first larger variant of the char that fits the
     828                 :     container' size.  We first search for larger variants using the glyph
     829                 :     table corresponding to the first existing font specified in the list of
     830                 :     stretchy fonts held by the leaf style context (from -moz-math-stretchy in
     831                 :     mathml.css).  Generic fonts are resolved by the preference
     832                 :     "font.mathfont-family".
     833                 :     Issues :
     834                 :     a) the largeop and display settings determine the starting
     835                 :        size when we do the above search, regardless of whether
     836                 :        smaller variants already fit the container' size.
     837                 :     b) if it is a largeopOnly request (i.e., a displaystyle operator
     838                 :        with largeop=true and stretchy=false), we break after finding
     839                 :        the first starting variant, regardless of whether that
     840                 :        variant fits the container's size.
     841                 : 
     842                 :  3) If a variant of appropriate size wasn't found, we see if the char
     843                 :     can be built by parts using the same glyph table.
     844                 :     Issues:
     845                 :     a) Certain chars like over/underbrace in CMEX10 have to be built
     846                 :        from two half stretchy chars and joined in the middle. Such
     847                 :        chars are handled in a special manner. When this situation is
     848                 :        detected, the initial char (referred to as "parent") creates a
     849                 :        singly-linked list of child chars, asking them to stretch in
     850                 :        a divided space. A convention is used in the setup of
     851                 :        nsGlyphTable to express that a composite parent char can be built
     852                 :        from child chars.
     853                 :     b) There are some chars that have no middle and glue glyphs. For
     854                 :        such chars, the parts need to be joined using the rule.
     855                 :        By convention (TeXbook p.225), the descent of the parts is
     856                 :        zero while their ascent gives the thickness of the rule that
     857                 :        should be used to join them.
     858                 : 
     859                 :  4) If a match was not found in that glyph table, repeat from 2 to search the
     860                 :     ordered list of stretchy fonts for the first font with a glyph table that
     861                 :     provides a fit to the container size.  If no fit is found, the closest fit
     862                 :     is used.
     863                 : 
     864                 :  Of note:
     865                 :  When the pipeline completes successfully, the desired size of the
     866                 :  stretched char can actually be slightly larger or smaller than
     867                 :  aContainerSize. But it is the responsibility of the caller to
     868                 :  account for the spacing when setting aContainerSize, and to leave
     869                 :  any extra margin when placing the stretched char.
     870                 : */
     871                 : // -----------------------------------------------------------------------------------
     872                 : 
     873                 : 
     874                 : // plain TeX settings (TeXbook p.152)
     875                 : #define NS_MATHML_DELIMITER_FACTOR             0.901f
     876                 : #define NS_MATHML_DELIMITER_SHORTFALL_POINTS   5.0f
     877                 : 
     878                 : static bool
     879               0 : IsSizeOK(nsPresContext* aPresContext, nscoord a, nscoord b, PRUint32 aHint)
     880                 : {
     881                 :   // Normal: True if 'a' is around +/-10% of the target 'b' (10% is
     882                 :   // 1-DelimiterFactor). This often gives a chance to the base size to
     883                 :   // win, especially in the context of <mfenced> without tall elements
     884                 :   // or in sloppy markups without protective <mrow></mrow>
     885                 :   bool isNormal =
     886                 :     (aHint & NS_STRETCH_NORMAL)
     887               0 :     && bool(float(NS_ABS(a - b))
     888               0 :               < (1.0f - NS_MATHML_DELIMITER_FACTOR) * float(b));
     889                 :   // Nearer: True if 'a' is around max{ +/-10% of 'b' , 'b' - 5pt },
     890                 :   // as documented in The TeXbook, Ch.17, p.152.
     891                 :   // i.e. within 10% and within 5pt
     892               0 :   bool isNearer = false;
     893               0 :   if (aHint & (NS_STRETCH_NEARER | NS_STRETCH_LARGEOP)) {
     894                 :     float c = NS_MAX(float(b) * NS_MATHML_DELIMITER_FACTOR,
     895               0 :                      float(b) - nsPresContext::CSSPointsToAppUnits(NS_MATHML_DELIMITER_SHORTFALL_POINTS));
     896               0 :     isNearer = bool(float(NS_ABS(b - a)) <= (float(b) - c));
     897                 :   }
     898                 :   // Smaller: Mainly for transitory use, to compare two candidate
     899                 :   // choices
     900                 :   bool isSmaller =
     901                 :     (aHint & NS_STRETCH_SMALLER)
     902                 :     && bool((float(a) >= (NS_MATHML_DELIMITER_FACTOR * float(b)))
     903               0 :               && (a <= b));
     904                 :   // Larger: Critical to the sqrt code to ensure that the radical
     905                 :   // size is tall enough
     906                 :   bool isLarger =
     907                 :     (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP))
     908               0 :     && bool(a >= b);
     909               0 :   return (isNormal || isSmaller || isNearer || isLarger);
     910                 : }
     911                 : 
     912                 : static bool
     913               0 : IsSizeBetter(nscoord a, nscoord olda, nscoord b, PRUint32 aHint)
     914                 : {
     915               0 :   if (0 == olda)
     916               0 :     return true;
     917               0 :   if (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP))
     918               0 :     return (a >= olda) ? (olda < b) : (a >= b);
     919               0 :   if (aHint & NS_STRETCH_SMALLER)
     920               0 :     return (a <= olda) ? (olda > b) : (a <= b);
     921                 : 
     922                 :   // XXXkt prob want log scale here i.e. 1.5 is closer to 1 than 0.5
     923               0 :   return NS_ABS(a - b) < NS_ABS(olda - b);
     924                 : }
     925                 : 
     926                 : // We want to place the glyphs even when they don't fit at their
     927                 : // full extent, i.e., we may clip to tolerate a small amount of
     928                 : // overlap between the parts. This is important to cater for fonts
     929                 : // with long glues.
     930                 : static nscoord
     931               0 : ComputeSizeFromParts(nsPresContext* aPresContext,
     932                 :                      nsGlyphCode* aGlyphs,
     933                 :                      nscoord*     aSizes,
     934                 :                      nscoord      aTargetSize)
     935                 : {
     936                 :   enum {first, middle, last, glue};
     937                 :   // Add the parts that cannot be left out.
     938               0 :   nscoord sum = 0;
     939               0 :   for (PRInt32 i = first; i <= last; i++) {
     940               0 :     if (aGlyphs[i] != aGlyphs[glue]) {
     941               0 :       sum += aSizes[i];
     942                 :     }
     943                 :   }
     944                 : 
     945                 :   // Determine how much is used in joins
     946               0 :   nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
     947               0 :   PRInt32 joins = aGlyphs[middle] == aGlyphs[glue] ? 1 : 2;
     948                 : 
     949                 :   // Pick a maximum size using a maximum number of glue glyphs that we are
     950                 :   // prepared to draw for one character.
     951               0 :   const PRInt32 maxGlyphs = 1000;
     952                 : 
     953                 :   // This also takes into account the fact that, if the glue has no size,
     954                 :   // then the character can't be lengthened.
     955               0 :   nscoord maxSize = sum - 2 * joins * oneDevPixel + maxGlyphs * aSizes[glue];
     956               0 :   if (maxSize < aTargetSize)
     957               0 :     return maxSize; // settle with the maximum size
     958                 : 
     959                 :   // Get the minimum allowable size using some flex.
     960               0 :   nscoord minSize = NSToCoordRound(NS_MATHML_DELIMITER_FACTOR * sum);
     961                 : 
     962               0 :   if (minSize > aTargetSize)
     963               0 :     return minSize; // settle with the minimum size
     964                 : 
     965                 :   // Fill-up the target area
     966               0 :   return aTargetSize;
     967                 : }
     968                 : 
     969                 : // Insert aFallbackFamilies before the first generic family in or at the end
     970                 : // of a CSS aFontName.
     971                 : static void
     972               0 : AddFallbackFonts(nsAString& aFontName, const nsAString& aFallbackFamilies)
     973                 : {
     974               0 :   if (aFallbackFamilies.IsEmpty())
     975               0 :     return;
     976                 : 
     977               0 :   if (aFontName.IsEmpty()) {
     978               0 :     return;
     979                 :   }
     980                 : 
     981                 :   static const PRUnichar kSingleQuote  = PRUnichar('\'');
     982                 :   static const PRUnichar kDoubleQuote  = PRUnichar('\"');
     983                 :   static const PRUnichar kComma        = PRUnichar(',');
     984                 : 
     985                 :   const PRUnichar *p_begin, *p_end;
     986               0 :   aFontName.BeginReading(p_begin);
     987               0 :   aFontName.EndReading(p_end);
     988                 : 
     989               0 :   const PRUnichar *p = p_begin;
     990               0 :   const PRUnichar *p_name = nsnull;
     991               0 :   while (p < p_end) {
     992               0 :     while (nsCRT::IsAsciiSpace(*p))
     993               0 :       if (++p == p_end)
     994               0 :         goto insert;
     995                 : 
     996               0 :     p_name = p;
     997               0 :     if (*p == kSingleQuote || *p == kDoubleQuote) {
     998                 :       // quoted font family
     999               0 :       PRUnichar quoteMark = *p;
    1000               0 :       if (++p == p_end)
    1001               0 :         goto insert;
    1002                 : 
    1003                 :       // XXX What about CSS character escapes?
    1004               0 :       while (*p != quoteMark)
    1005               0 :         if (++p == p_end)
    1006               0 :           goto insert;
    1007                 : 
    1008               0 :       while (++p != p_end && *p != kComma)
    1009               0 :         /* nothing */ ;
    1010                 : 
    1011                 :     } else {
    1012                 :       // unquoted font family
    1013               0 :       const PRUnichar *nameStart = p;
    1014               0 :       while (++p != p_end && *p != kComma)
    1015                 :         /* nothing */ ;
    1016                 : 
    1017               0 :       nsAutoString family;
    1018               0 :       family = Substring(nameStart, p);
    1019               0 :       family.CompressWhitespace(false, true);
    1020                 : 
    1021                 :       PRUint8 generic;
    1022               0 :       nsFont::GetGenericID(family, &generic);
    1023               0 :       if (generic != kGenericFont_NONE)
    1024                 :         goto insert;
    1025                 :     }
    1026                 : 
    1027               0 :     ++p; // may advance past p_end
    1028                 :   }
    1029                 : 
    1030               0 :   aFontName.Append(NS_LITERAL_STRING(",") + aFallbackFamilies);
    1031               0 :   return;
    1032                 : 
    1033                 : insert:
    1034               0 :   if (p_name) {
    1035               0 :     aFontName.Insert(aFallbackFamilies + NS_LITERAL_STRING(","),
    1036               0 :                      p_name - p_begin);
    1037                 :   }
    1038                 :   else { // whitespace or empty
    1039               0 :     aFontName = aFallbackFamilies;
    1040                 :   }
    1041                 : }
    1042                 : 
    1043                 : // Update the font and rendering context if there is a family change
    1044                 : static void
    1045               0 : SetFontFamily(nsStyleContext*      aStyleContext,
    1046                 :               nsRenderingContext&  aRenderingContext,
    1047                 :               nsFont&              aFont,
    1048                 :               const nsGlyphTable*  aGlyphTable,
    1049                 :               const nsGlyphCode&   aGlyphCode,
    1050                 :               const nsAString&     aDefaultFamily)
    1051                 : {
    1052                 :   const nsAString& family =
    1053               0 :     aGlyphCode.font ? aGlyphTable->FontNameFor(aGlyphCode) : aDefaultFamily;
    1054               0 :   if (! family.Equals(aFont.name)) {
    1055               0 :     aFont.name = family;
    1056               0 :     nsRefPtr<nsFontMetrics> fm;
    1057                 :     aRenderingContext.DeviceContext()->GetMetricsFor(aFont,
    1058               0 :       aStyleContext->GetStyleFont()->mLanguage,
    1059                 :       aStyleContext->PresContext()->GetUserFontSet(),
    1060               0 :       *getter_AddRefs(fm));
    1061               0 :     aRenderingContext.SetFont(fm);
    1062                 :   }
    1063               0 : }
    1064                 : 
    1065               0 : class nsMathMLChar::StretchEnumContext {
    1066                 : public:
    1067               0 :   StretchEnumContext(nsMathMLChar*        aChar,
    1068                 :                      nsPresContext*       aPresContext,
    1069                 :                      nsRenderingContext& aRenderingContext,
    1070                 :                      nsStretchDirection   aStretchDirection,
    1071                 :                      nscoord              aTargetSize,
    1072                 :                      PRUint32             aStretchHint,
    1073                 :                      nsBoundingMetrics&   aStretchedMetrics,
    1074                 :                      const nsAString&     aFamilies,
    1075                 :                      bool&              aGlyphFound)
    1076                 :     : mChar(aChar),
    1077                 :       mPresContext(aPresContext),
    1078                 :       mRenderingContext(aRenderingContext),
    1079                 :       mDirection(aStretchDirection),
    1080                 :       mTargetSize(aTargetSize),
    1081                 :       mStretchHint(aStretchHint),
    1082                 :       mBoundingMetrics(aStretchedMetrics),
    1083                 :       mFamilies(aFamilies),
    1084                 :       mTryVariants(true),
    1085                 :       mTryParts(true),
    1086               0 :       mGlyphFound(aGlyphFound) {}
    1087                 : 
    1088                 :   static bool
    1089                 :   EnumCallback(const nsString& aFamily, bool aGeneric, void *aData);
    1090                 : 
    1091                 : private:
    1092                 :   static bool
    1093                 :   ResolverCallback (const nsAString& aFamily, void *aData);
    1094                 : 
    1095                 :   bool TryVariants(nsGlyphTable* aGlyphTable, const nsAString& aFamily);
    1096                 :   bool TryParts(nsGlyphTable* aGlyphTable, const nsAString& aFamily);
    1097                 : 
    1098                 :   nsMathMLChar* mChar;
    1099                 :   nsPresContext* mPresContext;
    1100                 :   nsRenderingContext& mRenderingContext;
    1101                 :   const nsStretchDirection mDirection;
    1102                 :   const nscoord mTargetSize;
    1103                 :   const PRUint32 mStretchHint;
    1104                 :   nsBoundingMetrics& mBoundingMetrics;
    1105                 :   // Font families to search
    1106                 :   const nsAString& mFamilies;
    1107                 : 
    1108                 : public:
    1109                 :   bool mTryVariants;
    1110                 :   bool mTryParts;
    1111                 : 
    1112                 : private:
    1113                 :   nsAutoTArray<nsGlyphTable*,16> mTablesTried;
    1114                 :   nsGlyphTable* mGlyphTable; // for this callback
    1115                 :   bool&       mGlyphFound;
    1116                 : };
    1117                 : 
    1118                 : 
    1119                 : // 2. See if there are any glyphs of the appropriate size.
    1120                 : // Returns true if the size is OK, false to keep searching.
    1121                 : // Always updates the char if a better match is found.
    1122                 : bool
    1123               0 : nsMathMLChar::StretchEnumContext::TryVariants(nsGlyphTable*    aGlyphTable,
    1124                 :                                               const nsAString& aFamily)
    1125                 : {
    1126                 :   // Use our stretchy style context now that stretching is in progress
    1127               0 :   nsStyleContext *sc = mChar->mStyleContext;
    1128               0 :   nsFont font = sc->GetStyleFont()->mFont;
    1129                 :   // Ensure mRenderingContext.SetFont will be called:
    1130               0 :   font.name.Truncate();
    1131                 : 
    1132               0 :   bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
    1133               0 :   bool largeop = (NS_STRETCH_LARGEOP & mStretchHint) != 0;
    1134                 :   bool largeopOnly =
    1135               0 :     largeop && (NS_STRETCH_VARIABLE_MASK & mStretchHint) == 0;
    1136               0 :   bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
    1137                 : 
    1138                 :   nscoord bestSize =
    1139                 :     isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
    1140               0 :                : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
    1141               0 :   bool haveBetter = false;
    1142                 : 
    1143                 :   // start at size = 1 (size = 0 is the char at its normal size)
    1144               0 :   PRInt32 size = 1;
    1145                 : #ifdef NOISY_SEARCH
    1146                 :   printf("  searching in %s ...\n",
    1147                 :            NS_LossyConvertUTF16toASCII(aFamily).get());
    1148                 : #endif
    1149                 : 
    1150                 :   nsGlyphCode ch;
    1151               0 :   while ((ch = aGlyphTable->BigOf(mPresContext, mChar, size)).Exists()) {
    1152                 : 
    1153               0 :     SetFontFamily(sc, mRenderingContext, font, aGlyphTable, ch, aFamily);
    1154                 : 
    1155               0 :     NS_ASSERTION(maxWidth || ch.code[0] != mChar->mGlyph.code[0] ||
    1156                 :                  ch.code[1] != mChar->mGlyph.code[1] ||
    1157                 :                  !font.name.Equals(mChar->mFamily),
    1158                 :                  "glyph table incorrectly set -- duplicate found");
    1159                 : 
    1160                 :     nsBoundingMetrics bm = mRenderingContext.GetBoundingMetrics(ch.code,
    1161               0 :                                                                 ch.Length());
    1162                 :     nscoord charSize =
    1163                 :       isVertical ? bm.ascent + bm.descent
    1164               0 :       : bm.rightBearing - bm.leftBearing;
    1165                 : 
    1166               0 :     if (largeopOnly ||
    1167               0 :         IsSizeBetter(charSize, bestSize, mTargetSize, mStretchHint)) {
    1168               0 :       mGlyphFound = true;
    1169               0 :       if (maxWidth) {
    1170                 :         // IsSizeBetter() checked that charSize < maxsize;
    1171                 :         // Leave ascent, descent, and bestsize as these contain maxsize.
    1172               0 :         if (mBoundingMetrics.width < bm.width)
    1173               0 :           mBoundingMetrics.width = bm.width;
    1174               0 :         if (mBoundingMetrics.leftBearing > bm.leftBearing)
    1175               0 :           mBoundingMetrics.leftBearing = bm.leftBearing;
    1176               0 :         if (mBoundingMetrics.rightBearing < bm.rightBearing)
    1177               0 :           mBoundingMetrics.rightBearing = bm.rightBearing;
    1178                 :         // Continue to check other sizes unless largeopOnly
    1179               0 :         haveBetter = largeopOnly;
    1180                 :       }
    1181                 :       else {
    1182               0 :         mBoundingMetrics = bm;
    1183               0 :         haveBetter = true;
    1184               0 :         bestSize = charSize;
    1185               0 :         mChar->mGlyphTable = aGlyphTable;
    1186               0 :         mChar->mGlyph = ch;
    1187               0 :         mChar->mFamily = font.name;
    1188                 :       }
    1189                 : #ifdef NOISY_SEARCH
    1190                 :       printf("    size:%d Current best\n", size);
    1191                 : #endif
    1192                 :     }
    1193                 :     else {
    1194                 : #ifdef NOISY_SEARCH
    1195                 :       printf("    size:%d Rejected!\n", size);
    1196                 : #endif
    1197               0 :       if (haveBetter)
    1198               0 :         break; // Not making an futher progress, stop searching
    1199                 :     }
    1200                 : 
    1201                 :     // if largeopOnly is set, break now
    1202               0 :     if (largeopOnly) break;
    1203               0 :     ++size;
    1204                 :   }
    1205                 : 
    1206                 :   return haveBetter &&
    1207               0 :     (largeopOnly || IsSizeOK(mPresContext, bestSize, mTargetSize, mStretchHint));
    1208                 : }
    1209                 : 
    1210                 : // 3. Build by parts.
    1211                 : // Returns true if the size is OK, false to keep searching.
    1212                 : // Always updates the char if a better match is found.
    1213                 : bool
    1214               0 : nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable*    aGlyphTable,
    1215                 :                                            const nsAString& aFamily)
    1216                 : {
    1217               0 :   if (!aGlyphTable->HasPartsOf(mPresContext, mChar))
    1218               0 :     return false; // to next table
    1219                 : 
    1220                 :   // See if this is a composite character /////////////////////////////////////
    1221               0 :   if (aGlyphTable->IsComposite(mPresContext, mChar)) {
    1222                 :     // let the child chars do the job
    1223               0 :     nsBoundingMetrics compositeSize;
    1224                 :     nsresult rv =
    1225                 :       mChar->ComposeChildren(mPresContext, mRenderingContext, aGlyphTable,
    1226               0 :                              mTargetSize, compositeSize, mStretchHint);
    1227                 : #ifdef NOISY_SEARCH
    1228                 :     printf("    Composing %d chars in font %s %s!\n",
    1229                 :            aGlyphTable->ChildCountOf(mPresContext, mChar),
    1230                 :            NS_LossyConvertUTF16toASCII(fontName).get(),
    1231                 :            NS_SUCCEEDED(rv)? "OK" : "Rejected");
    1232                 : #endif
    1233               0 :     if (NS_FAILED(rv))
    1234               0 :       return false; // to next table
    1235                 : 
    1236                 :     // all went well, painting will be delegated from now on to children
    1237               0 :     mChar->mGlyph = kNullGlyph; // this will tell paint to build by parts
    1238               0 :     mGlyphFound = true;
    1239               0 :     mChar->mGlyphTable = aGlyphTable;
    1240               0 :     mBoundingMetrics = compositeSize;
    1241               0 :     return true; // no more searching
    1242                 :   }
    1243                 : 
    1244                 :   // See if the parts of this table fit in the desired space //////////////////
    1245                 : 
    1246                 :   // Use our stretchy style context now that stretching is in progress
    1247               0 :   nsFont font = mChar->mStyleContext->GetStyleFont()->mFont;
    1248                 :   // Ensure mRenderingContext.SetFont will be called:
    1249               0 :   font.name.Truncate();
    1250                 : 
    1251                 :   // Compute the bounding metrics of all partial glyphs
    1252                 :   nsGlyphCode chdata[4];
    1253               0 :   nsBoundingMetrics bmdata[4];
    1254                 :   nscoord sizedata[4];
    1255               0 :   nsGlyphCode glue = aGlyphTable->GlueOf(mPresContext, mChar);
    1256                 : 
    1257               0 :   bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
    1258               0 :   bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
    1259                 : 
    1260               0 :   for (PRInt32 i = 0; i < 4; i++) {
    1261                 :     nsGlyphCode ch;
    1262               0 :     switch (i) {
    1263               0 :     case 0: ch = aGlyphTable->TopOf(mPresContext, mChar);    break;
    1264               0 :     case 1: ch = aGlyphTable->MiddleOf(mPresContext, mChar); break;
    1265               0 :     case 2: ch = aGlyphTable->BottomOf(mPresContext, mChar); break;
    1266               0 :     case 3: ch = glue;                                       break;
    1267                 :     }
    1268                 :     // empty slots are filled with the glue if it is not null
    1269               0 :     if (!ch.Exists()) ch = glue;
    1270               0 :     chdata[i] = ch;
    1271               0 :     if (!ch.Exists()) {
    1272                 :       // Null glue indicates that a rule will be drawn, which can stretch to
    1273                 :       // fill any space.  Leave bounding metrics at 0.
    1274               0 :       sizedata[i] = mTargetSize;
    1275                 :     }
    1276                 :     else {
    1277                 :       SetFontFamily(mChar->mStyleContext, mRenderingContext,
    1278               0 :                     font, aGlyphTable, ch, aFamily);
    1279                 :       nsBoundingMetrics bm = mRenderingContext.GetBoundingMetrics(ch.code,
    1280               0 :                                                                   ch.Length());
    1281                 : 
    1282                 :       // TODO: For the generic Unicode table, ideally we should check that the
    1283                 :       // glyphs are actually found and that they each come from the same
    1284                 :       // font.
    1285               0 :       bmdata[i] = bm;
    1286                 :       sizedata[i] = isVertical ? bm.ascent + bm.descent
    1287               0 :                                : bm.rightBearing - bm.leftBearing;
    1288                 :     }
    1289                 :   }
    1290                 : 
    1291                 :   // Build by parts if we have successfully computed the
    1292                 :   // bounding metrics of all parts.
    1293                 :   nscoord computedSize = ComputeSizeFromParts(mPresContext, chdata, sizedata,
    1294               0 :                                               mTargetSize);
    1295                 : 
    1296                 :   nscoord currentSize =
    1297                 :     isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
    1298               0 :                : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
    1299                 : 
    1300               0 :   if (!IsSizeBetter(computedSize, currentSize, mTargetSize, mStretchHint)) {
    1301                 : #ifdef NOISY_SEARCH
    1302                 :     printf("    Font %s Rejected!\n",
    1303                 :            NS_LossyConvertUTF16toASCII(fontName).get());
    1304                 : #endif
    1305               0 :     return false; // to next table
    1306                 :   }
    1307                 : 
    1308                 : #ifdef NOISY_SEARCH
    1309                 :   printf("    Font %s Current best!\n",
    1310                 :          NS_LossyConvertUTF16toASCII(fontName).get());
    1311                 : #endif
    1312                 : 
    1313                 :   // The computed size is the best we have found so far...
    1314                 :   // now is the time to compute and cache our bounding metrics
    1315               0 :   if (isVertical) {
    1316                 :     PRInt32 i;
    1317                 :     nscoord lbearing;
    1318                 :     nscoord rbearing;
    1319                 :     nscoord width;
    1320               0 :     if (maxWidth) {
    1321               0 :       lbearing = mBoundingMetrics.leftBearing;
    1322               0 :       rbearing = mBoundingMetrics.rightBearing;
    1323               0 :       width = mBoundingMetrics.width;
    1324               0 :       i = 0;
    1325                 :     }
    1326                 :     else {
    1327               0 :       lbearing = bmdata[0].leftBearing;
    1328               0 :       rbearing = bmdata[0].rightBearing;
    1329               0 :       width = bmdata[0].width;
    1330               0 :       i = 1;
    1331                 :     }
    1332               0 :     for (; i < 4; i++) {
    1333               0 :       const nsBoundingMetrics& bm = bmdata[i];
    1334               0 :       if (width < bm.width) width = bm.width;
    1335               0 :       if (lbearing > bm.leftBearing) lbearing = bm.leftBearing;
    1336               0 :       if (rbearing < bm.rightBearing) rbearing = bm.rightBearing;
    1337                 :     }
    1338               0 :     mBoundingMetrics.width = width;
    1339                 :     // When maxWidth, updating ascent and descent indicates that no characters
    1340                 :     // larger than this character's minimum size need to be checked as they
    1341                 :     // will not be used.
    1342               0 :     mBoundingMetrics.ascent = bmdata[0].ascent; // not used except with descent for height
    1343               0 :     mBoundingMetrics.descent = computedSize - mBoundingMetrics.ascent;
    1344               0 :     mBoundingMetrics.leftBearing = lbearing;
    1345               0 :     mBoundingMetrics.rightBearing = rbearing;
    1346                 :   }
    1347                 :   else {
    1348               0 :     nscoord ascent = bmdata[0].ascent;
    1349               0 :     nscoord descent = bmdata[0].descent;
    1350               0 :     for (PRInt32 i = 1; i < 4; i++) {
    1351               0 :       const nsBoundingMetrics& bm = bmdata[i];
    1352               0 :       if (ascent < bm.ascent) ascent = bm.ascent;
    1353               0 :       if (descent < bm.descent) descent = bm.descent;
    1354                 :     }
    1355               0 :     mBoundingMetrics.width = computedSize;
    1356               0 :     mBoundingMetrics.ascent = ascent;
    1357               0 :     mBoundingMetrics.descent = descent;
    1358               0 :     mBoundingMetrics.leftBearing = 0;
    1359               0 :     mBoundingMetrics.rightBearing = computedSize;
    1360                 :   }
    1361               0 :   mGlyphFound = true;
    1362               0 :   if (maxWidth)
    1363               0 :     return false; // Continue to check other sizes
    1364                 : 
    1365                 :   // reset
    1366               0 :   mChar->mGlyph = kNullGlyph; // this will tell paint to build by parts
    1367               0 :   mChar->mGlyphTable = aGlyphTable;
    1368               0 :   mChar->mFamily = aFamily;
    1369                 : 
    1370               0 :   return IsSizeOK(mPresContext, computedSize, mTargetSize, mStretchHint);
    1371                 : }
    1372                 : 
    1373                 : // This is only called for glyph table corresponding to a family that exists.
    1374                 : // See if the table has a glyph that matches the container
    1375                 : bool
    1376               0 : nsMathMLChar::StretchEnumContext::ResolverCallback (const nsAString& aFamily,
    1377                 :                                                     void *aData)
    1378                 : {
    1379               0 :   StretchEnumContext* context = static_cast<StretchEnumContext*>(aData);
    1380               0 :   nsGlyphTable* glyphTable = context->mGlyphTable;
    1381                 : 
    1382                 :   // Only try this table once.
    1383               0 :   context->mTablesTried.AppendElement(glyphTable);
    1384                 : 
    1385                 :   // If the unicode table is being used, then search all font families.  If a
    1386                 :   // special table is being used then the font in this family should have the
    1387                 :   // specified glyphs.
    1388                 :   const nsAString& family = glyphTable == &gGlyphTableList->mUnicodeTable ?
    1389               0 :     context->mFamilies : aFamily;
    1390                 : 
    1391               0 :   if(context->mTryVariants) {
    1392               0 :     bool isOK = context->TryVariants(glyphTable, family);
    1393               0 :     if (isOK)
    1394               0 :       return false; // no need to continue
    1395                 :   }
    1396                 : 
    1397               0 :   if(context->mTryParts) {
    1398               0 :     bool isOK = context->TryParts(glyphTable, family);
    1399               0 :     if (isOK)
    1400               0 :       return false; // no need to continue
    1401                 :   }
    1402               0 :   return true;
    1403                 : }
    1404                 : 
    1405                 : // This is called for each family, whether it exists or not
    1406                 : bool
    1407               0 : nsMathMLChar::StretchEnumContext::EnumCallback(const nsString& aFamily,
    1408                 :                                                bool aGeneric, void *aData)
    1409                 : {
    1410               0 :   StretchEnumContext* context = static_cast<StretchEnumContext*>(aData);
    1411                 : 
    1412                 :   // See if there is a special table for the family, but always use the
    1413                 :   // Unicode table for generic fonts.
    1414                 :   nsGlyphTable* glyphTable = aGeneric ?
    1415               0 :     &gGlyphTableList->mUnicodeTable : gGlyphTableList->GetGlyphTableFor(aFamily);
    1416                 : 
    1417               0 :   if (context->mTablesTried.Contains(glyphTable))
    1418               0 :     return true; // already tried this one
    1419                 : 
    1420               0 :   context->mGlyphTable = glyphTable;
    1421                 : 
    1422               0 :   if (aGeneric)
    1423               0 :     return ResolverCallback(aFamily, aData);
    1424                 : 
    1425                 :   bool aborted;
    1426               0 :   gfxPlatform *pf = gfxPlatform::GetPlatform();
    1427                 :   nsresult rv =
    1428               0 :     pf->ResolveFontName(aFamily, ResolverCallback, aData, aborted);
    1429               0 :   return NS_SUCCEEDED(rv) && !aborted; // true means continue
    1430                 : }
    1431                 : 
    1432                 : nsresult
    1433               0 : nsMathMLChar::StretchInternal(nsPresContext*           aPresContext,
    1434                 :                               nsRenderingContext&     aRenderingContext,
    1435                 :                               nsStretchDirection&      aStretchDirection,
    1436                 :                               const nsBoundingMetrics& aContainerSize,
    1437                 :                               nsBoundingMetrics&       aDesiredStretchSize,
    1438                 :                               PRUint32                 aStretchHint,
    1439                 :                               // These are currently only used when
    1440                 :                               // aStretchHint & NS_STRETCH_MAXWIDTH:
    1441                 :                               float                    aMaxSize,
    1442                 :                               bool                     aMaxSizeIsAbsolute)
    1443                 : {
    1444                 :   // if we have been called before, and we didn't actually stretch, our
    1445                 :   // direction may have been set to NS_STRETCH_DIRECTION_UNSUPPORTED.
    1446                 :   // So first set our direction back to its instrinsic value
    1447               0 :   nsStretchDirection direction = nsMathMLOperators::GetStretchyDirection(mData);
    1448                 : 
    1449                 :   // Set default font and get the default bounding metrics
    1450                 :   // mStyleContext is a leaf context used only when stretching happens.
    1451                 :   // For the base size, the default font should come from the parent context
    1452               0 :   nsFont font = mStyleContext->GetParent()->GetStyleFont()->mFont;
    1453                 : 
    1454                 :   // Override with specific fonts if applicable for this character
    1455               0 :   nsAutoString families;
    1456               0 :   if (GetFontExtensionPref(mData[0], eExtension_base, families)) {
    1457               0 :     font.name = families;
    1458                 :   }
    1459                 : 
    1460                 :   // Don't modify this nsMathMLChar when doing GetMaxWidth()
    1461               0 :   bool maxWidth = (NS_STRETCH_MAXWIDTH & aStretchHint) != 0;
    1462               0 :   if (!maxWidth) {
    1463                 :     // Record the families in case there is no stretch.  But don't bother
    1464                 :     // storing families when they are just those from the StyleContext.
    1465               0 :     mFamily = families;
    1466                 :   }
    1467                 : 
    1468               0 :   nsRefPtr<nsFontMetrics> fm;
    1469                 :   aRenderingContext.DeviceContext()->GetMetricsFor(font,
    1470               0 :     mStyleContext->GetStyleFont()->mLanguage,
    1471               0 :     aPresContext->GetUserFontSet(), *getter_AddRefs(fm));
    1472               0 :   aRenderingContext.SetFont(fm);
    1473                 :   aDesiredStretchSize =
    1474               0 :     aRenderingContext.GetBoundingMetrics(mData.get(), PRUint32(mData.Length()));
    1475                 : 
    1476               0 :   if (!maxWidth) {
    1477               0 :     mUnscaledAscent = aDesiredStretchSize.ascent;
    1478                 :   }
    1479                 : 
    1480                 :   ////////////////////////////////////////////////////////////////////////////////////
    1481                 :   // 1. Check the common situations where stretching is not actually needed
    1482                 :   ////////////////////////////////////////////////////////////////////////////////////
    1483                 : 
    1484                 :   // quick return if there is nothing special about this char
    1485               0 :   if ((aStretchDirection != direction &&
    1486                 :        aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT) ||
    1487                 :       (aStretchHint & ~NS_STRETCH_MAXWIDTH) == NS_STRETCH_NONE) {
    1488               0 :     mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
    1489               0 :     return NS_OK;
    1490                 :   }
    1491                 : 
    1492                 :   // if no specified direction, attempt to stretch in our preferred direction
    1493               0 :   if (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT) {
    1494               0 :     aStretchDirection = direction;
    1495                 :   }
    1496                 : 
    1497                 :   // see if this is a particular largeop or largeopOnly request
    1498               0 :   bool largeop = (NS_STRETCH_LARGEOP & aStretchHint) != 0;
    1499               0 :   bool stretchy = (NS_STRETCH_VARIABLE_MASK & aStretchHint) != 0;
    1500               0 :   bool largeopOnly = largeop && !stretchy;
    1501                 : 
    1502               0 :   bool isVertical = (direction == NS_STRETCH_DIRECTION_VERTICAL);
    1503                 : 
    1504                 :   nscoord targetSize =
    1505                 :     isVertical ? aContainerSize.ascent + aContainerSize.descent
    1506               0 :     : aContainerSize.rightBearing - aContainerSize.leftBearing;
    1507                 : 
    1508               0 :   if (maxWidth) {
    1509                 :     // See if it is only necessary to consider glyphs up to some maximum size.
    1510                 :     // Set the current height to the maximum size, and set aStretchHint to
    1511                 :     // NS_STRETCH_SMALLER if the size is variable, so that only smaller sizes
    1512                 :     // are considered.  targetSize from GetMaxWidth() is 0.
    1513               0 :     if (stretchy) {
    1514                 :       // variable size stretch - consider all sizes < maxsize
    1515                 :       aStretchHint =
    1516               0 :         (aStretchHint & ~NS_STRETCH_VARIABLE_MASK) | NS_STRETCH_SMALLER;
    1517                 :     }
    1518                 : 
    1519                 :     // Use NS_MATHML_DELIMITER_FACTOR to allow some slightly larger glyphs as
    1520                 :     // maxsize is not enforced exactly.
    1521               0 :     if (aMaxSize == NS_MATHML_OPERATOR_SIZE_INFINITY) {
    1522               0 :       aDesiredStretchSize.ascent = nscoord_MAX;
    1523               0 :       aDesiredStretchSize.descent = 0;
    1524                 :     }
    1525                 :     else {
    1526               0 :       nscoord height = aDesiredStretchSize.ascent + aDesiredStretchSize.descent;
    1527               0 :       if (height == 0) {
    1528               0 :         if (aMaxSizeIsAbsolute) {
    1529                 :           aDesiredStretchSize.ascent =
    1530               0 :             NSToCoordRound(aMaxSize / NS_MATHML_DELIMITER_FACTOR);
    1531               0 :           aDesiredStretchSize.descent = 0;
    1532                 :         }
    1533                 :         // else: leave height as 0
    1534                 :       }
    1535                 :       else {
    1536               0 :         float scale = aMaxSizeIsAbsolute ? aMaxSize / height : aMaxSize;
    1537               0 :         scale /= NS_MATHML_DELIMITER_FACTOR;
    1538                 :         aDesiredStretchSize.ascent =
    1539               0 :           NSToCoordRound(scale * aDesiredStretchSize.ascent);
    1540                 :         aDesiredStretchSize.descent =
    1541               0 :           NSToCoordRound(scale * aDesiredStretchSize.descent);
    1542                 :       }
    1543                 :     }
    1544                 :   }
    1545                 : 
    1546               0 :   nsBoundingMetrics initialSize = aDesiredStretchSize;
    1547                 :   nscoord charSize =
    1548                 :     isVertical ? initialSize.ascent + initialSize.descent
    1549               0 :     : initialSize.rightBearing - initialSize.leftBearing;
    1550                 : 
    1551               0 :   bool done = (mGlyphTable ? false : true);
    1552                 : 
    1553               0 :   if (!done && !maxWidth && !largeop) {
    1554                 :     // Doing Stretch() not GetMaxWidth(),
    1555                 :     // and not a largeop in display mode; we're done if size fits
    1556               0 :     if ((targetSize <= 0) || 
    1557                 :         ((isVertical && charSize >= targetSize) ||
    1558               0 :          IsSizeOK(aPresContext, charSize, targetSize, aStretchHint)))
    1559               0 :       done = true;
    1560                 :   }
    1561                 : 
    1562                 :   ////////////////////////////////////////////////////////////////////////////////////
    1563                 :   // 2/3. Search for a glyph or set of part glyphs of appropriate size
    1564                 :   ////////////////////////////////////////////////////////////////////////////////////
    1565                 : 
    1566               0 :   bool glyphFound = false;
    1567               0 :   nsAutoString cssFamilies;
    1568                 : 
    1569               0 :   if (!done) {
    1570               0 :     font = mStyleContext->GetStyleFont()->mFont;
    1571               0 :     cssFamilies = font.name;
    1572                 :   }
    1573                 : 
    1574                 :   // See if there are preferred fonts for the variants of this char
    1575               0 :   if (!done && GetFontExtensionPref(mData[0], eExtension_variants, families)) {
    1576               0 :     font.name = families;
    1577                 : 
    1578                 :     StretchEnumContext enumData(this, aPresContext, aRenderingContext,
    1579                 :                                 aStretchDirection, targetSize, aStretchHint,
    1580               0 :                                 aDesiredStretchSize, font.name, glyphFound);
    1581               0 :     enumData.mTryParts = false;
    1582                 : 
    1583               0 :     done = !font.EnumerateFamilies(StretchEnumContext::EnumCallback, &enumData);
    1584                 :   }
    1585                 : 
    1586                 :   // See if there are preferred fonts for the parts of this char
    1587               0 :   if (!done && !largeopOnly
    1588               0 :       && GetFontExtensionPref(mData[0], eExtension_parts, families)) {
    1589               0 :     font.name = families;
    1590                 : 
    1591                 :     StretchEnumContext enumData(this, aPresContext, aRenderingContext,
    1592                 :                                 aStretchDirection, targetSize, aStretchHint,
    1593               0 :                                 aDesiredStretchSize, font.name, glyphFound);
    1594               0 :     enumData.mTryVariants = false;
    1595                 : 
    1596               0 :     done = !font.EnumerateFamilies(StretchEnumContext::EnumCallback, &enumData);
    1597                 :   }
    1598                 : 
    1599               0 :   if (!done) { // normal case
    1600                 :     // Use the css font-family but add preferred fallback fonts.
    1601               0 :     font.name = cssFamilies;
    1602               0 :     NS_NAMED_LITERAL_CSTRING(defaultKey, "font.mathfont-family");
    1603               0 :     nsAdoptingString fallbackFonts = Preferences::GetString(defaultKey.get());
    1604               0 :     if (!fallbackFonts.IsEmpty()) {
    1605               0 :       AddFallbackFonts(font.name, fallbackFonts);
    1606                 :     }
    1607                 : 
    1608                 : #ifdef NOISY_SEARCH
    1609                 :     printf("Searching in "%s" for a glyph of appropriate size for: 0x%04X:%c\n",
    1610                 :            font.name, mData[0], mData[0]&0x00FF);
    1611                 : #endif
    1612                 :     StretchEnumContext enumData(this, aPresContext, aRenderingContext,
    1613                 :                                 aStretchDirection, targetSize, aStretchHint,
    1614               0 :                                 aDesiredStretchSize, font.name, glyphFound);
    1615               0 :     enumData.mTryParts = !largeopOnly;
    1616                 : 
    1617               0 :     font.EnumerateFamilies(StretchEnumContext::EnumCallback, &enumData);
    1618                 :   }
    1619                 : 
    1620               0 :   if (!maxWidth) {
    1621                 :     // Now, we know how we are going to draw the char. Update the member
    1622                 :     // variables accordingly.
    1623               0 :     mDrawNormal = !glyphFound;
    1624               0 :     mUnscaledAscent = aDesiredStretchSize.ascent;
    1625                 :   }
    1626                 :     
    1627                 :   // stretchy character
    1628               0 :   if (stretchy) {
    1629               0 :     if (isVertical) {
    1630                 :       float scale =
    1631                 :         float(aContainerSize.ascent + aContainerSize.descent) /
    1632               0 :         (aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
    1633               0 :       if (!largeop || scale > 1.0) {
    1634                 :         // make the character match the desired height.
    1635               0 :         if (!maxWidth) {
    1636               0 :           mScaleY *= scale;
    1637                 :         }
    1638               0 :         aDesiredStretchSize.ascent *= scale;
    1639               0 :         aDesiredStretchSize.descent *= scale;
    1640                 :       }
    1641                 :     } else {
    1642                 :       float scale =
    1643                 :         float(aContainerSize.rightBearing - aContainerSize.leftBearing) /
    1644               0 :         (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing);
    1645               0 :       if (!largeop || scale > 1.0) {
    1646                 :         // make the character match the desired width.
    1647               0 :         if (!maxWidth) {
    1648               0 :           mScaleX *= scale;
    1649                 :         }
    1650               0 :         aDesiredStretchSize.leftBearing *= scale;
    1651               0 :         aDesiredStretchSize.rightBearing *= scale;
    1652               0 :         aDesiredStretchSize.width *= scale;
    1653                 :       }
    1654                 :     }
    1655                 :   }
    1656                 : 
    1657                 :   // We do not have a char variant for this largeop in display mode, so we
    1658                 :   // apply a scale transform to the base char.
    1659               0 :   if (!glyphFound && largeop) {
    1660                 :     float scale;
    1661               0 :     float largeopFactor = M_SQRT2;
    1662                 : 
    1663                 :     // increase the width if it is not largeopFactor times larger
    1664                 :     // than the initial one.
    1665               0 :     if ((aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing) <
    1666                 :         largeopFactor * (initialSize.rightBearing - initialSize.leftBearing)) {
    1667                 :       scale = (largeopFactor *
    1668                 :                (initialSize.rightBearing - initialSize.leftBearing)) /
    1669               0 :         (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing);
    1670               0 :       if (!maxWidth) {
    1671               0 :         mScaleX *= scale;
    1672                 :       }
    1673               0 :       aDesiredStretchSize.leftBearing *= scale;
    1674               0 :       aDesiredStretchSize.rightBearing *= scale;
    1675               0 :       aDesiredStretchSize.width *= scale;
    1676                 :     }
    1677                 : 
    1678                 :     // increase the height if it is not largeopFactor times larger
    1679                 :     // than the initial one.
    1680               0 :     if (NS_STRETCH_INTEGRAL & aStretchHint) {
    1681                 :       // integrals are drawn taller
    1682               0 :       largeopFactor = 2.0;
    1683                 :     }
    1684               0 :     if ((aDesiredStretchSize.ascent + aDesiredStretchSize.descent) <
    1685                 :         largeopFactor * (initialSize.ascent + initialSize.descent)) {
    1686                 :       scale = (largeopFactor *
    1687                 :                (initialSize.ascent + initialSize.descent)) /
    1688               0 :         (aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
    1689               0 :       if (!maxWidth) {
    1690               0 :         mScaleY *= scale;
    1691                 :       }
    1692               0 :       aDesiredStretchSize.ascent *= scale;
    1693               0 :       aDesiredStretchSize.descent *= scale;
    1694                 :     }
    1695                 :   }
    1696                 : 
    1697               0 :   return NS_OK;
    1698                 : }
    1699                 : 
    1700                 : nsresult
    1701               0 : nsMathMLChar::Stretch(nsPresContext*           aPresContext,
    1702                 :                       nsRenderingContext&     aRenderingContext,
    1703                 :                       nsStretchDirection       aStretchDirection,
    1704                 :                       const nsBoundingMetrics& aContainerSize,
    1705                 :                       nsBoundingMetrics&       aDesiredStretchSize,
    1706                 :                       PRUint32                 aStretchHint,
    1707                 :                       bool                     aRTL)
    1708                 : {
    1709               0 :   NS_ASSERTION(!(aStretchHint &
    1710                 :                  ~(NS_STRETCH_VARIABLE_MASK | NS_STRETCH_LARGEOP |
    1711                 :                    NS_STRETCH_INTEGRAL)),
    1712                 :                "Unexpected stretch flags");
    1713                 : 
    1714               0 :   mDrawNormal = true;
    1715               0 :   mMirrored = aRTL && nsMathMLOperators::IsMirrorableOperator(mData);
    1716               0 :   mScaleY = mScaleX = 1.0;
    1717               0 :   mDirection = aStretchDirection;
    1718                 :   nsresult rv =
    1719                 :     StretchInternal(aPresContext, aRenderingContext, mDirection,
    1720               0 :                     aContainerSize, aDesiredStretchSize, aStretchHint);
    1721                 : 
    1722                 :   // Record the metrics
    1723               0 :   mBoundingMetrics = aDesiredStretchSize;
    1724                 : 
    1725               0 :   return rv;
    1726                 : }
    1727                 : 
    1728                 : // What happens here is that the StretchInternal algorithm is used but
    1729                 : // modified by passing the NS_STRETCH_MAXWIDTH stretch hint.  That causes
    1730                 : // StretchInternal to return horizontal bounding metrics that are the maximum
    1731                 : // that might be returned from a Stretch.
    1732                 : //
    1733                 : // In order to avoid considering widths of some characters in fonts that will
    1734                 : // not be used for any stretch size, StretchInternal sets the initial height
    1735                 : // to infinity and looks for any characters smaller than this height.  When a
    1736                 : // character built from parts is considered, (it will be used by Stretch for
    1737                 : // any characters greater than its minimum size, so) the height is set to its
    1738                 : // minimum size, so that only widths of smaller subsequent characters are
    1739                 : // considered.
    1740                 : nscoord
    1741               0 : nsMathMLChar::GetMaxWidth(nsPresContext* aPresContext,
    1742                 :                           nsRenderingContext& aRenderingContext,
    1743                 :                           PRUint32 aStretchHint,
    1744                 :                           float aMaxSize, bool aMaxSizeIsAbsolute)
    1745                 : {
    1746               0 :   nsBoundingMetrics bm;
    1747               0 :   nsStretchDirection direction = NS_STRETCH_DIRECTION_VERTICAL;
    1748               0 :   const nsBoundingMetrics container; // zero target size
    1749                 : 
    1750                 :   StretchInternal(aPresContext, aRenderingContext, direction, container,
    1751               0 :                   bm, aStretchHint | NS_STRETCH_MAXWIDTH);
    1752                 : 
    1753               0 :   return NS_MAX(bm.width, bm.rightBearing) - NS_MIN(0, bm.leftBearing);
    1754                 : }
    1755                 : 
    1756                 : nsresult
    1757               0 : nsMathMLChar::ComposeChildren(nsPresContext*      aPresContext,
    1758                 :                               nsRenderingContext& aRenderingContext,
    1759                 :                               nsGlyphTable*        aGlyphTable,
    1760                 :                               nscoord              aTargetSize,
    1761                 :                               nsBoundingMetrics&   aCompositeSize,
    1762                 :                               PRUint32             aStretchHint)
    1763                 : {
    1764               0 :   PRInt32 i = 0;
    1765                 :   nsMathMLChar* child;
    1766               0 :   PRInt32 count = aGlyphTable->ChildCountOf(aPresContext, this);
    1767               0 :   NS_ASSERTION(count, "something is wrong somewhere");
    1768               0 :   if (!count) return NS_ERROR_FAILURE;
    1769                 :   // if we haven't been here before, create the linked list of children now
    1770                 :   // otherwise, use what we have, adding more children as needed or deleting the extra
    1771               0 :   nsMathMLChar* last = this;
    1772               0 :   while ((i < count) && last->mSibling) {
    1773               0 :     i++;
    1774               0 :     last = last->mSibling;
    1775                 :   }
    1776               0 :   while (i < count) {
    1777               0 :     child = new nsMathMLChar(this);
    1778               0 :     last->mSibling = child;
    1779               0 :     last = child;
    1780               0 :     i++;
    1781                 :   }
    1782               0 :   if (last->mSibling) {
    1783               0 :     delete last->mSibling;
    1784               0 :     last->mSibling = nsnull;
    1785                 :   }
    1786                 :   // let children stretch in an equal space
    1787               0 :   nsBoundingMetrics splitSize;
    1788               0 :   if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
    1789               0 :     splitSize.width = aTargetSize / count;
    1790                 :   else {
    1791               0 :     splitSize.ascent = aTargetSize / (count * 2);
    1792               0 :     splitSize.descent = splitSize.ascent;
    1793                 :   }
    1794               0 :   nscoord dx = 0, dy = 0;
    1795               0 :   for (i = 0, child = mSibling; child; child = child->mSibling, i++) {
    1796                 :     // child chars should just inherit our values - which may change between calls...
    1797               0 :     child->mData = mData;
    1798               0 :     child->mDirection = mDirection;
    1799               0 :     child->mStyleContext = mStyleContext;
    1800               0 :     child->mGlyphTable = aGlyphTable; // the child is associated to this table
    1801               0 :     child->mMirrored = mMirrored;
    1802                 :     // there goes the Stretch() ...
    1803               0 :     nsBoundingMetrics childSize;
    1804                 :     nsresult rv = child->Stretch(aPresContext, aRenderingContext, mDirection,
    1805               0 :                                  splitSize, childSize, aStretchHint, mMirrored);
    1806                 :     // check if something went wrong or the child couldn't fit in the alloted space
    1807               0 :     if (NS_FAILED(rv) || (NS_STRETCH_DIRECTION_UNSUPPORTED == child->mDirection)) {
    1808               0 :       delete mSibling; // don't leave a dangling list behind ...
    1809               0 :       mSibling = nsnull;
    1810               0 :       return NS_ERROR_FAILURE;
    1811                 :     }
    1812               0 :     child->SetRect(nsRect(dx, dy, childSize.width, childSize.ascent+childSize.descent));
    1813               0 :     if (0 == i)
    1814               0 :       aCompositeSize = childSize;
    1815                 :     else {
    1816               0 :       if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
    1817               0 :         aCompositeSize += childSize;
    1818                 :       else {
    1819               0 :         aCompositeSize.descent += childSize.ascent + childSize.descent;
    1820               0 :         if (aCompositeSize.leftBearing > childSize.leftBearing)
    1821               0 :           aCompositeSize.leftBearing = childSize.leftBearing;
    1822               0 :         if (aCompositeSize.rightBearing < childSize.rightBearing)
    1823               0 :           aCompositeSize.rightBearing = childSize.rightBearing;
    1824                 :       }
    1825                 :     }
    1826               0 :     if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
    1827               0 :       dx += childSize.width;
    1828                 :     else
    1829               0 :       dy += childSize.ascent + childSize.descent;
    1830                 :   }
    1831               0 :   return NS_OK;
    1832                 : }
    1833                 : 
    1834                 : class nsDisplayMathMLSelectionRect : public nsDisplayItem {
    1835                 : public:
    1836               0 :   nsDisplayMathMLSelectionRect(nsDisplayListBuilder* aBuilder,
    1837                 :                                nsIFrame* aFrame, const nsRect& aRect)
    1838               0 :     : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
    1839               0 :     MOZ_COUNT_CTOR(nsDisplayMathMLSelectionRect);
    1840               0 :   }
    1841                 : #ifdef NS_BUILD_REFCNT_LOGGING
    1842               0 :   virtual ~nsDisplayMathMLSelectionRect() {
    1843               0 :     MOZ_COUNT_DTOR(nsDisplayMathMLSelectionRect);
    1844               0 :   }
    1845                 : #endif
    1846                 : 
    1847                 :   virtual void Paint(nsDisplayListBuilder* aBuilder,
    1848                 :                      nsRenderingContext* aCtx);
    1849               0 :   NS_DISPLAY_DECL_NAME("MathMLSelectionRect", TYPE_MATHML_SELECTION_RECT)
    1850                 : private:
    1851                 :   nsRect    mRect;
    1852                 : };
    1853                 : 
    1854               0 : void nsDisplayMathMLSelectionRect::Paint(nsDisplayListBuilder* aBuilder,
    1855                 :                                          nsRenderingContext* aCtx)
    1856                 : {
    1857                 :   // get color to use for selection from the look&feel object
    1858                 :   nscolor bgColor =
    1859                 :     LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground,
    1860               0 :                           NS_RGB(0, 0, 0));
    1861               0 :   aCtx->SetColor(bgColor);
    1862               0 :   aCtx->FillRect(mRect + ToReferenceFrame());
    1863               0 : }
    1864                 : 
    1865                 : class nsDisplayMathMLCharBackground : public nsDisplayItem {
    1866                 : public:
    1867               0 :   nsDisplayMathMLCharBackground(nsDisplayListBuilder* aBuilder,
    1868                 :                                 nsIFrame* aFrame, const nsRect& aRect,
    1869                 :                                 nsStyleContext* aStyleContext)
    1870               0 :     : nsDisplayItem(aBuilder, aFrame), mStyleContext(aStyleContext), mRect(aRect) {
    1871               0 :     MOZ_COUNT_CTOR(nsDisplayMathMLCharBackground);
    1872               0 :   }
    1873                 : #ifdef NS_BUILD_REFCNT_LOGGING
    1874               0 :   virtual ~nsDisplayMathMLCharBackground() {
    1875               0 :     MOZ_COUNT_DTOR(nsDisplayMathMLCharBackground);
    1876               0 :   }
    1877                 : #endif
    1878                 : 
    1879                 :   virtual void Paint(nsDisplayListBuilder* aBuilder,
    1880                 :                      nsRenderingContext* aCtx);
    1881               0 :   NS_DISPLAY_DECL_NAME("MathMLCharBackground", TYPE_MATHML_CHAR_BACKGROUND)
    1882                 : private:
    1883                 :   nsStyleContext* mStyleContext;
    1884                 :   nsRect          mRect;
    1885                 : };
    1886                 : 
    1887               0 : void nsDisplayMathMLCharBackground::Paint(nsDisplayListBuilder* aBuilder,
    1888                 :                                           nsRenderingContext* aCtx)
    1889                 : {
    1890               0 :   const nsStyleBorder* border = mStyleContext->GetStyleBorder();
    1891               0 :   nsRect rect(mRect + ToReferenceFrame());
    1892                 :   nsCSSRendering::PaintBackgroundWithSC(mFrame->PresContext(), *aCtx, mFrame,
    1893                 :                                         mVisibleRect, rect,
    1894                 :                                         mStyleContext, *border,
    1895               0 :                                         aBuilder->GetBackgroundPaintFlags());
    1896               0 : }
    1897                 : 
    1898                 : class nsDisplayMathMLCharForeground : public nsDisplayItem {
    1899                 : public:
    1900               0 :   nsDisplayMathMLCharForeground(nsDisplayListBuilder* aBuilder,
    1901                 :                                 nsIFrame* aFrame, nsMathMLChar* aChar,
    1902                 :                                                         bool aIsSelected)
    1903               0 :     : nsDisplayItem(aBuilder, aFrame), mChar(aChar), mIsSelected(aIsSelected) {
    1904               0 :     MOZ_COUNT_CTOR(nsDisplayMathMLCharForeground);
    1905               0 :   }
    1906                 : #ifdef NS_BUILD_REFCNT_LOGGING
    1907               0 :   virtual ~nsDisplayMathMLCharForeground() {
    1908               0 :     MOZ_COUNT_DTOR(nsDisplayMathMLCharForeground);
    1909               0 :   }
    1910                 : #endif
    1911                 : 
    1912               0 :   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
    1913               0 :     nsRect rect;
    1914               0 :     mChar->GetRect(rect);
    1915               0 :     nsPoint offset = ToReferenceFrame() + rect.TopLeft();
    1916               0 :     nsBoundingMetrics bm;
    1917               0 :     mChar->GetBoundingMetrics(bm);
    1918                 :     return nsRect(offset.x + bm.leftBearing, offset.y,
    1919               0 :                   bm.rightBearing - bm.leftBearing, bm.ascent + bm.descent);
    1920                 :   }
    1921                 : 
    1922               0 :   virtual void Paint(nsDisplayListBuilder* aBuilder,
    1923                 :                      nsRenderingContext* aCtx)
    1924                 :   {
    1925                 :     mChar->PaintForeground(mFrame->PresContext(), *aCtx,
    1926               0 :                            ToReferenceFrame(), mIsSelected);
    1927               0 :   }
    1928                 : 
    1929               0 :   NS_DISPLAY_DECL_NAME("MathMLCharForeground", TYPE_MATHML_CHAR_FOREGROUND)
    1930                 : 
    1931               0 :   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder)
    1932                 :   {
    1933               0 :     return GetBounds(aBuilder);
    1934                 :   }
    1935                 : 
    1936                 : private:
    1937                 :   nsMathMLChar* mChar;
    1938                 :   bool          mIsSelected;
    1939                 : };
    1940                 : 
    1941                 : #ifdef NS_DEBUG
    1942                 : class nsDisplayMathMLCharDebug : public nsDisplayItem {
    1943                 : public:
    1944                 :   nsDisplayMathMLCharDebug(nsDisplayListBuilder* aBuilder,
    1945                 :                            nsIFrame* aFrame, const nsRect& aRect)
    1946                 :     : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
    1947                 :     MOZ_COUNT_CTOR(nsDisplayMathMLCharDebug);
    1948                 :   }
    1949                 : #ifdef NS_BUILD_REFCNT_LOGGING
    1950               0 :   virtual ~nsDisplayMathMLCharDebug() {
    1951               0 :     MOZ_COUNT_DTOR(nsDisplayMathMLCharDebug);
    1952               0 :   }
    1953                 : #endif
    1954                 : 
    1955                 :   virtual void Paint(nsDisplayListBuilder* aBuilder,
    1956                 :                      nsRenderingContext* aCtx);
    1957               0 :   NS_DISPLAY_DECL_NAME("MathMLCharDebug", TYPE_MATHML_CHAR_DEBUG)
    1958                 : 
    1959                 : private:
    1960                 :   nsRect mRect;
    1961                 : };
    1962                 : 
    1963               0 : void nsDisplayMathMLCharDebug::Paint(nsDisplayListBuilder* aBuilder,
    1964                 :                                      nsRenderingContext* aCtx)
    1965                 : {
    1966                 :   // for visual debug
    1967               0 :   PRIntn skipSides = 0;
    1968               0 :   nsPresContext* presContext = mFrame->PresContext();
    1969               0 :   nsStyleContext* styleContext = mFrame->GetStyleContext();
    1970               0 :   nsRect rect = mRect + ToReferenceFrame();
    1971                 :   nsCSSRendering::PaintBorder(presContext, *aCtx, mFrame,
    1972               0 :                               mVisibleRect, rect, styleContext, skipSides);
    1973                 :   nsCSSRendering::PaintOutline(presContext, *aCtx, mFrame,
    1974               0 :                                mVisibleRect, rect, styleContext);
    1975               0 : }
    1976                 : #endif
    1977                 : 
    1978                 : 
    1979                 : nsresult
    1980               0 : nsMathMLChar::Display(nsDisplayListBuilder*   aBuilder,
    1981                 :                       nsIFrame*               aForFrame,
    1982                 :                       const nsDisplayListSet& aLists,
    1983                 :                       const nsRect*           aSelectedRect)
    1984                 : {
    1985               0 :   nsresult rv = NS_OK;
    1986               0 :   nsStyleContext* parentContext = mStyleContext->GetParent();
    1987               0 :   nsStyleContext* styleContext = mStyleContext;
    1988                 : 
    1989               0 :   if (mDrawNormal) {
    1990                 :     // normal drawing if there is nothing special about this char
    1991                 :     // Set default context to the parent context
    1992               0 :     styleContext = parentContext;
    1993                 :   }
    1994                 : 
    1995               0 :   if (!styleContext->GetStyleVisibility()->IsVisible())
    1996               0 :     return NS_OK;
    1997                 : 
    1998                 :   // if the leaf style context that we use for stretchy chars has a background
    1999                 :   // color we use it -- this feature is mostly used for testing and debugging
    2000                 :   // purposes. Normally, users will set the background on the container frame.
    2001                 :   // paint the selection background -- beware MathML frames overlap a lot
    2002               0 :   if (aSelectedRect && !aSelectedRect->IsEmpty()) {
    2003                 :     rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
    2004               0 :         nsDisplayMathMLSelectionRect(aBuilder, aForFrame, *aSelectedRect));
    2005               0 :     NS_ENSURE_SUCCESS(rv, rv);
    2006                 :   }
    2007               0 :   else if (mRect.width && mRect.height) {
    2008               0 :     const nsStyleBackground* backg = styleContext->GetStyleBackground();
    2009               0 :     if (styleContext != parentContext &&
    2010                 :         NS_GET_A(backg->mBackgroundColor) > 0) {
    2011                 :       rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
    2012               0 :           nsDisplayMathMLCharBackground(aBuilder, aForFrame, mRect, styleContext));
    2013               0 :       NS_ENSURE_SUCCESS(rv, rv);
    2014                 :     }
    2015                 :     //else
    2016                 :     //  our container frame will take care of painting its background
    2017                 : 
    2018                 : #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
    2019                 :     // for visual debug
    2020                 :     rv = aLists.BorderBackground()->AppendToTop(new (aBuilder)
    2021                 :         nsDisplayMathMLCharDebug(aBuilder, aForFrame, mRect));
    2022                 :     NS_ENSURE_SUCCESS(rv, rv);
    2023                 : #endif
    2024                 :   }
    2025                 :   return aLists.Content()->AppendNewToTop(new (aBuilder)
    2026                 :         nsDisplayMathMLCharForeground(aBuilder, aForFrame, this,
    2027               0 :                                       aSelectedRect && !aSelectedRect->IsEmpty()));
    2028                 : }
    2029                 : 
    2030                 : void
    2031               0 : nsMathMLChar::ApplyTransforms(nsRenderingContext& aRenderingContext, nsRect &r)
    2032                 : {
    2033                 :   // apply the transforms
    2034               0 :   if (mMirrored) {
    2035               0 :     aRenderingContext.Translate(r.TopRight());
    2036               0 :     aRenderingContext.Scale(-mScaleX, mScaleY);
    2037                 :   } else {
    2038               0 :     aRenderingContext.Translate(r.TopLeft());
    2039               0 :     aRenderingContext.Scale(mScaleX, mScaleY);
    2040                 :   }
    2041                 : 
    2042                 :   // update the bounding rectangle.
    2043               0 :   r.x = r.y = 0;
    2044               0 :   r.width /= mScaleX;
    2045               0 :   r.height /= mScaleY;
    2046               0 : }
    2047                 : 
    2048                 : void
    2049               0 : nsMathMLChar::PaintForeground(nsPresContext* aPresContext,
    2050                 :                               nsRenderingContext& aRenderingContext,
    2051                 :                               nsPoint aPt,
    2052                 :                               bool aIsSelected)
    2053                 : {
    2054               0 :   nsStyleContext* parentContext = mStyleContext->GetParent();
    2055               0 :   nsStyleContext* styleContext = mStyleContext;
    2056                 : 
    2057               0 :   if (mDrawNormal) {
    2058                 :     // normal drawing if there is nothing special about this char
    2059                 :     // Set default context to the parent context
    2060               0 :     styleContext = parentContext;
    2061                 :   }
    2062                 : 
    2063                 :   // Set color ...
    2064               0 :   nscolor fgColor = styleContext->GetVisitedDependentColor(eCSSProperty_color);
    2065               0 :   if (aIsSelected) {
    2066                 :     // get color to use for selection from the look&feel object
    2067                 :     fgColor = LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground,
    2068               0 :                                     fgColor);
    2069                 :   }
    2070               0 :   aRenderingContext.SetColor(fgColor);
    2071                 : 
    2072               0 :   nsFont theFont(styleContext->GetStyleFont()->mFont);
    2073               0 :   if (! mFamily.IsEmpty()) {
    2074               0 :     theFont.name = mFamily;
    2075                 :   }
    2076               0 :   nsRefPtr<nsFontMetrics> fm;
    2077                 :   aRenderingContext.DeviceContext()->GetMetricsFor(theFont,
    2078               0 :     styleContext->GetStyleFont()->mLanguage,
    2079                 :     aPresContext->GetUserFontSet(),
    2080               0 :     *getter_AddRefs(fm));
    2081               0 :   aRenderingContext.SetFont(fm);
    2082                 : 
    2083               0 :   aRenderingContext.PushState();
    2084               0 :   nsRect r = mRect + aPt;
    2085               0 :   ApplyTransforms(aRenderingContext, r);
    2086                 : 
    2087               0 :   if (mDrawNormal) {
    2088                 :     // normal drawing if there is nothing special about this char ...
    2089                 :     // Grab some metrics to adjust the placements ...
    2090               0 :     PRUint32 len = PRUint32(mData.Length());
    2091                 : //printf("Painting %04X like a normal char\n", mData[0]);
    2092                 : //aRenderingContext.SetColor(NS_RGB(255,0,0));
    2093               0 :     aRenderingContext.DrawString(mData.get(), len, 0, mUnscaledAscent);
    2094                 :   }
    2095                 :   else {
    2096                 :     // Grab some metrics to adjust the placements ...
    2097                 :     // if there is a glyph of appropriate size, paint that glyph
    2098               0 :     if (mGlyph.Exists()) {
    2099                 : //printf("Painting %04X with a glyph of appropriate size\n", mData[0]);
    2100                 : //aRenderingContext.SetColor(NS_RGB(0,0,255));
    2101               0 :       aRenderingContext.DrawString(mGlyph.code, mGlyph.Length(),
    2102               0 :                                    0, mUnscaledAscent);
    2103                 :     }
    2104                 :     else { // paint by parts
    2105                 : //aRenderingContext.SetColor(NS_RGB(0,255,0));
    2106               0 :       if (NS_STRETCH_DIRECTION_VERTICAL == mDirection)
    2107                 :         PaintVertically(aPresContext, aRenderingContext, theFont, styleContext,
    2108               0 :                         mGlyphTable, r);
    2109               0 :       else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
    2110                 :         PaintHorizontally(aPresContext, aRenderingContext, theFont, styleContext,
    2111               0 :                           mGlyphTable, r);
    2112                 :     }
    2113                 :   }
    2114                 : 
    2115               0 :   aRenderingContext.PopState();
    2116               0 : }
    2117                 : 
    2118                 : /* =================================================================================
    2119                 :   And now the helper routines that actually do the job of painting the char by parts
    2120                 :  */
    2121                 : 
    2122                 : class AutoPushClipRect {
    2123                 :   nsRenderingContext& mCtx;
    2124                 : public:
    2125               0 :   AutoPushClipRect(nsRenderingContext& aCtx, const nsRect& aRect)
    2126               0 :     : mCtx(aCtx) {
    2127               0 :     mCtx.PushState();
    2128               0 :     mCtx.IntersectClip(aRect);
    2129               0 :   }
    2130               0 :   ~AutoPushClipRect() {
    2131               0 :     mCtx.PopState();
    2132               0 :   }
    2133                 : };
    2134                 : 
    2135                 : static nsPoint
    2136               0 : SnapToDevPixels(const gfxContext* aThebesContext, PRInt32 aAppUnitsPerGfxUnit,
    2137                 :                 const nsPoint& aPt)
    2138                 : {
    2139               0 :   gfxPoint pt(NSAppUnitsToFloatPixels(aPt.x, aAppUnitsPerGfxUnit),
    2140               0 :               NSAppUnitsToFloatPixels(aPt.y, aAppUnitsPerGfxUnit));
    2141               0 :   pt = aThebesContext->UserToDevice(pt);
    2142               0 :   pt.Round();
    2143               0 :   pt = aThebesContext->DeviceToUser(pt);
    2144                 :   return nsPoint(NSFloatPixelsToAppUnits(pt.x, aAppUnitsPerGfxUnit),
    2145               0 :                  NSFloatPixelsToAppUnits(pt.y, aAppUnitsPerGfxUnit));
    2146                 : }
    2147                 : 
    2148                 : // paint a stretchy char by assembling glyphs vertically
    2149                 : nsresult
    2150               0 : nsMathMLChar::PaintVertically(nsPresContext*      aPresContext,
    2151                 :                               nsRenderingContext& aRenderingContext,
    2152                 :                               nsFont&              aFont,
    2153                 :                               nsStyleContext*      aStyleContext,
    2154                 :                               nsGlyphTable*        aGlyphTable,
    2155                 :                               nsRect&              aRect)
    2156                 : {
    2157                 :   // Get the device pixel size in the vertical direction.
    2158                 :   // (This makes no effort to optimize for non-translation transformations.)
    2159               0 :   nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
    2160                 : 
    2161                 :   // get metrics data to be re-used later
    2162               0 :   PRInt32 i = 0;
    2163                 :   nsGlyphCode ch, chdata[4];
    2164               0 :   nsBoundingMetrics bmdata[4];
    2165                 :   PRInt32 glue, bottom;
    2166               0 :   nsGlyphCode chGlue = aGlyphTable->GlueOf(aPresContext, this);
    2167               0 :   for (PRInt32 j = 0; j < 4; ++j) {
    2168               0 :     switch (j) {
    2169                 :       case 0:
    2170               0 :         ch = aGlyphTable->TopOf(aPresContext, this);
    2171               0 :         break;
    2172                 :       case 1:
    2173               0 :         ch = aGlyphTable->MiddleOf(aPresContext, this);
    2174               0 :         if (!ch.Exists())
    2175               0 :           continue; // no middle
    2176               0 :         break;
    2177                 :       case 2:
    2178               0 :         ch = aGlyphTable->BottomOf(aPresContext, this);
    2179               0 :         bottom = i;
    2180               0 :         break;
    2181                 :       case 3:
    2182               0 :         ch = chGlue;
    2183               0 :         glue = i;
    2184               0 :         break;
    2185                 :     }
    2186                 :     // empty slots are filled with the glue if it is not null
    2187               0 :     if (!ch.Exists()) ch = chGlue;
    2188                 :     // if (!ch.Exists()) glue is null, leave bounding metrics at 0
    2189               0 :     if (ch.Exists()) {
    2190                 :       SetFontFamily(aStyleContext, aRenderingContext,
    2191               0 :                     aFont, aGlyphTable, ch, mFamily);
    2192               0 :       bmdata[i] = aRenderingContext.GetBoundingMetrics(ch.code, ch.Length());
    2193                 :     }
    2194               0 :     chdata[i] = ch;
    2195               0 :     ++i;
    2196                 :   }
    2197               0 :   nscoord dx = aRect.x;
    2198                 :   nscoord offset[3], start[3], end[3];
    2199               0 :   nsRefPtr<gfxContext> ctx = aRenderingContext.ThebesContext();
    2200               0 :   for (i = 0; i <= bottom; ++i) {
    2201               0 :     ch = chdata[i];
    2202               0 :     const nsBoundingMetrics& bm = bmdata[i];
    2203                 :     nscoord dy;
    2204               0 :     if (0 == i) { // top
    2205               0 :       dy = aRect.y + bm.ascent;
    2206                 :     }
    2207               0 :     else if (bottom == i) { // bottom
    2208               0 :       dy = aRect.y + aRect.height - bm.descent;
    2209                 :     }
    2210                 :     else { // middle
    2211               0 :       dy = aRect.y + bm.ascent + (aRect.height - (bm.ascent + bm.descent))/2;
    2212                 :     }
    2213                 :     // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
    2214                 :     // Do this now so that we can get the other dimensions right.
    2215                 :     // (This may not achieve much with non-rectangular transformations.)
    2216               0 :     dy = SnapToDevPixels(ctx, oneDevPixel, nsPoint(dx, dy)).y;
    2217                 :     // abcissa passed to DrawString
    2218               0 :     offset[i] = dy;
    2219                 :     // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
    2220                 :     // pixel, so the bm values can include 1 row of faint pixels on each edge.
    2221                 :     // Don't rely on this pixel as it can look like a gap.
    2222               0 :     start[i] = dy - bm.ascent + oneDevPixel; // top join
    2223               0 :     end[i] = dy + bm.descent - oneDevPixel; // bottom join
    2224                 :   }
    2225                 : 
    2226                 :   // If there are overlaps, then join at the mid point
    2227               0 :   for (i = 0; i < bottom; ++i) {
    2228               0 :     if (end[i] > start[i+1]) {
    2229               0 :       end[i] = (end[i] + start[i+1]) / 2;
    2230               0 :       start[i+1] = end[i];
    2231                 :     }
    2232                 :   }
    2233                 : 
    2234               0 :   nsRect unionRect = aRect;
    2235               0 :   unionRect.x += mBoundingMetrics.leftBearing;
    2236                 :   unionRect.width =
    2237               0 :     mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
    2238               0 :   unionRect.Inflate(oneDevPixel, oneDevPixel);
    2239                 : 
    2240                 :   /////////////////////////////////////
    2241                 :   // draw top, middle, bottom
    2242               0 :   for (i = 0; i <= bottom; ++i) {
    2243               0 :     ch = chdata[i];
    2244                 :     // glue can be null, and other parts could have been set to glue
    2245               0 :     if (ch.Exists()) {
    2246                 : #ifdef SHOW_BORDERS
    2247                 :       // bounding box of the part
    2248                 :       aRenderingContext.SetColor(NS_RGB(0,0,0));
    2249                 :       aRenderingContext.DrawRect(nsRect(dx,start[i],aRect.width+30*(i+1),end[i]-start[i]));
    2250                 : #endif
    2251               0 :       nscoord dy = offset[i];
    2252                 :       // Draw a glyph in a clipped area so that we don't have hairy chars
    2253                 :       // pending outside
    2254               0 :       nsRect clipRect = unionRect;
    2255                 :       // Clip at the join to get a solid edge (without overlap or gap), when
    2256                 :       // this won't change the glyph too much.  If the glyph is too small to
    2257                 :       // clip then we'll overlap rather than have a gap.
    2258               0 :       nscoord height = bmdata[i].ascent + bmdata[i].descent;
    2259               0 :       if (ch == chGlue ||
    2260                 :           height * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
    2261               0 :         if (0 == i) { // top
    2262               0 :           clipRect.height = end[i] - clipRect.y;
    2263                 :         }
    2264               0 :         else if (bottom == i) { // bottom
    2265               0 :           clipRect.height -= start[i] - clipRect.y;
    2266               0 :           clipRect.y = start[i];
    2267                 :         }
    2268                 :         else { // middle
    2269               0 :           clipRect.y = start[i];
    2270               0 :           clipRect.height = end[i] - start[i];
    2271                 :         }
    2272                 :       }
    2273               0 :       if (!clipRect.IsEmpty()) {
    2274               0 :         AutoPushClipRect clip(aRenderingContext, clipRect);
    2275                 :         SetFontFamily(aStyleContext, aRenderingContext,
    2276               0 :                       aFont, aGlyphTable, ch, mFamily);
    2277               0 :         aRenderingContext.DrawString(ch.code, ch.Length(), dx, dy);
    2278                 :       }
    2279                 :     }
    2280                 :   }
    2281                 : 
    2282                 :   ///////////////
    2283                 :   // fill the gap between top and middle, and between middle and bottom.
    2284               0 :   if (!chGlue.Exists()) { // null glue : draw a rule
    2285                 :     // figure out the dimensions of the rule to be drawn :
    2286                 :     // set lbearing to rightmost lbearing among the two current successive parts.
    2287                 :     // set rbearing to leftmost rbearing among the two current successive parts.
    2288                 :     // this not only satisfies the convention used for over/underbraces
    2289                 :     // in TeX, but also takes care of broken fonts like the stretchy integral
    2290                 :     // in Symbol for small font sizes in unix.
    2291                 :     nscoord lbearing, rbearing;
    2292               0 :     PRInt32 first = 0, last = 1;
    2293               0 :     while (last <= bottom) {
    2294               0 :       if (chdata[last].Exists()) {
    2295               0 :         lbearing = bmdata[last].leftBearing;
    2296               0 :         rbearing = bmdata[last].rightBearing;
    2297               0 :         if (chdata[first].Exists()) {
    2298               0 :           if (lbearing < bmdata[first].leftBearing)
    2299               0 :             lbearing = bmdata[first].leftBearing;
    2300               0 :           if (rbearing > bmdata[first].rightBearing)
    2301               0 :             rbearing = bmdata[first].rightBearing;
    2302                 :         }
    2303                 :       }
    2304               0 :       else if (chdata[first].Exists()) {
    2305               0 :         lbearing = bmdata[first].leftBearing;
    2306               0 :         rbearing = bmdata[first].rightBearing;
    2307                 :       }
    2308                 :       else {
    2309               0 :         NS_ERROR("Cannot stretch - All parts missing");
    2310               0 :         return NS_ERROR_UNEXPECTED;
    2311                 :       }
    2312                 :       // paint the rule between the parts
    2313                 :       nsRect rule(aRect.x + lbearing, end[first],
    2314               0 :                   rbearing - lbearing, start[last] - end[first]);
    2315               0 :       if (!rule.IsEmpty())
    2316               0 :         aRenderingContext.FillRect(rule);
    2317               0 :       first = last;
    2318               0 :       last++;
    2319                 :     }
    2320                 :   }
    2321               0 :   else if (bmdata[glue].ascent + bmdata[glue].descent > 0) {
    2322                 :     // glue is present
    2323               0 :     nsBoundingMetrics& bm = bmdata[glue];
    2324                 :     // Ensure the stride for the glue is not reduced to less than one pixel
    2325               0 :     if (bm.ascent + bm.descent >= 3 * oneDevPixel) {
    2326                 :       // To protect against gaps, pretend the glue is smaller than it is,
    2327                 :       // in order to trim off ends and thus get a solid edge for the join.
    2328               0 :       bm.ascent -= oneDevPixel;
    2329               0 :       bm.descent -= oneDevPixel;
    2330                 :     }
    2331                 : 
    2332                 :     SetFontFamily(aStyleContext, aRenderingContext,
    2333               0 :                   aFont, aGlyphTable, chGlue, mFamily);
    2334               0 :     nsRect clipRect = unionRect;
    2335                 : 
    2336               0 :     for (i = 0; i < bottom; ++i) {
    2337                 :       // Make sure not to draw outside the character
    2338               0 :       nscoord dy = NS_MAX(end[i], aRect.y);
    2339               0 :       nscoord fillEnd = NS_MIN(start[i+1], aRect.YMost());
    2340                 : #ifdef SHOW_BORDERS
    2341                 :       // exact area to fill
    2342                 :       aRenderingContext.SetColor(NS_RGB(255,0,0));
    2343                 :       clipRect.y = dy;
    2344                 :       clipRect.height = fillEnd - dy;
    2345                 :       aRenderingContext.DrawRect(clipRect);
    2346                 :       {
    2347                 : #endif
    2348               0 :       while (dy < fillEnd) {
    2349               0 :         clipRect.y = dy;
    2350               0 :         clipRect.height = NS_MIN(bm.ascent + bm.descent, fillEnd - dy);
    2351               0 :         AutoPushClipRect clip(aRenderingContext, clipRect);
    2352               0 :         dy += bm.ascent;
    2353               0 :         aRenderingContext.DrawString(chGlue.code, chGlue.Length(), dx, dy);
    2354               0 :         dy += bm.descent;
    2355                 :       }
    2356                 : #ifdef SHOW_BORDERS
    2357                 :       }
    2358                 :       // last glyph that may cross past its boundary and collide with the next
    2359                 :       nscoord height = bm.ascent + bm.descent;
    2360                 :       aRenderingContext.SetColor(NS_RGB(0,255,0));
    2361                 :       aRenderingContext.DrawRect(nsRect(dx, dy-bm.ascent, aRect.width, height));
    2362                 : #endif
    2363                 :     }
    2364                 :   }
    2365                 : #ifdef DEBUG
    2366                 :   else {
    2367               0 :     for (i = 0; i < bottom; ++i) {
    2368               0 :       NS_ASSERTION(end[i] >= start[i+1],
    2369                 :                    "gap between parts with missing glue glyph");
    2370                 :     }
    2371                 :   }
    2372                 : #endif
    2373               0 :   return NS_OK;
    2374                 : }
    2375                 : 
    2376                 : // paint a stretchy char by assembling glyphs horizontally
    2377                 : nsresult
    2378               0 : nsMathMLChar::PaintHorizontally(nsPresContext*      aPresContext,
    2379                 :                                 nsRenderingContext& aRenderingContext,
    2380                 :                                 nsFont&              aFont,
    2381                 :                                 nsStyleContext*      aStyleContext,
    2382                 :                                 nsGlyphTable*        aGlyphTable,
    2383                 :                                 nsRect&              aRect)
    2384                 : {
    2385                 :   // Get the device pixel size in the horizontal direction.
    2386                 :   // (This makes no effort to optimize for non-translation transformations.)
    2387               0 :   nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
    2388                 : 
    2389                 :   // get metrics data to be re-used later
    2390               0 :   PRInt32 i = 0;
    2391                 :   nsGlyphCode ch, chdata[4];
    2392               0 :   nsBoundingMetrics bmdata[4];
    2393                 :   PRInt32 glue, right;
    2394               0 :   nsGlyphCode chGlue = aGlyphTable->GlueOf(aPresContext, this);
    2395               0 :   for (PRInt32 j = 0; j < 4; ++j) {
    2396               0 :     switch (j) {
    2397                 :       case 0:
    2398               0 :         ch = aGlyphTable->LeftOf(aPresContext, this);
    2399               0 :         break;
    2400                 :       case 1:
    2401               0 :         ch = aGlyphTable->MiddleOf(aPresContext, this);
    2402               0 :         if (!ch.Exists())
    2403               0 :           continue; // no middle
    2404               0 :         break;
    2405                 :       case 2:
    2406               0 :         ch = aGlyphTable->RightOf(aPresContext, this);
    2407               0 :         right = i;
    2408               0 :         break;
    2409                 :       case 3:
    2410               0 :         ch = chGlue;
    2411               0 :         glue = i;
    2412               0 :         break;
    2413                 :     }
    2414                 :     // empty slots are filled with the glue if it is not null
    2415               0 :     if (!ch.Exists()) ch = chGlue;
    2416                 :     // if (!ch.Exists()) glue is null, leave bounding metrics at 0.
    2417               0 :     if (ch.Exists()) {
    2418                 :       SetFontFamily(aStyleContext, aRenderingContext,
    2419               0 :                     aFont, aGlyphTable, ch, mFamily);
    2420               0 :       bmdata[i] = aRenderingContext.GetBoundingMetrics(ch.code, ch.Length());
    2421                 :     }
    2422               0 :     chdata[i] = ch;
    2423               0 :     ++i;
    2424                 :   }
    2425               0 :   nscoord dy = aRect.y + mBoundingMetrics.ascent;
    2426                 :   nscoord offset[3], start[3], end[3];
    2427               0 :   nsRefPtr<gfxContext> ctx = aRenderingContext.ThebesContext();
    2428               0 :   for (i = 0; i <= right; ++i) {
    2429               0 :     ch = chdata[i];
    2430               0 :     const nsBoundingMetrics& bm = bmdata[i];
    2431                 :     nscoord dx;
    2432               0 :     if (0 == i) { // left
    2433               0 :       dx = aRect.x - bm.leftBearing;
    2434                 :     }
    2435               0 :     else if (right == i) { // right
    2436               0 :       dx = aRect.x + aRect.width - bm.rightBearing;
    2437                 :     }
    2438                 :     else { // middle
    2439               0 :       dx = aRect.x + (aRect.width - bm.width)/2;
    2440                 :     }
    2441                 :     // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
    2442                 :     // Do this now so that we can get the other dimensions right.
    2443                 :     // (This may not achieve much with non-rectangular transformations.)
    2444               0 :     dx = SnapToDevPixels(ctx, oneDevPixel, nsPoint(dx, dy)).x;
    2445                 :     // abcissa passed to DrawString
    2446               0 :     offset[i] = dx;
    2447                 :     // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
    2448                 :     // pixel, so the bm values can include 1 row of faint pixels on each edge.
    2449                 :     // Don't rely on this pixel as it can look like a gap.
    2450               0 :     start[i] = dx + bm.leftBearing + oneDevPixel; // left join
    2451               0 :     end[i] = dx + bm.rightBearing - oneDevPixel; // right join
    2452                 :   }
    2453                 : 
    2454                 :   // If there are overlaps, then join at the mid point
    2455               0 :   for (i = 0; i < right; ++i) {
    2456               0 :     if (end[i] > start[i+1]) {
    2457               0 :       end[i] = (end[i] + start[i+1]) / 2;
    2458               0 :       start[i+1] = end[i];
    2459                 :     }
    2460                 :   }
    2461                 : 
    2462               0 :   nsRect unionRect = aRect;
    2463               0 :   unionRect.Inflate(oneDevPixel, oneDevPixel);
    2464                 : 
    2465                 :   ///////////////////////////
    2466                 :   // draw left, middle, right
    2467               0 :   for (i = 0; i <= right; ++i) {
    2468               0 :     ch = chdata[i];
    2469                 :     // glue can be null, and other parts could have been set to glue
    2470               0 :     if (ch.Exists()) {
    2471                 : #ifdef SHOW_BORDERS
    2472                 :       aRenderingContext.SetColor(NS_RGB(255,0,0));
    2473                 :       aRenderingContext.DrawRect(nsRect(start[i], dy - bmdata[i].ascent,
    2474                 :                                  end[i] - start[i], bmdata[i].ascent + bmdata[i].descent));
    2475                 : #endif
    2476               0 :       nscoord dx = offset[i];
    2477               0 :       nsRect clipRect = unionRect;
    2478                 :       // Clip at the join to get a solid edge (without overlap or gap), when
    2479                 :       // this won't change the glyph too much.  If the glyph is too small to
    2480                 :       // clip then we'll overlap rather than have a gap.
    2481               0 :       nscoord width = bmdata[i].rightBearing - bmdata[i].leftBearing;
    2482               0 :       if (ch == chGlue ||
    2483                 :           width * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
    2484               0 :         if (0 == i) { // left
    2485               0 :           clipRect.width = end[i] - clipRect.x;
    2486                 :         }
    2487               0 :         else if (right == i) { // right
    2488               0 :           clipRect.width -= start[i] - clipRect.x;
    2489               0 :           clipRect.x = start[i];
    2490                 :         }
    2491                 :         else { // middle
    2492               0 :           clipRect.x = start[i];
    2493               0 :           clipRect.width = end[i] - start[i];
    2494                 :         }
    2495                 :       }
    2496               0 :       if (!clipRect.IsEmpty()) {
    2497               0 :         AutoPushClipRect clip(aRenderingContext, clipRect);
    2498                 :         SetFontFamily(aStyleContext, aRenderingContext,
    2499               0 :                       aFont, aGlyphTable, ch, mFamily);
    2500               0 :         aRenderingContext.DrawString(ch.code, ch.Length(), dx, dy);
    2501                 :       }
    2502                 :     }
    2503                 :   }
    2504                 : 
    2505                 :   ////////////////
    2506                 :   // fill the gap between left and middle, and between middle and right.
    2507               0 :   if (!chGlue.Exists()) { // null glue : draw a rule
    2508                 :     // figure out the dimensions of the rule to be drawn :
    2509                 :     // set ascent to lowest ascent among the two current successive parts.
    2510                 :     // set descent to highest descent among the two current successive parts.
    2511                 :     // this satisfies the convention used for over/underbraces, and helps
    2512                 :     // fix broken fonts.
    2513                 :     nscoord ascent, descent;
    2514               0 :     PRInt32 first = 0, last = 1;
    2515               0 :     while (last <= right) {
    2516               0 :       if (chdata[last].Exists()) {
    2517               0 :         ascent = bmdata[last].ascent;
    2518               0 :         descent = bmdata[last].descent;
    2519               0 :         if (chdata[first].Exists()) {
    2520               0 :           if (ascent > bmdata[first].ascent)
    2521               0 :             ascent = bmdata[first].ascent;
    2522               0 :           if (descent > bmdata[first].descent)
    2523               0 :             descent = bmdata[first].descent;
    2524                 :         }
    2525                 :       }
    2526               0 :       else if (chdata[first].Exists()) {
    2527               0 :         ascent = bmdata[first].ascent;
    2528               0 :         descent = bmdata[first].descent;
    2529                 :       }
    2530                 :       else {
    2531               0 :         NS_ERROR("Cannot stretch - All parts missing");
    2532               0 :         return NS_ERROR_UNEXPECTED;
    2533                 :       }
    2534                 :       // paint the rule between the parts
    2535                 :       nsRect rule(end[first], dy - ascent,
    2536               0 :                   start[last] - end[first], ascent + descent);
    2537               0 :       if (!rule.IsEmpty())
    2538               0 :         aRenderingContext.FillRect(rule);
    2539               0 :       first = last;
    2540               0 :       last++;
    2541                 :     }
    2542                 :   }
    2543               0 :   else if (bmdata[glue].rightBearing - bmdata[glue].leftBearing > 0) {
    2544                 :     // glue is present
    2545               0 :     nsBoundingMetrics& bm = bmdata[glue];
    2546                 :     // Ensure the stride for the glue is not reduced to less than one pixel
    2547               0 :     if (bm.rightBearing - bm.leftBearing >= 3 * oneDevPixel) {
    2548                 :       // To protect against gaps, pretend the glue is smaller than it is,
    2549                 :       // in order to trim off ends and thus get a solid edge for the join.
    2550               0 :       bm.leftBearing += oneDevPixel;
    2551               0 :       bm.rightBearing -= oneDevPixel;
    2552                 :     }
    2553                 : 
    2554                 :     SetFontFamily(aStyleContext, aRenderingContext,
    2555               0 :                   aFont, aGlyphTable, chGlue, mFamily);
    2556               0 :     nsRect clipRect = unionRect;
    2557                 : 
    2558               0 :     for (i = 0; i < right; ++i) {
    2559                 :       // Make sure not to draw outside the character
    2560               0 :       nscoord dx = NS_MAX(end[i], aRect.x);
    2561               0 :       nscoord fillEnd = NS_MIN(start[i+1], aRect.XMost());
    2562                 : #ifdef SHOW_BORDERS
    2563                 :       // rectangles in-between that are to be filled
    2564                 :       aRenderingContext.SetColor(NS_RGB(255,0,0));
    2565                 :       clipRect.x = dx;
    2566                 :       clipRect.width = fillEnd - dx;
    2567                 :       aRenderingContext.DrawRect(clipRect);
    2568                 :       {
    2569                 : #endif
    2570               0 :       while (dx < fillEnd) {
    2571               0 :         clipRect.x = dx;
    2572               0 :         clipRect.width = NS_MIN(bm.rightBearing - bm.leftBearing, fillEnd - dx);
    2573               0 :         AutoPushClipRect clip(aRenderingContext, clipRect);
    2574               0 :         dx -= bm.leftBearing;
    2575               0 :         aRenderingContext.DrawString(chGlue.code, chGlue.Length(), dx, dy);
    2576               0 :         dx += bm.rightBearing;
    2577                 :       }
    2578                 : #ifdef SHOW_BORDERS
    2579                 :       }
    2580                 :       // last glyph that may cross past its boundary and collide with the next
    2581                 :       nscoord width = bm.rightBearing - bm.leftBearing;
    2582                 :       aRenderingContext.SetColor(NS_RGB(0,255,0));
    2583                 :       aRenderingContext.DrawRect(nsRect(dx + bm.leftBearing, aRect.y, width, aRect.height));
    2584                 : #endif
    2585                 :     }
    2586                 :   }
    2587                 : #ifdef DEBUG
    2588                 :   else { // no glue
    2589               0 :     for (i = 0; i < right; ++i) {
    2590               0 :       NS_ASSERTION(end[i] >= start[i+1],
    2591                 :                    "gap between parts with missing glue glyph");
    2592                 :     }
    2593                 :   }
    2594                 : #endif
    2595               0 :   return NS_OK;
    2596                 : }

Generated by: LCOV version 1.7