Back to index

lightning-sunbird  0.9+nobinonly
nsFontMetricsGTK.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
00025  *   Brian Stell <bstell@ix.netcom.com>
00026  *   Morten Nilsen <morten@nilsen.com>
00027  *   Jungshik Shin <jshin@mailaps.org>
00028  *   IBM Corporation
00029  *
00030  * Alternatively, the contents of this file may be used under the terms of
00031  * either of the GNU General Public License Version 2 or later (the "GPL"),
00032  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00033  * in which case the provisions of the GPL or the LGPL are applicable instead
00034  * of those above. If you wish to allow use of your version of this file only
00035  * under the terms of either the GPL or the LGPL, and not to allow others to
00036  * use your version of this file under the terms of the MPL, indicate your
00037  * decision by deleting the provisions above and replace them with the notice
00038  * and other provisions required by the GPL or the LGPL. If you do not delete
00039  * the provisions above, a recipient may use your version of this file under
00040  * the terms of any one of the MPL, the GPL or the LGPL.
00041  *
00042  * ***** END LICENSE BLOCK ***** */
00043 
00044 #define ENABLE_X_FONT_BANNING 1
00045 
00046 #include <sys/types.h>
00047 #include "gfx-config.h"
00048 #include "nscore.h"
00049 #include "nsQuickSort.h"
00050 #include "nsFontMetricsGTK.h"
00051 #include "nsIServiceManager.h"
00052 #include "nsICharsetConverterManager.h"
00053 #include "nsILanguageAtomService.h"
00054 #include "nsISaveAsCharset.h"
00055 #include "nsIPref.h"
00056 #include "nsCOMPtr.h"
00057 #include "nsPrintfCString.h"
00058 #include "nspr.h"
00059 #include "nsHashtable.h"
00060 #include "nsReadableUtils.h"
00061 #include "nsAString.h"
00062 #include "nsXPIDLString.h"
00063 #include "nsFontDebug.h"
00064 #include "nsCharTraits.h"
00065 #ifdef MOZ_ENABLE_FREETYPE2
00066 #include "nsFT2FontNode.h"
00067 #include "nsFontFreeType.h"
00068 #endif
00069 #include "nsXFontNormal.h"
00070 #include "nsX11AlphaBlend.h"
00071 #include "nsXFontAAScaledBitmap.h"
00072 #include "nsUnicharUtils.h"
00073 #ifdef ENABLE_X_FONT_BANNING
00074 #include <regex.h>
00075 #endif /* ENABLE_X_FONT_BANNING */
00076 
00077 #include <X11/Xatom.h>
00078 #include <gdk/gdk.h>
00079 
00080 #define SAFE_CCMAP_HAS_CHAR_EXT(ccmap,c) ((ccmap) && ((IS_SURROGATE(c) && (ccmap)==gDoubleByteSpecialCharsCCMap) ? PR_FALSE : (CCMAP_HAS_CHAR_EXT(ccmap,c))))
00081 
00082 #ifdef PR_LOGGING 
00083 static PRLogModuleInfo * FontMetricsGTKLM = PR_NewLogModule("FontMetricsGTK");
00084 #endif /* PR_LOGGING */
00085 
00086 #ifdef ENABLE_X_FONT_BANNING
00087 /* Not all platforms may have REG_OK */
00088 #ifndef REG_OK
00089 #define REG_OK (0)
00090 #endif /* !REG_OK */
00091 #endif /* ENABLE_X_FONT_BANNING */
00092 
00093 #undef USER_DEFINED
00094 #define USER_DEFINED "x-user-def"
00095 
00096 // This is the scaling factor that we keep fonts limited to against
00097 // the display size.  If a pixel size is requested that is more than
00098 // this factor larger than the height of the display, it's clamped to
00099 // that value instead of the requested size.
00100 #define FONT_MAX_FONT_SCALE 2
00101 
00102 #undef NOISY_FONTS
00103 #undef REALLY_NOISY_FONTS
00104 
00105 #ifndef MOZ_ENABLE_FREETYPE2
00106 static PRUint32 gFontDebug = 0 | NS_FONT_DEBUG_FONT_SCAN;
00107 #endif
00108 
00109 struct nsFontCharSetMap;
00110 struct nsFontFamilyName;
00111 struct nsFontPropertyName;
00112 struct nsFontStyle;
00113 struct nsFontWeight;
00114 struct nsFontLangGroup;
00115 
00116 struct nsFontCharSetInfo
00117 {
00118   const char*            mCharSet;
00119   nsFontCharSetConverter Convert;
00120   PRUint8                mSpecialUnderline;
00121 #ifdef MOZ_ENABLE_FREETYPE2
00122   PRInt32                mCodeRange1Bits;
00123   PRInt32                mCodeRange2Bits;
00124 #endif
00125   PRUint16*              mCCMap;
00126   nsIUnicodeEncoder*     mConverter;
00127   nsIAtom*               mLangGroup;
00128   PRBool                 mInitedSizeInfo;
00129   PRInt32                mOutlineScaleMin;
00130   PRInt32                mAABitmapScaleMin;
00131   double                 mAABitmapOversize;
00132   double                 mAABitmapUndersize;
00133   PRBool                 mAABitmapScaleAlways;
00134   PRInt32                mBitmapScaleMin;
00135   double                 mBitmapOversize;
00136   double                 mBitmapUndersize;
00137 };
00138 
00139 struct nsFontFamily
00140 {
00141   NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
00142 
00143   nsFontNodeArray mNodes;
00144 };
00145 
00146 struct nsFontFamilyName
00147 {
00148   const char* mName;
00149   const char* mXName;
00150 };
00151 
00152 struct nsFontPropertyName
00153 {
00154   const char* mName;
00155   int         mValue;
00156 };
00157 
00158 static NS_DEFINE_CID(kCharSetManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
00159 static NS_DEFINE_CID(kPrefCID, NS_PREF_CID);
00160 static NS_DEFINE_CID(kSaveAsCharsetCID, NS_SAVEASCHARSET_CID);
00161 static void SetCharsetLangGroup(nsFontCharSetInfo* aCharSetInfo);
00162 
00163 static int gFontMetricsGTKCount = 0;
00164 static int gInitialized = 0;
00165 static PRBool gForceOutlineScaledFonts = PR_FALSE;
00166 static PRBool gAllowDoubleByteSpecialChars = PR_TRUE;
00167 
00168 // XXX many of these statics need to be freed at shutdown time
00169 
00170 static nsIPref* gPref = nsnull;
00171 static float gDevScale = 0.0f; /* Scaler value from |GetCanonicalPixelScale()| */
00172 static PRBool gScaleBitmapFontsWithDevScale = PR_FALSE;
00173 static nsICharsetConverterManager* gCharSetManager = nsnull;
00174 static nsIUnicodeEncoder* gUserDefinedConverter = nsnull;
00175 
00176 static nsHashtable* gAliases = nsnull;
00177 static nsHashtable* gCharSetMaps = nsnull;
00178 static nsHashtable* gFamilies = nsnull;
00179 static nsHashtable* gFFRENodes = nsnull;
00180 static nsHashtable* gAFRENodes = nsnull;
00181 // gCachedFFRESearches holds the "already looked up"
00182 // FFRE (Foundry Family Registry Encoding) font searches
00183 static nsHashtable* gCachedFFRESearches = nsnull;
00184 static nsHashtable* gSpecialCharSets = nsnull;
00185 static nsHashtable* gStretches = nsnull;
00186 static nsHashtable* gWeights = nsnull;
00187 nsISaveAsCharset* gFontSubConverter = nsnull;
00188 
00189 static nsFontNodeArray* gGlobalList = nsnull;
00190 
00191 static nsIAtom* gUnicode = nsnull;
00192 static nsIAtom* gUserDefined = nsnull;
00193 static nsIAtom* gZHTW = nsnull;
00194 static nsIAtom* gZHHK = nsnull;
00195 static nsIAtom* gZHTWHK = nsnull;  // for fonts common to zh-TW and zh-HK
00196 static nsIAtom* gUsersLocale = nsnull;
00197 static nsIAtom* gWesternLocale = nsnull;
00198 
00199 // Controls for Outline Scaled Fonts (okay looking)
00200 
00201 static PRInt32 gOutlineScaleMinimum = 6;
00202 // Controls for Anti-Aliased Scaled Bitmaps (okay looking)
00203 static PRBool  gAABitmapScaleEnabled = PR_TRUE;
00204 static PRBool  gAABitmapScaleAlways = PR_FALSE;
00205 static PRInt32 gAABitmapScaleMinimum = 6;
00206 static double  gAABitmapOversize = 1.1;
00207 static double  gAABitmapUndersize = 0.9;
00208 
00209 // Controls for (regular) Scaled Bitmaps (very ugly)
00210 static PRInt32 gBitmapScaleMinimum = 10;
00211 static double  gBitmapOversize = 1.2;
00212 static double  gBitmapUndersize = 0.8;
00213 
00214 #ifdef ENABLE_X_FONT_BANNING
00215 static regex_t *gFontRejectRegEx = nsnull,
00216                *gFontAcceptRegEx = nsnull;
00217 #endif /* ENABLE_X_FONT_BANNING */
00218 
00219 static gint SingleByteConvert(nsFontCharSetInfo* aSelf, XFontStruct* aFont,
00220   const PRUnichar* aSrcBuf, PRInt32 aSrcLen, char* aDestBuf, PRInt32 aDestLen);
00221 static gint DoubleByteConvert(nsFontCharSetInfo* aSelf, XFontStruct* aFont,
00222   const PRUnichar* aSrcBuf, PRInt32 aSrcLen, char* aDestBuf, PRInt32 aDestLen);
00223 static gint ISO10646Convert(nsFontCharSetInfo* aSelf, XFontStruct* aFont,
00224   const PRUnichar* aSrcBuf, PRInt32 aSrcLen, char* aDestBuf, PRInt32 aDestLen);
00225 
00226 static nsFontCharSetInfo Unknown = { nsnull };
00227 static nsFontCharSetInfo Special = { nsnull };
00228 
00229 #ifdef MOZ_ENABLE_FREETYPE2
00230 static nsFontCharSetInfo USASCII =
00231   { "us-ascii", SingleByteConvert, 0,
00232     TT_OS2_CPR1_LATIN1 | TT_OS2_CPR1_MAC_ROMAN,
00233     TT_OS2_CPR2_CA_FRENCH |  TT_OS2_CPR2_PORTUGESE
00234     | TT_OS2_CPR2_WE_LATIN1 |  TT_OS2_CPR2_US };
00235 static nsFontCharSetInfo ISO88591 =
00236   { "ISO-8859-1", SingleByteConvert, 0,
00237     TT_OS2_CPR1_LATIN1 | TT_OS2_CPR1_MAC_ROMAN,
00238     TT_OS2_CPR2_CA_FRENCH |  TT_OS2_CPR2_PORTUGESE
00239     | TT_OS2_CPR2_WE_LATIN1 |  TT_OS2_CPR2_US };
00240 static nsFontCharSetInfo ISO88592 =
00241   { "ISO-8859-2", SingleByteConvert, 0,
00242     TT_OS2_CPR1_LATIN2, TT_OS2_CPR2_LATIN2 };
00243 static nsFontCharSetInfo ISO88593 =
00244   { "ISO-8859-3", SingleByteConvert, 0,
00245     TT_OS2_CPR1_TURKISH, TT_OS2_CPR2_TURKISH };
00246 static nsFontCharSetInfo ISO88594 =
00247   { "ISO-8859-4", SingleByteConvert, 0,
00248     TT_OS2_CPR1_BALTIC, TT_OS2_CPR2_BALTIC };
00249 static nsFontCharSetInfo ISO88595 =
00250   { "ISO-8859-5", SingleByteConvert, 0,
00251     TT_OS2_CPR1_CYRILLIC, TT_OS2_CPR2_RUSSIAN | TT_OS2_CPR2_CYRILLIC };
00252 static nsFontCharSetInfo ISO88596 =
00253   { "ISO-8859-6", SingleByteConvert, 0,
00254       TT_OS2_CPR1_ARABIC, TT_OS2_CPR2_ARABIC | TT_OS2_CPR2_ARABIC_708 };
00255 static nsFontCharSetInfo ISO885968x =
00256   { "x-iso-8859-6-8-x", SingleByteConvert, 0,
00257       TT_OS2_CPR1_ARABIC, TT_OS2_CPR2_ARABIC | TT_OS2_CPR2_ARABIC_708 };
00258 static nsFontCharSetInfo ISO8859616 =
00259   { "x-iso-8859-6-16", SingleByteConvert, 0,
00260       TT_OS2_CPR1_ARABIC, TT_OS2_CPR2_ARABIC | TT_OS2_CPR2_ARABIC_708 };
00261 static nsFontCharSetInfo IBM1046 =
00262   { "x-IBM1046", SingleByteConvert, 0,
00263       TT_OS2_CPR1_ARABIC, TT_OS2_CPR2_ARABIC | TT_OS2_CPR2_ARABIC_708 };
00264 static nsFontCharSetInfo ISO88597 =
00265   { "ISO-8859-7", SingleByteConvert, 0,
00266     TT_OS2_CPR1_GREEK, TT_OS2_CPR2_GREEK | TT_OS2_CPR2_GREEK_437G };
00267 static nsFontCharSetInfo ISO88598 =
00268   { "ISO-8859-8", SingleByteConvert, 0,
00269     TT_OS2_CPR1_HEBREW, TT_OS2_CPR2_HEBREW };
00270 // change from  
00271 // { "ISO-8859-8", SingleByteConvertReverse, 0, 0, 0 };
00272 // untill we fix the layout and ensure we only call this with pure RTL text
00273 static nsFontCharSetInfo ISO88599 =
00274   { "ISO-8859-9", SingleByteConvert, 0,
00275     TT_OS2_CPR1_TURKISH, TT_OS2_CPR2_TURKISH };
00276 // no support for iso-8859-10 (Nordic/Icelandic) currently
00277 // static nsFontCharSetInfo ISO885910 =
00278 // { "ISO-8859-10", SingleByteConvert, 0,
00279 //   0, TT_OS2_CPR2_NORDIC | TT_OS2_CPR2_ICELANDIC };
00280 // no support for iso-8859-12 (Vietnamese) currently
00281 // static nsFontCharSetInfo ISO885912 =
00282 // { "ISO-8859-12", SingleByteConvert, 0,
00283 //   TT_OS2_CPR1_VIETNAMESE, 0 };
00284 static nsFontCharSetInfo ISO885913 =
00285   { "ISO-8859-13", SingleByteConvert, 0,
00286     TT_OS2_CPR1_BALTIC, TT_OS2_CPR2_BALTIC };
00287 static nsFontCharSetInfo ISO885915 =
00288   { "ISO-8859-15", SingleByteConvert, 0,
00289     TT_OS2_CPR1_LATIN2, TT_OS2_CPR2_LATIN2 };
00290 static nsFontCharSetInfo JISX0201 =
00291   { "jis_0201", SingleByteConvert, 1,
00292     TT_OS2_CPR1_JAPANESE, 0 };
00293 static nsFontCharSetInfo KOI8R =
00294   { "KOI8-R", SingleByteConvert, 0,
00295     TT_OS2_CPR1_CYRILLIC, TT_OS2_CPR2_RUSSIAN | TT_OS2_CPR2_CYRILLIC };
00296 static nsFontCharSetInfo KOI8U =
00297   { "KOI8-U", SingleByteConvert, 0,
00298     TT_OS2_CPR1_CYRILLIC, TT_OS2_CPR2_RUSSIAN | TT_OS2_CPR2_CYRILLIC };
00299 static nsFontCharSetInfo TIS6202 =
00300 /* Added to support thai context sensitive shaping if
00301  * CTL extension is is in force */
00302 #ifdef SUNCTL
00303   { "tis620-2", SingleByteConvert, 0,
00304     TT_OS2_CPR1_THAI, 0 };
00305 #else
00306   { "windows-874", SingleByteConvert, 0,
00307     TT_OS2_CPR1_THAI, 0 };
00308 #endif /* SUNCTL */
00309 static nsFontCharSetInfo TIS620 =
00310   { "TIS-620", SingleByteConvert, 0,
00311     TT_OS2_CPR1_THAI, 0 };
00312 static nsFontCharSetInfo ISO885911 =
00313   { "ISO-8859-11", SingleByteConvert, 0,
00314     TT_OS2_CPR1_THAI, 0 };
00315 static nsFontCharSetInfo Big5 =
00316   { "x-x-big5", DoubleByteConvert, 1,
00317     TT_OS2_CPR1_CHINESE_TRAD, 0 };
00318 // a kludge to distinguish zh-TW only fonts in Big5 (such as hpbig5-)
00319 // from zh-TW/zh-HK common fonts in Big5 (such as big5-1)
00320 static nsFontCharSetInfo Big5TWHK =
00321   { "x-x-big5", DoubleByteConvert, 1,
00322     TT_OS2_CPR1_CHINESE_TRAD, 0 };
00323 static nsFontCharSetInfo CNS116431 =
00324   { "x-cns-11643-1", DoubleByteConvert, 1,
00325     TT_OS2_CPR1_CHINESE_TRAD, 0 };
00326 static nsFontCharSetInfo CNS116432 =
00327   { "x-cns-11643-2", DoubleByteConvert, 1,
00328     TT_OS2_CPR1_CHINESE_TRAD, 0 };
00329 static nsFontCharSetInfo CNS116433 =
00330   { "x-cns-11643-3", DoubleByteConvert, 1,
00331     TT_OS2_CPR1_CHINESE_TRAD, 0 };
00332 static nsFontCharSetInfo CNS116434 =
00333   { "x-cns-11643-4", DoubleByteConvert, 1,
00334     TT_OS2_CPR1_CHINESE_TRAD, 0 };
00335 static nsFontCharSetInfo CNS116435 =
00336   { "x-cns-11643-5", DoubleByteConvert, 1,
00337     TT_OS2_CPR1_CHINESE_TRAD, 0 };
00338 static nsFontCharSetInfo CNS116436 =
00339   { "x-cns-11643-6", DoubleByteConvert, 1,
00340     TT_OS2_CPR1_CHINESE_TRAD, 0 };
00341 static nsFontCharSetInfo CNS116437 =
00342   { "x-cns-11643-7", DoubleByteConvert, 1,
00343     TT_OS2_CPR1_CHINESE_TRAD, 0 };
00344 static nsFontCharSetInfo GB2312 =
00345   { "gb_2312-80", DoubleByteConvert, 1,
00346     TT_OS2_CPR1_CHINESE_SIMP, 0 };
00347 static nsFontCharSetInfo GB18030_0 =
00348   { "gb18030.2000-0", DoubleByteConvert, 1,
00349     TT_OS2_CPR1_CHINESE_SIMP, 0 };
00350 static nsFontCharSetInfo GB18030_1 =
00351   { "gb18030.2000-1", DoubleByteConvert, 1,
00352     TT_OS2_CPR1_CHINESE_SIMP, 0 };
00353 static nsFontCharSetInfo GBK =
00354   { "x-gbk-noascii", DoubleByteConvert, 1,
00355     TT_OS2_CPR1_CHINESE_SIMP, 0 };
00356 static nsFontCharSetInfo HKSCS =
00357   { "hkscs-1", DoubleByteConvert, 1,
00358     TT_OS2_CPR1_CHINESE_TRAD, 0 };
00359 static nsFontCharSetInfo JISX0208 =
00360   { "jis_0208-1983", DoubleByteConvert, 1,
00361     TT_OS2_CPR1_JAPANESE, 0 };
00362 static nsFontCharSetInfo JISX0212 =
00363   { "jis_0212-1990", DoubleByteConvert, 1,
00364     TT_OS2_CPR1_JAPANESE, 0 };
00365 static nsFontCharSetInfo KSC5601 =
00366   { "ks_c_5601-1987", DoubleByteConvert, 1,
00367     TT_OS2_CPR1_KO_WANSUNG | TT_OS2_CPR1_KO_JOHAB, 0 };
00368 static nsFontCharSetInfo X11Johab =
00369   { "x-x11johab", DoubleByteConvert, 1,
00370     TT_OS2_CPR1_KO_WANSUNG | TT_OS2_CPR1_KO_JOHAB, 0 };
00371 static nsFontCharSetInfo JohabNoAscii =
00372   { "x-johab-noascii", DoubleByteConvert, 1,
00373     TT_OS2_CPR1_KO_WANSUNG | TT_OS2_CPR1_KO_JOHAB, 0 };
00374 static nsFontCharSetInfo JamoTTF =
00375   { "x-koreanjamo-0", DoubleByteConvert, 1,
00376     TT_OS2_CPR1_KO_WANSUNG | TT_OS2_CPR1_KO_JOHAB, 0 };
00377 static nsFontCharSetInfo TamilTTF =
00378   { "x-tamilttf-0", DoubleByteConvert, 0,
00379     0, 0 };
00380 static nsFontCharSetInfo CP1250 =
00381   { "windows-1250", SingleByteConvert, 0,
00382     TT_OS2_CPR1_LATIN2, TT_OS2_CPR2_LATIN2 };
00383 static nsFontCharSetInfo CP1251 =
00384   { "windows-1251", SingleByteConvert, 0,
00385     TT_OS2_CPR1_CYRILLIC, TT_OS2_CPR2_RUSSIAN };
00386 static nsFontCharSetInfo CP1252 =
00387   { "windows-1252", SingleByteConvert, 0,
00388     TT_OS2_CPR1_LATIN1 | TT_OS2_CPR1_MAC_ROMAN,
00389     TT_OS2_CPR2_CA_FRENCH |  TT_OS2_CPR2_PORTUGESE
00390     | TT_OS2_CPR2_WE_LATIN1 |  TT_OS2_CPR2_US };
00391 static nsFontCharSetInfo CP1253 =
00392   { "windows-1253", SingleByteConvert, 0,
00393     TT_OS2_CPR1_GREEK, TT_OS2_CPR2_GREEK | TT_OS2_CPR2_GREEK_437G };
00394 static nsFontCharSetInfo CP1257 =
00395   { "windows-1257", SingleByteConvert, 0,
00396     TT_OS2_CPR1_BALTIC, TT_OS2_CPR2_BALTIC };
00397 
00398 #ifdef SUNCTL
00399 /* Hindi range currently unsupported in FT2 range. Change TT* once we 
00400    arrive at a way to identify hindi */
00401 static nsFontCharSetInfo SunIndic =
00402   { "x-sun-unicode-india-0", DoubleByteConvert, 0,
00403     0, 0 };
00404 #endif /* SUNCTL */
00405 
00406 static nsFontCharSetInfo ISO106461 =
00407   { nsnull, ISO10646Convert, 1, 0xFFFFFFFF, 0xFFFFFFFF };
00408 
00409 static nsFontCharSetInfo AdobeSymbol =
00410    { "Adobe-Symbol-Encoding", SingleByteConvert, 0,
00411     TT_OS2_CPR1_SYMBOL, 0 };
00412 static nsFontCharSetInfo AdobeEuro =
00413   { "x-adobe-euro", SingleByteConvert, 0,
00414     0, 0 };
00415 
00416 #ifdef MOZ_MATHML
00417 static nsFontCharSetInfo CMCMEX =
00418    { "x-t1-cmex", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0};
00419 static nsFontCharSetInfo CMCMSY =
00420    { "x-t1-cmsy", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0};
00421 static nsFontCharSetInfo CMCMR =
00422    { "x-t1-cmr", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0};
00423 static nsFontCharSetInfo CMCMMI =
00424    { "x-t1-cmmi", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0};
00425 static nsFontCharSetInfo Mathematica1 =
00426    { "x-mathematica1", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0};
00427 static nsFontCharSetInfo Mathematica2 =
00428    { "x-mathematica2", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0};
00429 static nsFontCharSetInfo Mathematica3 =
00430    { "x-mathematica3", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0};
00431 static nsFontCharSetInfo Mathematica4 =
00432    { "x-mathematica4", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0};
00433 static nsFontCharSetInfo Mathematica5 =
00434    { "x-mathematica5", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0};
00435 #endif /* MATHML */
00436 
00437 #else
00438 
00439 static nsFontCharSetInfo USASCII =
00440   { "us-ascii", SingleByteConvert, 0 };
00441 static nsFontCharSetInfo ISO88591 =
00442   { "ISO-8859-1", SingleByteConvert, 0 };
00443 static nsFontCharSetInfo ISO88592 =
00444   { "ISO-8859-2", SingleByteConvert, 0 };
00445 static nsFontCharSetInfo ISO88593 =
00446   { "ISO-8859-3", SingleByteConvert, 0 };
00447 static nsFontCharSetInfo ISO88594 =
00448   { "ISO-8859-4", SingleByteConvert, 0 };
00449 static nsFontCharSetInfo ISO88595 =
00450   { "ISO-8859-5", SingleByteConvert, 0 };
00451 static nsFontCharSetInfo ISO88596 =
00452   { "ISO-8859-6", SingleByteConvert, 0 };
00453 static nsFontCharSetInfo ISO885968x =
00454   { "x-iso-8859-6-8-x", SingleByteConvert, 0 };
00455 static nsFontCharSetInfo ISO8859616 =
00456   { "x-iso-8859-6-16", SingleByteConvert, 0 };
00457 static nsFontCharSetInfo IBM1046 =
00458   { "x-IBM1046", SingleByteConvert, 0 };
00459 static nsFontCharSetInfo ISO88597 =
00460   { "ISO-8859-7", SingleByteConvert, 0 };
00461 static nsFontCharSetInfo ISO88598 =
00462   { "ISO-8859-8", SingleByteConvert, 0 };
00463 // change from  
00464 // { "ISO-8859-8", SingleByteConvertReverse, 0, 0, 0 };
00465 // untill we fix the layout and ensure we only call this with pure RTL text
00466 static nsFontCharSetInfo ISO88599 =
00467   { "ISO-8859-9", SingleByteConvert, 0 };
00468 // no support for iso-8859-10 (Nordic/Icelandic) currently
00469 // static nsFontCharSetInfo ISO885910 =
00470 // { "ISO-8859-10", SingleByteConvert, 0,
00471 //   0, TT_OS2_CPR2_NORDIC | TT_OS2_CPR2_ICELANDIC };
00472 // no support for iso-8859-12 (Vietnamese) currently
00473 // static nsFontCharSetInfo ISO885912 =
00474 // { "ISO-8859-12", SingleByteConvert, 0,
00475 //   TT_OS2_CPR1_VIETNAMESE, 0 };
00476 static nsFontCharSetInfo ISO885913 =
00477   { "ISO-8859-13", SingleByteConvert, 0 };
00478 static nsFontCharSetInfo ISO885915 =
00479   { "ISO-8859-15", SingleByteConvert, 0 };
00480 static nsFontCharSetInfo JISX0201 =
00481   { "jis_0201", SingleByteConvert, 1 };
00482 static nsFontCharSetInfo KOI8R =
00483   { "KOI8-R", SingleByteConvert, 0 };
00484 static nsFontCharSetInfo KOI8U =
00485   { "KOI8-U", SingleByteConvert, 0 };
00486 static nsFontCharSetInfo TIS6202 =
00487 /* Added to support thai context sensitive shaping if
00488  * CTL extension is is in force */
00489 #ifdef SUNCTL
00490   { "tis620-2", SingleByteConvert, 0 };
00491 #else
00492   { "windows-874", SingleByteConvert, 0 };
00493 #endif /* SUNCTL */
00494 static nsFontCharSetInfo TIS620 =
00495   { "TIS-620", SingleByteConvert, 0 };
00496 static nsFontCharSetInfo ISO885911 =
00497   { "ISO-8859-11", SingleByteConvert, 0 };
00498 static nsFontCharSetInfo Big5 =
00499   { "x-x-big5", DoubleByteConvert, 1 };
00500 // a kludge to distinguish zh-TW only fonts in Big5 (such as hpbig5-)
00501 // from zh-TW/zh-HK common fonts in Big5 (such as big5-1)
00502 static nsFontCharSetInfo Big5TWHK =
00503   { "x-x-big5", DoubleByteConvert, 1 };
00504 static nsFontCharSetInfo CNS116431 =
00505   { "x-cns-11643-1", DoubleByteConvert, 1 };
00506 static nsFontCharSetInfo CNS116432 =
00507   { "x-cns-11643-2", DoubleByteConvert, 1 };
00508 static nsFontCharSetInfo CNS116433 =
00509   { "x-cns-11643-3", DoubleByteConvert, 1 };
00510 static nsFontCharSetInfo CNS116434 =
00511   { "x-cns-11643-4", DoubleByteConvert, 1 };
00512 static nsFontCharSetInfo CNS116435 =
00513   { "x-cns-11643-5", DoubleByteConvert, 1 };
00514 static nsFontCharSetInfo CNS116436 =
00515   { "x-cns-11643-6", DoubleByteConvert, 1 };
00516 static nsFontCharSetInfo CNS116437 =
00517   { "x-cns-11643-7", DoubleByteConvert, 1 };
00518 static nsFontCharSetInfo GB2312 =
00519   { "gb_2312-80", DoubleByteConvert, 1 };
00520 static nsFontCharSetInfo GB18030_0 =
00521   { "gb18030.2000-0", DoubleByteConvert, 1 };
00522 static nsFontCharSetInfo GB18030_1 =
00523   { "gb18030.2000-1", DoubleByteConvert, 1 };
00524 static nsFontCharSetInfo GBK =
00525   { "x-gbk-noascii", DoubleByteConvert, 1 };
00526 static nsFontCharSetInfo HKSCS =
00527   { "hkscs-1", DoubleByteConvert, 1 };
00528 static nsFontCharSetInfo JISX0208 =
00529   { "jis_0208-1983", DoubleByteConvert, 1 };
00530 static nsFontCharSetInfo JISX0212 =
00531   { "jis_0212-1990", DoubleByteConvert, 1 };
00532 static nsFontCharSetInfo KSC5601 =
00533   { "ks_c_5601-1987", DoubleByteConvert, 1 };
00534 static nsFontCharSetInfo X11Johab =
00535   { "x-x11johab", DoubleByteConvert, 1 };
00536 static nsFontCharSetInfo JohabNoAscii =
00537   { "x-johab-noascii", DoubleByteConvert, 1 };
00538 static nsFontCharSetInfo JamoTTF =
00539   { "x-koreanjamo-0", DoubleByteConvert, 1 };
00540 static nsFontCharSetInfo TamilTTF =
00541   { "x-tamilttf-0", DoubleByteConvert, 0 };
00542 static nsFontCharSetInfo CP1250 =
00543   { "windows-1250", SingleByteConvert, 0 };
00544 static nsFontCharSetInfo CP1251 =
00545   { "windows-1251", SingleByteConvert, 0 };
00546 static nsFontCharSetInfo CP1252 =
00547   { "windows-1252", SingleByteConvert, 0 };
00548 static nsFontCharSetInfo CP1253 =
00549   { "windows-1253", SingleByteConvert, 0 };
00550 static nsFontCharSetInfo CP1257 =
00551   { "windows-1257", SingleByteConvert, 0 };
00552 
00553 #ifdef SUNCTL
00554 /* Hindi range currently unsupported in FT2 range. Change TT* once we 
00555    arrive at a way to identify hindi */
00556 static nsFontCharSetInfo SunIndic =
00557   { "x-sun-unicode-india-0", DoubleByteConvert, 0 };
00558 #endif /* SUNCTL */
00559 
00560 static nsFontCharSetInfo ISO106461 =
00561   { nsnull, ISO10646Convert, 1};
00562 
00563 static nsFontCharSetInfo AdobeSymbol =
00564    { "Adobe-Symbol-Encoding", SingleByteConvert, 0 };
00565 static nsFontCharSetInfo AdobeEuro =
00566   { "x-adobe-euro", SingleByteConvert, 0 };
00567          
00568 #ifdef MOZ_MATHML
00569 static nsFontCharSetInfo CMCMEX =
00570    { "x-t1-cmex", SingleByteConvert, 0};
00571 static nsFontCharSetInfo CMCMSY =
00572    { "x-t1-cmsy", SingleByteConvert, 0};
00573 static nsFontCharSetInfo CMCMR =
00574    { "x-t1-cmr", SingleByteConvert, 0};
00575 static nsFontCharSetInfo CMCMMI =
00576    { "x-t1-cmmi", SingleByteConvert, 0};
00577 static nsFontCharSetInfo Mathematica1 =
00578    { "x-mathematica1", SingleByteConvert, 0};
00579 static nsFontCharSetInfo Mathematica2 =
00580    { "x-mathematica2", SingleByteConvert, 0}; 
00581 static nsFontCharSetInfo Mathematica3 =
00582    { "x-mathematica3", SingleByteConvert, 0};
00583 static nsFontCharSetInfo Mathematica4 =
00584    { "x-mathematica4", SingleByteConvert, 0}; 
00585 static nsFontCharSetInfo Mathematica5 =
00586    { "x-mathematica5", SingleByteConvert, 0};
00587 #endif /* MATHML */
00588 #endif /* FREETYPE2 */
00589 
00590 static nsFontLangGroup FLG_WESTERN = { "x-western",     nsnull };
00591 static nsFontLangGroup FLG_RUSSIAN = { "x-cyrillic",    nsnull };
00592 static nsFontLangGroup FLG_BALTIC  = { "x-baltic",      nsnull };
00593 static nsFontLangGroup FLG_CE      = { "x-central-euro",nsnull };
00594 static nsFontLangGroup FLG_GREEK   = { "el",            nsnull };
00595 static nsFontLangGroup FLG_TURKISH = { "tr",            nsnull };
00596 static nsFontLangGroup FLG_HEBREW  = { "he",            nsnull };
00597 static nsFontLangGroup FLG_ARABIC  = { "ar",            nsnull };
00598 static nsFontLangGroup FLG_THAI    = { "th",            nsnull };
00599 static nsFontLangGroup FLG_ZHCN    = { "zh-CN",         nsnull };
00600 static nsFontLangGroup FLG_ZHTW    = { "zh-TW",         nsnull };
00601 static nsFontLangGroup FLG_ZHHK    = { "zh-HK",         nsnull };
00602 static nsFontLangGroup FLG_ZHTWHK  = { "x-zh-TWHK",     nsnull }; // TW + HK
00603 static nsFontLangGroup FLG_JA      = { "ja",            nsnull };
00604 static nsFontLangGroup FLG_KO      = { "ko",            nsnull };
00605 #ifdef SUNCTL
00606 static nsFontLangGroup FLG_INDIC   = { "x-devanagari",  nsnull };
00607 #endif
00608 static nsFontLangGroup FLG_TAMIL   = { "x-tamil",       nsnull };
00609 static nsFontLangGroup FLG_NONE    = { nsnull,          nsnull };
00610 
00611 /*
00612  * Normally, the charset of an X font can be determined simply by looking at
00613  * the last 2 fields of the long XLFD font name (CHARSET_REGISTRY and
00614  * CHARSET_ENCODING). However, there are a number of special cases:
00615  *
00616  * Sometimes, X server vendors use the same name to mean different things. For
00617  * example, IRIX uses "cns11643-1" to mean the 2nd plane of CNS 11643, while
00618  * Solaris uses that name for the 1st plane.
00619  *
00620  * Some X server vendors use certain names for something completely different.
00621  * For example, some Solaris fonts say "gb2312.1980-0" but are actually ASCII
00622  * fonts. These cases can be detected by looking at the POINT_SIZE and
00623  * AVERAGE_WIDTH fields. If the average width is half the point size, this is
00624  * an ASCII font, not GB 2312.
00625  *
00626  * Some fonts say "fontspecific" in the CHARSET_ENCODING field. Their charsets
00627  * depend on the FAMILY_NAME. For example, the following is a "Symbol" font:
00628  *
00629  *   -adobe-symbol-medium-r-normal--17-120-100-100-p-95-adobe-fontspecific
00630  *
00631  * Some vendors use one name to mean 2 different things, depending on the font.
00632  * For example, AIX has some "ksc5601.1987-0" fonts that require the 8th bit of
00633  * both bytes to be zero, while other fonts require them to be set to one.
00634  * These cases can be distinguished by looking at the FOUNDRY field, but a
00635  * better way is to look at XFontStruct.min_byte1.
00636  */
00637 static nsFontCharSetMap gCharSetMap[] =
00638 {
00639   { "-ascii",             &FLG_NONE,    &Unknown       },
00640   { "-ibm pc",            &FLG_NONE,    &Unknown       },
00641   { "adobe-fontspecific", &FLG_NONE,    &Special       },
00642   { "ansi-1251",          &FLG_RUSSIAN, &CP1251        },
00643   // On Solaris, big5-0 is used for ASCII-only fonts while in XFree86, 
00644   // it's for Big5 fonts without US-ASCII. When a non-Solaris binary
00645   // is displayed on a Solaris X server, this would break. 
00646 #ifndef SOLARIS
00647   { "big5-0",             &FLG_ZHTWHK,  &Big5TWHK      }, // for both TW and HK
00648 #else
00649   { "big5-0",             &FLG_ZHTW,    &USASCII       }, 
00650 #endif
00651   { "big5-1",             &FLG_ZHTWHK,  &Big5TWHK      }, // ditto
00652   { "big5.et-0",          &FLG_ZHTW,    &Big5          },
00653   { "big5.et.ext-0",      &FLG_ZHTW,    &Big5          },
00654   { "big5.etext-0",       &FLG_ZHTW,    &Big5          },
00655   { "big5.hku-0",         &FLG_ZHTW,    &Big5          },
00656   { "big5.hku-1",         &FLG_ZHTW,    &Big5          },
00657   { "big5.pc-0",          &FLG_ZHTW,    &Big5          },
00658   { "big5.shift-0",       &FLG_ZHTW,    &Big5          },
00659   { "big5hkscs-0",        &FLG_ZHHK,    &HKSCS         },
00660   { "cns11643.1986-1",    &FLG_ZHTW,    &CNS116431     },
00661   { "cns11643.1986-2",    &FLG_ZHTW,    &CNS116432     },
00662   { "cns11643.1992-1",    &FLG_ZHTW,    &CNS116431     },
00663   { "cns11643.1992.1-0",  &FLG_ZHTW,    &CNS116431     },
00664   { "cns11643.1992-12",   &FLG_NONE,    &Unknown       },
00665   { "cns11643.1992.2-0",  &FLG_ZHTW,    &CNS116432     },
00666   { "cns11643.1992-2",    &FLG_ZHTW,    &CNS116432     },
00667   { "cns11643.1992-3",    &FLG_ZHTW,    &CNS116433     },
00668   { "cns11643.1992.3-0",  &FLG_ZHTW,    &CNS116433     },
00669   { "cns11643.1992.4-0",  &FLG_ZHTW,    &CNS116434     },
00670   { "cns11643.1992-4",    &FLG_ZHTW,    &CNS116434     },
00671   { "cns11643.1992.5-0",  &FLG_ZHTW,    &CNS116435     },
00672   { "cns11643.1992-5",    &FLG_ZHTW,    &CNS116435     },
00673   { "cns11643.1992.6-0",  &FLG_ZHTW,    &CNS116436     },
00674   { "cns11643.1992-6",    &FLG_ZHTW,    &CNS116436     },
00675   { "cns11643.1992.7-0",  &FLG_ZHTW,    &CNS116437     },
00676   { "cns11643.1992-7",    &FLG_ZHTW,    &CNS116437     },
00677   { "cns11643-1",         &FLG_ZHTW,    &CNS116431     },
00678   { "cns11643-2",         &FLG_ZHTW,    &CNS116432     },
00679   { "cns11643-3",         &FLG_ZHTW,    &CNS116433     },
00680   { "cns11643-4",         &FLG_ZHTW,    &CNS116434     },
00681   { "cns11643-5",         &FLG_ZHTW,    &CNS116435     },
00682   { "cns11643-6",         &FLG_ZHTW,    &CNS116436     },
00683   { "cns11643-7",         &FLG_ZHTW,    &CNS116437     },
00684   { "cp1251-1",           &FLG_RUSSIAN, &CP1251        },
00685   { "dec-dectech",        &FLG_NONE,    &Unknown       },
00686   { "dtsymbol-1",         &FLG_NONE,    &Unknown       },
00687   { "fontspecific-0",     &FLG_NONE,    &Unknown       },
00688   { "gb2312.1980-0",      &FLG_ZHCN,    &GB2312        },
00689   { "gb2312.1980-1",      &FLG_ZHCN,    &GB2312        },
00690   { "gb13000.1993-1",     &FLG_ZHCN,    &GBK           },
00691   { "gb18030.2000-0",     &FLG_ZHCN,    &GB18030_0     },
00692   { "gb18030.2000-1",     &FLG_ZHCN,    &GB18030_1     },
00693   { "gbk-0",              &FLG_ZHCN,    &GBK           },
00694   { "gbk1988.1989-0",     &FLG_ZHCN,    &USASCII       },
00695   { "hkscs-1",            &FLG_ZHHK,    &HKSCS         },
00696   { "hp-japanese15",      &FLG_NONE,    &Unknown       },
00697   { "hp-japaneseeuc",     &FLG_NONE,    &Unknown       },
00698   { "hp-roman8",          &FLG_NONE,    &Unknown       },
00699   { "hp-schinese15",      &FLG_NONE,    &Unknown       },
00700   { "hp-tchinese15",      &FLG_NONE,    &Unknown       },
00701   { "hp-tchinesebig5",    &FLG_ZHTW,    &Big5          },
00702   { "hp-wa",              &FLG_NONE,    &Unknown       },
00703   { "hpbig5-",            &FLG_ZHTW,    &Big5          },
00704   { "hphkbig5-",          &FLG_ZHHK,    &HKSCS         },
00705   { "hproc16-",           &FLG_NONE,    &Unknown       },
00706   { "ibm-1046",           &FLG_ARABIC,  &IBM1046       },
00707   { "ibm-1252",           &FLG_NONE,    &Unknown       },
00708   { "ibm-850",            &FLG_NONE,    &Unknown       },
00709   { "ibm-fontspecific",   &FLG_NONE,    &Unknown       },
00710   { "ibm-sbdcn",          &FLG_NONE,    &Unknown       },
00711   { "ibm-sbdtw",          &FLG_NONE,    &Unknown       },
00712   { "ibm-special",        &FLG_NONE,    &Unknown       },
00713   { "ibm-udccn",          &FLG_NONE,    &Unknown       },
00714   { "ibm-udcjp",          &FLG_NONE,    &Unknown       },
00715   { "ibm-udctw",          &FLG_NONE,    &Unknown       },
00716   { "iso646.1991-irv",    &FLG_NONE,    &Unknown       },
00717   { "iso8859-1",          &FLG_WESTERN, &ISO88591      },
00718   { "iso8859-13",         &FLG_BALTIC,  &ISO885913     },
00719   { "iso8859-15",         &FLG_WESTERN, &ISO885915     },
00720   { "iso8859-1@cn",       &FLG_NONE,    &Unknown       },
00721   { "iso8859-1@kr",       &FLG_NONE,    &Unknown       },
00722   { "iso8859-1@tw",       &FLG_NONE,    &Unknown       },
00723   { "iso8859-1@zh",       &FLG_NONE,    &Unknown       },
00724   { "iso8859-2",          &FLG_CE,      &ISO88592      },
00725   { "iso8859-3",          &FLG_WESTERN, &ISO88593      },
00726   { "iso8859-4",          &FLG_BALTIC,  &ISO88594      },
00727   { "iso8859-5",          &FLG_RUSSIAN, &ISO88595      },
00728   { "iso8859-6",          &FLG_ARABIC,  &ISO88596      },
00729   { "iso8859-6.8x",       &FLG_ARABIC,  &ISO885968x    },
00730   { "iso8859-6.16" ,      &FLG_ARABIC,  &ISO8859616    },
00731   { "iso8859-7",          &FLG_GREEK,   &ISO88597      },
00732   { "iso8859-8",          &FLG_HEBREW,  &ISO88598      },
00733   { "iso8859-9",          &FLG_TURKISH, &ISO88599      },
00734   { "iso10646-1",         &FLG_NONE,    &ISO106461     },
00735   { "jisx0201.1976-0",    &FLG_JA,      &JISX0201      },
00736   { "jisx0201.1976-1",    &FLG_JA,      &JISX0201      },
00737   { "jisx0208.1983-0",    &FLG_JA,      &JISX0208      },
00738   { "jisx0208.1990-0",    &FLG_JA,      &JISX0208      },
00739   { "jisx0212.1990-0",    &FLG_JA,      &JISX0212      },
00740   { "koi8-r",             &FLG_RUSSIAN, &KOI8R         },
00741   { "koi8-u",             &FLG_RUSSIAN, &KOI8U         },
00742   { "johab-1",            &FLG_KO,      &X11Johab      },
00743   { "johabs-1",           &FLG_KO,      &X11Johab      },
00744   { "johabsh-1",          &FLG_KO,      &X11Johab      },
00745   { "ksc5601.1987-0",     &FLG_KO,      &KSC5601       },
00746   // we can handle GR fonts with GL encoders (KSC5601 and GB2312)
00747   // See |DoubleByteConvert|.
00748   { "ksc5601.1987-1",     &FLG_KO,      &KSC5601       },
00749   { "ksc5601.1992-3",     &FLG_KO,      &JohabNoAscii  },
00750   { "koreanjamo-0",       &FLG_KO,      &JamoTTF       },
00751   { "microsoft-cp1250",   &FLG_CE,      &CP1250        },
00752   { "microsoft-cp1251",   &FLG_RUSSIAN, &CP1251        },
00753   { "microsoft-cp1252",   &FLG_WESTERN, &CP1252        },
00754   { "microsoft-cp1253",   &FLG_GREEK,   &CP1253        },
00755   { "microsoft-cp1257",   &FLG_BALTIC,  &CP1257        },
00756   { "misc-fontspecific",  &FLG_NONE,    &Unknown       },
00757   { "sgi-fontspecific",   &FLG_NONE,    &Unknown       },
00758   { "sun-fontspecific",   &FLG_NONE,    &Unknown       },
00759   { "sunolcursor-1",      &FLG_NONE,    &Unknown       },
00760   { "sunolglyph-1",       &FLG_NONE,    &Unknown       },
00761   { "symbol-fontspecific",&FLG_NONE,    &Special       },
00762   { "tis620.2529-1",      &FLG_THAI,    &TIS620        },
00763   { "tis620.2533-0",      &FLG_THAI,    &TIS620        },
00764   { "tis620.2533-1",      &FLG_THAI,    &TIS620        },
00765   { "tis620-0",           &FLG_THAI,    &TIS620        },
00766   { "tis620-2",           &FLG_THAI,    &TIS6202       },
00767   { "iso8859-11",         &FLG_THAI,    &ISO885911     },
00768   { "ucs2.cjk-0",         &FLG_NONE,    &ISO106461     },
00769   { "ucs2.cjk_china-0",   &FLG_ZHCN,    &ISO106461     },
00770   { "iso10646.2000-cn",   &FLG_ZHCN,    &ISO106461     },  // HP/UX
00771   { "ucs2.cjk_japan-0",   &FLG_JA,      &ISO106461     },
00772   { "ucs2.cjk_korea-0",   &FLG_KO,      &ISO106461     },
00773   { "korean.ucs2-0",      &FLG_KO,      &ISO106461     },  // HP/UX
00774   { "ucs2.cjk_taiwan-0",  &FLG_ZHTW,    &ISO106461     },
00775   { "ucs2.thai-0",        &FLG_THAI,    &ISO106461     },
00776   { "tamilttf-0",         &FLG_TAMIL,   &TamilTTF      },
00777 #ifdef SUNCTL
00778   { "sun.unicode.india-0",&FLG_INDIC,   &SunIndic      },
00779 #endif /* SUNCTL */
00780 
00781   { nsnull,               nsnull,       nsnull         }
00782 };
00783 
00784 static nsFontFamilyName gFamilyNameTable[] =
00785 {
00786   { "arial",           "helvetica" },
00787   { "courier new",     "courier" },
00788   { "times new roman", "times" },
00789 
00790 #ifdef MOZ_MATHML
00791   { "cmex",             "cmex10" },
00792   { "cmsy",             "cmsy10" },
00793   { "-moz-math-text",   "times" },
00794   { "-moz-math-symbol", "symbol" },
00795 #endif
00796 
00797   { nsnull, nsnull }
00798 };
00799 
00800 static nsFontCharSetMap gNoneCharSetMap[] = { { nsnull }, };
00801 
00802 static nsFontCharSetMap gSpecialCharSetMap[] =
00803 {
00804   { "symbol-adobe-fontspecific", &FLG_NONE, &AdobeSymbol  },
00805   { "euromono-adobe-fontspecific",  &FLG_NONE, &AdobeEuro },
00806   { "eurosans-adobe-fontspecific",  &FLG_NONE, &AdobeEuro },
00807   { "euroserif-adobe-fontspecific", &FLG_NONE, &AdobeEuro },
00808 
00809 #ifdef MOZ_MATHML
00810   { "cmex10-adobe-fontspecific", &FLG_NONE, &CMCMEX  },
00811   { "cmsy10-adobe-fontspecific", &FLG_NONE, &CMCMSY  },
00812   { "cmr10-adobe-fontspecific",  &FLG_NONE, &CMCMR  },
00813   { "cmmi10-adobe-fontspecific", &FLG_NONE, &CMCMMI  },
00814 
00815   { "math1-adobe-fontspecific", &FLG_NONE, &Mathematica1 },
00816   { "math2-adobe-fontspecific", &FLG_NONE, &Mathematica2 },
00817   { "math3-adobe-fontspecific", &FLG_NONE, &Mathematica3 },
00818   { "math4-adobe-fontspecific", &FLG_NONE, &Mathematica4 },
00819   { "math5-adobe-fontspecific", &FLG_NONE, &Mathematica5 },
00820  
00821   { "math1mono-adobe-fontspecific", &FLG_NONE, &Mathematica1 },
00822   { "math2mono-adobe-fontspecific", &FLG_NONE, &Mathematica2 },
00823   { "math3mono-adobe-fontspecific", &FLG_NONE, &Mathematica3 },
00824   { "math4mono-adobe-fontspecific", &FLG_NONE, &Mathematica4 },
00825   { "math5mono-adobe-fontspecific", &FLG_NONE, &Mathematica5 },
00826 #endif
00827 
00828   { nsnull,                      nsnull        }
00829 };
00830 
00831 static nsFontPropertyName gStretchNames[] =
00832 {
00833   { "block",         5 }, // XXX
00834   { "bold",          7 }, // XXX
00835   { "double wide",   9 },
00836   { "medium",        5 },
00837   { "narrow",        3 },
00838   { "normal",        5 },
00839   { "semicondensed", 4 },
00840   { "wide",          7 },
00841 
00842   { nsnull,          0 }
00843 };
00844 
00845 static nsFontPropertyName gWeightNames[] =
00846 {
00847   { "black",    900 },
00848   { "bold",     700 },
00849   { "book",     400 },
00850   { "demi",     600 },
00851   { "demibold", 600 },
00852   { "light",    300 },
00853   { "medium",   400 },
00854   { "regular",  400 },
00855   
00856   { nsnull,     0 }
00857 };
00858 
00859 static char*
00860 atomToName(nsIAtom* aAtom)
00861 {
00862   const char *namePRU;
00863   aAtom->GetUTF8String(&namePRU);
00864   return ToNewCString(nsDependentCString(namePRU));
00865 }
00866 
00867 static PRUint16* gUserDefinedCCMap = nsnull;
00868 static PRUint16* gEmptyCCMap = nsnull;
00869 
00870 //
00871 // smart quotes (and other special chars) in Asian (double byte)
00872 // fonts are too large to use is western fonts.
00873 // Here we define those characters.
00874 // XXX: This array can (and need, for performance) be made |const| when 
00875 // GTK port of gfx gets sync'd with Xlib port for multiple device contexts.
00876   
00877 #include "dbyte_special_chars.ccmap"
00878 DEFINE_CCMAP(gDoubleByteSpecialCharsCCMap, /* nothing */);
00879 
00880 static PRBool
00881 FreeCharSetMap(nsHashKey* aKey, void* aData, void* aClosure)
00882 {
00883   nsFontCharSetMap* charsetMap = (nsFontCharSetMap*) aData;
00884   NS_IF_RELEASE(charsetMap->mInfo->mConverter);
00885   NS_IF_RELEASE(charsetMap->mInfo->mLangGroup);
00886   FreeCCMap(charsetMap->mInfo->mCCMap);
00887 
00888   return PR_TRUE;
00889 }
00890 
00891 static PRBool
00892 FreeFamily(nsHashKey* aKey, void* aData, void* aClosure)
00893 {
00894   delete (nsFontFamily*) aData;
00895 
00896   return PR_TRUE;
00897 }
00898 
00899 static void
00900 FreeStretch(nsFontStretch* aStretch)
00901 {
00902   PR_smprintf_free(aStretch->mScalable);
00903 
00904   for (PRInt32 count = aStretch->mScaledFonts.Count()-1; count >= 0; --count) {
00905     nsFontGTK *font = (nsFontGTK*)aStretch->mScaledFonts.ElementAt(count);
00906     if (font) delete font;
00907   }
00908   // aStretch->mScaledFonts.Clear(); handled by delete of aStretch
00909 
00910   for (int i = 0; i < aStretch->mSizesCount; i++) {
00911     delete aStretch->mSizes[i];
00912   }
00913   delete [] aStretch->mSizes;
00914   delete aStretch;
00915 }
00916 
00917 static void
00918 FreeWeight(nsFontWeight* aWeight)
00919 {
00920   for (int i = 0; i < 9; i++) {
00921     if (aWeight->mStretches[i]) {
00922       for (int j = i + 1; j < 9; j++) {
00923         if (aWeight->mStretches[j] == aWeight->mStretches[i]) {
00924           aWeight->mStretches[j] = nsnull;
00925         }
00926       }
00927       FreeStretch(aWeight->mStretches[i]);
00928     }
00929   }
00930   delete aWeight;
00931 }
00932 
00933 static void
00934 FreeStyle(nsFontStyle* aStyle)
00935 {
00936   for (int i = 0; i < 9; i++) {
00937     if (aStyle->mWeights[i]) {
00938       for (int j = i + 1; j < 9; j++) {
00939         if (aStyle->mWeights[j] == aStyle->mWeights[i]) {
00940           aStyle->mWeights[j] = nsnull;
00941         }
00942       }
00943       FreeWeight(aStyle->mWeights[i]);
00944     }
00945   }
00946   delete aStyle;
00947 }
00948 
00949 PRBool
00950 FreeNode(nsHashKey* aKey, void* aData, void* aClosure)
00951 {
00952   nsFontNode* node = (nsFontNode*) aData;
00953   for (int i = 0; i < 3; i++) {
00954     if (node->mStyles[i]) {
00955       for (int j = i + 1; j < 3; j++) {
00956         if (node->mStyles[j] == node->mStyles[i]) {
00957           node->mStyles[j] = nsnull;
00958         }
00959       }
00960       FreeStyle(node->mStyles[i]);
00961     }
00962   }
00963   delete node;
00964 
00965   return PR_TRUE;
00966 }
00967 
00968 static PRBool
00969 FreeNodeArray(nsHashKey* aKey, void* aData, void* aClosure)
00970 {
00971   nsFontNodeArray* nodes = (nsFontNodeArray*) aData;
00972   delete nodes;
00973 
00974   return PR_TRUE;
00975 }
00976 
00977 static void
00978 FreeGlobals(void)
00979 {
00980   // XXX complete this
00981 
00982   gInitialized = 0;
00983 
00984 #ifdef MOZ_ENABLE_FREETYPE2
00985   nsFT2FontNode::FreeGlobals();
00986 #endif
00987 
00988 #ifdef ENABLE_X_FONT_BANNING
00989   if (gFontRejectRegEx) {
00990     regfree(gFontRejectRegEx);
00991     delete gFontRejectRegEx;
00992     gFontRejectRegEx = nsnull;
00993   }
00994   
00995   if (gFontAcceptRegEx) {
00996     regfree(gFontAcceptRegEx);
00997     delete gFontAcceptRegEx;
00998     gFontAcceptRegEx = nsnull;
00999   }  
01000 #endif /* ENABLE_X_FONT_BANNING */
01001 
01002   nsXFontAAScaledBitmap::FreeGlobals();
01003   nsX11AlphaBlendFreeGlobals();
01004 
01005   if (gAliases) {
01006     delete gAliases;
01007     gAliases = nsnull;
01008   }
01009   NS_IF_RELEASE(gCharSetManager);
01010   if (gCharSetMaps) {
01011     gCharSetMaps->Reset(FreeCharSetMap, nsnull);
01012     delete gCharSetMaps;
01013     gCharSetMaps = nsnull;
01014   }
01015   if (gFamilies) {
01016     gFamilies->Reset(FreeFamily, nsnull);
01017     delete gFamilies;
01018     gFamilies = nsnull;
01019   }
01020   if (gGlobalList) {
01021     delete gGlobalList;
01022     gGlobalList = nsnull;
01023   }
01024   if (gCachedFFRESearches) {
01025     gCachedFFRESearches->Reset(FreeNodeArray, nsnull);
01026     delete gCachedFFRESearches;
01027     gCachedFFRESearches = nsnull;
01028   }
01029   if (gFFRENodes) {
01030     gFFRENodes->Reset(FreeNode, nsnull);
01031     delete gFFRENodes;
01032     gFFRENodes = nsnull;
01033   }
01034   if (gAFRENodes) {
01035     gAFRENodes->Reset(FreeNode, nsnull);
01036     delete gAFRENodes;
01037     gAFRENodes = nsnull;
01038   }
01039   NS_IF_RELEASE(gPref);
01040   if (gSpecialCharSets) {
01041     gSpecialCharSets->Reset(FreeCharSetMap, nsnull);
01042     delete gSpecialCharSets;
01043     gSpecialCharSets = nsnull;
01044   }
01045   if (gStretches) {
01046     delete gStretches;
01047     gStretches = nsnull;
01048   }
01049   NS_IF_RELEASE(gUnicode);
01050   NS_IF_RELEASE(gUserDefined);
01051   NS_IF_RELEASE(gZHTW);
01052   NS_IF_RELEASE(gZHHK);
01053   NS_IF_RELEASE(gZHTWHK);
01054   NS_IF_RELEASE(gUserDefinedConverter);
01055   NS_IF_RELEASE(gUsersLocale);
01056   NS_IF_RELEASE(gWesternLocale);
01057   NS_IF_RELEASE(gFontSubConverter);
01058   if (gWeights) {
01059     delete gWeights;
01060     gWeights = nsnull;
01061   }
01062   nsFontCharSetMap* charSetMap;
01063   for (charSetMap=gCharSetMap; charSetMap->mFontLangGroup; charSetMap++) {
01064     NS_IF_RELEASE(charSetMap->mFontLangGroup->mFontLangGroupAtom);
01065     charSetMap->mFontLangGroup->mFontLangGroupAtom = nsnull;
01066   }
01067   FreeCCMap(gUserDefinedCCMap);
01068   FreeCCMap(gEmptyCCMap);
01069 }
01070 
01071 /*
01072  * Initialize all the font lookup hash tables and other globals
01073  */
01074 static nsresult
01075 InitGlobals(nsIDeviceContext *aDevice)
01076 {
01077 #ifdef NS_FONT_DEBUG
01078   /* First check gfx/src/gtk/-specific env var "NS_FONT_DEBUG_GTK",
01079    * then the more general "NS_FONT_DEBUG" if "NS_FONT_DEBUG_GTK"
01080    * is not present */
01081   const char *debug = PR_GetEnv("NS_FONT_DEBUG_GTK");
01082   if (!debug) {
01083     debug = PR_GetEnv("NS_FONT_DEBUG");
01084   }
01085   
01086   if (debug) {
01087     PR_sscanf(debug, "%lX", &gFontDebug);
01088   }
01089 #endif /* NS_FONT_DEBUG */
01090 
01091   NS_ENSURE_TRUE(nsnull != aDevice, NS_ERROR_NULL_POINTER);
01092 
01093   aDevice->GetCanonicalPixelScale(gDevScale);
01094 
01095   CallGetService(kCharSetManagerCID, &gCharSetManager);
01096   if (!gCharSetManager) {
01097     FreeGlobals();
01098     return NS_ERROR_FAILURE;
01099   }
01100   CallGetService(kPrefCID, &gPref);
01101   if (!gPref) {
01102     FreeGlobals();
01103     return NS_ERROR_FAILURE;
01104   }
01105 
01106   nsCompressedCharMap empty_ccmapObj;
01107   gEmptyCCMap = empty_ccmapObj.NewCCMap();
01108   if (!gEmptyCCMap)
01109     return NS_ERROR_OUT_OF_MEMORY;
01110 
01111   // get the "disable double byte font special chars" setting
01112   PRBool val = PR_TRUE;
01113   nsresult rv = gPref->GetBoolPref("font.allow_double_byte_special_chars", &val);
01114   if (NS_SUCCEEDED(rv))
01115     gAllowDoubleByteSpecialChars = val;
01116 
01117   PRInt32 scale_minimum = 0;
01118   rv = gPref->GetIntPref("font.scale.outline.min", &scale_minimum);
01119   if (NS_SUCCEEDED(rv)) {
01120     gOutlineScaleMinimum = scale_minimum;
01121     SIZE_FONT_PRINTF(("gOutlineScaleMinimum = %d", gOutlineScaleMinimum));
01122   }
01123 
01124   val = PR_TRUE;
01125   rv = gPref->GetBoolPref("font.scale.aa_bitmap.enable", &val);
01126   if (NS_SUCCEEDED(rv)) {
01127     gAABitmapScaleEnabled = val;
01128     SIZE_FONT_PRINTF(("gAABitmapScaleEnabled = %d", gAABitmapScaleEnabled));
01129   }
01130 
01131   val = PR_FALSE;
01132   rv = gPref->GetBoolPref("font.scale.aa_bitmap.always", &val);
01133   if (NS_SUCCEEDED(rv)) {
01134     gAABitmapScaleAlways = val;
01135     SIZE_FONT_PRINTF(("gAABitmapScaleAlways = %d", gAABitmapScaleAlways));
01136   }
01137 
01138   rv = gPref->GetIntPref("font.scale.aa_bitmap.min", &scale_minimum);
01139   if (NS_SUCCEEDED(rv)) {
01140     gAABitmapScaleMinimum = scale_minimum;
01141     SIZE_FONT_PRINTF(("gAABitmapScaleMinimum = %d", gAABitmapScaleMinimum));
01142   }
01143 
01144   PRInt32 percent = 0;
01145   rv = gPref->GetIntPref("font.scale.aa_bitmap.undersize", &percent);
01146   if ((NS_SUCCEEDED(rv)) && (percent)) {
01147     gAABitmapUndersize = percent/100.0;
01148     SIZE_FONT_PRINTF(("gAABitmapUndersize = %g", gAABitmapUndersize));
01149   }
01150   percent = 0;
01151   rv = gPref->GetIntPref("font.scale.aa_bitmap.oversize", &percent);
01152   if ((NS_SUCCEEDED(rv)) && (percent)) {
01153     gAABitmapOversize = percent/100.0;
01154     SIZE_FONT_PRINTF(("gAABitmapOversize = %g", gAABitmapOversize));
01155   }
01156   PRInt32 int_val = 0;
01157   rv = gPref->GetIntPref("font.scale.aa_bitmap.dark_text.min", &int_val);
01158   if (NS_SUCCEEDED(rv)) {
01159     gAASBDarkTextMinValue = int_val;
01160     SIZE_FONT_PRINTF(("gAASBDarkTextMinValue = %d", gAASBDarkTextMinValue));
01161   }
01162   nsXPIDLCString str;
01163   rv = gPref->GetCharPref("font.scale.aa_bitmap.dark_text.gain",
01164                            getter_Copies(str));
01165   if (NS_SUCCEEDED(rv)) {
01166     gAASBDarkTextGain = atof(str.get());
01167     SIZE_FONT_PRINTF(("gAASBDarkTextGain = %g", gAASBDarkTextGain));
01168   }
01169   int_val = 0;
01170   rv = gPref->GetIntPref("font.scale.aa_bitmap.light_text.min", &int_val);
01171   if (NS_SUCCEEDED(rv)) {
01172     gAASBLightTextMinValue = int_val;
01173     SIZE_FONT_PRINTF(("gAASBLightTextMinValue = %d", gAASBLightTextMinValue));
01174   }
01175   rv = gPref->GetCharPref("font.scale.aa_bitmap.light_text.gain",
01176                            getter_Copies(str));
01177   if (NS_SUCCEEDED(rv)) {
01178     gAASBLightTextGain = atof(str.get());
01179     SIZE_FONT_PRINTF(("gAASBLightTextGain = %g", gAASBLightTextGain));
01180   }
01181 
01182   rv = gPref->GetIntPref("font.scale.bitmap.min", &scale_minimum);
01183   if (NS_SUCCEEDED(rv)) {
01184     gBitmapScaleMinimum = scale_minimum;
01185     SIZE_FONT_PRINTF(("gBitmapScaleMinimum = %d", gBitmapScaleMinimum));
01186   }
01187   percent = 0;
01188   gPref->GetIntPref("font.scale.bitmap.oversize", &percent);
01189   if (percent) {
01190     gBitmapOversize = percent/100.0;
01191     SIZE_FONT_PRINTF(("gBitmapOversize = %g", gBitmapOversize));
01192   }
01193   percent = 0;
01194   gPref->GetIntPref("font.scale.bitmap.undersize", &percent);
01195   if (percent) {
01196     gBitmapUndersize = percent/100.0;
01197     SIZE_FONT_PRINTF(("gBitmapUndersize = %g", gBitmapUndersize));
01198   }
01199 
01200   PRBool force_outline_scaled_fonts = gForceOutlineScaledFonts;
01201   rv = gPref->GetBoolPref("font.x11.force_outline_scaled_fonts", &force_outline_scaled_fonts);
01202   if (NS_SUCCEEDED(rv)) {
01203     gForceOutlineScaledFonts = force_outline_scaled_fonts;
01204   }
01205   
01206   PRBool scale_bitmap_fonts_with_devscale = gScaleBitmapFontsWithDevScale;
01207 
01208   rv = gPref->GetBoolPref("font.x11.scale_bitmap_fonts_with_devscale", &scale_bitmap_fonts_with_devscale);
01209   if (NS_SUCCEEDED(rv)) {
01210     gScaleBitmapFontsWithDevScale = scale_bitmap_fonts_with_devscale;
01211   }
01212 
01213   gFFRENodes = new nsHashtable();
01214   if (!gFFRENodes) {
01215     FreeGlobals();
01216     return NS_ERROR_OUT_OF_MEMORY;
01217   }
01218   gAFRENodes = new nsHashtable();
01219   if (!gAFRENodes) {
01220     FreeGlobals();
01221     return NS_ERROR_OUT_OF_MEMORY;
01222   }
01223   gCachedFFRESearches = new nsHashtable();
01224   if (!gCachedFFRESearches) {
01225     FreeGlobals();
01226     return NS_ERROR_OUT_OF_MEMORY;
01227   }
01228   gFamilies = new nsHashtable();
01229   if (!gFamilies) {
01230     FreeGlobals();
01231     return NS_ERROR_OUT_OF_MEMORY;
01232   }
01233   gAliases = new nsHashtable();
01234   if (!gAliases) {
01235     FreeGlobals();
01236     return NS_ERROR_OUT_OF_MEMORY;
01237   }
01238   nsFontFamilyName* f = gFamilyNameTable;
01239   while (f->mName) {
01240     nsCStringKey key(f->mName);
01241     gAliases->Put(&key, (void *)f->mXName);
01242     f++;
01243   }
01244   gWeights = new nsHashtable();
01245   if (!gWeights) {
01246     FreeGlobals();
01247     return NS_ERROR_OUT_OF_MEMORY;
01248   }
01249   nsFontPropertyName* p = gWeightNames;
01250   while (p->mName) {
01251     nsCStringKey key(p->mName);
01252     gWeights->Put(&key, (void*) p->mValue);
01253     p++;
01254   }
01255   gStretches = new nsHashtable();
01256   if (!gStretches) {
01257     FreeGlobals();
01258     return NS_ERROR_OUT_OF_MEMORY;
01259   }
01260   p = gStretchNames;
01261   while (p->mName) {
01262     nsCStringKey key(p->mName);
01263     gStretches->Put(&key, (void*) p->mValue);
01264     p++;
01265   }
01266   gCharSetMaps = new nsHashtable();
01267   if (!gCharSetMaps) {
01268     FreeGlobals();
01269     return NS_ERROR_OUT_OF_MEMORY;
01270   }
01271   nsFontCharSetMap* charSetMap = gCharSetMap;
01272   while (charSetMap->mName) {
01273     nsCStringKey key(charSetMap->mName);
01274     gCharSetMaps->Put(&key, charSetMap);
01275     charSetMap++;
01276   }
01277   gSpecialCharSets = new nsHashtable();
01278   if (!gSpecialCharSets) {
01279     FreeGlobals();
01280     return NS_ERROR_OUT_OF_MEMORY;
01281   }
01282   nsFontCharSetMap* specialCharSetMap = gSpecialCharSetMap;
01283   while (specialCharSetMap->mName) {
01284     nsCStringKey key(specialCharSetMap->mName);
01285     gSpecialCharSets->Put(&key, specialCharSetMap);
01286     specialCharSetMap++;
01287   }
01288 
01289   gUnicode = NS_NewAtom("x-unicode");
01290   if (!gUnicode) {
01291     FreeGlobals();
01292     return NS_ERROR_OUT_OF_MEMORY;
01293   }
01294   gUserDefined = NS_NewAtom(USER_DEFINED);
01295   if (!gUserDefined) {
01296     FreeGlobals();
01297     return NS_ERROR_OUT_OF_MEMORY;
01298   }
01299   gZHTW = NS_NewAtom("zh-TW");
01300   if (!gZHTW) {
01301     FreeGlobals();
01302     return NS_ERROR_OUT_OF_MEMORY;
01303   }
01304   gZHHK = NS_NewAtom("zh-HK");
01305   if (!gZHHK) {
01306     FreeGlobals();
01307     return NS_ERROR_OUT_OF_MEMORY;
01308   }
01309   gZHTWHK = NS_NewAtom("x-zh-TWHK");
01310   if (!gZHTWHK) {
01311     FreeGlobals();
01312     return NS_ERROR_OUT_OF_MEMORY;
01313   }
01314 
01315   // the user's locale
01316   nsCOMPtr<nsILanguageAtomService> langService;
01317   langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
01318   if (langService) {
01319     NS_IF_ADDREF(gUsersLocale = langService->GetLocaleLanguageGroup());
01320   }
01321   if (!gUsersLocale) {
01322     gUsersLocale = NS_NewAtom("x-western");
01323   }
01324   gWesternLocale = NS_NewAtom("x-western");
01325   if (!gUsersLocale) {
01326     FreeGlobals();
01327     return NS_ERROR_OUT_OF_MEMORY;
01328   }
01329 
01330   rv = nsX11AlphaBlendInitGlobals(GDK_DISPLAY());
01331   if (NS_FAILED(rv) || (!nsX11AlphaBlend::CanAntiAlias())) {
01332     gAABitmapScaleEnabled = PR_FALSE;
01333   }
01334 
01335   if (gAABitmapScaleEnabled) {
01336       gAABitmapScaleEnabled = nsXFontAAScaledBitmap::InitGlobals(GDK_DISPLAY(),
01337                                      DefaultScreen(GDK_DISPLAY()));
01338   }
01339   
01340 #ifdef ENABLE_X_FONT_BANNING
01341   /* get the font banning pattern */
01342   nsXPIDLCString fbpattern;
01343   rv = gPref->GetCharPref("font.x11.rejectfontpattern", getter_Copies(fbpattern));
01344   if (NS_SUCCEEDED(rv)) {
01345     gFontRejectRegEx = new regex_t;
01346     if (!gFontRejectRegEx) {
01347       FreeGlobals();
01348       return NS_ERROR_OUT_OF_MEMORY;
01349     }
01350     
01351     /* Compile the pattern - and return an error if we get an invalid pattern... */
01352     if (regcomp(gFontRejectRegEx, fbpattern.get(), REG_EXTENDED|REG_NOSUB) != REG_OK) {
01353       PR_LOG(FontMetricsGTKLM, PR_LOG_DEBUG, ("Invalid rejectfontpattern '%s'\n", fbpattern.get()));
01354       BANNED_FONT_PRINTF(("Invalid font.x11.rejectfontpattern '%s'", fbpattern.get()));
01355       delete gFontRejectRegEx;
01356       gFontRejectRegEx = nsnull;
01357       
01358       FreeGlobals();
01359       return NS_ERROR_INVALID_ARG;
01360     }    
01361   }
01362 
01363   rv = gPref->GetCharPref("font.x11.acceptfontpattern", getter_Copies(fbpattern));
01364   if (NS_SUCCEEDED(rv)) {
01365     gFontAcceptRegEx = new regex_t;
01366     if (!gFontAcceptRegEx) {
01367       FreeGlobals();
01368       return NS_ERROR_OUT_OF_MEMORY;
01369     }
01370     
01371     /* Compile the pattern - and return an error if we get an invalid pattern... */
01372     if (regcomp(gFontAcceptRegEx, fbpattern.get(), REG_EXTENDED|REG_NOSUB) != REG_OK) {
01373       PR_LOG(FontMetricsGTKLM, PR_LOG_DEBUG, ("Invalid acceptfontpattern '%s'\n", fbpattern.get()));
01374       BANNED_FONT_PRINTF(("Invalid font.x11.acceptfontpattern '%s'", fbpattern.get()));
01375       delete gFontAcceptRegEx;
01376       gFontAcceptRegEx = nsnull;
01377       
01378       FreeGlobals();
01379       return NS_ERROR_INVALID_ARG;
01380     }    
01381   }
01382 #endif /* ENABLE_X_FONT_BANNING */
01383 
01384 #ifdef MOZ_ENABLE_FREETYPE2
01385   rv = nsFT2FontNode::InitGlobals();
01386   if (NS_FAILED(rv)) {
01387     FreeGlobals();
01388     return NS_ERROR_OUT_OF_MEMORY;
01389   }
01390 #endif
01391 
01392   gInitialized = 1;
01393 
01394   return NS_OK;
01395 }
01396 
01397 // do the 8 to 16 bit conversion on the stack
01398 // if the data is less than this size
01399 #define WIDEN_8_TO_16_BUF_SIZE 1024
01400 
01401 // handle 8 bit data with a 16 bit font
01402 gint
01403 Widen8To16AndMove(const gchar *char_p, 
01404                   gint char_len, 
01405                   XChar2b *xchar2b_p)
01406 {
01407   int i;
01408   for (i=0; i<char_len; i++) {
01409     (xchar2b_p)->byte1 = 0;
01410     (xchar2b_p++)->byte2 = *char_p++;
01411   }
01412   return(char_len*2);
01413 }
01414 
01415 // handle 8 bit data with a 16 bit font
01416 gint
01417 Widen8To16AndGetWidth (nsXFont        *xFont,
01418                        const gchar    *text,
01419                        gint            text_length)
01420 {
01421   NS_ASSERTION(!xFont->IsSingleByte(),"wrong string/font size");
01422   XChar2b buf[WIDEN_8_TO_16_BUF_SIZE];
01423   XChar2b *p = buf;
01424   int uchar_size;
01425   gint rawWidth;
01426 
01427   if (text_length > WIDEN_8_TO_16_BUF_SIZE) {
01428     p = (XChar2b*)PR_Malloc(text_length*sizeof(XChar2b));
01429     if (!p) return(0); // handle malloc failure
01430   }
01431 
01432   uchar_size = Widen8To16AndMove(text, text_length, p);
01433   rawWidth = xFont->TextWidth16(p, uchar_size/2);
01434 
01435   if (text_length > WIDEN_8_TO_16_BUF_SIZE) {
01436     PR_Free((char*)p);
01437   }
01438   return(rawWidth);
01439 }
01440 
01441 void
01442 Widen8To16AndDraw (GdkDrawable *drawable,
01443                    nsXFont     *xFont,
01444                    GdkGC       *gc,
01445                    gint         x,
01446                    gint         y,
01447                    const gchar *text,
01448                    gint         text_length)
01449 {
01450   NS_ASSERTION(!xFont->IsSingleByte(),"wrong string/font size");
01451   XChar2b buf[WIDEN_8_TO_16_BUF_SIZE];
01452   XChar2b *p = buf;
01453   int uchar_size;
01454 
01455   if (text_length > WIDEN_8_TO_16_BUF_SIZE) {
01456     p = (XChar2b*)PR_Malloc(text_length*sizeof(XChar2b));
01457     if (!p) return; // handle malloc failure
01458   }
01459 
01460   uchar_size = Widen8To16AndMove(text, text_length, p);
01461   xFont->DrawText16(drawable, gc, x, y, p, uchar_size/2);
01462 
01463   if (text_length > WIDEN_8_TO_16_BUF_SIZE) {
01464     PR_Free((char*)p);
01465   }
01466 }
01467 
01468 #ifdef MOZ_MATHML
01469 
01470 void
01471 Widen8To16AndGetTextExtents (nsXFont *xFont,  
01472                         const gchar *text,
01473                         gint         text_length,
01474                         gint        *lbearing,
01475                         gint        *rbearing,
01476                         gint        *width,
01477                         gint        *ascent,
01478                         gint        *descent)
01479 {
01480   NS_ASSERTION(!xFont->IsSingleByte(),"wrong string/font size");
01481   XChar2b buf[WIDEN_8_TO_16_BUF_SIZE];
01482   XChar2b *p = buf;
01483   int uchar_size;
01484 
01485   if (text_length > WIDEN_8_TO_16_BUF_SIZE) {
01486     p = (XChar2b*)PR_Malloc(text_length*sizeof(XChar2b));
01487     if (!p) { // handle malloc failure
01488       *lbearing = 0;
01489       *rbearing = 0;
01490       *width    = 0;
01491       *ascent   = 0;
01492       *descent  = 0;
01493       return;
01494     }
01495   }
01496 
01497   uchar_size = Widen8To16AndMove(text, text_length, p);
01498   xFont->TextExtents16(p, uchar_size/2,
01499                     lbearing, 
01500                     rbearing, 
01501                     width, 
01502                     ascent, 
01503                     descent);
01504 
01505   if (text_length > WIDEN_8_TO_16_BUF_SIZE) {
01506     PR_Free((char*)p);
01507   }
01508 }
01509 
01510 #endif /* MOZ_MATHML */
01511 
01512 nsFontMetricsGTK::nsFontMetricsGTK()
01513   : mFonts() // I'm not sure what the common size is here - I generally
01514   // see 2-5 entries.  For now, punt and let it be allocated later.  We can't
01515   // make it an nsAutoVoidArray since it's a cString array.
01516   // XXX mFontIsGeneric will generally need to be the same size; right now
01517   // it's an nsAutoVoidArray.  If the average is under 8, that's ok.
01518 {
01519   gFontMetricsGTKCount++;
01520 }
01521 
01522 nsFontMetricsGTK::~nsFontMetricsGTK()
01523 {
01524   // do not free mGeneric here
01525 
01526   if (mLoadedFonts) {
01527     PR_Free(mLoadedFonts);
01528     mLoadedFonts = nsnull;
01529   }
01530 
01531   if (mSubstituteFont) {
01532     delete mSubstituteFont;
01533     mSubstituteFont = nsnull;
01534   }
01535 
01536   mWesternFont = nsnull;
01537   mCurrentFont = nsnull;
01538 
01539   if (mDeviceContext) {
01540     // Notify our device context that owns us so that it can update its font cache
01541     mDeviceContext->FontMetricsDeleted(this);
01542     mDeviceContext = nsnull;
01543   }
01544 
01545   if (!--gFontMetricsGTKCount) {
01546     FreeGlobals();
01547   }
01548 }
01549 
01550 NS_IMPL_ISUPPORTS1(nsFontMetricsGTK, nsIFontMetrics)
01551 
01552 static PRBool
01553 IsASCIIFontName(const nsString& aName)
01554 {
01555   PRUint32 len = aName.Length();
01556   const PRUnichar* str = aName.get();
01557   for (PRUint32 i = 0; i < len; i++) {
01558     /*
01559      * X font names are printable ASCII, ignore others (for now)
01560      */
01561     if ((str[i] < 0x20) || (str[i] > 0x7E)) {
01562       return PR_FALSE;
01563     }
01564   }
01565 
01566   return PR_TRUE;
01567 }
01568 
01569 static PRBool
01570 FontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
01571 {
01572 #ifdef REALLY_NOISY_FONTS
01573   printf("font = '");
01574   fputs(NS_LossyConvertUCS2toASCII(aFamily).get(), stdout);
01575   printf("'\n");
01576 #endif
01577 
01578   if (!IsASCIIFontName(aFamily)) {
01579     return PR_TRUE; // skip and continue
01580   }
01581 
01582   nsCAutoString name;
01583   name.AssignWithConversion(aFamily.get());
01584   ToLowerCase(name);
01585   nsFontMetricsGTK* metrics = (nsFontMetricsGTK*) aData;
01586   metrics->mFonts.AppendCString(name);
01587   metrics->mFontIsGeneric.AppendElement((void*) aGeneric);
01588   if (aGeneric) {
01589     metrics->mGeneric = metrics->mFonts.CStringAt(metrics->mFonts.Count() - 1);
01590     return PR_FALSE; // stop
01591   }
01592 
01593   return PR_TRUE; // continue
01594 }
01595 
01596 NS_IMETHODIMP nsFontMetricsGTK::Init(const nsFont& aFont, nsIAtom* aLangGroup,
01597   nsIDeviceContext* aContext)
01598 {
01599   NS_ASSERTION(!(nsnull == aContext), "attempt to init fontmetrics with null device context");
01600 
01601   nsresult res = NS_OK;
01602   mDocConverterType = nsnull;
01603 
01604   if (!gInitialized) {
01605     res = InitGlobals(aContext);
01606     if (NS_FAILED(res))
01607       return res;
01608   }
01609 
01610   mFont = aFont;
01611   mLangGroup = aLangGroup;
01612 
01613   mDeviceContext = aContext;
01614 
01615   float app2dev;
01616   app2dev = mDeviceContext->AppUnitsToDevUnits();
01617 
01618   mPixelSize = NSToIntRound(app2dev * mFont.size);
01619   // Make sure to clamp the pixel size to something reasonable so we
01620   // don't make the X server blow up.
01621   mPixelSize = PR_MIN((gdk_screen_height() - 1) * FONT_MAX_FONT_SCALE, mPixelSize);
01622   mPixelSize = PR_MIN(2000, mPixelSize);
01623 
01624   mStretchIndex = 4; // normal
01625   mStyleIndex = mFont.style;
01626 
01627   mFont.EnumerateFamilies(FontEnumCallback, this);
01628   nsXPIDLCString value;
01629   const char* langGroup;
01630   mLangGroup->GetUTF8String(&langGroup);
01631   if (!mGeneric) {
01632     nsCAutoString name("font.default.");
01633     name.Append(langGroup);
01634     gPref->CopyCharPref(name.get(), getter_Copies(value));
01635     if (value.get()) {
01636       mDefaultFont = value.get();
01637     }
01638     else {
01639       mDefaultFont = "serif";
01640     }
01641     mGeneric = &mDefaultFont;
01642   }
01643 
01644   if (mLangGroup) {
01645     nsCAutoString name("font.min-size.");
01646     if (mGeneric->Equals("monospace")) {
01647       name.Append("fixed");
01648     }
01649     else {
01650       name.Append("variable");
01651     }
01652     name.Append(char('.'));
01653     name.Append(langGroup);
01654     PRInt32 minimum = 0;
01655     res = gPref->GetIntPref(name.get(), &minimum);
01656     if (NS_FAILED(res)) {
01657       gPref->GetDefaultIntPref(name.get(), &minimum);
01658     }
01659     if (minimum < 0) {
01660       minimum = 0;
01661     }
01662     if (mPixelSize < minimum) {
01663       mPixelSize = minimum;
01664     }
01665   }
01666 
01667   if (mLangGroup.get() == gUserDefined) {
01668     if (!gUserDefinedConverter) {
01669       res = gCharSetManager->GetUnicodeEncoderRaw("x-user-defined",
01670                                                  &gUserDefinedConverter);
01671       if (NS_FAILED(res)) {
01672         return res;
01673       }
01674       res = gUserDefinedConverter->SetOutputErrorBehavior(
01675           gUserDefinedConverter->kOnError_Replace, nsnull, '?');
01676       nsCOMPtr<nsICharRepresentable> mapper =
01677         do_QueryInterface(gUserDefinedConverter);
01678       if (mapper) {
01679         gUserDefinedCCMap = MapperToCCMap(mapper);
01680         if (!gUserDefinedCCMap)
01681           return NS_ERROR_OUT_OF_MEMORY;          
01682       }
01683     }
01684 
01685     nsCAutoString name("font.name.");
01686     name.Append(*mGeneric);
01687     name.Append(char('.'));
01688     name.Append(USER_DEFINED);
01689     gPref->CopyCharPref(name.get(), getter_Copies(value));
01690     if (value.get()) {
01691       mUserDefined = value.get();
01692       mIsUserDefined = 1;
01693     }
01694   }
01695 
01696   mWesternFont = FindFont('a');
01697   if (!mWesternFont) {
01698     return NS_ERROR_FAILURE;
01699   }
01700 
01701 
01702   mCurrentFont = mWesternFont;
01703 
01704   RealizeFont();
01705 
01706   return NS_OK;
01707 }
01708 
01709 NS_IMETHODIMP  nsFontMetricsGTK::Destroy()
01710 {
01711   mDeviceContext = nsnull;
01712   return NS_OK;
01713 }
01714 
01715 void nsFontMetricsGTK::RealizeFont()
01716 {
01717   float f;
01718   f = mDeviceContext->DevUnitsToAppUnits();
01719 
01720 #ifdef MOZ_ENABLE_FREETYPE2
01721   if (mWesternFont->IsFreeTypeFont()) {
01722     nsFreeTypeFont *ft = (nsFreeTypeFont *)mWesternFont;
01723     if (!ft)
01724       return;
01725     // now that there are multiple font types (eg: core X fonts
01726     // and TrueType fonts) there should be a common set of methods 
01727     // to get the metrics info from the font object. These methods
01728     // probably should be virtual functions defined in nsFontGTK.
01729     int lineSpacing = ft->ascent() + ft->descent();
01730     if (lineSpacing > mWesternFont->mSize) {
01731       mLeading = nscoord((lineSpacing - mWesternFont->mSize) * f);
01732     }
01733     else {
01734       mLeading = 0;
01735     }
01736     mEmHeight = PR_MAX(1, nscoord(mWesternFont->mSize * f));
01737     mEmAscent = nscoord(ft->ascent() * mWesternFont->mSize * f / lineSpacing);
01738     mEmDescent = mEmHeight - mEmAscent;
01739 
01740     mMaxHeight  = nscoord((ft->max_ascent() + ft->max_descent()) * f);
01741     mMaxAscent  = nscoord(ft->max_ascent() * f) ;
01742     mMaxDescent = nscoord(ft->max_descent() * f);
01743 
01744     mMaxAdvance = nscoord(ft->max_width() * f);
01745     // X may screw up if we try to measure/draw more than 32767 pixels in
01746     // one operation
01747     mMaxStringLength = (PRInt32)floor(32767.0/ft->max_width());
01748     mMaxStringLength = PR_MAX(1, mMaxStringLength);
01749 
01750     // 56% of ascent, best guess for non-true type
01751     mXHeight = NSToCoordRound((float) ft->ascent()* f * 0.56f);
01752 
01753     PRUnichar space = (PRUnichar)' ';
01754     mSpaceWidth = NSToCoordRound(ft->GetWidth(&space, 1) * f);
01755 
01756     PRUnichar averageX = (PRUnichar)'x';
01757     mAveCharWidth = NSToCoordRound(ft->GetWidth(&averageX, 1) * f);
01758 
01759     unsigned long pr = 0;
01760     if (ft->getXHeight(pr)) {
01761       mXHeight = nscoord(pr * f);
01762     }
01763 
01764     float height;
01765     long val;
01766     if (ft->underlinePosition(val)) {
01767       /* this will only be provided from adobe .afm fonts and TrueType
01768        * fonts served by xfsft (not xfstt!) */
01769       mUnderlineOffset = -NSToIntRound(val * f);
01770     }
01771     else {
01772       height = ft->ascent() + ft->descent();
01773       mUnderlineOffset = -NSToIntRound(MAX (1, floor (0.1 * height + 0.5)) * f);
01774     }
01775 
01776     if (ft->underline_thickness(pr)) {
01777       /* this will only be provided from adobe .afm fonts */
01778       mUnderlineSize = nscoord(MAX(f, NSToIntRound(pr * f)));
01779     }
01780     else {
01781       height = ft->ascent() + ft->descent();
01782       mUnderlineSize = NSToIntRound(MAX(1, floor (0.05 * height + 0.5)) * f);
01783     }
01784 
01785     if (ft->superscript_y(val)) {
01786       mSuperscriptOffset = nscoord(MAX(f, NSToIntRound(val * f)));
01787     }
01788     else {
01789       mSuperscriptOffset = mXHeight;
01790     }
01791 
01792     if (ft->subscript_y(val)) {
01793       mSubscriptOffset = nscoord(MAX(f, NSToIntRound(val * f)));
01794     }
01795     else {
01796      mSubscriptOffset = mXHeight;
01797     }
01798 
01799     /* need better way to calculate this */
01800     mStrikeoutOffset = NSToCoordRound(mXHeight / 2.0);
01801     mStrikeoutSize = mUnderlineSize;
01802 
01803     return;
01804   }
01805 #endif
01806   nsXFont *xFont = mWesternFont->GetXFont();
01807   XFontStruct *fontInfo = xFont->GetXFontStruct();
01808   f = mDeviceContext->DevUnitsToAppUnits();
01809 
01810   nscoord lineSpacing = nscoord((fontInfo->ascent + fontInfo->descent) * f);
01811   mEmHeight = PR_MAX(1, nscoord(mWesternFont->mSize * f));
01812   if (lineSpacing > mEmHeight) {
01813     mLeading = lineSpacing - mEmHeight;
01814   }
01815   else {
01816     mLeading = 0;
01817   }
01818   mMaxHeight = nscoord((fontInfo->ascent + fontInfo->descent) * f);
01819   mMaxAscent = nscoord(fontInfo->ascent * f);
01820   mMaxDescent = nscoord(fontInfo->descent * f);
01821 
01822   if (lineSpacing == 0) {
01823     mEmAscent = mEmHeight;
01824   }
01825   else {
01826     mEmAscent = nscoord(mMaxAscent * mEmHeight / lineSpacing);
01827   }
01828   mEmDescent = mEmHeight - mEmAscent;
01829 
01830   mMaxAdvance = nscoord(fontInfo->max_bounds.width * f);
01831   // X may screw up if we try to measure/draw more than 32767 pixels in
01832   // one operation.
01833   mMaxStringLength = (PRInt32)floor(32767.0/fontInfo->max_bounds.width);
01834   mMaxStringLength = PR_MAX(1, mMaxStringLength);
01835 
01836   gint rawWidth, rawAverage;
01837   if ((fontInfo->min_byte1 == 0) && (fontInfo->max_byte1 == 0)) {
01838     rawWidth = xFont->TextWidth8(" ", 1);
01839     rawAverage = xFont->TextWidth8("x", 1);
01840   }
01841   else {
01842     XChar2b _16bit_space, _16bit_x;
01843     _16bit_space.byte1 = 0;
01844     _16bit_space.byte2 = ' ';
01845     _16bit_x.byte1 = 0;
01846     _16bit_x.byte2 = 'x';
01847     rawWidth = xFont->TextWidth16(&_16bit_space, sizeof(_16bit_space)/2);
01848     rawAverage = xFont->TextWidth16(&_16bit_x, sizeof( _16bit_x)/2);
01849   }
01850   mSpaceWidth = NSToCoordRound(rawWidth * f);
01851   mAveCharWidth = NSToCoordRound(rawAverage * f);
01852 
01853   unsigned long pr = 0;
01854   if (xFont->GetXFontProperty(XA_X_HEIGHT, &pr) && pr != 0 &&
01855       pr < 0x00ffffff)  // Bug 43214: arbitrary to exclude garbage values
01856   {
01857     mXHeight = nscoord(pr * f);
01858 #ifdef REALLY_NOISY_FONTS
01859     printf("xHeight=%d\n", mXHeight);
01860 #endif
01861   }
01862   else 
01863   {
01864     // 56% of ascent, best guess for non-true type
01865     mXHeight = NSToCoordRound((float) fontInfo->ascent* f * 0.56f);
01866   }
01867 
01868   if (xFont->GetXFontProperty(XA_UNDERLINE_POSITION, &pr))
01869   {
01870     /* this will only be provided from adobe .afm fonts and TrueType
01871      * fonts served by xfsft (not xfstt!) */
01872     mUnderlineOffset = -NSToIntRound(pr * f);
01873 #ifdef REALLY_NOISY_FONTS
01874     printf("underlineOffset=%d\n", mUnderlineOffset);
01875 #endif
01876   }
01877   else
01878   {
01879     /* this may need to be different than one for those weird asian fonts */
01880     float height;
01881     height = fontInfo->ascent + fontInfo->descent;
01882     mUnderlineOffset = -NSToIntRound(MAX (1, floor (0.1 * height + 0.5)) * f);
01883   }
01884 
01885   if (xFont->GetXFontProperty(XA_UNDERLINE_THICKNESS, &pr))
01886   {
01887     /* this will only be provided from adobe .afm fonts */
01888     mUnderlineSize = nscoord(MAX(f, NSToIntRound(pr * f)));
01889 #ifdef REALLY_NOISY_FONTS
01890     printf("underlineSize=%d\n", mUnderlineSize);
01891 #endif
01892   }
01893   else
01894   {
01895     float height;
01896     height = fontInfo->ascent + fontInfo->descent;
01897     mUnderlineSize = NSToIntRound(MAX(1, floor (0.05 * height + 0.5)) * f);
01898   }
01899 
01900   if (xFont->GetXFontProperty(XA_SUPERSCRIPT_Y, &pr))
01901   {
01902     mSuperscriptOffset = nscoord(MAX(f, NSToIntRound(pr * f)));
01903 #ifdef REALLY_NOISY_FONTS
01904     printf("superscriptOffset=%d\n", mSuperscriptOffset);
01905 #endif
01906   }
01907   else
01908   {
01909     mSuperscriptOffset = mXHeight;
01910   }
01911 
01912   if (xFont->GetXFontProperty(XA_SUBSCRIPT_Y, &pr))
01913   {
01914     mSubscriptOffset = nscoord(MAX(f, NSToIntRound(pr * f)));
01915 #ifdef REALLY_NOISY_FONTS
01916     printf("subscriptOffset=%d\n", mSubscriptOffset);
01917 #endif
01918   }
01919   else
01920   {
01921     mSubscriptOffset = mXHeight;
01922   }
01923 
01924   /* need better way to calculate this */
01925   mStrikeoutOffset = NSToCoordRound(mXHeight / 2.0);
01926   mStrikeoutSize = mUnderlineSize;
01927 }
01928 
01929 NS_IMETHODIMP  nsFontMetricsGTK::GetXHeight(nscoord& aResult)
01930 {
01931   aResult = mXHeight;
01932   return NS_OK;
01933 }
01934 
01935 NS_IMETHODIMP  nsFontMetricsGTK::GetSuperscriptOffset(nscoord& aResult)
01936 {
01937   aResult = mSuperscriptOffset;
01938   return NS_OK;
01939 }
01940 
01941 NS_IMETHODIMP  nsFontMetricsGTK::GetSubscriptOffset(nscoord& aResult)
01942 {
01943   aResult = mSubscriptOffset;
01944   return NS_OK;
01945 }
01946 
01947 NS_IMETHODIMP  nsFontMetricsGTK::GetStrikeout(nscoord& aOffset, nscoord& aSize)
01948 {
01949   aOffset = mStrikeoutOffset;
01950   aSize = mStrikeoutSize;
01951   return NS_OK;
01952 }
01953 
01954 NS_IMETHODIMP  nsFontMetricsGTK::GetUnderline(nscoord& aOffset, nscoord& aSize)
01955 {
01956   aOffset = mUnderlineOffset;
01957   aSize = mUnderlineSize;
01958   return NS_OK;
01959 }
01960 
01961 NS_IMETHODIMP  nsFontMetricsGTK::GetHeight(nscoord &aHeight)
01962 {
01963   aHeight = mMaxHeight;
01964   return NS_OK;
01965 }
01966 
01967 NS_IMETHODIMP  nsFontMetricsGTK::GetNormalLineHeight(nscoord &aHeight)
01968 {
01969   aHeight = mEmHeight + mLeading;
01970   return NS_OK;
01971 }
01972 
01973 NS_IMETHODIMP  nsFontMetricsGTK::GetLeading(nscoord &aLeading)
01974 {
01975   aLeading = mLeading;
01976   return NS_OK;
01977 }
01978 
01979 NS_IMETHODIMP  nsFontMetricsGTK::GetEmHeight(nscoord &aHeight)
01980 {
01981   aHeight = mEmHeight;
01982   return NS_OK;
01983 }
01984 
01985 NS_IMETHODIMP  nsFontMetricsGTK::GetEmAscent(nscoord &aAscent)
01986 {
01987   aAscent = mEmAscent;
01988   return NS_OK;
01989 }
01990 
01991 NS_IMETHODIMP  nsFontMetricsGTK::GetEmDescent(nscoord &aDescent)
01992 {
01993   aDescent = mEmDescent;
01994   return NS_OK;
01995 }
01996 
01997 NS_IMETHODIMP  nsFontMetricsGTK::GetMaxHeight(nscoord &aHeight)
01998 {
01999   aHeight = mMaxHeight;
02000   return NS_OK;
02001 }
02002 
02003 NS_IMETHODIMP  nsFontMetricsGTK::GetMaxAscent(nscoord &aAscent)
02004 {
02005   aAscent = mMaxAscent;
02006   return NS_OK;
02007 }
02008 
02009 NS_IMETHODIMP  nsFontMetricsGTK::GetMaxDescent(nscoord &aDescent)
02010 {
02011   aDescent = mMaxDescent;
02012   return NS_OK;
02013 }
02014 
02015 NS_IMETHODIMP  nsFontMetricsGTK::GetMaxAdvance(nscoord &aAdvance)
02016 {
02017   aAdvance = mMaxAdvance;
02018   return NS_OK;
02019 }
02020 
02021 NS_IMETHODIMP nsFontMetricsGTK::GetAveCharWidth(nscoord &aAveCharWidth)
02022 {
02023   aAveCharWidth = mAveCharWidth;
02024   return NS_OK;
02025 }
02026 
02027 PRInt32 nsFontMetricsGTK::GetMaxStringLength()
02028 {
02029   return mMaxStringLength;
02030 }
02031 
02032 NS_IMETHODIMP  nsFontMetricsGTK::GetLangGroup(nsIAtom** aLangGroup)
02033 {
02034   if (!aLangGroup) {
02035     return NS_ERROR_NULL_POINTER;
02036   }
02037 
02038   *aLangGroup = mLangGroup;
02039   NS_IF_ADDREF(*aLangGroup);
02040 
02041   return NS_OK;
02042 }
02043 
02044 NS_IMETHODIMP  nsFontMetricsGTK::GetFontHandle(nsFontHandle &aHandle)
02045 {
02046   return NS_ERROR_NOT_IMPLEMENTED;
02047 }
02048 
02049 nsFontGTK*
02050 nsFontMetricsGTK::LocateFont(PRUint32 aChar, PRInt32 & aCount)
02051 {
02052   nsFontGTK *font;
02053   PRInt32 i;
02054 
02055   // see if one of our loaded fonts can represent the character
02056   for (i = 0; i < aCount; ++i) {
02057     font = (nsFontGTK*)mLoadedFonts[i];
02058     if (SAFE_CCMAP_HAS_CHAR_EXT(font->mCCMap, aChar))
02059       return font;
02060   }
02061 
02062   font = FindFont(aChar);
02063   aCount = mLoadedFontsCount; // update since FindFont() can change it
02064 
02065   return font;
02066 }
02067 
02068 nsresult
02069 nsFontMetricsGTK::ResolveForwards(const PRUnichar        *aString,
02070                                   PRUint32                aLength,
02071                                   nsFontSwitchCallbackGTK aFunc, 
02072                                   void                   *aData)
02073 {
02074   NS_ASSERTION(aString || !aLength, "invalid call");
02075   const PRUnichar* firstChar = aString;
02076   const PRUnichar* currChar = firstChar;
02077   const PRUnichar* lastChar  = aString + aLength;
02078   nsFontGTK* currFont;
02079   nsFontGTK* nextFont;
02080   PRInt32 count;
02081   nsFontSwitchGTK fontSwitch;
02082 
02083   if (firstChar == lastChar)
02084     return NS_OK;
02085 
02086   count = mLoadedFontsCount;
02087 
02088   if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
02089     currFont = LocateFont(SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
02090     currChar += 2;
02091   }
02092   else {
02093     currFont = LocateFont(*currChar, count);
02094     ++currChar;
02095   }
02096 
02097   //This if block is meant to speedup the process in normal situation, when
02098   //most characters can be found in first font
02099   if (currFont == mLoadedFonts[0]) {
02100     while (currChar < lastChar && SAFE_CCMAP_HAS_CHAR_EXT(currFont->mCCMap,*currChar))
02101       ++currChar;
02102     fontSwitch.mFontGTK = currFont;
02103     if (!(*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData))
02104       return NS_OK;
02105     if (currChar == lastChar)
02106       return NS_OK;
02107     // continue with the next substring, re-using the available loaded fonts
02108     firstChar = currChar;
02109     if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
02110       currFont = LocateFont(SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
02111       currChar += 2;
02112     }
02113     else {
02114       currFont = LocateFont(*currChar, count);
02115       ++currChar;
02116     }
02117   }
02118 
02119   // see if we can keep the same font for adjacent characters
02120   PRInt32 lastCharLen;
02121   while (currChar < lastChar) {
02122     if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
02123       nextFont = LocateFont(SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
02124       lastCharLen = 2;
02125     }
02126     else {
02127       nextFont = LocateFont(*currChar, count);
02128       lastCharLen = 1;
02129     }
02130     if (nextFont != currFont) {
02131       // We have a substring that can be represented with the same font, and
02132       // we are about to switch fonts, it is time to notify our caller.
02133       fontSwitch.mFontGTK = currFont;
02134       if (!(*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData))
02135         return NS_OK;
02136       // continue with the next substring, re-using the available loaded fonts
02137       firstChar = currChar;
02138 
02139       currFont = nextFont; // use the font found earlier for the char
02140     }
02141     currChar += lastCharLen;
02142   }
02143 
02144   //do it for last part of the string
02145   fontSwitch.mFontGTK = currFont;
02146   (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
02147   return NS_OK;
02148 }
02149 
02150 NS_IMETHODIMP
02151 nsFontMetricsGTK::GetSpaceWidth(nscoord &aSpaceWidth)
02152 {
02153   aSpaceWidth = mSpaceWidth;
02154   return NS_OK;
02155 }
02156 
02157 /*
02158  * CSS2 "font properties":
02159  *   font-family
02160  *   font-style
02161  *   font-variant
02162  *   font-weight
02163  *   font-stretch
02164  *   font-size
02165  *   font-size-adjust
02166  *   font
02167  */
02168 
02169 /*
02170  * CSS2 "font descriptors":
02171  *   font-family
02172  *   font-style
02173  *   font-variant
02174  *   font-weight
02175  *   font-stretch
02176  *   font-size
02177  *   unicode-range
02178  *   units-per-em
02179  *   src
02180  *   panose-1
02181  *   stemv
02182  *   stemh
02183  *   slope
02184  *   cap-height
02185  *   x-height
02186  *   ascent
02187  *   descent
02188  *   widths
02189  *   bbox
02190  *   definition-src
02191  *   baseline
02192  *   centerline
02193  *   mathline
02194  *   topline
02195  */
02196 
02197 /*
02198  * XLFD 1.5 "FontName fields":
02199  *   FOUNDRY
02200  *   FAMILY_NAME
02201  *   WEIGHT_NAME
02202  *   SLANT
02203  *   SETWIDTH_NAME
02204  *   ADD_STYLE_NAME
02205  *   PIXEL_SIZE
02206  *   POINT_SIZE
02207  *   RESOLUTION_X
02208  *   RESOLUTION_Y
02209  *   SPACING
02210  *   AVERAGE_WIDTH
02211  *   CHARSET_REGISTRY
02212  *   CHARSET_ENCODING
02213  * XLFD example:
02214  *   -adobe-times-medium-r-normal--17-120-100-100-p-84-iso8859-1
02215  */
02216 
02217 /*
02218  * XLFD 1.5 "font properties":
02219  *   FOUNDRY
02220  *   FAMILY_NAME
02221  *   WEIGHT_NAME
02222  *   SLANT
02223  *   SETWIDTH_NAME
02224  *   ADD_STYLE_NAME
02225  *   PIXEL_SIZE
02226  *   POINT_SIZE
02227  *   RESOLUTION_X
02228  *   RESOLUTION_Y
02229  *   SPACING
02230  *   AVERAGE_WIDTH
02231  *   CHARSET_REGISTRY
02232  *   CHARSET_ENCODING
02233  *   MIN_SPACE
02234  *   NORM_SPACE
02235  *   MAX_SPACE
02236  *   END_SPACE
02237  *   AVG_CAPITAL_WIDTH
02238  *   AVG_LOWERCASE_WIDTH
02239  *   QUAD_WIDTH
02240  *   FIGURE_WIDTH
02241  *   SUPERSCRIPT_X
02242  *   SUPERSCRIPT_Y
02243  *   SUBSCRIPT_X
02244  *   SUBSCRIPT_Y
02245  *   SUPERSCRIPT_SIZE
02246  *   SUBSCRIPT_SIZE
02247  *   SMALL_CAP_SIZE
02248  *   UNDERLINE_POSITION
02249  *   UNDERLINE_THICKNESS
02250  *   STRIKEOUT_ASCENT
02251  *   STRIKEOUT_DESCENT
02252  *   ITALIC_ANGLE
02253  *   CAP_HEIGHT
02254  *   X_HEIGHT
02255  *   RELATIVE_SETWIDTH
02256  *   RELATIVE_WEIGHT
02257  *   WEIGHT
02258  *   RESOLUTION
02259  *   FONT
02260  *   FACE_NAME
02261  *   FULL_NAME
02262  *   COPYRIGHT
02263  *   NOTICE
02264  *   DESTINATION
02265  *   FONT_TYPE
02266  *   FONT_VERSION
02267  *   RASTERIZER_NAME
02268  *   RASTERIZER_VERSION
02269  *   RAW_ASCENT
02270  *   RAW_DESCENT
02271  *   RAW_*
02272  *   AXIS_NAMES
02273  *   AXIS_LIMITS
02274  *   AXIS_TYPES
02275  */
02276 
02277 /*
02278  * XLFD 1.5 BDF 2.1 properties:
02279  *   FONT_ASCENT
02280  *   FONT_DESCENT
02281  *   DEFAULT_CHAR
02282  */
02283 
02284 /*
02285  * CSS2 algorithm, in the following order:
02286  *   font-family:  FAMILY_NAME (and FOUNDRY? (XXX))
02287  *   font-style:   SLANT (XXX: XLFD's RI and RO)
02288  *   font-variant: implemented in mozilla/layout/html/base/src/nsTextFrame.cpp
02289  *   font-weight:  RELATIVE_WEIGHT (XXX), WEIGHT (XXX), WEIGHT_NAME
02290  *   font-size:    XFontStruct.max_bounds.ascent + descent
02291  *
02292  * The following property is not specified in the algorithm spec. It will be
02293  * inserted between the font-weight and font-size steps for now:
02294  *   font-stretch: RELATIVE_SETWIDTH (XXX), SETWIDTH_NAME
02295  */
02296 
02297 /*
02298  * XXX: Things to investigate in the future:
02299  *   ADD_STYLE_NAME font-family's serif and sans-serif
02300  *   SPACING        font-family's monospace; however, there are very few
02301  *                  proportional fonts in non-Latin-1 charsets, so beware in
02302  *                  font prefs dialog
02303  *   AVERAGE_WIDTH  none (see SETWIDTH_NAME)
02304  */
02305 
02306 static gint
02307 SingleByteConvert(nsFontCharSetInfo* aSelf, XFontStruct* aFont,
02308   const PRUnichar* aSrcBuf, PRInt32 aSrcLen, char* aDestBuf, PRInt32 aDestLen)
02309 {
02310   gint count = 0;
02311   if (aSelf->mConverter) {
02312     aSelf->mConverter->Convert(aSrcBuf, &aSrcLen, aDestBuf, &aDestLen);
02313     count = aDestLen;
02314   }
02315 
02316   return count;
02317 }
02318 
02319 /*
02320 static void 
02321 ReverseBuffer(char* aBuf, gint count)
02322 {
02323     char *head, *tail, *med;
02324     head = aBuf;
02325     tail = &aBuf[count-1];
02326     med = &aBuf[count/2];
02327 
02328     while(head < med)
02329     {
02330        char tmp = *head;
02331        *head++ = *tail;
02332        *tail-- = tmp;
02333     }
02334 }
02335 */
02336 
02337 // the following code assume all the PRUnichar is draw in the same
02338 // direction- left to right, without mixing with characters which should
02339 // draw from right to left. This mean it should not be used untill the 
02340 // upper level code resolve bi-di and ensure this assumption. otherwise
02341 // it may break non-bidi pages on a system which have hebrew/arabic fonts
02342 /*
02343 static gint
02344 SingleByteConvertReverse(nsFontCharSetInfo* aSelf, const PRUnichar* aSrcBuf,
02345   PRInt32 aSrcLen, char* aDestBuf, PRInt32 aDestLen)
02346 {
02347     gint count = SingleByteConvert(aSelf, aSrcBuf,
02348                        aSrcLen, aDestBuf,  aDestLen);
02349     ReverseBuffer(aDestBuf, count);
02350     return count;
02351 }
02352 */
02353 
02354 static gint
02355 DoubleByteConvert(nsFontCharSetInfo* aSelf, XFontStruct* aFont,
02356   const PRUnichar* aSrcBuf, PRInt32 aSrcLen, char* aDestBuf, PRInt32 aDestLen)
02357 {
02358   gint count;
02359   if (aSelf->mConverter) {
02360     aSelf->mConverter->Convert(aSrcBuf, &aSrcLen, aDestBuf, &aDestLen);
02361     count = aDestLen;
02362     if (count > 0) {
02363       if ((aDestBuf[0] & 0x80) && (!(aFont->max_byte1 & 0x80))) {
02364         for (PRInt32 i = 0; i < aDestLen; i++) {
02365           aDestBuf[i] &= 0x7F;
02366         }
02367       }
02368       // We're using a GL encoder (KSC5601 or GB2312) but the font is a GR font.
02369       // (ksc5601.1987-1 or gb2312.1980-1)
02370       else if ((!(aDestBuf[0] & 0x80)) && (aFont->min_byte1 & 0x80)) {
02371         for (PRInt32 i = 0; i < aDestLen; i++) {
02372           aDestBuf[i] |= 0x80;
02373         }
02374       }
02375     }
02376   }
02377   else {
02378     count = 0;
02379   }
02380 
02381   return count;
02382 }
02383 
02384 static gint
02385 ISO10646Convert(nsFontCharSetInfo* aSelf, XFontStruct* aFont,
02386   const PRUnichar* aSrcBuf, PRInt32 aSrcLen, char* aDestBuf, PRInt32 aDestLen)
02387 {
02388   aDestLen /= 2;
02389   if (aSrcLen > aDestLen) {
02390     aSrcLen = aDestLen;
02391   }
02392   if (aSrcLen < 0) {
02393     aSrcLen = 0;
02394   }
02395   XChar2b* dest = (XChar2b*) aDestBuf;
02396   for (PRInt32 i = 0; i < aSrcLen; i++) {
02397     dest[i].byte1 = (aSrcBuf[i] >> 8);
02398     dest[i].byte2 = (aSrcBuf[i] & 0xFF);
02399   }
02400 
02401   return (gint) aSrcLen * 2;
02402 }
02403 
02404 #ifdef DEBUG
02405 
02406 static void
02407 CheckMap(nsFontCharSetMap* aEntry)
02408 {
02409   while (aEntry->mName) {
02410     if (aEntry->mInfo->mCharSet) {
02411       // used to use NS_NewAtom??
02412       nsresult res;
02413       nsCOMPtr<nsIUnicodeEncoder> converter;
02414       res = gCharSetManager->GetUnicodeEncoderRaw(aEntry->mInfo->mCharSet,
02415                                                   getter_AddRefs(converter));
02416       if (NS_FAILED(res)) {
02417         printf("=== %s failed (%s)\n", aEntry->mInfo->mCharSet, __FILE__);
02418       }
02419     }
02420     aEntry++;
02421   }
02422 }
02423 
02424 static void
02425 CheckSelf(void)
02426 {
02427   CheckMap(gCharSetMap);
02428 
02429 #ifdef MOZ_MATHML
02430   // For this to pass, the ucvmath module must be built as well
02431   CheckMap(gSpecialCharSetMap);
02432 #endif
02433 }
02434 
02435 #endif /* DEBUG */
02436 
02437 static PRBool
02438 SetUpFontCharSetInfo(nsFontCharSetInfo* aSelf)
02439 {
02440 
02441 #ifdef DEBUG
02442   static int checkedSelf = 0;
02443   if (!checkedSelf) {
02444     CheckSelf();
02445     checkedSelf = 1;
02446   }
02447 #endif
02448 
02449   nsresult res;
02450   // used NS_NewAtom before??
02451   nsIUnicodeEncoder* converter = nsnull;
02452   res = gCharSetManager->GetUnicodeEncoderRaw(aSelf->mCharSet, &converter);
02453   if (NS_SUCCEEDED(res)) {
02454     aSelf->mConverter = converter;
02455     res = converter->SetOutputErrorBehavior(converter->kOnError_Replace,
02456                                             nsnull, '?');
02457     nsCOMPtr<nsICharRepresentable> mapper = do_QueryInterface(converter);
02458     if (mapper) {
02459       aSelf->mCCMap = MapperToCCMap(mapper);
02460       if (aSelf->mCCMap) {
02461 #ifdef DEBUG_bzbarsky
02462           NS_WARNING(nsPrintfCString("\n\ncharset = %s", aSelf->mCharSet).get());
02463 #endif /* DEBUG */
02464   
02465         /*
02466          * We used to disable special characters like smart quotes
02467          * in CJK fonts because if they are quite a bit larger than
02468          * western glyphs and we did not want glyph fill-in to use them
02469          * in single byte documents.
02470          *
02471          * Now, single byte documents find these special chars before
02472          * the CJK fonts are searched so this is no longer needed
02473          * but is useful when trying to determine which font(s) the
02474          * special chars are found in.
02475          */
02476         if ((aSelf->Convert == DoubleByteConvert) 
02477             && (!gAllowDoubleByteSpecialChars)) {
02478           PRUint16* ccmap = aSelf->mCCMap;
02479           PRUint32 page = CCMAP_BEGIN_AT_START_OF_MAP;
02480           const PRUint16* specialmap = gDoubleByteSpecialCharsCCMap;
02481           while (NextNonEmptyCCMapPage(specialmap, &page)) {
02482             PRUint32 pagechar = page;
02483             for (int i=0; i < CCMAP_BITS_PER_PAGE; i++) {
02484               if (CCMAP_HAS_CHAR(specialmap, pagechar)) 
02485                 CCMAP_UNSET_CHAR(ccmap, pagechar);
02486               pagechar++;
02487             }
02488           }
02489         }
02490         return PR_TRUE;
02491       }
02492     }
02493     else {
02494       NS_WARNING("cannot get nsICharRepresentable");
02495     }
02496   }
02497   else {
02498     NS_WARNING("cannot get Unicode converter");
02499   }
02500 
02501   //
02502   // always try to return a map even if it is empty
02503   //
02504   nsCompressedCharMap empty_ccmapObj;
02505   aSelf->mCCMap = empty_ccmapObj.NewCCMap();
02506 
02507   // return false if unable to alloc a map
02508   if (aSelf->mCCMap == nsnull)
02509     return PR_FALSE;
02510 
02511   return PR_TRUE;
02512 }
02513 
02514 #undef DEBUG_DUMP_TREE
02515 #ifdef DEBUG_DUMP_TREE
02516 
02517 static char* gDumpStyles[3] = { "normal", "italic", "oblique" };
02518 
02519 static PRIntn
02520 DumpCharSet(PLHashEntry* he, PRIntn i, void* arg)
02521 {
02522   printf("        %s\n", (char*) he->key);
02523   nsFontCharSet* charSet = (nsFontCharSet*) he->value;
02524   for (int sizeIndex = 0; sizeIndex < charSet->mSizesCount; sizeIndex++) {
02525     nsFontGTK* size = &charSet->mSizes[sizeIndex];
02526     printf("          %d %s\n", size->mSize, size->mName);
02527   }
02528   return HT_ENUMERATE_NEXT;
02529 }
02530 
02531 static void
02532 DumpFamily(nsFontFamily* aFamily)
02533 {
02534   for (int styleIndex = 0; styleIndex < 3; styleIndex++) {
02535     nsFontStyle* style = aFamily->mStyles[styleIndex];
02536     if (style) {
02537       printf("  style: %s\n", gDumpStyles[styleIndex]);
02538       for (int weightIndex = 0; weightIndex < 8; weightIndex++) {
02539         nsFontWeight* weight = style->mWeights[weightIndex];
02540         if (weight) {
02541           printf("    weight: %d\n", (weightIndex + 1) * 100);
02542           for (int stretchIndex = 0; stretchIndex < 9; stretchIndex++) {
02543             nsFontStretch* stretch = weight->mStretches[stretchIndex];
02544             if (stretch) {
02545               printf("      stretch: %d\n", stretchIndex + 1);
02546               PL_HashTableEnumerateEntries(stretch->mCharSets, DumpCharSet,
02547                 nsnull);
02548             }
02549           }
02550         }
02551       }
02552     }
02553   }
02554 }
02555 
02556 // this existing debug code was broken and I have partly fixed it
02557 static PRBool
02558 DumpFamilyEnum(nsHashKey* hashKey, void *aData, void* closure)
02559 {
02560   printf("family: %s\n",
02561          NS_LossyConvertUCS2toASCII(*NS_STATIC_CAST(nsString*,he->key)));
02562   nsFontFamily* family = (nsFontFamily*) he->value;
02563   DumpFamily(family);
02564 
02565   return HT_ENUMERATE_NEXT;
02566 }
02567 
02568 static void
02569 DumpTree(void)
02570 {
02571   gFamilies->Enumerate(DumpFamilyEnum, nsnull);
02572 }
02573 #endif /* DEBUG_DUMP_TREE */
02574 
02575 struct nsFontSearch
02576 {
02577   nsFontMetricsGTK* mMetrics;
02578   PRUint32          mChar;
02579   nsFontGTK*        mFont;
02580 };
02581 
02582 #if 0
02583 static void
02584 GetUnderlineInfo(nsXFont* aFont, unsigned long* aPositionX2,
02585   unsigned long* aThickness)
02586 {
02587   /*
02588    * XLFD 1.5 says underline position defaults descent/2.
02589    * Hence we return position*2 to avoid rounding error.
02590    */
02591   if (aFont->GetXFontProperty(XA_UNDERLINE_POSITION, aPositionX2)) {
02592     *aPositionX2 *= 2;
02593   }
02594   else {
02595     *aPositionX2 = aFont->max_bounds.descent;
02596   }
02597 
02598   /*
02599    * XLFD 1.5 says underline thickness defaults to cap stem width.
02600    * We don't know what that is, so we just take the thickness of "_".
02601    * This way, we get thicker underlines for bold fonts.
02602    */
02603   if (!xFont->GetXFontProperty(XA_UNDERLINE_THICKNESS, aThickness)) {
02604     int dir, ascent, descent;
02605     XCharStruct overall;
02606     XTextExtents(aFont, "_", 1, &dir, &ascent, &descent, &overall);
02607     *aThickness = (overall.ascent + overall.descent);
02608   }
02609 }
02610 #endif /* 0 */
02611 
02612 static PRUint16*
02613 GetMapFor10646Font(XFontStruct* aFont)
02614 {
02615   if (!aFont->per_char)
02616     return nsnull;
02617 
02618   nsCompressedCharMap ccmapObj;
02619   PRInt32 minByte1 = aFont->min_byte1;
02620   PRInt32 maxByte1 = aFont->max_byte1;
02621   PRInt32 minByte2 = aFont->min_char_or_byte2;
02622   PRInt32 maxByte2 = aFont->max_char_or_byte2;
02623   PRInt32 charsPerRow = maxByte2 - minByte2 + 1;
02624   for (PRInt32 row = minByte1; row <= maxByte1; row++) {
02625     PRInt32 offset = (((row - minByte1) * charsPerRow) - minByte2);
02626     for (PRInt32 cell = minByte2; cell <= maxByte2; cell++) {
02627       XCharStruct* bounds = &aFont->per_char[offset + cell];
02628       // From Section 8.5 Font Metrics in the Xlib programming manual:
02629       // A nonexistent character is represented with all members of its XCharStruct set to zero. 
02630       if (bounds->ascent ||
02631           bounds->descent ||
02632           bounds->lbearing ||
02633           bounds->rbearing ||
02634           bounds->width ||
02635           bounds->attributes) {
02636         ccmapObj.SetChar((row << 8) | cell);
02637       }
02638     }
02639   }
02640   PRUint16 *ccmap = ccmapObj.NewCCMap();
02641   return ccmap;
02642 }
02643 
02644 PRBool
02645 nsFontGTK::IsEmptyFont(XFontStruct* xFont)
02646 {
02647 
02648   //
02649   // scan and see if we can find at least one glyph
02650   //
02651   if (xFont->per_char) {
02652     PRInt32 minByte1 = xFont->min_byte1;
02653     PRInt32 maxByte1 = xFont->max_byte1;
02654     PRInt32 minByte2 = xFont->min_char_or_byte2;
02655     PRInt32 maxByte2 = xFont->max_char_or_byte2;
02656     PRInt32 charsPerRow = maxByte2 - minByte2 + 1;
02657     for (PRInt32 row = minByte1; row <= maxByte1; row++) {
02658       PRInt32 offset = (((row - minByte1) * charsPerRow) - minByte2);
02659       for (PRInt32 cell = minByte2; cell <= maxByte2; cell++) {
02660         XCharStruct* bounds = &xFont->per_char[offset + cell];
02661         if (bounds->ascent || bounds->descent) {
02662           return PR_FALSE;
02663         }
02664       }
02665     }
02666   }
02667 
02668   return PR_TRUE;
02669 }
02670 
02671 void
02672 nsFontGTK::LoadFont(void)
02673 {
02674   if (mAlreadyCalledLoadFont) {
02675     return;
02676   }
02677 
02678   mAlreadyCalledLoadFont = PR_TRUE;
02679   GdkFont* gdkFont;
02680   NS_ASSERTION(!mFont, "mFont should not be loaded");
02681   if (mAABaseSize==0) {
02682     NS_ASSERTION(!mFontHolder, "mFontHolder should not be loaded");
02683     gdk_error_trap_push();
02684     gdkFont = ::gdk_font_load(mName);
02685     gdk_error_trap_pop();
02686     if (!gdkFont)
02687       return;
02688     mXFont = new nsXFontNormal(gdkFont);
02689   }
02690   else {
02691     NS_ASSERTION(mFontHolder, "mFontHolder should be loaded");
02692     gdkFont = mFontHolder;
02693     mXFont = new nsXFontAAScaledBitmap(GDK_DISPLAY(),
02694                                        DefaultScreen(GDK_DISPLAY()),
02695                                        gdkFont, mSize, mAABaseSize);
02696   }
02697 
02698   NS_ASSERTION(mXFont,"failed to load mXFont");
02699   if (!mXFont)
02700     return;
02701   if (!mXFont->LoadFont()) {
02702     delete mXFont;
02703     mXFont = nsnull;
02704     return;
02705   }
02706 
02707   if (gdkFont) {
02708     XFontStruct* xFont = mXFont->GetXFontStruct();
02709     XFontStruct* xFont_with_per_char;
02710     if (mAABaseSize==0)
02711       xFont_with_per_char = xFont;
02712     else
02713       xFont_with_per_char = (XFontStruct *)GDK_FONT_XFONT(mFontHolder);
02714 
02715     mMaxAscent = xFont->ascent;
02716     mMaxDescent = xFont->descent;
02717 
02718     if (mCharSetInfo == &ISO106461) {
02719       mCCMap = GetMapFor10646Font(xFont_with_per_char);
02720       if (!mCCMap) {
02721         mXFont->UnloadFont();
02722         mXFont = nsnull;
02723         ::gdk_font_unref(gdkFont);
02724         mFontHolder = nsnull;
02725         return;
02726       }
02727     }
02728 
02729 //
02730 // since we are very close to a release point
02731 // limit the risk of this fix 
02732 // please remove soon
02733 //
02734 // Redhat 6.2 Japanese has invalid jisx201 fonts
02735 // Solaris 2.6 has invalid cns11643 fonts for planes 4-7
02736 if ((mCharSetInfo == &JISX0201)
02737     || (mCharSetInfo == &CNS116434)
02738     || (mCharSetInfo == &CNS116435)
02739     || (mCharSetInfo == &CNS116436)
02740     || (mCharSetInfo == &CNS116437)
02741    ) {
02742 
02743     if (IsEmptyFont(xFont_with_per_char)) {
02744 #ifdef NS_FONT_DEBUG_LOAD_FONT
02745       if (gFontDebug & NS_FONT_DEBUG_LOAD_FONT) {
02746         printf("\n");
02747         printf("***************************************\n");
02748         printf("invalid font \"%s\", %s %d\n", mName, __FILE__, __LINE__);
02749         printf("***************************************\n");
02750         printf("\n");
02751       }
02752 #endif
02753       mXFont->UnloadFont();
02754       mXFont = nsnull;
02755       ::gdk_font_unref(gdkFont);
02756       mFontHolder = nsnull;
02757       return;
02758     }
02759 }
02760     mFont = gdkFont;
02761 
02762 #ifdef NS_FONT_DEBUG_LOAD_FONT
02763     if (gFontDebug & NS_FONT_DEBUG_LOAD_FONT) {
02764       printf("loaded %s\n", mName);
02765     }
02766 #endif
02767 
02768   }
02769 
02770 #ifdef NS_FONT_DEBUG_LOAD_FONT
02771   else if (gFontDebug & NS_FONT_DEBUG_LOAD_FONT) {
02772     printf("cannot load %s\n", mName);
02773   }
02774 #endif
02775 
02776 }
02777 
02778 GdkFont* 
02779 nsFontGTK::GetGDKFont(void)
02780 {
02781   return mFont;
02782 }
02783 
02784 nsXFont*
02785 nsFontGTK::GetXFont(void)
02786 {
02787   return mXFont;
02788 }
02789 
02790 PRBool
02791 nsFontGTK::GetXFontIs10646(void)
02792 {
02793   return ((PRBool) (mCharSetInfo == &ISO106461));
02794 }
02795 
02796 PRBool
02797 nsFontGTK::IsFreeTypeFont(void)
02798 {
02799   return PR_FALSE;
02800 }
02801 
02802 MOZ_DECL_CTOR_COUNTER(nsFontGTK)
02803 
02804 nsFontGTK::nsFontGTK()
02805 {
02806   MOZ_COUNT_CTOR(nsFontGTK);
02807 }
02808 
02809 nsFontGTK::~nsFontGTK()
02810 {
02811   MOZ_COUNT_DTOR(nsFontGTK);
02812   if (mXFont) {
02813     delete mXFont;
02814   }
02815   if (mFont && (mAABaseSize==0)) {
02816     gdk_font_unref(mFont);
02817   }
02818   if (mCharSetInfo == &ISO106461) {
02819     FreeCCMap(mCCMap);
02820   }
02821   if (mName) {
02822     PR_smprintf_free(mName);
02823   }
02824 }
02825 
02826 class nsFontGTKNormal : public nsFontGTK
02827 {
02828 public:
02829   nsFontGTKNormal();
02830   nsFontGTKNormal(nsFontGTK*);
02831   virtual ~nsFontGTKNormal();
02832 
02833   virtual gint GetWidth(const PRUnichar* aString, PRUint32 aLength);
02834   virtual gint DrawString(nsRenderingContextGTK* aContext,
02835                           nsDrawingSurfaceGTK* aSurface, nscoord aX,
02836                           nscoord aY, const PRUnichar* aString,
02837                           PRUint32 aLength);
02838 #ifdef MOZ_MATHML
02839   virtual nsresult GetBoundingMetrics(const PRUnichar*   aString,
02840                                       PRUint32           aLength,
02841                                       nsBoundingMetrics& aBoundingMetrics);
02842 #endif
02843 };
02844 
02845 nsFontGTKNormal::nsFontGTKNormal()
02846 {
02847   mFontHolder = nsnull;
02848 }
02849 
02850 nsFontGTKNormal::nsFontGTKNormal(nsFontGTK *aFont)
02851 {
02852   mAABaseSize = aFont->mSize;
02853   mFontHolder = aFont->GetGDKFont();
02854   if (!mFontHolder) {
02855     aFont->LoadFont();
02856     mFontHolder = aFont->GetGDKFont();
02857   }
02858   NS_ASSERTION(mFontHolder, "font to copy not loaded");
02859   if (mFontHolder)
02860     ::gdk_font_ref(mFontHolder);
02861 }
02862 
02863 nsFontGTKNormal::~nsFontGTKNormal()
02864 {
02865   if (mFontHolder)
02866     ::gdk_font_unref(mFontHolder);
02867 }
02868 
02869 gint
02870 nsFontGTKNormal::GetWidth(const PRUnichar* aString, PRUint32 aLength)
02871 {
02872   if (!mFont) {
02873     LoadFont();
02874     if (!mFont) {
02875       return 0;
02876     }
02877   }
02878 
02879   XChar2b buf[512];
02880   char* p;
02881   PRInt32 bufLen;
02882   ENCODER_BUFFER_ALLOC_IF_NEEDED(p, mCharSetInfo->mConverter,
02883                          aString, aLength, buf, sizeof(buf), bufLen);
02884   gint len = mCharSetInfo->Convert(mCharSetInfo, mXFont->GetXFontStruct(),
02885                                    aString, aLength, p, bufLen);
02886   gint outWidth;
02887   if (mXFont->IsSingleByte())
02888     outWidth = mXFont->TextWidth8(p, len);
02889   else
02890     outWidth = mXFont->TextWidth16((const XChar2b*)p, len/2);
02891   ENCODER_BUFFER_FREE_IF_NEEDED(p, buf);
02892   return outWidth;
02893 }
02894 
02895 gint
02896 nsFontGTKNormal::DrawString(nsRenderingContextGTK* aContext,
02897                             nsDrawingSurfaceGTK* aSurface,
02898                             nscoord aX, nscoord aY,
02899                             const PRUnichar* aString, PRUint32 aLength)
02900 {
02901   if (!mFont) {
02902     LoadFont();
02903     if (!mFont) {
02904       return 0;
02905     }
02906   }
02907 
02908   XChar2b buf[512];
02909   char* p;
02910   PRInt32 bufLen;
02911   ENCODER_BUFFER_ALLOC_IF_NEEDED(p, mCharSetInfo->mConverter,
02912                          aString, aLength, buf, sizeof(buf), bufLen);
02913   gint len = mCharSetInfo->Convert(mCharSetInfo, mXFont->GetXFontStruct(),
02914                                    aString, aLength, p, bufLen);
02915   GdkGC *gc = aContext->GetGC();
02916   gint outWidth;
02917   if (mXFont->IsSingleByte()) {
02918     mXFont->DrawText8(aSurface->GetDrawable(), gc, aX,
02919                                           aY + mBaselineAdjust, p, len);
02920     outWidth = mXFont->TextWidth8(p, len);
02921   }
02922   else {
02923     mXFont->DrawText16(aSurface->GetDrawable(), gc, aX, aY + mBaselineAdjust,
02924                        (const XChar2b*)p, len/2);
02925     outWidth = mXFont->TextWidth16((const XChar2b*)p, len/2);
02926   }
02927   gdk_gc_unref(gc);
02928   ENCODER_BUFFER_FREE_IF_NEEDED(p, buf);
02929   return outWidth;
02930 }
02931 
02932 #ifdef MOZ_MATHML
02933 // bounding metrics for a string 
02934 // remember returned values are not in app units
02935 nsresult
02936 nsFontGTKNormal::GetBoundingMetrics (const PRUnichar*   aString,
02937                                      PRUint32           aLength,
02938                                      nsBoundingMetrics& aBoundingMetrics)                                 
02939 {
02940   aBoundingMetrics.Clear();               
02941 
02942   if (!mFont) {
02943     LoadFont();
02944     if (!mFont) {
02945       return NS_ERROR_FAILURE;
02946     }
02947   }
02948 
02949   if (aString && 0 < aLength) {
02950     XFontStruct *fontInfo = mXFont->GetXFontStruct();
02951     XChar2b buf[512];
02952     char* p;
02953     PRInt32 bufLen;
02954     ENCODER_BUFFER_ALLOC_IF_NEEDED(p, mCharSetInfo->mConverter,
02955                          aString, aLength, buf, sizeof(buf), bufLen);
02956     gint len = mCharSetInfo->Convert(mCharSetInfo, fontInfo, aString, aLength,
02957                                      p, bufLen);
02958     if (mXFont->IsSingleByte()) {
02959       mXFont->TextExtents8(p, len,
02960                            &aBoundingMetrics.leftBearing,
02961                            &aBoundingMetrics.rightBearing,
02962                            &aBoundingMetrics.width,
02963                            &aBoundingMetrics.ascent,
02964                            &aBoundingMetrics.descent);
02965     }
02966     else {
02967       mXFont->TextExtents16((const XChar2b*)p, len,
02968                            &aBoundingMetrics.leftBearing,
02969                            &aBoundingMetrics.rightBearing,
02970                            &aBoundingMetrics.width,
02971                            &aBoundingMetrics.ascent,
02972                            &aBoundingMetrics.descent);
02973     }
02974     ENCODER_BUFFER_FREE_IF_NEEDED(p, buf);
02975   }
02976 
02977   return NS_OK;
02978 }
02979 #endif
02980 
02981 class nsFontGTKSubstitute : public nsFontGTK
02982 {
02983 public:
02984   nsFontGTKSubstitute(nsFontGTK* aFont);
02985   virtual ~nsFontGTKSubstitute();
02986 
02987   virtual GdkFont* GetGDKFont(void);
02988   virtual nsXFont* GetXFont(void);
02989   virtual PRBool   GetXFontIs10646(void);
02990   virtual gint GetWidth(const PRUnichar* aString, PRUint32 aLength);
02991   virtual gint DrawString(nsRenderingContextGTK* aContext,
02992                           nsDrawingSurfaceGTK* aSurface, nscoord aX,
02993                           nscoord aY, const PRUnichar* aString,
02994                           PRUint32 aLength);
02995 #ifdef MOZ_MATHML
02996   virtual nsresult GetBoundingMetrics(const PRUnichar*   aString,
02997                                       PRUint32           aLength,
02998                                       nsBoundingMetrics& aBoundingMetrics);
02999 #endif
03000   virtual PRUint32 Convert(const PRUnichar* aSrc, PRUint32 aSrcLen,
03001                            PRUnichar* aDest, PRUint32 aDestLen);
03002 
03003   nsFontGTK* mSubstituteFont;
03004 };
03005 
03006 nsFontGTKSubstitute::nsFontGTKSubstitute(nsFontGTK* aFont)
03007 {
03008   mSubstituteFont = aFont;
03009 }
03010 
03011 nsFontGTKSubstitute::~nsFontGTKSubstitute()
03012 {
03013   // Do not free mSubstituteFont here. It is owned by somebody else.
03014 }
03015 
03016 PRUint32
03017 nsFontGTKSubstitute::Convert(const PRUnichar* aSrc, PRUint32 aSrcLen,
03018   PRUnichar* aDest, PRUint32 aDestLen)
03019 {
03020   nsresult res;
03021   if (!gFontSubConverter) {
03022     CallCreateInstance(kSaveAsCharsetCID, &gFontSubConverter);
03023     if (gFontSubConverter) {
03024       res = gFontSubConverter->Init("ISO-8859-1",
03025                              nsISaveAsCharset::attr_FallbackQuestionMark +
03026                                nsISaveAsCharset::attr_EntityAfterCharsetConv +
03027                                nsISaveAsCharset::attr_IgnoreIgnorables, 
03028                              nsIEntityConverter::transliterate);
03029       if (NS_FAILED(res)) {
03030         NS_RELEASE(gFontSubConverter);
03031       }
03032     }
03033   }
03034 
03035   if (gFontSubConverter) {
03036     nsAutoString tmp(aSrc, aSrcLen);
03037     char* conv = nsnull;
03038     res = gFontSubConverter->Convert(tmp.get(), &conv);
03039     if (NS_SUCCEEDED(res) && conv) {
03040       char* p = conv;
03041       PRUint32 i;
03042       for (i = 0; i < aDestLen; i++) {
03043         if (*p) {
03044           aDest[i] = *p;
03045         }
03046         else {
03047           break;
03048         }
03049         p++;
03050       }
03051       nsMemory::Free(conv);
03052       conv = nsnull;
03053       return i;
03054     }
03055   }
03056 
03057   if (aSrcLen > aDestLen) {
03058     aSrcLen = aDestLen;
03059   }
03060   for (PRUint32 i = 0; i < aSrcLen; i++) {
03061     aDest[i] = '?';
03062   }
03063 
03064   return aSrcLen;
03065 }
03066 
03067 gint
03068 nsFontGTKSubstitute::GetWidth(const PRUnichar* aString, PRUint32 aLength)
03069 {
03070   PRUnichar buf[512];
03071   PRUnichar *p = buf;
03072   PRUint32 bufLen = sizeof(buf)/sizeof(PRUnichar);
03073   if ((aLength*2) > bufLen) {
03074     PRUnichar *tmp;
03075     tmp = (PRUnichar*)nsMemory::Alloc(sizeof(PRUnichar) * (aLength*2));
03076     if (tmp) {
03077       p = tmp;
03078       bufLen = (aLength*2);
03079     }
03080   }
03081   PRUint32 len = Convert(aString, aLength, p, bufLen);
03082   gint outWidth = mSubstituteFont->GetWidth(p, len);
03083   if (p != buf)
03084     nsMemory::Free(p);
03085   return outWidth;
03086 
03087 }
03088 
03089 gint
03090 nsFontGTKSubstitute::DrawString(nsRenderingContextGTK* aContext,
03091                                 nsDrawingSurfaceGTK* aSurface,
03092                                 nscoord aX, nscoord aY,
03093                                 const PRUnichar* aString, PRUint32 aLength)
03094 {
03095   PRUnichar buf[512];
03096   PRUnichar *p = buf;
03097   PRUint32 bufLen = sizeof(buf)/sizeof(PRUnichar);
03098   if ((aLength*2) > bufLen) {
03099     PRUnichar *tmp;
03100     tmp = (PRUnichar*)nsMemory::Alloc(sizeof(PRUnichar) * (aLength*2));
03101     if (tmp) {
03102       p = tmp;
03103       bufLen = (aLength*2);
03104     }
03105   }
03106   PRUint32 len = Convert(aString, aLength, p, bufLen);
03107   gint outWidth = mSubstituteFont->DrawString(aContext, aSurface, 
03108                                         aX, aY, p, len);
03109   if (p != buf)
03110     nsMemory::Free(p);
03111   return outWidth;
03112 }
03113 
03114 #ifdef MOZ_MATHML
03115 // bounding metrics for a string 
03116 // remember returned values are not in app units
03117 nsresult
03118 nsFontGTKSubstitute::GetBoundingMetrics(const PRUnichar*   aString,
03119                                         PRUint32           aLength,
03120                                         nsBoundingMetrics& aBoundingMetrics)                                 
03121 {
03122   PRUnichar buf[512];
03123   PRUnichar *p = buf;
03124   PRUint32 bufLen = sizeof(buf)/sizeof(PRUnichar);
03125   if ((aLength*2) > bufLen) {
03126     PRUnichar *tmp;
03127     tmp = (PRUnichar*)nsMemory::Alloc(sizeof(PRUnichar) * (aLength*2));
03128     if (tmp) {
03129       p = tmp;
03130       bufLen = (aLength*2);
03131     }
03132   }
03133   PRUint32 len = Convert(aString, aLength, p, bufLen);
03134   nsresult res = mSubstituteFont->GetBoundingMetrics(p, len, 
03135                                     aBoundingMetrics);
03136   if (p != buf)
03137     nsMemory::Free(p);
03138   return res;
03139 }
03140 #endif
03141 
03142 GdkFont* 
03143 nsFontGTKSubstitute::GetGDKFont(void)
03144 {
03145   return mSubstituteFont->GetGDKFont();
03146 }
03147 
03148 nsXFont*
03149 nsFontGTKSubstitute::GetXFont(void)
03150 {
03151   return mSubstituteFont->GetXFont();
03152 }
03153 
03154 PRBool
03155 nsFontGTKSubstitute::GetXFontIs10646(void)
03156 {
03157   return mSubstituteFont->GetXFontIs10646();
03158 }
03159 
03160 class nsFontGTKUserDefined : public nsFontGTK
03161 {
03162 public:
03163   nsFontGTKUserDefined();
03164   virtual ~nsFontGTKUserDefined();
03165 
03166   virtual PRBool Init(nsFontGTK* aFont);
03167   virtual gint GetWidth(const PRUnichar* aString, PRUint32 aLength);
03168   virtual gint DrawString(nsRenderingContextGTK* aContext,
03169                           nsDrawingSurfaceGTK* aSurface, nscoord aX,
03170                           nscoord aY, const PRUnichar* aString,
03171                           PRUint32 aLength);
03172 #ifdef MOZ_MATHML
03173   virtual nsresult GetBoundingMetrics(const PRUnichar*   aString,
03174                                       PRUint32           aLength,
03175                                       nsBoundingMetrics& aBoundingMetrics);
03176 #endif
03177   virtual PRUint32 Convert(const PRUnichar* aSrc, PRInt32 aSrcLen,
03178                            char* aDest, PRInt32 aDestLen);
03179 };
03180 
03181 nsFontGTKUserDefined::nsFontGTKUserDefined()
03182 {
03183 }
03184 
03185 nsFontGTKUserDefined::~nsFontGTKUserDefined()
03186 {
03187   // Do not free mFont here. It is owned by somebody else.
03188 }
03189 
03190 PRBool
03191 nsFontGTKUserDefined::Init(nsFontGTK* aFont)
03192 {
03193   if (!aFont->GetXFont()) {
03194     aFont->LoadFont();
03195     if (!aFont->GetXFont()) {
03196       mCCMap = gEmptyCCMap;
03197       return PR_FALSE;
03198     }
03199   }
03200   mXFont = aFont->GetXFont();
03201   mCCMap = gUserDefinedCCMap;
03202   mName = aFont->mName;
03203 
03204   return PR_TRUE;
03205 }
03206 
03207 PRUint32
03208 nsFontGTKUserDefined::Convert(const PRUnichar* aSrc, PRInt32 aSrcLen,
03209   char* aDest, PRInt32 aDestLen)
03210 {
03211   if (aSrcLen > aDestLen) {
03212     aSrcLen = aDestLen;
03213   }
03214   gUserDefinedConverter->Convert(aSrc, &aSrcLen, aDest, &aDestLen);
03215 
03216   return aSrcLen;
03217 }
03218 
03219 gint
03220 nsFontGTKUserDefined::GetWidth(const PRUnichar* aString, PRUint32 aLength)
03221 {
03222   char buf[1024];
03223   char* p;
03224   PRInt32 bufLen;
03225   ENCODER_BUFFER_ALLOC_IF_NEEDED(p, gUserDefinedConverter,
03226                          aString, aLength, buf, sizeof(buf), bufLen);
03227   PRUint32 len = Convert(aString, aLength, p, bufLen);
03228 
03229   gint outWidth;
03230   if (mXFont->IsSingleByte())
03231     outWidth = mXFont->TextWidth8(p, len);
03232   else
03233     outWidth = mXFont->TextWidth16((const XChar2b*)p, len/2);
03234   ENCODER_BUFFER_FREE_IF_NEEDED(p, buf);
03235   return outWidth;
03236 }
03237 
03238 gint
03239 nsFontGTKUserDefined::DrawString(nsRenderingContextGTK* aContext,
03240                                 nsDrawingSurfaceGTK* aSurface,
03241                                 nscoord aX, nscoord aY,
03242                                 const PRUnichar* aString, PRUint32 aLength)
03243 {
03244   char buf[1024];
03245   char* p;
03246   PRInt32 bufLen;
03247   ENCODER_BUFFER_ALLOC_IF_NEEDED(p, gUserDefinedConverter,
03248                          aString, aLength, buf, sizeof(buf), bufLen);
03249   PRUint32 len = Convert(aString, aLength, p, bufLen);
03250   GdkGC *gc = aContext->GetGC();
03251 
03252   gint outWidth;
03253   if (mXFont->IsSingleByte()) {
03254     mXFont->DrawText8(aSurface->GetDrawable(), gc, aX,
03255                                           aY + mBaselineAdjust, p, len);
03256     outWidth = mXFont->TextWidth8(p, len);
03257   }
03258   else {
03259     mXFont->DrawText16(aSurface->GetDrawable(), gc, aX, aY + mBaselineAdjust,
03260                        (const XChar2b*)p, len);
03261     outWidth = mXFont->TextWidth16((const XChar2b*)p, len/2);
03262   }
03263   gdk_gc_unref(gc);
03264   ENCODER_BUFFER_FREE_IF_NEEDED(p, buf);
03265   return outWidth;
03266 }
03267 
03268 #ifdef MOZ_MATHML
03269 // bounding metrics for a string 
03270 // remember returned values are not in app units
03271 nsresult
03272 nsFontGTKUserDefined::GetBoundingMetrics(const PRUnichar*   aString,
03273                                         PRUint32           aLength,
03274                                         nsBoundingMetrics& aBoundingMetrics)                                 
03275 {
03276   aBoundingMetrics.Clear();               
03277 
03278   if (aString && 0 < aLength) {
03279     char buf[1024];
03280     char* p;
03281     PRInt32 bufLen;
03282     ENCODER_BUFFER_ALLOC_IF_NEEDED(p, gUserDefinedConverter,
03283                          aString, aLength, buf, sizeof(buf), bufLen);
03284     PRUint32 len = Convert(aString, aLength, p, bufLen);
03285     if (mXFont->IsSingleByte()) {
03286       mXFont->TextExtents8(p, len,
03287                            &aBoundingMetrics.leftBearing,
03288                            &aBoundingMetrics.rightBearing,
03289                            &aBoundingMetrics.width,
03290                            &aBoundingMetrics.ascent,
03291                            &aBoundingMetrics.descent);
03292     }
03293     else {
03294       mXFont->TextExtents16((const XChar2b*)p, len,
03295                            &aBoundingMetrics.leftBearing,
03296                            &aBoundingMetrics.rightBearing,
03297                            &aBoundingMetrics.width,
03298                            &aBoundingMetrics.ascent,
03299                            &aBoundingMetrics.descent);
03300     }
03301     ENCODER_BUFFER_FREE_IF_NEEDED(p, buf);
03302   }
03303 
03304   return NS_OK;
03305 }
03306 #endif
03307 
03308 nsFontGTK*
03309 nsFontMetricsGTK::AddToLoadedFontsList(nsFontGTK* aFont)
03310 {
03311   if (mLoadedFontsCount == mLoadedFontsAlloc) {
03312     int newSize;
03313     if (mLoadedFontsAlloc) {
03314       newSize = (2 * mLoadedFontsAlloc);
03315     }
03316     else {
03317       newSize = 1;
03318     }
03319     nsFontGTK** newPointer = (nsFontGTK**) 
03320       PR_Realloc(mLoadedFonts, newSize * sizeof(nsFontGTK*));
03321     if (newPointer) {
03322       mLoadedFonts = newPointer;
03323       mLoadedFontsAlloc = newSize;
03324     }
03325     else {
03326       return nsnull;
03327     }
03328   }
03329   mLoadedFonts[mLoadedFontsCount++] = aFont;
03330   return aFont;
03331 }
03332 
03333 // define a size such that a scaled font would always be closer
03334 // to the desired size than this
03335 #define NOT_FOUND_FONT_SIZE 1000*1000*1000
03336 
03337 nsFontGTK*
03338 nsFontMetricsGTK::FindNearestSize(nsFontStretch* aStretch, PRUint16 aSize)
03339 {
03340   nsFontGTK* font = nsnull;
03341   if (aStretch->mSizes) {
03342     nsFontGTK** begin = aStretch->mSizes;
03343     nsFontGTK** end = &aStretch->mSizes[aStretch->mSizesCount];
03344     nsFontGTK** s;
03345     // scan the list of sizes
03346     for (s = begin; s < end; s++) {
03347       // stop when we hit or overshoot the size
03348       if ((*s)->mSize >= aSize) {
03349         break;
03350       }
03351     }
03352     // backup if we hit the end of the list
03353     if (s == end) {
03354       s--;
03355     }
03356     else if (s != begin) {
03357       // if we overshot pick the closest size
03358       if (((*s)->mSize - aSize) >= (aSize - (*(s - 1))->mSize)) {
03359         s--;
03360       }
03361     }
03362     // this is the nearest bitmap font
03363     font = *s;
03364   }
03365   return font;
03366 }
03367 
03368 static PRBool
03369 SetFontCharsetInfo(nsFontGTK *aFont, nsFontCharSetInfo* aCharSet,
03370                    PRUint32 aChar)
03371 {
03372   if (aCharSet->mCharSet) {
03373     aFont->mCCMap = aCharSet->mCCMap;
03374     // check that the font is not empty
03375     if (!aFont->mCCMap || CCMAP_HAS_CHAR_EXT(aFont->mCCMap, aChar)) {
03376       aFont->LoadFont();
03377       if (!aFont->GetXFont()) {
03378         return PR_FALSE;
03379       }
03380     }
03381   }
03382   else {
03383     if (aCharSet == &ISO106461) {
03384       aFont->LoadFont();
03385       if (!aFont->GetXFont()) {
03386         return PR_FALSE;
03387       }
03388     }
03389   }
03390   return PR_TRUE;
03391 }
03392 
03393 static nsFontGTK*
03394 SetupUserDefinedFont(nsFontGTK *aFont)
03395 {
03396   if (!aFont->mUserDefinedFont) {
03397     aFont->mUserDefinedFont = new nsFontGTKUserDefined();
03398     if (!aFont->mUserDefinedFont) {
03399       return nsnull;
03400     }
03401     if (!aFont->mUserDefinedFont->Init(aFont)) {
03402       return nsnull;
03403     }
03404   }
03405   return aFont->mUserDefinedFont;
03406 }
03407 
03408 
03409 nsFontGTK*
03410 nsFontMetricsGTK::GetAASBBaseFont(nsFontStretch* aStretch, 
03411                               nsFontCharSetInfo* aCharSet)
03412 {
03413   nsFontGTK* base_aafont;
03414   PRInt32 scale_size;
03415   PRUint32 aa_target_size;
03416 
03417   scale_size = PR_MAX(mPixelSize, aCharSet->mAABitmapScaleMin);
03418   aa_target_size = MAX((scale_size*2), 16);
03419   base_aafont = FindNearestSize(aStretch, aa_target_size);
03420   NS_ASSERTION(base_aafont,
03421              "failed to find a base font for Anti-Aliased bitmap Scaling");
03422   return base_aafont;
03423 }
03424 
03425 nsFontGTK*
03426 nsFontMetricsGTK::PickASizeAndLoad(nsFontStretch* aStretch,
03427   nsFontCharSetInfo* aCharSet, PRUint32 aChar, const char *aName)
03428 {
03429 
03430 #ifdef MOZ_ENABLE_FREETYPE2
03431   if (aStretch->mFreeTypeFaceID) {
03432     //FREETYPE_FONT_PRINTF(("mFreeTypeFaceID = 0x%p", aStretch->mFreeTypeFaceID));
03433     nsFreeTypeFont *ftfont = nsFreeTypeFont::NewFont(aStretch->mFreeTypeFaceID,
03434                                                      mPixelSize,
03435                                                      aName);
03436     if (!ftfont) {
03437       FREETYPE_FONT_PRINTF(("failed to create font"));
03438       return nsnull;
03439     }
03440     //FREETYPE_FONT_PRINTF(("created ftfont"));
03441     /*
03442      * XXX Instead of passing pixel size, we ought to take underline
03443      * into account. (Extra space for underline for Asian fonts.)
03444      */
03445     ftfont->mName = PR_smprintf("%s", aName);
03446     if (!ftfont->mName) {
03447       FREETYPE_FONT_PRINTF(("failed to create mName"));
03448       delete ftfont;
03449       return nsnull;
03450     }
03451     SetCharsetLangGroup(aCharSet);
03452     ftfont->mSize = mPixelSize;
03453     ftfont->LoadFont();
03454     ftfont->mCharSetInfo = &ISO106461;
03455     //FREETYPE_FONT_PRINTF(("add the ftfont"));
03456     return AddToLoadedFontsList(ftfont);
03457   }
03458 
03459   if (!IS_IN_BMP(aChar)) {
03460     // Non-BMP is only supported by FreeType
03461     return nsnull;
03462   }
03463 #endif
03464 
03465   PRBool use_scaled_font = PR_FALSE;
03466   PRBool have_nearly_rightsized_bitmap = PR_FALSE;
03467   nsFontGTK* base_aafont = nsnull;
03468 
03469   PRInt32 bitmap_size = NOT_FOUND_FONT_SIZE;
03470   PRInt32 scale_size = mPixelSize;
03471   nsFontGTK* font = FindNearestSize(aStretch, mPixelSize);
03472   if (font) {
03473     bitmap_size = font->mSize;
03474     if (   (bitmap_size >= mPixelSize-(mPixelSize/10))
03475         && (bitmap_size <= mPixelSize+(mPixelSize/10)))
03476       // When the size of a hand tuned font is close to the desired size
03477       // favor it over outline scaled font
03478       have_nearly_rightsized_bitmap = PR_TRUE;
03479   }
03480 
03481   //
03482   // If the user says always try to aasb (anti alias scaled bitmap) scale
03483   //
03484   if (gAABitmapScaleEnabled && aCharSet->mAABitmapScaleAlways) {
03485     base_aafont = GetAASBBaseFont(aStretch, aCharSet);
03486     if (base_aafont) {
03487       use_scaled_font = PR_TRUE;
03488       SIZE_FONT_PRINTF(("anti-aliased bitmap scaled font: %s\n"
03489             "                    desired=%d, aa-scaled=%d, bitmap=%d, "
03490             "aa_bitmap=%d",
03491             aName, mPixelSize, scale_size, bitmap_size, base_aafont->mSize));
03492     }
03493   }
03494 
03495   //
03496   // if not already aasb scaling and
03497   // if we do not have a bitmap that is nearly the correct size 
03498   //
03499   if (!use_scaled_font && !have_nearly_rightsized_bitmap) {
03500     // check if we can use an outline scaled font
03501     if (aStretch->mOutlineScaled) {
03502       scale_size = PR_MAX(mPixelSize, aCharSet->mOutlineScaleMin);
03503 
03504       if (PR_ABS(mPixelSize-scale_size) < PR_ABS(mPixelSize-bitmap_size)) {
03505         use_scaled_font = 1;
03506         SIZE_FONT_PRINTF(("outline font:______ %s\n"
03507                   "                    desired=%d, scaled=%d, bitmap=%d", 
03508                   aStretch->mScalable, mPixelSize, scale_size,
03509                   (bitmap_size=NOT_FOUND_FONT_SIZE?0:bitmap_size)));
03510       }
03511     }
03512     // see if we can aasb (anti alias scaled bitmap)
03513     if (!use_scaled_font 
03514         && (bitmap_size<NOT_FOUND_FONT_SIZE) && gAABitmapScaleEnabled) {
03515       // if we do not have a near-the-right-size font or scalable font
03516       // see if we can anti-alias bitmap scale one
03517       scale_size = PR_MAX(mPixelSize, aCharSet->mAABitmapScaleMin);
03518       double ratio = (bitmap_size / ((double) mPixelSize));
03519       if (   (ratio < aCharSet->mAABitmapUndersize)
03520           || (ratio > aCharSet->mAABitmapOversize)) {
03521         //
03522         // Try to get a size font to scale that is 2x larger 
03523         // (but at least 16 pixel)
03524         //
03525         base_aafont = GetAASBBaseFont(aStretch, aCharSet);
03526         if (base_aafont) {
03527           use_scaled_font = PR_TRUE;
03528           SIZE_FONT_PRINTF(("anti-aliased bitmap scaled font: %s\n"
03529               "                    desired=%d, aa-scaled=%d, bitmap=%d, "
03530               "aa_bitmap=%d",
03531               aName, mPixelSize, scale_size, bitmap_size, base_aafont->mSize));
03532         }
03533       }
03534     }
03535     // last resort: consider a bitmap scaled font (ugly!)
03536     if (!use_scaled_font && aStretch->mScalable) {
03537       scale_size = PR_MAX(mPixelSize, aCharSet->mBitmapScaleMin);
03538       double ratio = (bitmap_size / ((double) mPixelSize));
03539       if ((ratio < aCharSet->mBitmapUndersize)
03540         || (ratio > aCharSet->mBitmapOversize)) {
03541         if ((PR_ABS(mPixelSize-scale_size) < PR_ABS(mPixelSize-bitmap_size))) {
03542           use_scaled_font = 1;
03543           SIZE_FONT_PRINTF(("bitmap scaled font: %s\n"
03544                 "                    desired=%d, scaled=%d, bitmap=%d", 
03545                 aStretch->mScalable, mPixelSize, scale_size,
03546                 (bitmap_size=NOT_FOUND_FONT_SIZE?0:bitmap_size)));
03547         }
03548       }
03549     }
03550   }
03551 
03552   NS_ASSERTION((bitmap_size<NOT_FOUND_FONT_SIZE)||use_scaled_font,
03553                 "did not find font size");
03554   if (!use_scaled_font) {
03555     SIZE_FONT_PRINTF(("bitmap font:_______ %s\n" 
03556                       "                    desired=%d, scaled=%d, bitmap=%d", 
03557                       aName, mPixelSize, scale_size, bitmap_size));
03558   }
03559 
03560   if (use_scaled_font) {
03561    SIZE_FONT_PRINTF(("scaled font:_______ %s\n"
03562                      "                    desired=%d, scaled=%d, bitmap=%d",
03563                      aName, mPixelSize, scale_size, bitmap_size));
03564 
03565     PRInt32 i;
03566     PRInt32 n = aStretch->mScaledFonts.Count();
03567     nsFontGTK* p = nsnull;
03568     for (i = 0; i < n; i++) {
03569       p = (nsFontGTK*) aStretch->mScaledFonts.ElementAt(i);
03570       if (p->mSize == scale_size) {
03571         break;
03572       }
03573     }
03574     if (i == n) {
03575       if (base_aafont) {
03576         // setup the base font
03577         if (!SetFontCharsetInfo(base_aafont, aCharSet, aChar))
03578           return nsnull;
03579         if (mIsUserDefined) {
03580           base_aafont = SetupUserDefinedFont(base_aafont);
03581           if (!base_aafont)
03582             return nsnull;
03583         }
03584         font = new nsFontGTKNormal(base_aafont);
03585       }
03586       else
03587         font = new nsFontGTKNormal;
03588       if (font) {
03589         /*
03590          * XXX Instead of passing pixel size, we ought to take underline
03591          * into account. (Extra space for underline for Asian fonts.)
03592          */
03593         if (base_aafont) {
03594           font->mName = PR_smprintf("%s", base_aafont->mName);
03595           font->mAABaseSize = base_aafont->mSize;
03596         }
03597         else {
03598           font->mName = PR_smprintf(aStretch->mScalable, scale_size);
03599           font->mAABaseSize = 0;
03600         }
03601         if (!font->mName) {
03602           delete font;
03603           return nsnull;
03604         }
03605         font->mSize = scale_size;
03606         font->mCharSetInfo = aCharSet;
03607         aStretch->mScaledFonts.AppendElement(font);
03608       }
03609       else {
03610         return nsnull;
03611       }
03612     }
03613     else {
03614       font = p;
03615     }
03616   }
03617 
03618   if (!SetFontCharsetInfo(font, aCharSet, aChar))
03619     return nsnull;
03620 
03621   if (mIsUserDefined) {
03622     font = SetupUserDefinedFont(font);
03623     if (!font)
03624       return nsnull;
03625   }
03626 
03627   return AddToLoadedFontsList(font);
03628 }
03629 
03630 nsresult
03631 nsFontMetricsGTK::GetWidth  (const char* aString, PRUint32 aLength,
03632                              nscoord& aWidth,
03633                              nsRenderingContextGTK *aContext)
03634 {
03635     if (aLength == 0) {
03636         aWidth = 0;
03637         return NS_OK;
03638     }
03639 
03640     nsXFont *xFont = mCurrentFont->GetXFont();
03641     gint rawWidth;
03642     
03643     if (mCurrentFont->IsFreeTypeFont()) {
03644         // this function is only supposed to be called for ascii data
03645         rawWidth = mCurrentFont->
03646           GetWidth(NS_ConvertASCIItoUTF16(aString, aLength).get(), aLength);
03647     }
03648     else if (!mCurrentFont->GetXFontIs10646()) {
03649         NS_ASSERTION(xFont->IsSingleByte(),"wrong string/font size");
03650         // 8 bit data with an 8 bit font
03651         rawWidth = xFont->TextWidth8(aString, aLength);
03652     }
03653     else {
03654         NS_ASSERTION(!xFont->IsSingleByte(),"wrong string/font size");
03655         // we have 8 bit data but a 16 bit font
03656         rawWidth = Widen8To16AndGetWidth (mCurrentFont->GetXFont(),
03657                                           aString, aLength);
03658     }
03659 
03660     float f;
03661     f = mDeviceContext->DevUnitsToAppUnits();
03662     aWidth = NSToCoordRound(rawWidth * f);
03663 
03664     return NS_OK;
03665 }
03666 
03667 nsresult
03668 nsFontMetricsGTK::GetWidth  (const PRUnichar* aString, PRUint32 aLength,
03669                              nscoord& aWidth, PRInt32* aFontID,
03670                              nsRenderingContextGTK *aContext)
03671 {
03672     if (aLength == 0) {
03673         aWidth = 0;
03674         return NS_OK;
03675     }
03676 
03677     nsFontGTK* prevFont = nsnull;
03678     gint rawWidth = 0;
03679     PRUint32 start = 0;
03680     PRUint32 i;
03681 
03682     PRUint32 extraSurrogateLength;
03683     for (i = 0; i < aLength; i+=1+extraSurrogateLength) {
03684         PRUint32 c = aString[i];
03685         extraSurrogateLength=0;
03686 
03687         if(i < aLength-1 && IS_HIGH_SURROGATE(c) && IS_LOW_SURROGATE(aString[i+1])) {
03688           // if surrogate, make UCS4 code point from high aString[i] and
03689           // low surrogate aString[i+1]
03690           c = SURROGATE_TO_UCS4(c, aString[i+1]);
03691 
03692           // skip aString[i+1], it is already used as low surrogate
03693           extraSurrogateLength = 1;
03694         }
03695         nsFontGTK* currFont = nsnull;
03696         nsFontGTK** font = mLoadedFonts;
03697         nsFontGTK** end = &mLoadedFonts[mLoadedFontsCount];
03698         while (font < end) {
03699             if (SAFE_CCMAP_HAS_CHAR_EXT((*font)->mCCMap, c)) {
03700                 currFont = *font;
03701                 goto FoundFont; // for speed -- avoid "if" statement
03702             }
03703             font++;
03704         }
03705         currFont = FindFont(c);
03706     FoundFont:
03707         // XXX avoid this test by duplicating code -- erik
03708         if (prevFont) {
03709             if (currFont != prevFont) {
03710                 rawWidth += prevFont->GetWidth(&aString[start], i - start);
03711                 prevFont = currFont;
03712                 start = i;
03713             }
03714         }
03715         else {
03716             prevFont = currFont;
03717             start = i;
03718         }
03719     }
03720 
03721     if (prevFont) {
03722         rawWidth += prevFont->GetWidth(&aString[start], i - start);
03723     }
03724 
03725     float f;
03726     f = mDeviceContext->DevUnitsToAppUnits();
03727     aWidth = NSToCoordRound(rawWidth * f);
03728 
03729     if (nsnull != aFontID)
03730         *aFontID = 0;
03731 
03732     return NS_OK;
03733 }
03734 
03735 nsresult
03736 nsFontMetricsGTK::DrawString(const char *aString, PRUint32 aLength,
03737                              nscoord aX, nscoord aY,
03738                              const nscoord* aSpacing,
03739                              nsRenderingContextGTK *aContext,
03740                              nsDrawingSurfaceGTK *aSurface)
03741 {
03742   if (!aLength)
03743       return NS_ERROR_FAILURE;
03744 
03745   nsresult rv = NS_OK;
03746 
03747   g_return_val_if_fail(aString != NULL, NS_ERROR_FAILURE);
03748   g_return_val_if_fail(mCurrentFont != NULL, NS_ERROR_FAILURE);
03749 
03750   nscoord x = aX;
03751   nscoord y = aY;
03752 
03753   aContext->UpdateGC();
03754 
03755   nsXFont *xFont = mCurrentFont->GetXFont();
03756 
03757   // Get the gc - note that we have to unref this later
03758   GdkGC *gc = aContext->GetGC();
03759 
03760   if (nsnull != aSpacing) {
03761       // Render the string, one character at a time...
03762       const char* end = aString + aLength;
03763 
03764       while (aString < end) {
03765           char ch = *aString++;
03766           nscoord xx = x;
03767           nscoord yy = y;
03768           aContext->GetTranMatrix()->TransformCoord(&xx, &yy);
03769 
03770           if (mCurrentFont->IsFreeTypeFont()) {
03771               // this function is only supposed to be called for ascii data
03772               rv = mCurrentFont->DrawString(aContext, aSurface, xx, yy, 
03773                                  NS_ConvertASCIItoUTF16(aString, aLength).get(),
03774                                  aLength);
03775           }
03776           else if (!mCurrentFont->GetXFontIs10646()) {
03777               // 8 bit data with an 8 bit font
03778               NS_ASSERTION(xFont->IsSingleByte(),"wrong string/font size");
03779               xFont->DrawText8(aSurface->GetDrawable(), gc, xx, yy, &ch, 1);
03780           }
03781           else {
03782               // we have 8 bit data but a 16 bit font
03783               NS_ASSERTION(!xFont->IsSingleByte(),"wrong string/font size");
03784               Widen8To16AndDraw(aSurface->GetDrawable(), xFont, gc,
03785                                 xx, yy, &ch, 1);
03786           }
03787 
03788           x += *aSpacing++;
03789       }
03790   }
03791   else {
03792       aContext->GetTranMatrix()->TransformCoord(&x, &y);
03793 
03794       if (mCurrentFont->IsFreeTypeFont()) {
03795           // this function is only supposed to be called for ascii data
03796           rv = mCurrentFont->DrawString(aContext, aSurface, x, y, 
03797                              NS_ConvertASCIItoUTF16(aString, aLength).get(),
03798                              aLength);
03799       }
03800       else if (!mCurrentFont->GetXFontIs10646()) { // keep 8 bit path fast
03801           // 8 bit data with an 8 bit font
03802           NS_ASSERTION(xFont->IsSingleByte(),"wrong string/font size");
03803           xFont->DrawText8(aSurface->GetDrawable(), gc,
03804                            x, y, aString, aLength);
03805       }
03806       else {
03807           // we have 8 bit data but a 16 bit font
03808           NS_ASSERTION(!xFont->IsSingleByte(),"wrong string/font size");
03809           Widen8To16AndDraw(aSurface->GetDrawable(), xFont, gc,
03810                             x, y, aString, aLength);
03811       }
03812   }
03813 
03814   gdk_gc_unref(gc);
03815 
03816   return rv;
03817 }
03818 
03819 nsresult
03820 nsFontMetricsGTK::DrawString(const PRUnichar* aString, PRUint32 aLength,
03821                              nscoord aX, nscoord aY,
03822                              PRInt32 aFontID,
03823                              const nscoord* aSpacing,
03824                              nsRenderingContextGTK *aContext,
03825                              nsDrawingSurfaceGTK *aSurface)
03826 {
03827     if (!aLength)
03828         return NS_ERROR_FAILURE;
03829 
03830     g_return_val_if_fail(aSurface != NULL, NS_ERROR_FAILURE);
03831     g_return_val_if_fail(aString != NULL, NS_ERROR_FAILURE);
03832 
03833     nscoord x = aX;
03834     nscoord y = aY;
03835 
03836     aContext->GetTranMatrix()->TransformCoord(&x, &y);
03837 
03838     nsFontGTK* prevFont = nsnull;
03839     PRUint32 start = 0;
03840     PRUint32 i;
03841 
03842     PRUint32 extraSurrogateLength;
03843     for (i = 0; i < aLength; i+=1+extraSurrogateLength) {
03844         PRUint32 c = aString[i];
03845         extraSurrogateLength=0;
03846         if(i < aLength-1 && IS_HIGH_SURROGATE(c) && IS_LOW_SURROGATE(aString[i+1])) {
03847           // if surrogate, make UCS4 code point from high aString[i] and
03848           // low surrogate aString[i+1]
03849           c = SURROGATE_TO_UCS4(c, aString[i+1]);
03850  
03851           // skip aString[i+1], it is already used as low surrogate
03852           extraSurrogateLength = 1;
03853         }
03854         nsFontGTK* currFont = nsnull;
03855         nsFontGTK** font = mLoadedFonts;
03856         nsFontGTK** lastFont = &mLoadedFonts[mLoadedFontsCount];
03857         while (font < lastFont) {
03858             if (SAFE_CCMAP_HAS_CHAR_EXT((*font)->mCCMap, c)) {
03859                 currFont = *font;
03860                 goto FoundFont; // for speed -- avoid "if" statement
03861             }
03862             font++;
03863         }
03864 
03865         currFont = FindFont(c);
03866 
03867     FoundFont:
03868         // XXX avoid this test by duplicating code -- erik
03869         if (prevFont) {
03870             if (currFont != prevFont) {
03871                 if (aSpacing) {
03872                     const PRUnichar* str = &aString[start];
03873                     const PRUnichar* end = &aString[i];
03874 
03875                     // save off mCurrentFont and set it so that we
03876                     // cache the GC's font correctly
03877                     nsFontGTK *oldFont = mCurrentFont;
03878                     mCurrentFont = prevFont;
03879                     aContext->UpdateGC();
03880 
03881                     while (str < end) {
03882                         x = aX;
03883                         y = aY;
03884                         aContext->GetTranMatrix()->TransformCoord(&x, &y);
03885                         prevFont->DrawString(aContext, aSurface, x, y, str, 1);
03886                         aX += *aSpacing++;
03887                         str++;
03888                     }
03889 
03890                     mCurrentFont = oldFont;
03891                 }
03892                 else {
03893                     nsFontGTK *oldFont = mCurrentFont;
03894                     mCurrentFont = prevFont;
03895                     aContext->UpdateGC();
03896 
03897                     x += prevFont->DrawString(aContext, aSurface,
03898                                               x, y, &aString[start],
03899                                               i - start);
03900 
03901                     mCurrentFont = oldFont;
03902                 }
03903 
03904                 prevFont = currFont;
03905                 start = i;
03906             }
03907         }
03908         else {
03909             prevFont = currFont;
03910             start = i;
03911         }
03912     }
03913 
03914     if (prevFont) {
03915         nsFontGTK *oldFont = mCurrentFont;
03916         mCurrentFont = prevFont;
03917         aContext->UpdateGC();
03918     
03919         if (aSpacing) {
03920             const PRUnichar* str = &aString[start];
03921             const PRUnichar* end = &aString[i];
03922 
03923             while (str < end) {
03924                 x = aX;
03925                 y = aY;
03926                 aContext->GetTranMatrix()->TransformCoord(&x, &y);
03927                 prevFont->DrawString(aContext, aSurface, x, y, str, 1);
03928                 aX += *aSpacing++;
03929                 str++;
03930             }
03931         }
03932         else {
03933             prevFont->DrawString(aContext, aSurface, x, y,
03934                                  &aString[start], i - start);
03935         }
03936 
03937         mCurrentFont = oldFont;
03938     }
03939 
03940   return NS_OK;
03941 }
03942 
03943 #ifdef MOZ_MATHML
03944 
03945 nsresult
03946 nsFontMetricsGTK::GetBoundingMetrics(const char *aString, PRUint32 aLength,
03947                                      nsBoundingMetrics &aBoundingMetrics,
03948                                      nsRenderingContextGTK *aContext)
03949 {
03950     aBoundingMetrics.Clear();
03951 
03952     if (!aString || !aLength)
03953         return NS_ERROR_FAILURE;
03954 
03955     nsresult rv = NS_OK;
03956 
03957     nsXFont *xFont = mCurrentFont->GetXFont();
03958 
03959     if (mCurrentFont->IsFreeTypeFont()) {
03960         // this function is only supposed to be called for ascii data
03961         rv = mCurrentFont->GetBoundingMetrics(
03962                            NS_ConvertASCIItoUTF16(aString, aLength).get(),
03963                            aLength, aBoundingMetrics);
03964     }
03965     else if (!mCurrentFont->GetXFontIs10646()) {
03966         // 8 bit data with an 8 bit font
03967         NS_ASSERTION(xFont->IsSingleByte(),"wrong string/font size");
03968         xFont->TextExtents8(aString, aLength,
03969                             &aBoundingMetrics.leftBearing, 
03970                             &aBoundingMetrics.rightBearing, 
03971                             &aBoundingMetrics.width, 
03972                             &aBoundingMetrics.ascent, 
03973                             &aBoundingMetrics.descent);
03974     }
03975     else {
03976         // we have 8 bit data but a 16 bit font
03977         NS_ASSERTION(!xFont->IsSingleByte(),"wrong string/font size");
03978         Widen8To16AndGetTextExtents (mCurrentFont->GetXFont(), 
03979                                      aString, aLength,
03980                                      &aBoundingMetrics.leftBearing, 
03981                                      &aBoundingMetrics.rightBearing, 
03982                                      &aBoundingMetrics.width, 
03983                                      &aBoundingMetrics.ascent, 
03984                                      &aBoundingMetrics.descent);
03985     }
03986 
03987     float P2T;
03988     P2T = mDeviceContext->DevUnitsToAppUnits();
03989 
03990     aBoundingMetrics.leftBearing =
03991         NSToCoordRound(aBoundingMetrics.leftBearing * P2T);
03992     aBoundingMetrics.rightBearing =
03993         NSToCoordRound(aBoundingMetrics.rightBearing * P2T);
03994     aBoundingMetrics.width = NSToCoordRound(aBoundingMetrics.width * P2T);
03995     aBoundingMetrics.ascent = NSToCoordRound(aBoundingMetrics.ascent * P2T);
03996     aBoundingMetrics.descent = NSToCoordRound(aBoundingMetrics.descent * P2T);
03997 
03998     return rv;
03999 }
04000 
04001 nsresult
04002 nsFontMetricsGTK::GetBoundingMetrics(const PRUnichar *aString,
04003                                      PRUint32 aLength,
04004                                      nsBoundingMetrics &aBoundingMetrics,
04005                                      PRInt32 *aFontID,
04006                                      nsRenderingContextGTK *aContext)
04007 {
04008     aBoundingMetrics.Clear(); 
04009 
04010     if (!aString || !aLength)
04011         return NS_ERROR_FAILURE;
04012 
04013     nsFontGTK* prevFont = nsnull;
04014 
04015     nsBoundingMetrics rawbm;
04016     PRBool firstTime = PR_TRUE;
04017     PRUint32 start = 0;
04018     PRUint32 i;
04019     PRUint32 extraSurrogateLength;
04020     for (i = 0; i < aLength; i+=1+extraSurrogateLength) {
04021         PRUint32 c = aString[i];
04022         extraSurrogateLength=0;
04023         if(i < aLength-1 && IS_HIGH_SURROGATE(c) && IS_LOW_SURROGATE(aString[i+1])) {
04024           // if surrogate, make UCS4 code point from high aString[i] and
04025           // low surrogate aString[i+1]
04026           c = SURROGATE_TO_UCS4(c, aString[i+1]);
04027  
04028           // skip aString[i+1], it is already used as low surrogate
04029           extraSurrogateLength = 1;
04030         }
04031         nsFontGTK* currFont = nsnull;
04032         nsFontGTK** font = mLoadedFonts;
04033         nsFontGTK** end = &mLoadedFonts[mLoadedFontsCount];
04034 
04035         while (font < end) {
04036             if (SAFE_CCMAP_HAS_CHAR_EXT((*font)->mCCMap, c)) {
04037                 currFont = *font;
04038                 goto FoundFont; // for speed -- avoid "if" statement
04039             }
04040             font++;
04041         }
04042         currFont = FindFont(c);
04043 
04044     FoundFont:
04045         // XXX avoid this test by duplicating code -- erik
04046         if (prevFont) {
04047             if (currFont != prevFont) {
04048                 prevFont->GetBoundingMetrics((const PRUnichar*)&aString[start],
04049                                              i - start, rawbm);
04050                 if (firstTime) {
04051                     firstTime = PR_FALSE;
04052                     aBoundingMetrics = rawbm;
04053                 } 
04054                 else {
04055                     aBoundingMetrics += rawbm;
04056                 }
04057                 prevFont = currFont;
04058                 start = i;
04059             }
04060         }
04061         else {
04062             prevFont = currFont;
04063             start = i;
04064         }
04065     }
04066     
04067     if (prevFont) {
04068         prevFont->GetBoundingMetrics((const PRUnichar*) &aString[start],
04069                                      i - start, rawbm);
04070         if (firstTime)
04071             aBoundingMetrics = rawbm;
04072         else
04073             aBoundingMetrics += rawbm;
04074     }
04075 
04076     // convert to app units
04077     float P2T;
04078     P2T = mDeviceContext->DevUnitsToAppUnits();
04079 
04080     aBoundingMetrics.leftBearing =
04081         NSToCoordRound(aBoundingMetrics.leftBearing * P2T);
04082     aBoundingMetrics.rightBearing =
04083         NSToCoordRound(aBoundingMetrics.rightBearing * P2T);
04084     aBoundingMetrics.width = NSToCoordRound(aBoundingMetrics.width * P2T);
04085     aBoundingMetrics.ascent = NSToCoordRound(aBoundingMetrics.ascent * P2T);
04086     aBoundingMetrics.descent = NSToCoordRound(aBoundingMetrics.descent * P2T);
04087 
04088     if (nsnull != aFontID)
04089         *aFontID = 0;
04090 
04091     return NS_OK;
04092 }
04093 
04094 #endif /* MOZ_MATHML */
04095 
04096 nsresult
04097 nsFontMetricsGTK::GetTextDimensions (const PRUnichar* aString,
04098                                      PRUint32 aLength,
04099                                      nsTextDimensions& aDimensions,
04100                                      PRInt32* aFontID,
04101                                      nsRenderingContextGTK *aContext)
04102 {
04103     aDimensions.Clear();
04104 
04105     if (!aString || !aLength)
04106         return NS_ERROR_FAILURE;
04107 
04108     nsFontGTK* prevFont = nsnull;
04109     gint rawWidth = 0, rawAscent = 0, rawDescent = 0;
04110     PRUint32 start = 0;
04111     PRUint32 i;
04112 
04113     PRUint32 extraSurrogateLength;
04114     for (i = 0; i < aLength; i+=1+extraSurrogateLength) {
04115         PRUint32 c = aString[i];
04116         extraSurrogateLength=0;
04117         if(i < aLength-1 && IS_HIGH_SURROGATE(c) && IS_LOW_SURROGATE(aString[i+1])) {
04118           // if surrogate, make UCS4 code point from high aString[i] and
04119           // low surrogate aString[i+1]
04120           c = SURROGATE_TO_UCS4(c, aString[i+1]);
04121  
04122           // skip aString[i+1], it is already used as low surrogate
04123           extraSurrogateLength = 1;
04124         }
04125         nsFontGTK* currFont = nsnull;
04126         nsFontGTK** font = mLoadedFonts;
04127         nsFontGTK** end = &mLoadedFonts[mLoadedFontsCount];
04128 
04129         while (font < end) {
04130             if (SAFE_CCMAP_HAS_CHAR_EXT((*font)->mCCMap, c)) {
04131                 currFont = *font;
04132                 goto FoundFont; // for speed -- avoid "if" statement
04133             }
04134             font++;
04135         }
04136         currFont = FindFont(c);
04137 
04138     FoundFont:
04139         // XXX avoid this test by duplicating code -- erik
04140         if (prevFont) {
04141             if (currFont != prevFont) {
04142                 rawWidth += prevFont->GetWidth(&aString[start], i - start);
04143                 if (rawAscent < prevFont->mMaxAscent)
04144                     rawAscent = prevFont->mMaxAscent;
04145                 if (rawDescent < prevFont->mMaxDescent)
04146                     rawDescent = prevFont->mMaxDescent;
04147                 prevFont = currFont;
04148                 start = i;
04149             }
04150         }
04151         else {
04152             prevFont = currFont;
04153             start = i;
04154         }
04155     }
04156 
04157     if (prevFont) {
04158         rawWidth += prevFont->GetWidth(&aString[start], i - start);
04159         if (rawAscent < prevFont->mMaxAscent)
04160             rawAscent = prevFont->mMaxAscent;
04161         if (rawDescent < prevFont->mMaxDescent)
04162             rawDescent = prevFont->mMaxDescent;
04163     }
04164 
04165     float P2T;
04166     P2T = mDeviceContext->DevUnitsToAppUnits();
04167 
04168     aDimensions.width = NSToCoordRound(rawWidth * P2T);
04169     aDimensions.ascent = NSToCoordRound(rawAscent * P2T);
04170     aDimensions.descent = NSToCoordRound(rawDescent * P2T);
04171 
04172     if (nsnull != aFontID)
04173         *aFontID = 0;
04174 
04175     return NS_OK;
04176 }
04177 
04178 nsresult
04179 nsFontMetricsGTK::GetTextDimensions (const char*         aString,
04180                                      PRInt32             aLength,
04181                                      PRInt32             aAvailWidth,
04182                                      PRInt32*            aBreaks,
04183                                      PRInt32             aNumBreaks,
04184                                      nsTextDimensions&   aDimensions,
04185                                      PRInt32&            aNumCharsFit,
04186                                      nsTextDimensions&   aLastWordDimensions,
04187                                      PRInt32*            aFontID,
04188                                      nsRenderingContextGTK *aContext)
04189 {
04190     NS_PRECONDITION(aBreaks[aNumBreaks - 1] == aLength, "invalid break array");
04191 
04192     // If we need to back up this state represents the last place
04193     // we could break. We can use this to avoid remeasuring text
04194     PRInt32 prevBreakState_BreakIndex = -1; // not known
04195                                             // (hasn't been computed)
04196     nscoord prevBreakState_Width = 0; // accumulated width to this point
04197 
04198     // Initialize OUT parameters
04199     GetMaxAscent(aLastWordDimensions.ascent);
04200     GetMaxDescent(aLastWordDimensions.descent);
04201     aLastWordDimensions.width = -1;
04202     aNumCharsFit = 0;
04203 
04204     // Iterate each character in the string and determine which font to use
04205     nscoord width = 0;
04206     PRInt32 start = 0;
04207     nscoord aveCharWidth;
04208     GetAveCharWidth(aveCharWidth);
04209 
04210     while (start < aLength) {
04211       // Estimate how many characters will fit. Do that by
04212       // diving the available space by the average character
04213       // width. Make sure the estimated number of characters is
04214       // at least 1
04215       PRInt32 estimatedNumChars = 0;
04216 
04217       if (aveCharWidth > 0)
04218         estimatedNumChars = (aAvailWidth - width) / aveCharWidth;
04219 
04220       if (estimatedNumChars < 1)
04221         estimatedNumChars = 1;
04222 
04223       // Find the nearest break offset
04224       PRInt32 estimatedBreakOffset = start + estimatedNumChars;
04225       PRInt32 breakIndex;
04226       nscoord numChars;
04227 
04228       // Find the nearest place to break that is less than or equal to
04229       // the estimated break offset
04230       if (aLength <= estimatedBreakOffset) {
04231         // All the characters should fit
04232         numChars = aLength - start;
04233         breakIndex = aNumBreaks - 1;
04234       } 
04235       else {
04236         breakIndex = prevBreakState_BreakIndex;
04237         while (((breakIndex + 1) < aNumBreaks) &&
04238                (aBreaks[breakIndex + 1] <= estimatedBreakOffset)) {
04239           ++breakIndex;
04240         }
04241 
04242         if (breakIndex == prevBreakState_BreakIndex) {
04243           ++breakIndex; // make sure we advanced past the
04244           // previous break index
04245         }
04246 
04247         numChars = aBreaks[breakIndex] - start;
04248       }
04249 
04250       // Measure the text
04251       nscoord twWidth = 0;
04252       if ((1 == numChars) && (aString[start] == ' '))
04253         GetSpaceWidth(twWidth);
04254       else if (numChars > 0)
04255         GetWidth(&aString[start], numChars, twWidth, aContext);
04256 
04257       // See if the text fits
04258       PRBool  textFits = (twWidth + width) <= aAvailWidth;
04259 
04260       // If the text fits then update the width and the number of
04261       // characters that fit
04262       if (textFits) {
04263         aNumCharsFit += numChars;
04264         width += twWidth;
04265         start += numChars;
04266 
04267         // This is a good spot to back up to if we need to so remember
04268         // this state
04269         prevBreakState_BreakIndex = breakIndex;
04270         prevBreakState_Width = width;
04271       }
04272       else {
04273         // See if we can just back up to the previous saved
04274         // state and not have to measure any text
04275         if (prevBreakState_BreakIndex > 0) {
04276           // If the previous break index is just before the
04277           // current break index then we can use it
04278           if (prevBreakState_BreakIndex == (breakIndex - 1)) {
04279             aNumCharsFit = aBreaks[prevBreakState_BreakIndex];
04280             width = prevBreakState_Width;
04281             break;
04282           }
04283         }
04284 
04285         // We can't just revert to the previous break state
04286         if (0 == breakIndex) {
04287           // There's no place to back up to, so even though
04288           // the text doesn't fit return it anyway
04289           aNumCharsFit += numChars;
04290           width += twWidth;
04291           break;
04292         }
04293 
04294         // Repeatedly back up until we get to where the text
04295         // fits or we're all the way back to the first word
04296         width += twWidth;
04297         while ((breakIndex >= 1) && (width > aAvailWidth)) {
04298           twWidth = 0;
04299           start = aBreaks[breakIndex - 1];
04300           numChars = aBreaks[breakIndex] - start;
04301 
04302           if ((1 == numChars) && (aString[start] == ' '))
04303             GetSpaceWidth(twWidth);
04304           else if (numChars > 0)
04305             GetWidth(&aString[start], numChars, twWidth,
04306                      aContext);
04307           width -= twWidth;
04308           aNumCharsFit = start;
04309           breakIndex--;
04310         }
04311         break;
04312       }
04313     }
04314 
04315     aDimensions.width = width;
04316     GetMaxAscent(aDimensions.ascent);
04317     GetMaxDescent(aDimensions.descent);
04318 
04319     return NS_OK;
04320 }
04321 
04322 struct BreakGetTextDimensionsData {
04323   float    mP2T;               // IN
04324   PRInt32  mAvailWidth;        // IN
04325   PRInt32* mBreaks;            // IN
04326   PRInt32  mNumBreaks;         // IN
04327   nscoord  mSpaceWidth;        // IN
04328   nscoord  mAveCharWidth;      // IN
04329   PRInt32  mEstimatedNumChars; // IN (running -- to handle the edge case of one word)
04330 
04331   PRInt32  mNumCharsFit;  // IN/OUT -- accumulated number of chars that fit so far
04332   nscoord  mWidth;        // IN/OUT -- accumulated width so far
04333 
04334   // If we need to back up, this state represents the last place
04335   // we could break. We can use this to avoid remeasuring text
04336   PRInt32 mPrevBreakState_BreakIndex; // IN/OUT, initialized as -1, i.e., not yet computed
04337   nscoord mPrevBreakState_Width;      // IN/OUT, initialized as  0
04338 
04339   // Remember the fonts that we use so that we can deal with
04340   // line-breaking in-between fonts later. mOffsets[0] is also used
04341   // to initialize the current offset from where to start measuring
04342   nsVoidArray* mFonts;   // IN/OUT
04343   nsVoidArray* mOffsets; // IN/OUT
04344 };
04345 
04346 static PRBool PR_CALLBACK
04347 do_BreakGetTextDimensions(const nsFontSwitchGTK *aFontSwitch,
04348                           const PRUnichar*       aSubstring,
04349                           PRUint32               aSubstringLength,
04350                           void*                  aData)
04351 {
04352   nsFontGTK* fontGTK = aFontSwitch->mFontGTK;
04353 
04354   // Make sure the font is selected
04355   BreakGetTextDimensionsData* data = (BreakGetTextDimensionsData*)aData;
04356 
04357   // Our current state relative to the _full_ string...
04358   // This allows emulation of the previous code...
04359   const PRUnichar* pstr = (const PRUnichar*)data->mOffsets->ElementAt(0);
04360   PRInt32 numCharsFit = data->mNumCharsFit;
04361   nscoord width = data->mWidth;
04362   PRInt32 start = (PRInt32)(aSubstring - pstr);
04363   PRInt32 end = start + aSubstringLength;
04364   PRBool allDone = PR_FALSE;
04365 
04366   while (start < end) {
04367     // Estimate how many characters will fit. Do that by dividing the
04368     // available space by the average character width
04369     PRInt32 estimatedNumChars = data->mEstimatedNumChars;
04370     if (!estimatedNumChars && data->mAveCharWidth > 0) {
04371       estimatedNumChars = (data->mAvailWidth - width) / data->mAveCharWidth;
04372     }
04373     // Make sure the estimated number of characters is at least 1
04374     if (estimatedNumChars < 1) {
04375       estimatedNumChars = 1;
04376     }
04377 
04378     // Find the nearest break offset
04379     PRInt32 estimatedBreakOffset = start + estimatedNumChars;
04380     PRInt32 breakIndex = -1; // not yet computed
04381     PRBool  inMiddleOfSegment = PR_FALSE;
04382     nscoord numChars;
04383 
04384     // Avoid scanning the break array in the case where we think all
04385     // the text should fit
04386     if (end <= estimatedBreakOffset) {
04387       // Everything should fit
04388       numChars = end - start;
04389     }
04390     else {
04391       // Find the nearest place to break that is less than or equal to
04392       // the estimated break offset
04393       breakIndex = data->mPrevBreakState_BreakIndex;
04394       while (breakIndex + 1 < data->mNumBreaks &&
04395              data->mBreaks[breakIndex + 1] <= estimatedBreakOffset) {
04396         ++breakIndex;
04397       }
04398 
04399       if (breakIndex == -1)
04400         breakIndex = 0;
04401 
04402       // We found a place to break that is before the estimated break
04403       // offset. Where we break depends on whether the text crosses a
04404       // segment boundary
04405       if (start < data->mBreaks[breakIndex]) {
04406         // The text crosses at least one segment boundary so measure to the
04407         // break point just before the estimated break offset
04408         numChars = PR_MIN(data->mBreaks[breakIndex], end) - start;
04409       } 
04410       else {
04411         // See whether there is another segment boundary between this one
04412         // and the end of the text
04413         if ((breakIndex < (data->mNumBreaks - 1)) && (data->mBreaks[breakIndex] < end)) {
04414           ++breakIndex;
04415           numChars = PR_MIN(data->mBreaks[breakIndex], end) - start;
04416         }
04417         else {
04418           NS_ASSERTION(end != data->mBreaks[breakIndex], "don't expect to be at segment boundary");
04419 
04420           // The text is all within the same segment
04421           numChars = end - start;
04422 
04423           // Remember we're in the middle of a segment and not between
04424           // two segments
04425           inMiddleOfSegment = PR_TRUE;
04426         }
04427       }
04428     }
04429 
04430     // Measure the text
04431     nscoord twWidth, pxWidth;
04432     if ((1 == numChars) && (pstr[start] == ' ')) {
04433       twWidth = data->mSpaceWidth;
04434     }
04435     else {
04436       pxWidth = fontGTK->GetWidth(&pstr[start], numChars);
04437       twWidth = NSToCoordRound(float(pxWidth) * data->mP2T);
04438     }
04439 
04440     // See if the text fits
04441     PRBool textFits = (twWidth + width) <= data->mAvailWidth;
04442 
04443     // If the text fits then update the width and the number of
04444     // characters that fit
04445     if (textFits) {
04446       numCharsFit += numChars;
04447       width += twWidth;
04448 
04449       // If we computed the break index and we're not in the middle
04450       // of a segment then this is a spot that we can back up to if
04451       // we need to, so remember this state
04452       if ((breakIndex != -1) && !inMiddleOfSegment) {
04453         data->mPrevBreakState_BreakIndex = breakIndex;
04454         data->mPrevBreakState_Width = width;
04455       }
04456     }
04457     else {
04458       // The text didn't fit. If we're out of room then we're all done
04459       allDone = PR_TRUE;
04460 
04461       // See if we can just back up to the previous saved state and not
04462       // have to measure any text
04463       if (data->mPrevBreakState_BreakIndex != -1) {
04464         PRBool canBackup;
04465 
04466         // If we're in the middle of a word then the break index
04467         // must be the same if we can use it. If we're at a segment
04468         // boundary, then if the saved state is for the previous
04469         // break index then we can use it
04470         if (inMiddleOfSegment) {
04471           canBackup = data->mPrevBreakState_BreakIndex == breakIndex;
04472         } else {
04473           canBackup = data->mPrevBreakState_BreakIndex == (breakIndex - 1);
04474         }
04475 
04476         if (canBackup) {
04477           numCharsFit = data->mBreaks[data->mPrevBreakState_BreakIndex];
04478           width = data->mPrevBreakState_Width;
04479           break;
04480         }
04481       }
04482 
04483       // We can't just revert to the previous break state. Find the break
04484       // index just before the end of the text
04485       end = start + numChars;
04486       breakIndex = 0;
04487       if (data->mBreaks[breakIndex] < end) {
04488         while ((breakIndex + 1 < data->mNumBreaks) && (data->mBreaks[breakIndex + 1] < end)) {
04489           ++breakIndex;
04490         }
04491       }
04492 
04493       if ((0 == breakIndex) && (end <= data->mBreaks[0])) {
04494         // There's no place to back up to, so even though the text doesn't fit
04495         // return it anyway
04496         numCharsFit += numChars;
04497         width += twWidth;
04498 
04499         // Edge case of one word: it could be that we just measured a fragment of the
04500         // first word and its remainder involves other fonts, so we want to keep going
04501         // until we at least measure the entire first word
04502         if (numCharsFit < data->mBreaks[0]) {
04503           allDone = PR_FALSE;         
04504           // From now on we don't care anymore what is the _real_ estimated
04505           // number of characters that fits. Rather, we have no where to break
04506           // and have to measure one word fully, but the real estimate is less
04507           // than that one word. However, since the other bits of code rely on
04508           // what is in "data->mEstimatedNumChars", we want to override
04509           // "data->mEstimatedNumChars" and pass in what _has_ to be measured
04510           // so that it is transparent to the other bits that depend on it.
04511           data->mEstimatedNumChars = data->mBreaks[0] - numCharsFit;
04512           start += numChars;
04513         }
04514 
04515         break;
04516       }
04517 
04518       // Repeatedly back up until we get to where the text fits or we're
04519       // all the way back to the first word
04520       width += twWidth;
04521       while ((breakIndex >= 0) && (width > data->mAvailWidth)) {
04522         start = data->mBreaks[breakIndex];
04523         numChars = end - start;
04524         numCharsFit = start;
04525         if ((1 == numChars) && (pstr[start] == ' ')) {
04526           width -= data->mSpaceWidth;
04527         }
04528         else if (pstr + start >= aSubstring) {
04529           // The entire fragment to chop is within the current font.
04530           pxWidth = fontGTK->GetWidth(&pstr[start], numChars);
04531           width -= NSToCoordRound(float(pxWidth) * data->mP2T);
04532         }
04533         else {
04534           // The fragment that we want to chop extends back into previous fonts.
04535           // We need to reverse into previous fonts. Fortunately,
04536           // data->mFonts[] and data->mOffsets[] tell us which fonts are used
04537           // and when. 
04538           end = data->mNumCharsFit; // same as aSubstring - pstr
04539           data->mNumCharsFit = numCharsFit; // has got shorter...
04540           PRInt32 k = data->mFonts->Count() - 1;
04541           for ( ; k >= 0 && start < end; --k, end -= numChars) {
04542             fontGTK = (nsFontGTK*)data->mFonts->ElementAt(k);
04543             const PRUnichar* ps = (const PRUnichar*)data->mOffsets->ElementAt(k);
04544             if (ps < pstr + start)
04545               ps = pstr + start;
04546 
04547             numChars = pstr + end - ps;
04548             NS_ASSERTION(numChars > 0, "empty string");
04549 
04550             pxWidth = fontGTK->GetWidth(ps, numChars);
04551             data->mWidth -= NSToCoordRound(float(pxWidth) * data->mP2T);
04552 
04553             // By construction, mFonts[k] is the last font, and
04554             // mOffsets[k+1] is the last offset.
04555             data->mFonts->RemoveElementAt(k);
04556             data->mOffsets->RemoveElementAt(k+1);
04557           }
04558 
04559           // We are done, update the data now because we won't do it later.
04560           // The |if (data->mNumCharsFit != numCharsFit)| won't apply below
04561           data->mFonts->AppendElement(fontGTK);
04562           data->mOffsets->AppendElement((void*)&pstr[numCharsFit]);
04563           break;
04564         }
04565 
04566         --breakIndex;
04567         end = start;
04568       }
04569     }
04570 
04571     start += numChars;
04572   }
04573 
04574 #ifdef DEBUG_rbs
04575   NS_ASSERTION(allDone || start == end, "internal error");
04576   NS_ASSERTION(allDone || data->mNumCharsFit != numCharsFit, "internal error");
04577 #endif /* DEBUG_rbs */
04578 
04579   if (data->mNumCharsFit != numCharsFit) {
04580     // some text was actually retained
04581     data->mWidth = width;
04582     data->mNumCharsFit = numCharsFit;
04583     data->mFonts->AppendElement(fontGTK);
04584     data->mOffsets->AppendElement((void*)&pstr[numCharsFit]);
04585   }
04586 
04587   if (allDone) {
04588     // stop now
04589     return PR_FALSE;
04590   }
04591 
04592   return PR_TRUE; // don't stop if we still need to measure more characters
04593 }
04594 
04595 nsresult
04596 nsFontMetricsGTK::GetTextDimensions (const PRUnichar*    aString,
04597                                      PRInt32             aLength,
04598                                      PRInt32             aAvailWidth,
04599                                      PRInt32*            aBreaks,
04600                                      PRInt32             aNumBreaks,
04601                                      nsTextDimensions&   aDimensions,
04602                                      PRInt32&            aNumCharsFit,
04603                                      nsTextDimensions&   aLastWordDimensions,
04604                                      PRInt32*            aFontID,
04605                                      nsRenderingContextGTK *aContext)
04606 {
04607 
04608     nscoord spaceWidth, aveCharWidth;
04609     GetSpaceWidth(spaceWidth);
04610     GetAveCharWidth(aveCharWidth);
04611 
04612     // Note: aBreaks[] is supplied to us so that the first word is
04613     // located at aString[0 .. aBreaks[0]-1] and more generally, the
04614     // k-th word is located at aString[aBreaks[k-1]
04615     // .. aBreaks[k]-1]. Whitespace can be included and each of them
04616     // counts as a word in its own right.
04617 
04618     // Upon completion of glyph resolution, characters that can be
04619     // represented with fonts[i] are at offsets[i] .. offsets[i+1]-1
04620 
04621     nsAutoVoidArray fonts, offsets;
04622     offsets.AppendElement((void*)aString);
04623 
04624     float f;
04625     f = mDeviceContext->DevUnitsToAppUnits();
04626     BreakGetTextDimensionsData data = { f, aAvailWidth,
04627                                         aBreaks, aNumBreaks,
04628                                         spaceWidth, aveCharWidth,
04629                                         0, 0, 0, -1, 0, &fonts, &offsets };
04630 
04631     ResolveForwards(aString, aLength, do_BreakGetTextDimensions,
04632                     &data);
04633 
04634     if (aFontID)
04635         *aFontID = 0;
04636 
04637     aNumCharsFit = data.mNumCharsFit;
04638     aDimensions.width = data.mWidth;
04639 
04641     // Post-processing for the ascent and descent:
04642     //
04643     // The width of the last word is included in the final width, but
04644     // its ascent and descent are kept aside for the moment. The
04645     // problem is that line-breaking may occur _before_ the last word,
04646     // and we don't want its ascent and descent to interfere. We can
04647     // re-measure the last word and substract its width
04648     // later. However, we need a special care for the ascent and
04649     // descent at the break-point. The idea is to keep the ascent and
04650     // descent of the last word separate, and let layout consider them
04651     // later when it has determined that line-breaking doesn't occur
04652     // before the last word.
04653     //
04654     // Therefore, there are two things to do:
04655     // 1. Determine the ascent and descent up to where line-breaking may occur.
04656     // 2. Determine the ascent and descent of the remainder.  For
04657     //   efficiency however, it is okay to bail out early if there is
04658     //   only one font (in this case, the height of the last word has no
04659     //   special effect on the total height).
04660 
04661     // aLastWordDimensions.width should be set to -1 to reply that we
04662     // don't know the width of the last word since we measure multiple
04663     // words
04664     aLastWordDimensions.Clear();
04665     aLastWordDimensions.width = -1;
04666 
04667     PRInt32 count = fonts.Count();
04668     if (!count)
04669         return NS_OK;
04670 
04671     nsFontGTK* fontGTK = (nsFontGTK*)fonts[0];
04672     NS_ASSERTION(fontGTK, "internal error in do_BreakGetTextDimensions");
04673     aDimensions.ascent = fontGTK->mMaxAscent;
04674     aDimensions.descent = fontGTK->mMaxDescent;
04675 
04676     // fast path - normal case, quick return if there is only one font
04677     if (count == 1)
04678         return NS_OK;
04679 
04680     // get the last break index.
04681     // If there is only one word, we end up with lastBreakIndex =
04682     // 0. We don't need to worry about aLastWordDimensions in this
04683     // case too. But if we didn't return earlier, it would mean that
04684     // the unique word needs several fonts and we will still have to
04685     // loop over the fonts to return the final height
04686     PRInt32 lastBreakIndex = 0;
04687     while (aBreaks[lastBreakIndex] < aNumCharsFit)
04688         ++lastBreakIndex;
04689 
04690     const PRUnichar* lastWord = (lastBreakIndex > 0) 
04691         ? aString + aBreaks[lastBreakIndex-1]
04692         : aString + aNumCharsFit; // let it point outside to play nice
04693                                   // with the loop
04694 
04695     // now get the desired ascent and descent information... this is
04696     // however a very fast loop of the order of the number of
04697     // additional fonts
04698 
04699     PRInt32 currFont = 0;
04700     const PRUnichar* pstr = aString;
04701     const PRUnichar* last = aString + aNumCharsFit;
04702 
04703     while (pstr < last) {
04704         fontGTK = (nsFontGTK*)fonts[currFont];
04705         PRUnichar* nextOffset = (PRUnichar*)offsets[++currFont]; 
04706 
04707         // For consistent word-wrapping, we are going to handle the
04708         // whitespace character with special care because a whitespace
04709         // character can come from a font different from that of the
04710         // previous word. If 'x', 'y', 'z', are Unicode points that
04711         // require different fonts, we want 'xyz <br>' and 'xyz<br>'
04712         // to have the same height because it gives a more stable
04713         // rendering, especially when the window is resized at the
04714         // edge of the word.
04715         // If we don't do this, a 'tall' trailing whitespace, i.e., if
04716         // the whitespace happens to come from a font with a bigger
04717         // ascent and/or descent than all current fonts on the line,
04718         // this can cause the next lines to be shifted down when the
04719         // window is slowly resized to fit that whitespace.
04720         if (*pstr == ' ') {
04721             // skip pass the whitespace to ignore the height that it
04722             // may contribute
04723             ++pstr;
04724             // get out if we reached the end
04725             if (pstr == last) {
04726                 break;
04727             }
04728             // switch to the next font if we just passed the current font 
04729             if (pstr == nextOffset) {
04730                 fontGTK = (nsFontGTK*)fonts[currFont];
04731                 nextOffset = (PRUnichar*)offsets[++currFont];
04732             } 
04733         }
04734 
04735         // see if the last word intersects with the current font
04736         // (we are testing for 'nextOffset-1 >= lastWord' since the
04737         // current font ends at nextOffset-1)
04738         if (nextOffset > lastWord) {
04739             if (aLastWordDimensions.ascent < fontGTK->mMaxAscent) {
04740                 aLastWordDimensions.ascent = fontGTK->mMaxAscent;
04741             }
04742             if (aLastWordDimensions.descent < fontGTK->mMaxDescent) {
04743                 aLastWordDimensions.descent = fontGTK->mMaxDescent;
04744             }
04745         }
04746 
04747         // see if we have not reached the last word yet
04748         if (pstr < lastWord) {
04749             if (aDimensions.ascent < fontGTK->mMaxAscent) {
04750                 aDimensions.ascent = fontGTK->mMaxAscent;
04751             }
04752             if (aDimensions.descent < fontGTK->mMaxDescent) {
04753                 aDimensions.descent = fontGTK->mMaxDescent;
04754             }
04755         }
04756 
04757         // advance to where the next font starts
04758         pstr = nextOffset;
04759     }
04760 
04761     return NS_OK;
04762 }
04763 
04764 GdkFont *
04765 nsFontMetricsGTK::GetCurrentGDKFont(void)
04766 {
04767   return mCurrentFont->GetGDKFont();
04768 }
04769 
04770 nsresult
04771 nsFontMetricsGTK::SetRightToLeftText(PRBool aIsRTL)
04772 {
04773     return NS_OK;
04774 }
04775 
04776 PRBool
04777 nsFontMetricsGTK::GetRightToLeftText()
04778 {
04779     return PR_FALSE;
04780 }
04781 
04782 nsresult
04783 nsFontMetricsGTK::GetClusterInfo(const PRUnichar *aText,
04784                                  PRUint32 aLength,
04785                                  PRUint8 *aClusterStarts)
04786 {
04787     return NS_ERROR_NOT_IMPLEMENTED;
04788 }
04789 
04790 PRInt32
04791 nsFontMetricsGTK::GetPosition(const PRUnichar *aText,
04792                               PRUint32 aLength,
04793                               nsPoint aPt)
04794 {
04795     return -1;
04796 }
04797 
04798 
04799 nsresult
04800 nsFontMetricsGTK::GetRangeWidth(const PRUnichar *aText,
04801                                 PRUint32 aLength,
04802                                 PRUint32 aStart,
04803                                 PRUint32 aEnd,
04804                                 PRUint32 &aWidth)
04805 {
04806     return NS_ERROR_NOT_IMPLEMENTED;
04807 }
04808 
04809 nsresult
04810 nsFontMetricsGTK::GetRangeWidth(const char *aText,
04811                                 PRUint32 aLength,
04812                                 PRUint32 aStart,
04813                                 PRUint32 aEnd,
04814                                 PRUint32 &aWidth)
04815 {
04816     return NS_ERROR_NOT_IMPLEMENTED;
04817 }
04818 
04819 PR_BEGIN_EXTERN_C
04820 static int
04821 CompareSizes(const void* aArg1, const void* aArg2, void *data)
04822 {
04823   return (*((nsFontGTK**) aArg1))->mSize - (*((nsFontGTK**) aArg2))->mSize;
04824 }
04825 PR_END_EXTERN_C
04826 
04827 void
04828 nsFontStretch::SortSizes(void)
04829 {
04830   NS_QuickSort(mSizes, mSizesCount, sizeof(*mSizes), CompareSizes, NULL);
04831 }
04832 
04833 void
04834 nsFontWeight::FillStretchHoles(void)
04835 {
04836   int i, j;
04837 
04838   for (i = 0; i < 9; i++) {
04839     if (mStretches[i]) {
04840       mStretches[i]->SortSizes();
04841     }
04842   }
04843 
04844   if (!mStretches[4]) {
04845     for (i = 5; i < 9; i++) {
04846       if (mStretches[i]) {
04847         mStretches[4] = mStretches[i];
04848         break;
04849       }
04850     }
04851     if (!mStretches[4]) {
04852       for (i = 3; i >= 0; i--) {
04853         if (mStretches[i]) {
04854           mStretches[4] = mStretches[i];
04855           break;
04856         }
04857       }
04858     }
04859   }
04860 
04861   for (i = 5; i < 9; i++) {
04862     if (!mStretches[i]) {
04863       for (j = i + 1; j < 9; j++) {
04864         if (mStretches[j]) {
04865           mStretches[i] = mStretches[j];
04866           break;
04867         }
04868       }
04869       if (!mStretches[i]) {
04870         for (j = i - 1; j >= 0; j--) {
04871           if (mStretches[j]) {
04872             mStretches[i] = mStretches[j];
04873             break;
04874           }
04875         }
04876       }
04877     }
04878   }
04879   for (i = 3; i >= 0; i--) {
04880     if (!mStretches[i]) {
04881       for (j = i - 1; j >= 0; j--) {
04882         if (mStretches[j]) {
04883           mStretches[i] = mStretches[j];
04884           break;
04885         }
04886       }
04887       if (!mStretches[i]) {
04888         for (j = i + 1; j < 9; j++) {
04889           if (mStretches[j]) {
04890             mStretches[i] = mStretches[j];
04891             break;
04892           }
04893         }
04894       }
04895     }
04896   }
04897 }
04898 
04899 void
04900 nsFontStyle::FillWeightHoles(void)
04901 {
04902   int i, j;
04903 
04904   for (i = 0; i < 9; i++) {
04905     if (mWeights[i]) {
04906       mWeights[i]->FillStretchHoles();
04907     }
04908   }
04909 
04910   if (!mWeights[3]) {
04911     for (i = 4; i < 9; i++) {
04912       if (mWeights[i]) {
04913         mWeights[3] = mWeights[i];
04914         break;
04915       }
04916     }
04917     if (!mWeights[3]) {
04918       for (i = 2; i >= 0; i--) {
04919         if (mWeights[i]) {
04920           mWeights[3] = mWeights[i];
04921           break;
04922         }
04923       }
04924     }
04925   }
04926 
04927   // CSS2, section 15.5.1
04928   if (!mWeights[4]) {
04929     mWeights[4] = mWeights[3];
04930   }
04931   for (i = 5; i < 9; i++) {
04932     if (!mWeights[i]) {
04933       for (j = i + 1; j < 9; j++) {
04934         if (mWeights[j]) {
04935           mWeights[i] = mWeights[j];
04936           break;
04937         }
04938       }
04939       if (!mWeights[i]) {
04940         for (j = i - 1; j >= 0; j--) {
04941           if (mWeights[j]) {
04942             mWeights[i] = mWeights[j];
04943             break;
04944           }
04945         }
04946       }
04947     }
04948   }
04949   for (i = 2; i >= 0; i--) {
04950     if (!mWeights[i]) {
04951       for (j = i - 1; j >= 0; j--) {
04952         if (mWeights[j]) {
04953           mWeights[i] = mWeights[j];
04954           break;
04955         }
04956       }
04957       if (!mWeights[i]) {
04958         for (j = i + 1; j < 9; j++) {
04959           if (mWeights[j]) {
04960             mWeights[i] = mWeights[j];
04961             break;
04962           }
04963         }
04964       }
04965     }
04966   }
04967 }
04968 
04969 void
04970 nsFontNode::FillStyleHoles(void)
04971 {
04972   if (mHolesFilled) {
04973     return;
04974   }
04975   mHolesFilled = 1;
04976 
04977 #ifdef DEBUG_DUMP_TREE
04978   DumpFamily(this);
04979 #endif
04980 
04981   for (int i = 0; i < 3; i++) {
04982     if (mStyles[i]) {
04983       mStyles[i]->FillWeightHoles();
04984     }
04985   }
04986 
04987   // XXX If both italic and oblique exist, there is probably something
04988   // wrong. Try counting the fonts, and removing the one that has less.
04989   if (!mStyles[NS_FONT_STYLE_NORMAL]) {
04990     if (mStyles[NS_FONT_STYLE_ITALIC]) {
04991       mStyles[NS_FONT_STYLE_NORMAL] = mStyles[NS_FONT_STYLE_ITALIC];
04992     }
04993     else {
04994       mStyles[NS_FONT_STYLE_NORMAL] = mStyles[NS_FONT_STYLE_OBLIQUE];
04995     }
04996   }
04997   if (!mStyles[NS_FONT_STYLE_ITALIC]) {
04998     if (mStyles[NS_FONT_STYLE_OBLIQUE]) {
04999       mStyles[NS_FONT_STYLE_ITALIC] = mStyles[NS_FONT_STYLE_OBLIQUE];
05000     }
05001     else {
05002       mStyles[NS_FONT_STYLE_ITALIC] = mStyles[NS_FONT_STYLE_NORMAL];
05003     }
05004   }
05005   if (!mStyles[NS_FONT_STYLE_OBLIQUE]) {
05006     if (mStyles[NS_FONT_STYLE_ITALIC]) {
05007       mStyles[NS_FONT_STYLE_OBLIQUE] = mStyles[NS_FONT_STYLE_ITALIC];
05008     }
05009     else {
05010       mStyles[NS_FONT_STYLE_OBLIQUE] = mStyles[NS_FONT_STYLE_NORMAL];
05011     }
05012   }
05013 
05014 #ifdef DEBUG_DUMP_TREE
05015   DumpFamily(this);
05016 #endif
05017 }
05018 
05019 static void
05020 SetCharsetLangGroup(nsFontCharSetInfo* aCharSetInfo)
05021 {
05022   if (!aCharSetInfo->mCharSet || aCharSetInfo->mLangGroup)
05023     return;
05024 
05025   nsresult res;
05026   
05027   res = gCharSetManager->GetCharsetLangGroupRaw(aCharSetInfo->mCharSet,
05028                                                 &aCharSetInfo->mLangGroup);
05029   if (NS_FAILED(res)) {
05030     aCharSetInfo->mLangGroup = NS_NewAtom("");
05031 #ifdef NOISY_FONTS
05032     printf("=== cannot get lang group for %s\n", aCharSetInfo->mCharSet);
05033 #endif
05034   }
05035 }
05036 
05037 #define GET_WEIGHT_INDEX(index, weight) \
05038   do {                                  \
05039     (index) = WEIGHT_INDEX(weight);     \
05040     if ((index) < 0) {                  \
05041       (index) = 0;                      \
05042     }                                   \
05043     else if ((index) > 8) {             \
05044       (index) = 8;                      \
05045     }                                   \
05046   } while (0)
05047 
05048 nsFontGTK*
05049 nsFontMetricsGTK::SearchNode(nsFontNode* aNode, PRUint32 aChar)
05050 {
05051   if (aNode->mDummy) {
05052     return nsnull;
05053   }
05054 
05055   nsFontCharSetInfo* charSetInfo = aNode->mCharSetInfo;
05056 
05057   /*
05058    * mCharSet is set if we know which glyphs will be found in these fonts.
05059    * If mCCMap has already been created for this charset, we compare it with
05060    * the mCCMaps of the previously loaded fonts. If it is the same as any of
05061    * the previous ones, we return nsnull because there is no point in
05062    * loading a font with the same map.
05063    */
05064   if (charSetInfo->mCharSet) {
05065     // if not BMP char, ignore charSetInfo->mCCMap checking
05066     // because the exact ccmap is never created before loading
05067     // NEED TO FIX: need better way
05068     if (!IS_IN_BMP(aChar) ) {
05069       goto check_done;
05070     }
05071     PRUint16* ccmap = charSetInfo->mCCMap;
05072     if (ccmap) {
05073       for (int i = 0; i < mLoadedFontsCount; i++) {
05074         if (mLoadedFonts[i]->mCCMap == ccmap) {
05075           return nsnull;
05076         }
05077       }
05078     }
05079     else {
05080       if (!SetUpFontCharSetInfo(charSetInfo))
05081         return nsnull;
05082     }
05083   }
05084   else {
05085     if ((!mIsUserDefined) && (charSetInfo == &Unknown)) {
05086       return nsnull;
05087     }
05088   }
05089 
05090 check_done:
05091 
05092   aNode->FillStyleHoles();
05093   nsFontStyle* style = aNode->mStyles[mStyleIndex];
05094 
05095   nsFontWeight** weights = style->mWeights;
05096   int weight = mFont.weight;
05097   int steps = (weight % 100);
05098   int weightIndex;
05099   if (steps) {
05100     if (steps < 10) {
05101       int base = (weight - steps);
05102       GET_WEIGHT_INDEX(weightIndex, base);
05103       while (steps--) {
05104         nsFontWeight* prev = weights[weightIndex];
05105         for (weightIndex++; weightIndex < 9; weightIndex++) {
05106           if (weights[weightIndex] != prev) {
05107             break;
05108           }
05109         }
05110         if (weightIndex >= 9) {
05111           weightIndex = 8;
05112         }
05113       }
05114     }
05115     else if (steps > 90) {
05116       steps = (100 - steps);
05117       int base = (weight + steps);
05118       GET_WEIGHT_INDEX(weightIndex, base);
05119       while (steps--) {
05120         nsFontWeight* prev = weights[weightIndex];
05121         for (weightIndex--; weightIndex >= 0; weightIndex--) {
05122           if (weights[weightIndex] != prev) {
05123             break;
05124           }
05125         }
05126         if (weightIndex < 0) {
05127           weightIndex = 0;
05128         }
05129       }
05130     }
05131     else {
05132       GET_WEIGHT_INDEX(weightIndex, weight);
05133     }
05134   }
05135   else {
05136     GET_WEIGHT_INDEX(weightIndex, weight);
05137   }
05138 
05139   FIND_FONT_PRINTF(("        load font %s", aNode->mName.get()));
05140   return PickASizeAndLoad(weights[weightIndex]->mStretches[mStretchIndex],
05141     charSetInfo, aChar, aNode->mName.get());
05142 }
05143 
05144 static void 
05145 SetFontLangGroupInfo(nsFontCharSetMap* aCharSetMap)
05146 {
05147   nsFontLangGroup *fontLangGroup = aCharSetMap->mFontLangGroup;
05148   if (!fontLangGroup)
05149     return;
05150 
05151   // get the atom for mFontLangGroup->mFontLangGroupName so we can
05152   // apply fontLangGroup operations to it
05153   // eg: search for related groups, check for scaling prefs
05154   const char *langGroup = fontLangGroup->mFontLangGroupName;
05155 
05156   // the font.scale prefs don't make sense without a langGroup
05157   // XXX FIX!!!  if langGroup is "", we need to bypass the font.scale stuff
05158   if (!langGroup)
05159     langGroup = "";
05160   if (!fontLangGroup->mFontLangGroupAtom) {
05161     fontLangGroup->mFontLangGroupAtom = NS_NewAtom(langGroup);
05162   }
05163 
05164   // hack : map 'x-zh-TWHK' to 'zh-TW' when retrieving font scaling-control
05165   // preferences via |Get*Pref|.
05166   // XXX : This would make the font scaling controls for 
05167   // zh-HK NOT work if a font common to zh-TW and zh-HK (e.g. big5-0) 
05168   // were chosen for zh-HK. An alternative would be to make it 
05169   // locale-dependent. Even with that, it'd work only under zh-HK locale.
05170   if (fontLangGroup->mFontLangGroupAtom == gZHTWHK) {
05171     langGroup = "zh-TW";  
05172   }
05173 
05174   // get the font scaling controls
05175   nsFontCharSetInfo *charSetInfo = aCharSetMap->mInfo;
05176   if (!charSetInfo->mInitedSizeInfo) {
05177     charSetInfo->mInitedSizeInfo = PR_TRUE;
05178 
05179     nsCAutoString name;
05180     nsresult rv;
05181     name.Assign("font.scale.outline.min.");
05182     name.Append(langGroup);
05183     rv = gPref->GetIntPref(name.get(), &charSetInfo->mOutlineScaleMin);
05184     if (NS_SUCCEEDED(rv))
05185       SIZE_FONT_PRINTF(("%s = %d", name.get(), charSetInfo->mOutlineScaleMin));
05186     else
05187       charSetInfo->mOutlineScaleMin = gOutlineScaleMinimum;
05188 
05189     name.Assign("font.scale.aa_bitmap.min.");
05190     name.Append(langGroup);
05191     rv = gPref->GetIntPref(name.get(), &charSetInfo->mAABitmapScaleMin);
05192     if (NS_SUCCEEDED(rv))
05193       SIZE_FONT_PRINTF(("%s = %d", name.get(), charSetInfo->mAABitmapScaleMin));
05194     else
05195       charSetInfo->mAABitmapScaleMin = gAABitmapScaleMinimum;
05196 
05197     name.Assign("font.scale.bitmap.min.");
05198     name.Append(langGroup);
05199     rv = gPref->GetIntPref(name.get(), &charSetInfo->mBitmapScaleMin);
05200     if (NS_SUCCEEDED(rv))
05201       SIZE_FONT_PRINTF(("%s = %d", name.get(), charSetInfo->mBitmapScaleMin));
05202     else
05203       charSetInfo->mBitmapScaleMin = gBitmapScaleMinimum;
05204 
05205     name.Assign("font.scale.aa_bitmap.oversize.");
05206     name.Append(langGroup);
05207     PRInt32 percent = 0;
05208     rv = gPref->GetIntPref(name.get(), &percent);
05209     if (NS_SUCCEEDED(rv)) {
05210       charSetInfo->mAABitmapOversize = percent/100.0;
05211       SIZE_FONT_PRINTF(("%s = %g", name.get(), charSetInfo->mAABitmapOversize));
05212     }
05213     else
05214       charSetInfo->mAABitmapOversize = gAABitmapOversize;
05215 
05216     percent = 0;
05217     name.Assign("font.scale.aa_bitmap.undersize.");
05218     name.Append(langGroup);
05219     rv = gPref->GetIntPref(name.get(), &percent);
05220     if (NS_SUCCEEDED(rv)) {
05221       charSetInfo->mAABitmapUndersize = percent/100.0;
05222       SIZE_FONT_PRINTF(("%s = %g", name.get(),charSetInfo->mAABitmapUndersize));
05223     }
05224     else
05225       charSetInfo->mAABitmapUndersize = gAABitmapUndersize;
05226 
05227     PRBool val = PR_TRUE;
05228     name.Assign("font.scale.aa_bitmap.always.");
05229     name.Append(langGroup);
05230     rv = gPref->GetBoolPref(name.get(), &val);
05231     if (NS_SUCCEEDED(rv)) {
05232       charSetInfo->mAABitmapScaleAlways = val;
05233       SIZE_FONT_PRINTF(("%s = %d", name.get(),charSetInfo->mAABitmapScaleAlways));
05234     }
05235     else
05236       charSetInfo->mAABitmapScaleAlways = gAABitmapScaleAlways;
05237 
05238     percent = 0;
05239     name.Assign("font.scale.bitmap.oversize.");
05240     name.Append(langGroup);
05241     rv = gPref->GetIntPref(name.get(), &percent);
05242     if (NS_SUCCEEDED(rv)) {
05243       charSetInfo->mBitmapOversize = percent/100.0;
05244       SIZE_FONT_PRINTF(("%s = %g", name.get(), charSetInfo->mBitmapOversize));
05245     }
05246     else
05247       charSetInfo->mBitmapOversize = gBitmapOversize;
05248 
05249     percent = 0;
05250     name.Assign("font.scale.bitmap.undersize.");
05251     name.Append(langGroup);
05252     rv = gPref->GetIntPref(name.get(), &percent);
05253     if (NS_SUCCEEDED(rv)) {
05254       charSetInfo->mBitmapUndersize = percent/100.0;
05255       SIZE_FONT_PRINTF(("%s = %g", name.get(), charSetInfo->mBitmapUndersize));
05256     }
05257     else
05258       charSetInfo->mBitmapUndersize = gBitmapUndersize;
05259   }
05260 }
05261 
05262 static nsFontStyle*
05263 NodeGetStyle(nsFontNode* aNode, int aStyleIndex)
05264 {
05265   nsFontStyle* style = aNode->mStyles[aStyleIndex];
05266   if (!style) {
05267     style = new nsFontStyle;
05268     if (!style) {
05269       return nsnull;
05270     }
05271     aNode->mStyles[aStyleIndex] = style;
05272   }
05273   return style;
05274 }
05275 
05276 static nsFontWeight*
05277 NodeGetWeight(nsFontStyle* aStyle, int aWeightIndex)
05278 {
05279   nsFontWeight* weight = aStyle->mWeights[aWeightIndex];
05280   if (!weight) {
05281     weight = new nsFontWeight;
05282     if (!weight) {
05283       return nsnull;
05284     }
05285     aStyle->mWeights[aWeightIndex] = weight;
05286   }
05287   return weight;
05288 }
05289 
05290 static nsFontStretch* 
05291 NodeGetStretch(nsFontWeight* aWeight, int aStretchIndex)
05292 {
05293   nsFontStretch* stretch = aWeight->mStretches[aStretchIndex];
05294   if (!stretch) {
05295     stretch = new nsFontStretch;
05296     if (!stretch) {
05297       return nsnull;
05298     }
05299     aWeight->mStretches[aStretchIndex] = stretch;
05300   }
05301   return stretch;
05302 }
05303 
05304 static PRBool
05305 NodeAddScalable(nsFontStretch* aStretch, PRBool aOutlineScaled, 
05306                 const char *aDashFoundry, const char *aFamily, 
05307                 const char *aWeight,      const char * aSlant, 
05308                 const char *aWidth,       const char *aStyle, 
05309                 const char *aSpacing,     const char *aCharSet)
05310 {
05311   // if we have both an outline scaled font and a bitmap 
05312   // scaled font pick the outline scaled font
05313   if ((aStretch->mScalable) && (!aStretch->mOutlineScaled) 
05314       && (aOutlineScaled)) {
05315     PR_smprintf_free(aStretch->mScalable);
05316     aStretch->mScalable = nsnull;
05317   }
05318   if (!aStretch->mScalable) {
05319     aStretch->mOutlineScaled = aOutlineScaled;
05320     if (aOutlineScaled) {
05321       aStretch->mScalable = 
05322           PR_smprintf("%s-%s-%s-%s-%s-%s-%%d-*-0-0-%s-*-%s", 
05323           aDashFoundry, aFamily, aWeight, aSlant, aWidth, aStyle, 
05324           aSpacing, aCharSet);
05325       if (!aStretch->mScalable)
05326         return PR_FALSE;
05327     }
05328     else {
05329       aStretch->mScalable = 
05330           PR_smprintf("%s-%s-%s-%s-%s-%s-%%d-*-*-*-%s-*-%s", 
05331           aDashFoundry, aFamily, aWeight, aSlant, aWidth, aStyle, 
05332           aSpacing, aCharSet);
05333       if (!aStretch->mScalable)
05334         return PR_FALSE;
05335     }
05336   }
05337   return PR_TRUE;
05338 }
05339 
05340 static PRBool
05341 NodeAddSize(nsFontStretch* aStretch, 
05342             int aPixelSize, int aPointSize,
05343             float scaler,
05344             int aResX,      int aResY,
05345             const char *aDashFoundry, const char *aFamily, 
05346             const char *aWeight,      const char * aSlant, 
05347             const char *aWidth,       const char *aStyle, 
05348             const char *aSpacing,     const char *aCharSet,
05349             nsFontCharSetInfo* aCharSetInfo)
05350 {
05351   if (scaler!=1.0f)
05352   {
05353     aPixelSize = int(float(aPixelSize) * scaler);
05354     aPointSize = int(float(aPointSize) * scaler);
05355     aResX = 0;
05356     aResY = 0;
05357   }
05358 
05359   PRBool haveSize = PR_FALSE;
05360   if (aStretch->mSizesCount) {
05361     nsFontGTK** end = &aStretch->mSizes[aStretch->mSizesCount];
05362     nsFontGTK** s;
05363     for (s = aStretch->mSizes; s < end; s++) {
05364       if ((*s)->mSize == aPixelSize) {
05365         haveSize = PR_TRUE;
05366         break;
05367       }
05368     }
05369   }
05370   if (!haveSize) {
05371     if (aStretch->mSizesCount == aStretch->mSizesAlloc) {
05372       int newSize = 2 * (aStretch->mSizesAlloc ? aStretch->mSizesAlloc : 1);
05373       nsFontGTK** newSizes = new nsFontGTK*[newSize];
05374       if (!newSizes)
05375         return PR_FALSE;
05376       for (int j = aStretch->mSizesAlloc - 1; j >= 0; j--) {
05377         newSizes[j] = aStretch->mSizes[j];
05378       }
05379       aStretch->mSizesAlloc = newSize;
05380       delete [] aStretch->mSizes;
05381       aStretch->mSizes = newSizes;
05382     }
05383     char *name = PR_smprintf("%s-%s-%s-%s-%s-%s-%d-%d-%d-%d-%s-*-%s", 
05384                              aDashFoundry, aFamily, aWeight, aSlant, aWidth, aStyle, 
05385                              aPixelSize, aPointSize, aResX, aResY, aSpacing, aCharSet);  
05386 
05387     if (!name) {
05388       return PR_FALSE;
05389     }
05390     nsFontGTK* size = new nsFontGTKNormal();
05391     if (!size) {
05392       return PR_FALSE;
05393     }
05394     aStretch->mSizes[aStretch->mSizesCount++] = size;
05395     size->mName           = name;
05396     // size->mFont is initialized in the constructor
05397     size->mSize           = aPixelSize;
05398     size->mBaselineAdjust = 0;
05399     size->mCCMap          = nsnull;
05400     size->mCharSetInfo    = aCharSetInfo;
05401   }
05402   return PR_TRUE;
05403 }
05404 
05405 static void
05406 GetFontNames(const char* aPattern, PRBool aAnyFoundry, PRBool aOnlyOutlineScaledFonts, nsFontNodeArray* aNodes)
05407 {
05408 #ifdef NS_FONT_DEBUG_CALL_TRACE
05409   if (gFontDebug & NS_FONT_DEBUG_CALL_TRACE) {
05410     printf("GetFontNames %s\n", aPattern);
05411   }
05412 #endif
05413 
05414 #ifdef MOZ_ENABLE_FREETYPE2
05415   // get FreeType fonts
05416   nsFT2FontNode::GetFontNames(aPattern, aNodes);
05417 #endif
05418 
05419   nsCAutoString previousNodeName;
05420   nsHashtable* node_hash;
05421   if (aAnyFoundry) {
05422     NS_ASSERTION(aPattern[1] == '*', "invalid 'anyFoundry' pattern");
05423     node_hash = gAFRENodes;
05424   }
05425   else {
05426     node_hash = gFFRENodes;
05427   }
05428   
05429 #ifdef ENABLE_X_FONT_BANNING
05430   int  screen_xres,
05431        screen_yres;
05432   /* Get Xserver DPI. 
05433    * We cannot use Mozilla's API here because it may "override" the DPI
05434    * got from the Xserver via prefs. But we want to filter ("ban") fonts
05435    * we get from the Xserver which _it_(=Xserver) has "choosen" for us
05436    * using its DPI value ...
05437    */
05438   screen_xres = int((float(::gdk_screen_width())  / (float(::gdk_screen_width_mm())  / 25.4f)) + 0.5f);
05439   screen_yres = int((float(::gdk_screen_height()) / (float(::gdk_screen_height_mm()) / 25.4f)) + 0.5f);
05440 #endif /* ENABLE_X_FONT_BANNING */
05441       
05442   BANNED_FONT_PRINTF(("Loading font '%s'", aPattern));
05443   /*
05444    * We do not use XListFontsWithInfo here, because it is very expensive.
05445    * Instead, we get that info at the time when we actually load the font.
05446    */
05447   int count;
05448   char** list = ::XListFonts(GDK_DISPLAY(), aPattern, INT_MAX, &count);
05449   if ((!list) || (count < 1)) {
05450     return;
05451   }
05452   for (int i = 0; i < count; i++) {
05453     char name[256]; /* X11 font names are never larger than 255 chars */
05454     strcpy(name, list[i]);
05455  
05456     /* Check if we can handle the font name ('*' and '?' are only valid in
05457      * input patterns passed as argument to |XListFont()|&co. but _not_ in
05458      * font names returned by these functions (see bug 136743 ("xlib complains
05459      * a lot about fonts with '*' in the XLFD string"))) */
05460     if ((!name) || (name[0] != '-') || (PL_strpbrk(name, "*?") != nsnull)) {
05461       continue;
05462     }
05463     
05464     char buf[512];
05465     PL_strncpyz(buf, name, sizeof(buf));
05466     char *fName = buf;
05467     char* p = name + 1;
05468     int scalable = 0;
05469     PRBool outline_scaled = PR_FALSE;
05470     int    resX = -1,
05471            resY = -1;
05472 
05473 #ifdef FIND_FIELD
05474 #undef FIND_FIELD
05475 #endif
05476 #define FIND_FIELD(var)           \
05477   char* var = p;                  \
05478   while ((*p) && ((*p) != '-')) { \
05479     p++;                          \
05480   }                               \
05481   if (*p) {                       \
05482     *p++ = 0;                     \
05483   }                               \
05484   else {                          \
05485     continue;                     \
05486   }
05487 
05488 #ifdef SKIP_FIELD
05489 #undef SKIP_FIELD
05490 #endif
05491 #define SKIP_FIELD(var)           \
05492   while ((*p) && ((*p) != '-')) { \
05493     p++;                          \
05494   }                               \
05495   if (*p) {                       \
05496     p++;                          \
05497   }                               \
05498   else {                          \
05499     continue;                     \
05500   }
05501 
05502     FIND_FIELD(foundry);
05503     // XXX What to do about the many Applix fonts that start with "ax"?
05504     FIND_FIELD(familyName);
05505     FIND_FIELD(weightName);
05506     FIND_FIELD(slant);
05507     FIND_FIELD(setWidth);
05508     FIND_FIELD(addStyle);
05509     FIND_FIELD(pixelSize);
05510     if (pixelSize[0] == '0') {
05511       scalable = 1;
05512     }
05513     FIND_FIELD(pointSize);
05514     if (pointSize[0] == '0') {
05515       scalable = 1;
05516     }
05517     FIND_FIELD(resolutionX);
05518     resX = atoi(resolutionX);
05519     NS_ASSERTION(!(resolutionX[0] != '0' && resX == 0), "atoi(resolutionX) failure.");
05520     if (resolutionX[0] == '0') {
05521       scalable = 1;
05522     }
05523     FIND_FIELD(resolutionY);
05524     resY = atoi(resolutionY);
05525     NS_ASSERTION(!(resolutionY[0] != '0' && resY == 0), "atoi(resolutionY) failure.");
05526     if (resolutionY[0] == '0') {
05527       scalable = 1;
05528     }
05529     // check if bitmap non-scaled font
05530     if ((pixelSize[0] != '0') || (pointSize[0] != '0')) {
05531       SCALED_FONT_PRINTF(("bitmap (non-scaled) font: %s", fName));
05532     }
05533     // check if bitmap scaled font
05534     else if ((pixelSize[0] == '0') && (pointSize[0] == '0')
05535           && (resolutionX[0] != '0') && (resolutionY[0] != '0')) {
05536       SCALED_FONT_PRINTF(("bitmap scaled font: %s", fName));
05537     }
05538     // check if outline scaled font
05539     else if ((pixelSize[0] == '0') && (pointSize[0] == '0')
05540           && (resolutionX[0] == '0') && (resolutionY[0] == '0')) {
05541       outline_scaled = PR_TRUE;
05542       SCALED_FONT_PRINTF(("outline scaled font: %s", fName));
05543     }
05544     else {
05545       SCALED_FONT_PRINTF(("unexpected font values: %s", fName));
05546       SCALED_FONT_PRINTF(("      pixelSize[0] = %c", pixelSize[0]));
05547       SCALED_FONT_PRINTF(("      pointSize[0] = %c", pointSize[0]));
05548       SCALED_FONT_PRINTF(("    resolutionX[0] = %c", resolutionX[0]));
05549       SCALED_FONT_PRINTF(("    resolutionY[0] = %c", resolutionY[0]));
05550       static PRBool already_complained = PR_FALSE;
05551       // only complaing once 
05552       if (!already_complained) {
05553         already_complained = PR_TRUE;
05554         NS_ASSERTION(pixelSize[0] == '0', "font scaler type test failed");
05555         NS_ASSERTION(pointSize[0] == '0', "font scaler type test failed");
05556         NS_ASSERTION(resolutionX[0] == '0', "font scaler type test failed");
05557         NS_ASSERTION(resolutionY[0] == '0', "font scaler type test failed");
05558       }
05559     }
05560     FIND_FIELD(spacing);
05561     FIND_FIELD(averageWidth);
05562     if (averageWidth[0] == '0') {
05563       scalable = 1;
05564 /* Workaround for bug 103159 ("sorting fonts by foundry names cause font
05565  * size of css ignored in some cases").
05566  * Hardcoded font ban until bug 104075 ("need X font banning") has been
05567  * implemented. See http://bugzilla.mozilla.org/show_bug.cgi?id=94327#c34
05568  * for additional comments...
05569  */      
05570 #ifndef DISABLE_WORKAROUND_FOR_BUG_103159
05571       // skip 'mysterious' and 'spurious' cases like
05572       // -adobe-times-medium-r-normal--17-120-100-100-p-0-iso8859-9
05573       if ((pixelSize[0] != '0' || pointSize[0] != 0) && 
05574           (outline_scaled == PR_FALSE)) {
05575         PR_LOG(FontMetricsGTKLM, PR_LOG_DEBUG, ("rejecting font '%s' (via hardcoded workaround for bug 103159)\n", list[i]));
05576         BANNED_FONT_PRINTF(("rejecting font '%s' (via hardcoded workaround for bug 103159)", list[i]));          
05577         continue;
05578       }  
05579 #endif /* DISABLE_WORKAROUND_FOR_BUG_103159 */
05580     }
05581     char* charSetName = p; // CHARSET_REGISTRY & CHARSET_ENCODING
05582     if (!*charSetName) {
05583       continue;
05584     }
05585     
05586     if (aOnlyOutlineScaledFonts && (outline_scaled == PR_FALSE)) {
05587       continue;
05588     }
05589 
05590 #ifdef ENABLE_X_FONT_BANNING
05591 #define BOOL2STR(b) ((b)?("true"):("false"))    
05592     if (gFontRejectRegEx || gFontAcceptRegEx) {
05593       char fmatchbuf[512]; /* See sprintf() below. */
05594            
05595       sprintf(fmatchbuf, "fname=%s;scalable=%s;outline_scaled=%s;xdisplay=%s;xdpy=%d;ydpy=%d;xdevice=%s",
05596               list[i], /* full font name */
05597               BOOL2STR(scalable), 
05598               BOOL2STR(outline_scaled),
05599               XDisplayString(GDK_DISPLAY()),
05600               screen_xres,
05601               screen_yres,
05602               "display" /* Xlib gfx supports other devices like "printer", too - DO NOT REMOVE! */
05603               );
05604 #undef BOOL2STR
05605                   
05606       if (gFontRejectRegEx) {
05607         /* reject font if reject pattern matches it... */        
05608         if (regexec(gFontRejectRegEx, fmatchbuf, 0, nsnull, 0) == REG_OK) {
05609           PR_LOG(FontMetricsGTKLM, PR_LOG_DEBUG, ("rejecting font '%s' (via reject pattern)\n", fmatchbuf));
05610           BANNED_FONT_PRINTF(("rejecting font '%s' (via reject pattern)", fmatchbuf));
05611           continue;
05612         }  
05613       }
05614 
05615       if (gFontAcceptRegEx) {
05616         if (regexec(gFontAcceptRegEx, fmatchbuf, 0, nsnull, 0) == REG_NOMATCH) {
05617           PR_LOG(FontMetricsGTKLM, PR_LOG_DEBUG, ("rejecting font '%s' (via accept pattern)\n", fmatchbuf));
05618           BANNED_FONT_PRINTF(("rejecting font '%s' (via accept pattern)", fmatchbuf));
05619           continue;
05620         }
05621       }       
05622     }    
05623 #endif /* ENABLE_X_FONT_BANNING */    
05624 
05625     nsFontCharSetMap *charSetMap = GetCharSetMap(charSetName);
05626     nsFontCharSetInfo* charSetInfo = charSetMap->mInfo;
05627     // indirection for font specific charset encoding 
05628     if (charSetInfo == &Special) {
05629       nsCAutoString familyCharSetName(familyName);
05630       familyCharSetName.Append('-');
05631       familyCharSetName.Append(charSetName);
05632       nsCStringKey familyCharSetKey(familyCharSetName);
05633       charSetMap = NS_STATIC_CAST(nsFontCharSetMap*, gSpecialCharSets->Get(&familyCharSetKey));
05634       if (!charSetMap)
05635         charSetMap = gNoneCharSetMap;
05636       charSetInfo = charSetMap->mInfo;
05637     }
05638     if (!charSetInfo) {
05639 #ifdef NOISY_FONTS
05640       printf("cannot find charset %s\n", charSetName);
05641 #endif
05642       charSetInfo = &Unknown;
05643     }
05644     SetCharsetLangGroup(charSetInfo);
05645     SetFontLangGroupInfo(charSetMap);
05646 
05647     nsCAutoString nodeName;
05648     if (aAnyFoundry)
05649       nodeName.Assign('*');
05650     else
05651       nodeName.Assign(foundry);
05652     nodeName.Append('-');
05653     nodeName.Append(familyName);
05654     nodeName.Append('-');
05655     nodeName.Append(charSetName);
05656     nsCStringKey key(nodeName);
05657     nsFontNode* node = (nsFontNode*) node_hash->Get(&key);
05658     if (!node) {
05659       node = new nsFontNode;
05660       if (!node) {
05661         continue;
05662       }
05663       node_hash->Put(&key, node);
05664       node->mName = nodeName;
05665       node->mCharSetInfo = charSetInfo;
05666     }
05667 
05668     int found = 0;
05669     if (nodeName == previousNodeName) {
05670       found = 1;
05671     }
05672     else {
05673       found = (aNodes->IndexOf(node) >= 0);
05674     }
05675     previousNodeName = nodeName;
05676     if (!found) {
05677       aNodes->AppendElement(node);
05678     }
05679 
05680     int styleIndex;
05681     // XXX This does not cover the full XLFD spec for SLANT.
05682     switch (slant[0]) {
05683     case 'i':
05684       styleIndex = NS_FONT_STYLE_ITALIC;
05685       break;
05686     case 'o':
05687       styleIndex = NS_FONT_STYLE_OBLIQUE;
05688       break;
05689     case 'r':
05690     default:
05691       styleIndex = NS_FONT_STYLE_NORMAL;
05692       break;
05693     }
05694     nsFontStyle* style = NodeGetStyle(node, styleIndex);
05695     if (!style)
05696       continue;
05697 
05698     nsCStringKey weightKey(weightName);
05699     int weightNumber = NS_PTR_TO_INT32(gWeights->Get(&weightKey));
05700     if (!weightNumber) {
05701 #ifdef NOISY_FONTS
05702       printf("cannot find weight %s\n", weightName);
05703 #endif
05704       weightNumber = NS_FONT_WEIGHT_NORMAL;
05705     }
05706     int weightIndex = WEIGHT_INDEX(weightNumber);
05707     nsFontWeight* weight = NodeGetWeight(style, weightIndex);
05708     if (!weight)
05709       continue;
05710   
05711     nsCStringKey setWidthKey(setWidth);
05712     int stretchIndex = NS_PTR_TO_INT32(gStretches->Get(&setWidthKey));
05713     if (!stretchIndex) {
05714 #ifdef NOISY_FONTS
05715       printf("cannot find stretch %s\n", setWidth);
05716 #endif
05717       stretchIndex = 5;
05718     }
05719     stretchIndex--;
05720     nsFontStretch* stretch = NodeGetStretch(weight, stretchIndex);
05721     if (!stretch)
05722       continue;
05723 
05724     if (scalable) {
05725       if (!NodeAddScalable(stretch, outline_scaled, name, familyName, 
05726            weightName, slant, setWidth, addStyle, spacing, charSetName))
05727         continue;
05728     }
05729   
05730      // get pixel size before the string is changed
05731     int pixels,
05732         points;
05733 
05734     pixels = atoi(pixelSize);
05735     points = atoi(pointSize);
05736 
05737     if (pixels) {
05738       if (gScaleBitmapFontsWithDevScale && (gDevScale > 1.0f)) {
05739         /* Add a font size which is exactly scaled as the scaling factor ... */
05740         if (!NodeAddSize(stretch, pixels, points, gDevScale, resX, resY, name, familyName, weightName, 
05741                          slant, setWidth, addStyle, spacing, charSetName, charSetInfo))
05742           continue;
05743 
05744         /* ... and offer a range of scaled fonts with integer scaling factors
05745          * (we're taking half steps between integers, too - to avoid too big
05746          * steps between font sizes) */
05747         float minScaler = PR_MAX(gDevScale / 2.0f, 1.5f),
05748               maxScaler = gDevScale * 2.f,
05749               scaler;
05750         for( scaler = minScaler ; scaler <= maxScaler ; scaler += 0.5f )
05751         {
05752           if (!NodeAddSize(stretch, pixels, points, scaler, resX, resY, name, familyName, weightName, 
05753                            slant, setWidth, addStyle, spacing, charSetName, charSetInfo))
05754             break;
05755         }
05756         if (scaler <= maxScaler) {
05757           continue; /* |NodeAddSize| returned an error in the loop above... */
05758         }
05759       }
05760       else
05761       {
05762         if (!NodeAddSize(stretch, pixels, points, 1.0f, resX, resY, name, familyName, weightName, 
05763                          slant, setWidth, addStyle, spacing, charSetName, charSetInfo))
05764           continue;     
05765       }
05766     }
05767   }
05768   XFreeFontNames(list);
05769 
05770 #ifdef DEBUG_DUMP_TREE
05771   DumpTree();
05772 #endif
05773 }
05774 
05775 static nsresult
05776 GetAllFontNames(void)
05777 {
05778   if (!gGlobalList) {
05779     // This may well expand further (families * sizes * styles?), but it's
05780     // only created once.
05781     gGlobalList = new nsFontNodeArray;
05782     if (!gGlobalList) {
05783       return NS_ERROR_OUT_OF_MEMORY;
05784     }
05785     /* Using "-*" instead of the full-qualified "-*-*-*-*-*-*-*-*-*-*-*-*-*-*"
05786      * because it's faster and "smarter" - see bug 34242 for details. */
05787     GetFontNames("-*", PR_FALSE, PR_FALSE, gGlobalList);
05788   }
05789 
05790   return NS_OK;
05791 }
05792 
05793 static nsFontFamily*
05794 FindFamily(nsCString* aName)
05795 {
05796   nsCStringKey key(*aName);
05797   nsFontFamily* family = (nsFontFamily*) gFamilies->Get(&key);
05798   if (!family) {
05799     family = new nsFontFamily();
05800     if (family) {
05801       char pattern[256];
05802       PR_snprintf(pattern, sizeof(pattern), "-*-%s-*-*-*-*-*-*-*-*-*-*-*-*",
05803         aName->get());
05804       GetFontNames(pattern, PR_TRUE, gForceOutlineScaledFonts, &family->mNodes);
05805       gFamilies->Put(&key, family);
05806     }
05807   }
05808 
05809   return family;
05810 }
05811 
05812 nsresult
05813 nsFontMetricsGTK::FamilyExists(nsIDeviceContext *aDevice, const nsString& aName)
05814 {
05815   if (!gInitialized) {
05816     nsresult res = InitGlobals(aDevice);
05817     if (NS_FAILED(res))
05818       return res;
05819   }
05820 
05821   if (!IsASCIIFontName(aName)) {
05822     return NS_ERROR_FAILURE;
05823   }
05824 
05825   nsCAutoString name;
05826   name.AssignWithConversion(aName.get());
05827   ToLowerCase(name);
05828   nsFontFamily* family = FindFamily(&name);
05829   if (family && family->mNodes.Count()) {
05830     return NS_OK;
05831   }
05832 
05833   return NS_ERROR_FAILURE;
05834 }
05835 
05836 PRUint32
05837 nsFontMetricsGTK::GetHints(void)
05838 {
05839   PRUint32 result = 0;
05840 
05841   /* We can't enable fast text measuring (yet) on platforms which
05842    * force natural alignment of datatypes (see
05843    * http://bugzilla.mozilla.org/show_bug.cgi?id=36146#c46) ... ;-(
05844    */
05845 
05846 #ifndef CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT
05847 #if defined(__i386)
05848 #define CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT 1
05849 #endif /* __i386 */
05850 #endif /* !CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT */
05851 
05852   static PRBool enable_fast_measure;
05853   static PRBool getenv_done = PR_FALSE;
05854     
05855   /* Check for the env vars "MOZILLA_GFX_ENABLE_FAST_MEASURE" and
05856    * "MOZILLA_GFX_DISABLE_FAST_MEASURE" to enable/disable fast text
05857    * measuring (for debugging the feature and doing regression tests).
05858    * This code will be removed one all issues around this new feature
05859    * have been fixed. */
05860   if (!getenv_done) {
05861 #ifdef CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT
05862     enable_fast_measure = PR_TRUE;
05863 #else
05864     enable_fast_measure = PR_FALSE;
05865 #endif /* CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT */
05866 
05867     if (PR_GetEnv("MOZILLA_GFX_ENABLE_FAST_MEASURE"))
05868       enable_fast_measure = PR_TRUE;
05869 
05870     if (PR_GetEnv("MOZILLA_GFX_DISABLE_FAST_MEASURE"))
05871       enable_fast_measure = PR_FALSE;
05872         
05873     getenv_done = PR_TRUE;
05874   }
05875 
05876   if (enable_fast_measure) {
05877     // We have GetTextDimensions()
05878     result |= NS_RENDERING_HINT_FAST_MEASURE;
05879   }
05880 
05881   return result;
05882 }
05883 
05884 //
05885 // convert a FFRE (Foundry-Family-Registry-Encoding) To XLFD Pattern
05886 //
05887 static void
05888 FFREToXLFDPattern(nsACString &aFFREName, nsACString &oPattern)
05889 {
05890   PRInt32 charsetHyphen;
05891 
05892   oPattern.Append("-");
05893   oPattern.Append(aFFREName);
05894   /* Search for the 3rd appearance of '-' */
05895   charsetHyphen = oPattern.FindChar('-');
05896   charsetHyphen = oPattern.FindChar('-', charsetHyphen + 1);
05897   charsetHyphen = oPattern.FindChar('-', charsetHyphen + 1);
05898   oPattern.Insert("-*-*-*-*-*-*-*-*-*-*", charsetHyphen);
05899 }
05900 
05901 //
05902 // substitute the charset in a FFRE (Foundry-Family-Registry-Encoding)
05903 //
05904 static void
05905 FFRESubstituteCharset(nsACString &aFFREName,
05906                       const char *aReplacementCharset)
05907 {
05908   PRInt32 charsetHyphen = aFFREName.FindChar('-');
05909   charsetHyphen = aFFREName.FindChar('-', charsetHyphen + 1);
05910   aFFREName.Truncate(charsetHyphen+1);
05911   aFFREName.Append(aReplacementCharset);
05912 }
05913 
05914 //
05915 // substitute the encoding in a FFRE (Foundry-Family-Registry-Encoding)
05916 //
05917 static void
05918 FFRESubstituteEncoding(nsACString &aFFREName,
05919                        const char *aReplacementEncoding)
05920 {
05921   PRInt32 encodingHyphen;
05922   /* Search for the 3rd apperance of '-' */
05923   encodingHyphen = aFFREName.FindChar('-');
05924   encodingHyphen = aFFREName.FindChar('-', encodingHyphen + 1);
05925   encodingHyphen = aFFREName.FindChar('-', encodingHyphen + 1);
05926   aFFREName.Truncate(encodingHyphen+1);
05927   aFFREName.Append(aReplacementEncoding);
05928 }
05929 
05930 nsFontGTK*
05931 nsFontMetricsGTK::TryNodes(nsACString &aFFREName, PRUint32 aChar)
05932 {
05933   const nsPromiseFlatCString& FFREName = PromiseFlatCString(aFFREName);
05934 
05935   FIND_FONT_PRINTF(("        TryNodes aFFREName = %s", FFREName.get()));
05936   nsCStringKey key(FFREName);
05937   PRBool anyFoundry = (FFREName.First() == '*');
05938   nsFontNodeArray* nodes = (nsFontNodeArray*) gCachedFFRESearches->Get(&key);
05939   if (!nodes) {
05940     nsCAutoString pattern;
05941     FFREToXLFDPattern(aFFREName, pattern);
05942     nodes = new nsFontNodeArray;
05943     if (!nodes)
05944       return nsnull;
05945     GetFontNames(pattern.get(), anyFoundry, gForceOutlineScaledFonts, nodes);
05946     gCachedFFRESearches->Put(&key, nodes);
05947   }
05948   int i, cnt = nodes->Count();
05949   for (i=0; i<cnt; i++) {
05950     nsFontNode* node = nodes->GetElement(i);
05951     nsFontGTK * font;
05952     font = SearchNode(node, aChar);
05953     if (font && font->SupportsChar(aChar))
05954       return font;
05955   }
05956   return nsnull;
05957 }
05958 
05959 nsFontGTK*
05960 nsFontMetricsGTK::TryNode(nsCString* aName, PRUint32 aChar)
05961 {
05962   FIND_FONT_PRINTF(("        TryNode aName = %s", (*aName).get()));
05963   //
05964   // check the specified font (foundry-family-registry-encoding)
05965   //
05966   if (aName->IsEmpty()) {
05967     return nsnull;
05968   }
05969   nsFontGTK* font;
05970  
05971   nsCStringKey key(*aName);
05972   nsFontNode* node = (nsFontNode*) gFFRENodes->Get(&key);
05973   if (!node) {
05974     nsCAutoString pattern;
05975     FFREToXLFDPattern(*aName, pattern);
05976     nsFontNodeArray nodes;
05977     GetFontNames(pattern.get(), PR_FALSE, gForceOutlineScaledFonts, &nodes);
05978     // no need to call gFFRENodes->Put() since GetFontNames already did
05979     if (nodes.Count() > 0) {
05980       // This assertion is not spurious; when searching for an FFRE
05981       // like -*-courier-iso8859-1 TryNodes should be called not TryNode
05982       NS_ASSERTION((nodes.Count() == 1), "unexpected number of nodes");
05983       node = nodes.GetElement(0);
05984     }
05985     else {
05986       // add a dummy node to the hash table to avoid calling XListFonts again
05987       node = new nsFontNode();
05988       if (!node) {
05989         return nsnull;
05990       }
05991       gFFRENodes->Put(&key, node);
05992       node->mDummy = 1;
05993     }
05994   }
05995 
05996   if (node) {
05997     font = SearchNode(node, aChar);
05998     if (font && font->SupportsChar(aChar))
05999       return font;
06000   }
06001 
06002   //
06003   // do not check related sub-planes for UserDefined
06004   //
06005   if (mIsUserDefined) {
06006     return nsnull;
06007   }
06008   //
06009   // check related sub-planes (wild-card the encoding)
06010   //
06011   nsCAutoString ffreName(*aName);
06012   FFRESubstituteEncoding(ffreName, "*");
06013   FIND_FONT_PRINTF(("        TrySubplane: wild-card the encoding"));
06014   font = TryNodes(ffreName, aChar);
06015   if (font) {
06016     NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char");
06017     return font;
06018   }
06019   return nsnull;
06020 }
06021 
06022 nsFontGTK* 
06023 nsFontMetricsGTK::TryLangGroup(nsIAtom* aLangGroup, nsCString* aName, PRUint32 aChar)
06024 {
06025   //
06026   // for this family check related registry-encoding (for the language)
06027   //
06028   FIND_FONT_PRINTF(("      TryLangGroup lang group = %s, aName = %s", 
06029                             atomToName(aLangGroup), (*aName).get()));
06030   if (aName->IsEmpty()) {
06031     return nsnull;
06032   }
06033   nsFontGTK* font = FindLangGroupFont(aLangGroup, aChar, aName);
06034   return font;
06035 }
06036 
06037 nsFontGTK*
06038 nsFontMetricsGTK::TryFamily(nsCString* aName, PRUint32 aChar)
06039 {
06040   //
06041   // check the patterh "*-familyname-registry-encoding" for language
06042   //
06043   nsFontFamily* family = FindFamily(aName);
06044   if (family) {
06045     // try family name of language group first
06046     nsCAutoString FFREName("*-");
06047     FFREName.Append(*aName);
06048     FFREName.Append("-*-*");
06049     FIND_FONT_PRINTF(("        TryFamily %s with lang group = %s", (*aName).get(),
06050                                                          atomToName(mLangGroup)));
06051     nsFontGTK* font = TryLangGroup(mLangGroup, &FFREName, aChar);
06052     if(font) {
06053       return font;
06054     }
06055 
06056     // then try family name regardless of language group
06057     nsFontNodeArray* nodes = &family->mNodes;
06058     PRInt32 n = nodes->Count();
06059     for (PRInt32 i = 0; i < n; i++) {
06060       FIND_FONT_PRINTF(("        TryFamily %s", nodes->GetElement(i)->mName.get()));
06061       nsFontGTK* font = SearchNode(nodes->GetElement(i), aChar);
06062       if (font && font->SupportsChar(aChar)) {
06063         return font;
06064       }
06065     }
06066   }
06067 
06068   return nsnull;
06069 }
06070 
06071 nsFontGTK*
06072 nsFontMetricsGTK::TryAliases(nsCString* aAlias, PRUint32 aChar)
06073 {
06074   nsCStringKey key(*aAlias);
06075   char* name = (char*) gAliases->Get(&key);
06076   if (name) {
06077     nsCAutoString str(name);
06078     return TryFamily(&str, aChar);
06079   }
06080 
06081   return nsnull;
06082 }
06083 
06084 nsFontGTK*
06085 nsFontMetricsGTK::FindUserDefinedFont(PRUint32 aChar)
06086 {
06087   if (mIsUserDefined) {
06088     FIND_FONT_PRINTF(("        FindUserDefinedFont"));
06089     nsFontGTK* font = TryNode(&mUserDefined, aChar);
06090     mIsUserDefined = PR_FALSE;
06091     if (font) {
06092       NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char");
06093       return font;
06094     }
06095   }
06096 
06097   return nsnull;
06098 }
06099 
06100 nsFontGTK*
06101 nsFontMetricsGTK::FindStyleSheetSpecificFont(PRUint32 aChar)
06102 {
06103   FIND_FONT_PRINTF(("    FindStyleSheetSpecificFont"));
06104   while (mFontsIndex < mFonts.Count()) {
06105     if (mFontIsGeneric[mFontsIndex]) {
06106       return nsnull;
06107     }
06108     nsCString* familyName = mFonts.CStringAt(mFontsIndex);
06109 
06110     /*
06111      * count hyphens
06112      * XXX It might be good to try to pre-cache this information instead
06113      * XXX of recalculating it on every font access!
06114      */
06115     const char* str = familyName->get();
06116     FIND_FONT_PRINTF(("        familyName = %s", str));
06117     PRUint32 len = familyName->Length();
06118     int hyphens = 0;
06119     for (PRUint32 i = 0; i < len; i++) {
06120       if (str[i] == '-') {
06121         hyphens++;
06122       }
06123     }
06124 
06125     /*
06126      * if there are 3 hyphens, the name is in FFRE form
06127      * (foundry-family-registry-encoding)
06128      * ie: something like this:
06129      *
06130      *   adobe-times-iso8859-1
06131      *
06132      * otherwise it is something like
06133      *
06134      *   times new roman
06135      */
06136     nsFontGTK* font;
06137     if (hyphens == 3) {
06138       font = TryNode(familyName, aChar);
06139       if (font) {
06140         NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char");
06141         return font;
06142       }
06143     }
06144     else {
06145       font = TryFamily(familyName, aChar);
06146       if (font) {
06147         NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char");
06148         return font;
06149       }
06150       font = TryAliases(familyName, aChar);
06151       if (font) {
06152         NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char");
06153         return font;
06154       }
06155     }
06156     // bug 42917: increment only after all of the above fails
06157     mFontsIndex++;
06158   }
06159 
06160   return nsnull;
06161 }
06162 
06163 static void
06164 PrefEnumCallback(const char* aName, void* aClosure)
06165 {
06166   nsFontSearch* s = (nsFontSearch*) aClosure;
06167   if (s->mFont) {
06168     NS_ASSERTION(s->mFont->SupportsChar(s->mChar), "font supposed to support this char");
06169     return;
06170   }
06171   nsXPIDLCString value;
06172   gPref->CopyCharPref(aName, getter_Copies(value));
06173   nsCAutoString name;
06174   if (value.get()) {
06175     name = value;
06176     FIND_FONT_PRINTF(("       PrefEnumCallback"));
06177     s->mFont = s->mMetrics->TryNode(&name, s->mChar);
06178     if (s->mFont) {
06179       NS_ASSERTION(s->mFont->SupportsChar(s->mChar), "font supposed to support this char");
06180       return;
06181     }
06182     s->mFont = s->mMetrics->TryLangGroup(s->mMetrics->mLangGroup, &name, s->mChar);
06183     if (s->mFont) {
06184       NS_ASSERTION(s->mFont->SupportsChar(s->mChar), "font supposed to support this char");
06185       return;
06186     }
06187   }
06188   gPref->CopyDefaultCharPref(aName, getter_Copies(value));
06189   if (value.get() && (!name.Equals(value))) {
06190     name = value;
06191     FIND_FONT_PRINTF(("       PrefEnumCallback:default"));
06192     s->mFont = s->mMetrics->TryNode(&name, s->mChar);
06193     if (s->mFont) {
06194       NS_ASSERTION(s->mFont->SupportsChar(s->mChar), "font supposed to support this char");
06195       return;
06196     }
06197     s->mFont = s->mMetrics->TryLangGroup(s->mMetrics->mLangGroup, &name, s->mChar);
06198     NS_ASSERTION(s->mFont ? s->mFont->SupportsChar(s->mChar) : 1, "font supposed to support this char");
06199   }
06200 }
06201 
06202 nsFontGTK*
06203 nsFontMetricsGTK::FindStyleSheetGenericFont(PRUint32 aChar)
06204 {
06205   FIND_FONT_PRINTF(("    FindStyleSheetGenericFont"));
06206   nsFontGTK* font;
06207 
06208   if (mTriedAllGenerics) {
06209     return nsnull;
06210   }
06211 
06212   //
06213   // find font based on document's lang group
06214   //
06215   font = FindLangGroupPrefFont(mLangGroup, aChar);
06216   if (font) {
06217     NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char");
06218     return font;
06219   }
06220 
06221   //
06222   // Asian smart quote glyphs are much too large for western
06223   // documents so if this is a single byte document add a
06224   // special "font" to tranliterate those chars rather than
06225   // possibly find them in double byte fonts
06226   //
06227   // (risk management: since we are close to a ship point we have a 
06228   //  control (gAllowDoubleByteSpecialChars) to disable this new feature)
06229   //
06230 if (gAllowDoubleByteSpecialChars) {
06231   if (!mDocConverterType) {
06232     if (mLoadedFontsCount) {
06233       FIND_FONT_PRINTF(("just use the 1st converter type"));
06234       nsFontGTK* first_font = mLoadedFonts[0];
06235       if (first_font->mCharSetInfo) {
06236         mDocConverterType = first_font->mCharSetInfo->Convert;
06237         if (mDocConverterType == SingleByteConvert ) {
06238           FIND_FONT_PRINTF(("single byte converter for %s", atomToName(mLangGroup)));
06239         }
06240         else {
06241           FIND_FONT_PRINTF(("double byte converter for %s", atomToName(mLangGroup)));
06242         }
06243       }
06244     }
06245     if (!mDocConverterType) {
06246       mDocConverterType = SingleByteConvert;
06247     }
06248     if (mDocConverterType == SingleByteConvert) {
06249       // before we put in the transliterator to disable double byte special chars
06250       // add the x-western font before the early transliterator
06251       // to get the EURO sign (hack)
06252 
06253       nsFontGTK* western_font = nsnull;
06254       if (mLangGroup != gWesternLocale)
06255         western_font = FindLangGroupPrefFont(gWesternLocale, aChar);
06256 
06257       // add the symbol font before the early transliterator
06258       // to get the bullet (hack)
06259       nsCAutoString symbol_ffre("*-symbol-adobe-fontspecific");
06260       nsFontGTK* symbol_font = TryNodes(symbol_ffre, 0x0030);
06261 
06262       // Add the Adobe Euro fonts before the early transliterator
06263       nsCAutoString euro_ffre("*-euro*-adobe-fontspecific");
06264       nsFontGTK* euro_font = TryNodes(euro_ffre, 0x20AC);
06265 
06266       // add the early transliterator
06267       // to avoid getting Japanese "special chars" such as smart
06268       // since they are very oversized compared to western fonts
06269       nsFontGTK* sub_font = FindSubstituteFont(aChar);
06270       NS_ASSERTION(sub_font, "failed to get a special chars substitute font");
06271       if (sub_font) {
06272         sub_font->mCCMap = gDoubleByteSpecialCharsCCMap;
06273         AddToLoadedFontsList(sub_font);
06274       }
06275       if (western_font && SAFE_CCMAP_HAS_CHAR_EXT(western_font->mCCMap, aChar)) {
06276         return western_font;
06277       }
06278       else if (symbol_font && SAFE_CCMAP_HAS_CHAR_EXT(symbol_font->mCCMap, aChar)) {
06279         return symbol_font;
06280       }
06281       else if (euro_font && SAFE_CCMAP_HAS_CHAR_EXT(euro_font->mCCMap, aChar)) {
06282         return euro_font;
06283       }
06284       else if (sub_font && SAFE_CCMAP_HAS_CHAR_EXT(sub_font->mCCMap, aChar)) {
06285         FIND_FONT_PRINTF(("      transliterate special chars for single byte docs"));
06286         return sub_font;
06287       }
06288     }
06289   }
06290 }
06291 
06292   //
06293   // find font based on user's locale's lang group
06294   // if different from documents locale
06295   if (gUsersLocale != mLangGroup) {
06296     FIND_FONT_PRINTF(("      find font based on user's locale's lang group"));
06297     font = FindLangGroupPrefFont(gUsersLocale, aChar);
06298     if (font) {
06299       NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char");
06300       return font;
06301     }
06302   }
06303 
06304   //
06305   // Search all font prefs for generic
06306   //
06307   nsCAutoString prefix("font.name.");
06308   prefix.Append(*mGeneric);
06309   nsFontSearch search = { this, aChar, nsnull };
06310   FIND_FONT_PRINTF(("      Search all font prefs for generic"));
06311   gPref->EnumerateChildren(prefix.get(), PrefEnumCallback, &search);
06312   if (search.mFont) {
06313     NS_ASSERTION(search.mFont->SupportsChar(aChar), "font supposed to support this char");
06314     return search.mFont;
06315   }
06316 
06317   //
06318   // Search all font prefs
06319   //
06320   // find based on all prefs (no generic part (eg: sans-serif))
06321   nsCAutoString allPrefs("font.name.");
06322   search.mFont = nsnull;
06323   FIND_FONT_PRINTF(("      Search all font prefs"));
06324   gPref->EnumerateChildren(allPrefs.get(), PrefEnumCallback, &search);
06325   if (search.mFont) {
06326     NS_ASSERTION(search.mFont->SupportsChar(aChar), "font supposed to support this char");
06327     return search.mFont;
06328   }
06329 
06330   mTriedAllGenerics = 1;
06331   return nsnull;
06332 }
06333 
06334 nsFontGTK*
06335 nsFontMetricsGTK::FindAnyFont(PRUint32 aChar)
06336 {
06337   FIND_FONT_PRINTF(("    FindAnyFont"));
06338   // XXX If we get to this point, that means that we have exhausted all the
06339   // families in the lists. Maybe we should try a list of fonts that are
06340   // specific to the vendor of the X server here. Because XListFonts for the
06341   // whole list is very expensive on some Unixes.
06342 
06343   /*
06344    * Try all the fonts on the system.
06345    */
06346   nsresult res = GetAllFontNames();
06347   if (NS_FAILED(res)) {
06348     return nsnull;
06349   }
06350 
06351   PRInt32 n = gGlobalList->Count();
06352   for (PRInt32 i = 0; i < n; i++) {
06353     nsFontGTK* font = SearchNode(gGlobalList->GetElement(i), aChar);
06354     if (font && font->SupportsChar(aChar)) {
06355       // XXX We should probably write this family name out to disk, so that
06356       // we can use it next time. I.e. prefs file or something.
06357       return font;
06358     }
06359   }
06360 
06361   // future work:
06362   // to properly support the substitute font we
06363   // need to indicate here that all fonts have been tried
06364   return nsnull;
06365 }
06366 
06367 nsFontGTK*
06368 nsFontMetricsGTK::FindSubstituteFont(PRUint32 aChar)
06369 {
06370   if (!mSubstituteFont) {
06371     for (int i = 0; i < mLoadedFontsCount; i++) {
06372       if (SAFE_CCMAP_HAS_CHAR_EXT(mLoadedFonts[i]->mCCMap, 'a')) {
06373         mSubstituteFont = new nsFontGTKSubstitute(mLoadedFonts[i]);
06374         break;
06375       }
06376     }
06377     // Currently the substitute font does not have a glyph map.
06378     // This means that even if we have already checked all fonts
06379     // for a particular character the mLoadedFonts will not know it.
06380     // Thus we reparse *all* font glyph maps every time we see
06381     // a character that ends up using a substitute font.
06382     // future work:
06383     // create an empty mCCMap and every time we determine a
06384     // character will get its "glyph" from the substitute font
06385     // mark that character in the mCCMap.
06386   }
06387   // mark the mCCMap to indicate that this character has a "glyph"
06388 
06389   // If we know that mLoadedFonts has every font's glyph map loaded
06390   // then we can now set all the bit in the substitute font's glyph map
06391   // and thus direct all umapped characters to the substitute
06392   // font (without the font search).
06393   // if tried all glyphs {
06394   //   create a substitute font with all bits set
06395   //   set all bits in mCCMap
06396   // }
06397 
06398   return mSubstituteFont;
06399 }
06400 
06401 //
06402 // find font based on lang group
06403 //
06404 
06405 nsFontGTK* 
06406 nsFontMetricsGTK::FindLangGroupPrefFont(nsIAtom* aLangGroup, PRUint32 aChar)
06407 { 
06408   nsFontGTK* font;
06409   //
06410   // get the font specified in prefs
06411   //
06412   nsCAutoString prefix("font.name."); 
06413   prefix.Append(*mGeneric); 
06414   if (aLangGroup) { 
06415     // check user set pref
06416     nsCAutoString pref = prefix;
06417     pref.Append(char('.'));
06418     const char* langGroup = nsnull;
06419     aLangGroup->GetUTF8String(&langGroup);
06420     pref.Append(langGroup);
06421     nsXPIDLCString value;
06422     gPref->CopyCharPref(pref.get(), getter_Copies(value));
06423     nsCAutoString str;
06424     nsCAutoString str_user;
06425     if (value.get()) {
06426       str = value.get();
06427       str_user = value.get();
06428       FIND_FONT_PRINTF(("      user pref %s = %s", pref.get(), str.get()));
06429       font = TryNode(&str, aChar);
06430       if (font) {
06431         NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char");
06432         return font;
06433       }
06434       font = TryLangGroup(aLangGroup, &str, aChar);
06435       if (font) {
06436         NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char");
06437         return font;
06438       }
06439     }
06440     // check factory set pref
06441     gPref->CopyDefaultCharPref(pref.get(), getter_Copies(value));
06442     if (value.get()) {
06443       str = value.get();
06444       // check if we already tried this name
06445       if (str != str_user) {
06446         FIND_FONT_PRINTF(("      default pref %s = %s", pref.get(), str.get()));
06447         font = TryNode(&str, aChar);
06448         if (font) {
06449           NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char");
06450           return font;
06451         }
06452         font = TryLangGroup(aLangGroup, &str, aChar);
06453         if (font) {
06454           NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char");
06455           return font;
06456         }
06457       }
06458     }
06459   }
06460 
06461   //
06462   // find any style font based on lang group
06463   //
06464   FIND_FONT_PRINTF(("      find font based on lang group"));
06465   font = FindLangGroupFont(aLangGroup, aChar, nsnull);
06466   if (font) {
06467     NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char");
06468     return font;
06469   }
06470 
06471   return nsnull;
06472 }
06473 
06474 nsFontGTK*
06475 nsFontMetricsGTK::FindLangGroupFont(nsIAtom* aLangGroup, PRUint32 aChar, nsCString *aName)
06476 {
06477   nsFontGTK* font;
06478 
06479   FIND_FONT_PRINTF(("      lang group = %s", atomToName(aLangGroup)));
06480 
06481   //  scan gCharSetMap for encodings with matching lang groups
06482   nsFontCharSetMap* charSetMap;
06483   for (charSetMap=gCharSetMap; charSetMap->mName; charSetMap++) {
06484     nsFontLangGroup* fontLangGroup = charSetMap->mFontLangGroup;
06485 
06486     if ((!fontLangGroup) || (!fontLangGroup->mFontLangGroupName)) {
06487       continue;
06488     }
06489 
06490     if (!charSetMap->mInfo->mLangGroup) {
06491       SetCharsetLangGroup(charSetMap->mInfo);
06492     }
06493 
06494     if (!fontLangGroup->mFontLangGroupAtom) {
06495       SetFontLangGroupInfo(charSetMap);
06496     }
06497 
06498     // if font's langGroup is different from requested langGroup, continue.
06499     // An exception is that font's langGroup ZHTWHK is regarded as matching
06500     // both ZHTW and ZHHK (Freetype2 and Solaris).
06501     if ((aLangGroup != fontLangGroup->mFontLangGroupAtom) &&
06502         (aLangGroup != charSetMap->mInfo->mLangGroup) &&
06503         (fontLangGroup->mFontLangGroupAtom != gZHTWHK || 
06504         (aLangGroup != gZHHK && aLangGroup != gZHTW))) {
06505       continue;
06506     }
06507     // look for a font with this charset (registry-encoding) & char
06508     //
06509     nsCAutoString ffreName;
06510     if(aName) {
06511       // if aName was specified so call TryNode() not TryNodes()
06512       ffreName.Assign(*aName);
06513       FFRESubstituteCharset(ffreName, charSetMap->mName); 
06514       FIND_FONT_PRINTF(("      %s ffre = %s", charSetMap->mName, ffreName.get()));
06515       if(aName->First() == '*') {
06516          // called from TryFamily()
06517          font = TryNodes(ffreName, aChar);
06518       } else {
06519          font = TryNode(&ffreName, aChar);
06520       }
06521       NS_ASSERTION(font ? font->SupportsChar(aChar) : 1, "font supposed to support this char");
06522     } else {
06523       // no name was specified so call TryNodes() for this charset
06524       ffreName.Assign("*-*-*-*");
06525       FFRESubstituteCharset(ffreName, charSetMap->mName); 
06526       FIND_FONT_PRINTF(("      %s ffre = %s", charSetMap->mName, ffreName.get()));
06527       font = TryNodes(ffreName, aChar);
06528       NS_ASSERTION(font ? font->SupportsChar(aChar) : 1, "font supposed to support this char");
06529     }
06530     if (font) {
06531       NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char");
06532       return font;
06533     }
06534   }
06535 
06536   return nsnull;
06537 }
06538 
06539 /*
06540  * First we try to load the user-defined font, if the user-defined charset
06541  * has been selected in the menu.
06542  *
06543  * Next, we try the fonts listed in the font-family property (FindStyleSheetSpecificFont).
06544  *
06545  * Next, we try any CSS generic font encountered in the font-family list and
06546  * all of the fonts specified by the user for the generic (FindStyleSheetGenericFont).
06547  *
06548  * Next, we try all of the fonts on the system (FindAnyFont). This is
06549  * expensive on some Unixes.
06550  *
06551  * Finally, we try to create a substitute font that offers substitute glyphs
06552  * for the characters (FindSubstituteFont).
06553  */
06554 nsFontGTK*
06555 nsFontMetricsGTK::FindFont(PRUint32 aChar)
06556 {
06557   FIND_FONT_PRINTF(("\nFindFont(%c/0x%04x)", aChar, aChar));
06558 
06559   // If this is is the 'unknown' char (ie: converter could not 
06560   // convert it) there is no sense in searching any further for 
06561   // a font. Just returing mWesternFont
06562   if (aChar == UCS2_REPLACEMENT_CHAR) {
06563     FIND_FONT_PRINTF(("      ignore the 'UCS2_REPLACEMENT_CHAR' character, return mWesternFont"));
06564     return mWesternFont;
06565   }
06566 
06567   nsFontGTK* font = FindUserDefinedFont(aChar);
06568   if (!font) {
06569     font = FindStyleSheetSpecificFont(aChar);
06570     if (!font) {
06571       font = FindStyleSheetGenericFont(aChar);
06572       if (!font) {
06573         font = FindAnyFont(aChar);
06574         if (!font) {
06575           font = FindSubstituteFont(aChar);
06576         }
06577       }
06578     }
06579   }
06580 
06581 #ifdef NS_FONT_DEBUG_CALL_TRACE
06582   if (gFontDebug & NS_FONT_DEBUG_CALL_TRACE) {
06583     printf("FindFont(%04X)[", aChar);
06584     for (PRInt32 i = 0; i < mFonts.Count(); i++) {
06585       printf("%s, ", mFonts.CStringAt(i)->get());
06586     }
06587     printf("]\nreturns ");
06588     if (font) {
06589       printf("%s\n", font->mName ? font->mName : "(substitute)");
06590     }
06591     else {
06592       printf("NULL\n");
06593     }
06594   }
06595 #endif
06596 
06597   return font;
06598 }
06599 
06600 
06601 // The Font Enumerator
06602 
06603 nsFontEnumeratorGTK::nsFontEnumeratorGTK()
06604 {
06605 }
06606 
06607 NS_IMPL_ISUPPORTS1(nsFontEnumeratorGTK, nsIFontEnumerator)
06608 
06609 typedef struct EnumerateNodeInfo
06610 {
06611   PRUnichar** mArray;
06612   int         mIndex;
06613   nsIAtom*    mLangGroup;
06614 } EnumerateNodeInfo;
06615 
06616 static PRIntn
06617 EnumerateNode(void* aElement, void* aData)
06618 {
06619   nsFontNode* node = (nsFontNode*) aElement;
06620   EnumerateNodeInfo* info = (EnumerateNodeInfo*) aData;
06621   if (info->mLangGroup != gUserDefined) {
06622     if (node->mCharSetInfo == &Unknown) {
06623       return PR_TRUE; // continue
06624     }
06625     else if (info->mLangGroup != gUnicode) {
06626       // if font's langGroup is different from requested langGroup, continue.
06627       // An exception is that font's langGroup ZHTWHK is regarded as matching
06628       // both ZHTW and ZHHK (Freetype2 and Solaris).
06629       if (node->mCharSetInfo->mLangGroup != info->mLangGroup &&
06630          (node->mCharSetInfo->mLangGroup != gZHTWHK || 
06631          (info->mLangGroup != gZHHK && info->mLangGroup != gZHTW))) {
06632         return PR_TRUE; // continue
06633       }
06634     }
06635     // else {
06636     //   if (lang == add-style-field) {
06637     //     consider it part of the lang group
06638     //   }
06639     //   else if (a Unicode font reports its lang group) {
06640     //     consider it part of the lang group
06641     //   }
06642     //   else if (lang's ranges in list of ranges) {
06643     //     consider it part of the lang group
06644     //     // Note: at present we have no way to do this test but we 
06645     //     // could in the future and this would be the place to enable
06646     //     // to make the font show up in the preferences dialog
06647     //   }
06648     // }
06649 
06650   }
06651   PRUnichar** array = info->mArray;
06652   int j = info->mIndex;
06653   PRUnichar* str = ToNewUnicode(node->mName);
06654   if (!str) {
06655     for (j = j - 1; j >= 0; j--) {
06656       nsMemory::Free(array[j]);
06657     }
06658     info->mIndex = 0;
06659     return PR_FALSE; // stop
06660   }
06661   array[j] = str;
06662   info->mIndex++;
06663 
06664   return PR_TRUE; // continue
06665 }
06666 
06667 PR_BEGIN_EXTERN_C
06668 static int
06669 CompareFontNames(const void* aArg1, const void* aArg2, void* aClosure)
06670 {
06671   const PRUnichar* str1 = *((const PRUnichar**) aArg1);
06672   const PRUnichar* str2 = *((const PRUnichar**) aArg2);
06673 
06674   // XXX add nsICollation stuff
06675 
06676   return nsCRT::strcmp(str1, str2);
06677 }
06678 PR_END_EXTERN_C
06679 
06680 static nsresult
06681 EnumFonts(nsIAtom* aLangGroup, const char* aGeneric, PRUint32* aCount,
06682   PRUnichar*** aResult)
06683 {
06684   nsresult res = GetAllFontNames();
06685   if (NS_FAILED(res)) {
06686     return res;
06687   }
06688 
06689   PRUnichar** array =
06690     (PRUnichar**) nsMemory::Alloc(gGlobalList->Count() * sizeof(PRUnichar*));
06691   if (!array) {
06692     return NS_ERROR_OUT_OF_MEMORY;
06693   }
06694   EnumerateNodeInfo info = { array, 0, aLangGroup };
06695   if (!gGlobalList->EnumerateForwards(EnumerateNode, &info)) {
06696     nsMemory::Free(array);
06697     return NS_ERROR_OUT_OF_MEMORY;
06698   }
06699 
06700   NS_QuickSort(array, info.mIndex, sizeof(PRUnichar*), CompareFontNames,
06701                nsnull);
06702 
06703   *aCount = info.mIndex;
06704   if (*aCount) {
06705     *aResult = array;
06706   }
06707   else {
06708     nsMemory::Free(array);
06709   }
06710 
06711   return NS_OK;
06712 }
06713 
06714 NS_IMETHODIMP
06715 nsFontEnumeratorGTK::EnumerateAllFonts(PRUint32* aCount, PRUnichar*** aResult)
06716 {
06717   NS_ENSURE_ARG_POINTER(aResult);
06718   *aResult = nsnull;
06719   NS_ENSURE_ARG_POINTER(aCount);
06720   *aCount = 0;
06721 
06722   return EnumFonts(nsnull, nsnull, aCount, aResult);
06723 }
06724 
06725 NS_IMETHODIMP
06726 nsFontEnumeratorGTK::EnumerateFonts(const char* aLangGroup,
06727   const char* aGeneric, PRUint32* aCount, PRUnichar*** aResult)
06728 {
06729   NS_ENSURE_ARG_POINTER(aResult);
06730   *aResult = nsnull;
06731   NS_ENSURE_ARG_POINTER(aCount);
06732   *aCount = 0;
06733 
06734   // aLangGroup=null or ""  means any (i.e., don't care)
06735   // aGeneric=null or ""  means any (i.e, don't care)
06736   nsCOMPtr<nsIAtom> langGroup;
06737   if (aLangGroup && *aLangGroup)
06738     langGroup = do_GetAtom(aLangGroup);
06739   const char* generic = nsnull;
06740   if (aGeneric && *aGeneric)
06741     generic = aGeneric;
06742 
06743   // XXX still need to implement aLangGroup and aGeneric
06744   return EnumFonts(langGroup, generic, aCount, aResult);
06745 }
06746 
06747 NS_IMETHODIMP
06748 nsFontEnumeratorGTK::HaveFontFor(const char* aLangGroup, PRBool* aResult)
06749 {
06750   NS_ENSURE_ARG_POINTER(aResult);
06751   *aResult = PR_FALSE;
06752   NS_ENSURE_ARG_POINTER(aLangGroup);
06753 
06754   *aResult = PR_TRUE; // always return true for now.
06755   // Finish me - ftang
06756   return NS_OK;
06757 }
06758 
06759 NS_IMETHODIMP
06760 nsFontEnumeratorGTK::GetDefaultFont(const char *aLangGroup, 
06761   const char *aGeneric, PRUnichar **aResult)
06762 {
06763   // aLangGroup=null or ""  means any (i.e., don't care)
06764   // aGeneric=null or ""  means any (i.e, don't care)
06765 
06766   NS_ENSURE_ARG_POINTER(aResult);
06767   *aResult = nsnull;
06768 
06769   return NS_OK;
06770 }
06771 
06772 NS_IMETHODIMP
06773 nsFontEnumeratorGTK::UpdateFontList(PRBool *updateFontList)
06774 {
06775   *updateFontList = PR_FALSE; // always return false for now
06776   return NS_OK;
06777 }
06778 
06779 nsFontCharSetMap *
06780 GetCharSetMap(const char *aCharSetName)
06781 {
06782     nsCStringKey charSetKey(aCharSetName);
06783     nsFontCharSetMap* charSetMap =
06784       (nsFontCharSetMap*) gCharSetMaps->Get(&charSetKey);
06785     if (!charSetMap)
06786       charSetMap = gNoneCharSetMap;
06787   return charSetMap;
06788 }
06789 
06790 #ifdef MOZ_ENABLE_FREETYPE2
06791 void
06792 CharSetNameToCodeRangeBits(const char *aCharset,
06793                            PRUint32 *aCodeRange1, PRUint32 *aCodeRange2)
06794 {
06795   nsFontCharSetMap *charSetMap = GetCharSetMap(aCharset);
06796   nsFontCharSetInfo* charSetInfo = charSetMap->mInfo;
06797 
06798   *aCodeRange1 = charSetInfo->mCodeRange1Bits;
06799   *aCodeRange2 = charSetInfo->mCodeRange2Bits;
06800 }
06801 #endif