Back to index

lightning-sunbird  0.9+nobinonly
nsFontMetricsXft.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* vim:expandtab:shiftwidth=4:tabstop=4:
00003  */
00004 /* ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is mozilla.org code.
00018  *
00019  * The Initial Developer of the Original Code is Christopher Blizzard
00020  * <blizzard@mozilla.org>.  Portions created by the Initial Developer
00021  * are Copyright (C) 2002 the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Pierre Phaneuf <pp@ludusdesign.com> 
00025  *   Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
00026  *   Brian Stell <bstell@ix.netcom.com>
00027  *   Morten Nilsen <morten@nilsen.com>
00028  *   Jungshik Shin <jshin@mailaps.org>
00029  *   Jim Nance <jim_nance@yahoo.com>
00030  *
00031  * Alternatively, the contents of this file may be used under the terms of
00032  * either the GNU General Public License Version 2 or later (the "GPL"), or
00033  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00034  * in which case the provisions of the GPL or the LGPL are applicable instead
00035  * of those above. If you wish to allow use of your version of this file only
00036  * under the terms of either the GPL or the LGPL, and not to allow others to
00037  * use your version of this file under the terms of the MPL, indicate your
00038  * decision by deleting the provisions above and replace them with the notice
00039  * and other provisions required by the GPL or the LGPL. If you do not delete
00040  * the provisions above, a recipient may use your version of this file under
00041  * the terms of any one of the MPL, the GPL or the LGPL.
00042  *
00043  * ***** END LICENSE BLOCK ***** */
00044 
00045 #include "nsISupportsUtils.h"
00046 #include "nsServiceManagerUtils.h"
00047 #include "nsIPref.h"
00048 #include "nsFontMetricsXft.h"
00049 #include "prenv.h"
00050 #include "prprf.h"
00051 #include "prlink.h"
00052 #include "nsQuickSort.h"
00053 #include "nsFont.h"
00054 #include "nsIDeviceContext.h"
00055 #include "nsRenderingContextGTK.h"
00056 #include "nsDeviceContextGTK.h"
00057 #include "nsReadableUtils.h"
00058 #include "nsUnicharUtils.h"
00059 #include "nsITimelineService.h"
00060 #include "nsICharsetConverterManager.h"
00061 #include "nsICharRepresentable.h"
00062 #include "nsIPersistentProperties2.h"
00063 #include "nsCompressedCharMap.h"
00064 #include "nsNetUtil.h"
00065 #include "nsClassHashtable.h"
00066 #include "nsAutoBuffer.h"
00067 #include "nsFontConfigUtils.h"
00068 
00069 #include <gdk/gdkx.h>
00070 #include <freetype/tttables.h>
00071 #include <freetype/freetype.h>
00072 
00073 #define FORCE_PR_LOG
00074 #include "prlog.h"
00075 
00076 // Abstract class nsFontXft is the base class for nsFontXftUnicode and
00077 // nsFontXftCustom, either of which is an instance of fonts.  The 
00078 // |nsFontMetricsXft| class is made up of a collection of these little 
00079 // fonts, really.
00080 
00081 class nsAutoDrawSpecBuffer;
00082 
00083 class nsFontXft {
00084 public:
00085     nsFontXft(FcPattern *aPattern, FcPattern *aFontName);
00086     virtual ~nsFontXft() = 0;
00087 
00088     // Callers outside of FindFont and DoMatch (which deal directly with
00089     // mLoadedFonts) do not need to call this; they can access mXftFont
00090     // directly since it is guaranteed to be non-null.
00091     XftFont   *GetXftFont (void);
00092     virtual nsresult GetTextExtents32 (const FcChar32 *aString, PRUint32 aLen, 
00093                                        XGlyphInfo &aGlyphInfo);
00094     gint     GetWidth32               (const FcChar32 *aString, PRUint32 aLen);
00095 
00096 #ifdef MOZ_MATHML
00097     nsresult GetBoundingMetrics32 (const FcChar32 *aString, 
00098                                    PRUint32 aLength,
00099                                    nsBoundingMetrics &aBoundingMetrics);
00100 #endif /* MOZ_MATHML */
00101 
00102     PRInt16    GetMaxAscent(void);
00103     PRInt16    GetMaxDescent(void);
00104 
00105     virtual PRBool     HasChar(PRUint32 aChar) = 0;
00106     virtual FT_UInt    CharToGlyphIndex(FcChar32 aChar);
00107 
00108     virtual nsresult   DrawStringSpec(FcChar32* aString, PRUint32 aLen,
00109                                       void *aData);
00110                                       
00111     // a reference to the font loaded and information about it.  
00112     XftFont   *mXftFont;
00113     FcPattern *mPattern;
00114     FcPattern *mFontName;
00115     FcCharSet *mCharset;
00116 };
00117 
00118 class nsFontXftInfo;
00119 
00120 // class for regular 'Unicode' fonts that are actually what they claim to be. 
00121 class nsFontXftUnicode : public nsFontXft {
00122 public:
00123     nsFontXftUnicode(FcPattern *aPattern, FcPattern *aFontName)
00124       : nsFontXft(aPattern, aFontName)
00125     { }
00126 
00127     virtual ~nsFontXftUnicode();
00128 
00129     virtual PRBool     HasChar (PRUint32 aChar);
00130 };
00131 
00132 // class for custom encoded fonts that pretend to have Unicode cmap.
00133 // There are two kinds of them: 
00134 // 1. 'narrow' custom encoded fonts such as  mathematica fonts, 
00135 // TeX Computer Roman fonts, Symbol font, etc.
00136 // 2. 'wide' custom  encoded fonts such as 
00137 // Korean Jamo fonts, non-opentype fonts for Indic scripts.
00138 //
00139 // The biggest difference between 'narrow' and 'wide' fonts is that 
00140 // the result of calling mConverter for 'wide' fonts is a sequence of 
00141 // 16bit pseudo-Unicode code points.  For 'narrow' fonts, it's 
00142 // a sequence of 8bit code points. 
00143 class nsFontXftCustom : public nsFontXft {
00144 public:
00145     nsFontXftCustom(FcPattern* aPattern, 
00146                     FcPattern* aFontName, 
00147                     nsFontXftInfo* aFontInfo)
00148       : nsFontXft(aPattern, aFontName)
00149       , mFontInfo(aFontInfo)
00150       , mFT_Face(nsnull)
00151     { }
00152 
00153     virtual ~nsFontXftCustom();
00154 
00155     virtual PRBool   HasChar            (PRUint32 aChar);
00156     virtual FT_UInt  CharToGlyphIndex   (FcChar32 aChar);
00157     virtual nsresult GetTextExtents32   (const FcChar32 *aString, 
00158                                          PRUint32 aLen, XGlyphInfo &aGlyphInfo);
00159     virtual nsresult DrawStringSpec     (FcChar32* aString, PRUint32 aLen,
00160                                          void *aData);
00161 
00162 private:
00163     nsFontXftInfo *mFontInfo; 
00164 
00165     // freetype fontface : used for direct access to glyph indices
00166     // in GetWidth32() and DrawStringSpec().
00167     FT_Face    mFT_Face;
00168     nsresult   SetFT_FaceCharmap (void);
00169 };
00170 
00171 enum nsXftFontType {
00172     eFontTypeUnicode,
00173     eFontTypeCustom,
00174     eFontTypeCustomWide
00175 };
00176 
00177 // a class to hold some essential information about font. 
00178 // it's shared and cached with 'family name' as hash key.
00179 class nsFontXftInfo {
00180     public:
00181     nsFontXftInfo() : mCCMap(nsnull), mConverter(0), 
00182                       mFontType(eFontTypeUnicode) 
00183                       { }
00184 
00185     ~nsFontXftInfo() {
00186         if (mCCMap)
00187             FreeCCMap(mCCMap);
00188     }
00189 
00190     // Char. coverage map(replacing mCharset in nsFontXft)
00191     PRUint16*                   mCCMap;
00192     // converter from Unicode to font-specific encoding
00193     nsCOMPtr<nsIUnicodeEncoder> mConverter;
00194     // Unicode, Custom, CustomWide
00195     nsXftFontType               mFontType;
00196     // Truetype cmap to use for direct retrieval of GIDs with FT_Get_Char_Index
00197     // for 'narrow' custom fonts.
00198     FT_Encoding                 mFT_Encoding;
00199 };
00200 
00201 struct DrawStringData {
00202     nscoord                x;
00203     nscoord                y;
00204     const nscoord         *spacing;
00205     nscoord                xOffset;
00206     nsRenderingContextGTK *context;
00207     XftDraw               *draw;
00208     XftColor               color;
00209     float                  p2t;
00210     nsAutoDrawSpecBuffer  *drawBuffer;
00211 };
00212 
00213 #ifdef MOZ_MATHML
00214 struct BoundingMetricsData {
00215     nsBoundingMetrics *bm;
00216     PRBool firstTime;
00217 };
00218 #endif /* MOZ_MATHML */
00219 
00220 #define AUTO_BUFFER_SIZE 3000
00221 typedef nsAutoBuffer<FcChar32, AUTO_BUFFER_SIZE> nsAutoFcChar32Buffer;
00222 
00223 static int      CompareFontNames (const void* aArg1, const void* aArg2,
00224                                   void* aClosure);
00225 static nsresult EnumFontsXft     (nsIAtom* aLangGroup, const char* aGeneric,
00226                                   PRUint32* aCount, PRUnichar*** aResult);
00227 
00228 static        void ConvertCharToUCS4    (const char *aString,
00229                                          PRUint32 aLength,
00230                                          nsAutoFcChar32Buffer &aOutBuffer,
00231                                          PRUint32 *aOutLen);
00232 static        void ConvertUnicharToUCS4 (const PRUnichar *aString,
00233                                          PRUint32 aLength,
00234                                          nsAutoFcChar32Buffer &aOutBuffer,
00235                                          PRUint32 *aOutLen);
00236 static    nsresult ConvertUCS4ToCustom  (FcChar32 *aSrc, PRUint32 aSrcLen,
00237                                          PRUint32& aDestLen, 
00238                                          nsIUnicodeEncoder *aConverter, 
00239                                          PRBool aIsWide, 
00240                                          nsAutoFcChar32Buffer &Result);
00241 
00242 #ifdef MOZ_WIDGET_GTK2
00243 static void GdkRegionSetXftClip(GdkRegion *aGdkRegion, XftDraw *aDraw);
00244 #endif
00245 
00246 // This is the scaling factor that we keep fonts limited to against
00247 // the display size.  If a pixel size is requested that is more than
00248 // this factor larger than the height of the display, it's clamped to
00249 // that value instead of the requested size.
00250 #define FONT_MAX_FONT_SCALE 2
00251 
00252 #define UCS2_REPLACEMENT 0xFFFD
00253 
00254 #define IS_NON_BMP(c) ((c) >> 16)
00255 #define IS_NON_SURROGATE(c) ((c < 0xd800 || c > 0xdfff))
00256 
00257 // a helper class for Xft glyph drawings
00258 class nsAutoDrawSpecBuffer {
00259 public:
00260     enum {BUFFER_LEN=1024};
00261     nsAutoDrawSpecBuffer(XftDraw *aDraw, XftColor *aColor) :
00262                          mDraw(aDraw), mColor(aColor), mSpecPos(0) {}
00263 
00264     ~nsAutoDrawSpecBuffer() {
00265         Flush();
00266     }
00267 
00268     void Flush();
00269     void Draw(nscoord x, nscoord y, XftFont *font, FT_UInt glyph);
00270 
00271 private:
00272     XftDraw         *mDraw;
00273     XftColor        *mColor;
00274     PRUint32         mSpecPos;
00275     XftGlyphFontSpec mSpecBuffer[BUFFER_LEN];
00276 };
00277 
00278 
00279 PRLogModuleInfo *gXftFontLoad = nsnull;
00280 static int gNumInstances = 0;
00281 
00282 #undef DEBUG_XFT_MEMORY
00283 #ifdef DEBUG_XFT_MEMORY
00284 
00285 extern "C" {
00286 extern void XftMemReport(void);
00287 extern void FcMemReport(void);
00288 }
00289 #endif
00290 
00291 static nsresult
00292 EnumFontsXft(nsIAtom* aLangGroup, const char* aGeneric,
00293              PRUint32* aCount, PRUnichar*** aResult);
00294  
00295 static NS_DEFINE_CID(kCharsetConverterManagerCID,
00296                      NS_ICHARSETCONVERTERMANAGER_CID);
00297 
00298 static PRBool                      gInitialized = PR_FALSE;
00299 static nsIPersistentProperties*    gFontEncodingProperties = nsnull;
00300 static nsICharsetConverterManager* gCharsetManager = nsnull;
00301 
00302 typedef nsClassHashtable<nsCharPtrHashKey, nsFontXftInfo> nsFontXftInfoHash; 
00303 static nsFontXftInfoHash           gFontXftMaps;
00304 #define INITIAL_FONT_MAP_SIZE      32
00305 
00306 static nsresult       GetEncoding(const char* aFontName,
00307                                   char **aEncoding,
00308                                   nsXftFontType &aType,
00309                                   FT_Encoding &aFTEncoding);
00310 static nsresult       GetConverter(const char* aEncoding,
00311                                    nsIUnicodeEncoder** aConverter);
00312 static nsresult       FreeGlobals(void);
00313 static nsFontXftInfo* GetFontXftInfo(FcPattern* aPattern);
00314 
00315 nsFontMetricsXft::nsFontMetricsXft(): mMiniFont(nsnull)
00316 {
00317     if (!gXftFontLoad)
00318         gXftFontLoad = PR_NewLogModule("XftFontLoad");
00319 
00320     ++gNumInstances;
00321 }
00322 
00323 nsFontMetricsXft::~nsFontMetricsXft()
00324 {
00325     if (mDeviceContext)
00326         mDeviceContext->FontMetricsDeleted(this);
00327 
00328     if (mPattern)
00329         FcPatternDestroy(mPattern);
00330 
00331     for (PRInt32 i= mLoadedFonts.Count() - 1; i >= 0; --i) {
00332         nsFontXft *font = (nsFontXft *)mLoadedFonts.ElementAt(i);
00333         delete font;
00334     }
00335 
00336     if (mMiniFont)
00337         XftFontClose(GDK_DISPLAY(), mMiniFont);
00338 
00339     if (--gNumInstances == 0) {
00340         FreeGlobals();
00341 #ifdef DEBUG_XFT_MEMORY
00342         XftMemReport();
00343         FcMemReport();
00344 #endif
00345     }
00346 }
00347 
00348 NS_IMPL_ISUPPORTS1(nsFontMetricsXft, nsIFontMetrics)
00349 
00350 // nsIFontMetrics impl
00351 
00352 NS_IMETHODIMP
00353 nsFontMetricsXft::Init(const nsFont& aFont, nsIAtom* aLangGroup,
00354                        nsIDeviceContext *aContext)
00355 {
00356     mFont = aFont;
00357     mLangGroup = aLangGroup;
00358 
00359     // Hang onto the device context
00360     mDeviceContext = aContext;
00361 
00362     float app2dev = mDeviceContext->AppUnitsToDevUnits();
00363     mPixelSize = NSTwipsToFloatPixels(mFont.size, app2dev);
00364 
00365     // Make sure to clamp the pixel size to something reasonable so we
00366     // don't make the X server blow up.
00367     nscoord screenPixels = gdk_screen_height();
00368     mPixelSize = PR_MIN((screenPixels - 1) * FONT_MAX_FONT_SCALE, mPixelSize);
00369     mPixelSize = PR_MIN(2000, mPixelSize);
00370 
00371     // enumerate over the font names passed in
00372     mFont.EnumerateFamilies(nsFontMetricsXft::EnumFontCallback, this);
00373 
00374     nsCOMPtr<nsIPref> prefService;
00375     prefService = do_GetService(NS_PREF_CONTRACTID);
00376     if (!prefService)
00377         return NS_ERROR_FAILURE;
00378         
00379     nsXPIDLCString value;
00380     const char* langGroup;
00381     mLangGroup->GetUTF8String(&langGroup);
00382 
00383     // Set up the default font name if it's not set
00384     if (!mGenericFont) {
00385         nsCAutoString name("font.default.");
00386         name.Append(langGroup);
00387         prefService->CopyCharPref(name.get(), getter_Copies(value));
00388 
00389         if (value.get())
00390             mDefaultFont = value.get();
00391         else
00392             mDefaultFont = "serif";
00393         
00394         mGenericFont = &mDefaultFont;
00395     }
00396 
00397     // set up the minimum sizes for fonts
00398     if (mLangGroup) {
00399         nsCAutoString name("font.min-size.");
00400 
00401         if (mGenericFont->Equals("monospace"))
00402             name.Append("fixed");
00403         else
00404             name.Append("variable");
00405 
00406         name.Append(char('.'));
00407         name.Append(langGroup);
00408 
00409         PRInt32 minimum = 0;
00410         nsresult res;
00411         res = prefService->GetIntPref(name.get(), &minimum);
00412         if (NS_FAILED(res))
00413             prefService->GetDefaultIntPref(name.get(), &minimum);
00414 
00415         if (minimum < 0)
00416             minimum = 0;
00417 
00418         if (mPixelSize < minimum)
00419             mPixelSize = minimum;
00420     }
00421 
00422     // Make sure that the pixel size is at least greater than zero
00423     if (mPixelSize < 1) {
00424 #ifdef DEBUG
00425         printf("*** Warning: nsFontMetricsXft was passed a pixel size of %f\n",
00426                mPixelSize);
00427 #endif
00428         mPixelSize = 1;
00429     }
00430     if (!gInitialized) {
00431         CallGetService(kCharsetConverterManagerCID, &gCharsetManager);
00432         if (!gCharsetManager) {
00433             FreeGlobals();
00434             return NS_ERROR_FAILURE;
00435         }
00436 
00437         if (!gFontXftMaps.IsInitialized() && 
00438             !gFontXftMaps.Init(INITIAL_FONT_MAP_SIZE)) {
00439             FreeGlobals();
00440             return NS_ERROR_OUT_OF_MEMORY;
00441         }
00442 
00443         gInitialized = PR_TRUE;
00444     }
00445 
00446     if (NS_FAILED(RealizeFont()))
00447         return NS_ERROR_FAILURE;
00448 
00449     return NS_OK;
00450 }
00451 
00452 NS_IMETHODIMP
00453 nsFontMetricsXft::Destroy()
00454 {
00455     mDeviceContext = nsnull;
00456 
00457     return NS_OK;
00458 }
00459 
00460 NS_IMETHODIMP
00461 nsFontMetricsXft::GetLangGroup(nsIAtom** aLangGroup)
00462 {
00463     *aLangGroup = mLangGroup;
00464     NS_IF_ADDREF(*aLangGroup);
00465 
00466     return NS_OK;
00467 }
00468 
00469 NS_IMETHODIMP
00470 nsFontMetricsXft::GetFontHandle(nsFontHandle &aHandle)
00471 {
00472     return NS_ERROR_NOT_IMPLEMENTED;
00473 }
00474 
00475 // nsIFontMetricsXft impl
00476 
00477 nsresult
00478 nsFontMetricsXft::GetWidth(const char* aString, PRUint32 aLength,
00479                            nscoord& aWidth,
00480                            nsRenderingContextGTK *aContext)
00481 {
00482     NS_TIMELINE_MARK_FUNCTION("GetWidth");
00483 
00484     XftFont *font = mWesternFont->mXftFont;
00485     NS_ASSERTION(font, "FindFont returned a bad font");
00486 
00487     XGlyphInfo glyphInfo;
00488 
00489     // casting away const for aString but it  should be safe
00490     XftTextExtents8(GDK_DISPLAY(), font, (FcChar8 *)aString,
00491                     aLength, &glyphInfo);
00492 
00493     float f;
00494     f = mDeviceContext->DevUnitsToAppUnits();
00495     aWidth = NSToCoordRound(glyphInfo.xOff * f);
00496 
00497     return NS_OK;
00498 }
00499 
00500 nsresult
00501 nsFontMetricsXft::GetWidth(const PRUnichar* aString, PRUint32 aLength,
00502                            nscoord& aWidth, PRInt32 *aFontID,
00503                            nsRenderingContextGTK *aContext)
00504 {
00505     NS_TIMELINE_MARK_FUNCTION("GetWidth");
00506     if (!aLength) {
00507         aWidth = 0;
00508         return NS_OK;
00509     }
00510 
00511     gint rawWidth = RawGetWidth(aString, aLength);
00512 
00513     float f;
00514     f = mDeviceContext->DevUnitsToAppUnits();
00515     aWidth = NSToCoordRound(rawWidth * f);
00516 
00517     if (aFontID)
00518         *aFontID = 0;
00519 
00520     return NS_OK;
00521 }
00522 
00523 nsresult
00524 nsFontMetricsXft::GetTextDimensions(const PRUnichar* aString,
00525                                     PRUint32 aLength,
00526                                     nsTextDimensions& aDimensions, 
00527                                     PRInt32* aFontID,
00528                                     nsRenderingContextGTK *aContext)
00529 {
00530     NS_TIMELINE_MARK_FUNCTION("GetTextDimensions");
00531     aDimensions.Clear();
00532 
00533     if (!aLength)
00534         return NS_OK;
00535 
00536     nsresult rv;
00537     rv = EnumerateGlyphs(aString, aLength,
00538                          &nsFontMetricsXft::TextDimensionsCallback,
00539                          &aDimensions);
00540 
00541     NS_ENSURE_SUCCESS(rv, rv);
00542 
00543     float P2T;
00544     P2T = mDeviceContext->DevUnitsToAppUnits();
00545 
00546     aDimensions.width = NSToCoordRound(aDimensions.width * P2T);
00547     aDimensions.ascent = NSToCoordRound(aDimensions.ascent * P2T);
00548     aDimensions.descent = NSToCoordRound(aDimensions.descent * P2T);
00549 
00550     if (nsnull != aFontID)
00551         *aFontID = 0;
00552 
00553     return NS_OK;
00554 }
00555 
00556 nsresult
00557 nsFontMetricsXft::GetTextDimensions(const char*         aString,
00558                                     PRInt32             aLength,
00559                                     PRInt32             aAvailWidth,
00560                                     PRInt32*            aBreaks,
00561                                     PRInt32             aNumBreaks,
00562                                     nsTextDimensions&   aDimensions,
00563                                     PRInt32&            aNumCharsFit,
00564                                     nsTextDimensions&   aLastWordDimensions,
00565                                     PRInt32*            aFontID,
00566                                     nsRenderingContextGTK *aContext)
00567 {
00568     NS_NOTREACHED("GetTextDimensions");
00569     return NS_ERROR_NOT_IMPLEMENTED;
00570 }
00571 
00572 nsresult
00573 nsFontMetricsXft::GetTextDimensions(const PRUnichar*    aString,
00574                                     PRInt32             aLength,
00575                                     PRInt32             aAvailWidth,
00576                                     PRInt32*            aBreaks,
00577                                     PRInt32             aNumBreaks,
00578                                     nsTextDimensions&   aDimensions,
00579                                     PRInt32&            aNumCharsFit,
00580                                     nsTextDimensions&   aLastWordDimensions,
00581                                     PRInt32*            aFontID,
00582                                     nsRenderingContextGTK *aContext)
00583 {
00584     NS_NOTREACHED("GetTextDimensions");
00585     return NS_ERROR_NOT_IMPLEMENTED;
00586 }
00587 
00588 nsresult
00589 nsFontMetricsXft::DrawString(const char *aString, PRUint32 aLength,
00590                              nscoord aX, nscoord aY,
00591                              const nscoord* aSpacing,
00592                              nsRenderingContextGTK *aContext,
00593                              nsDrawingSurfaceGTK *aSurface)
00594 {
00595     NS_TIMELINE_MARK_FUNCTION("DrawString");
00596 
00597     // The data we will carry through the function
00598     DrawStringData data;
00599     memset(&data, 0, sizeof(data));
00600 
00601     data.x = aX;
00602     data.y = aY;
00603     data.spacing = aSpacing;
00604     data.context = aContext;
00605     data.p2t = mDeviceContext->DevUnitsToAppUnits();
00606 
00607     PrepareToDraw(aContext, aSurface, &data.draw, data.color);
00608 
00609     nsAutoDrawSpecBuffer drawBuffer(data.draw, &data.color);
00610     data.drawBuffer = &drawBuffer;
00611 
00612     return EnumerateGlyphs(aString, aLength,
00613                            &nsFontMetricsXft::DrawStringCallback, &data);
00614 }
00615 
00616 nsresult
00617 nsFontMetricsXft::DrawString(const PRUnichar* aString, PRUint32 aLength,
00618                              nscoord aX, nscoord aY,
00619                              PRInt32 aFontID,
00620                              const nscoord* aSpacing,
00621                              nsRenderingContextGTK *aContext,
00622                              nsDrawingSurfaceGTK *aSurface)
00623 {
00624     NS_TIMELINE_MARK_FUNCTION("DrawString");
00625 
00626     // The data we will carry through the function
00627     DrawStringData data;
00628     memset(&data, 0, sizeof(data));
00629 
00630     data.x = aX;
00631     data.y = aY;
00632     data.spacing = aSpacing;
00633     data.context = aContext;
00634     data.p2t = mDeviceContext->DevUnitsToAppUnits();
00635 
00636     // set up our colors and clip regions
00637     PrepareToDraw(aContext, aSurface, &data.draw, data.color);
00638 
00639     nsAutoDrawSpecBuffer drawBuffer(data.draw, &data.color);
00640     data.drawBuffer = &drawBuffer;
00641 
00642     return EnumerateGlyphs(aString, aLength,
00643                            &nsFontMetricsXft::DrawStringCallback, &data);
00644 }
00645 
00646 #ifdef MOZ_MATHML
00647 
00648 nsresult
00649 nsFontMetricsXft::GetBoundingMetrics(const char *aString, PRUint32 aLength,
00650                                      nsBoundingMetrics &aBoundingMetrics,
00651                                      nsRenderingContextGTK *aContext)
00652 {
00653     aBoundingMetrics.Clear(); 
00654 
00655     if (!aString || !aLength)
00656         return NS_ERROR_FAILURE;
00657 
00658     // The data we will carry through the function
00659     BoundingMetricsData data;
00660     data.bm = &aBoundingMetrics;
00661     // the beginning of a string needs a special treatment (see
00662     // 'operator +' definition of nsBoundingMetrics.)
00663     data.firstTime = PR_TRUE; 
00664 
00665     nsresult rv;
00666     rv = EnumerateGlyphs(aString, aLength,
00667                          &nsFontMetricsXft::BoundingMetricsCallback, &data);
00668     NS_ENSURE_SUCCESS(rv, rv);
00669 
00670     float P2T;
00671     P2T = mDeviceContext->DevUnitsToAppUnits();
00672 
00673     aBoundingMetrics.leftBearing =
00674         NSToCoordRound(aBoundingMetrics.leftBearing * P2T);
00675     aBoundingMetrics.rightBearing =
00676         NSToCoordRound(aBoundingMetrics.rightBearing * P2T);
00677     aBoundingMetrics.width = NSToCoordRound(aBoundingMetrics.width * P2T);
00678     aBoundingMetrics.ascent = NSToCoordRound(aBoundingMetrics.ascent * P2T);
00679     aBoundingMetrics.descent = NSToCoordRound(aBoundingMetrics.descent * P2T);
00680 
00681     return NS_OK;
00682 }
00683 
00684 nsresult
00685 nsFontMetricsXft::GetBoundingMetrics(const PRUnichar *aString,
00686                                      PRUint32 aLength,
00687                                      nsBoundingMetrics &aBoundingMetrics,
00688                                      PRInt32 *aFontID,
00689                                      nsRenderingContextGTK *aContext)
00690 {
00691     aBoundingMetrics.Clear(); 
00692 
00693     if (!aString || !aLength)
00694         return NS_ERROR_FAILURE;
00695 
00696     // The data we will carry through the function
00697     BoundingMetricsData data;
00698     data.bm = &aBoundingMetrics;
00699     // the beginning of a string needs a special treatment (see
00700     // 'operator +' definition of nsBoundingMetrics.)
00701     data.firstTime = PR_TRUE; 
00702 
00703     nsresult rv;
00704     rv = EnumerateGlyphs(aString, aLength,
00705                          &nsFontMetricsXft::BoundingMetricsCallback, &data);
00706     NS_ENSURE_SUCCESS(rv, rv);
00707 
00708     float P2T;
00709     P2T = mDeviceContext->DevUnitsToAppUnits();
00710 
00711     aBoundingMetrics.leftBearing =
00712         NSToCoordRound(aBoundingMetrics.leftBearing * P2T);
00713     aBoundingMetrics.rightBearing =
00714         NSToCoordRound(aBoundingMetrics.rightBearing * P2T);
00715     aBoundingMetrics.width = NSToCoordRound(aBoundingMetrics.width * P2T);
00716     aBoundingMetrics.ascent = NSToCoordRound(aBoundingMetrics.ascent * P2T);
00717     aBoundingMetrics.descent = NSToCoordRound(aBoundingMetrics.descent * P2T);
00718 
00719     if (nsnull != aFontID)
00720         *aFontID = 0;
00721 
00722     return NS_OK;
00723 }
00724 
00725 #endif /* MOZ_MATHML */
00726 
00727 GdkFont*
00728 nsFontMetricsXft::GetCurrentGDKFont(void)
00729 {
00730     return nsnull;
00731 }
00732 
00733 nsresult
00734 nsFontMetricsXft::SetRightToLeftText(PRBool aIsRTL)
00735 {
00736     return NS_OK;
00737 }
00738 
00739 PRBool
00740 nsFontMetricsXft::GetRightToLeftText()
00741 {
00742     return PR_FALSE;
00743 }
00744 
00745 nsresult
00746 nsFontMetricsXft::GetClusterInfo(const PRUnichar *aText,
00747                                  PRUint32 aLength,
00748                                  PRUint8 *aClusterStarts)
00749 {
00750     return NS_ERROR_NOT_IMPLEMENTED;
00751 }
00752 
00753 PRInt32
00754 nsFontMetricsXft::GetPosition(const PRUnichar *aText,
00755                               PRUint32 aLength,
00756                               nsPoint aPt)
00757 {
00758     return -1;
00759 }
00760 
00761 nsresult
00762 nsFontMetricsXft::GetRangeWidth(const PRUnichar *aText,
00763                                 PRUint32 aLength,
00764                                 PRUint32 aStart,
00765                                 PRUint32 aEnd,
00766                                 PRUint32 &aWidth)
00767 {
00768     return NS_ERROR_NOT_IMPLEMENTED;
00769 }
00770 
00771 nsresult
00772 nsFontMetricsXft::GetRangeWidth(const char *aText,
00773                                 PRUint32 aLength,
00774                                 PRUint32 aStart,
00775                                 PRUint32 aEnd,
00776                                 PRUint32 &aWidth)
00777 {
00778     return NS_ERROR_NOT_IMPLEMENTED;
00779 }
00780 
00781 PRUint32
00782 nsFontMetricsXft::GetHints(void)
00783 {
00784     return 0;
00785 }
00786 
00787 nsresult
00788 nsFontMetricsXft::RealizeFont(void)
00789 {
00790     // make sure that the western font is loaded since we need that to
00791     // get the font metrics
00792     mWesternFont = FindFont('a');
00793     if (!mWesternFont)
00794         return NS_ERROR_FAILURE;
00795 
00796     return CacheFontMetrics();
00797 }
00798 
00799 // rounding and truncation functions for a Freetype floating point number 
00800 // (FT26Dot6) stored in a 32bit integer with high 26 bits for the integer
00801 // part and low 6 bits for the fractional part. 
00802 #define MOZ_FT_ROUND(x) (((x) + 32) & ~63) // 63 = 2^6 - 1
00803 #define MOZ_FT_TRUNC(x) ((x) >> 6)
00804 #define CONVERT_DESIGN_UNITS_TO_PIXELS(v, s) \
00805         MOZ_FT_TRUNC(MOZ_FT_ROUND(FT_MulFix((v) , (s))))
00806 
00807 nsresult
00808 nsFontMetricsXft::CacheFontMetrics(void)
00809 {
00810     // Get our scale factor
00811     float f;
00812     float val;
00813     f = mDeviceContext->DevUnitsToAppUnits();
00814     
00815     // Get our font face
00816     XftFont *xftFont = mWesternFont->mXftFont;
00817     NS_ASSERTION(xftFont, "FindFont returned a bad font");
00818 
00819     FT_Face face = XftLockFace(xftFont);
00820     if (!face)
00821         return NS_ERROR_NOT_AVAILABLE;
00822 
00823     // mEmHeight (size in pixels of EM height)
00824     int size;
00825     if (FcPatternGetInteger(mWesternFont->mPattern, FC_PIXEL_SIZE, 0, &size) !=
00826         FcResultMatch) {
00827         size = 12;
00828     }
00829     mEmHeight = PR_MAX(1, nscoord(size * f));
00830 
00831     // mMaxAscent
00832     mMaxAscent = nscoord(xftFont->ascent * f);
00833 
00834     // mMaxDescent
00835     mMaxDescent = nscoord(xftFont->descent * f);
00836 
00837     nscoord lineHeight = mMaxAscent + mMaxDescent;
00838 
00839     // mLeading (needs ascent and descent and EM height) 
00840     if (lineHeight > mEmHeight)
00841         mLeading = lineHeight - mEmHeight;
00842     else
00843         mLeading = 0;
00844 
00845     // mMaxHeight (needs ascent and descent)
00846     mMaxHeight = lineHeight;
00847 
00848     // mEmAscent (needs maxascent, EM height, ascent and descent)
00849     mEmAscent = nscoord(mMaxAscent * mEmHeight / lineHeight);
00850 
00851     // mEmDescent (needs EM height and EM ascent
00852     mEmDescent = mEmHeight - mEmAscent;
00853 
00854     // mMaxAdvance
00855     mMaxAdvance = nscoord(xftFont->max_advance_width * f);
00856     // X may screw up if we try to measure/draw more than 32767 pixels in
00857     // one operation.
00858     mMaxStringLength = (PRInt32)floor(32767.0/xftFont->max_advance_width);
00859     mMaxStringLength = PR_MAX(1, mMaxStringLength);
00860 
00861     // mSpaceWidth (width of a space)
00862     gint rawWidth;
00863     PRUnichar unispace(' ');
00864     rawWidth = RawGetWidth(&unispace, 1);
00865     mSpaceWidth = NSToCoordRound(rawWidth * f);
00866 
00867     // mAveCharWidth (width of an 'average' char)
00868     PRUnichar xUnichar('x');
00869     rawWidth = RawGetWidth(&xUnichar, 1);
00870     mAveCharWidth = NSToCoordRound(rawWidth * f);
00871 
00872     // mXHeight (height of an 'x' character)
00873     if (FcCharSetHasChar(mWesternFont->mCharset, xUnichar)) {
00874         XGlyphInfo extents;
00875         XftTextExtents16(GDK_DISPLAY(), xftFont, &xUnichar, 1, &extents);
00876         mXHeight = extents.height;
00877     }
00878     else {
00879         // 56% of ascent, best guess for non-true type or asian fonts
00880         mXHeight = nscoord(((float)mMaxAscent) * 0.56);
00881     }
00882     mXHeight = nscoord(mXHeight * f);
00883 
00884     // mUnderlineOffset (offset for underlines)
00885     val = CONVERT_DESIGN_UNITS_TO_PIXELS(face->underline_position,
00886                                          face->size->metrics.y_scale);
00887     if (val) {
00888         mUnderlineOffset = NSToIntRound(val * f);
00889     }
00890     else {
00891         mUnderlineOffset =
00892             -NSToIntRound(PR_MAX(1, floor(0.1 * xftFont->height + 0.5)) * f);
00893     }
00894 
00895     // mUnderlineSize (thickness of an underline)
00896     val = CONVERT_DESIGN_UNITS_TO_PIXELS(face->underline_thickness,
00897                                          face->size->metrics.y_scale);
00898     if (val) {
00899         mUnderlineSize = nscoord(PR_MAX(f, NSToIntRound(val * f)));
00900     }
00901     else {
00902         mUnderlineSize =
00903             NSToIntRound(PR_MAX(1, floor(0.05 * xftFont->height + 0.5)) * f);
00904     }
00905 
00906     TT_OS2 *os2 = (TT_OS2 *) FT_Get_Sfnt_Table(face, ft_sfnt_os2);
00907 
00908     // mSuperscriptOffset
00909     if (os2 && os2->ySuperscriptYOffset) {
00910         val = CONVERT_DESIGN_UNITS_TO_PIXELS(os2->ySuperscriptYOffset,
00911                                              face->size->metrics.y_scale);
00912         mSuperscriptOffset = nscoord(PR_MAX(f, NSToIntRound(val * f)));
00913     }
00914     else {
00915         mSuperscriptOffset = mXHeight;
00916     }
00917 
00918     // mSubscriptOffset
00919     if (os2 && os2->ySubscriptYOffset) {
00920         val = CONVERT_DESIGN_UNITS_TO_PIXELS(os2->ySubscriptYOffset,
00921                                              face->size->metrics.y_scale);
00922         // some fonts have the incorrect sign. 
00923         val = (val < 0) ? -val : val;
00924         mSubscriptOffset = nscoord(PR_MAX(f, NSToIntRound(val * f)));
00925     }
00926     else {
00927         mSubscriptOffset = mXHeight;
00928     }
00929 
00930     // mStrikeoutOffset
00931     mStrikeoutOffset = NSToCoordRound(mXHeight / 2.0);
00932 
00933     // mStrikeoutSize
00934     mStrikeoutSize = mUnderlineSize;
00935 
00936     XftUnlockFace(xftFont);
00937 
00938     return NS_OK;
00939 }
00940 
00941 nsFontXft *
00942 nsFontMetricsXft::FindFont(PRUint32 aChar)
00943 {
00944 
00945     // If mPattern is null, set up the base bits of it so we can
00946     // match.  If we need to match later we don't have to set it up
00947     // again.
00948     if (!mPattern) {
00949         SetupFCPattern();
00950         // did we fail to set it up?
00951         if (!mPattern)
00952             return nsnull;
00953     }
00954 
00955     // go forth and find us some fonts
00956     if (mMatchType == eNoMatch) {
00957         // Optimistically just look for the best match font.
00958         // This can be completed in linear time, which is ideal if all
00959         // of the characters we're asked for can be found in this font.
00960         DoMatch(PR_FALSE);
00961     }
00962 
00963     // Now that we have the fonts loaded and ready to run, return the
00964     // font in our loaded list that supports the character
00965 
00966     if (mLoadedFonts.Count() == 0) {
00967         // No fonts were matched at all.  This probably means out-of-memory
00968         // or some equally bad condition.
00969         return nsnull;
00970     }
00971 
00972     PRBool removeFirstFont = PR_FALSE;
00973     nsFontXft *font = (nsFontXft *)mLoadedFonts.ElementAt(0);
00974     if (font->HasChar(aChar)) {
00975         if (font->GetXftFont())
00976             return font;
00977         removeFirstFont = PR_TRUE;
00978     }
00979 
00980     // We failed to find the character in the best-match font, so load
00981     // _all_ matching fonts if we haven't already done so.
00982 
00983     if (mMatchType == eBestMatch)
00984         DoMatch(PR_TRUE);
00985 
00986     PRInt32 i = 1;
00987     if (removeFirstFont) {
00988         // The first font was bad, so remove it (see below).  But do this
00989         // after |DoMatch| since otherwise it will get re-added.
00990         mLoadedFonts.RemoveElementAt(0);
00991         i = 0;
00992     }
00993 
00994     // Now check the remaining fonts
00995     for (; i < mLoadedFonts.Count(); ++i) {
00996         nsFontXft *font = (nsFontXft *)mLoadedFonts.ElementAt(i);
00997         if (font->HasChar(aChar)) {
00998             if (font->GetXftFont())
00999                 return font;
01000             // This is a bad font, so remove it from mLoadedFonts.  This
01001             // could happen if it's in fc.cache-1 but the font doesn't exist
01002             // (https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=111973)
01003             // or isn't readable.
01004             mLoadedFonts.RemoveElementAt(i--);
01005         }
01006     }
01007 
01008     // If we got this far, none of the fonts support this character.
01009     // Return nothing.
01010     return nsnull;
01011 }
01012 
01013 void
01014 nsFontMetricsXft::SetupFCPattern(void)
01015 {
01016     if (PR_LOG_TEST(gXftFontLoad, PR_LOG_DEBUG)) {
01017         printf("[%p] setting up pattern with the following specification:\n",
01018                (void *)this);
01019 
01020         // non-generic families
01021         if (mFontList.Count() && !mFontIsGeneric[0]) {
01022             printf("\tadding non-generic families: ");
01023             for (int i=0; i < mFontList.Count(); ++i) {
01024                 if (mFontIsGeneric[i])
01025                     break;
01026 
01027                 nsCString *familyName = mFontList.CStringAt(i);
01028                 printf("%s, ", familyName->get());
01029             }
01030             printf("\n");
01031         }
01032 
01033         // language group
01034         const char *name;
01035         mLangGroup->GetUTF8String(&name);
01036         printf("\tlang group: %s\n", name);
01037 
01038 
01039     }
01040 
01041     mPattern = FcPatternCreate();
01042     if (!mPattern)
01043         return;
01044 
01045     if (gdk_rgb_get_cmap() != gdk_colormap_get_system())
01046         XftPatternAddBool(mPattern, XFT_RENDER, False);
01047 
01048     // XXX need to add user defined family
01049 
01050     // Add CSS names - walk the list of fonts, adding the generic as
01051     // the last font
01052     for (int i=0; i < mFontList.Count(); ++i) {
01053         // if this was a generic name, break out of the loop since we
01054         // don't want to add it to the pattern yet
01055         if (mFontIsGeneric[i])
01056             break;;
01057 
01058         nsCString *familyName = mFontList.CStringAt(i);
01059         NS_AddFFRE(mPattern, familyName, PR_FALSE);
01060     }
01061 
01062     // Add the language group.  Note that we do this before adding any
01063     // generics.  That's because the language is more important than
01064     // any generic font.
01065     NS_AddLangGroup (mPattern, mLangGroup);
01066 
01067     // If there's a generic add a pref for the generic if there's one
01068     // set.
01069     if (mGenericFont && !mFont.systemFont) {
01070         NS_AddGenericFontFromPref(mGenericFont, mLangGroup, mPattern,
01071                                   gXftFontLoad);
01072     }
01073 
01074     // Add the generic if there is one.
01075     if (mGenericFont && !mFont.systemFont)
01076         NS_AddFFRE(mPattern, mGenericFont, PR_FALSE);
01077 
01078     if (PR_LOG_TEST(gXftFontLoad, PR_LOG_DEBUG)) {
01079         // generic font
01080         if (mGenericFont && !mFont.systemFont) {
01081             printf("\tadding generic family: %s\n", mGenericFont->get());
01082         }
01083 
01084         // pixel size
01085         printf("\tpixel,twip size: %f,%d\n", mPixelSize, mFont.size);
01086 
01087         // slant type
01088         printf("\tslant: ");
01089         switch(mFont.style) {
01090         case NS_FONT_STYLE_ITALIC:
01091             printf("italic\n");
01092             break;
01093         case NS_FONT_STYLE_OBLIQUE:
01094             printf("oblique\n");
01095             break;
01096         default:
01097             printf("roman\n");
01098             break;
01099         }
01100 
01101         // weight
01102         printf("\tweight: (orig,calc) %d,%d\n",
01103                mFont.weight, NS_CalculateWeight(mFont.weight));
01104 
01105     }        
01106 
01107     // add the point size
01108     // We've done some round-tripping of floating point numbers so they
01109     // might not be quite right.  Since Xft rounds down, add a little,
01110     // so we don't go from 9.00000 to 8.99999 to 8.
01111     FcPatternAddDouble(mPattern, FC_PIXEL_SIZE, mPixelSize + 0.000001);
01112 
01113     // Add the slant type
01114     FcPatternAddInteger(mPattern, FC_SLANT,
01115                         NS_CalculateSlant(mFont.style));
01116 
01117     // Add the weight
01118     FcPatternAddInteger(mPattern, FC_WEIGHT,
01119                         NS_CalculateWeight(mFont.weight));
01120 
01121     // Set up the default substitutions for this font
01122     FcConfigSubstitute(0, mPattern, FcMatchPattern);
01123     XftDefaultSubstitute(GDK_DISPLAY(), DefaultScreen(GDK_DISPLAY()),
01124                          mPattern);
01125 }
01126 
01127 void
01128 nsFontMetricsXft::DoMatch(PRBool aMatchAll)
01129 {
01130     FcFontSet *set = nsnull;
01131     // now that we have a pattern we can match
01132     FcResult   result;
01133 
01134     if (aMatchAll) {
01135         set = FcFontSort(0, mPattern, FcTrue, NULL, &result);
01136         if (!set || set->nfont == 1) {
01137             // There is a bug in older fontconfig versions that causes it to
01138             // bail if it hits a font it can't deal with.
01139             // If this has happened, try just the generic font-family by
01140             // removing everything else from the font list and rebuilding
01141             // the pattern.
01142 
01143             NS_WARNING("Detected buggy fontconfig, falling back to generic font");
01144 
01145             nsCAutoString genericFont;
01146             if (mGenericFont)
01147                 genericFont.Assign(*mGenericFont);
01148 
01149             mFontList.Clear();
01150             mFontIsGeneric.Clear();
01151 
01152             mFontList.AppendCString(genericFont);
01153             mFontIsGeneric.AppendElement((void*) PR_TRUE);
01154             mGenericFont = mFontList.CStringAt(0);
01155 
01156             FcPatternDestroy(mPattern);
01157             SetupFCPattern();
01158 
01159             if (set)
01160                 FcFontSetDestroy(set);
01161 
01162             set = FcFontSort(0, mPattern, FcTrue, NULL, &result);
01163         }
01164     }
01165     else {
01166         FcPattern* font = FcFontMatch(0, mPattern, &result);
01167         if (font) {
01168             set = FcFontSetCreate();
01169             FcFontSetAdd(set, font);
01170         }
01171     }
01172 
01173     // did we not match anything?
01174     if (!set) {
01175         goto loser;
01176     }
01177 
01178     if (PR_LOG_TEST(gXftFontLoad, PR_LOG_DEBUG)) {
01179         printf("matched the following (%d) fonts:\n", set->nfont);
01180     }
01181 
01182     // Create a list of new font objects based on the fonts returned
01183     // as part of the query. We start at mLoadedFonts.Count() so as to
01184     // not re-add the best match font we've already loaded.
01185     for (int i=mLoadedFonts.Count(); i < set->nfont; ++i) {
01186         if (PR_LOG_TEST(gXftFontLoad, PR_LOG_DEBUG)) {
01187             char *name;
01188             FcPatternGetString(set->fonts[i], FC_FAMILY, 0, (FcChar8 **)&name);
01189             printf("\t%s\n", name);
01190         }
01191 
01192         nsFontXft *font;
01193         nsFontXftInfo *info;
01194         nsCOMPtr<nsIUnicodeEncoder> converter = 0;
01195 
01196         info = GetFontXftInfo(set->fonts[i]);
01197         if (info) {
01198             if (info->mFontType == eFontTypeUnicode)
01199                 font = new nsFontXftUnicode(mPattern, set->fonts[i]);
01200             else
01201                 font = new nsFontXftCustom(mPattern, set->fonts[i], info);
01202         }
01203         else {  // if null is returned, treat it as Unicode font.
01204             font = new nsFontXftUnicode(mPattern, set->fonts[i]);
01205         }
01206 
01207         if (!font)
01208             goto loser;
01209 
01210         // append this font to our list of loaded fonts
01211         mLoadedFonts.AppendElement((void *)font);
01212     }
01213 
01214     // we're done with the set now
01215     FcFontSetDestroy(set);
01216     set = nsnull;
01217 
01218     // Done matching!
01219     if (aMatchAll)
01220         mMatchType = eAllMatching;
01221     else
01222         mMatchType = eBestMatch;
01223     return;
01224 
01225     // if we got this far, something went terribly wrong
01226  loser:
01227     NS_WARNING("nsFontMetricsXft::DoMatch failed to match anything");
01228 
01229     if (set)
01230         FcFontSetDestroy(set);
01231 
01232     for (PRInt32 i = mLoadedFonts.Count() - 1; i >= 0; --i) {
01233         nsFontXft *font = (nsFontXft *)mLoadedFonts.ElementAt(i);
01234         mLoadedFonts.RemoveElementAt(i);
01235         delete font;
01236     }
01237 }
01238 
01239 gint
01240 nsFontMetricsXft::RawGetWidth(const PRUnichar* aString, PRUint32 aLength)
01241 {
01242     nscoord width = 0;
01243     nsresult rv;
01244 
01245     rv = EnumerateGlyphs(aString, aLength,
01246                          &nsFontMetricsXft::GetWidthCallback, &width);
01247 
01248     if (NS_FAILED(rv))
01249         width = 0;
01250 
01251     return width;
01252 }
01253 
01254 nsresult
01255 nsFontMetricsXft::SetupMiniFont(void)
01256 {
01257     // The minifont is initialized lazily.
01258     if (mMiniFont)
01259         return NS_OK;
01260 
01261     FcPattern *pattern = nsnull;
01262     XftFont *font = nsnull;
01263     XftFont *xftFont = mWesternFont->mXftFont;
01264     NS_ASSERTION(xftFont, "FindFont returned a bad font");
01265 
01266     mMiniFontAscent = xftFont->ascent;
01267     mMiniFontDescent = xftFont->descent;
01268 
01269     pattern = FcPatternCreate();
01270     if (!pattern)
01271         return NS_ERROR_FAILURE;
01272 
01273     if (gdk_rgb_get_cmap() != gdk_colormap_get_system())
01274         XftPatternAddBool(mPattern, XFT_RENDER, False);
01275 
01276     FcPatternAddString(pattern, FC_FAMILY, (FcChar8 *)"monospace");
01277 
01278     FcPatternAddInteger(pattern, FC_PIXEL_SIZE, int(0.5 * mPixelSize));
01279 
01280     FcPatternAddInteger(pattern, FC_WEIGHT,
01281                         NS_CalculateWeight(mFont.weight));
01282 
01283     FcConfigSubstitute(0, pattern, FcMatchPattern);
01284     XftDefaultSubstitute(GDK_DISPLAY(), DefaultScreen(GDK_DISPLAY()),
01285                          pattern);
01286 
01287     FcResult res;
01288     
01289     FcPattern *pat = FcFontMatch(0, pattern, &res);
01290 
01291     if (pat) {
01292         font = XftFontOpenPattern(GDK_DISPLAY(), pat);
01293 
01294         if (font) {
01295             mMiniFont = font;
01296             pat = nsnull; // the font owns the pattern now
01297         }
01298         else {
01299             font = xftFont;
01300         }
01301     }
01302 
01303     // now that the font has been loaded, measure the fonts to find
01304     // the bounds
01305     for (int i=0; i < 16; ++i) {
01306         // create a little mini-string that contains what we want to
01307         // measure.
01308         char c = i < 10 ? '0' + i : 'A' + i - 10;
01309         char str[2];
01310         str[0] = c;
01311         str[1] = '\0';
01312 
01313         XGlyphInfo extents;
01314         XftTextExtents8(GDK_DISPLAY(), font,
01315                         (FcChar8 *)str, 1, &extents);
01316 
01317         mMiniFontWidth = PR_MAX (mMiniFontWidth, extents.width);
01318         mMiniFontHeight = PR_MAX (mMiniFontHeight, extents.height);
01319     }
01320 
01321     if (!mMiniFont) {
01322         mMiniFontWidth /= 2;
01323         mMiniFontHeight /= 2;
01324     }
01325 
01326     mMiniFontPadding = PR_MAX(mMiniFontHeight / 10, 1);
01327     mMiniFontYOffset = ((mMiniFontAscent + mMiniFontDescent) -
01328                         (mMiniFontHeight * 2 + mMiniFontPadding * 5)) / 2;
01329 
01330 
01331     if (pat)
01332         FcPatternDestroy(pat);
01333     if (pattern)
01334         FcPatternDestroy(pattern);
01335 
01336     return NS_OK;
01337 }
01338 
01339 nsresult
01340 nsFontMetricsXft::DrawUnknownGlyph(FcChar32   aChar,
01341                                    nscoord    aX,
01342                                    nscoord    aY,
01343                                    XftColor  *aColor,
01344                                    XftDraw   *aDraw)
01345 {
01346     int width,height;
01347     int ndigit = (IS_NON_BMP(aChar)) ? 3 : 2;
01348 
01349     // ndigit characters + padding around the fonts.  From left to
01350     // right it would be one padding for the glyph box, one for the
01351     // padding in between the box and the left most digit, ndigit
01352     // padding following each of ndigit letters and one for the
01353     // rightmost part of the box.  This pattern is used throughout the
01354     // rest of this function.
01355     width = mMiniFontWidth * ndigit + mMiniFontPadding * (ndigit + 3);
01356     height = mMiniFontHeight * 2 + mMiniFontPadding * 5;
01357 
01358     // Draw an outline of a box.  We do with this with four calls
01359     // since with one call it would fill in the box.
01360     
01361     // horizontal lines
01362     XftDrawRect(aDraw, aColor,
01363                 aX, aY - height,
01364                 width, mMiniFontPadding);
01365     XftDrawRect(aDraw, aColor,
01366                 aX, aY - mMiniFontPadding,
01367                 width, mMiniFontPadding);
01368 
01369     // vertical lines
01370     XftDrawRect(aDraw, aColor,
01371                 aX,
01372                 aY - height + mMiniFontPadding,
01373                 mMiniFontPadding, height - mMiniFontPadding * 2);
01374     XftDrawRect(aDraw, aColor,
01375                 aX + width - mMiniFontPadding,
01376                 aY - height + mMiniFontPadding,
01377                 mMiniFontPadding, height - mMiniFontPadding * 2);
01378 
01379     // If for some reason the mini font couldn't be loaded, just
01380     // return - the box is enough.
01381     if (!mMiniFont)
01382         return NS_OK;
01383 
01384     // now draw the characters
01385     char buf[7];
01386     PR_snprintf (buf, sizeof(buf), "%0*X", ndigit * 2, aChar);
01387 
01388     // Draw the 'ndigit * 2' characters
01389     XftDrawString8(aDraw, aColor, mMiniFont,
01390                    aX + mMiniFontPadding * 2,
01391                    aY - mMiniFontHeight - mMiniFontPadding * 3,
01392                    (FcChar8 *)&buf[0], 1);
01393     XftDrawString8(aDraw, aColor, mMiniFont,
01394                    aX + mMiniFontWidth + mMiniFontPadding * 3,
01395                    aY - mMiniFontHeight - mMiniFontPadding * 3,
01396                    (FcChar8 *)&buf[1], 1);
01397 
01398     if (ndigit == 2) {
01399         XftDrawString8(aDraw, aColor, mMiniFont,
01400                        aX + mMiniFontPadding * 2,
01401                        aY - mMiniFontPadding * 2,
01402                        (FcChar8 *)&buf[2], 1);
01403         XftDrawString8(aDraw, aColor, mMiniFont,
01404                        aX + mMiniFontWidth + mMiniFontPadding * 3,
01405                        aY - mMiniFontPadding * 2,
01406                        (FcChar8 *)&buf[3], 1);
01407 
01408         return NS_OK;
01409     }
01410 
01411     XftDrawString8(aDraw, aColor, mMiniFont,
01412                    aX + mMiniFontWidth * 2 + mMiniFontPadding * 4,
01413                    aY - mMiniFontHeight - mMiniFontPadding * 3,
01414                    (FcChar8 *)&buf[2], 1);
01415     XftDrawString8(aDraw, aColor, mMiniFont,
01416                    aX + mMiniFontPadding * 2,
01417                    aY - mMiniFontPadding * 2,
01418                    (FcChar8 *)&buf[3], 1);
01419     XftDrawString8(aDraw, aColor, mMiniFont,
01420                    aX + mMiniFontWidth + mMiniFontPadding * 3,
01421                    aY - mMiniFontPadding * 2,
01422                    (FcChar8 *)&buf[4], 1);
01423     XftDrawString8(aDraw, aColor, mMiniFont,
01424                    aX + mMiniFontWidth * 2 + mMiniFontPadding * 4,
01425                    aY - mMiniFontPadding * 2,
01426                    (FcChar8 *)&buf[5], 1);
01427 
01428     return NS_OK;
01429 }
01430 
01431 nsresult
01432 nsFontMetricsXft::EnumerateXftGlyphs(const FcChar32 *aString, PRUint32 aLen,
01433                                      GlyphEnumeratorCallback aCallback,
01434                                      void *aCallbackData)
01435 {
01436     nsFontXft* prevFont = nsnull;
01437     PRUint32 start = 0;
01438     nsresult rv = NS_OK;
01439     PRUint32 i = 0;
01440 
01441     for ( ; i < aLen; i ++) {
01442         nsFontXft *currFont = FindFont(aString[i]);
01443 
01444         // Don't try to handle more than 512 characters at once, since
01445         // Xft text measurement can't deal with anything with a width of
01446         // more than 2^15 (32768) pixels.  This is a hack, and it could
01447         // break things like combining characters, but that's not nearly
01448         // as bad as not displaying anything, and it's also very rare to
01449         // draw strings this long without any breaks.
01450         if (currFont != prevFont || i - start > 512) {
01451             if (i > start) {
01452                 rv = (this->*aCallback)(&aString[start], i - start, prevFont,
01453                                         aCallbackData);
01454                 NS_ENSURE_SUCCESS(rv, rv);
01455             }
01456             prevFont = currFont;
01457             start = i;
01458         }
01459     }
01460 
01461     if (i > start)
01462         rv = (this->*aCallback)(&aString[start], i - start, prevFont,
01463                                 aCallbackData);
01464 
01465     return rv;
01466 }
01467 
01468 nsresult
01469 nsFontMetricsXft::EnumerateGlyphs(const PRUnichar *aString,
01470                                   PRUint32 aLen,
01471                                   GlyphEnumeratorCallback aCallback,
01472                                   void *aCallbackData)
01473 {
01474     PRUint32 len;
01475     nsAutoFcChar32Buffer charBuffer;
01476 
01477     NS_ENSURE_TRUE(aLen, NS_OK); 
01478 
01479     ConvertUnicharToUCS4(aString, aLen, charBuffer, &len);
01480     if (!len)
01481         return NS_ERROR_OUT_OF_MEMORY;
01482 
01483     return EnumerateXftGlyphs(charBuffer.get(), len, aCallback, aCallbackData);
01484 }
01485 
01486 nsresult
01487 nsFontMetricsXft::EnumerateGlyphs(const char *aString,
01488                                   PRUint32 aLen,
01489                                   GlyphEnumeratorCallback aCallback,
01490                                   void *aCallbackData)
01491 {
01492     PRUint32 len;
01493     nsAutoFcChar32Buffer charBuffer;
01494 
01495     NS_ENSURE_TRUE(aLen, NS_OK); 
01496 
01497     // Convert the incoming string into an array of UCS4 chars
01498     ConvertCharToUCS4(aString, aLen, charBuffer, &len);
01499     if (!len)
01500         return NS_ERROR_OUT_OF_MEMORY;
01501 
01502     return EnumerateXftGlyphs(charBuffer.get(), len, aCallback, aCallbackData);
01503 }
01504 
01505 void
01506 nsFontMetricsXft::PrepareToDraw(nsRenderingContextGTK *aContext,
01507                                 nsDrawingSurfaceGTK *aSurface,
01508                                 XftDraw **aDraw, XftColor &aColor)
01509 {
01510     // Set our color
01511     nscolor rccolor;
01512 
01513     aContext->GetColor(rccolor);
01514 
01515     aColor.pixel = gdk_rgb_xpixel_from_rgb(NS_TO_GDK_RGB(rccolor));
01516     aColor.color.red = (NS_GET_R(rccolor) << 8) | NS_GET_R(rccolor);
01517     aColor.color.green = (NS_GET_G(rccolor) << 8) | NS_GET_G(rccolor);
01518     aColor.color.blue = (NS_GET_B(rccolor) << 8) | NS_GET_B(rccolor);
01519     aColor.color.alpha = 0xffff;
01520 
01521     *aDraw = aSurface->GetXftDraw();
01522 
01523     nsCOMPtr<nsIRegion> lastRegion;
01524     nsCOMPtr<nsIRegion> clipRegion;
01525 
01526     aSurface->GetLastXftClip(getter_AddRefs(lastRegion));
01527     aContext->GetClipRegion(getter_AddRefs(clipRegion));
01528 
01529     // avoid setting the clip, if possible
01530     if (!lastRegion || !clipRegion || !lastRegion->IsEqual(*clipRegion)) {
01531         aSurface->SetLastXftClip(clipRegion);
01532 
01533         GdkRegion *rgn = nsnull;
01534         clipRegion->GetNativeRegion((void *&)rgn);
01535 
01536 #ifdef MOZ_WIDGET_GTK
01537         GdkRegionPrivate  *priv = (GdkRegionPrivate *)rgn;
01538         XftDrawSetClip(*aDraw, priv->xregion);
01539 #endif
01540 
01541 #ifdef MOZ_WIDGET_GTK2
01542         GdkRegionSetXftClip(rgn, *aDraw);
01543 #endif
01544     }
01545 }
01546 
01547 nsresult
01548 nsFontMetricsXft::DrawStringCallback(const FcChar32 *aString, PRUint32 aLen,
01549                                      nsFontXft *aFont, void *aData)
01550 {
01551     DrawStringData *data = (DrawStringData *)aData;
01552 
01553     // If there was no font found for this character, just draw the
01554     // unknown glyph character
01555     if (!aFont) {
01556         SetupMiniFont();
01557 
01558         for (PRUint32 i = 0; i<aLen; i++) {
01559             // position in X is the location offset in the string plus
01560             // whatever offset is required for the spacing argument
01561             const FcChar32 ch = aString[i];
01562             nscoord x = data->x + data->xOffset;
01563             nscoord y = data->y;
01564 
01565             // convert this into device coordinates
01566             data->context->GetTranMatrix()->TransformCoord(&x, &y);
01567 
01568             DrawUnknownGlyph(ch, x, y + mMiniFontYOffset, &data->color,
01569                              data->draw);
01570 
01571             if (data->spacing) {
01572                 data->xOffset += *data->spacing;
01573                 data->spacing += IS_NON_BMP(ch) ? 2 : 1;
01574             }
01575             else {
01576                 data->xOffset +=
01577                     NSToCoordRound((mMiniFontWidth*(IS_NON_BMP(ch) ? 3 : 2) +
01578                                 mMiniFontPadding*(IS_NON_BMP(ch) ? 6:5)) *
01579                             data->p2t);
01580             }
01581         }
01582 
01583         // We're done.
01584         return NS_OK;
01585     }
01586 
01587     // actually process the specbuffer converting the input string
01588     // to custom font code if necessary.
01589     return aFont->DrawStringSpec(NS_CONST_CAST(FcChar32 *, aString), 
01590                                  aLen, data);
01591 }
01592 
01593 nsresult
01594 nsFontMetricsXft::TextDimensionsCallback(const FcChar32 *aString, PRUint32 aLen,
01595                                          nsFontXft *aFont, void *aData)
01596 {
01597     nsTextDimensions *dimensions = (nsTextDimensions *)aData;
01598 
01599     if (!aFont) {
01600         SetupMiniFont();
01601         for (PRUint32 i = 0; i<aLen; i++) {
01602             dimensions->width += 
01603                 mMiniFontWidth * (IS_NON_BMP(aString[i]) ? 3 : 2) +
01604                 mMiniFontPadding * (IS_NON_BMP(aString[i]) ? 6 : 5);
01605         }
01606 
01607         if (dimensions->ascent < mMiniFontAscent)
01608             dimensions->ascent = mMiniFontAscent;
01609         if (dimensions->descent < mMiniFontDescent)
01610             dimensions->descent = mMiniFontDescent;
01611 
01612         return NS_OK;
01613     }
01614 
01615     // get the metric after converting the input string to
01616     // custom font code if necessary.
01617     XGlyphInfo glyphInfo;
01618     nsresult rv = aFont->GetTextExtents32(aString, aLen, glyphInfo);
01619     NS_ENSURE_SUCCESS(rv, rv);
01620 
01621     dimensions->width += glyphInfo.xOff;
01622 
01623     nscoord tmpMaxAscent = aFont->GetMaxAscent();
01624     nscoord tmpMaxDescent = aFont->GetMaxDescent();
01625 
01626     if (dimensions->ascent < tmpMaxAscent)
01627         dimensions->ascent = tmpMaxAscent;
01628     if (dimensions->descent < tmpMaxDescent)
01629         dimensions->descent = tmpMaxDescent;
01630 
01631     return NS_OK;
01632 }
01633 
01634 nsresult
01635 nsFontMetricsXft::GetWidthCallback(const FcChar32 *aString, PRUint32 aLen,
01636                                    nsFontXft *aFont, void *aData)
01637 {
01638     nscoord *width = (nscoord*)aData;
01639 
01640     if (!aFont) {
01641         SetupMiniFont();
01642         for (PRUint32 i = 0; i < aLen; i++) {
01643             *width += mMiniFontWidth * (IS_NON_BMP(aString[i]) ? 3 : 2) + 
01644                       mMiniFontPadding * (IS_NON_BMP(aString[i]) ? 6 : 5);
01645         }
01646         return NS_OK;
01647     }
01648 
01649     *width += aFont->GetWidth32(aString, aLen);
01650     return NS_OK;
01651 }
01652 
01653 #ifdef MOZ_MATHML
01654 nsresult
01655 nsFontMetricsXft::BoundingMetricsCallback(const FcChar32 *aString, 
01656                                           PRUint32 aLen, nsFontXft *aFont, 
01657                                           void *aData)
01658 {
01659     BoundingMetricsData *data = (BoundingMetricsData *)aData;
01660     nsBoundingMetrics bm;
01661 
01662     if (!aFont) {
01663         SetupMiniFont();
01664 
01665         // Note:  All fields of bm initialized to 0 in its constructor.
01666         for (PRUint32 i = 0; i < aLen; i++) {
01667             bm.width += mMiniFontWidth * (IS_NON_BMP(aString[i]) ? 3 : 2) +
01668                         mMiniFontPadding * (IS_NON_BMP(aString[i]) ? 6 : 5);
01669             bm.rightBearing += bm.width; // no need to set leftBearing.
01670         }
01671         bm.ascent = mMiniFontAscent;
01672         bm.descent = mMiniFontDescent;
01673     }
01674     else {
01675         nsresult rv;
01676         rv = aFont->GetBoundingMetrics32(aString, aLen, bm);
01677         NS_ENSURE_SUCCESS(rv, rv);
01678     }
01679 
01680     if (data->firstTime) {  
01681         *(data->bm) = bm;
01682         data->firstTime = PR_FALSE;
01683     }
01684     else {
01685         *(data->bm) += bm;
01686     }
01687 
01688     return NS_OK;
01689 }
01690 #endif /* MOZ_MATHML */
01691 
01692 /* static */
01693 nsresult
01694 nsFontMetricsXft::FamilyExists(nsIDeviceContext *aDevice,
01695                                const nsString &aName)
01696 {
01697     // fontconfig family name is always in UTF-8
01698     NS_ConvertUTF16toUTF8 name(aName);
01699 
01700     FcFontSet *set = nsnull;
01701     FcObjectSet *os = nsnull;
01702 
01703     FcPattern *pat = FcPatternCreate();
01704     if (!pat)
01705         return NS_ERROR_FAILURE;
01706 
01707     nsresult rv = NS_ERROR_FAILURE;
01708     
01709     // Build a list of familes and walk the list looking to see if we
01710     // have it.
01711     os = FcObjectSetBuild(FC_FAMILY, NULL);
01712     if (!os)
01713         goto end;
01714 
01715     set = FcFontList(0, pat, os);
01716 
01717     if (!set || !set->nfont)
01718         goto end;
01719 
01720     for (int i = 0; i < set->nfont; ++i) {
01721         const char *tmpname = NULL;
01722         if (FcPatternGetString(set->fonts[i], FC_FAMILY, 0,
01723                                (FcChar8 **)&tmpname) != FcResultMatch) {
01724             continue;
01725         }
01726 
01727         // do they match?
01728         if (!Compare(nsDependentCString(tmpname), name,
01729                      nsCaseInsensitiveCStringComparator())) {
01730             rv = NS_OK;
01731             break;
01732         }
01733     }
01734 
01735  end:
01736     if (set)
01737         FcFontSetDestroy(set);
01738     if (os)
01739         FcObjectSetDestroy(os);
01740 
01741     FcPatternDestroy(pat);
01742 
01743     return rv;
01744 }
01745 
01746 /* static */
01747 PRBool
01748 nsFontMetricsXft::EnumFontCallback(const nsString &aFamily, PRBool aIsGeneric,
01749                                    void *aData)
01750 {
01751     NS_ConvertUTF16toUTF8 name(aFamily);
01752 
01753     // The newest fontconfig does the full Unicode case folding so that 
01754     // we're being lazy here by calling |ToLowerCase| after converting
01755     // to UTF-8  assuming that in virtually all cases, we just have to
01756     // fold [A-Z].  (bug 223653). 
01757     ToLowerCase(name);
01758     nsFontMetricsXft *metrics = (nsFontMetricsXft *)aData;
01759     metrics->mFontList.AppendCString(name);
01760     metrics->mFontIsGeneric.AppendElement((void *)aIsGeneric);
01761     if (aIsGeneric) {
01762         metrics->mGenericFont = 
01763             metrics->mFontList.CStringAt(metrics->mFontList.Count() - 1);
01764         return PR_FALSE; // stop processing
01765     }
01766 
01767     return PR_TRUE; // keep processing
01768 }
01769 
01770 // nsFontEnumeratorXft class
01771 
01772 nsFontEnumeratorXft::nsFontEnumeratorXft()
01773 {
01774 }
01775 
01776 NS_IMPL_ISUPPORTS1(nsFontEnumeratorXft, nsIFontEnumerator)
01777 
01778 NS_IMETHODIMP
01779 nsFontEnumeratorXft::EnumerateAllFonts(PRUint32 *aCount, PRUnichar ***aResult)
01780 {
01781     NS_ENSURE_ARG_POINTER(aResult);
01782     *aResult = nsnull;
01783     NS_ENSURE_ARG_POINTER(aCount);
01784     *aCount = 0;
01785 
01786     return EnumFontsXft(nsnull, nsnull, aCount, aResult);
01787 }
01788 
01789 NS_IMETHODIMP
01790 nsFontEnumeratorXft::EnumerateFonts(const char *aLangGroup,
01791                                     const char *aGeneric,
01792                                     PRUint32 *aCount, PRUnichar ***aResult)
01793 {
01794     NS_ENSURE_ARG_POINTER(aResult);
01795     *aResult = nsnull;
01796     NS_ENSURE_ARG_POINTER(aCount);
01797     *aCount = 0;
01798 
01799     // aLangGroup=null or ""  means any (i.e., don't care)
01800     // aGeneric=null or ""  means any (i.e, don't care)
01801     nsCOMPtr<nsIAtom> langGroup;
01802     if (aLangGroup && *aLangGroup)
01803       langGroup = do_GetAtom(aLangGroup);
01804     const char* generic = nsnull;
01805     if (aGeneric && *aGeneric)
01806       generic = aGeneric;
01807 
01808     return EnumFontsXft(langGroup, generic, aCount, aResult);
01809 }
01810 
01811 NS_IMETHODIMP
01812 nsFontEnumeratorXft::HaveFontFor(const char *aLangGroup, PRBool *aResult)
01813 {
01814     NS_ENSURE_ARG_POINTER(aResult);
01815     *aResult = PR_FALSE;
01816     NS_ENSURE_ARG_POINTER(aLangGroup);
01817 
01818     *aResult = PR_TRUE; // always return true for now.
01819     // Finish me - ftang
01820     return NS_OK;
01821 }
01822 
01823 NS_IMETHODIMP
01824 nsFontEnumeratorXft::GetDefaultFont(const char *aLangGroup, 
01825   const char *aGeneric, PRUnichar **aResult)
01826 {
01827   // aLangGroup=null or ""  means any (i.e., don't care)
01828   // aGeneric=null or ""  means any (i.e, don't care)
01829 
01830   NS_ENSURE_ARG_POINTER(aResult);
01831   *aResult = nsnull;
01832 
01833 #if 0
01834   //XXX Some voodoo inspired from the thread:
01835   //XXX http://www.mail-archive.com/fonts@xfree86.org/msg01344.html
01836   //XXX please iterate to turn this on when you are happy/ready
01837 
01838   FcResult res;
01839   FcPattern* match_pattern = NULL;
01840   if (aGeneric && *aGeneric)
01841     match_pattern = FcNameParse(aGeneric);
01842   else
01843     match_pattern = FcPatternCreate();
01844 
01845   if (!match_pattern)
01846     return NS_OK; // not fatal, just return an empty default name
01847 
01848   if (aLangGroup && *aLangGroup) {
01849     nsCOMPtr<nsIAtom> langGroup = do_GetAtom(aLangGroup);
01850     NS_AddLangGroup(match_pattern, langGroup);
01851   }
01852 
01853   FcConfigSubstitute(0, match_pattern, FcMatchPattern); 
01854   FcDefaultSubstitute(match_pattern);
01855   FcPattern* result_pattern = FcFontMatch(0, match_pattern, &res);
01856   if (result_pattern) {
01857     char *family;
01858     FcPatternGetString(result_pattern, FC_FAMILY, 0, (FcChar8 **)&family);
01859     PRUnichar* name = NS_STATIC_CAST(PRUnichar *,
01860                               nsMemory::Alloc ((strlen (family) + 1)
01861                                                * sizeof (PRUnichar)));
01862     if (name) {
01863       PRUnichar *r = name;
01864       for (char *f = family; *f; ++f)
01865         *r++ = *f;
01866       *r = '\0';
01867 
01868       *aResult = name;
01869     }
01870 
01871     FcPatternDestroy(result_pattern);
01872   }
01873   FcPatternDestroy(match_pattern);
01874 #endif
01875 
01876   return NS_OK;
01877 }
01878 
01879 NS_IMETHODIMP
01880 nsFontEnumeratorXft::UpdateFontList(PRBool *_retval)
01881 {
01882     *_retval = PR_FALSE; // always return false for now
01883     return NS_OK;
01884 }
01885 
01886 // class nsFontXft
01887 
01888 nsFontXft::nsFontXft(FcPattern *aPattern, FcPattern *aFontName)
01889 {
01890     // save our pattern - we own it now
01891     mPattern = aPattern;
01892     mFontName = aFontName;
01893     // don't destroy it out from under us
01894     FcPatternReference(aPattern);
01895     FcPatternReference(mFontName);
01896 
01897     mXftFont = nsnull;
01898 
01899     // set up our charset
01900     mCharset = nsnull;
01901     FcCharSet *charset = nsnull;
01902 
01903     // this references an internal charset, we need to copy it
01904     FcPatternGetCharSet(aFontName, FC_CHARSET, 0, &charset);
01905     if (charset)
01906         mCharset = FcCharSetCopy(charset);
01907 }
01908 
01909 nsFontXft::~nsFontXft()
01910 {
01911     if (mXftFont)
01912         XftFontClose(GDK_DISPLAY(), mXftFont);
01913     if (mCharset)
01914         FcCharSetDestroy(mCharset);
01915     if (mPattern)
01916         FcPatternDestroy(mPattern);
01917     if (mFontName)
01918         FcPatternDestroy(mFontName);
01919 }
01920 
01921 XftFont *
01922 nsFontXft::GetXftFont(void)
01923 {
01924     if (!mXftFont) {
01925         FcPattern *pat = FcFontRenderPrepare(0, mPattern, mFontName);
01926         if (!pat)
01927             return nsnull;
01928 
01929         // bug 196312 : work around problem with CJK bi-width fonts
01930         // and fontconfig prior to 2.2. CJK bi-width fonts are regarded
01931         // as genuinely monospace by fontconfig that  sets FC_SPACING
01932         // to FC_MONOSPACE, which makes Xft drawing/measuring routines
01933         // use the global advance width (the width of double width glyphs 
01934         // and twice the width of single width glyphs) even for single width
01935         // characters (e.g. Latin letters and digits). This results in
01936         // spaced-out ('s p a c e d - o u t') rendering. By deleting
01937         // FC_SPACING here, we're emulating the behavior of fontconfig 2.2 or 
01938         // later that does not set FC_SPACING for any font.
01939         if (FcGetVersion() < 20300)
01940             FcPatternDel(pat, FC_SPACING);
01941 
01942         mXftFont = XftFontOpenPattern(GDK_DISPLAY(), pat);
01943         if (!mXftFont)
01944             FcPatternDestroy(pat);
01945     }
01946 
01947     return mXftFont;
01948 }
01949 
01950 // used by GetWidth32, GetBoundingMetrics32, and TextDimensionCallback
01951 nsresult
01952 nsFontXft::GetTextExtents32(const FcChar32 *aString, PRUint32 aLen, 
01953                             XGlyphInfo &aGlyphInfo)
01954 {
01955     NS_PRECONDITION(mXftFont, "FindFont should not return bad fonts");
01956 
01957     // NS_CONST_CAST needed for older versions of Xft
01958     XftTextExtents32(GDK_DISPLAY(), mXftFont,
01959                      NS_CONST_CAST(FcChar32*, aString), aLen, &aGlyphInfo);
01960 
01961     return NS_OK;
01962 }
01963 
01964 gint
01965 nsFontXft::GetWidth32(const FcChar32 *aString, PRUint32 aLen)
01966 {
01967     XGlyphInfo glyphInfo;
01968     GetTextExtents32(aString, aLen, glyphInfo);
01969 
01970     return glyphInfo.xOff;
01971 }
01972 
01973 #ifdef MOZ_MATHML
01974 nsresult
01975 nsFontXft::GetBoundingMetrics32(const FcChar32*    aString,
01976                                 PRUint32           aLength,
01977                                 nsBoundingMetrics& aBoundingMetrics)
01978 {
01979     aBoundingMetrics.Clear ();
01980 
01981     if (aString && aLength) {
01982         XGlyphInfo glyphInfo;
01983         GetTextExtents32 (aString, aLength, glyphInfo);
01984         aBoundingMetrics.leftBearing = - glyphInfo.x;
01985         aBoundingMetrics.rightBearing = glyphInfo.width - glyphInfo.x;
01986         aBoundingMetrics.ascent = glyphInfo.y;
01987         aBoundingMetrics.descent = glyphInfo.height - glyphInfo.y;
01988         aBoundingMetrics.width = glyphInfo.xOff;
01989     }
01990     return NS_OK;
01991 }
01992 #endif /* MOZ_MATHML */
01993 
01994 PRInt16
01995 nsFontXft::GetMaxAscent(void)
01996 {
01997     NS_PRECONDITION(mXftFont, "FindFont should not return bad fonts");
01998     return mXftFont->ascent;
01999 }
02000 
02001 PRInt16
02002 nsFontXft::GetMaxDescent(void)
02003 {
02004     NS_PRECONDITION(mXftFont, "FindFont should not return bad fonts");
02005     return mXftFont->descent;
02006 }
02007 
02008 FT_UInt
02009 nsFontXft::CharToGlyphIndex(FcChar32 aChar)
02010 {
02011     return XftCharIndex(GDK_DISPLAY(), mXftFont, aChar);
02012 }
02013 
02014 // used by DrawStringCallback
02015 nsresult
02016 nsFontXft::DrawStringSpec(FcChar32 *aString, PRUint32 aLen, void *aData)
02017 {
02018     NS_PRECONDITION(mXftFont, "FindFont should not return bad fonts");
02019     DrawStringData *data = (DrawStringData *)aData;
02020 
02021     FcChar32 *pstr = aString;
02022     const FcChar32 *end = aString + aLen;
02023 
02024     while(pstr < end) {
02025         nscoord x = data->x + data->xOffset;               
02026         nscoord y = data->y;                        
02027         /* Convert to device coordinate. */   
02028         data->context->GetTranMatrix()->TransformCoord(&x, &y);
02029                                                                  
02030         /* position in X is the location offset in the string 
02031            plus whatever offset is required for the spacing   
02032            argument                                           
02033         */                                                  
02034 
02035         FT_UInt glyph = CharToGlyphIndex(*pstr);
02036         data->drawBuffer->Draw(x, y, mXftFont, glyph);
02037 
02038         if (data->spacing) {
02039             data->xOffset += *data->spacing;
02040             data->spacing += IS_NON_BMP(*pstr) ? 2 : 1; 
02041         }
02042         else {
02043             XGlyphInfo info;                        
02044             XftGlyphExtents(GDK_DISPLAY(), mXftFont, &glyph, 1, &info);
02045             data->xOffset += NSToCoordRound(info.xOff * data->p2t);
02046         }
02047 
02048         ++pstr;
02049     }                                                          
02050     return NS_OK;
02051 }
02052 
02053 // class nsFontXftUnicode impl
02054   
02055 nsFontXftUnicode::~nsFontXftUnicode()
02056 {
02057 }
02058 
02059 PRBool
02060 nsFontXftUnicode::HasChar(PRUint32 aChar)
02061 {
02062     return FcCharSetHasChar(mCharset, (FcChar32) aChar);
02063 }
02064 
02065 // class nsFontXftCustom impl
02066 
02067 nsFontXftCustom::~nsFontXftCustom()
02068 {
02069     if (mXftFont && mFT_Face)
02070        XftUnlockFace(mXftFont);
02071 }
02072 
02073 // used by GetWidth32, GetBoundingMetrics32, and TextDimensionCallback
02074 // Convert the input to custom font code before measuring.
02075 nsresult
02076 nsFontXftCustom::GetTextExtents32(const FcChar32 *aString, PRUint32 aLen, 
02077                                   XGlyphInfo &aGlyphInfo)
02078 {
02079     NS_PRECONDITION(mXftFont, "FindFont should not return bad fonts");
02080 
02081     nsAutoFcChar32Buffer buffer;
02082     nsresult rv;
02083     PRUint32 destLen = aLen;
02084     PRBool isWide = (mFontInfo->mFontType == eFontTypeCustomWide); 
02085 
02086     // we won't use this again. it's safe to cast away const.
02087     rv = ConvertUCS4ToCustom(NS_CONST_CAST(FcChar32 *, aString), 
02088                              aLen, destLen, mFontInfo->mConverter,
02089                              isWide, buffer);
02090     NS_ENSURE_SUCCESS(rv, rv);
02091       
02092     FcChar32 *str = buffer.get();
02093 
02094     // short cut for the common case
02095     if (isWide) { 
02096         XftTextExtents32(GDK_DISPLAY(), mXftFont, str, destLen, &aGlyphInfo);
02097         return NS_OK;
02098     }
02099 
02100     // If FT_SelectCharMap succeeds for MacRoman or Unicode,
02101     // use glyph indices directly for 'narrow' custom encoded fonts.
02102     rv = SetFT_FaceCharmap();
02103     NS_ENSURE_SUCCESS(rv, rv);
02104 
02105     // replace chars with glyphs in place. 
02106     for (PRUint32 i = 0; i < destLen; i++) {
02107         str[i] = FT_Get_Char_Index (mFT_Face, FT_ULong(str[i]));
02108     }
02109 
02110     XftGlyphExtents(GDK_DISPLAY(), mXftFont, str, destLen, &aGlyphInfo);
02111         
02112     return NS_OK;
02113 }
02114 
02115 PRBool
02116 nsFontXftCustom::HasChar(PRUint32 aChar)
02117 {
02118     return (mFontInfo->mCCMap &&
02119             CCMAP_HAS_CHAR_EXT(mFontInfo->mCCMap, aChar)); 
02120 }
02121 
02122 FT_UInt
02123 nsFontXftCustom::CharToGlyphIndex(FcChar32 aChar)
02124 {
02125     if (mFontInfo->mFontType == eFontTypeCustomWide)
02126         return XftCharIndex(GDK_DISPLAY(), mXftFont, aChar);
02127     else
02128         return FT_Get_Char_Index(mFT_Face, aChar);
02129 }
02130 
02131 // used by DrawStringCallback
02132 // Convert the input to custom font code before filling up the buffer.
02133 nsresult
02134 nsFontXftCustom::DrawStringSpec(FcChar32* aString, PRUint32 aLen,
02135                                 void* aData)
02136 {
02137     NS_PRECONDITION(mXftFont, "FindFont should not return bad fonts");
02138 
02139     nsresult rv = NS_OK;
02140     nsAutoFcChar32Buffer buffer;
02141     PRUint32 destLen = aLen;
02142     PRBool isWide = (mFontInfo->mFontType == eFontTypeCustomWide); 
02143 
02144     rv = ConvertUCS4ToCustom(aString, aLen, destLen, mFontInfo->mConverter, 
02145                              isWide, buffer);
02146     NS_ENSURE_SUCCESS(rv, rv);
02147 
02148     if (!isWide) {
02149         // For some narrow fonts(Mathematica, Symbol, and MTExtra),  
02150         // what we get back  after the conversion  is in the encoding 
02151         // of a specific  FT_Charmap (Apple Roman) instead of Unicode 
02152         // so that we  can't call XftCharIndex() which regards input 
02153         // as a Unicode codepoint. Instead, we have to directly invoke 
02154         // FT_Get_Char_Index() with FT_Face corresponding to XftFont 
02155         // after setting FT_Charmap to the cmap of our choice(Apple Roman).
02156         rv = SetFT_FaceCharmap();
02157         NS_ENSURE_SUCCESS(rv, rv);
02158     }
02159 
02160     FcChar32 *str = buffer.get();
02161 
02162     return nsFontXft::DrawStringSpec(str, destLen, aData);
02163 }
02164 
02165 nsresult
02166 nsFontXftCustom::SetFT_FaceCharmap(void)
02167 {
02168     NS_PRECONDITION(mXftFont, "FindFont should not return bad fonts");
02169 
02170     if (mFT_Face)
02171         return NS_OK;
02172 
02173     mFT_Face = XftLockFace(mXftFont);
02174 
02175     NS_ENSURE_TRUE(mFT_Face != nsnull, NS_ERROR_UNEXPECTED);
02176 
02177     // Select FT_Encoding to use for custom narrow fonts in 
02178     // glyph index look-up with FT_Get_Char_Index succeeds.
02179         
02180     if (FT_Select_Charmap (mFT_Face, mFontInfo->mFT_Encoding))
02181         return NS_ERROR_UNEXPECTED;
02182 
02183     return NS_OK;
02184 }
02185 
02186 void
02187 nsAutoDrawSpecBuffer::Draw(nscoord x, nscoord y, XftFont *font, FT_UInt glyph)
02188 {
02189     if (mSpecPos >= BUFFER_LEN-1)
02190         Flush();
02191 
02192     mSpecBuffer[mSpecPos].x = x;
02193     mSpecBuffer[mSpecPos].y = y;
02194     mSpecBuffer[mSpecPos].font = font;
02195     mSpecBuffer[mSpecPos].glyph = glyph;
02196     ++mSpecPos;
02197 }
02198 
02199 void
02200 nsAutoDrawSpecBuffer::Flush()
02201 {
02202     if (mSpecPos) {
02203         // Some Xft libraries will crash if none of the glyphs have any
02204         // area.  So before we draw, we scan through the glyphs.  If we
02205         // find any that have area, we can draw.
02206         for (PRUint32 i = 0; i < mSpecPos; i++) {
02207             XftGlyphFontSpec *sp = &mSpecBuffer[i];
02208             XGlyphInfo info;
02209             XftGlyphExtents(GDK_DISPLAY(), sp->font, &sp->glyph, 1, &info);
02210             if (info.width && info.height) {
02211                 // If we get here it means we found a drawable glyph.  We will
02212                 // Draw all the remaining glyphs and then break out of the loop
02213                 XftDrawGlyphFontSpec(mDraw, mColor, mSpecBuffer+i, mSpecPos-i);
02214                 break;
02215             }
02216         }
02217         mSpecPos = 0;
02218     }
02219 }
02220 
02221 // Static functions
02222 
02223 /* static */
02224 int
02225 CompareFontNames (const void* aArg1, const void* aArg2, void* aClosure)
02226 {
02227     const PRUnichar* str1 = *((const PRUnichar**) aArg1);
02228     const PRUnichar* str2 = *((const PRUnichar**) aArg2);
02229 
02230     return nsCRT::strcmp(str1, str2);
02231 }
02232 
02233 /* static */
02234 nsresult
02235 EnumFontsXft(nsIAtom* aLangGroup, const char* aGeneric,
02236              PRUint32* aCount, PRUnichar*** aResult)
02237 {
02238     FcPattern   *pat = NULL;
02239     FcObjectSet *os  = NULL;
02240     FcFontSet   *fs  = NULL;
02241     nsresult     rv  = NS_ERROR_FAILURE;
02242 
02243     PRUnichar **array = NULL;
02244     PRUint32    narray = 0;
02245     PRInt32     serif = 0, sansSerif = 0, monospace = 0, nGenerics;
02246 
02247     *aCount = 0;
02248     *aResult = nsnull;
02249 
02250     pat = FcPatternCreate();
02251     if (!pat)
02252         goto end;
02253 
02254     os = FcObjectSetBuild(FC_FAMILY, FC_FOUNDRY, NULL);
02255     if (!os)
02256         goto end;
02257 
02258     // take the pattern and add the lang group to it
02259     if (aLangGroup)
02260         NS_AddLangGroup(pat, aLangGroup);
02261 
02262     // get the font list
02263     fs = FcFontList(0, pat, os);
02264 
02265     if (!fs)
02266         goto end;
02267 
02268     // Fontconfig supports 3 generic fonts, "serif", "sans-serif", and
02269     // "monospace", slightly different from CSS's 5.
02270     if (!aGeneric)
02271         serif = sansSerif = monospace = 1;
02272     else if (!strcmp(aGeneric, "serif"))
02273         serif = 1;
02274     else if (!strcmp(aGeneric, "sans-serif"))
02275         sansSerif = 1;
02276     else if (!strcmp(aGeneric, "monospace"))
02277         monospace = 1;
02278     else if (!strcmp(aGeneric, "cursive") || !strcmp(aGeneric, "fantasy"))
02279         serif = sansSerif =  1;
02280     else
02281         NS_NOTREACHED("unexpected generic family");
02282     nGenerics = serif + sansSerif + monospace;
02283 
02284     array = NS_STATIC_CAST(PRUnichar **,
02285                nsMemory::Alloc((fs->nfont + nGenerics) * sizeof(PRUnichar *)));
02286     if (!array)
02287         goto end;
02288 
02289     if (serif) {
02290         PRUnichar *name = ToNewUnicode(NS_LITERAL_STRING("serif"));
02291         if (!name)
02292             goto end;
02293         array[narray++] = name;
02294     }
02295 
02296     if (sansSerif) {
02297         PRUnichar *name = ToNewUnicode(NS_LITERAL_STRING("sans-serif"));
02298         if (!name)
02299             goto end;
02300         array[narray++] = name;
02301     }
02302 
02303     if (monospace) {
02304         PRUnichar *name = ToNewUnicode(NS_LITERAL_STRING("monospace"));
02305         if (!name)
02306             goto end;
02307         array[narray++] = name;
02308     }
02309 
02310     for (int i=0; i < fs->nfont; ++i) {
02311         char *family;
02312 
02313         // if there's no family, just move to the next iteration
02314         if (FcPatternGetString (fs->fonts[i], FC_FAMILY, 0,
02315                                 (FcChar8 **) &family) != FcResultMatch) {
02316             continue;
02317         }
02318 
02319         // fontconfig always returns family names in UTF-8
02320         PRUnichar* name =  UTF8ToNewUnicode(nsDependentCString(family));
02321 
02322         if (!name)
02323             goto end;
02324 
02325         array[narray++] = name;
02326     }
02327 
02328     NS_QuickSort(array + nGenerics, narray - nGenerics, sizeof (PRUnichar*),
02329                  CompareFontNames, nsnull);
02330 
02331     *aCount = narray;
02332     if (narray)
02333         *aResult = array;
02334     else
02335         nsMemory::Free(array);
02336 
02337     rv = NS_OK;
02338 
02339  end:
02340     if (NS_FAILED(rv) && array) {
02341         while (narray)
02342             nsMemory::Free (array[--narray]);
02343         nsMemory::Free (array);
02344     }
02345     if (pat)
02346         FcPatternDestroy(pat);
02347     if (os)
02348         FcObjectSetDestroy(os);
02349     if (fs)
02350         FcFontSetDestroy(fs);
02351 
02352     return rv;
02353 }
02354 
02355 /* static */
02356 void
02357 ConvertCharToUCS4(const char *aString, PRUint32 aLength, 
02358                   nsAutoFcChar32Buffer &aOutBuffer, PRUint32 *aOutLen)
02359 {
02360     *aOutLen = 0;
02361     FcChar32 *outBuffer;
02362 
02363     if (!aOutBuffer.EnsureElemCapacity(aLength))
02364         return;
02365     outBuffer  = aOutBuffer.get();
02366     
02367     for (PRUint32 i = 0; i < aLength; ++i) {
02368         outBuffer[i] = PRUint8(aString[i]); // to convert char >= 0x80 correctly
02369     }
02370 
02371     *aOutLen = aLength;
02372 }
02373 
02374 // Convert the incoming string into an array of UCS4 chars
02375   
02376 /* static */
02377 void
02378 ConvertUnicharToUCS4(const PRUnichar *aString, PRUint32 aLength,
02379                      nsAutoFcChar32Buffer &aOutBuffer, PRUint32 *aOutLen)
02380 {
02381     *aOutLen = 0;
02382     FcChar32 *outBuffer;
02383 
02384     if (!aOutBuffer.EnsureElemCapacity(aLength))
02385         return;
02386     outBuffer  = aOutBuffer.get();
02387 
02388     PRUint32 outLen = 0;
02389 
02390     // Walk the passed in string looking for surrogates to convert to
02391     // their full ucs4 representation.
02392     for (PRUint32 i = 0; i < aLength; ++i) {
02393         PRUnichar c = aString[i];
02394 
02395         // Optimized for the non-surrogate case
02396         if (IS_NON_SURROGATE(c)) {
02397             outBuffer[outLen] = c;
02398         }
02399         else if (IS_HIGH_SURROGATE(aString[i])) {
02400             if (i + 1 < aLength && IS_LOW_SURROGATE(aString[i+1])) {
02401                 outBuffer[outLen] = SURROGATE_TO_UCS4(c, aString[i + 1]);
02402                 ++i;
02403             }
02404             else { // Unpaired high surrogate
02405                 outBuffer[outLen] = UCS2_REPLACEMENT;
02406             }
02407         }
02408         else if (IS_LOW_SURROGATE(aString[i])) { // Unpaired low surrogate?
02409             outBuffer[outLen] = UCS2_REPLACEMENT;
02410         }
02411 
02412         outLen++;
02413     }
02414 
02415     *aOutLen = outLen;
02416 }
02417 
02418 #ifdef MOZ_WIDGET_GTK2
02419 /* static */
02420 void
02421 GdkRegionSetXftClip(GdkRegion *aGdkRegion, XftDraw *aDraw)
02422 {
02423     GdkRectangle  *rects   = nsnull;
02424     int            n_rects = 0;
02425 
02426     gdk_region_get_rectangles(aGdkRegion, &rects, &n_rects);
02427 
02428     XRectangle *xrects = g_new(XRectangle, n_rects);
02429 
02430     for (int i=0; i < n_rects; ++i) {
02431         xrects[i].x = CLAMP(rects[i].x, G_MINSHORT, G_MAXSHORT);
02432         xrects[i].y = CLAMP(rects[i].y, G_MINSHORT, G_MAXSHORT);
02433         xrects[i].width = CLAMP(rects[i].width, G_MINSHORT, G_MAXSHORT);
02434         xrects[i].height = CLAMP(rects[i].height, G_MINSHORT, G_MAXSHORT);
02435     }
02436 
02437     XftDrawSetClipRectangles(aDraw, 0, 0, xrects, n_rects);
02438 
02439     g_free(xrects);
02440     g_free(rects);
02441 }
02442 #endif /* MOZ_WIDGET_GTK2 */
02443 
02444 // Helper to determine if a font has a private encoding that we know
02445 // something about
02446 /* static */
02447 nsresult
02448 GetEncoding(const char *aFontName, char **aEncoding, nsXftFontType &aType,
02449             FT_Encoding &aFTEncoding)
02450 {
02451   // below is a list of common used name for startup
02452     if ((!strcmp(aFontName, "Helvetica" )) ||
02453          (!strcmp(aFontName, "Times" )) ||
02454          (!strcmp(aFontName, "Times New Roman" )) ||
02455          (!strcmp(aFontName, "Courier New" )) ||
02456          (!strcmp(aFontName, "Courier" )) ||
02457          (!strcmp(aFontName, "Arial" )) ||
02458          (!strcmp(aFontName, "MS P Gothic" )) ||
02459         (!strcmp(aFontName, "Verdana" ))) {
02460         // error mean do not get a special encoding
02461         return NS_ERROR_NOT_AVAILABLE; 
02462     }
02463 
02464     nsCAutoString name;
02465     name.Assign(NS_LITERAL_CSTRING("encoding.") + 
02466                 nsDependentCString(aFontName) + NS_LITERAL_CSTRING(".ttf"));
02467 
02468     name.StripWhitespace();
02469     ToLowerCase(name);
02470 
02471     // if we have not init the property yet, init it right now.
02472     if (!gFontEncodingProperties)
02473         NS_LoadPersistentPropertiesFromURISpec(&gFontEncodingProperties,
02474             NS_LITERAL_CSTRING("resource://gre/res/fonts/fontEncoding.properties"));
02475 
02476     nsAutoString encoding;
02477     *aEncoding = nsnull;
02478     if (gFontEncodingProperties) {
02479         nsresult rv = gFontEncodingProperties->GetStringProperty(name,
02480                                                                  encoding);
02481         if (NS_FAILED(rv)) 
02482             return NS_ERROR_NOT_AVAILABLE;  // Unicode font
02483 
02484         // '.wide' at the end indicates 'wide' font.
02485         if (encoding.Length() > 5 && 
02486             StringEndsWith(encoding, NS_LITERAL_STRING(".wide"))) {
02487             aType = eFontTypeCustomWide;
02488             encoding.Truncate(encoding.Length() - 5);
02489         }
02490         else  {
02491             aType = eFontTypeCustom;
02492 
02493             // Mathematica and TeX CM truetype fonts have both Apple Roman
02494             // (PID=1, EID=0) and Unicode (PID=3, EID=1) cmaps. In the
02495             // former, Unicode cmap uses codepoints in PUA beginning
02496             // at U+F000 not representable in a single byte encoding
02497             // like MathML encodings. ( On the other hand, TeX CM fonts
02498             // have 'pseudo-Unicode' cmap with codepoints below U+0100.)
02499             // Unicode to font encoding converters for MathML map input 
02500             // Unicode codepoints to 'pseudo-glyph indices' in Apple Roman 
02501             // for  Mathematica, Symbol and MTExtra fonts while it maps
02502             // input Unicode codepoints  to 'pseudo-glyph indices' in 
02503             // 'Unicode cmap' for TeX CM fonts. Therefore we have to select
02504             // different FT_Encoding for two groups to guarantee that
02505             // glyph index look-up with FT_Get_Char_Index succeeds for
02506             // all MathML fonts. Instead of hard-coding this relation,
02507             // it's put in fontEncoding.properties file and is parsed here.
02508           
02509             nsAutoString ftCharMap; 
02510             nsresult rv = gFontEncodingProperties->GetStringProperty(
02511                           Substring(name, 0, name.Length() - 4) + 
02512                           NS_LITERAL_CSTRING(".ftcmap"), ftCharMap);
02513           
02514             if (NS_FAILED(rv)) 
02515                 aFTEncoding = ft_encoding_none;
02516             else if (ftCharMap.LowerCaseEqualsLiteral("mac_roman"))
02517                 aFTEncoding = ft_encoding_apple_roman;
02518             else if (ftCharMap.LowerCaseEqualsLiteral("unicode"))
02519                 aFTEncoding = ft_encoding_unicode;
02520         }
02521 
02522         // encoding name is always in US-ASCII so that there's no loss here.
02523         *aEncoding = ToNewCString(encoding);
02524         if (PR_LOG_TEST(gXftFontLoad, PR_LOG_DEBUG)) {
02525           printf("\t\tc> it's %s and encoding is %s\n",
02526                   aType==eFontTypeCustom ? "narrow" : "wide", *aEncoding);
02527         }
02528 
02529         return NS_OK;
02530     }
02531 
02532     return NS_ERROR_NOT_AVAILABLE;
02533 }
02534 
02535 /* static */
02536 static nsresult
02537 GetConverter(const char* aEncoding, nsIUnicodeEncoder **aConverter)
02538 {
02539     nsresult rv;
02540 
02541     if (!gCharsetManager) {
02542         CallGetService(kCharsetConverterManagerCID, &gCharsetManager);
02543         if (!gCharsetManager) {
02544             FreeGlobals();
02545             return NS_ERROR_FAILURE;
02546         }
02547     }
02548 
02549     // encoding name obtained from fontEncoding.properties is
02550     // canonical so that we don't need the alias resolution. use 'Raw'
02551     // version.
02552     rv = gCharsetManager->GetUnicodeEncoderRaw(aEncoding, aConverter);
02553     NS_ENSURE_SUCCESS(rv, rv);
02554     if (PR_LOG_TEST(gXftFontLoad, PR_LOG_DEBUG)) {
02555         printf("\t\tc> got the converter for %s \n", aEncoding);
02556     }
02557 
02558     return (*aConverter)->SetOutputErrorBehavior(
02559             (*aConverter)->kOnError_Replace, nsnull, '?');
02560 }
02561 
02562 
02563 /* static */
02564 nsresult
02565 FreeGlobals(void)
02566 {
02567     gInitialized = 0;
02568 
02569     NS_IF_RELEASE(gFontEncodingProperties);
02570     NS_IF_RELEASE(gCharsetManager);
02571 
02572     gFontXftMaps.Clear();
02573 
02574     return NS_OK;
02575 }
02576 
02577 nsFontXftInfo*
02578 GetFontXftInfo(FcPattern* aPattern)
02579 {
02580     const char* family;
02581 
02582     // If there's no family, just treat it as if it's a normal Unicode font
02583     if (FcPatternGetString(aPattern, FC_FAMILY, 0, (FcChar8 **) &family) 
02584          != FcResultMatch) {
02585         return nsnull;
02586     }
02587 
02588     NS_ASSERTION(gFontXftMaps.IsInitialized(), "gFontXMaps should be init'd by now.");
02589 
02590     nsFontXftInfo* info;
02591 
02592     // cached entry found. 
02593     if (gFontXftMaps.Get(family, &info))
02594         return info;
02595 
02596     nsCOMPtr<nsIUnicodeEncoder> converter;
02597     nsXftFontType fontType =  eFontTypeUnicode; 
02598     nsXPIDLCString encoding;
02599     FT_Encoding ftEncoding = ft_encoding_unicode;
02600     PRUint16* ccmap = nsnull;
02601 
02602     // See if a font has a custom/private encoding by matching
02603     // its family name against the list in fontEncoding.properties 
02604     // with GetEncoding(). It also sets fonttype (wide or narrow). 
02605     // Then get the converter and see if has a valid coverage map. 
02606     
02607     // XXX these two if-statements used to be logically AND'ed, but
02608     // string changes (bug 231995) made it impossible to use getter_Copies(encoding)
02609     // and encoding.get() in a single statement. Until Darin comes up with 
02610     // a solution, we need to split it into two if-statements. (bug 234908)
02611     if (NS_SUCCEEDED(GetEncoding(family, getter_Copies(encoding), 
02612                      fontType, ftEncoding))) {
02613         if (NS_SUCCEEDED(GetConverter(encoding.get(), 
02614                          getter_AddRefs(converter)))) {
02615             nsCOMPtr<nsICharRepresentable> mapper(do_QueryInterface(converter));
02616             if (PR_LOG_TEST(gXftFontLoad, PR_LOG_DEBUG)) {
02617                printf("\t\tc> got the converter and CMap :%s !!\n",
02618                       encoding.get());
02619             }
02620 
02621             if (mapper) {
02622                 ccmap = MapperToCCMap(mapper);
02623             }
02624         }
02625     }
02626 
02627     // XXX Need to check if an identical map has already been added - Bug 75260
02628     // For Xft, this doesn't look as critical as in GFX Win.
02629 
02630     info = new nsFontXftInfo; 
02631     if (!info) 
02632         return nsnull; 
02633 
02634     info->mCCMap =  ccmap;  
02635     info->mConverter = converter;
02636     info->mFontType = fontType;
02637     info->mFT_Encoding = ftEncoding;
02638 
02639     gFontXftMaps.Put(family, info); 
02640 
02641     return info;
02642 }
02643 
02644 /* static */
02645 // Convert input UCS4 string to "Pseudo-UCS4" string made of
02646 // custom font-specific codepoints with Unicode converter.
02647 nsresult
02648 ConvertUCS4ToCustom(FcChar32 *aSrc,  PRUint32 aSrcLen,
02649                     PRUint32& aDestLen, nsIUnicodeEncoder *aConverter,
02650                     PRBool aIsWide, nsAutoFcChar32Buffer& aResult)
02651 {
02652     nsresult rv = NS_OK;
02653 
02654     nsCOMPtr<nsIUnicodeEncoder> converter = aConverter;
02655     if (!converter )
02656         return NS_ERROR_UNEXPECTED;
02657 
02658     // Convert to UTF-16 because UnicodeEncoder expects input to be in
02659     // UTF-16.  We can get away with in-place replacement because
02660     // UTF-16 is at most as long as UCS-4 so that UCS-4(source) buffer
02661     // pointer is always ahead of UTF-16(target) buffer pointer and we
02662     // won't revisit the buffer we already processed.
02663     
02664     PRUnichar *utf16Src  = NS_REINTERPRET_CAST(PRUnichar *, aSrc);
02665     PRUnichar *utf16Ptr = utf16Src;
02666     for (PRUint32 i = 0; i < aSrcLen; ++i, ++utf16Ptr) {
02667         if (!IS_NON_BMP(aSrc[i])) {
02668             *utf16Ptr = PRUnichar(aSrc[i]);
02669         }
02670         else {
02671             *utf16Ptr = H_SURROGATE(aSrc[i]);
02672             *++utf16Ptr = L_SURROGATE(aSrc[i]);
02673         }
02674     }
02675 
02676     PRInt32 utf16SrcLen = utf16Ptr - utf16Src;
02677     PRInt32 medLen = utf16SrcLen;
02678     // Length can increase for 'Wide' custom fonts.
02679     if (aIsWide &&
02680         NS_FAILED(aConverter->GetMaxLength(utf16Src, utf16SrcLen, &medLen))) {
02681         return NS_ERROR_UNEXPECTED;
02682     }
02683 
02684     nsAutoBuffer<char, AUTO_BUFFER_SIZE> medBuffer;
02685     if (!medBuffer.EnsureElemCapacity(medLen))
02686         return NS_ERROR_OUT_OF_MEMORY;
02687     char *med  = medBuffer.get();
02688 
02689     // Convert utf16Src  to font-specific encoding with mConverter.
02690     rv = converter->Convert(utf16Src, &utf16SrcLen, med, &medLen);
02691     NS_ENSURE_SUCCESS(rv, rv);
02692 
02693     // Put pseudo-unicode str. into font specific pseudo-UCS-4 str.
02694     if (aIsWide) {
02695 #ifdef IS_LITTLE_ENDIAN
02696         // Convert BE UTF-16 to LE UTF-16 for 'wide' custom fonts
02697         char* pstr = med;
02698         while (pstr < med + medLen) {
02699             PRUint8 tmp = pstr[0];
02700             pstr[0] = pstr[1];
02701             pstr[1] = tmp;
02702             pstr += 2; // swap every two bytes
02703         }
02704 #endif
02705         // Convert 16bit  custom font codes to UCS4
02706         ConvertUnicharToUCS4(NS_REINTERPRET_CAST(PRUnichar *, med),
02707                              medLen >> 1, aResult, &aDestLen);
02708         rv = aDestLen ? rv : NS_ERROR_OUT_OF_MEMORY;
02709     }
02710     else {
02711         // Convert 8bit custom font codes to UCS4
02712         ConvertCharToUCS4(med, medLen, aResult, &aDestLen);
02713         rv = aDestLen ? rv : NS_ERROR_OUT_OF_MEMORY;
02714     }
02715 
02716     return rv;
02717 }