Back to index

lightning-sunbird  0.9+nobinonly
nsFontMetricsOS2.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Mozilla OS/2 libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * John Fairhurst, <john_fairhurst@iname.com>.
00018  * Portions created by the Initial Developer are Copyright (C) 1999
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Pierre Phaneuf <pp@ludusdesign.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsGfxDefs.h"
00039 #include "nsIPref.h"
00040 #include "nsIServiceManager.h"
00041 #include "nsILanguageAtomService.h"
00042 #include "nsICharsetConverterManager.h"
00043 #include "nsLocaleCID.h"
00044 #include "nsILocaleService.h"
00045 #include "nsICharRepresentable.h"
00046 #include "nsISaveAsCharset.h"
00047 #include "nsIObserver.h"
00048 #include "nsIObserverService.h"
00049 #include "nsFontMetricsOS2.h"
00050 #include "nsQuickSort.h"
00051 #include "nsTextFormatter.h"
00052 #include "prmem.h"
00053 #include "plhash.h"
00054 #include "prprf.h"
00055 #include "nsReadableUtils.h"
00056 #include "nsUnicodeRange.h"
00057 #include "nsUnicharUtils.h"
00058 
00059 #include "nsOS2Uni.h"
00060 #include <math.h>
00061 
00062 #ifdef MOZ_MATHML
00063   #include <math.h>
00064 #endif
00065 
00066 #undef USER_DEFINED
00067 #define USER_DEFINED "x-user-def"
00068 
00069 #define NS_REPLACEMENT_CHAR  PRUnichar(0x003F) // question mark
00070 
00071 static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
00072 static NS_DEFINE_CID(kPrefCID, NS_PREF_CID);
00073 static NS_DEFINE_CID(kLocaleServiceCID, NS_LOCALESERVICE_CID); 
00074 
00075 #define NS_MAX_FONT_WEIGHT 900
00076 #define NS_MIN_FONT_WEIGHT 100
00077 
00078 #undef CHAR_BUFFER_SIZE
00079 #define CHAR_BUFFER_SIZE 1024
00080 
00081 
00082 /***** Global variables *****/
00083 nsTHashtable<GlobalFontEntry>* nsFontMetricsOS2::gGlobalFonts = nsnull;
00084 PRBool        nsFontMetricsOS2::gSubstituteVectorFonts = PR_TRUE;
00085 PLHashTable  *nsFontMetricsOS2::gFamilyNames = nsnull;
00086 
00087 static nsIPref           *gPref = nsnull;
00088 static nsIAtom           *gUsersLocale = nsnull;
00089 static nsIAtom           *gSystemLocale = nsnull;
00090 static nsIAtom           *gUserDefined = nsnull;
00091 static int                gInitialized = 0;
00092 static ULONG              gSystemCodePage = 0;
00093 static ULONG              gCurrHashValue = 1;
00094 
00095 #ifdef USE_FREETYPE
00096 PRBool nsFontMetricsOS2::gUseFTFunctions = PR_FALSE;
00097 static nsISaveAsCharset* gFontSubstituteConverter = nsnull;
00098 
00099 static nsIAtom* gJA = nsnull;
00100 static nsIAtom* gKO = nsnull;
00101 static nsIAtom* gZHTW = nsnull;
00102 static nsIAtom* gZHCN = nsnull;
00103 static nsIAtom* gZHHK = nsnull;
00104 #endif /* use_freetype */
00105 
00106 /***** Charset structures *****/
00107 enum nsCharset
00108 {
00109   eCharset_DEFAULT = 0,
00110   eCharset_ANSI,
00111   eCharset_EASTEUROPE,
00112   eCharset_RUSSIAN,
00113   eCharset_GREEK,
00114   eCharset_TURKISH,
00115   eCharset_HEBREW,
00116   eCharset_ARABIC,
00117   eCharset_BALTIC,
00118   eCharset_THAI,
00119   eCharset_SHIFTJIS,
00120   eCharset_GB2312,
00121   eCharset_HANGEUL,
00122   eCharset_CHINESEBIG5,
00123   eCharset_JOHAB,
00124   eCharset_COUNT
00125 };
00126 
00127 struct nsCharsetInfo
00128 {
00129   char*    mName;
00130 /*  USHORT   mMask; */
00131   PRUint16 mCodePage;
00132   char*    mLangGroup;
00133 };
00134 
00135 static nsCharsetInfo gCharsetInfo[eCharset_COUNT] =
00136 {
00137   { "DEFAULT",     /* 0,                */  0,    "" },
00138   { "ANSI",        /* FM_DEFN_LATIN1,   */  1252, "x-western" },
00139   { "EASTEUROPE",  /* FM_DEFN_LATIN2,   */  1250, "x-central-euro" },
00140   { "RUSSIAN",     /* FM_DEFN_CYRILLIC, */  1251, "x-cyrillic" },
00141   { "GREEK",       /* FM_DEFN_GREEK,    */  813,  "el" },
00142   { "TURKISH",     /* 0,                */  1254, "tr" },
00143   { "HEBREW",      /* FM_DEFN_HEBREW,   */  1208,  "he" },
00144   { "ARABIC",      /* FM_DEFN_ARABIC,   */  864,  "ar" },
00145   { "BALTIC",      /* 0,                */  1257, "x-baltic" },
00146   { "THAI",        /* FM_DEFN_THAI,     */  874,  "th" },
00147   { "SHIFTJIS",    /* FM_DEFN_KANA,     */  932,  "ja" },
00148   { "GB2312",      /* FM_DEFN_KANA,     */  1381, "zh-CN" },
00149   { "HANGEUL",     /* FM_DEFN_KANA,     */  949,  "ko" },
00150   { "CHINESEBIG5", /* FM_DEFN_KANA,     */  950,  "zh-TW" },
00151   { "JOHAB",       /* FM_DEFN_KANA,     */  1361, "ko-XXX", }
00152 };
00153 
00154 
00155 /**********************************************************
00156     nsFontMetricsOS2
00157  **********************************************************/    
00158 
00159 static void
00160 FreeGlobals(void)
00161 {
00162   gInitialized = 0;
00163 
00164   NS_IF_RELEASE(gPref);
00165   NS_IF_RELEASE(gUsersLocale);
00166   NS_IF_RELEASE(gSystemLocale);
00167   NS_IF_RELEASE(gUserDefined);
00168 #ifdef USE_FREETYPE
00169   if (nsFontMetricsOS2::gUseFTFunctions) {
00170     // disable the freetype engine
00171     nsFontMetricsOS2FT::pfnFt2EnableFontEngine(FALSE);
00172 
00173     NS_IF_RELEASE(gJA);
00174     NS_IF_RELEASE(gKO);
00175     NS_IF_RELEASE(gZHTW);
00176     NS_IF_RELEASE(gZHCN);
00177     NS_IF_RELEASE(gZHHK);
00178   }
00179 #endif /* use_freetype */
00180 
00181   if (nsFontMetricsOS2::gGlobalFonts) {
00182     nsFontMetricsOS2::gGlobalFonts->Clear();
00183     delete nsFontMetricsOS2::gGlobalFonts;
00184     nsFontMetricsOS2::gGlobalFonts = nsnull;
00185   }
00186 
00187   if (nsFontMetricsOS2::gFamilyNames) {
00188     PL_HashTableDestroy(nsFontMetricsOS2::gFamilyNames);
00189     nsFontMetricsOS2::gFamilyNames = nsnull;
00190   }
00191 }
00192 
00193 class nsFontCleanupObserver : public nsIObserver {
00194 public:
00195   NS_DECL_ISUPPORTS
00196   NS_DECL_NSIOBSERVER
00197 
00198   nsFontCleanupObserver() { }
00199   virtual ~nsFontCleanupObserver() {}
00200 };
00201 
00202 NS_IMPL_ISUPPORTS1(nsFontCleanupObserver, nsIObserver)
00203 
00204 NS_IMETHODIMP nsFontCleanupObserver::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
00205 {
00206   if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
00207     FreeGlobals();
00208   }
00209   return NS_OK;
00210 }
00211 
00212 static nsFontCleanupObserver *gFontCleanupObserver;
00213 
00214 static nsresult
00215 InitGlobals(void)
00216 {
00217   CallGetService(kPrefCID, &gPref);
00218   if (!gPref) {
00219     FreeGlobals();
00220     return NS_ERROR_FAILURE;
00221   }
00222 
00223   nsCOMPtr<nsILanguageAtomService> langService;
00224   langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
00225   if (langService) {
00226     NS_IF_ADDREF(gUsersLocale = langService->GetLocaleLanguageGroup());
00227   }
00228   if (!gUsersLocale) {
00229     gUsersLocale = NS_NewAtom("x-western");
00230   }
00231   if (!gUsersLocale) {
00232     FreeGlobals();
00233     return NS_ERROR_OUT_OF_MEMORY;
00234   }
00235 
00236   UINT cp = ::WinQueryCp(HMQ_CURRENT);
00237   for (int i = 1; i < eCharset_COUNT; ++i) {
00238     if (gCharsetInfo[i].mCodePage == cp) {
00239       gSystemLocale = NS_NewAtom(gCharsetInfo[i].mLangGroup);
00240       break;
00241     }
00242   }
00243   if (!gSystemLocale) {
00244     gSystemLocale = gUsersLocale;
00245     NS_ADDREF(gSystemLocale);
00246   }
00247 
00248   gUserDefined = NS_NewAtom(USER_DEFINED);
00249   if (!gUserDefined) {
00250     FreeGlobals();
00251     return NS_ERROR_OUT_OF_MEMORY;
00252   }
00253 #ifdef USE_FREETYPE
00254   if (nsFontMetricsOS2::gUseFTFunctions) {
00255     // enable the freetype engine
00256     nsFontMetricsOS2FT::pfnFt2EnableFontEngine(TRUE);
00257 
00258     gJA = NS_NewAtom("ja");
00259     if (!gJA) {
00260       FreeGlobals();
00261       return NS_ERROR_OUT_OF_MEMORY;
00262     }
00263     gKO = NS_NewAtom("ko");
00264     if (!gKO) {
00265       FreeGlobals();
00266       return NS_ERROR_OUT_OF_MEMORY;
00267     }
00268     gZHCN = NS_NewAtom("zh-CN");
00269     if (!gZHCN) {
00270       FreeGlobals();
00271       return NS_ERROR_OUT_OF_MEMORY;
00272     }
00273     gZHTW = NS_NewAtom("zh-TW");
00274     if (!gZHTW) {
00275       FreeGlobals();
00276       return NS_ERROR_OUT_OF_MEMORY;
00277     }
00278     gZHHK = NS_NewAtom("zh-HK");
00279     if (!gZHHK) {
00280       FreeGlobals();
00281       return NS_ERROR_OUT_OF_MEMORY;
00282     }
00283   }
00284 #endif /* use_freetype */
00285 
00286   // Not all versions of OS/2 support the 1386 and 943 codepages, so by default
00287   // we use 1381 and 932 (see the declaration of gCharsetInfo above).
00288   // Here, we check to see if the OS supports the newer codepages, and if so,
00289   // use them.
00290   ULONG numCP = ::WinQueryCpList((HAB)0, 0, NULL);
00291   if (numCP > 0) {
00292      ULONG * pCPList = (ULONG*)nsMemory::Alloc(numCP*sizeof(ULONG));
00293      if (::WinQueryCpList((HAB)0, numCP, pCPList)) {
00294         for (PRUint32 i = 0; i < numCP; i++) {
00295            if (pCPList[i] == 1386) {
00296               gCharsetInfo[11].mCodePage = 1386;
00297            } else if (pCPList[i] == 943) {
00298               gCharsetInfo[10].mCodePage = 943;
00299            }
00300         }
00301      }
00302      nsMemory::Free(pCPList);
00303   }
00304 
00305   gSystemCodePage = ::WinQueryCp(HMQ_CURRENT);
00306 
00307   nsresult res;
00308 
00309   if (!nsFontMetricsOS2::gGlobalFonts) {
00310     res = nsFontMetricsOS2::InitializeGlobalFonts();
00311     if (NS_FAILED(res)) {
00312       FreeGlobals();
00313       return res;
00314     }
00315   }
00316 
00317   res = gPref->GetBoolPref("browser.display.substitute_vector_fonts",
00318                                     &nsFontMetricsOS2::gSubstituteVectorFonts);
00319   NS_ASSERTION( NS_SUCCEEDED(res), "Could not get pref 'browser.display.substitute_vector_fonts'" );
00320 
00321   //register an observer to take care of cleanup
00322   gFontCleanupObserver = new nsFontCleanupObserver();
00323   NS_ASSERTION(gFontCleanupObserver, "failed to create observer");
00324   if (gFontCleanupObserver) {
00325     // register for shutdown
00326     nsresult rv;
00327     nsCOMPtr<nsIObserverService> observerService(do_GetService("@mozilla.org/observer-service;1", &rv));
00328     if (NS_SUCCEEDED(rv)) {
00329       rv = observerService->AddObserver(gFontCleanupObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
00330     }    
00331   }
00332   gInitialized = 1;
00333 
00334   return NS_OK;
00335 }
00336 
00337 #ifdef DEBUG_FONT_STRUCT_ALLOCS
00338 unsigned long nsFontMetricsOS2::mRefCount = 0;
00339 #endif
00340 
00341 nsFontMetricsOS2::nsFontMetricsOS2()
00342 {
00343 #ifdef DEBUG_FONT_STRUCT_ALLOCS
00344   mRefCount++;
00345   printf("+++ nsFontMetricsOS2 total = %d\n", mRefCount);
00346 #endif
00347 }
00348 
00349 nsFontMetricsOS2::~nsFontMetricsOS2()
00350 {
00351   mFontHandle = nsnull; // released below
00352   mUnicodeFont = nsnull; // release below
00353   mWesternFont = nsnull; // release below
00354 
00355   for (PRInt32 i = mLoadedFonts.Count()-1; i >= 0; --i) {
00356     delete (nsFontOS2*)mLoadedFonts[i];
00357   }
00358   mLoadedFonts.Clear();
00359 
00360   if (mDeviceContext) {
00361     // Notify our device context that owns us so that it can update its font cache
00362     mDeviceContext->FontMetricsDeleted(this);
00363     mDeviceContext = nsnull;
00364   }
00365 #ifdef DEBUG_FONT_STRUCT_ALLOCS
00366   mRefCount--;
00367   printf("--- nsFontMetricsOS2 total = %d\n", mRefCount);
00368 #endif
00369 }
00370 
00371 NS_IMPL_ISUPPORTS1(nsFontMetricsOS2, nsIFontMetrics)
00372 
00373 NS_IMETHODIMP
00374 nsFontMetricsOS2::Init( const nsFont &aFont,  nsIAtom* aLangGroup,
00375                         nsIDeviceContext *aContext)
00376 {
00377   nsresult res;
00378   if (!gInitialized) {
00379     res = InitGlobals();
00380     if (NS_FAILED(res)) {
00381       return res;
00382     }
00383   }
00384 
00385   mFont = aFont;
00386   mLangGroup = aLangGroup;
00387 
00388   //don't addref this to avoid circular refs
00389   mDeviceContext = (nsDeviceContextOS2 *) aContext;
00390   return RealizeFont();
00391 }
00392 
00393 NS_IMETHODIMP
00394 nsFontMetricsOS2::Destroy()
00395 {
00396   mDeviceContext = nsnull;
00397   return NS_OK;
00398 }
00399 
00400 // Utility; delete [] when done.
00401 static FONTMETRICS* getMetrics( long &lFonts, PCSZ facename, HPS hps)
00402 {
00403    LONG lWant = 0;
00404    lFonts = GFX (::GpiQueryFonts(hps, QF_PUBLIC | QF_PRIVATE,
00405                                  facename, &lWant, 0, 0),
00406                  GPI_ALTERROR);
00407    if (!lFonts) {
00408       return NULL;
00409    }
00410    FONTMETRICS* pMetrics = (FONTMETRICS*)nsMemory::Alloc(lFonts * sizeof(FONTMETRICS));
00411 
00412    GFX (::GpiQueryFonts(hps, QF_PUBLIC | QF_PRIVATE, facename,
00413                         &lFonts, sizeof (FONTMETRICS), pMetrics),
00414         GPI_ALTERROR);
00415 
00416    return pMetrics;
00417 }
00418 
00419 /* For a vector font, we create a charbox based directly on mFont.size, so
00420  * we don't need to calculate anything here.  For image fonts, though, we
00421  * want to find the closest match for the given mFont.size.
00422  */
00423 nsFontOS2*
00424 nsFontMetricsOS2::SetFontHandle(HPS aPS, GlobalFontEntry* aEntry,
00425                                 nsMiniMetrics* aMetrics, PRBool aDoFakeEffects)
00426 {
00427   nsFontOS2* font;
00428 #ifdef USE_FREETYPE
00429   if (gUseFTFunctions) {
00430     font = new nsFontOS2FT();
00431   } else
00432 #endif
00433   {
00434     font = new nsFontOS2();
00435   }
00436 
00437   strcpy(font->mFattrs.szFacename, aMetrics->szFacename);
00438   if (aMetrics->fsDefn & FM_DEFN_OUTLINE ||
00439       !mDeviceContext->SupportsRasterFonts()) {
00440     font->mFattrs.fsFontUse = FATTR_FONTUSE_OUTLINE |
00441                                FATTR_FONTUSE_TRANSFORMABLE;
00442   }
00443   if (aMetrics->fsType & FM_TYPE_MBCS)
00444     font->mFattrs.fsType |= FATTR_TYPE_MBCS;
00445   if (aMetrics->fsType & FM_TYPE_DBCS)
00446     font->mFattrs.fsType |= FATTR_TYPE_DBCS;
00447 
00448   if (aDoFakeEffects) {
00449     // fake the effects
00450     if (mFont.weight > NS_FONT_WEIGHT_NORMAL)
00451       font->mFattrs.fsSelection |= FATTR_SEL_BOLD;
00452     if (mFont.style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE))
00453       font->mFattrs.fsSelection |= FATTR_SEL_ITALIC;
00454   }
00455 
00456 #ifdef USE_FREETYPE
00457   // We only want to set the codepage in the freetype case if the codepage for
00458   // the font is 65400 (a symbol font).
00459   if (!gUseFTFunctions || aEntry->mCodePage == 65400)
00460 #endif
00461     font->mFattrs.usCodePage = aEntry->mCodePage;
00462 
00463 #ifdef PERF_HASGLYPH_CHAR_MAP
00464   if (gUseFTFunctions) {
00465     if (aEntry->mHaveCheckedCharMap == nsnull) {
00466       aEntry->mHaveCheckedCharMap = (PRUint32*)calloc(UCS2_MAP_LEN, sizeof(PRUint32));
00467       aEntry->mRepresentableCharMap = (PRUint32*)calloc(UCS2_MAP_LEN, sizeof(PRUint32));
00468     }
00469     font->mHaveCheckedCharMap = aEntry->mHaveCheckedCharMap;
00470     font->mRepresentableCharMap = aEntry->mRepresentableCharMap;
00471   }
00472 #endif
00473 
00474   float app2dev, reqEmHeight;
00475   app2dev = mDeviceContext->AppUnitsToDevUnits();
00476   reqEmHeight = mFont.size * app2dev;
00477 
00478   FATTRS* fattrs = &(font->mFattrs);
00479   if (fattrs->fsFontUse == 0)  // if image font
00480   {
00481     long lFonts = 0;
00482     FONTMETRICS* pMetrics = getMetrics( lFonts, fattrs->szFacename, aPS);
00483 
00484     int reqPointSize = NSTwipsToIntPoints(mFont.size);
00485     int browserRes = mDeviceContext->GetDPI();
00486 
00487     int minSize = 99, maxSize = 0, curEmHeight = 0;
00488     for (int i = 0; i < lFonts; i++)
00489     {
00490       // If we are asked for a specific point size for which we have an
00491       // appropriate font, use it.  This avoids us choosing an incorrect
00492       // size due to rounding issues
00493       if (pMetrics[i].sYDeviceRes == browserRes &&
00494           pMetrics[i].sNominalPointSize / 10 == reqPointSize)
00495       {
00496         // image face found fine, set required size in fattrs.
00497         curEmHeight = pMetrics[i].lEmHeight;
00498         fattrs->lMaxBaselineExt = pMetrics[i].lMaxBaselineExt;
00499         fattrs->lAveCharWidth = pMetrics[i].lAveCharWidth;
00500         minSize = maxSize = pMetrics[i].lEmHeight;
00501         break;
00502       }
00503       else
00504       {
00505         if (fabs(pMetrics[i].lEmHeight - reqEmHeight) < 
00506             fabs(curEmHeight - reqEmHeight))
00507         {
00508           curEmHeight = pMetrics[i].lEmHeight;
00509           fattrs->lMaxBaselineExt = pMetrics[i].lMaxBaselineExt;
00510           fattrs->lAveCharWidth = pMetrics[i].lAveCharWidth;
00511         }
00512         else if (fabs(pMetrics[i].lEmHeight - reqEmHeight) == 
00513                  fabs(curEmHeight - reqEmHeight))
00514         {
00515           if ((pMetrics[i].lEmHeight) > curEmHeight)
00516           {
00517             curEmHeight = pMetrics[i].lEmHeight;
00518             fattrs->lMaxBaselineExt = pMetrics[i].lMaxBaselineExt;
00519             fattrs->lAveCharWidth = pMetrics[i].lAveCharWidth;
00520           }
00521         }
00522       }
00523       
00524       // record the min/max point size available for given font
00525       if (pMetrics[i].lEmHeight > maxSize)
00526         maxSize = pMetrics[i].lEmHeight;
00527       if (pMetrics[i].lEmHeight < minSize)
00528         minSize = pMetrics[i].lEmHeight;
00529     }
00530 
00531     nsMemory::Free(pMetrics);
00532     
00533     // Enable font substitution if the requested size is outside of the range
00534     //  of available sizes by more than 3
00535     if (reqEmHeight < minSize - 3 ||
00536         reqEmHeight > maxSize + 3)
00537     {
00538       // If the browser.display.substitute_vector_fonts pref is set, then we
00539       //  exchange Times New Roman for Tms Rmn and Helvetica for Helv if the 
00540       //  requested points size is less than the minimum or more than the 
00541       //  maximum point size available for Tms Rmn and Helv.
00542       nsAutoString alias;
00543       if (gSubstituteVectorFonts &&
00544           GetVectorSubstitute(aPS, aEntry->GetKey(), alias))
00545       {
00546         strcpy(fattrs->szFacename, NS_LossyConvertUCS2toASCII(alias).get());
00547         fattrs->fsFontUse = FATTR_FONTUSE_OUTLINE | FATTR_FONTUSE_TRANSFORMABLE;
00548         fattrs->fsSelection &= ~(FM_SEL_BOLD | FM_SEL_ITALIC);
00549       }
00550     }
00551     
00552     if (fattrs->fsFontUse == 0) {    // if still image font
00553       reqEmHeight = curEmHeight;
00554     }
00555   }
00556 
00557    // Add effects
00558   if (mFont.decorations & NS_FONT_DECORATION_UNDERLINE) {
00559     fattrs->fsSelection |= FATTR_SEL_UNDERSCORE;
00560   }
00561   if (mFont.decorations & NS_FONT_DECORATION_LINE_THROUGH) {
00562     fattrs->fsSelection |= FATTR_SEL_STRIKEOUT;
00563   }
00564 
00565 #ifdef USE_FREETYPE
00566   if (!gUseFTFunctions)
00567 #endif
00568   {
00569      // Encoding:
00570      //  There doesn't seem to be any encoding stuff yet, so guess.
00571      //  (XXX unicode hack; use same codepage as converter!)
00572     const char* langGroup;
00573     mLangGroup->GetUTF8String(&langGroup);
00574     for (int j=0; j < eCharset_COUNT; j++)
00575     {
00576       if (langGroup[0] == gCharsetInfo[j].mLangGroup[0])
00577       {
00578         if (!strcmp(langGroup, gCharsetInfo[j].mLangGroup))
00579         {
00580           mConvertCodePage = gCharsetInfo[j].mCodePage;
00581           break;
00582         }
00583       }
00584     }
00585   
00586     // Symbols fonts must be created with codepage 65400,
00587     // so use 65400 for the fattrs codepage. We still do
00588     // conversions with the charset codepage
00589     if (fattrs->usCodePage != 65400) {
00590       fattrs->usCodePage = mConvertCodePage;
00591     }
00592   }
00593 
00594   // set up the charbox;  set for image fonts also, in case we need to
00595   //  substitute a vector font later on (for UTF-8, etc)
00596   long lFloor = NSToIntFloor(reqEmHeight); 
00597   font->mCharbox.cx = MAKEFIXED(lFloor, (reqEmHeight - (float)lFloor) * 65536.0f);
00598   font->mCharbox.cy = font->mCharbox.cx;
00599 
00600   return font;
00601 }
00602 
00603 /* aName is a font family name.  see if fonts of that family exist
00604  *  if so, return font structure with family name
00605  */
00606 nsFontOS2*
00607 nsFontMetricsOS2::LoadFont(HPS aPS, const nsAString& aFontname)
00608 {
00609   nsFontOS2* font = nsnull;
00610 
00611    // set style flags
00612   PRBool bBold = mFont.weight > NS_FONT_WEIGHT_NORMAL;
00613   PRBool bItalic = (mFont.style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE));
00614   USHORT flags = bBold ? FM_SEL_BOLD : 0;
00615   flags |= bItalic ? FM_SEL_ITALIC : 0;
00616 
00617   // always pass vector fonts to the printer
00618   nsAutoString fontptr;
00619   if (mDeviceContext->SupportsRasterFonts() ||
00620       !GetVectorSubstitute(aPS, aFontname, fontptr))
00621   {
00622     fontptr = aFontname;
00623   }
00624 
00625   GlobalFontEntry* globalEntry = gGlobalFonts->GetEntry(fontptr);
00626   if (globalEntry) {
00627     nsMiniMetrics* metrics = globalEntry->mMetrics;
00628     nsMiniMetrics* plainFont = nsnull;
00629     while (metrics) {
00630       if ((metrics->fsSelection & (FM_SEL_ITALIC | FM_SEL_BOLD)) == flags) {
00631         font = SetFontHandle(aPS, globalEntry, metrics, PR_FALSE);
00632         break;
00633       }
00634 
00635       // Save ref to 'plain' font (non-bold, non-italic)
00636       if (!plainFont && !(metrics->fsSelection & (FM_SEL_ITALIC | FM_SEL_BOLD)))
00637         plainFont = metrics;
00638 
00639       metrics = metrics->mNext;
00640     }
00641 
00642     // A font of family "familyname" and with the applied effects (bold &
00643     // italic) was not found.  Therefore, just look for a 'regular' font
00644     // (that is not italic and not bold), and then have the
00645     // system simulate the appropriate effects (see RealizeFont()).
00646     if (!font && plainFont) {
00647       font = SetFontHandle(aPS, globalEntry, plainFont, PR_TRUE);
00648     }
00649   }
00650 
00651   if (!font) {
00652     // If a font was not found, then maybe "familyname" is really a face name.
00653     // See if a font with that facename exists on system and load the font.
00654     long lFonts = 0;
00655     nsAutoCharBuffer facename;
00656     PRInt32 len;
00657     WideCharToMultiByte(0, PromiseFlatString(aFontname).get(),
00658                         aFontname.Length(), facename, len);
00659     FONTMETRICS* pMetrics = getMetrics(lFonts, facename.get(), aPS);
00660 
00661     if (lFonts > 0) {
00662       nsAutoChar16Buffer familyname;
00663       MultiByteToWideChar(0, pMetrics[0].szFamilyname,
00664                           strlen(pMetrics[0].szFamilyname), familyname, len);
00665       nsAutoString name(familyname.get());
00666       GlobalFontEntry* globalEntry = gGlobalFonts->GetEntry(name);
00667       if (globalEntry) {
00668         // Look through metrics for one that matches given facename
00669         nsMiniMetrics* metrics = globalEntry->mMetrics;
00670         while (metrics) {
00671           if (stricmp(metrics->szFacename, facename.get()) == 0) {
00672             font = SetFontHandle(aPS, globalEntry, metrics, PR_TRUE);
00673             break;
00674           }
00675           metrics = metrics->mNext;
00676         }
00677       }
00678     }
00679     nsMemory::Free(pMetrics);
00680   }
00681 
00682   if (font) {
00683     mLoadedFonts.AppendElement(font);
00684   }
00685 
00686   return font;
00687 }
00688 
00689 typedef struct nsFontFamilyName
00690 {
00691   char* mName;
00692   char* mWinName;
00693 } nsFontFamilyName;
00694 
00695 static nsFontFamilyName gBadDBCSFontMapping[] =
00696 {
00697   { "\xB7\xC2\xCB\xCE\xB3\xA3\xB9\xE6", "IBM Fang Song SC"},
00698   { "\xBA\xDA\xCC\xE5\xB3\xA3\xB9\xE6", "IBM Hei SC"},
00699   { "\xBF\xAC\xCC\xE5\xB3\xA3\xB9\xE6", "IBM Kai SC"},
00700   { "\x5B\x8B\x4F\x53\x5E\x38\x89\xC4", "IBM Song SC"},
00701   { "\x91\x76\x91\xCC\x8F\xED",         "IBM Song SC"},
00702 
00703   { "@\xB7\xC2\xCB\xCE\xB3\xA3\xB9\xE6", "@IBM Fang Song SC"},
00704   { "@\xBA\xDA\xCC\xE5\xB3\xA3\xB9\xE6", "@IBM Hei SC"},
00705   { "@\xBF\xAC\xCC\xE5\xB3\xA3\xB9\xE6", "@IBM Kai SC"},
00706   { "@\x5B\x8B\x4F\x53\x5E\x38\x89\xC4", "@IBM Song SC"},
00707   { "@\x91\x76\x91\xCC\x8F\xED",         "@IBM Song SC"},
00708 
00709   { "\xAC\xD1\xFA\x80\xFA\xBD\x8F\x82",  "MOEKai TC"},
00710   { "\xBC\xD0\xB7\xC7\xB7\xA2\xC5\xE9",  "MOEKai TC"},
00711   { "\xAC\xD1\xFA\x80\x15\xA7\x8F\x82",  "MOESung TC"},
00712   { "\xBC\xD0\xB7\xC7\xA7\xBA\xC5\xE9",  "MOESung TC"},
00713   { "\xCF\xCF\x14\xB6\x8F\x82",          "Hei TC"},
00714   { "\xA4\xA4\xB6\xC2\xC5\xE9",          "Hei TC"},
00715 
00716   { "@\xAC\xD1\xFA\x80\xFA\xBD\x8F\x82",  "@MOEKai TC"},
00717   { "@\xBC\xD0\xB7\xC7\xB7\xA2\xC5\xE9",  "@MOEKai TC"},
00718   { "@\xAC\xD1\xFA\x80\x15\xA7\x8F\x82",  "@MOESong TC"},
00719   { "@\xBC\xD0\xB7\xC7\xA7\xBA\xC5\xE9",  "@MOESong TC"},
00720   { "@\xCF\xCF\x14\xB6\x8F\x82",          "@Hei TC"},
00721   { "@\xA4\xA4\xB6\xC2\xC5\xE9",          "@Hei TC"},
00722   { nsnull, nsnull }
00723 };
00724 
00725 #ifdef DEBUG
00726 PR_STATIC_CALLBACK(PLDHashOperator)
00727 DebugOutputEnumFunc(GlobalFontEntry* aEntry, void* aData)
00728 {
00729   printf("---------------------------------------------------------------------\n");
00730   printf(" [[]] %s\n", NS_LossyConvertUCS2toASCII(aEntry->GetKey()).get());
00731   nsMiniMetrics* metrics = aEntry->mMetrics;
00732   while (metrics) {
00733     printf("  %32s", metrics->szFacename);
00734 
00735     if (metrics->fsDefn & FM_DEFN_OUTLINE)
00736       printf(" : vector");
00737     else
00738       printf(" : bitmap");
00739       
00740     if (metrics->fsSelection & FM_SEL_BOLD)
00741       printf(" : bold");
00742     else
00743       printf(" :     ");
00744       
00745     if (metrics->fsSelection & FM_SEL_ITALIC)
00746       printf(" : italic");
00747     else
00748       printf(" :       ");
00749 
00750     if (metrics->fsType & FM_TYPE_DBCS ||
00751         metrics->fsType & FM_TYPE_MBCS)
00752       printf(" : M/DBCS");
00753     else
00754       printf(" :       ");
00755 
00756     printf(" : cp=%5d\n", aEntry->mCodePage );
00757     
00758     metrics = metrics->mNext;
00759   }
00760 
00761   return PL_DHASH_NEXT;
00762 }
00763 #endif
00764 
00765 nsresult
00766 nsFontMetricsOS2::InitializeGlobalFonts()
00767 {
00768   gGlobalFonts = new nsTHashtable<GlobalFontEntry>;
00769   if (!gGlobalFonts->Init(64))
00770     return NS_ERROR_OUT_OF_MEMORY;
00771 
00772   HPS ps = ::WinGetScreenPS(HWND_DESKTOP);
00773   LONG lRemFonts = 0, lNumFonts;
00774   lNumFonts = GFX (::GpiQueryFonts(ps, QF_PUBLIC, NULL, &lRemFonts, 0, 0),
00775                    GPI_ALTERROR);
00776   FONTMETRICS* pFontMetrics = (PFONTMETRICS) nsMemory::Alloc(lNumFonts * sizeof(FONTMETRICS));
00777   lRemFonts = GFX (::GpiQueryFonts(ps, QF_PUBLIC, NULL, &lNumFonts,
00778                                    sizeof (FONTMETRICS), pFontMetrics),
00779                    GPI_ALTERROR);
00780   ::WinReleasePS(ps);
00781 
00782   for (int i = 0; i < lNumFonts; i++) {
00783     FONTMETRICS* font = &(pFontMetrics[i]);
00784 
00785     // The discrepencies between the Courier bitmap and outline fonts are
00786     // too much to deal with, so we only use the outline font
00787     if (strcmp(font->szFamilyname, "Courier") == 0 &&
00788         !(font->fsDefn & FM_DEFN_OUTLINE)) {
00789       continue;
00790     }
00791 
00792     // The facenames for Roman are "Tms Rmn...", for Swiss "Helv...".  This
00793     // conflicts with the actual "Tms Rmn" and "Helv" font families.  So, we
00794     // skip over these.
00795     if (strcmp(pFontMetrics[i].szFamilyname, "Roman") == 0 ||
00796         strcmp(pFontMetrics[i].szFamilyname, "Swiss") == 0) {
00797       continue;
00798     }
00799 
00800      // Problem:  OS/2 has many non-standard fonts that do not follow the
00801      //           normal Family-name/Face-name conventions (i.e. 'foo',
00802      //           'foo bold', 'foo italic', 'foo bold italic').  This is
00803      //           especially true for DBCS fonts (i.e. the 'WarpSans' family
00804      //           can contain the 'WarpSans', 'WarpSans Bold', and 'WarpSans
00805      //           Combined' faces).
00806      // Solution: Unfortunately, there is no perfect way to handle this.  After
00807      //           many attempts, we will attempt to remedy the situation by
00808      //           searching the Facename for certain indicators ('bold',
00809      //           'italic', 'oblique', 'regular').  If the Facename contains
00810      //           one of these indicators, then we will create the sort key
00811      //           based on the Familyname.  Otherwise, use the Facename.
00812     char* f;
00813     if (PL_strcasestr(font->szFacename, "bold") != nsnull ||
00814         PL_strcasestr(font->szFacename, "italic") != nsnull ||
00815         PL_strcasestr(font->szFacename, "oblique") != nsnull ||
00816         PL_strcasestr(font->szFacename, "regular") != nsnull ||
00817         PL_strcasestr(font->szFacename, "-normal") != nsnull)
00818     {
00819       f = NS_STATIC_CAST(char*, font->szFamilyname);
00820     } else {
00821       f = NS_STATIC_CAST(char*, font->szFacename);
00822     }
00823     nsAutoChar16Buffer fontname;
00824     PRInt32 len;
00825     MultiByteToWideChar(0, f, strlen(f), fontname, len);
00826     nsAutoString fontptr(fontname.get());
00827 
00828     // The fonts in gBadDBCSFontMapping do not display well in non-Chinese
00829     //   systems.  Map them to a more intelligible name.
00830     if (font->fsType & FM_TYPE_DBCS)
00831     {
00832       if ((gSystemCodePage != 1386) &&
00833           (gSystemCodePage != 1381) &&
00834           (gSystemCodePage != 950))
00835       {
00836         for (int i = 0; gBadDBCSFontMapping[i].mName != nsnull; i++) {
00837           if (strcmp(f, gBadDBCSFontMapping[i].mName) == 0)
00838           {
00839             CopyASCIItoUCS2(nsDependentCString(gBadDBCSFontMapping[i].mWinName),
00840                             fontptr);
00841             break;
00842           }
00843         }
00844       }
00845     }
00846 
00847     // Create entry for family name, or retrieve already created entry
00848     GlobalFontEntry* globalEntry = gGlobalFonts->PutEntry(fontptr);
00849     if (!globalEntry)
00850       return NS_ERROR_OUT_OF_MEMORY;
00851 
00852     // Init the nsMiniMetrics structure...
00853     nsMiniMetrics* metrics = new nsMiniMetrics;
00854     strcpy(metrics->szFacename, font->szFacename);
00855     metrics->fsType = font->fsType;
00856     metrics->fsDefn = font->fsDefn;
00857     metrics->fsSelection = font->fsSelection;
00858      // Set the FM_SEL_BOLD flag in fsSelection.  This makes the check for
00859      // bold and italic much easier in LoadFont
00860     if (font->usWeightClass > 5)
00861       metrics->fsSelection |= FM_SEL_BOLD;
00862 
00863     // The 'Lucida' set of fonts does not set the bold flag correctly on OS/2,
00864     // so we'll just set it properly.
00865     if (strncmp(font->szFamilyname, "Lucida", 6) == 0 &&
00866         PL_strcasestr(font->szFacename, "bold") != nsnull) {
00867       metrics->fsSelection |= FM_SEL_BOLD;
00868     }
00869 
00870     // ... and add it to globalEntry
00871     metrics->mNext = globalEntry->mMetrics;
00872     globalEntry->mMetrics = metrics;
00873     globalEntry->mCodePage = font->usCodePage;
00874   }
00875 
00876 #ifdef DEBUG_pedemonte
00877   gGlobalFonts->EnumerateEntries(DebugOutputEnumFunc, nsnull);
00878   fflush(stdout);
00879 #endif
00880 
00881   nsMemory::Free(pFontMetrics);
00882 
00883   return NS_OK;
00884 }
00885 
00886 nsFontOS2*
00887 nsFontMetricsOS2::FindGlobalFont(HPS aPS, PRUint32 aChar)
00888 {
00889   nsFontOS2* fh = nsnull;
00890   nsAutoString fontname;
00891   if (!IsDBCS())
00892     fontname.AssignLiteral("Helv");
00893   else
00894     fontname.AssignLiteral("Helv Combined");
00895   fh = LoadFont(aPS, fontname);
00896   NS_ASSERTION(fh, "Couldn't load default font - BAD things are happening");
00897   return fh;
00898 }
00899 
00900 nsFontOS2*
00901 nsFontMetricsOS2::FindUserDefinedFont(HPS aPS, PRUint32 aChar)
00902 {
00903   if (mIsUserDefined) {
00904     // the user-defined font is always loaded as the first font
00905     nsFontOS2* font = LoadFont(aPS, mUserDefined);
00906     mIsUserDefined = PR_FALSE;
00907     if (font && font->HasGlyph(aPS, aChar)) {
00908       return font;
00909     }
00910   }
00911   return nsnull;
00912 }
00913 
00914 static nsFontFamilyName gFamilyNameTable[] =
00915 {
00916 #ifdef MOZ_MATHML
00917   { "-moz-math-text",   "Times New Roman" },
00918   { "-moz-math-symbol", "Symbol" },
00919 #endif
00920   { "times",           "Times New Roman" },
00921   { "times roman",     "Times New Roman" },
00922 
00923   { nsnull, nsnull }
00924 };
00925 
00926 static nsFontFamilyName gFamilyNameTableDBCS[] =
00927 {
00928 #ifdef MOZ_MATHML
00929   { "-moz-math-text",   "Times New Roman" },
00930   { "-moz-math-symbol", "Symbol" },
00931 #endif
00932   { "times",           "Times New Roman" },
00933   { "times roman",     "Times New Roman" },
00934   { "warpsans",        "WarpSans Combined" },
00935 
00936   { nsnull, nsnull }
00937 };
00938 
00939 /*-----------------------
00940 ** Hash table allocation
00941 **----------------------*/
00942 static PLHashNumber PR_CALLBACK
00943 HashKey(const void* aString)
00944 {
00945   const nsString* str = (const nsString*)aString;
00946   return (PLHashNumber) nsCRT::HashCode(str->get());
00947 }
00948 
00949 static PRIntn PR_CALLBACK
00950 CompareKeys(const void* aStr1, const void* aStr2)
00951 {
00952   return nsCRT::strcmp(((const nsString*) aStr1)->get(),
00953     ((const nsString*) aStr2)->get()) == 0;
00954 }
00955 
00956 PR_STATIC_CALLBACK(void*) familyname_AllocTable(void *pool, size_t size)
00957 {
00958   return nsMemory::Alloc(size);
00959 }
00960 
00961 PR_STATIC_CALLBACK(void) familyname_FreeTable(void *pool, void *item)
00962 {
00963   nsMemory::Free(item);
00964 }
00965 
00966 PR_STATIC_CALLBACK(PLHashEntry*) familyname_AllocEntry(void *pool, const void *key)
00967 {
00968   return PR_NEW(PLHashEntry);
00969 }
00970 
00971 PR_STATIC_CALLBACK(void) familyname_FreeEntry(void *pool, PLHashEntry *he, PRUint32 flag)
00972 {
00973   delete (nsString *) (he->value);
00974 
00975   if (flag == HT_FREE_ENTRY)  {
00976     delete (nsString *) (he->key);
00977     nsMemory::Free(he);
00978   }
00979 }
00980 
00981 PLHashAllocOps familyname_HashAllocOps = {
00982   familyname_AllocTable, familyname_FreeTable,
00983   familyname_AllocEntry, familyname_FreeEntry
00984 };
00985 
00986 PLHashTable*
00987 nsFontMetricsOS2::InitializeFamilyNames(void)
00988 {
00989   if (!gFamilyNames) {
00990     gFamilyNames = PL_NewHashTable( 0, HashKey, CompareKeys, nsnull,
00991                                     &familyname_HashAllocOps, nsnull );
00992     if (!gFamilyNames) {
00993       return nsnull;
00994     }
00995 
00996     nsFontFamilyName* f;
00997     if (!IsDBCS()) {
00998       f = gFamilyNameTable;
00999     } else {
01000       f = gFamilyNameTableDBCS;
01001     }
01002 
01003     while (f->mName) {
01004       nsString* name = new nsString;
01005       nsString* winName = new nsString;
01006       if (name && winName) {
01007         name->AssignWithConversion(f->mName);
01008         winName->AssignWithConversion(f->mWinName);
01009         if (PL_HashTableAdd(gFamilyNames, name, (void*) winName)) { 
01010           ++f;
01011           continue;
01012         }
01013       }
01014       // if we reach here, no FamilyName was added to the hashtable
01015       if (name) delete name;
01016       if (winName) delete winName;
01017       return nsnull;
01018     }
01019   }
01020 
01021   return gFamilyNames;
01022 }
01023 
01024 nsFontOS2*
01025 nsFontMetricsOS2::FindLocalFont(HPS aPS, PRUint32 aChar)
01026 {
01027   if (!gFamilyNames) {
01028     if (!InitializeFamilyNames()) {
01029       return nsnull;
01030     }
01031   }
01032   while (mFontsIndex < mFonts.Count()) {
01033     if (mFontsIndex == mGenericIndex) {
01034       return nsnull;
01035     }
01036 
01037     nsString* name = mFonts.StringAt(mFontsIndex++);
01038     nsAutoString low(*name);
01039     ToLowerCase(low);
01040     nsString* winName = (nsString*) PL_HashTableLookup(gFamilyNames, &low);
01041     if (!winName) {
01042       winName = name;
01043     }
01044 #ifdef DEBUG_FONT_SELECTION
01045     printf(" FindLocalFont(): attempting to load %s\n",
01046            NS_LossyConvertUCS2toASCII(*winName).get());
01047 #endif
01048     nsFontOS2* font = LoadFont(aPS, *winName);
01049     if (font && font->HasGlyph(aPS, aChar)) {
01050       return font;
01051     }
01052   }
01053   return nsnull;
01054 }
01055 
01056 nsFontOS2*
01057 nsFontMetricsOS2::LoadGenericFont(HPS aPS, PRUint32 aChar, const nsAString& aName)
01058 {
01059   for (int i = mLoadedFonts.Count()-1; i >= 0; --i) {
01060     // woah, this seems bad
01061     const nsACString& fontName =
01062       nsDependentCString(((nsFontOS2*)mLoadedFonts[i])->mFattrs.szFacename);
01063     if (aName.Equals(NS_ConvertASCIItoUCS2(fontName),
01064                      nsCaseInsensitiveStringComparator()))
01065       return nsnull;
01066 
01067   }
01068 #ifdef DEBUG_FONT_SELECTION
01069   printf(" LoadGenericFont(): attempting to load %s\n",
01070          NS_LossyConvertUCS2toASCII(aName).get());
01071 #endif
01072   nsFontOS2* font = LoadFont(aPS, aName);
01073   if (font && font->HasGlyph(aPS, aChar)) {
01074     return font;
01075   }
01076   return nsnull;
01077 }
01078 
01079 struct GenericFontEnumContext
01080 {
01081   HPS               mPS;        // IN
01082   PRUint32          mChar;      // IN
01083   nsFontOS2        *mFont;      // OUT
01084   nsFontMetricsOS2 *mMetrics;   // IN
01085 };
01086 
01087 static PRBool
01088 GenericFontEnumCallback(const nsString& aFamily, PRBool aGeneric, void* aData)
01089 {
01090   GenericFontEnumContext* context = (GenericFontEnumContext*)aData;
01091   HPS ps = context->mPS;
01092   PRUint32 ch = context->mChar;
01093   nsFontMetricsOS2* metrics = context->mMetrics;
01094   context->mFont = metrics->LoadGenericFont(ps, ch, aFamily);
01095   if (context->mFont) {
01096     return PR_FALSE; // stop enumerating the list
01097   }
01098   return PR_TRUE; // don't stop
01099 }
01100 
01101 #define MAKE_FONT_PREF_KEY(_pref, _s0, _s1) \
01102  _pref.Assign(_s0); \
01103  _pref.Append(_s1);
01104 
01105 static void 
01106 AppendGenericFontFromPref(nsString& aFontname,
01107                           const char* aLangGroup,
01108                           const char* aGeneric)
01109 {
01110   nsresult res;
01111   nsCAutoString pref;
01112   nsXPIDLString value;
01113   nsCAutoString generic_dot_langGroup;
01114 
01115   generic_dot_langGroup.Assign(aGeneric);
01116   generic_dot_langGroup.Append('.');
01117   generic_dot_langGroup.Append(aLangGroup);
01118 
01119   // font.name.[generic].[langGroup]
01120   // the current user' selected font, it gives the first preferred font
01121   MAKE_FONT_PREF_KEY(pref, "font.name.", generic_dot_langGroup);
01122   res = gPref->CopyUnicharPref(pref.get(), getter_Copies(value));      
01123   if (NS_SUCCEEDED(res)) {
01124     if(!aFontname.IsEmpty())
01125       aFontname.Append((PRUnichar)',');
01126     aFontname.Append(value);
01127   }
01128 
01129   // font.name-list.[generic].[langGroup]
01130   // the pre-built list of default fonts, it gives alternative fonts
01131   MAKE_FONT_PREF_KEY(pref, "font.name-list.", generic_dot_langGroup);
01132   res = gPref->CopyUnicharPref(pref.get(), getter_Copies(value));      
01133   if (NS_SUCCEEDED(res)) {
01134     if(!aFontname.IsEmpty())
01135       aFontname.Append((PRUnichar)',');
01136     aFontname.Append(value);
01137   }
01138 }
01139 
01140 nsFontOS2*
01141 nsFontMetricsOS2::FindGenericFont(HPS aPS, PRUint32 aChar)
01142 {
01143   if (mTriedAllGenerics) {
01144     // don't bother anymore because mLoadedFonts[] already has all our generic fonts
01145     return nsnull;
01146   }
01147 
01148   // This is a nifty hook that we will use to just iterate over
01149   // the list of names using the callback mechanism of nsFont...
01150   nsFont font("", 0, 0, 0, 0, 0);
01151 
01152   if (mLangGroup) {
01153     const char* langGroup;
01154     mLangGroup->GetUTF8String(&langGroup);
01155   
01156     // x-unicode pseudo-langGroup should be the last resort to turn to.
01157     // That is, it should be refered to only when we don't  recognize 
01158     // |langGroup| specified by the authors of documents and  the 
01159     // determination of |langGroup| based  on Unicode range also fails 
01160     // in |FindPrefFont|. 
01161 
01162     if (!strcmp(langGroup, "x-unicode")) {
01163       mTriedAllGenerics = 1;
01164       return nsnull;
01165     }
01166 
01167     AppendGenericFontFromPref(font.name, langGroup, 
01168                               NS_ConvertUCS2toUTF8(mGeneric).get());
01169   }
01170 
01171   // Iterate over the list of names using the callback mechanism of nsFont...
01172   GenericFontEnumContext context = {aPS, aChar, nsnull, this};
01173   font.EnumerateFamilies(GenericFontEnumCallback, &context);
01174   if (context.mFont) { // a suitable font was found
01175     return context.mFont;
01176   }
01177 
01178   mTriedAllGenerics = 1;
01179   return nsnull;
01180 }
01181 
01182 nsFontOS2*
01183 nsFontMetricsOS2::FindPrefFont(HPS aPS, PRUint32 aChar)
01184 {
01185   if (mTriedAllPref) {
01186     // don't bother anymore because mLoadedFonts[] already has all our pref fonts
01187     return nsnull;
01188   }
01189   nsFont font("", 0, 0, 0, 0, 0);
01190   // Try the pref of the user's ui lang group
01191   // For example, if the ui language is Japanese, try pref from "ja"
01192   // Make localized build work better on other OS
01193   if (gUsersLocale != mLangGroup) {
01194     nsAutoString langGroup;
01195     gUsersLocale->ToString(langGroup);
01196     AppendGenericFontFromPref(font.name, 
01197                               NS_ConvertUCS2toUTF8(langGroup).get(), 
01198                               NS_ConvertUCS2toUTF8(mGeneric).get());
01199   }
01200   // Try the pref of the user's system lang group
01201   // For example, if the os language is Simplified Chinese, 
01202   // try pref from "zh-CN"
01203   // Make English build work better on other OS
01204   if ((gSystemLocale != mLangGroup) && (gSystemLocale != gUsersLocale)) {
01205     nsAutoString langGroup;
01206     gSystemLocale->ToString(langGroup);
01207     AppendGenericFontFromPref(font.name, 
01208                               NS_ConvertUCS2toUTF8(langGroup).get(), 
01209                               NS_ConvertUCS2toUTF8(mGeneric).get());
01210   }
01211 
01212   // Also try all the default pref fonts enlisted from other languages
01213   for (int i = 1; i < eCharset_COUNT; ++i) {
01214     nsIAtom* langGroup = NS_NewAtom(gCharsetInfo[i].mLangGroup); 
01215     if((gUsersLocale != langGroup) && (gSystemLocale != langGroup)) {
01216       AppendGenericFontFromPref(font.name, gCharsetInfo[i].mLangGroup, 
01217                                 NS_ConvertUCS2toUTF8(mGeneric).get());
01218     }
01219     NS_IF_RELEASE(langGroup);
01220   }
01221   GenericFontEnumContext context = {aPS, aChar, nsnull, this};
01222   font.EnumerateFamilies(GenericFontEnumCallback, &context);
01223   if (context.mFont) { // a suitable font was found
01224     return context.mFont;
01225   }
01226   mTriedAllPref = 1;
01227   return nsnull;
01228 }
01229 
01230 // returns family name of font that can display given char
01231 nsFontOS2*
01232 nsFontMetricsOS2::FindFont(HPS aPS, PRUint32 aChar)
01233 {
01234   nsFontOS2* font = FindUserDefinedFont(aPS, aChar);
01235   if (!font) {
01236     font = FindLocalFont(aPS, aChar);
01237     if (!font) {
01238       font = FindGenericFont(aPS, aChar);
01239       if (!font) {
01240         font = FindPrefFont(aPS, aChar);
01241         if (!font) {
01242           font = FindGlobalFont(aPS, aChar);
01243 #ifdef USE_FREETYPE
01244           if (!font) {
01245             font = FindSubstituteFont(aPS, aChar);
01246           }
01247 #endif
01248         }
01249       }
01250     }
01251   }
01252 #ifdef DEBUG_FONT_SELECTION
01253   if (font) {
01254     printf(" FindFont(): found font %s for char 0x%04x\n",
01255            font->mFattrs.szFacename, aChar);
01256   }
01257 #endif
01258   return font;
01259 }
01260 
01261 static PRBool
01262 FontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
01263 {
01264   nsFontMetricsOS2* metrics = (nsFontMetricsOS2*) aData;
01265 
01266 #ifdef USE_FREETYPE
01267   if (!nsFontMetricsOS2::gUseFTFunctions)
01268 #endif
01269   {
01270     /* Hack for Truetype on OS/2 - if it's Arial and not 1252 or 0, just get another font */
01271     if (aFamily.Find("Arial", IGNORE_CASE) != -1) {
01272        if (metrics->mConvertCodePage != 1252) {
01273           if ((metrics->mConvertCodePage == 0) &&
01274               (gSystemCodePage != 850) &&
01275               (gSystemCodePage != 437)) {
01276              return PR_TRUE; // don't stop
01277           }
01278        }
01279     }
01280   }
01281 
01282   metrics->mFonts.AppendString(aFamily);
01283   if (aGeneric) {
01284     metrics->mGeneric.Assign(aFamily);
01285     ToLowerCase(metrics->mGeneric);
01286     return PR_FALSE; // stop
01287   }
01288   ++metrics->mGenericIndex;
01289 
01290   return PR_TRUE; // don't stop
01291 }
01292 
01293 PRBool
01294 nsFontMetricsOS2::GetVectorSubstitute(HPS aPS, const nsAString& aFamilyname,
01295                                       nsAString& aAlias)
01296 {
01297   if (aFamilyname.EqualsLiteral("Tms Rmn")) {
01298     aAlias.AssignLiteral("Times New Roman");
01299   } else if (aFamilyname.EqualsLiteral("Helv")) {
01300     aAlias.AssignLiteral("Helvetica");
01301   }
01302 
01303   // When printing, substitute vector fonts for these common bitmap fonts
01304   if (!mDeviceContext->SupportsRasterFonts()) {
01305     if (aFamilyname.EqualsLiteral("System Proportional") ||
01306         aFamilyname.EqualsLiteral("WarpSans"))
01307     {
01308       aAlias.AssignLiteral("Helvetica");
01309     } else if (aFamilyname.EqualsLiteral("System Monospaced") ||
01310                aFamilyname.EqualsLiteral("System VIO"))
01311     {
01312       aAlias.AssignLiteral("Courier");
01313     }
01314   }
01315 
01316   if (aAlias.IsEmpty())
01317     return PR_FALSE;
01318   else
01319     return PR_TRUE;
01320 }
01321 
01322 nsresult
01323 nsFontMetricsOS2::RealizeFont()
01324 {
01325   nsresult  rv;
01326   HPS       ps = NULL;
01327 
01328   if (mDeviceContext->mPrintDC){
01329     ps = mDeviceContext->mPrintPS;
01330   } else {
01331     HWND win = (HWND)mDeviceContext->mWidget;
01332     ps = ::WinGetPS(win);
01333     if (!ps) {
01334       ps = ::WinGetPS(HWND_DESKTOP);
01335     }
01336   }
01337 
01338   mFont.EnumerateFamilies(FontEnumCallback, this);
01339 
01340   nsCAutoString pref;
01341   nsXPIDLString value;
01342 
01343   // set a fallback generic font if the font-family list didn't have one
01344   if (mGeneric.IsEmpty()) {
01345     const char* langGroup = nsnull;
01346     mLangGroup->GetUTF8String(&langGroup);
01347     pref.Assign("font.default.");
01348     pref.Append(langGroup);
01349     rv = gPref->CopyUnicharPref(pref.get(), getter_Copies(value));
01350     if (NS_SUCCEEDED(rv)) {
01351       mGeneric.Assign(value);
01352     }
01353     else {
01354       mGeneric.AssignLiteral("serif");
01355     }
01356   }
01357 
01358   if (mLangGroup.get() == gUserDefined) {
01359     // See if this is a special user-defined font encoding by checking:
01360     // font.name.[generic].x-user-def
01361     pref.Assign("font.name.");
01362     pref.AppendWithConversion(mGeneric);
01363     pref.Append(".x-user-def");
01364     rv = gPref->CopyUnicharPref(pref.get(), getter_Copies(value));
01365     if (NS_SUCCEEDED(rv)) {
01366       mUserDefined.Assign(value);
01367       mIsUserDefined = 1;
01368     }
01369   }
01370 
01371   nsFontOS2* font = FindFont(ps, 'a');
01372   NS_ASSERTION(font, "FindFont() returned null.  THIS IS BAD!");
01373   if (!font) {
01374     if (mDeviceContext->mPrintDC == NULL) {
01375       ::WinReleasePS(ps);
01376     }
01377     return NS_ERROR_FAILURE;
01378   }
01379 
01380   font->mConvertCodePage = mConvertCodePage;
01381 
01382    // Record font handle & record various font metrics to cache
01383   mFontHandle = font;
01384   CHK_SUCCESS (::GpiCreateLogFont(ps, 0, 1, &(font->mFattrs)), FONT_MATCH);
01385   font->SelectIntoPS( ps, 1 );
01386 
01387   FONTMETRICS fm;
01388   GFX (::GpiQueryFontMetrics(ps, sizeof(fm), &fm), FALSE);
01389   /* Due to a bug in OS/2 MINCHO, need to cast lInternalLeading */
01390   fm.lInternalLeading = (signed short)fm.lInternalLeading;
01391 
01392   float dev2app;
01393   dev2app = mDeviceContext->DevUnitsToAppUnits();
01394   
01395   mMaxAscent  = NSToCoordRound( (fm.lMaxAscender-1) * dev2app );
01396   mMaxDescent = NSToCoordRound( (fm.lMaxDescender+1) * dev2app );
01397   mFontHandle->mMaxAscent = mMaxAscent;
01398   mFontHandle->mMaxDescent = mMaxDescent;
01399 
01400   mInternalLeading = NSToCoordRound( fm.lInternalLeading * dev2app );
01401   mExternalLeading = NSToCoordRound( fm.lExternalLeading * dev2app );
01402   
01403   /* These two values aren't really used by mozilla */
01404   mEmAscent = mMaxAscent - mInternalLeading;
01405   mEmDescent  = mMaxDescent;
01406 
01407   mMaxHeight  = mMaxAscent + mMaxDescent;
01408   mEmHeight = mEmAscent + mEmDescent;
01409 
01410   mMaxAdvance = NSToCoordRound( fm.lMaxCharInc * dev2app );
01411   mXHeight    = NSToCoordRound( fm.lXHeight * dev2app );
01412 
01413   nscoord onePixel = NSToCoordRound(1 * dev2app);
01414 
01415    // Not all fonts specify these two values correctly, and some not at all
01416   mSuperscriptYOffset = mXHeight;
01417   mSubscriptYOffset   = NSToCoordRound( mXHeight / 3.0f );
01418 
01419    // Using lStrikeoutPosition puts the strikeout too high
01420    // Use 50% of lXHeight instead
01421   mStrikeoutPosition  = NSToCoordRound( mXHeight / 2.0f);
01422   mStrikeoutSize      = PR_MAX(onePixel, NSToCoordRound(fm.lStrikeoutSize * dev2app));
01423 
01424 #if 1
01425   // Can't trust the fonts to give us good results for the underline position
01426   //  and size.  This calculation, though, gives very good results.
01427   float height = fm.lMaxAscender + fm.lMaxDescender;
01428   mUnderlinePosition = -PR_MAX(onePixel, NSToIntRound(floor(0.1 * height + 0.5) * dev2app));
01429   mUnderlineSize = PR_MAX(onePixel, NSToIntRound(floor(0.05 * height + 0.5) * dev2app));
01430 #else
01431   mUnderlinePosition  = -NSToCoordRound( fm.lUnderscorePosition * dev2app );
01432   mUnderlineSize      = PR_MAX(onePixel, NSToCoordRound(fm.lUnderscoreSize * dev2app));
01433 #endif
01434 
01435   mAveCharWidth       = PR_MAX(1, NSToCoordRound(fm.lAveCharWidth * dev2app));
01436 
01437    // Cache the width of a single space.
01438   SIZEL  size;
01439   GetTextExtentPoint32(ps, " ", 1, &size);
01440   mSpaceWidth = NSToCoordRound(float(size.cx) * dev2app);
01441 
01442    // 10) Clean up
01443   GFX (::GpiSetCharSet (ps, LCID_DEFAULT), FALSE);
01444   GFX (::GpiDeleteSetId (ps, 1), FALSE);
01445   if (mDeviceContext->mPrintDC == NULL)
01446     ::WinReleasePS(ps);
01447 
01448   return NS_OK;
01449 }
01450 
01451 NS_IMETHODIMP nsFontMetricsOS2::GetSpaceWidth(nscoord &aSpaceWidth)
01452 {
01453   aSpaceWidth = mSpaceWidth;
01454   return NS_OK;
01455 }
01456 
01457 // Other metrics
01458 NS_IMETHODIMP nsFontMetricsOS2::GetXHeight( nscoord &aResult)
01459 {
01460   aResult = mXHeight;
01461   return NS_OK;
01462 }
01463 
01464 NS_IMETHODIMP nsFontMetricsOS2::GetSuperscriptOffset(nscoord& aResult)
01465 {
01466   aResult = mSuperscriptYOffset;
01467   return NS_OK;
01468 }
01469 
01470 NS_IMETHODIMP nsFontMetricsOS2::GetSubscriptOffset(nscoord& aResult)
01471 {
01472   aResult = mSubscriptYOffset;
01473   return NS_OK;
01474 }
01475 
01476 NS_IMETHODIMP nsFontMetricsOS2::GetStrikeout(nscoord& aOffset, nscoord& aSize)
01477 {
01478   aOffset = mStrikeoutPosition;
01479   aSize = mStrikeoutSize;
01480   return NS_OK;
01481 }
01482 
01483 NS_IMETHODIMP nsFontMetricsOS2::GetUnderline(nscoord& aOffset, nscoord& aSize)
01484 {
01485   aOffset = mUnderlinePosition;
01486   aSize = mUnderlineSize;
01487   return NS_OK;
01488 }
01489 
01490 NS_IMETHODIMP nsFontMetricsOS2::GetHeight( nscoord &aHeight)
01491 {
01492   aHeight = mMaxHeight;
01493   return NS_OK;
01494 }
01495 
01496 #ifdef FONT_LEADING_APIS_V2
01497 NS_IMETHODIMP
01498 nsFontMetricsOS2::GetInternalLeading(nscoord &aLeading)
01499 {
01500   aLeading = mInternalLeading;
01501   return NS_OK;
01502 }
01503 
01504 NS_IMETHODIMP
01505 nsFontMetricsOS2::GetExternalLeading(nscoord &aLeading)
01506 {
01507   aLeading = mExternalLeading;
01508   return NS_OK;
01509 }
01510 #else
01511 NS_IMETHODIMP nsFontMetricsOS2::GetLeading( nscoord &aLeading)
01512 {
01513    aLeading = mInternalLeading;
01514    return NS_OK;
01515 }
01516 
01517 NS_IMETHODIMP
01518 nsFontMetricsOS2::GetNormalLineHeight(nscoord &aHeight)
01519 {
01520   aHeight = mEmHeight + mInternalLeading;
01521   return NS_OK;
01522 }
01523 #endif //FONT_LEADING_APIS_V2
01524 
01525 NS_IMETHODIMP nsFontMetricsOS2::GetMaxAscent( nscoord &aAscent)
01526 {
01527   aAscent = mMaxAscent;
01528   return NS_OK;
01529 }
01530 
01531 NS_IMETHODIMP nsFontMetricsOS2::GetMaxDescent( nscoord &aDescent)
01532 {
01533   aDescent = mMaxDescent;
01534   return NS_OK;
01535 }
01536 
01537 NS_IMETHODIMP nsFontMetricsOS2::GetMaxAdvance( nscoord &aAdvance)
01538 {
01539   aAdvance = mMaxAdvance;
01540   return NS_OK;
01541 }
01542 
01543 NS_IMETHODIMP nsFontMetricsOS2::GetFontHandle( nsFontHandle &aHandle)
01544 {
01545   aHandle = mFontHandle;
01546   return NS_OK;
01547 }
01548 
01549 NS_IMETHODIMP nsFontMetricsOS2::GetLangGroup(nsIAtom** aLangGroup)
01550 {
01551   NS_ENSURE_ARG_POINTER(aLangGroup);
01552   *aLangGroup = mLangGroup;
01553   NS_IF_ADDREF(*aLangGroup);
01554   return NS_OK;
01555 }
01556 
01557 NS_IMETHODIMP
01558 nsFontMetricsOS2::GetEmHeight(nscoord &aHeight)
01559 {
01560   aHeight = mEmHeight;
01561   return NS_OK;
01562 }
01563 
01564 NS_IMETHODIMP
01565 nsFontMetricsOS2::GetEmAscent(nscoord &aAscent)
01566 {
01567   aAscent = mEmAscent;
01568   return NS_OK;
01569 }
01570 
01571 NS_IMETHODIMP
01572 nsFontMetricsOS2::GetEmDescent(nscoord &aDescent)
01573 {
01574   aDescent = mEmDescent;
01575   return NS_OK;
01576 }
01577 
01578 NS_IMETHODIMP
01579 nsFontMetricsOS2::GetMaxHeight(nscoord &aHeight)
01580 {
01581   aHeight = mMaxHeight;
01582   return NS_OK;
01583 }
01584 
01585 NS_IMETHODIMP
01586 nsFontMetricsOS2::GetAveCharWidth(nscoord &aAveCharWidth)
01587 {
01588   aAveCharWidth = mAveCharWidth;
01589   return NS_OK;
01590 }
01591 
01592 nsFontOS2*
01593 nsFontMetricsOS2::LoadUnicodeFont(HPS aPS, const nsAString& aName)
01594 {
01595 #ifdef DEBUG_FONT_SELECTION
01596   printf(" LoadUnicodeFont(): attempting to load %s\n",
01597          NS_LossyConvertUCS2toASCII(aName).get());
01598 #endif
01599   nsFontOS2* font = LoadFont(aPS, aName);
01600   if (font) {
01601     return font;
01602   }
01603   return nsnull;
01604 }
01605 
01606 struct UnicodeFontEnumContext
01607 {
01608   HPS               mPS;        // IN
01609   nsFontOS2*        mFont;      // OUT
01610   nsFontMetricsOS2* mMetrics;   // IN
01611 };
01612 
01613 static PRBool
01614 UnicodeFontEnumCallback(const nsString& aFamily, PRBool aGeneric, void* aData)
01615 {
01616   UnicodeFontEnumContext* context = (UnicodeFontEnumContext*)aData;
01617   HPS ps = context->mPS;
01618   nsFontMetricsOS2* metrics = context->mMetrics;
01619   context->mFont = metrics->LoadUnicodeFont(ps, aFamily);
01620   if (context->mFont) {
01621     return PR_FALSE; // stop enumerating the list
01622   }
01623   return PR_TRUE; // don't stop
01624 }
01625 
01626 void
01627 nsFontMetricsOS2::FindUnicodeFont(HPS aPS)
01628 {
01629   nsresult res;
01630   nsCAutoString pref, generic;
01631   nsXPIDLString value;
01632 
01633   generic.Assign(NS_ConvertUCS2toUTF8(mGeneric));
01634 
01635   pref.Assign("font.name.");
01636   pref.Append(generic);
01637   pref.Append(".x-unicode");
01638    
01639   res = gPref->CopyUnicharPref(pref.get(), getter_Copies(value));
01640   if (NS_FAILED(res))
01641     return;
01642 
01643   nsAutoString fontname;
01644   fontname.Assign(value);
01645 
01646   nsFontOS2* fh = nsnull;
01647   fh = LoadUnicodeFont(aPS, fontname);
01648   if (!fh)
01649   {
01650      // User defined unicode font did not load.  Fall back to font
01651      // specified in font.name-list.%.x-unicode
01652     pref.Assign("font.name-list.");
01653     pref.Append(generic);
01654     pref.Append(".x-unicode");
01655 
01656     res = gPref->CopyUnicharPref(pref.get(), getter_Copies(value));
01657     if (NS_FAILED(res))
01658       return;
01659 
01660     nsFont font("", 0, 0, 0, 0, 0);
01661     font.name.Assign(value);
01662 
01663      // Iterate over the list of names using the callback mechanism of nsFont
01664     UnicodeFontEnumContext context = {aPS, nsnull, this};
01665     font.EnumerateFamilies(UnicodeFontEnumCallback, &context);
01666     fh = context.mFont;
01667   }
01668 
01669   if (fh) {   // a suitable font was found
01670     fh->mFattrs.usCodePage = 1208;
01671     fh->mConvertCodePage = 1208;
01672     mUnicodeFont = fh;
01673 #ifdef DEBUG_FONT_SELECTION
01674     printf(" FindUnicodeFont(): found new Unicode font %s\n",
01675            mUnicodeFont->mFattrs.szFacename);
01676 #endif
01677   } else {
01678     NS_ERROR("Could not find a Unicode font");
01679   }
01680   return;
01681 }
01682 
01683 void
01684 nsFontMetricsOS2::FindWesternFont()
01685 {
01686   // Create a 'western' font by making a copy of the currently selected font
01687   // and changing the codepage 1252
01688   nsFontOS2* font = new nsFontOS2();
01689   font->mFattrs = mFontHandle->mFattrs;
01690   font->mFattrs.usCodePage = 1252;
01691   font->mCharbox = mFontHandle->mCharbox;
01692   font->mMaxAscent = mFontHandle->mMaxAscent;
01693   font->mMaxDescent = mFontHandle->mMaxDescent;
01694   font->mConvertCodePage = 1252;
01695   mLoadedFonts.AppendElement(font);
01696   mWesternFont = font;
01697 }
01698 
01699 #define IS_SPECIAL_WO_ELLIPSE(x) \
01700                       ((x == 0x20AC) ||  /* euro */  \
01701                        (x == 0x2022) ||  /* bull */  \
01702                        (x == 0x201C) ||  /* ldquo */ \
01703                        (x == 0x201D) ||  /* rdquo */ \
01704                        (x == 0x2018) ||  /* lsquo */ \
01705                        (x == 0x2019) ||  /* rsquo */ \
01706                        (x == 0x2013) ||  /* ndash */ \
01707                        (x == 0x2014))    /* mdash */
01708 
01709 #define IS_SPECIAL(x) \
01710                        (IS_SPECIAL_WO_ELLIPSE(x) ||  \
01711                        (x == 0x2026)) /* hellip */
01712 
01713 nsresult
01714 nsFontMetricsOS2::ResolveForwards(HPS                  aPS,
01715                                   const PRUnichar*     aString,
01716                                   PRUint32             aLength,
01717                                   nsFontSwitchCallback aFunc, 
01718                                   void*                aData)
01719 {
01720   NS_ASSERTION(aString || !aLength, "invalid call");
01721   const PRUnichar* firstChar = aString;
01722   const PRUnichar* currChar = firstChar;
01723   const PRUnichar* lastChar  = aString + aLength;
01724   PRBool running = PR_TRUE;
01725 
01726   nsFontSwitch fontSwitch;
01727   fontSwitch.mFont = 0;
01728 
01729   if (mConvertCodePage == 1252)
01730   {
01731     while (running && firstChar < lastChar)
01732     {
01733       if ((*currChar > 0x00FF) && !IS_SPECIAL(*currChar))
01734       {
01735         if (!mUnicodeFont) {
01736           FindUnicodeFont(aPS);
01737           if (!mUnicodeFont) {
01738             mUnicodeFont = FindGlobalFont(aPS, *currChar);
01739           }
01740         }
01741         fontSwitch.mFont = mUnicodeFont;
01742         while( ++currChar < lastChar ) {
01743           if (( *currChar <= 0x00FF ) || IS_SPECIAL(*currChar))
01744             break;
01745         }
01746         running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
01747       } else {
01748         // Use currently selected font
01749         fontSwitch.mFont = mFontHandle;
01750         while( ++currChar < lastChar ) {
01751           if (( *currChar > 0x00FF ) && !IS_SPECIAL(*currChar))
01752             break;
01753         }
01754         running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
01755       }
01756       firstChar = currChar;
01757     }
01758   }
01759   else
01760   {
01761     while (running && firstChar < lastChar)
01762     {
01763       if ((*currChar >= 0x0080 && *currChar <= 0x00FF) || IS_SPECIAL_WO_ELLIPSE(*currChar))
01764       { 
01765         if (!mWesternFont) {
01766           FindWesternFont();
01767         }
01768         fontSwitch.mFont = mWesternFont;
01769         while( ++currChar < lastChar ) {
01770           if ((*currChar < 0x0080 || *currChar > 0x00FF) && !IS_SPECIAL_WO_ELLIPSE(*currChar))
01771             break;
01772         }
01773         running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
01774       } else {
01775         // Use currently selected font
01776         fontSwitch.mFont = mFontHandle;
01777         while( ++currChar < lastChar ) {
01778           if ((*currChar >= 0x0080 && *currChar <= 0x00FF) || IS_SPECIAL_WO_ELLIPSE(*currChar))
01779             break;
01780         }
01781         running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
01782       }
01783       firstChar = currChar;
01784     }
01785   }
01786   return NS_OK;
01787 }
01788 
01789 nsresult
01790 nsFontMetricsOS2::ResolveBackwards(HPS                  aPS,
01791                                    const PRUnichar*     aString,
01792                                    PRUint32             aLength,
01793                                    nsFontSwitchCallback aFunc, 
01794                                    void*                aData)
01795 {
01796   NS_ASSERTION(aString || !aLength, "invalid call");
01797   const PRUnichar* firstChar = aString + aLength - 1;
01798   const PRUnichar* lastChar  = aString - 1;
01799   const PRUnichar* currChar  = firstChar;
01800   PRBool running = PR_TRUE;
01801 
01802   nsFontSwitch fontSwitch;
01803   fontSwitch.mFont = 0;
01804   
01805   if (mConvertCodePage == 1252)
01806   {
01807     while (running && firstChar > lastChar)
01808     {
01809       if ((*currChar > 0x00FF) && !IS_SPECIAL(*currChar))
01810       {
01811         if (!mUnicodeFont) {
01812           FindUnicodeFont(aPS);
01813           if (!mUnicodeFont) {
01814             mUnicodeFont = FindGlobalFont(aPS, *currChar);
01815           }
01816         }
01817         fontSwitch.mFont = mUnicodeFont;
01818         while( --currChar > lastChar ) {
01819           if (( *currChar <= 0x00FF ) || IS_SPECIAL(*currChar))
01820             break;
01821         }
01822         running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
01823       }
01824       else
01825       {
01826          // Use currently selected font
01827         fontSwitch.mFont = mFontHandle;
01828         while( --currChar > lastChar ) {
01829           if (( *currChar > 0x00FF ) && !IS_SPECIAL(*currChar))
01830             break;
01831         }
01832         running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
01833       }
01834       firstChar = currChar;
01835     }
01836   }
01837   else
01838   {
01839     while (running && firstChar > lastChar)
01840     {
01841       if ((*currChar >= 0x0080 && *currChar <= 0x00FF) || IS_SPECIAL(*currChar))
01842       { 
01843         if (!mWesternFont) {
01844           FindWesternFont();
01845         }
01846         fontSwitch.mFont = mWesternFont;
01847         while( --currChar > lastChar ) {
01848           if ((*currChar < 0x0080 || *currChar > 0x00FF) && !IS_SPECIAL(*currChar))
01849             break;
01850         }
01851         running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
01852       }
01853       else
01854       {
01855          // Use currently selected font
01856         fontSwitch.mFont = mFontHandle;
01857         while( --currChar > lastChar ) {
01858           if ((*currChar >= 0x0080 && *currChar <= 0x00FF) || IS_SPECIAL(*currChar))
01859             break;
01860         }
01861         running = (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
01862       }
01863       firstChar = currChar;
01864     }
01865   }
01866   return NS_OK;
01867 }
01868 
01869 
01870 /**********************************************************
01871     nsFontOS2
01872  **********************************************************/
01873 #ifdef DEBUG_FONT_STRUCT_ALLOCS
01874 unsigned long nsFontOS2::mRefCount = 0;
01875 #endif
01876 
01877 nsFontOS2::nsFontOS2(void)
01878 {
01879   mFattrs.usRecordLength = sizeof(mFattrs);
01880   mHashMe = gCurrHashValue;
01881   gCurrHashValue++;
01882 #ifdef DEBUG_FONT_STRUCT_ALLOCS
01883   mRefCount++;
01884   printf("+++ nsFontOS2 total = %d\n", mRefCount);
01885 #endif
01886 }
01887 
01888 nsFontOS2::~nsFontOS2(void)
01889 {
01890 #ifdef DEBUG_FONT_STRUCT_ALLOCS
01891   mRefCount--;
01892   printf("--- nsFontOS2 total = %d\n", mRefCount);
01893 #endif
01894 }
01895 
01896 void
01897 nsFontOS2::SelectIntoPS( HPS aPS, long aLcid )
01898 {
01899    GFX (::GpiSetCharSet(aPS, aLcid), FALSE);
01900    GFX (::GpiSetCharBox(aPS, &mCharbox), FALSE);
01901 }
01902 
01903 PRInt32
01904 nsFontOS2::GetWidth(HPS aPS, const char* aString, PRUint32 aLength)
01905 {
01906   SIZEL size;
01907   GetTextExtentPoint32(aPS, aString, aLength, &size);
01908   return size.cx;
01909 }
01910 
01911 PRInt32
01912 nsFontOS2::GetWidth(HPS aPS, const PRUnichar* aString, PRUint32 aLength)
01913 {
01914   nsAutoCharBuffer buffer;
01915   PRInt32 destLength = aLength;
01916   WideCharToMultiByte(mConvertCodePage, aString, aLength, buffer, destLength);
01917 
01918   SIZEL size;
01919   GetTextExtentPoint32(aPS, buffer.get(), destLength, &size);
01920 
01921   return size.cx;
01922 }
01923 
01924 void
01925 nsFontOS2::DrawString(HPS aPS, nsDrawingSurfaceOS2* aSurface,
01926                       PRInt32 aX, PRInt32 aY,
01927                       const char* aString, PRUint32 aLength, INT* aDx0)
01928 {
01929   POINTL ptl = { aX, aY };
01930   aSurface->NS2PM(&ptl, 1);
01931   ExtTextOut(aPS, ptl.x, ptl.y, 0, NULL, aString, aLength, aDx0);
01932 }
01933 
01934 void
01935 nsFontOS2::DrawString(HPS aPS, nsDrawingSurfaceOS2* aSurface,
01936                       PRInt32 aX, PRInt32 aY,
01937                       const PRUnichar* aString, PRUint32 aLength)
01938 {
01939   nsAutoCharBuffer buffer;
01940   PRInt32 destLength = aLength;
01941   WideCharToMultiByte(mConvertCodePage, aString, aLength, buffer, destLength);
01942 
01943   POINTL ptl = { aX, aY };
01944   aSurface->NS2PM (&ptl, 1);
01945 
01946   ExtTextOut(aPS, ptl.x, ptl.y, 0, NULL, buffer.get(), destLength, NULL);
01947 }
01948 
01949 
01950 #ifdef USE_FREETYPE
01951 /**********************************************************
01952     nsFontMetricsOS2FT
01953  **********************************************************/
01954 Ft2EnableFontEngine nsFontMetricsOS2FT::pfnFt2EnableFontEngine = NULL;
01955 
01956 #define IsCJKLangGroupAtom(a)  ((a)==gJA || (a)==gKO || (a)==gZHCN || \
01957                                 (a)==gZHTW || (a) == gZHHK)
01958 
01959 nsFontMetricsOS2FT::~nsFontMetricsOS2FT()
01960 {
01961   mSubstituteFont = nsnull; // released in ~nsFontMetricsOS2()
01962 }
01963 
01964 nsFontOS2*
01965 nsFontMetricsOS2FT::FindPrefFont(HPS aPS, PRUint32 aChar)
01966 {
01967   if (mTriedAllPref) {
01968     // don't bother anymore because mLoadedFonts[] already has all our pref fonts
01969     return nsnull;
01970   }
01971   nsFont font("", 0, 0, 0, 0, 0);
01972 
01973   // Sometimes we could not find the font in doc's suggested langGroup,(this usually means  
01974   // the language specified by doc is incorrect). The characters can, to a certain degree, 
01975   // tell us what language it is. This allows us to quickly locate and use a more appropriate 
01976   // font as indicated by user's preference. In some situations a set of possible languages may
01977   // be identified instead of a single language (eg. CJK and latin). In this case we have to 
01978   // try every language in the set. gUserLocale and gSystemLocale provide some hints about 
01979   // which one should be tried first. This is important for CJK font, since the glyph for single 
01980   // char varies dramatically in different langauges. For latin languages, their glyphs are 
01981   // similar. In fact, they almost always share identical fonts. It will be a waste of time to 
01982   // figure out which one comes first. As a final fallback, unicode preference is always tried. 
01983 
01984   PRUint32 unicodeRange = FindCharUnicodeRange(aChar);
01985   if (unicodeRange < kRangeSpecificItemNum) {
01986     // a single language is identified
01987     AppendGenericFontFromPref(font.name, LangGroupFromUnicodeRange(unicodeRange), 
01988                               NS_ConvertUCS2toUTF8(mGeneric).get());
01989   } else if (kRangeSetLatin == unicodeRange) { 
01990     // Character is from a latin language set, so try western and central european
01991     // If mLangGroup is western or central european, this most probably will not be
01992     // used, but is here as a fallback scenario.    
01993     AppendGenericFontFromPref(font.name, "x-western",
01994                               NS_ConvertUCS2toUTF8(mGeneric).get());
01995     AppendGenericFontFromPref(font.name, "x-central-euro",
01996                               NS_ConvertUCS2toUTF8(mGeneric).get());
01997   } else if (kRangeSetCJK == unicodeRange) { 
01998     // CJK, we have to be careful about the order, use locale info as hint
01999     
02000     // then try user locale first, if it is CJK
02001     if ((gUsersLocale != mLangGroup) && IsCJKLangGroupAtom(gUsersLocale)) {
02002       nsCAutoString usersLocaleLangGroup;
02003       gUsersLocale->ToUTF8String(usersLocaleLangGroup);
02004       AppendGenericFontFromPref(font.name, usersLocaleLangGroup.get(), 
02005                                 NS_ConvertUCS2toUTF8(mGeneric).get());
02006     }
02007     
02008     // then system locale (os language)
02009     if ((gSystemLocale != mLangGroup) && (gSystemLocale != gUsersLocale) && IsCJKLangGroupAtom(gSystemLocale)) {
02010       nsCAutoString systemLocaleLangGroup;
02011       gSystemLocale->ToUTF8String(systemLocaleLangGroup);
02012       AppendGenericFontFromPref(font.name, systemLocaleLangGroup.get(), 
02013                                 NS_ConvertUCS2toUTF8(mGeneric).get());
02014     }
02015 
02016     // try all other languages in this set.
02017     if (mLangGroup != gJA && gUsersLocale != gJA && gSystemLocale != gJA)
02018       AppendGenericFontFromPref(font.name, "ja",
02019                                 NS_ConvertUCS2toUTF8(mGeneric).get());
02020     if (mLangGroup != gZHCN && gUsersLocale != gZHCN && gSystemLocale != gZHCN)
02021       AppendGenericFontFromPref(font.name, "zh-CN",
02022                                 NS_ConvertUCS2toUTF8(mGeneric).get());
02023     if (mLangGroup != gZHTW && gUsersLocale != gZHTW && gSystemLocale != gZHTW)
02024       AppendGenericFontFromPref(font.name, "zh-TW",
02025                                 NS_ConvertUCS2toUTF8(mGeneric).get());
02026     if (mLangGroup != gZHHK && gUsersLocale != gZHHK && gSystemLocale != gZHHK)
02027       AppendGenericFontFromPref(font.name, "zh-HK",
02028                                 NS_ConvertUCS2toUTF8(mGeneric).get());
02029     if (mLangGroup != gKO && gUsersLocale != gKO && gSystemLocale != gKO)
02030       AppendGenericFontFromPref(font.name, "ko",
02031                                 NS_ConvertUCS2toUTF8(mGeneric).get());
02032   } 
02033 
02034   // always try unicode as fallback
02035   AppendGenericFontFromPref(font.name, "x-unicode",
02036                             NS_ConvertUCS2toUTF8(mGeneric).get());
02037   
02038   // use the font list to find font
02039   GenericFontEnumContext context = {aPS, aChar, nsnull, this};
02040   font.EnumerateFamilies(GenericFontEnumCallback, &context);
02041   if (context.mFont) { // a suitable font was found
02042     return context.mFont;
02043   }
02044   mTriedAllPref = 1;
02045   return nsnull;
02046 }
02047 
02048 struct FindGlobalFontData
02049 {
02050   HPS               ps;       // IN
02051   PRUint32          ch;       // IN - looking for font that has this char
02052   nsFontOS2FT*      font;     // IN - scratch object
02053   GlobalFontEntry*  entry;    // OUT - set to matching global font entry
02054 };
02055 
02056 PR_STATIC_CALLBACK(PLDHashOperator)
02057 FindGlobalFontEnumFunc(GlobalFontEntry* aEntry, void* aData)
02058 {
02059   FindGlobalFontData* data = NS_STATIC_CAST(FindGlobalFontData*, aData);
02060   strcpy(data->font->mFattrs.szFacename, aEntry->mMetrics->szFacename);
02061 #ifdef PERF_HASGLYPH_CHAR_MAP
02062   if (aEntry->mHaveCheckedCharMap == nsnull) {
02063     aEntry->mHaveCheckedCharMap = (PRUint32*)calloc(UCS2_MAP_LEN, sizeof(PRUint32));
02064     aEntry->mRepresentableCharMap = (PRUint32*)calloc(UCS2_MAP_LEN, sizeof(PRUint32));
02065   }
02066   data->font->mHaveCheckedCharMap = aEntry->mHaveCheckedCharMap;
02067   data->font->mRepresentableCharMap = aEntry->mRepresentableCharMap;
02068 #endif
02069 
02070   if (data->font->HasGlyph(data->ps, data->ch)) {
02071     data->entry = aEntry;
02072     return PL_DHASH_STOP;
02073   } else {
02074     return PL_DHASH_NEXT;
02075   }
02076 }
02077 
02078 nsFontOS2*
02079 nsFontMetricsOS2FT::FindGlobalFont(HPS aPS, PRUint32 aChar)
02080 {
02081   //now try global font
02082   if (!gGlobalFonts) {
02083     if (!InitializeGlobalFonts()) {
02084       return nsnull;
02085     }
02086   }
02087 
02088   FindGlobalFontData data = {aPS, aChar, nsnull, nsnull};
02089   data.font = new nsFontOS2FT();
02090   gGlobalFonts->EnumerateEntries(FindGlobalFontEnumFunc, &data);
02091   delete data.font;
02092   if (data.entry) {
02093     return LoadFont(aPS, data.entry->GetKey());
02094   }
02095   return nsnull;
02096 }
02097 
02098 nsFontOS2*
02099 nsFontMetricsOS2FT::FindSubstituteFont(HPS aPS, PRUint32 c)
02100 {
02101   /*
02102   When this function is called, it means all other alternatives have
02103   been unsuccessfully tried! So the idea is this:
02104   
02105   See if the "substitute font" is already loaded?
02106   a/ if yes, ADD_GLYPH(c), to record that the font should be used
02107      to render this char from now on. 
02108   b/ if no, load the font, and ADD_GLYPH(c)
02109   */
02110 
02111   if (mSubstituteFont) {
02112     // Make the char representable so that we don't have to go over all fonts
02113     // before fallback to subsituteFont.
02114     ((nsFontOS2Substitute*)mSubstituteFont)->SetRepresentable(c);
02115     return mSubstituteFont;
02116   }
02117 
02118   // The "substitute font" has not yet been created... 
02119   // The first font that has the
02120   // replacement char is taken and placed as the substitute font.
02121 
02122   // Try local/loaded fonts first
02123   int i, count = mLoadedFonts.Count();
02124   for (i = 0; i < count; ++i) {
02125     nsFontOS2* font = (nsFontOS2*)mLoadedFonts[i];
02126     if (font->HasGlyph(aPS, NS_REPLACEMENT_CHAR)) {
02127       nsFontOS2Substitute* subFont = new nsFontOS2Substitute(font);
02128       mLoadedFonts.AppendElement((nsFontOS2*)subFont);
02129       subFont->SetRepresentable(c);
02130       mSubstituteFont = subFont;
02131       return subFont;
02132     }
02133   }
02134 
02135   // Try global fonts
02136   // Since we reach here after FindGlobalFont() is called, we have already
02137   // scanned the global list of fonts and have set the attributes of interest
02138   FindGlobalFontData data = {aPS, NS_REPLACEMENT_CHAR, nsnull, nsnull};
02139   data.font = new nsFontOS2FT();
02140   gGlobalFonts->EnumerateEntries(FindGlobalFontEnumFunc, &data);
02141   delete data.font;
02142   if (data.entry) {
02143     nsFontOS2* font = LoadFont(aPS, data.entry->GetKey());
02144     if (font) {
02145       nsFontOS2Substitute* subFont = new nsFontOS2Substitute(font);
02146       // LoadFont() appends given font to end of mLoadedFonts.  Just replace
02147       //  font with our substitute font.
02148       mLoadedFonts.ReplaceElementAt((nsFontOS2*)subFont, mLoadedFonts.Count());
02149       subFont->SetRepresentable(c);
02150       mSubstituteFont = subFont;
02151       return subFont;
02152     }
02153   }
02154 
02155   NS_ERROR("Could not provide a substititute font");
02156   return nsnull;
02157 }
02158 
02159 nsFontOS2*
02160 nsFontMetricsOS2FT::LocateFont(HPS aPS, PRUint32 aChar, PRInt32 & aCount)
02161 {
02162   nsFontOS2* font;
02163   PRInt32 i;
02164 
02165   // see if one of our loaded fonts can represent the character
02166   for (i = 0; i < aCount; ++i) {
02167     font = (nsFontOS2*)mLoadedFonts[i];
02168     if (font->HasGlyph(aPS, aChar))
02169       return font;
02170   }
02171 
02172   font = FindFont(aPS, aChar);
02173   aCount = mLoadedFonts.Count(); // update since FindFont() can change it
02174   NS_ASSERTION(font && mLoadedFonts.IndexOf(font) >= 0,
02175                "Could not find a font");
02176   return font;
02177 }
02178 
02179 nsresult
02180 nsFontMetricsOS2FT::ResolveForwards(HPS                  aPS,
02181                                     const PRUnichar*     aString,
02182                                     PRUint32             aLength,
02183                                     nsFontSwitchCallback aFunc, 
02184                                     void*                aData)
02185 {
02186   NS_ASSERTION(aString || !aLength, "invalid call");
02187   const PRUnichar* firstChar = aString;
02188   const PRUnichar* currChar = firstChar;
02189   const PRUnichar* lastChar  = aString + aLength;
02190   nsFontOS2* currFont;
02191   nsFontOS2* nextFont;
02192   PRInt32 count;
02193   nsFontSwitch fontSwitch;
02194 
02195   if (firstChar == lastChar)
02196     return NS_OK;
02197 
02198   count = mLoadedFonts.Count();
02199 
02200   if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
02201     currFont = LocateFont(aPS, SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
02202     currChar += 2;
02203   }
02204   else {
02205     currFont = LocateFont(aPS, *currChar, count);
02206     ++currChar;
02207   }
02208 
02209   //This if block is meant to speedup the process in normal situation, when
02210   //most characters can be found in first font
02211   if (currFont == mLoadedFonts[0]) {
02212     while (currChar < lastChar && (currFont->HasGlyph(aPS, *currChar)))
02213       ++currChar;
02214     fontSwitch.mFont = currFont;
02215     if (!(*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData))
02216       return NS_OK;
02217     if (currChar == lastChar)
02218       return NS_OK;
02219     // continue with the next substring, re-using the available loaded fonts
02220     firstChar = currChar;
02221     if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
02222       currFont = LocateFont(aPS, SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
02223       currChar += 2;
02224     }
02225     else {
02226       currFont = LocateFont(aPS, *currChar, count);
02227       ++currChar;
02228     }
02229   }
02230 
02231   // see if we can keep the same font for adjacent characters
02232   PRInt32 lastCharLen;
02233   while (currChar < lastChar) {
02234     if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) {
02235       nextFont = LocateFont(aPS, SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count);
02236       lastCharLen = 2;
02237     }
02238     else {
02239       nextFont = LocateFont(aPS, *currChar, count);
02240       lastCharLen = 1;
02241     }
02242     if (nextFont != currFont) {
02243       // We have a substring that can be represented with the same font, and
02244       // we are about to switch fonts, it is time to notify our caller.
02245       fontSwitch.mFont = currFont;
02246       if (!(*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData))
02247         return NS_OK;
02248       // continue with the next substring, re-using the available loaded fonts
02249       firstChar = currChar;
02250 
02251       currFont = nextFont; // use the font found earlier for the char
02252     }
02253     currChar += lastCharLen;
02254   }
02255 
02256   //do it for last part of the string
02257   fontSwitch.mFont = currFont;
02258   (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData);
02259   return NS_OK;
02260 }
02261 
02262 nsresult
02263 nsFontMetricsOS2FT::ResolveBackwards(HPS                  aPS,
02264                                      const PRUnichar*     aString,
02265                                      PRUint32             aLength,
02266                                      nsFontSwitchCallback aFunc, 
02267                                      void*                aData)
02268 {
02269   NS_ASSERTION(aString || !aLength, "invalid call");
02270   const PRUnichar* firstChar = aString + aLength - 1;
02271   const PRUnichar* lastChar  = aString - 1;
02272   const PRUnichar* currChar  = firstChar;
02273   nsFontOS2* currFont;
02274   nsFontOS2* nextFont;
02275   PRInt32 count;
02276   nsFontSwitch fontSwitch;
02277 
02278   if (firstChar == lastChar)
02279     return NS_OK;
02280 
02281   count = mLoadedFonts.Count();
02282 
02283   // see if one of our loaded fonts can represent the current character
02284   if (IS_LOW_SURROGATE(*currChar) && (currChar-1) > lastChar && IS_HIGH_SURROGATE(*(currChar-1))) {
02285     currFont = LocateFont(aPS, SURROGATE_TO_UCS4(*(currChar-1), *currChar), count);
02286     currChar -= 2;
02287   }
02288   else {
02289     currFont = LocateFont(aPS, *currChar, count);
02290     --currChar;
02291   }
02292 
02293   //This if block is meant to speedup the process in normal situation, when
02294   //most characters can be found in first font
02295   if (currFont == mLoadedFonts[0]) {
02296     while (currChar > lastChar && (currFont->HasGlyph(aPS, *currChar)))
02297       --currChar;
02298     fontSwitch.mFont = currFont;
02299     if (!(*aFunc)(&fontSwitch, currChar+1, firstChar - currChar, aData))
02300       return NS_OK;
02301     if (currChar == lastChar)
02302       return NS_OK;
02303     // continue with the next substring, re-using the available loaded fonts
02304     firstChar = currChar;
02305     if (IS_LOW_SURROGATE(*currChar) && (currChar-1) > lastChar && IS_HIGH_SURROGATE(*(currChar-1))) {
02306       currFont = LocateFont(aPS, SURROGATE_TO_UCS4(*(currChar-1), *currChar), count);
02307       currChar -= 2;
02308     }
02309     else {
02310       currFont = LocateFont(aPS, *currChar, count);
02311       --currChar;
02312     }
02313   }
02314 
02315   // see if we can keep the same font for adjacent characters
02316   PRInt32 lastCharLen;
02317   PRUint32 codepoint;
02318 
02319   while (currChar > lastChar) {
02320     if (IS_LOW_SURROGATE(*currChar) && (currChar-1) > lastChar && IS_HIGH_SURROGATE(*(currChar-1))) {
02321       codepoint =  SURROGATE_TO_UCS4(*(currChar-1), *currChar);
02322       nextFont = LocateFont(aPS, codepoint, count);
02323       lastCharLen = 2;
02324     }
02325     else {
02326       codepoint = *currChar;
02327       nextFont = LocateFont(aPS, codepoint, count);
02328       lastCharLen = 1;
02329     }
02330     if (nextFont != currFont ||
02331         /* render right-to-left characters outside the BMP one by one, because
02332            OS/2 doesn't reorder them. */
02333         codepoint > 0xFFFF) {
02334       // We have a substring that can be represented with the same font, and
02335       // we are about to switch fonts, it is time to notify our caller.
02336       fontSwitch.mFont = currFont;
02337       if (!(*aFunc)(&fontSwitch, currChar+1, firstChar - currChar, aData))
02338         return NS_OK;
02339       // continue with the next substring, re-using the available loaded fonts
02340       firstChar = currChar;
02341       currFont = nextFont; // use the font found earlier for the char
02342     }
02343     currChar -= lastCharLen;
02344   }
02345 
02346   //do it for last part of the string
02347   fontSwitch.mFont = currFont;
02348   (*aFunc)(&fontSwitch, currChar+1, firstChar - currChar, aData);
02349 
02350   return NS_OK;
02351 }
02352 
02353 
02354 /**********************************************************
02355     nsFontOS2FT
02356  **********************************************************/
02357 Ft2FontSupportsUnicodeChar1 nsFontOS2FT::pfnFt2FontSupportsUnicodeChar1 = NULL;
02358 #ifdef USE_EXPANDED_FREETYPE_FUNCS
02359 Ft2QueryTextBoxW nsFontOS2FT::pfnFt2QueryTextBoxW = NULL;
02360 Ft2CharStringPosAtW nsFontOS2FT::pfnFt2CharStringPosAtW = NULL;
02361 #endif /* use_expanded_freetype_funcs */
02362 
02363 #ifdef DEBUG_FONT_STRUCT_ALLOCS
02364 unsigned long nsFontOS2FT::mRefCount = 0;
02365 #endif
02366 
02367 nsFontOS2FT::nsFontOS2FT(void) : nsFontOS2()
02368 {
02369   mFattrs.usCodePage = 1208;
02370 #ifdef DEBUG_FONT_STRUCT_ALLOCS
02371   mRefCount++;
02372   printf("+++ nsFontOS2FT total = %d\n", mRefCount);
02373 #endif
02374 }
02375 
02376 nsFontOS2FT::~nsFontOS2FT(void)
02377 {
02378 #ifdef DEBUG_FONT_STRUCT_ALLOCS
02379   mRefCount--;
02380   printf("--- nsFontOS2FT total = %d\n", mRefCount);
02381 #endif
02382 }
02383 
02384 PRBool
02385 nsFontOS2FT::HasGlyph(HPS aPS, PRUint32 aChar)
02386 {
02387 #ifdef PERF_HASGLYPH_CHAR_MAP
02388   if (IS_IN_BMP(aChar) && IS_REPRESENTABLE(mHaveCheckedCharMap, aChar)) {
02389     // we have already checked this char for this font
02390     return IS_REPRESENTABLE(mRepresentableCharMap, aChar);
02391   } else
02392 #endif
02393   {
02394     // If not in Plane 0, OS/2 can't render it.
02395     if (!IS_IN_BMP(aChar)) {
02396       return PR_FALSE;
02397     }
02398 
02399     PRBool rc = pfnFt2FontSupportsUnicodeChar1(0, &mFattrs, PR_TRUE, aChar);
02400 
02401 #ifdef PERF_HASGLYPH_CHAR_MAP
02402     // set that we have checked this char
02403     SET_REPRESENTABLE(mHaveCheckedCharMap, aChar);
02404     if (rc) {
02405       SET_REPRESENTABLE(mRepresentableCharMap, aChar);
02406     }
02407 #endif
02408 
02409     return rc;
02410   }
02411 }
02412 
02413 PRInt32
02414 nsFontOS2FT::GetWidth(HPS aPS, const PRUnichar* aString, PRUint32 aLength)
02415 {
02416   USHORT rc;
02417   SIZEL size;
02418 
02419   if (!IsSymbolFont()) {
02420 #ifdef USE_EXPANDED_FREETYPE_FUNCS
02421     POINTL ptls[5];
02422     rc = pfnFt2QueryTextBoxW(aPS, aLength, (LPWSTR)aString, 5, ptls);
02423     size.cx = ptls[TXTBOX_CONCAT].x;
02424     
02425 #ifdef DEBUG
02426     if (rc == FALSE) {
02427       // We only expect Ft2QueryTextBoxW() to fail with
02428       // PMERR_FUNCTION_NOT_SUPPORTED.  If it fails with any other error, then
02429       // print out a warning message
02430       USHORT errorCode = ERRORIDERROR (::WinGetLastError(0));
02431       if (errorCode != PMERR_FUNCTION_NOT_SUPPORTED) {
02432         printf("GFX_Err: pfnFt2QueryTextBoxW = 0x%X, 0x%X (%s - %s,  line %d)\n",
02433                rc, errorCode, __FILE__, __FUNCTION__, __LINE__);
02434       }
02435     }
02436 #endif /* debug */
02437 
02438     // Sometimes, Ft2QueryTextBoxW will fail with FALSE (WinGetLastError
02439     // will return PMERR_FUNCTION_NOT_SUPPORTED), particularly when printing
02440     // (this function is not supported when printing). So if it fails, fall back
02441     // to the straight GPI call case.
02442     if (rc == FALSE)
02443 #endif /* use_expanded_freetype_funcs */
02444     {
02445       NS_ConvertUCS2toUTF8 str(Substring(aString, aString + aLength));
02446       rc = GetTextExtentPoint32(aPS, (const char*)str.get(), str.Length(),
02447                                 &size);
02448     }
02449   } else {
02450     nsAutoCharBuffer buffer;
02451     PRInt32 destLength = aLength;
02452     WideCharToMultiByte(1252, aString, aLength, buffer, destLength);
02453     rc = GetTextExtentPoint32(aPS, buffer.get(), destLength, &size);
02454   }
02455 
02456   if (rc == TRUE) {
02457     return size.cx;
02458   } else {
02459     return 0;
02460   }
02461 }
02462 
02463 void
02464 nsFontOS2FT::DrawString(HPS aPS, nsDrawingSurfaceOS2* aSurface,
02465                         PRInt32 aX, PRInt32 aY,
02466                         const PRUnichar* aString, PRUint32 aLength)
02467 {
02468   USHORT rc;
02469   POINTL ptl = { aX, aY };
02470   aSurface->NS2PM(&ptl, 1);
02471 
02472   if (!IsSymbolFont()) {
02473 #ifdef USE_EXPANDED_FREETYPE_FUNCS
02474     rc = pfnFt2CharStringPosAtW(aPS, &ptl, NULL, 0, aLength,
02475                                 (LPWSTR)aString, NULL, NULL);
02476 
02477 #ifdef DEBUG
02478     if (rc == GPI_ERROR) {
02479       // We only expect Ft2CharStringPosAtW() to fail with
02480       // PMERR_FUNCTION_NOT_SUPPORTED.  If it fails with any other error, then
02481       // print out a warning message
02482       USHORT errorCode = ERRORIDERROR (::WinGetLastError(0));
02483       if (errorCode != PMERR_FUNCTION_NOT_SUPPORTED) {
02484         printf("GFX_Err: pfnFt2CharStringPosAtW = 0x%X, 0x%X (%s - %s,  line %d)\n",
02485                rc, errorCode, __FILE__, __FUNCTION__, __LINE__);
02486       }
02487     }
02488 #endif /* debug */
02489 
02490     // Sometimes, Ft2CharStringPosAtW will fail with GPI_ERROR (WinGetLastError
02491     // will return PMERR_FUNCTION_NOT_SUPPORTED), particularly when printing
02492     // (this function is not supported when printing). So if it fails, fall back
02493     // to the straight GPI call case.
02494     if (rc == GPI_ERROR)
02495 #endif /* use_expanded_freetype_funcs */
02496     {
02497       NS_ConvertUCS2toUTF8 str(Substring(aString, aString + aLength));
02498       ExtTextOut(aPS, ptl.x, ptl.y, 0, NULL, (const char*)str.get(),
02499                  str.Length(), NULL);
02500     }
02501   } else {
02502     nsAutoCharBuffer buffer;
02503     PRInt32 destLength = aLength;
02504     WideCharToMultiByte(1252, aString, aLength, buffer, destLength);
02505     ExtTextOut(aPS, ptl.x, ptl.y, 0, NULL, buffer.get(), destLength, NULL);
02506   }
02507 }
02508 
02509 /**********************************************************
02510     nsFontOS2Substitute
02511  **********************************************************/
02512 nsFontOS2Substitute::nsFontOS2Substitute(nsFontOS2* aFont)
02513 {
02514   mHashMe = gCurrHashValue;
02515   gCurrHashValue++;
02516 
02517   mFattrs = aFont->mFattrs;
02518   mCharbox = aFont->mCharbox;
02519   mMaxAscent = aFont->mMaxAscent;
02520   mMaxDescent = aFont->mMaxDescent;
02521   mConvertCodePage = aFont->mConvertCodePage;
02522 
02523   memset(mRepresentableCharMap, 0, sizeof(mRepresentableCharMap));
02524 #ifdef DEBUG_FONT_STRUCT_ALLOCS
02525   mRefCount++;
02526   printf("+++ nsFontOS2Substitute total = %d\n", mRefCount);
02527 #endif
02528 }
02529 
02530 nsFontOS2Substitute::~nsFontOS2Substitute(void)
02531 {
02532 #ifdef DEBUG_FONT_STRUCT_ALLOCS
02533   mRefCount--;
02534   printf("--- nsFontOS2Substitute total = %d\n", mRefCount);
02535 #endif
02536 }
02537 
02538 static nsresult
02539 SubstituteChars(const PRUnichar*    aString, 
02540                 PRUint32            aLength,
02541                 nsAutoChar16Buffer& aResult,
02542                 PRUint32*           aCount)
02543 {
02544   nsresult res;
02545   if (!gFontSubstituteConverter) {
02546     CallCreateInstance(NS_SAVEASCHARSET_CONTRACTID, &gFontSubstituteConverter);
02547     if (gFontSubstituteConverter) {
02548       res = gFontSubstituteConverter->Init("ISO-8859-1",
02549                               nsISaveAsCharset::attr_EntityAfterCharsetConv +
02550                               nsISaveAsCharset::attr_FallbackQuestionMark +
02551                               nsISaveAsCharset::attr_IgnoreIgnorables,
02552                               nsIEntityConverter::transliterate);
02553       if (NS_FAILED(res)) {
02554         NS_RELEASE(gFontSubstituteConverter);
02555       }
02556     }
02557   }
02558 
02559   // do the transliteration if we have a converter
02560   PRUnichar* result; 
02561   if (gFontSubstituteConverter) {
02562     nsXPIDLCString conv;
02563     nsAutoString tmp(aString, aLength); // we need to pass a null-terminated string
02564     res = gFontSubstituteConverter->Convert(tmp.get(), getter_Copies(conv));
02565     if (NS_SUCCEEDED(res)) {
02566       *aCount = conv.Length();
02567       if (*aCount > 0) {
02568         if (!aResult.EnsureElemCapacity(*aCount)) {
02569           return NS_ERROR_OUT_OF_MEMORY;
02570         }
02571         result = aResult.get();
02572         PRUnichar* u = result;
02573         const char* c = conv.get();
02574         for (; *c; ++c, ++u) {
02575           *u = *c;
02576         }
02577       }
02578       return NS_OK;
02579     }
02580   }
02581 
02582   // we reach here if we couldn't transliterate, so fallback to question marks 
02583   if (!aResult.EnsureElemCapacity(aLength)) return NS_ERROR_OUT_OF_MEMORY;
02584   result = aResult.get();
02585   for (PRUint32 i = 0; i < aLength; i++) {
02586     result[i] = NS_REPLACEMENT_CHAR;
02587   }
02588   *aCount = aLength;
02589   return NS_OK;
02590 }
02591 
02592 PRInt32
02593 nsFontOS2Substitute::GetWidth(HPS aPS, const PRUnichar* aString,
02594                               PRUint32 aLength)
02595 {
02596   nsAutoChar16Buffer buffer;
02597   nsresult rv = SubstituteChars(aString, aLength, buffer, &aLength);
02598   if (NS_FAILED(rv) || !aLength) return 0;
02599 
02600   SIZEL size;
02601   PRUnichar* string = buffer.get();
02602   NS_ConvertUCS2toUTF8 str(Substring(string, string + aLength));
02603   BOOL rc = GetTextExtentPoint32(aPS, (const char*)str.get(), str.Length(),
02604                                  &size);
02605   if (rc == TRUE) {
02606     return size.cx;
02607   } else {
02608     return 0;
02609   }
02610 }
02611 
02612 void
02613 nsFontOS2Substitute::DrawString(HPS aPS, nsDrawingSurfaceOS2* aSurface,
02614                                 PRInt32 aX, PRInt32 aY,
02615                                 const PRUnichar* aString, PRUint32 aLength)
02616 {
02617   nsAutoChar16Buffer buffer;
02618   nsresult rv = SubstituteChars(aString, aLength, buffer, &aLength);
02619   if (NS_FAILED(rv) || !aLength) return;
02620 
02621   POINTL ptl = { aX, aY };
02622   aSurface->NS2PM(&ptl, 1);
02623   PRUnichar* string = buffer.get();
02624   NS_ConvertUCS2toUTF8 str(Substring(string, string + aLength));
02625   ExtTextOut(aPS, ptl.x, ptl.y, 0, NULL, (const char*)str.get(), str.Length(), NULL);
02626 }
02627 #endif /* use_freetype */
02628 
02629 
02630 /**********************************************************
02631     nsFontEnumeratorOS2
02632  **********************************************************/
02633 nsFontEnumeratorOS2::nsFontEnumeratorOS2()
02634 {
02635 }
02636 
02637 NS_IMPL_ISUPPORTS1(nsFontEnumeratorOS2, nsIFontEnumerator)
02638 
02639 // We want vertical fonts (those whose name start with '@') to appear
02640 // immediately following the non-vertical font of the same name.
02641 static int
02642 CompareFontNames(const void* aArg1, const void* aArg2, void* aClosure)
02643 {
02644   const PRUnichar* str1 = *((const PRUnichar**) aArg1);
02645   const PRUnichar* str2 = *((const PRUnichar**) aArg2);
02646 
02647   int rc = 9;
02648   if ((char)str1[0] == '@') {
02649     str1++;
02650     rc = nsCRT::strcmp(str1, str2);
02651     if (rc == 0)
02652       return 1;
02653   }
02654   if ((char)str2[0] == '@') {
02655     str2++;
02656     rc = nsCRT::strcmp(str1, str2);
02657     if (rc == 0)
02658       return -1;
02659   }
02660 
02661   if (rc == 9)
02662     rc = nsCRT::strcmp(str1, str2);
02663   return rc;
02664 }
02665 
02666 NS_IMETHODIMP
02667 nsFontEnumeratorOS2::EnumerateFonts(const char* aLangGroup,
02668   const char* aGeneric, PRUint32* aCount, PRUnichar*** aResult)
02669 {
02670   return EnumerateAllFonts(aCount, aResult);
02671 }
02672 
02673 struct EnumerateAllFontsData
02674 {
02675   PRUnichar** array;      // OUT - array of family names
02676   int count;              // IN/OUT - running count of names in array
02677 };
02678 
02679 PR_STATIC_CALLBACK(PLDHashOperator)
02680 EnumerateAllFontsCallback(GlobalFontEntry* aEntry, void* aData)
02681 {
02682   EnumerateAllFontsData* data = NS_STATIC_CAST(EnumerateAllFontsData*, aData);
02683   data->array[data->count++] = ToNewUnicode(aEntry->GetKey());
02684   return PL_DHASH_NEXT;
02685 }
02686 
02687 NS_IMETHODIMP
02688 nsFontEnumeratorOS2::EnumerateAllFonts(PRUint32* aCount, PRUnichar*** aResult)
02689 {
02690   NS_ENSURE_ARG_POINTER(aCount);
02691   NS_ENSURE_ARG_POINTER(aResult);
02692 
02693   if (!nsFontMetricsOS2::gGlobalFonts) {
02694     nsresult res = nsFontMetricsOS2::InitializeGlobalFonts();
02695     if (NS_FAILED(res)) {
02696       FreeGlobals();
02697       return res;
02698     }
02699   }
02700 
02701   *aCount = nsFontMetricsOS2::gGlobalFonts->Count();
02702   PRUnichar** array = (PRUnichar**)nsMemory::Alloc(*aCount * sizeof(PRUnichar*));
02703   NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
02704 
02705   EnumerateAllFontsData data = {array, 0};
02706   nsFontMetricsOS2::gGlobalFonts->EnumerateEntries(EnumerateAllFontsCallback,
02707                                                   &data);
02708 
02709   NS_QuickSort(array, *aCount, sizeof(PRUnichar*), CompareFontNames, nsnull);
02710 
02711   *aResult = array;
02712 
02713   return NS_OK;
02714 }
02715 
02716 NS_IMETHODIMP
02717 nsFontEnumeratorOS2::HaveFontFor(const char* aLangGroup, PRBool* aResult)
02718 {
02719   NS_ENSURE_ARG_POINTER(aLangGroup);
02720   NS_ENSURE_ARG_POINTER(aResult);
02721   *aResult = PR_FALSE;
02722 
02723   // XXX stub
02724   NS_ASSERTION( 0, "HaveFontFor is not implemented" );
02725 
02726   return NS_ERROR_NOT_IMPLEMENTED;
02727 }
02728 
02729 NS_IMETHODIMP
02730 nsFontEnumeratorOS2::GetDefaultFont(const char *aLangGroup, 
02731   const char *aGeneric, PRUnichar **aResult)
02732 {
02733   // aLangGroup=null or ""  means any (i.e., don't care)
02734   // aGeneric=null or ""  means any (i.e, don't care)
02735 
02736   NS_ENSURE_ARG_POINTER(aResult);
02737   *aResult = nsnull;
02738 
02739   return NS_OK;
02740 }
02741 
02742 NS_IMETHODIMP
02743 nsFontEnumeratorOS2::UpdateFontList(PRBool *updateFontList)
02744 {
02745   *updateFontList = PR_FALSE; // always return false for now
02746   NS_ASSERTION( 0, "UpdateFontList is not implemented" );
02747   return NS_ERROR_NOT_IMPLEMENTED;
02748 }
02749