Back to index

lightning-sunbird  0.9+nobinonly
nsMathMLChar.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla MathML Project.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * The University Of Queensland.
00018  * Portions created by the Initial Developer are Copyright (C) 1999
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Roger B. Sidje <rbs@maths.uq.edu.au>
00023  *   Shyjan Mahamud <mahamud@cs.cmu.edu>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsCOMPtr.h"
00040 #include "nsFrame.h"
00041 #include "nsPresContext.h"
00042 #include "nsUnitConversion.h"
00043 #include "nsStyleContext.h"
00044 #include "nsStyleConsts.h"
00045 #include "nsString.h"
00046 #include "nsUnicharUtils.h"
00047 #include "nsIRenderingContext.h"
00048 #include "nsIFontMetrics.h"
00049 
00050 #include "nsIPrefBranch.h"
00051 #include "nsIPrefService.h"
00052 #include "nsISupportsPrimitives.h"
00053 #include "nsIComponentManager.h"
00054 #include "nsIPersistentProperties2.h"
00055 #include "nsIServiceManager.h"
00056 #include "nsIObserverService.h"
00057 #include "nsIObserver.h"
00058 #include "nsNetUtil.h"
00059 
00060 #include "nsILookAndFeel.h"
00061 #include "nsIDeviceContext.h"
00062 #include "nsCSSRendering.h"
00063 #include "prprf.h"         // For PR_snprintf()
00064 
00065 #include "nsIDOMWindow.h"
00066 #include "nsINonBlockingAlertService.h"
00067 #include "nsIWindowWatcher.h"
00068 #include "nsIStringBundle.h"
00069 #include "nsDoubleHashtable.h"
00070 
00071 #include "nsMathMLOperators.h"
00072 #include "nsMathMLChar.h"
00073 
00074 //#define SHOW_BORDERS 1
00075 //#define NOISY_SEARCH 1
00076 
00077 // -----------------------------------------------------------------------------------
00078 static const PRUnichar   kSpaceCh   = PRUnichar(' ');
00079 static const nsGlyphCode kNullGlyph = {0, 0};
00080 
00081 // -----------------------------------------------------------------------------------
00082 // nsGlyphTable is a class that provides an interface for accessing glyphs
00083 // of stretchy chars. It acts like a table that stores the variants of bigger
00084 // sizes (if any) and the partial glyphs needed to build extensible symbols.
00085 // An instance of nsGlyphTable is associated to one primary font. Extra glyphs
00086 // can be taken in other additional fonts when stretching certain characters.
00087 // These supplementary fonts are referred to as "external" fonts to the table.
00088 //
00089 // A char for which nsGlyphTable::Has(aChar) is true means that the table
00090 // contains some glyphs (bigger and/or partial) that can be used to render
00091 // the char. Bigger sizes (if any) of the char can then be retrieved with
00092 // BigOf(aSize). Partial glyphs can be retrieved with TopOf(), GlueOf(), etc.
00093 //
00094 // A table consists of "nsGlyphCode"s which are viewed either as Unicode
00095 // points or as direct glyph indices, depending on the type of the table.
00096 // XXX The latter is not yet supported.
00097 
00098 // General format of MathFont Property Files from which glyph data are retrieved:
00099 // -----------------------------------------------------------------------------------
00100 // Each font should have its set of glyph data. For example, the glyph data for
00101 // the "Symbol" font and the "MT Extra" font are in "mathfontSymbol.properties"
00102 // and "mathfontMTExtra.properties", respectively. The mathfont property file is a
00103 // set of all the stretchy MathML characters that can be rendered with that font
00104 // using larger and/or partial glyphs. The entry of each stretchy character in the
00105 // mathfont property file gives, in that order, the 4 partial glyphs: Top (or Left),
00106 // Middle, Bottom (or Right), Glue; and the variants of bigger sizes (if any).
00107 // A position that is not relevant to a particular character is indicated there
00108 // with the UNICODE REPLACEMENT CHARACTER 0xFFFD.
00109 // Characters that need to be built recursively from other characters are said
00110 // to be composite. For example, chars like over/underbrace in CMEX10 have to
00111 // be built from two half stretchy chars and joined in the middle (TeXbook, p.225).
00112 // Such chars are handled in a special manner by the nsMathMLChar class, which allows
00113 // several (2 or more) child chars to be composed in order to render another char.
00114 // To specify such chars, their list of glyphs in the property file should be given
00115 // as space-separated segments of glyphs. Each segment gives the 4 partial
00116 // glyphs with which to build the child char that will be joined with its other
00117 // siblings. In this code, when this situation happens (see the detailed description
00118 // of Stretch() below), the original char (referred to as "parent") creates a
00119 // singly-linked list of child chars, asking them to stretch in an equally divided
00120 // space. The nsGlyphTable embeds the necessary logic to guarantee correctness in a
00121 // recursive stretch (and in the use of TopOf(), GlueOf(), etc) on these child chars.
00122 // -----------------------------------------------------------------------------------
00123 
00124 #define NS_TABLE_TYPE_UNICODE       0
00125 #define NS_TABLE_TYPE_GLYPH_INDEX   1
00126 
00127 #define NS_TABLE_STATE_ERROR       -1
00128 #define NS_TABLE_STATE_EMPTY        0
00129 #define NS_TABLE_STATE_READY        1
00130 
00131 // Hook to resolve common assignments to the PUA
00132 static nsCOMPtr<nsIPersistentProperties> gPUAProperties; 
00133 
00134 // helper to check if a font is installed
00135 static PRBool
00136 CheckFontExistence(nsPresContext* aPresContext, const nsString& aFontName)
00137 {
00138   PRBool aliased;
00139   nsAutoString localName;
00140   nsIDeviceContext *deviceContext = aPresContext->DeviceContext();
00141   deviceContext->GetLocalFontName(aFontName, localName, aliased);
00142   PRBool rv = (aliased || (NS_OK == deviceContext->CheckFontExistence(localName)));
00143   // (see bug 35824 for comments about the aliased localName)
00144   return rv;
00145 }
00146 
00147 // alert the user if some of the needed MathML fonts are not installed.
00148 // it is non-modal (i.e., it doesn't wait for input from the user)
00149 static void
00150 AlertMissingFonts(nsString& aMissingFonts)
00151 {
00152   nsCOMPtr<nsIStringBundleService> sbs(do_GetService(NS_STRINGBUNDLE_CONTRACTID));
00153   if (!sbs)
00154     return;
00155 
00156   nsCOMPtr<nsIStringBundle> sb;
00157   sbs->CreateBundle("resource://gre/res/fonts/mathfont.properties", getter_AddRefs(sb));
00158   if (!sb)
00159     return;
00160 
00161   nsXPIDLString title, message;
00162   const PRUnichar* strings[] = { aMissingFonts.get() };
00163   sb->GetStringFromName(NS_LITERAL_STRING("mathfont_missing_dialog_title").get(), getter_Copies(title));
00164   sb->FormatStringFromName(NS_LITERAL_STRING("mathfont_missing_dialog_message").get(),
00165                            strings, 1, getter_Copies(message));
00166 
00167   nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
00168   if (!wwatch)
00169     return;
00170 
00171   nsCOMPtr<nsIDOMWindow> parent;
00172   wwatch->GetActiveWindow(getter_AddRefs(parent));
00173   nsresult rv;
00174   nsCOMPtr<nsINonBlockingAlertService> prompter =
00175     do_GetService("@mozilla.org/embedcomp/nbalert-service;1", &rv);
00176 
00177   if (prompter && parent) {
00178     prompter->ShowNonBlockingAlert(parent, title.get(), message.get());
00179   }
00180 }
00181 
00182 // helper to trim off comments from data in a MathFont Property File
00183 static void
00184 Clean(nsString& aValue)
00185 {
00186   // chop the trailing # comment portion if any ...
00187   PRInt32 comment = aValue.RFindChar('#');
00188   if (comment > 0) aValue.Truncate(comment);
00189   aValue.CompressWhitespace();
00190 }
00191 
00192 // helper to load a MathFont Property File
00193 static nsresult
00194 LoadProperties(const nsString& aName,
00195                nsCOMPtr<nsIPersistentProperties>& aProperties)
00196 {
00197   nsAutoString uriStr;
00198   uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
00199   uriStr.Append(aName);
00200   uriStr.StripWhitespace(); // that may come from aName
00201   uriStr.AppendLiteral(".properties");
00202   return NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(aProperties), 
00203                                                 NS_ConvertUTF16toUTF8(uriStr));
00204 }
00205 
00206 // helper to get the stretchy direction of a char
00207 static nsStretchDirection
00208 GetStretchyDirection(PRUnichar aChar)
00209 {
00210   PRInt32 k = nsMathMLOperators::FindStretchyOperator(aChar);
00211   return (k == kNotFound)
00212     ? NS_STRETCH_DIRECTION_UNSUPPORTED
00213     : nsMathMLOperators::GetStretchyDirectionAt(k);
00214 }
00215 
00216 // -----------------------------------------------------------------------------------
00217 
00218 MOZ_DECL_CTOR_COUNTER(nsGlyphTable)
00219 
00220 class nsGlyphTable {
00221 public:
00222   nsGlyphTable(const nsString& aPrimaryFontName)
00223   {
00224     MOZ_COUNT_CTOR(nsGlyphTable);
00225     mFontName.AppendString(aPrimaryFontName);
00226     mType = NS_TABLE_TYPE_UNICODE;
00227     mState = NS_TABLE_STATE_EMPTY;
00228     mCharCache = 0;
00229   }
00230 
00231   ~nsGlyphTable() // not a virtual destructor: this class is not intended to be subclassed
00232   {
00233     MOZ_COUNT_DTOR(nsGlyphTable);
00234   }
00235 
00236   void GetPrimaryFontName(nsString& aPrimaryFontName) {
00237     mFontName.StringAt(0, aPrimaryFontName);
00238   }
00239 
00240   // True if this table contains some glyphs (variants and/or parts)
00241   // or contains child chars that can be used to render this char
00242   PRBool Has(nsPresContext* aPresContext, nsMathMLChar* aChar);
00243   PRBool Has(nsPresContext* aPresContext, PRUnichar aChar);
00244 
00245   // True if this table contains variants of larger sizes to render this char
00246   PRBool HasVariantsOf(nsPresContext* aPresContext, nsMathMLChar* aChar);
00247   PRBool HasVariantsOf(nsPresContext* aPresContext, PRUnichar aChar);
00248 
00249   // True if this table contains parts (or composite parts) to render this char
00250   PRBool HasPartsOf(nsPresContext* aPresContext, nsMathMLChar* aChar);
00251   PRBool HasPartsOf(nsPresContext* aPresContext, PRUnichar aChar);
00252 
00253   // True if aChar is to be assembled from other child chars in this table
00254   PRBool IsComposite(nsPresContext* aPresContext, nsMathMLChar* aChar);
00255 
00256   // The number of child chars to assemble in order to render aChar
00257   PRInt32 ChildCountOf(nsPresContext* aPresContext, nsMathMLChar* aChar);
00258 
00259   // Getters for the parts
00260   nsGlyphCode TopOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
00261     return ElementAt(aPresContext, aChar, 0);
00262   }
00263   nsGlyphCode MiddleOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
00264     return ElementAt(aPresContext, aChar, 1);
00265   }
00266   nsGlyphCode BottomOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
00267     return ElementAt(aPresContext, aChar, 2);
00268   }
00269   nsGlyphCode GlueOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
00270     return ElementAt(aPresContext, aChar, 3);
00271   }
00272   nsGlyphCode BigOf(nsPresContext* aPresContext, nsMathMLChar* aChar, PRInt32 aSize) {
00273     return ElementAt(aPresContext, aChar, 4 + aSize);
00274   }
00275   nsGlyphCode LeftOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
00276     return ElementAt(aPresContext, aChar, 0);
00277   }
00278   nsGlyphCode RightOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
00279     return ElementAt(aPresContext, aChar, 2);
00280   }
00281 
00282   // use these to measure/render a glyph that comes from this table
00283   nsresult
00284   GetBoundingMetrics(nsIRenderingContext& aRenderingContext,
00285                      nsFont&              aFont,
00286                      nsGlyphCode&         aGlyphCode,
00287                      nsBoundingMetrics&   aBoundingMetrics);
00288   void
00289   DrawGlyph(nsIRenderingContext& aRenderingContext,
00290             nsFont&              aFont,
00291             nsGlyphCode&         aGlyphCode,
00292             nscoord              aX,
00293             nscoord              aY,
00294             nsRect*              aClipRect = nsnull);
00295 
00296 private:
00297   char   GetAnnotation(nsMathMLChar* aChar, PRInt32 aPosition);
00298   nsGlyphCode ElementAt(nsPresContext* aPresContext, nsMathMLChar* aChar, PRUint32 aPosition);
00299 
00300   // The type is either NS_TABLE_TYPE_UNICODE or NS_TABLE_TYPE_GLYPH_INDEX
00301   PRInt32 mType;    
00302                            
00303   // mFontName[0] is the primary font associated to this table. The others 
00304   // are possible "external" fonts for glyphs not in the primary font
00305   // but which are needed to stretch certain characters in the table
00306   nsStringArray mFontName; 
00307                                
00308   // Tri-state variable for error/empty/ready
00309   PRInt32 mState;
00310 
00311   // The set of glyph data in this table, as provided by the MathFont Property File
00312   nsCOMPtr<nsIPersistentProperties> mGlyphProperties;
00313 
00314   // For speedy re-use, we always cache the last data used in the table.
00315   // mCharCache is the Unicode point of the last char that was queried in this
00316   // table. mGlyphCache is a buffer containing the glyph data associated to
00317   // that char. For a property line 'key = value' in the MathFont Property File,
00318   // mCharCache will retain the 'key' -- which is a Unicode point, while mGlyphCache
00319   // will retain the 'value', which is a consecutive list of nsGlyphCodes, i.e.,
00320   // the pairs of 'code@font' needed by the char -- in which 'code@0' can be specified
00321   // without the optional '@0'. However, to ease subsequent processing, mGlyphCache
00322   // excludes the '@' symbol and explicitly inserts all optional '0' that indicates
00323   // the primary font identifier. Specifically therefore, the k-th glyph is
00324   // characterized by :
00325   // 1) mGlyphCache[2*k] : its Unicode point (or glyph index -- depending on mType),
00326   // 2) mGlyphCache[2*k+1] : the numeric identifier of the font where it comes from.
00327   // A font identifier of '0' means the default primary font associated to this
00328   // table. Other digits map to the "external" fonts that may have been specified
00329   // in the MathFont Property File.
00330   nsString  mGlyphCache;
00331   PRUnichar mCharCache;
00332 };
00333 
00334 char
00335 nsGlyphTable::GetAnnotation(nsMathMLChar* aChar, PRInt32 aPosition)
00336 {
00337   NS_ASSERTION(aChar->mDirection == NS_STRETCH_DIRECTION_VERTICAL ||
00338                aChar->mDirection == NS_STRETCH_DIRECTION_HORIZONTAL,
00339                "invalid call");
00340   static const char kVertical[]   = "TMBG";
00341   static const char kHorizontal[] = "LMRG";
00342   if (aPosition >= 4) {
00343     // return an ASCII digit for the size=0,1,2,...
00344     return '0' + aPosition - 4;
00345   }
00346   return (aChar->mDirection == NS_STRETCH_DIRECTION_VERTICAL) ?
00347       kVertical[aPosition] :
00348       kHorizontal[aPosition];
00349 }
00350 
00351 nsGlyphCode
00352 nsGlyphTable::ElementAt(nsPresContext* aPresContext, nsMathMLChar* aChar, PRUint32 aPosition)
00353 {
00354   if (mState == NS_TABLE_STATE_ERROR) return kNullGlyph;
00355   // Load glyph properties if this is the first time we have been here
00356   if (mState == NS_TABLE_STATE_EMPTY) {
00357     if (!CheckFontExistence(aPresContext, *mFontName[0])) {
00358       mState = NS_TABLE_STATE_ERROR;
00359       return kNullGlyph;
00360     }
00361     nsresult rv = LoadProperties(*mFontName[0], mGlyphProperties);
00362 #ifdef NS_DEBUG
00363     nsCAutoString uriStr;
00364     uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
00365     LossyAppendUTF16toASCII(*mFontName[0], uriStr);
00366     uriStr.StripWhitespace(); // that may come from mFontName
00367     uriStr.AppendLiteral(".properties");
00368     printf("Loading %s ... %s\n",
00369             uriStr.get(),
00370             (NS_FAILED(rv)) ? "Failed" : "Done");
00371 #endif
00372     if (NS_FAILED(rv)) {
00373       mState = NS_TABLE_STATE_ERROR; // never waste time with this table again
00374       return kNullGlyph;
00375     }
00376     mState = NS_TABLE_STATE_READY;
00377 
00378     // see if there are external fonts needed for certain chars in this table
00379     nsCAutoString key;
00380     nsAutoString value;
00381     for (PRInt32 i = 1; ; i++) {
00382       key.AssignLiteral("external.");
00383       key.AppendInt(i, 10);
00384       rv = mGlyphProperties->GetStringProperty(key, value);
00385       if (NS_FAILED(rv)) break;
00386       Clean(value);
00387       mFontName.AppendString(value); // i.e., mFontName[i] holds this font name
00388     }
00389   }
00390 
00391   // If aChar is a child char to be used by a parent composite char, make
00392   // sure that it is really attached to this table
00393   if (aChar->mParent && (aChar->mGlyphTable != this)) return kNullGlyph;
00394 
00395   // Update our cache if it is not associated to this character
00396   PRUnichar uchar = aChar->mData[0];
00397   if (mCharCache != uchar) {
00398     // The key in the property file is interpreted as ASCII and kept
00399     // as such ...
00400     char key[10]; PR_snprintf(key, sizeof(key), "\\u%04X", uchar);
00401     nsAutoString value;
00402     nsresult rv = mGlyphProperties->GetStringProperty(nsDependentCString(key), value);
00403     if (NS_FAILED(rv)) return kNullGlyph;
00404     Clean(value);
00405     // See if this char uses external fonts; e.g., if the 2nd glyph is taken from the
00406     // external font '1', the property line looks like \uNNNN = \uNNNN\uNNNN@1\uNNNN.
00407     // This is where mGlyphCache is pre-processed to explicitly store all glyph codes
00408     // as combined pairs of 'code@font', excluding the '@' separator. This means that
00409     // mGlyphCache[2*k] will later be rendered with mFontName[mGlyphCache[2*k+1]-'0']
00410     // Note: font identifier is internally an ASCII digit to avoid the null char issue
00411     nsAutoString buffer, puaValue;
00412     char puaKey[10];
00413     PRInt32 length = value.Length();
00414     for (PRInt32 i = 0, j = 0; i < length; i++, j++) {
00415       PRUnichar code = value[i];
00416       PRUnichar font = PRUnichar('0');
00417       // see if we are at the beginning of a child char
00418       if (code == kSpaceCh) {
00419         // reset the annotation indicator to be 0 for the next code point
00420         j = -1;
00421       }
00422       // see if this code point is an *indirect reference* to
00423       // the PUA, and lookup "key.[TLMBRG1-9]" in the PUA
00424       else if (code == PRUnichar(0xF8FF)) {
00425         PR_snprintf(puaKey, sizeof(puaKey), "%s.%c", key, GetAnnotation(aChar, j));
00426         rv = gPUAProperties->GetStringProperty(nsDependentCString(puaKey), puaValue);
00427         if (NS_FAILED(rv) || puaValue.IsEmpty()) return kNullGlyph;
00428         code = puaValue[0];
00429       }
00430       // see if this code point is a *direct reference* to
00431       // the PUA, and lookup "code.[TLMBRG1-9]" in the PUA
00432       else if ((i+2 < length) && (value[i+1] == PRUnichar('.'))) {
00433         i += 2;
00434         // safe cast of value[i], it's ascii
00435         PR_snprintf(puaKey, sizeof(puaKey), "\\u%04X.%c", code, char(value[i]));
00436         rv = gPUAProperties->GetStringProperty(nsDependentCString(puaKey), puaValue);
00437         if (NS_FAILED(rv) || puaValue.IsEmpty()) return kNullGlyph;
00438         code = puaValue[0];
00439       }
00440       // see if an external font is needed for the code point
00441       if ((i+2 < length) && (value[i+1] == PRUnichar('@')) &&
00442           (value[i+2] >= PRUnichar('0')) && (value[i+2] <= PRUnichar('9'))) {
00443         i += 2;
00444         font = value[i];
00445         // The char cannot be handled if this font is not installed
00446         nsAutoString fontName;
00447         mFontName.StringAt(font-'0', fontName);
00448         if (!fontName.Length() || !CheckFontExistence(aPresContext, fontName)) {
00449           return kNullGlyph;
00450         }
00451       }
00452       buffer.Append(code);
00453       buffer.Append(font);
00454     }
00455     // update our cache with the new settings
00456     mGlyphCache.Assign(buffer);
00457     mCharCache = uchar;
00458   }
00459 
00460   // If aChar is a composite char, only its children are allowed
00461   // to use its glyphs in this table, i.e., the parent char itself
00462   // is disabled and cannot be stretched directly with these glyphs.
00463   // This guarantees a coherent behavior in Stretch().
00464   if (!aChar->mParent && (kNotFound != mGlyphCache.FindChar(kSpaceCh))) {
00465     return kNullGlyph;
00466   }
00467 
00468   // If aChar is a child char, the index of the glyph is relative to
00469   // the offset of the list of glyphs corresponding to the child char
00470   PRUint32 offset = 0;
00471   PRUint32 length = mGlyphCache.Length();
00472   if (aChar->mParent) {
00473     nsMathMLChar* child = aChar->mParent->mSibling;
00474     while (child && (child != aChar)) {
00475       offset += 5; // skip the 4 partial glyphs + the whitespace separator
00476       child = child->mSibling;
00477     }
00478     length = 2*(offset + 4); // stay confined in the 4 partial glyphs of this child
00479   }
00480   PRUint32 index = 2*(offset + aPosition); // 2* is to account for the code@font pairs
00481   if (index+1 >= length) return kNullGlyph;
00482   nsGlyphCode ch;
00483   ch.code = mGlyphCache.CharAt(index);
00484   ch.font = mGlyphCache.CharAt(index + 1) - '0'; // the ASCII trick is kept internal...
00485   return (ch == PRUnichar(0xFFFD)) ? kNullGlyph : ch;
00486 }
00487 
00488 PRBool
00489 nsGlyphTable::IsComposite(nsPresContext* aPresContext, nsMathMLChar* aChar)
00490 {
00491   // there is only one level of recursion in our model. a child
00492   // cannot be composite because it cannot have its own children
00493   if (aChar->mParent) return PR_FALSE;
00494   // shortcut to sync the cache with this char...
00495   mCharCache = 0; mGlyphCache.Truncate(); ElementAt(aPresContext, aChar, 0);
00496   // the cache remained empty if the char wasn't found in this table
00497   if (8 >= mGlyphCache.Length()) return PR_FALSE;
00498   // the lists of glyphs of a composite char are space-separated
00499   return (kSpaceCh == mGlyphCache.CharAt(8));
00500 }
00501 
00502 PRInt32
00503 nsGlyphTable::ChildCountOf(nsPresContext* aPresContext, nsMathMLChar* aChar)
00504 {
00505   // this will sync the cache as well ...
00506   if (!IsComposite(aPresContext, aChar)) return 0;
00507   // the lists of glyphs of a composite char are space-separated
00508   return 1 + mGlyphCache.CountChar(kSpaceCh);
00509 }
00510 
00511 PRBool
00512 nsGlyphTable::Has(nsPresContext* aPresContext, nsMathMLChar* aChar)
00513 {
00514   return HasVariantsOf(aPresContext, aChar) || HasPartsOf(aPresContext, aChar);
00515 }
00516 
00517 PRBool
00518 nsGlyphTable::Has(nsPresContext* aPresContext, PRUnichar aChar)
00519 {
00520   nsMathMLChar tmp;
00521   tmp.mData = aChar;
00522   tmp.mDirection = GetStretchyDirection(aChar);
00523   return (tmp.mDirection == NS_STRETCH_DIRECTION_UNSUPPORTED)
00524     ? PR_FALSE
00525     : Has(aPresContext, &tmp);
00526 }
00527 
00528 PRBool
00529 nsGlyphTable::HasVariantsOf(nsPresContext* aPresContext, nsMathMLChar* aChar)
00530 {
00531   return BigOf(aPresContext, aChar, 1) != 0;
00532 }
00533 
00534 PRBool
00535 nsGlyphTable::HasVariantsOf(nsPresContext* aPresContext, PRUnichar aChar)
00536 {
00537   nsMathMLChar tmp;
00538   tmp.mData = aChar;
00539   tmp.mDirection = GetStretchyDirection(aChar);
00540   return (tmp.mDirection == NS_STRETCH_DIRECTION_UNSUPPORTED)
00541     ? PR_FALSE
00542     : HasVariantsOf(aPresContext, &tmp);
00543 }
00544 
00545 PRBool
00546 nsGlyphTable::HasPartsOf(nsPresContext* aPresContext, nsMathMLChar* aChar)
00547 {
00548   return  GlueOf(aPresContext, aChar) || TopOf(aPresContext, aChar) ||
00549           BottomOf(aPresContext, aChar) || MiddleOf(aPresContext, aChar) ||
00550           IsComposite(aPresContext, aChar);
00551 }
00552 
00553 PRBool
00554 nsGlyphTable::HasPartsOf(nsPresContext* aPresContext, PRUnichar aChar)
00555 {
00556   nsMathMLChar tmp;
00557   tmp.mData = aChar;
00558   tmp.mDirection = GetStretchyDirection(aChar);
00559   return (tmp.mDirection == NS_STRETCH_DIRECTION_UNSUPPORTED)
00560     ? PR_FALSE
00561     : HasPartsOf(aPresContext, &tmp);
00562 }
00563 
00564 // Get the bounding box of a glyph.
00565 // Our primary font is assumed to be the current font in the rendering context
00566 nsresult
00567 nsGlyphTable::GetBoundingMetrics(nsIRenderingContext& aRenderingContext,
00568                                  nsFont&              aFont,
00569                                  nsGlyphCode&         aGlyphCode,
00570                                  nsBoundingMetrics&   aBoundingMetrics)
00571 {
00572   nsresult rv;
00573   if (aGlyphCode.font) {
00574     // glyph not associated to our primary font, it comes from an external font
00575     mFontName.StringAt(aGlyphCode.font, aFont.name);
00576     aRenderingContext.SetFont(aFont, nsnull);
00577   }
00578 
00579   //if (mType == NS_TABLE_TYPE_UNICODE)
00580     rv = aRenderingContext.GetBoundingMetrics((PRUnichar*)&aGlyphCode.code, PRUint32(1), aBoundingMetrics);
00581   //else mType == NS_TABLE_TYPE_GLYPH_INDEX
00582   //return NS_ERROR_NOT_IMPLEMENTED;
00583   //rv = aRenderingContext.GetBoundingMetricsI((PRUint16*)&aGlyphCode.code, PRUint32(1), aBoundingMetrics);
00584 
00585   if (aGlyphCode.font) {
00586     // restore our primary font in the rendering context
00587     mFontName.StringAt(0, aFont.name);
00588     aRenderingContext.SetFont(aFont, nsnull);
00589   }
00590   return rv;
00591 }
00592 
00593 // Draw a glyph in a clipped area so that we don't have hairy chars pending outside
00594 // Our primary font is assumed to be the current font in the rendering context
00595 void
00596 nsGlyphTable::DrawGlyph(nsIRenderingContext& aRenderingContext,
00597                         nsFont&              aFont,
00598                         nsGlyphCode&         aGlyphCode,
00599                         nscoord              aX,
00600                         nscoord              aY,
00601                         nsRect*              aClipRect)
00602 {
00603   if (aClipRect) {
00604     aRenderingContext.PushState();
00605     aRenderingContext.SetClipRect(*aClipRect, nsClipCombine_kIntersect);
00606   }
00607   if (aGlyphCode.font) {
00608     // glyph not associated to our primary font, it comes from an external font
00609     mFontName.StringAt(aGlyphCode.font, aFont.name);
00610     aRenderingContext.SetFont(aFont, nsnull);
00611   }
00612 
00613   //if (mType == NS_TABLE_TYPE_UNICODE)
00614     aRenderingContext.DrawString((PRUnichar*)&aGlyphCode.code, PRUint32(1), aX, aY);
00615   //else
00616   //NS_ASSERTION(0, "Error *** Not yet implemented");
00617   //aRenderingContext.DrawStringI((PRUint16*)&aGlyphCode.code, PRUint32(1), aX, aY);
00618 
00619   if (aGlyphCode.font) {
00620     // restore our primary font in the rendering context
00621     mFontName.StringAt(0, aFont.name);
00622     aRenderingContext.SetFont(aFont, nsnull);
00623   }
00624   if (aClipRect)
00625     aRenderingContext.PopState();
00626 }
00627 
00628 // class to map a Unicode point to a string (used to store the list of
00629 // fonts preferred for the base size of certain characters, i.e., when
00630 // stretching doesn't happen with a char, we use its preferred base fonts)
00631 class nsBaseFontEntry : public PLDHashInt32Entry
00632 {
00633 public:
00634   nsBaseFontEntry(const void* aKey) : PLDHashInt32Entry(aKey) { }
00635   ~nsBaseFontEntry() { };
00636 
00637   nsString mFontFamily; // a font-family list a-la CSS
00638 };
00639 
00640 DECL_DHASH_WRAPPER(nsBaseFontHashtable, nsBaseFontEntry, PRInt32)
00641 DHASH_WRAPPER(nsBaseFontHashtable, nsBaseFontEntry, PRInt32)
00642 
00643 // -----------------------------------------------------------------------------------
00644 // This is the list of all the applicable glyph tables.
00645 // We will maintain a single global instance that will only reveal those
00646 // glyph tables that are associated to fonts currently installed on the
00647 // user' system. The class is an XPCOM shutdown observer to allow us to
00648 // free its allocated data at shutdown
00649 
00650 MOZ_DECL_CTOR_COUNTER(nsGlyphTableList)
00651 
00652 class nsGlyphTableList : public nsIObserver
00653 {
00654 public:
00655   NS_DECL_ISUPPORTS
00656   NS_DECL_NSIOBSERVER
00657 
00658   // Hashtable to cache the preferred fonts of some chars at their base size
00659   static nsBaseFontHashtable* gBaseFonts;
00660 
00661   // These are placeholders used to cache the indices (in mTableList) of
00662   // the preferred extension tables for the particular chars.
00663   // Each stretchy char can have a preferred ordered list of fonts to
00664   // be used for its parts, and/or another preferred ordered list of 
00665   // fonts to be used for its variants of larger sizes.
00666   // Several levels of indirection are used to store this information.
00667   // The stretchy chars are collated in an array in nsMathMLOperators.
00668   // If 'index' is the rank of a stretchy char in that array, then
00669   // mTableList[gParts[index]] is the first preferred table to be used for
00670   // the parts of that stretchy char, mTableList[gParts[index]+1] is the
00671   // second table, etc. The same reasoning applies with gVariants[index].
00672   static PRInt32* gParts;
00673   static PRInt32* gVariants;
00674 
00675   nsGlyphTableList()
00676   {
00677     MOZ_COUNT_CTOR(nsGlyphTableList);
00678     mDefaultCount = 0;
00679   }
00680 
00681   virtual ~nsGlyphTableList()
00682   {
00683     MOZ_COUNT_DTOR(nsGlyphTableList);
00684   }
00685 
00686   nsresult Initialize();
00687   nsresult Finalize();
00688 
00689   nsGlyphTable* TableAt(PRInt32 aIndex) {
00690     return NS_STATIC_CAST(nsGlyphTable*, mTableList.ElementAt(aIndex));
00691   }
00692   PRInt32 Count(PRBool aEverything = PR_FALSE) {
00693     return (aEverything) ? mTableList.Count() : mDefaultCount;
00694   }
00695 
00696   nsGlyphTable* AdditionalTableAt(PRInt32 aIndex) {
00697     return NS_STATIC_CAST(nsGlyphTable*, mAdditionalTableList.ElementAt(aIndex));
00698   }
00699   PRInt32 AdditionalCount() {
00700     return mAdditionalTableList.Count();
00701   }
00702 
00703   PRBool AppendTable(nsGlyphTable* aGlyphTable) {
00704     return mTableList.AppendElement(aGlyphTable);
00705   }
00706 
00707   // Add a glyph table in the list, return the new table that was added
00708   nsGlyphTable*
00709   AddGlyphTable(const nsString& aPrimaryFontName);
00710   nsGlyphTable*
00711   AddAdditionalGlyphTable(const nsString& aPrimaryFontName);
00712 
00713   // Find a glyph table in the list that has a glyph for the given char
00714   nsGlyphTable*
00715   GetGlyphTableFor(nsPresContext* aPresContext,
00716                    nsMathMLChar*   aChar);
00717 
00718   // Find the subset of glyph tables that are applicable to the given char,
00719   // knowing that the stretchy style context of the char has the given font.
00720   nsresult
00721   GetListFor(nsPresContext* aPresContext,
00722              nsMathMLChar*   aChar,
00723              nsFont*         aFont,
00724              nsVoidArray*    aGlyphTableList);
00725 
00726   // Retrieve the subset of preferred glyph tables that start at the given index
00727   // Return the number of installed fonts that are retrieved or 0 if none is found.
00728   // If at least one font is found, the preferred fonts become active and
00729   // take precedence (i.e., whatever was in the existing aGlyphTableList is
00730   // cleared). But if it turns out that no preferred font is actually installed,
00731   // the code behaves as if no preferred font was specified at all (i.e., whatever
00732   // was in aGlyphTableList is retained).
00733   nsresult
00734   GetPreferredListAt(nsPresContext* aPresContext,
00735                      PRInt32         aStartingIndex, 
00736                      nsVoidArray*    aGlyphTableList,
00737                      PRInt32*        aCount);
00738 
00739 private:
00740   // Ordered list of glyph tables subdivided in several null-separated segments.
00741   // The first segment contains mDefaultCount entries which are the default
00742   // fonts as provided in the mathfont.properties file. The remainder of the
00743   // list is used to store the preferred tables for the particular chars
00744   // as explained above.
00745   PRInt32     mDefaultCount;
00746   nsVoidArray mTableList;
00747   // Users can prefer certain fonts for a character, but without wanting those
00748   // fonts to be used for other characters. mAdditionalTableList is a list of
00749   // preferred fonts that are not meant to be used as a default sharable list by
00750   // all characters. Note that mTableList[0..mDefaultCount-1] and mAdditionalTableList
00751   // are kept mutually exclusive since there is no need to load the same table twice.
00752   nsVoidArray mAdditionalTableList; 
00753 };
00754 
00755 NS_IMPL_ISUPPORTS1(nsGlyphTableList, nsIObserver)
00756 
00757 // -----------------------------------------------------------------------------------
00758 // Here is the global list of applicable glyph tables that we will be using
00759 static nsGlyphTableList* gGlyphTableList = nsnull;
00760 nsBaseFontHashtable* nsGlyphTableList::gBaseFonts = nsnull;
00761 PRInt32* nsGlyphTableList::gParts = nsnull;
00762 PRInt32* nsGlyphTableList::gVariants = nsnull;
00763 
00764 static PRBool gInitialized = PR_FALSE;
00765 
00766 // XPCOM shutdown observer
00767 NS_IMETHODIMP
00768 nsGlyphTableList::Observe(nsISupports*     aSubject,
00769                           const char* aTopic,
00770                           const PRUnichar* someData)
00771 {
00772   Finalize();
00773   // destroy the PUA
00774   gPUAProperties = nsnull;
00775   return NS_OK;
00776 }
00777 
00778 // Add an observer to XPCOM shutdown so that we can free our data at shutdown
00779 nsresult
00780 nsGlyphTableList::Initialize()
00781 {
00782   nsresult rv = NS_OK;
00783   nsCOMPtr<nsIObserverService> obs = 
00784            do_GetService("@mozilla.org/observer-service;1", &rv);
00785   if (NS_SUCCEEDED(rv)) {
00786     rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
00787   }
00788   return rv;
00789 }
00790 
00791 // Remove our observer and free the memory that were allocated for us
00792 nsresult
00793 nsGlyphTableList::Finalize()
00794 {
00795   // Remove our observer from the observer service
00796   nsresult rv = NS_OK;
00797   nsCOMPtr<nsIObserverService> obs = 
00798            do_GetService("@mozilla.org/observer-service;1", &rv);
00799   if (NS_SUCCEEDED(rv)) {
00800     rv = obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
00801   }
00802   // delete the glyph tables
00803   PRInt32 i;
00804   for (i = Count() - 1; i >= 0; i--) {
00805     nsGlyphTable* glyphTable = TableAt(i);
00806     delete glyphTable;
00807   }
00808   for (i = AdditionalCount() - 1; i >= 0; i--) {
00809     nsGlyphTable* glyphTable = AdditionalTableAt(i);
00810     delete glyphTable;
00811   }
00812   // delete the other variables
00813   delete gBaseFonts;
00814   delete [] gParts;
00815   delete [] gVariants;
00816   gParts = gVariants = nsnull;
00817   gInitialized = PR_FALSE;
00818   // our oneself will be destroyed when our |Release| is called by the observer
00819   return rv;
00820 }
00821 
00822 nsGlyphTable*
00823 nsGlyphTableList::AddGlyphTable(const nsString& aPrimaryFontName)
00824 {
00825   // allocate a table to be deleted at shutdown
00826   nsGlyphTable* glyphTable = new nsGlyphTable(aPrimaryFontName);
00827   if (!glyphTable) return nsnull;
00828   mTableList.AppendElement(glyphTable);
00829   mDefaultCount++;
00830   return glyphTable;
00831 }
00832 
00833 nsGlyphTable*
00834 nsGlyphTableList::AddAdditionalGlyphTable(const nsString& aPrimaryFontName)
00835 {
00836   // allocate a table to be deleted at shutdown
00837   nsGlyphTable* glyphTable = new nsGlyphTable(aPrimaryFontName);
00838   if (!glyphTable) return nsnull;
00839   mAdditionalTableList.AppendElement(glyphTable);
00840   return glyphTable;
00841 }
00842 
00843 nsGlyphTable*
00844 nsGlyphTableList::GetGlyphTableFor(nsPresContext* aPresContext, 
00845                                    nsMathMLChar*   aChar)
00846 {
00847   PRInt32 i;
00848   for (i = 0; i < Count(); i++) {
00849     nsGlyphTable* glyphTable = TableAt(i);
00850     if (glyphTable->Has(aPresContext, aChar)) {
00851       return glyphTable;
00852     }
00853   }
00854   for (i = 0; i < AdditionalCount(); i++) {
00855     nsGlyphTable* glyphTable = AdditionalTableAt(i);
00856     if (glyphTable->Has(aPresContext, aChar)) {
00857       return glyphTable;
00858     }
00859   }
00860   return nsnull;
00861 }
00862 
00863 struct StretchyFontEnumContext {
00864   nsPresContext* mPresContext;
00865   nsMathMLChar*   mChar;
00866   nsVoidArray*    mGlyphTableList;
00867 };
00868 
00869 // check if the current font is associated to a known glyph table, if so the
00870 // glyph table is added to the list of tables that can be used for the char
00871 static PRBool
00872 StretchyFontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
00873 {
00874   if (aGeneric) return PR_FALSE; // stop now
00875   StretchyFontEnumContext* context = (StretchyFontEnumContext*)aData;
00876   nsPresContext* currPresContext = context->mPresContext;
00877   nsMathMLChar* currChar = context->mChar;
00878   nsVoidArray* currList = context->mGlyphTableList;
00879   // check if the current font is associated to a known glyph table
00880   for (PRInt32 i = 0; i < gGlyphTableList->Count(); i++) {
00881     nsGlyphTable* glyphTable = gGlyphTableList->TableAt(i);
00882     nsAutoString fontName;
00883     glyphTable->GetPrimaryFontName(fontName);
00884     if (fontName.Equals(aFamily, nsCaseInsensitiveStringComparator()) &&
00885         glyphTable->Has(currPresContext, currChar)) {
00886       currList->AppendElement(glyphTable); // the table is retained
00887       return PR_TRUE; // don't stop
00888     }
00889   }
00890   return PR_TRUE; // don't stop
00891 }
00892 
00893 nsresult
00894 nsGlyphTableList::GetListFor(nsPresContext* aPresContext,
00895                              nsMathMLChar*   aChar,
00896                              nsFont*         aFont,
00897                              nsVoidArray*    aGlyphTableList)
00898 {
00899   // @see the documentation of -moz-math-stretchy in mathml.css
00900   // for how this work
00901   aGlyphTableList->Clear();
00902   PRBool useDocumentFonts =
00903     aPresContext->GetCachedBoolPref(kPresContext_UseDocumentFonts);
00904 
00905   // Check to honor the pref("browser.display.use_document_fonts", 0)
00906   // Only include fonts from CSS if the pref to disallow authors' fonts isn't set
00907   if (useDocumentFonts) {
00908     // Convert the list of fonts in aFont (from -moz-math-stretchy)
00909     // to an ordered list of corresponding glyph extension tables
00910     StretchyFontEnumContext context = {aPresContext, aChar, aGlyphTableList};
00911     aFont->EnumerateFamilies(StretchyFontEnumCallback, &context);
00912   }
00913   if (!aGlyphTableList->Count()) {
00914     // No font was retained, fallback to our default tables
00915     PRInt32 count = Count();
00916     for (PRInt32 i = 0; i < count; i++) {
00917       nsGlyphTable* glyphTable = TableAt(i);
00918       if (glyphTable->Has(aPresContext, aChar)) {
00919         aGlyphTableList->AppendElement(glyphTable);
00920       }
00921     }
00922   }
00923   return NS_OK;
00924 }
00925 
00926 nsresult
00927 nsGlyphTableList::GetPreferredListAt(nsPresContext* aPresContext,
00928                                      PRInt32         aStartingIndex, 
00929                                      nsVoidArray*    aGlyphTableList,
00930                                      PRInt32*        aCount)
00931 {
00932   *aCount = 0;
00933   if (aStartingIndex == kNotFound) {
00934     return NS_OK;
00935   }
00936   nsAutoString fontName;
00937   PRInt32 index = aStartingIndex;
00938   NS_ASSERTION(index < Count(PR_TRUE), "invalid call");
00939   nsGlyphTable* glyphTable = TableAt(index);
00940   while (glyphTable) {
00941     glyphTable->GetPrimaryFontName(fontName);
00942     if (CheckFontExistence(aPresContext, fontName)) {
00943 #ifdef NOISY_SEARCH
00944       char str[50];
00945       fontName.ToCString(str, sizeof(str));
00946       printf("Found preferreed font %s\n", str);
00947 #endif
00948       if (index == aStartingIndex) {
00949         // At least one font is found, clear aGlyphTableList
00950         aGlyphTableList->Clear();
00951       }
00952       aGlyphTableList->AppendElement(glyphTable);
00953       ++*aCount;
00954     }
00955     glyphTable = TableAt(++index);
00956   } 
00957   // XXX append other tables if UseDocumentFonts is set?
00958   return NS_OK;
00959 }
00960 
00961 // -----------------------------------------------------------------------------------
00962 
00963 struct PreferredFontEnumContext {
00964   PRInt32   mCharIndex;
00965   PRBool    mIsFontForParts;
00966   PRInt32   mFontCount;
00967 };
00968 
00969 // mark a glyph table as a preferred table that can be used for a char
00970 static PRBool
00971 PreferredFontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
00972 {
00973   PRInt32 i;
00974   nsAutoString fontName;
00975   nsGlyphTable* glyphTable = nsnull;
00976   PreferredFontEnumContext* context = (PreferredFontEnumContext*)aData;
00977   // see if the table already exists in mTableList[0..mDefaultCount-1]
00978   PRBool found = PR_FALSE;
00979   PRInt32 count = gGlyphTableList->Count();
00980   for (i = 0; i < count; i++) {
00981     glyphTable = gGlyphTableList->TableAt(i);
00982     glyphTable->GetPrimaryFontName(fontName);
00983     if (fontName.Equals(aFamily, nsCaseInsensitiveStringComparator())) {
00984       found = PR_TRUE;
00985       break;
00986     }
00987   }
00988   if (!found) {
00989     // the table wasn't found in the default sharable list,
00990     // see if it exists in the additional list
00991     count = gGlyphTableList->AdditionalCount();
00992     for (i = 0; i < count; i++) {
00993       glyphTable = gGlyphTableList->AdditionalTableAt(i);
00994       glyphTable->GetPrimaryFontName(fontName);
00995       if (fontName.Equals(aFamily, nsCaseInsensitiveStringComparator())) {
00996         found = PR_TRUE;
00997         break;
00998       }
00999     }
01000     if (!found) {
01001       // the table wasn't found in the additional list either, add it now
01002       glyphTable = gGlyphTableList->AddAdditionalGlyphTable(aFamily);
01003       if (!glyphTable)
01004         return PR_FALSE; // stop in low-memory situations
01005     }
01006   }
01007 
01008   // Add the table to the list of preferred extension tables for this char
01009   if (!context->mFontCount) {
01010     // this is the first font to be retained, remember
01011     // the starting index where the first glyphTable was appended
01012     PRInt32 startingIndex = gGlyphTableList->Count(PR_TRUE);
01013     if (context->mIsFontForParts) {
01014       NS_ASSERTION(nsGlyphTableList::gParts[context->mCharIndex] == -1,
01015                    "remove duplicate property in mathfont.properties");
01016       nsGlyphTableList::gParts[context->mCharIndex] = startingIndex;
01017     }
01018     else {
01019       NS_ASSERTION(nsGlyphTableList::gVariants[context->mCharIndex] == -1,
01020                    "remove duplicate property in mathfont.properties");
01021       nsGlyphTableList::gVariants[context->mCharIndex] = startingIndex;
01022     }
01023   }
01024 
01025   gGlyphTableList->AppendTable(glyphTable);
01026   ++context->mFontCount;
01027 
01028   return PR_TRUE; // don't stop
01029 }
01030 
01031 // retrieve a pref value set by the user
01032 static PRBool
01033 GetPrefValue(nsIPrefBranch* aPrefBranch, const char* aPrefKey, nsString& aPrefValue)
01034 {
01035   aPrefValue.Truncate();
01036   if (aPrefBranch) {
01037     nsCOMPtr<nsISupportsString> prefString;
01038     aPrefBranch->GetComplexValue(aPrefKey,
01039                                  NS_GET_IID(nsISupportsString),
01040                                  getter_AddRefs(prefString));
01041     if (prefString) {
01042       prefString->GetData(aPrefValue);
01043     }
01044   }
01045   return !aPrefValue.IsEmpty();
01046 }
01047 
01048 // Store the list of preferred extension fonts for a char
01049 static void
01050 SetPreferredFonts(const char* aKey, nsString& aFamilyList)
01051 {
01052   NS_ASSERTION(30 < strlen(aKey), "invalid call");
01053 
01054   // expected key:
01055   // "font.mathfont-family.\uNNNN.base"     -- fonts for the base size
01056   // "font.mathfont-family.\uNNNN.parts"    -- fonts for partial glyphs
01057   // "font.mathfont-family.\uNNNN.variants" -- fonts for larger glyphs
01058   PRInt32 error = 0;
01059   // 22 is to skip "font.mathfont-family.\\u";
01060   PRUnichar uchar = nsCAutoString(aKey + 22).ToInteger(&error, 16);
01061   if (error) return;
01062   // 27 is to skip "font.mathfont-family.\\uNNNN"
01063   const char* extension = aKey + 27;
01064 
01065 #ifdef DEBUG_rbs
01066   char str[50];
01067   aFamilyList.ToCString(str, sizeof(str));
01068   printf("Setting preferred fonts for \\u%04X%s: %s\n", uchar, extension, str);
01069 #endif
01070 
01071   if (!strcmp(extension, ".base")) {
01072     // fonts to be used for the base size of the char (i.e., no stretching)
01073     nsBaseFontEntry* entry = nsGlyphTableList::gBaseFonts->AddEntry(uchar);
01074     if (entry) {
01075       entry->mFontFamily = aFamilyList;
01076     }
01077     return;
01078   }
01079 
01080   PRBool isFontForParts;
01081   if (!strcmp(extension, ".parts"))
01082     isFontForParts = PR_TRUE;
01083   else if (!strcmp(extension, ".variants"))
01084     isFontForParts = PR_FALSE;
01085   else return; // input is not applicable
01086 
01087   // Ensure that this is a valid stretchy operator
01088   PRInt32 k = nsMathMLOperators::FindStretchyOperator(uchar);
01089   if (k != kNotFound) {
01090     // We just want to iterate over the font-family list using the
01091     // callback mechanism that nsFont has...
01092     nsFont font(aFamilyList, 0, 0, 0, 0, 0);
01093     PreferredFontEnumContext context = {k, isFontForParts, 0};
01094     font.EnumerateFamilies(PreferredFontEnumCallback, &context);
01095     if (context.mFontCount) { // at least one font was retained
01096       // Append a null separator
01097       gGlyphTableList->AppendTable(nsnull);
01098     }
01099   }
01100 }
01101 
01102 struct MathFontEnumContext {
01103   nsPresContext* mPresContext;
01104   nsString*       mMissingFamilyList;
01105 };
01106 
01107 static PRBool
01108 MathFontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
01109 {
01110   // check if the font is missing
01111   MathFontEnumContext* context = (MathFontEnumContext*)aData;
01112   nsPresContext* presContext = context->mPresContext;
01113   nsString* missingFamilyList = context->mMissingFamilyList;
01114   if (!CheckFontExistence(presContext, aFamily)) {
01115 //#ifndef _WIN32
01116    // XXX In principle, the mathfont-family list in the mathfont.properties file
01117    // is customizable depending on the platform. For now, this is here since there
01118    // is no need to alert Linux users about TrueType fonts specific to Windows.
01119    if (aFamily.LowerCaseEqualsLiteral("mt extra"))
01120      return PR_TRUE; // continue to try other fonts
01121 //#endif
01122     if (!missingFamilyList->IsEmpty()) {
01123       missingFamilyList->AppendLiteral(", ");
01124     }
01125     missingFamilyList->Append(aFamily);
01126   }
01127 
01128   if (!gGlyphTableList->AddGlyphTable(aFamily))
01129     return PR_FALSE; // stop in low-memory situations
01130   return PR_TRUE; // don't stop
01131 }
01132 
01133 static nsresult
01134 InitGlobals(nsPresContext* aPresContext)
01135 {
01136   NS_ASSERTION(!gInitialized, "Error -- already initialized");
01137   gInitialized = PR_TRUE;
01138   PRUint32 count = nsMathMLOperators::CountStretchyOperator();
01139   if (!count) {
01140     // nothing to stretch, so why bother...
01141     return NS_OK;
01142   }
01143 
01144   // Allocate the placeholders for the preferred parts and variants
01145   nsresult rv = NS_ERROR_OUT_OF_MEMORY;
01146   gGlyphTableList = new nsGlyphTableList();
01147   nsGlyphTableList::gBaseFonts = new nsBaseFontHashtable();
01148   if (gGlyphTableList && nsGlyphTableList::gBaseFonts) {
01149     nsGlyphTableList::gParts = new PRInt32[count];
01150     nsGlyphTableList::gVariants = new PRInt32[count];
01151     if (nsGlyphTableList::gParts && nsGlyphTableList::gVariants) {
01152       rv = gGlyphTableList->Initialize();
01153     }
01154   }
01155   if (NS_FAILED(rv)) {
01156     delete gGlyphTableList;
01157     delete nsGlyphTableList::gBaseFonts;
01158     delete [] nsGlyphTableList::gParts;
01159     delete [] nsGlyphTableList::gVariants;
01160     gGlyphTableList = nsnull;
01161     nsGlyphTableList::gBaseFonts = nsnull;
01162     nsGlyphTableList::gParts = nsnull;
01163     nsGlyphTableList::gVariants = nsnull;
01164     return rv;
01165   }
01166   /*
01167   else
01168     The gGlyphTableList has been successfully registered as a shutdown observer.
01169     It will be deleted at shutdown, even if a failure happens below.
01170   */
01171 
01172   PRUint32 i;
01173   for (i = 0; i < count; i++) {
01174     nsGlyphTableList::gParts[i] = kNotFound; // i.e., -1
01175     nsGlyphTableList::gVariants[i] = kNotFound; // i.e., -1
01176   }
01177   nsGlyphTableList::gBaseFonts->Init(5);
01178 
01179   nsCAutoString key;
01180   nsAutoString value;
01181   nsCOMPtr<nsIPersistentProperties> mathfontProp;
01182   nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
01183 
01184   // Add the math fonts in the gGlyphTableList in order of preference ...
01185   // Note: we only load font-names at this stage. The actual glyph tables will
01186   // be loaded lazily (see nsGlyphTable::ElementAt()).
01187 
01188   // Load the "mathfont.properties" file
01189   value.Truncate();
01190   rv = LoadProperties(value, mathfontProp);
01191   if (NS_FAILED(rv)) return rv;
01192 
01193   // Load the "mathfontPUA.properties" file
01194   value.AssignLiteral("PUA");
01195   rv = LoadProperties(value, gPUAProperties);
01196   if (NS_FAILED(rv)) return rv;
01197 
01198   // Get the default list of mathfonts to be used for stretchy characters
01199   nsFont font(nsnull, 0, 0, 0, 0, 0);
01200   NS_NAMED_LITERAL_CSTRING(defaultKey, "font.mathfont-family");
01201   if (!GetPrefValue(prefBranch, defaultKey.get(), font.name)) {
01202     // fallback to the internal default list
01203     rv = mathfontProp->GetStringProperty(defaultKey, font.name);
01204     if (NS_FAILED(rv)) return rv;
01205   }
01206 
01207   // Parse the font list and append an entry for each family to gGlyphTableList
01208   nsAutoString missingFamilyList;
01209   MathFontEnumContext context = {aPresContext, &missingFamilyList};
01210   font.EnumerateFamilies(MathFontEnumCallback, &context);
01211   // Append a null separator
01212   gGlyphTableList->AppendTable(nsnull);
01213 
01214   // alert the user if some of the expected fonts are missing
01215   if (!missingFamilyList.IsEmpty()) {
01216     AlertMissingFonts(missingFamilyList);
01217   }
01218 
01219   // Let the particular characters have their preferred fonts
01220 
01221   // First, look the prefs of the user
01222   char **allKey = nsnull;
01223   prefBranch->GetChildList("font.mathfont-family.", &count, &allKey);    
01224   for (i = 0; i < count; ++i) {
01225 #ifdef DEBUG_rbs
01226     char str[50];
01227     GetPrefValue(prefBranch, allKey[i], value);
01228     value.ToCString(str, sizeof(str));
01229     printf("Found user pref %s: %s\n", allKey[i], str);
01230 #endif
01231     if ((30 < strlen(allKey[i])) && 
01232         GetPrefValue(prefBranch, allKey[i], value)) {
01233       SetPreferredFonts(allKey[i], value);
01234     }
01235   }
01236   NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, allKey);
01237 
01238   // Next, look our internal settings
01239   nsCOMPtr<nsISimpleEnumerator> iterator;
01240   if (NS_SUCCEEDED(mathfontProp->Enumerate(getter_AddRefs(iterator)))) {
01241     PRBool more;
01242     while ((NS_SUCCEEDED(iterator->HasMoreElements(&more))) && more) {
01243       nsCOMPtr<nsIPropertyElement> element;
01244       if (NS_SUCCEEDED(iterator->GetNext(getter_AddRefs(element)))) {
01245         if (NS_SUCCEEDED(element->GetKey(key))) {
01246           if ((30 < key.Length()) && 
01247               (0 == key.Find("font.mathfont-family.\\u")) &&
01248               !GetPrefValue(prefBranch, key.get(), value) && // priority to user
01249               NS_SUCCEEDED(element->GetValue(value))) {
01250             Clean(value);
01251             SetPreferredFonts(key.get(), value);
01252           }
01253         }
01254       }
01255     }
01256   }
01257   return rv;
01258 }
01259 
01260 // helper to override CSS and set the default font-family list to be used
01261 // for the base size of a particular character (i.e., in the situation where
01262 // stretching doesn't happen).
01263 static void
01264 SetBaseFamily(PRUnichar aChar, nsFont& aFont)
01265 {
01266   if (!nsGlyphTableList::gBaseFonts) return;
01267   nsBaseFontEntry* entry = nsGlyphTableList::gBaseFonts->GetEntry(aChar);
01268   if (entry) {
01269     aFont.name.Assign(entry->mFontFamily);
01270   }
01271 }
01272 
01273 // -----------------------------------------------------------------------------------
01274 // And now the implementation of nsMathMLChar
01275 
01276 nsStyleContext*
01277 nsMathMLChar::GetStyleContext() const
01278 {
01279   NS_ASSERTION(!mParent, "invalid call - not allowed for child chars");
01280   NS_ASSERTION(mStyleContext, "chars shoud always have style context");
01281   return mStyleContext;
01282   return NS_OK;
01283 }
01284 
01285 void
01286 nsMathMLChar::SetStyleContext(nsStyleContext* aStyleContext)
01287 {
01288   NS_ASSERTION(!mParent, "invalid call - not allowed for child chars");
01289   NS_PRECONDITION(aStyleContext, "null ptr");
01290   if (aStyleContext != mStyleContext) {
01291     if (mStyleContext)
01292       mStyleContext->Release();
01293     if (aStyleContext) {
01294       mStyleContext = aStyleContext;
01295       aStyleContext->AddRef();
01296 
01297       // Sync the pointers of child chars.
01298       nsMathMLChar* child = mSibling;
01299       while (child) {
01300         child->mStyleContext = mStyleContext;
01301         child = child->mSibling;
01302       }
01303     }
01304   }
01305 }
01306 
01307 void
01308 nsMathMLChar::SetData(nsPresContext* aPresContext,
01309                       nsString&       aData)
01310 {
01311   NS_ASSERTION(!mParent, "invalid call - not allowed for child chars");
01312   if (!gInitialized) {
01313     InitGlobals(aPresContext);
01314   }
01315   mData = aData;
01316   // some assumptions until proven otherwise
01317   // note that mGlyph is not initialized
01318   mOperator = -1;
01319   mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
01320   mBoundingMetrics.Clear();
01321   mGlyphTable = nsnull;
01322   // check if stretching is applicable ...
01323   if (gGlyphTableList && (1 == mData.Length())) {
01324     mOperator = nsMathMLOperators::FindStretchyOperator(mData[0]);
01325     if (mOperator >= 0) {
01326       mDirection = nsMathMLOperators::GetStretchyDirectionAt(mOperator);
01327       // default tentative table (not the one that is necessarily going to be used)
01328       mGlyphTable = gGlyphTableList->GetGlyphTableFor(aPresContext, this);
01329       // commom case: we won't bother with the stretching if there is
01330       // no glyph table for us...
01331       if (!mGlyphTable) {
01332         // never try to stretch this operator again
01333         nsMathMLOperators::DisableStretchyOperatorAt(mOperator);
01334         mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
01335         mOperator = -1;
01336       }
01337     }
01338   }
01339 }
01340 
01341 // -----------------------------------------------------------------------------------
01342 /*
01343  The Stretch:
01344  @param aContainerSize - suggested size for the stretched char
01345  @param aDesiredStretchSize - OUT parameter. The desired size
01346  after stretching. If no stretching is done, the output will
01347  simply give the base size.
01348 
01349  How it works?
01350  Summary:-
01351  The Stretch() method first looks for a glyph of appropriate
01352  size; If a glyph is found, it is cached by this object and
01353  its size is returned in aDesiredStretchSize. The cached
01354  glyph will then be used at the painting stage.
01355  If no glyph of appropriate size is found, a search is made
01356  to see if the char can be built by parts.
01357 
01358  Details:-
01359  A character gets stretched through the following pipeline :
01360 
01361  1) If the base size of the char is sufficient to cover the
01362     container' size, we use that. If not, it will still be
01363     used as a fallback if the other stages in the pipeline fail.
01364     Issues :
01365     a) The base size, the parts and the variants of a char can
01366        be in different fonts. For eg., the base size for '(' should
01367        come from a normal ascii font if CMEX10 is used, since CMEX10
01368        only contains the stretched versions. Hence, there are two
01369        style contexts in use throughout the process. The leaf style
01370        context of the char holds fonts with which to try to stretch
01371        the char. The parent style context of the char contains fonts
01372        for normal rendering. So the parent context is the one used
01373        to get the initial base size at the start of the pipeline.
01374     b) For operators that can be largeop's in display mode,
01375        we will skip the base size even if it fits, so that
01376        the next stage in the pipeline is given a chance to find
01377        a largeop variant. If the next stage fails, we fallback
01378        to the base size.
01379 
01380  2) We search for the first larger variant of the char that fits the
01381     container' size. We search fonts for larger variants in the order
01382     specified in the list of stretchy fonts held by the leaf style
01383     context (from -moz-math-stretchy in mathml.css).
01384     Issues :
01385     a) the largeop and display settings determine the starting
01386        size when we do the above search, regardless of whether
01387        smaller variants already fit the container' size.
01388     b) if it is a largeopOnly request (i.e., a displaystyle operator
01389        with largeop=true and stretchy=false), we break after finding
01390        the first starting variant, regardless of whether that
01391        variant fits the container's size.
01392 
01393  3) If a variant of appropriate size wasn't found, we see if the char
01394     can be built by parts. We search the ordered list of stretchy fonts
01395     for the first font which has parts that fit the container' size.
01396     Issues:
01397     a) Certain chars like over/underbrace in CMEX10 have to be built
01398        from two half stretchy chars and joined in the middle. Such
01399        chars are handled in a special manner. When this situation is
01400        detected, the initial char (referred to as "parent") creates a
01401        singly-linked list of child chars, asking them to stretch in
01402        a divided space. A convention is used in the the setup of
01403        nsGlyphTable to express that a composite parent char can be built
01404        from child chars.
01405     b) There are some chars that have no middle and glue glyphs. For
01406        such chars, the parts need to be joined using the rule.
01407        By convention (TeXbook p.225), the descent of the parts is
01408        zero while their ascent gives the thickness of the rule that
01409        should be used to join them.
01410 
01411  Of note:
01412  When the pipeline completes successfully, the desired size of the
01413  stretched char can actually be slighthly larger or smaller than
01414  aContainerSize. But it is the responsibility of the caller to
01415  account for the spacing when setting aContainerSize, and to leave
01416  any extra margin when placing the stretched char.
01417 */
01418 // -----------------------------------------------------------------------------------
01419 
01420 
01421 // plain TeX settings (TeXbook p.152)
01422 #define NS_MATHML_DELIMITER_FACTOR      0.901f
01423 #define NS_MATHML_DELIMITER_SHORTFALL   NSFloatPointsToTwips(5.0f)
01424 
01425 static PRBool
01426 IsSizeOK(nscoord a, nscoord b, PRUint32 aHint)
01427 {
01428   // Normal: True if 'a' is around +/-10% of the target 'b' (10% is
01429   // 1-DelimiterFactor). This often gives a chance to the base size to
01430   // win, especially in the context of <mfenced> without tall elements
01431   // or in sloppy markups without protective <mrow></mrow>
01432   PRBool isNormal =
01433     (aHint & NS_STRETCH_NORMAL)
01434     && PRBool(float(PR_ABS(a - b))
01435               < (1.0f - NS_MATHML_DELIMITER_FACTOR) * float(b));
01436   // Nearer: True if 'a' is around max{ +/-10% of 'b' , 'b' - 5pt },
01437   // as documented in The TeXbook, Ch.17, p.152.
01438   PRBool isNearer = PR_FALSE;
01439   if (aHint & (NS_STRETCH_NEARER | NS_STRETCH_LARGEOP)) {
01440     float c = PR_MAX(float(b) * NS_MATHML_DELIMITER_FACTOR,
01441                      float(b) - NS_MATHML_DELIMITER_SHORTFALL);
01442     isNearer = PRBool(float(PR_ABS(b - a)) <= (float(b) - c));
01443   }
01444   // Smaller: Mainly for transitory use, to compare two candidate
01445   // choices
01446   PRBool isSmaller =
01447     (aHint & NS_STRETCH_SMALLER)
01448     && PRBool((float(a) >= (NS_MATHML_DELIMITER_FACTOR * float(b)))
01449               && (a <= b));
01450   // Larger: Critical to the sqrt code to ensure that the radical
01451   // size is tall enough
01452   PRBool isLarger =
01453     (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP))
01454     && PRBool(a >= b);
01455   return (isNormal || isSmaller || isNearer || isLarger);
01456 }
01457 
01458 static PRBool
01459 IsSizeBetter(nscoord a, nscoord olda, nscoord b, PRUint32 aHint)
01460 {
01461   if (0 == olda) return PR_TRUE;
01462   if (PR_ABS(a - b) < PR_ABS(olda - b)) {
01463     if (aHint & (NS_STRETCH_NORMAL | NS_STRETCH_NEARER))
01464       return PR_TRUE;
01465     if (aHint & NS_STRETCH_SMALLER)
01466       return PRBool(a < olda);
01467     if (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP))
01468       return PRBool(a > olda);
01469   }
01470   return PR_FALSE;
01471 }
01472 
01473 // We want to place the glyphs even when they don't fit at their
01474 // full extent, i.e., we may clip to tolerate a small amount of
01475 // overlap between the parts. This is important to cater for fonts
01476 // with long glues.
01477 static nscoord
01478 ComputeSizeFromParts(nsGlyphCode* aGlyphs,
01479                      nscoord*     aSizes,
01480                      nscoord      aTargetSize,
01481                      PRUint32     aHint)
01482 {
01483   enum {first, middle, last, glue};
01484   float flex[] = {0.901f, 0.901f, 0.901f};
01485   // refine the flexibility depending on whether some parts can be left out
01486   if (aGlyphs[glue] == aGlyphs[middle]) flex[middle] = 0.0f;
01487   if (aGlyphs[glue] == aGlyphs[first]) flex[first] = 0.0f;
01488   if (aGlyphs[glue] == aGlyphs[last]) flex[last] = 0.0f;
01489 
01490   // get the minimum allowable size
01491   nscoord computedSize = nscoord(flex[first] * aSizes[first] +
01492                                  flex[middle] * aSizes[middle] +
01493                                  flex[last] * aSizes[last]);
01494 
01495   if (computedSize <= aTargetSize) {
01496     // if we can afford more room, the default is to fill-up the target area
01497     return aTargetSize;
01498   }
01499   if (IsSizeOK(computedSize, aTargetSize, aHint)) {
01500     // settle with the size, and let Paint() do the rest
01501     return computedSize;
01502   }
01503   // reject these parts
01504   return 0;
01505 }
01506 
01507 // Put aFamily in the first position of aFont to guarantee that our
01508 // desired font is the one that the GFX font sub-system will use
01509 inline void
01510 SetFirstFamily(nsFont& aFont, const nsString& aFamily)
01511 {
01512   // overwrite the old value of font-family:
01513   aFont.name.Assign(aFamily);
01514 }
01515 
01516 nsresult
01517 nsMathMLChar::Stretch(nsPresContext*      aPresContext,
01518                       nsIRenderingContext& aRenderingContext,
01519                       nsStretchDirection   aStretchDirection,
01520                       nsBoundingMetrics&   aContainerSize,
01521                       nsBoundingMetrics&   aDesiredStretchSize,
01522                       PRUint32             aStretchHint)
01523 {
01524   nsresult rv = NS_OK;
01525   nsStretchDirection direction = aStretchDirection;
01526 
01527   // if we have been called before, and we didn't actually stretch, our
01528   // direction may have been set to NS_STRETCH_DIRECTION_UNSUPPORTED.
01529   // So first set our direction back to its instrinsic value
01530   if (mOperator >= 0) {
01531     // mOperator is initialized in SetData() and remains unchanged
01532     mDirection = nsMathMLOperators::GetStretchyDirectionAt(mOperator);
01533   }
01534 
01535   // if no specified direction, attempt to stretch in our preferred direction
01536   if (direction == NS_STRETCH_DIRECTION_DEFAULT) {
01537     direction = mDirection;
01538   }
01539 
01540   // Set default font and get the default bounding metrics
01541   // mStyleContext is a leaf context used only when stretching happens.
01542   // For the base size, the default font should come from the parent context
01543   nsAutoString fontName;
01544   nsFont theFont(mStyleContext->GetParent()->GetStyleFont()->mFont);
01545 
01546   // Override with specific fonts if applicable for this character
01547   PRUnichar uchar = mData[0];
01548   SetBaseFamily(uchar, theFont);
01549   aRenderingContext.SetFont(theFont, nsnull);
01550   rv = aRenderingContext.GetBoundingMetrics(mData.get(),
01551                                             PRUint32(mData.Length()),
01552                                             mBoundingMetrics);
01553   if (NS_FAILED(rv)) {
01554     NS_WARNING("GetBoundingMetrics failed");
01555     // ensure that the char later behaves like a normal char
01556     // (will be reset back to its intrinsic value in case of dynamic updates)
01557     mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
01558     return rv;
01559   }
01560 
01561   // set the default desired metrics in case stretching doesn't happen
01562   aDesiredStretchSize = mBoundingMetrics;
01563 
01564   // quick return if there is nothing special about this char
01565   if (!mGlyphTable || (mDirection != direction)) {
01566     // ensure that the char later behaves like a normal char
01567     // (will be reset back to its intrinsic value in case of dynamic updates)
01568     mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
01569     return NS_OK;
01570   }
01571 
01572   // see if this is a particular largeop or largeopOnly request
01573   PRBool largeop = (NS_STRETCH_LARGEOP & aStretchHint) != 0;
01574   PRBool largeopOnly = (NS_STRETCH_LARGEOP == aStretchHint); // (==, not mask!)
01575 
01577   // 1. Check the common situations where stretching is not actually needed
01579 
01580   nscoord targetSize, charSize;
01581   PRBool isVertical = (direction == NS_STRETCH_DIRECTION_VERTICAL);
01582   if (isVertical) {
01583     charSize = aDesiredStretchSize.ascent + aDesiredStretchSize.descent;
01584     targetSize = aContainerSize.ascent + aContainerSize.descent;
01585   }
01586   else {
01587     charSize = aDesiredStretchSize.width;
01588     targetSize = aContainerSize.width;
01589   }
01590   // if we are not a largeop in display mode, return if size fits
01591   if ((targetSize <= 0) || 
01592       (!largeop && ((isVertical && charSize >= targetSize) ||
01593                     IsSizeOK(charSize, targetSize, aStretchHint)))) {
01594     // ensure that the char later behaves like a normal char
01595     // (will be reset back to its intrinsic value in case of dynamic updates)
01596     mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
01597     return NS_OK;
01598   }
01599 
01601   // 2. Try to search if there is a glyph of appropriate size
01603 
01604   PRInt32 size;
01605   nsGlyphTable* glyphTable;
01606   nsBoundingMetrics bm;
01607   nsGlyphCode startingGlyph = {uchar, 0}; // code@font
01608   nsGlyphCode ch;
01609 
01610   // this will be the best glyph that we encounter during the search...
01611   nsGlyphCode bestGlyph = startingGlyph;
01612   nsGlyphTable* bestGlyphTable = mGlyphTable;
01613   nsBoundingMetrics bestbm = mBoundingMetrics;
01614 
01615   // use our stretchy style context now that stretching is in progress
01616   theFont = mStyleContext->GetStyleFont()->mFont;
01617 
01618   // initialize the search list for this char
01619   PRBool alreadyCSS = PR_FALSE;
01620   nsAutoVoidArray tableList;
01621   // see if there are user-specified preferred tables for the variants of this char
01622   PRInt32 count, t = nsGlyphTableList::gVariants[mOperator];
01623   gGlyphTableList->GetPreferredListAt(aPresContext, t, &tableList, &count);
01624   if (!count) {
01625     // get a list that attempts to honor the css font-family
01626     gGlyphTableList->GetListFor(aPresContext, this, &theFont, &tableList);
01627     alreadyCSS = PR_TRUE;
01628   }
01629 
01630 #ifdef NOISY_SEARCH
01631   printf("Searching in %d fonts for a glyph of appropriate size for: 0x%04X:%c\n",
01632           tableList.Count(), uchar, uchar&0x00FF);
01633 #endif
01634 
01635   count = tableList.Count();
01636   for (t = 0; t < count; t++) {
01637     // see if this table has a glyph that matches the container
01638     glyphTable = NS_STATIC_CAST(nsGlyphTable*, tableList.ElementAt(t));
01639     // figure out the starting size : if this is a largeop, start at 2 else 1
01640     size = 1; // size=0 is the char at its normal size
01641     if (largeop && glyphTable->BigOf(aPresContext, this, 2)) {
01642       size = 2;
01643     }
01644     glyphTable->GetPrimaryFontName(fontName);
01645     SetFirstFamily(theFont, fontName);
01646     aRenderingContext.SetFont(theFont, nsnull);
01647 #ifdef NOISY_SEARCH
01648     char str[50];
01649     fontName.ToCString(str, sizeof(str));
01650     printf("  searching in %s ...\n", str);
01651 #endif
01652     ch = glyphTable->BigOf(aPresContext, this, size++);
01653     while (ch) {
01654       NS_ASSERTION(ch != uchar, "glyph table incorrectly set -- duplicate found");
01655       rv = glyphTable->GetBoundingMetrics(aRenderingContext, theFont, ch, bm);
01656       if (NS_SUCCEEDED(rv)) {
01657         charSize = (isVertical)
01658                  ? bm.ascent + bm.descent
01659                  : bm.rightBearing - bm.leftBearing;
01660         // always break when largeopOnly is set
01661         if (largeopOnly || IsSizeOK(charSize, targetSize, aStretchHint)) {
01662 #ifdef NOISY_SEARCH
01663           printf("    size:%d OK!\n", size-1);
01664 #endif
01665           bestbm = bm;
01666           bestGlyphTable = glyphTable;
01667           bestGlyph = ch;
01668           goto done; // get out...
01669         }
01670         nscoord oldSize = (isVertical)
01671                         ? bestbm.ascent + bestbm.descent
01672                         : bestbm.rightBearing - bestbm.leftBearing;
01673         if (IsSizeBetter(charSize, oldSize, targetSize, aStretchHint)) {
01674           bestGlyphTable = glyphTable;
01675           bestGlyph = ch;
01676           bestbm = bm;
01677 #ifdef NOISY_SEARCH
01678           printf("    size:%d Current best\n", size-1);
01679         }
01680         else {
01681           printf("    size:%d Rejected!\n", size-1);
01682 #endif
01683         }
01684       }
01685       // if largeopOnly is set, break now
01686       if (largeopOnly) break;
01687       ch = glyphTable->BigOf(aPresContext, this, size++);
01688     }
01689   }
01690   if (largeopOnly) goto done; // the user doesn't want to stretch
01691 
01693   // Build by parts. If no glyph of appropriate size was found, see if we can
01694   // build the char by parts. If there are preferred tables, they are used. Otherwise,
01695   // search for the first table with suitable parts for this char
01697 
01698   // see if there are user-specified preferred tables for the parts of this char
01699   t = nsGlyphTableList::gParts[mOperator];
01700   gGlyphTableList->GetPreferredListAt(aPresContext, t, &tableList, &count);
01701   if (!count && !alreadyCSS) {
01702     // we didn't do this earlier... so we need to do it here:
01703     // get a list that attempts to honor the css font-family
01704     gGlyphTableList->GetListFor(aPresContext, this, &theFont, &tableList);
01705   }
01706 
01707 #ifdef NOISY_SEARCH
01708   printf("Searching in %d fonts for the first font with suitable parts for: 0x%04X:%c\n",
01709           tableList.Count(), uchar, uchar&0x00FF);
01710 #endif
01711 
01712   count = tableList.Count();
01713   for (t = 0; t < count; t++) {
01714     glyphTable = NS_STATIC_CAST(nsGlyphTable*, tableList.ElementAt(t));
01715     if (!glyphTable->HasPartsOf(aPresContext, this)) continue; // to next table
01716 
01717     // See if this is a composite character //////////////////////////////////////////
01718     if (glyphTable->IsComposite(aPresContext, this)) {
01719       // let the child chars do the job
01720       nsBoundingMetrics compositeSize;
01721       rv = ComposeChildren(aPresContext, aRenderingContext, glyphTable,
01722                            aContainerSize, compositeSize, aStretchHint);
01723 #ifdef NOISY_SEARCH
01724       char str[50];
01725       fontName.ToCString(str, sizeof(str));
01726       printf("    Composing %d chars in font %s %s!\n",
01727              glyphTable->ChildCountOf(aPresContext, this), str,
01728              NS_SUCCEEDED(rv)? "OK" : "Rejected");
01729 #endif
01730       if (NS_FAILED(rv)) continue; // to next table
01731 
01732       // all went well, painting will be delegated from now on to children
01733       mGlyph = kNullGlyph; // this will tell paint to build by parts
01734       mGlyphTable = glyphTable;
01735       mBoundingMetrics = compositeSize;
01736       aDesiredStretchSize = compositeSize;
01737       return NS_OK; // get out ...
01738     }
01739 
01740     // See if the parts of this table fit in the desired space ///////////////////////
01741     glyphTable->GetPrimaryFontName(fontName);
01742     SetFirstFamily(theFont, fontName);
01743     aRenderingContext.SetFont(theFont, nsnull);
01744     // Compute the bounding metrics of all partial glyphs
01745     PRInt32 i;
01746     nsGlyphCode chdata[4];
01747     nsBoundingMetrics bmdata[4];
01748     nscoord computedSize, sizedata[4];
01749     nsGlyphCode glue = glyphTable->GlueOf(aPresContext, this);
01750     for (i = 0; i < 4; i++) {
01751       switch (i) {
01752         case 0: ch = glyphTable->TopOf(aPresContext, this);    break;
01753         case 1: ch = glyphTable->MiddleOf(aPresContext, this); break;
01754         case 2: ch = glyphTable->BottomOf(aPresContext, this); break;
01755         case 3: ch = glue;                                     break;
01756       }
01757       // empty slots are filled with the glue if it is not null
01758       if (!ch) ch = glue;
01759       if (!ch) { // glue is null, set bounding metrics to 0
01760         bm.Clear();
01761       }
01762       else {
01763         rv = glyphTable->GetBoundingMetrics(aRenderingContext, theFont, ch, bm);
01764         if (NS_FAILED(rv)) {
01765           // stop if we failed to compute the bounding metrics of a part.
01766           NS_WARNING("GetBoundingMetrics failed");
01767           break;
01768         }
01769       }
01770       chdata[i] = ch;
01771       bmdata[i] = bm;
01772       sizedata[i] = (isVertical)
01773                   ? bm.ascent + bm.descent
01774                   : bm.rightBearing - bm.leftBearing;
01775     }
01776     if (NS_FAILED(rv)) continue; // to next table
01777 
01778     // Build by parts if we have successfully computed the
01779     // bounding metrics of all parts.
01780     computedSize = ComputeSizeFromParts(chdata, sizedata, targetSize, aStretchHint);
01781 #ifdef NOISY_SEARCH
01782     char str[50];
01783     fontName.ToCString(str, sizeof(str));
01784     printf("    Font %s %s!\n", str, (computedSize) ? "OK" : "Rejected");
01785 #endif
01786     if (!computedSize) continue; // to next table
01787 
01788     // the computed size is suitable for the available space...
01789     // now is the time to compute and cache our bounding metrics
01790     if (isVertical) {
01791       nscoord lbearing = bmdata[0].leftBearing;
01792       nscoord rbearing = bmdata[0].rightBearing;
01793       nscoord width = bmdata[0].width;
01794       for (i = 1; i < 4; i++) {
01795         bm = bmdata[i];
01796         if (width < bm.width) width = bm.width;
01797         if (lbearing > bm.leftBearing) lbearing = bm.leftBearing;
01798         if (rbearing < bm.rightBearing) rbearing = bm.rightBearing;
01799       }
01800       bestbm.width = width;
01801       bestbm.ascent = bmdata[0].ascent; // Yes top, so that it works with TeX sqrt!
01802       bestbm.descent = computedSize - bestbm.ascent;
01803       bestbm.leftBearing = lbearing;
01804       bestbm.rightBearing = rbearing;
01805     }
01806     else {
01807       nscoord ascent = bmdata[0].ascent;
01808       nscoord descent = bmdata[0].descent;
01809       for (i = 1; i < 4; i++) {
01810         bm = bmdata[i];
01811         if (ascent < bm.ascent) ascent = bm.ascent;
01812         if (descent < bm.descent) descent = bm.descent;
01813       }
01814       bestbm.width = computedSize;
01815       bestbm.ascent = ascent;
01816       bestbm.descent = descent;
01817       bestbm.leftBearing = 0;
01818       bestbm.rightBearing = computedSize;
01819     }
01820     // reset
01821     bestGlyph = kNullGlyph; // this will tell paint to build by parts
01822     bestGlyphTable = glyphTable;
01823     goto done; // get out...
01824   }
01825 #ifdef NOISY_SEARCH
01826   printf("    No font with suitable parts found\n");
01827 #endif
01828   // if sum of parts doesn't fit in the space... we will use a single
01829   // glyph -- the base size or the best glyph encountered during the search
01830 
01831 done:
01832   if (bestGlyph == startingGlyph) { // nothing happened
01833     // ensure that the char behaves like a normal char
01834     // (will be reset back to its intrinsic value in case of dynamic updates)
01835     mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
01836   }
01837   else {
01838     // will stretch
01839     mGlyph = bestGlyph; // note that this can be null to tell paint to build by parts
01840     mGlyphTable = bestGlyphTable;
01841     mBoundingMetrics = bestbm;
01842     aDesiredStretchSize = bestbm;
01843   }
01844   return NS_OK;
01845 }
01846 
01847 nsresult
01848 nsMathMLChar::ComposeChildren(nsPresContext*      aPresContext,
01849                               nsIRenderingContext& aRenderingContext,
01850                               nsGlyphTable*        aGlyphTable,
01851                               nsBoundingMetrics&   aContainerSize,
01852                               nsBoundingMetrics&   aCompositeSize,
01853                               PRUint32             aStretchHint)
01854 {
01855   PRInt32 i = 0;
01856   nsMathMLChar* child;
01857   PRInt32 count = aGlyphTable->ChildCountOf(aPresContext, this);
01858   NS_ASSERTION(count, "something is wrong somewhere");
01859   if (!count) return NS_ERROR_FAILURE;
01860   // if we haven't been here before, create the linked list of children now
01861   // otherwise, use what we have, adding more children as needed or deleting the extra
01862   nsMathMLChar* last = this;
01863   while ((i < count) && last->mSibling) {
01864     i++;
01865     last = last->mSibling;
01866   }
01867   while (i < count) {
01868     child = new nsMathMLChar(this);
01869     if (!child) {
01870       if (mSibling) delete mSibling; // don't leave a dangling list ...
01871       mSibling = nsnull;
01872       return NS_ERROR_OUT_OF_MEMORY;
01873     }
01874     last->mSibling = child;
01875     last = child;
01876     i++;
01877   }
01878   if (last->mSibling) {
01879     delete last->mSibling;
01880     last->mSibling = nsnull;
01881   }
01882   // let children stretch in an equal space
01883   nsBoundingMetrics splitSize(aContainerSize);
01884   if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
01885     splitSize.width /= count;
01886   else {
01887     splitSize.ascent = ((splitSize.ascent + splitSize.descent) / count) / 2;
01888     splitSize.descent = splitSize.ascent;
01889   }
01890   nscoord dx = 0, dy = 0;
01891   for (i = 0, child = mSibling; child; child = child->mSibling, i++) {
01892     // child chars should just inherit our values - which may change between calls...
01893     child->mData = mData;
01894     child->mOperator = mOperator;
01895     child->mDirection = mDirection;
01896     child->mStyleContext = mStyleContext;
01897     child->mGlyphTable = aGlyphTable; // the child is associated to this table
01898     // there goes the Stretch() ...
01899     nsBoundingMetrics childSize;
01900     nsresult rv = child->Stretch(aPresContext, aRenderingContext, mDirection,
01901                                  splitSize, childSize, aStretchHint);
01902     // check if something went wrong or the child couldn't fit in the alloted space
01903     if (NS_FAILED(rv) || (NS_STRETCH_DIRECTION_UNSUPPORTED == child->mDirection)) {
01904       delete mSibling; // don't leave a dangling list behind ...
01905       mSibling = nsnull;
01906       return NS_ERROR_FAILURE;
01907     }
01908     child->SetRect(nsRect(dx, dy, childSize.width, childSize.ascent+childSize.descent));
01909     if (0 == i)
01910       aCompositeSize = childSize;
01911     else {
01912       if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
01913         aCompositeSize += childSize;
01914       else {
01915         aCompositeSize.descent += childSize.ascent + childSize.descent;
01916         if (aCompositeSize.leftBearing > childSize.leftBearing)
01917           aCompositeSize.leftBearing = childSize.leftBearing;
01918         if (aCompositeSize.rightBearing < childSize.rightBearing)
01919           aCompositeSize.rightBearing = childSize.rightBearing;
01920       }
01921     }
01922     if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
01923       dx += childSize.width;
01924     else
01925       dy += childSize.ascent + childSize.descent;
01926   }
01927   return NS_OK;
01928 }
01929 
01930 nsresult
01931 nsMathMLChar::Paint(nsPresContext*      aPresContext,
01932                     nsIRenderingContext& aRenderingContext,
01933                     const nsRect&        aDirtyRect,
01934                     nsFramePaintLayer    aWhichLayer,
01935                     nsIFrame*            aForFrame,
01936                     const nsRect*        aSelectedRect)
01937 {
01938   nsresult rv = NS_OK;
01939   nsStyleContext* parentContext = mStyleContext->GetParent();
01940   nsStyleContext* styleContext = mStyleContext;
01941 
01942   if (NS_STRETCH_DIRECTION_UNSUPPORTED == mDirection) {
01943     // normal drawing if there is nothing special about this char
01944     // Set default context to the parent context
01945     styleContext = parentContext;
01946   }
01947 
01948   if (!styleContext->GetStyleVisibility()->IsVisible())
01949     return NS_OK;
01950 
01951   // if the leaf style context that we use for stretchy chars has a background
01952   // color we use it -- this feature is mostly used for testing and debugging
01953   // purposes. Normally, users will set the background on the container frame.
01954   if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
01955     // paint the selection background -- beware MathML frames overlap a lot
01956     if (aSelectedRect && !aSelectedRect->IsEmpty()) {
01957       // get color to use for selection from the look&feel object
01958       nscolor bgColor = NS_RGB(0, 0, 0);
01959       aPresContext->LookAndFeel()->
01960        GetColor(nsILookAndFeel::eColor_TextSelectBackground, bgColor);
01961       aRenderingContext.SetColor(bgColor);
01962       aRenderingContext.FillRect(*aSelectedRect);
01963     }
01964     else if (mRect.width && mRect.height) {
01965       const nsStyleBorder* border = styleContext->GetStyleBorder();
01966       const nsStylePadding* padding = styleContext->GetStylePadding();
01967       const nsStyleBackground* backg = styleContext->GetStyleBackground();
01968       nsRect rect(mRect); //0, 0, mRect.width, mRect.height);
01969       if (styleContext != parentContext &&
01970           0 == (backg->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT))
01971         nsCSSRendering::PaintBackgroundWithSC(aPresContext, aRenderingContext, aForFrame,
01972                                               aDirtyRect, rect, *backg, *border, *padding,
01973                                               PR_TRUE);
01974       //else
01975       //  our container frame will take care of painting its background
01976       //  nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, aForFrame,
01977       //                                  aDirtyRect, rect, *border, *padding, PR_TRUE);
01978 #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
01979       // for visual debug
01980       PRIntn skipSides = 0; //aForFrame->GetSkipSides();
01981       const nsStyleOutline* outline = styleContext->GetStyleOutline();
01982       nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, aForFrame,
01983                                   aDirtyRect, rect, *border, styleContext, skipSides);
01984       nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, aForFrame,
01985                                    aDirtyRect, rect, *border, *outline, styleContext, 0);
01986 #endif
01987     }
01988   }
01989 
01990   if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
01991     // Set color ...
01992     nscolor fgColor = styleContext->GetStyleColor()->mColor;
01993     if (aSelectedRect && !aSelectedRect->IsEmpty()) {
01994       // get color to use for selection from the look&feel object
01995       aPresContext->LookAndFeel()->
01996        GetColor(nsILookAndFeel::eColor_TextSelectForeground, fgColor);
01997     }
01998     aRenderingContext.SetColor(fgColor);
01999 
02000     nsAutoString fontName;
02001     nsFont theFont(styleContext->GetStyleFont()->mFont);
02002 
02003     if (NS_STRETCH_DIRECTION_UNSUPPORTED == mDirection) {
02004       // normal drawing if there is nothing special about this char ...
02005       // Set the default font and grab some metrics to adjust the placements ...
02006       PRUint32 len = PRUint32(mData.Length());
02007       if (1 == len) {
02008         SetBaseFamily(mData[0], theFont);
02009       }
02010       aRenderingContext.SetFont(theFont, nsnull);
02011 //printf("Painting %04X like a normal char\n", mData[0]);
02012 //aRenderingContext.SetColor(NS_RGB(255,0,0));
02013       aRenderingContext.DrawString(mData.get(), len, mRect.x, mRect.y + mBoundingMetrics.ascent);
02014     }
02015     else {
02016       // Set the stretchy font and grab some metrics to adjust the placements ...
02017       mGlyphTable->GetPrimaryFontName(fontName);
02018       SetFirstFamily(theFont, fontName);
02019       aRenderingContext.SetFont(theFont, nsnull);
02020       // if there is a glyph of appropriate size, paint that glyph
02021       if (mGlyph) {
02022 //printf("Painting %04X with a glyph of appropriate size\n", mData[0]);
02023 //aRenderingContext.SetColor(NS_RGB(0,0,255));
02024         mGlyphTable->DrawGlyph(aRenderingContext, theFont, mGlyph,
02025                                mRect.x, mRect.y + mBoundingMetrics.ascent);
02026       }
02027       else { // paint by parts
02028         // see if this is a composite char and let children paint themselves
02029         if (!mParent && mSibling) { // only a "root" having child chars can enter here
02030           for (nsMathMLChar* child = mSibling; child; child = child->mSibling) {
02031 //if (!mStyleContext->Equals(child->mStyleContext))
02032 //  printf("char contexts are out of sync\n");
02033             child->Paint(aPresContext, aRenderingContext,
02034                          aDirtyRect, aWhichLayer, aForFrame, aSelectedRect);
02035           }
02036           return NS_OK; // that's all folks
02037         }
02038 //aRenderingContext.SetColor(NS_RGB(0,255,0));
02039         if (NS_STRETCH_DIRECTION_VERTICAL == mDirection)
02040           rv = PaintVertically(aPresContext, aRenderingContext, theFont, styleContext,
02041                                mGlyphTable, this, mRect);
02042         else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
02043           rv = PaintHorizontally(aPresContext, aRenderingContext, theFont, styleContext,
02044                                  mGlyphTable, this, mRect);
02045       }
02046     }
02047   }
02048   return rv;
02049 }
02050 
02051 /* =================================================================================
02052   And now the helper routines that actually do the job of painting the char by parts
02053  */
02054 
02055 // paint a stretchy char by assembling glyphs vertically
02056 nsresult
02057 nsMathMLChar::PaintVertically(nsPresContext*      aPresContext,
02058                               nsIRenderingContext& aRenderingContext,
02059                               nsFont&              aFont,
02060                               nsStyleContext*      aStyleContext,
02061                               nsGlyphTable*        aGlyphTable,
02062                               nsMathMLChar*        aChar,
02063                               nsRect&              aRect)
02064 {
02065   nsresult rv = NS_OK;
02066   nsRect clipRect;
02067   nscoord dx, dy;
02068 
02069   nscoord onePixel = aPresContext->IntScaledPixelsToTwips(1);
02070 
02071   // get metrics data to be re-used later
02072   PRInt32 i;
02073   nsGlyphCode ch, chdata[4];
02074   nsBoundingMetrics bm, bmdata[4];
02075   nscoord stride = 0, offset[3], start[3], end[3];
02076   nscoord width = aRect.width;
02077   nsGlyphCode glue = aGlyphTable->GlueOf(aPresContext, aChar);
02078   for (i = 0; i < 4; i++) {
02079     switch (i) {
02080       case 0: ch = aGlyphTable->TopOf(aPresContext, aChar);    break;
02081       case 1: ch = aGlyphTable->MiddleOf(aPresContext, aChar); break;
02082       case 2: ch = aGlyphTable->BottomOf(aPresContext, aChar); break;
02083       case 3: ch = glue;                                       break;
02084     }
02085     // empty slots are filled with the glue if it is not null
02086     if (!ch) ch = glue;
02087     if (!ch) {
02088       bm.Clear();  // glue is null, set bounding metrics to 0
02089     }
02090     else {
02091       rv = aGlyphTable->GetBoundingMetrics(aRenderingContext, aFont, ch, bm);
02092       if (NS_FAILED(rv)) {
02093         NS_WARNING("GetBoundingMetrics failed");
02094         return rv;
02095       }
02096       if (width < bm.rightBearing) width =  bm.rightBearing;
02097     }
02098     chdata[i] = ch;
02099     bmdata[i] = bm;
02100   }
02101   dx = aRect.x;
02102   for (i = 0; i < 3; i++) {
02103     ch = chdata[i];
02104     bm = bmdata[i];
02105     if (0 == i) { // top
02106       dy = aRect.y + bm.ascent;
02107     }
02108     else if (1 == i) { // middle
02109       dy = aRect.y + bm.ascent + (aRect.height - (bm.ascent + bm.descent))/2;
02110     }
02111     else { // bottom
02112       dy = aRect.y + aRect.height - bm.descent;
02113     }
02114     // abcissa passed to DrawString
02115     offset[i] = dy;
02116     // *exact* abcissa where the *top-most* pixel of the glyph is painted
02117     start[i] = dy - bm.ascent;
02118     // *exact* abcissa where the *bottom-most* pixel of the glyph is painted
02119     end[i] = dy + bm.descent; // end = start + height
02120   }
02121 
02123   // draw top, middle, bottom
02124   for (i = 0; i < 3; i++) {
02125     ch = chdata[i];
02126     // glue can be null, and other parts could have been set to glue
02127     if (ch) {
02128 #ifdef SHOW_BORDERS
02129       // bounding box of the part
02130       aRenderingContext.SetColor(NS_RGB(0,0,0));
02131       aRenderingContext.DrawRect(nsRect(dx,start[i],width+30*(i+1),end[i]-start[i]));
02132 #endif
02133       dy = offset[i];
02134       if (0 == i) { // top
02135         clipRect.SetRect(dx, aRect.y, width, aRect.height);
02136       }
02137       else if (1 == i) { // middle
02138         clipRect.SetRect(dx, end[0], width, start[2]-end[0]);
02139       }
02140       else { // bottom
02141         clipRect.SetRect(dx, start[2], width, end[2]-start[2]);
02142       }
02143       if (!clipRect.IsEmpty()) {
02144         clipRect.Inflate(onePixel, onePixel);
02145         aGlyphTable->DrawGlyph(aRenderingContext, aFont, ch, dx, dy, &clipRect);
02146       }
02147     }
02148   }
02149 
02151   // fill the gap between top and middle, and between middle and bottom.
02152   if (!glue) { // null glue : draw a rule
02153     // figure out the dimensions of the rule to be drawn :
02154     // set lbearing to rightmost lbearing among the two current successive parts.
02155     // set rbearing to leftmost rbearing among the two current successive parts.
02156     // this not only satisfies the convention used for over/underbraces
02157     // in TeX, but also takes care of broken fonts like the stretchy integral
02158     // in Symbol for small font sizes in unix.
02159     nscoord lbearing, rbearing;
02160     PRInt32 first = 0, last = 2;
02161     if (chdata[1]) { // middle part exists
02162       last = 1;
02163     }
02164     while (last <= 2) {
02165       if (chdata[last]) {
02166         lbearing = bmdata[last].leftBearing;
02167         rbearing = bmdata[last].rightBearing;
02168         if (chdata[first]) {
02169           if (lbearing < bmdata[first].leftBearing)
02170             lbearing = bmdata[first].leftBearing;
02171           if (rbearing > bmdata[first].rightBearing)
02172             rbearing = bmdata[first].rightBearing;
02173         }
02174       }
02175       else if (chdata[first]) {
02176         lbearing = bmdata[first].leftBearing;
02177         rbearing = bmdata[first].rightBearing;
02178       }
02179       else {
02180         NS_ERROR("Cannot stretch - All parts missing");
02181         return NS_ERROR_UNEXPECTED;
02182       }
02183       // paint the rule between the parts
02184       nsRect rule(aRect.x + lbearing, end[first] - onePixel,
02185                   rbearing - lbearing, start[last] - end[first] + 2*onePixel);
02186       if (!rule.IsEmpty())
02187         aRenderingContext.FillRect(rule);
02188       first = last;
02189       last++;
02190     }
02191   }
02192   else { // glue is present
02193     nscoord overlap;
02194     nsCOMPtr<nsIFontMetrics> fm;
02195     aRenderingContext.GetFontMetrics(*getter_AddRefs(fm));
02196     nsMathMLFrame::GetRuleThickness(fm, overlap);
02197     overlap = 2 * PR_MAX(overlap, onePixel);
02198     while (overlap > 0 && bmdata[3].ascent + bmdata[3].descent <= 2*overlap + onePixel)
02199       overlap -= onePixel;
02200 
02201     if (overlap > 0) {
02202       // to protect against gaps, pretend the glue is smaller than 
02203       // it says to allow a small overlap when adjoining it
02204       bmdata[3].ascent -= overlap;
02205       bmdata[3].descent -= overlap;
02206     }
02207     nscoord edge = PR_MAX(overlap, onePixel);
02208 
02209     for (i = 0; i < 2; i++) {
02210       PRInt32 count = 0;
02211       dy = offset[i];
02212       clipRect.SetRect(dx, end[i], width, start[i+1]-end[i]);
02213       clipRect.Inflate(edge, edge);
02214 #ifdef SHOW_BORDERS
02215       // exact area to fill
02216       aRenderingContext.SetColor(NS_RGB(255,0,0));
02217       aRenderingContext.DrawRect(clipRect);
02218 #endif
02219       aRenderingContext.PushState();
02220       aRenderingContext.SetClipRect(clipRect, nsClipCombine_kIntersect);
02221       bm = bmdata[i];
02222       while (dy + bm.descent < start[i+1]) {
02223         if (count++ < 2) {
02224           stride = bm.descent;
02225           bm = bmdata[3]; // glue
02226           stride += bm.ascent;
02227         }
02228         // defensive code against odd things such as a smallish TextZoom...
02229         NS_ASSERTION(1000 != count, "something is probably wrong somewhere");
02230         if (stride < onePixel || 1000 == count) {
02231           aRenderingContext.PopState();
02232           return NS_ERROR_UNEXPECTED;
02233         }
02234         dy += stride;
02235         aGlyphTable->DrawGlyph(aRenderingContext, aFont, glue, dx, dy);
02236       }
02237       aRenderingContext.PopState();
02238 #ifdef SHOW_BORDERS
02239       // last glyph that may cross past its boundary and collide with the next
02240       nscoord height = bm.ascent + bm.descent;
02241       aRenderingContext.SetColor(NS_RGB(0,255,0));
02242       aRenderingContext.DrawRect(nsRect(dx, dy-bm.ascent, width, height));
02243 #endif
02244     }
02245   }
02246   return NS_OK;
02247 }
02248 
02249 // paint a stretchy char by assembling glyphs horizontally
02250 nsresult
02251 nsMathMLChar::PaintHorizontally(nsPresContext*      aPresContext,
02252                                 nsIRenderingContext& aRenderingContext,
02253                                 nsFont&              aFont,
02254                                 nsStyleContext*      aStyleContext,
02255                                 nsGlyphTable*        aGlyphTable,
02256                                 nsMathMLChar*        aChar,
02257                                 nsRect&              aRect)
02258 {
02259   nsresult rv = NS_OK;
02260   nsRect clipRect;
02261   nscoord dx, dy;
02262 
02263   nscoord onePixel = aPresContext->IntScaledPixelsToTwips(1);
02264 
02265   // get metrics data to be re-used later
02266   PRInt32 i;
02267   nsGlyphCode ch, chdata[4];
02268   nsBoundingMetrics bm, bmdata[4];
02269   nscoord stride = 0, offset[3], start[3], end[3];
02270   dy = aRect.y;
02271   nsGlyphCode glue = aGlyphTable->GlueOf(aPresContext, aChar);
02272   for (i = 0; i < 4; i++) {
02273     switch (i) {
02274       case 0: ch = aGlyphTable->LeftOf(aPresContext, aChar);   break;
02275       case 1: ch = aGlyphTable->MiddleOf(aPresContext, aChar); break;
02276       case 2: ch = aGlyphTable->RightOf(aPresContext, aChar);  break;
02277       case 3: ch = glue;                                       break;
02278     }
02279     // empty slots are filled with the glue if it is not null
02280     if (!ch) ch = glue;
02281     if (!ch) {
02282       bm.Clear();  // glue is null, set bounding metrics to 0
02283     }
02284     else {
02285       rv = aGlyphTable->GetBoundingMetrics(aRenderingContext, aFont, ch, bm);
02286       if (NS_FAILED(rv)) {
02287         NS_WARNING("GetBoundingMetrics failed");
02288         return rv;
02289       }
02290       if (dy < aRect.y + bm.ascent) {
02291         dy = aRect.y + bm.ascent;
02292       }
02293     }
02294     chdata[i] = ch;
02295     bmdata[i] = bm;
02296   }
02297   for (i = 0; i < 3; i++) {
02298     ch = chdata[i];
02299     bm = bmdata[i];
02300     if (0 == i) { // left
02301       dx = aRect.x - bm.leftBearing;
02302     }
02303     else if (1 == i) { // middle
02304       dx = aRect.x + (aRect.width - bm.width)/2;
02305     }
02306     else { // right
02307       dx = aRect.x + aRect.width - bm.rightBearing;
02308     }
02309     // abcissa that DrawString used
02310     offset[i] = dx;
02311     // *exact* abcissa where the *left-most* pixel of the glyph is painted
02312     start[i] = dx + bm.leftBearing;
02313     // *exact* abcissa where the *right-most* pixel of the glyph is painted
02314     end[i] = dx + bm.rightBearing; // note: end = start + width
02315   }
02316 
02318   // draw left, middle, right
02319   for (i = 0; i < 3; i++) {
02320     ch = chdata[i];
02321     // glue can be null, and other parts could have been set to glue
02322     if (ch) {
02323 #ifdef SHOW_BORDERS
02324       aRenderingContext.SetColor(NS_RGB(255,0,0));
02325       aRenderingContext.DrawRect(nsRect(start[i], dy - bmdata[i].ascent,
02326                                  end[i] - start[i], bmdata[i].ascent + bmdata[i].descent));
02327 #endif
02328       dx = offset[i];
02329       if (0 == i) { // left
02330         clipRect.SetRect(dx, aRect.y, aRect.width, aRect.height);
02331       }
02332       else if (1 == i) { // middle
02333         clipRect.SetRect(end[0], aRect.y, start[2]-end[0], aRect.height);
02334       }
02335       else { // right
02336         clipRect.SetRect(start[2], aRect.y, end[2]-start[2], aRect.height);
02337       }
02338       if (!clipRect.IsEmpty()) {
02339         clipRect.Inflate(onePixel, onePixel);
02340         aGlyphTable->DrawGlyph(aRenderingContext, aFont, ch, dx, dy, &clipRect);
02341       }
02342     }
02343   }
02344 
02346   // fill the gap between left and middle, and between middle and right.
02347   if (!glue) { // null glue : draw a rule
02348     // figure out the dimensions of the rule to be drawn :
02349     // set ascent to lowest ascent among the two current successive parts.
02350     // set descent to highest descent among the two current successive parts.
02351     // this satisfies the convention used for over/underbraces, and helps
02352     // fix broken fonts.
02353     nscoord ascent, descent;
02354     PRInt32 first = 0, last = 2;
02355     if (chdata[1]) { // middle part exists
02356       last = 1;
02357     }
02358     while (last <= 2) {
02359       if (chdata[last]) {
02360         ascent = bmdata[last].ascent;
02361         descent = bmdata[last].descent;
02362         if (chdata[first]) {
02363           if (ascent > bmdata[first].ascent)
02364             ascent = bmdata[first].ascent;
02365           if (descent > bmdata[first].descent)
02366             descent = bmdata[first].descent;
02367         }
02368       }
02369       else if (chdata[first]) {
02370         ascent = bmdata[first].ascent;
02371         descent = bmdata[first].descent;
02372       }
02373       else {
02374         NS_ERROR("Cannot stretch - All parts missing");
02375         return NS_ERROR_UNEXPECTED;
02376       }
02377       // paint the rule between the parts
02378       nsRect rule(end[first] - onePixel, dy - ascent,
02379                   start[last] - end[first] + 2*onePixel, ascent + descent);
02380       if (!rule.IsEmpty())
02381         aRenderingContext.FillRect(rule);
02382       first = last;
02383       last++;
02384     }
02385   }
02386   else { // glue is present
02387     nscoord overlap;
02388     nsCOMPtr<nsIFontMetrics> fm;
02389     aRenderingContext.GetFontMetrics(*getter_AddRefs(fm));
02390     nsMathMLFrame::GetRuleThickness(fm, overlap);
02391     overlap = 2 * PR_MAX(overlap, onePixel);
02392     while (overlap > 0 && bmdata[3].rightBearing - bmdata[3].leftBearing <= 2*overlap + onePixel)
02393       overlap -= onePixel;
02394 
02395     if (overlap > 0) {
02396       // to protect against gaps, pretend the glue is smaller than 
02397       // it says to allow a small overlap when adjoining it
02398       bmdata[3].leftBearing += overlap;
02399       bmdata[3].rightBearing -= overlap;
02400     }
02401     nscoord edge = PR_MAX(overlap, onePixel);
02402 
02403     for (i = 0; i < 2; i++) {
02404       PRInt32 count = 0;
02405       dx = offset[i];
02406       clipRect.SetRect(end[i], aRect.y, start[i+1]-end[i], aRect.height);
02407       clipRect.Inflate(edge, edge);
02408 #ifdef SHOW_BORDERS
02409       // rectangles in-between that are to be filled
02410       aRenderingContext.SetColor(NS_RGB(255,0,0));
02411       aRenderingContext.DrawRect(clipRect);
02412 #endif
02413       aRenderingContext.PushState();
02414       aRenderingContext.SetClipRect(clipRect, nsClipCombine_kIntersect);
02415       bm = bmdata[i];
02416       while (dx + bm.rightBearing < start[i+1]) {
02417         if (count++ < 2) {
02418           stride = bm.rightBearing;
02419           bm = bmdata[3]; // glue
02420           stride -= bm.leftBearing;
02421         }
02422         // defensive code against odd things such as a smallish TextZoom...
02423         NS_ASSERTION(1000 != count, "something is probably wrong somewhere");
02424         if (stride < onePixel || 1000 == count) {
02425           aRenderingContext.PopState();
02426           return NS_ERROR_UNEXPECTED;
02427         }
02428         dx += stride;
02429         aGlyphTable->DrawGlyph(aRenderingContext, aFont, glue, dx, dy);
02430       }
02431       aRenderingContext.PopState();
02432 #ifdef SHOW_BORDERS
02433       // last glyph that may cross past its boundary and collide with the next
02434       nscoord width = bm.rightBearing - bm.leftBearing;
02435       aRenderingContext.SetColor(NS_RGB(0,255,0));
02436       aRenderingContext.DrawRect(nsRect(dx + bm.leftBearing, aRect.y, width, aRect.height));
02437 #endif
02438     }
02439   }
02440   return NS_OK;
02441 }