/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Pierre Phaneuf * Roland Mainz * Brian Stell * Morten Nilsen * Jungshik Shin * IBM Corporation * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #define ENABLE_X_FONT_BANNING 1 #include #include "gfx-config.h" #include "nscore.h" #include "nsQuickSort.h" #include "nsFontMetricsGTK.h" #include "nsIServiceManager.h" #include "nsICharsetConverterManager.h" #include "nsILanguageAtomService.h" #include "nsISaveAsCharset.h" #include "nsIPref.h" #include "nsCOMPtr.h" #include "nsPrintfCString.h" #include "nspr.h" #include "nsHashtable.h" #include "nsReadableUtils.h" #include "nsAString.h" #include "nsXPIDLString.h" #include "nsFontDebug.h" #ifdef MOZ_ENABLE_FREETYPE2 #include "nsFT2FontNode.h" #include "nsFontFreeType.h" #endif #include "nsXFontNormal.h" #include "nsX11AlphaBlend.h" #include "nsXFontAAScaledBitmap.h" #include "nsUnicharUtils.h" #ifdef ENABLE_X_FONT_BANNING #include #endif /* ENABLE_X_FONT_BANNING */ #include #include #define IS_SURROGATE(u) (u > 0x10000) #define UCS2_NOMAPPING 0XFFFD #ifdef PR_LOGGING static PRLogModuleInfo * FontMetricsGTKLM = PR_NewLogModule("FontMetricsGTK"); #endif /* PR_LOGGING */ #ifdef ENABLE_X_FONT_BANNING /* Not all platforms may have REG_OK */ #ifndef REG_OK #define REG_OK (0) #endif /* !REG_OK */ #endif /* ENABLE_X_FONT_BANNING */ #undef USER_DEFINED #define USER_DEFINED "x-user-def" // This is the scaling factor that we keep fonts limited to against // the display size. If a pixel size is requested that is more than // this factor larger than the height of the display, it's clamped to // that value instead of the requested size. #define FONT_MAX_FONT_SCALE 2 #undef NOISY_FONTS #undef REALLY_NOISY_FONTS #ifndef MOZ_ENABLE_FREETYPE2 static PRUint32 gFontDebug = 0 | NS_FONT_DEBUG_FONT_SCAN; #endif struct nsFontCharSetMap; struct nsFontFamilyName; struct nsFontPropertyName; struct nsFontStyle; struct nsFontWeight; struct nsFontLangGroup; struct nsFontCharSetInfo { const char* mCharSet; nsFontCharSetConverter Convert; PRUint8 mSpecialUnderline; #ifdef MOZ_ENABLE_FREETYPE2 PRInt32 mCodeRange1Bits; PRInt32 mCodeRange2Bits; #endif PRUint16* mCCMap; nsIUnicodeEncoder* mConverter; nsIAtom* mLangGroup; PRBool mInitedSizeInfo; PRInt32 mOutlineScaleMin; PRInt32 mAABitmapScaleMin; double mAABitmapOversize; double mAABitmapUndersize; PRBool mAABitmapScaleAlways; PRInt32 mBitmapScaleMin; double mBitmapOversize; double mBitmapUndersize; }; struct nsFontFamily { NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW nsFontNodeArray mNodes; }; struct nsFontFamilyName { const char* mName; const char* mXName; }; struct nsFontPropertyName { const char* mName; int mValue; }; static NS_DEFINE_CID(kCharSetManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); static NS_DEFINE_CID(kPrefCID, NS_PREF_CID); static NS_DEFINE_CID(kSaveAsCharsetCID, NS_SAVEASCHARSET_CID); static void SetCharsetLangGroup(nsFontCharSetInfo* aCharSetInfo); static int gFontMetricsGTKCount = 0; static int gInitialized = 0; static PRBool gForceOutlineScaledFonts = PR_FALSE; static PRBool gAllowDoubleByteSpecialChars = PR_TRUE; // XXX many of these statics need to be freed at shutdown time static nsIPref* gPref = nsnull; static float gDevScale = 0.0f; /* Scaler value from |GetCanonicalPixelScale()| */ static PRBool gScaleBitmapFontsWithDevScale = PR_FALSE; static nsICharsetConverterManager* gCharSetManager = nsnull; static nsIUnicodeEncoder* gUserDefinedConverter = nsnull; static nsHashtable* gAliases = nsnull; static nsHashtable* gCharSetMaps = nsnull; static nsHashtable* gFamilies = nsnull; static nsHashtable* gFFRENodes = nsnull; static nsHashtable* gAFRENodes = nsnull; // gCachedFFRESearches holds the "already looked up" // FFRE (Foundry Family Registry Encoding) font searches static nsHashtable* gCachedFFRESearches = nsnull; static nsHashtable* gSpecialCharSets = nsnull; static nsHashtable* gStretches = nsnull; static nsHashtable* gWeights = nsnull; nsISaveAsCharset* gFontSubConverter = nsnull; static nsFontNodeArray* gGlobalList = nsnull; static nsIAtom* gUnicode = nsnull; static nsIAtom* gUserDefined = nsnull; static nsIAtom* gZHTW = nsnull; static nsIAtom* gZHHK = nsnull; static nsIAtom* gZHTWHK = nsnull; // for fonts common to zh-TW and zh-HK static nsIAtom* gUsersLocale = nsnull; static nsIAtom* gWesternLocale = nsnull; // Controls for Outline Scaled Fonts (okay looking) static PRInt32 gOutlineScaleMinimum = 6; // Controls for Anti-Aliased Scaled Bitmaps (okay looking) static PRBool gAABitmapScaleEnabled = PR_TRUE; static PRBool gAABitmapScaleAlways = PR_FALSE; static PRInt32 gAABitmapScaleMinimum = 6; static double gAABitmapOversize = 1.1; static double gAABitmapUndersize = 0.9; // Controls for (regular) Scaled Bitmaps (very ugly) static PRInt32 gBitmapScaleMinimum = 10; static double gBitmapOversize = 1.2; static double gBitmapUndersize = 0.8; #ifdef ENABLE_X_FONT_BANNING static regex_t *gFontRejectRegEx = nsnull, *gFontAcceptRegEx = nsnull; #endif /* ENABLE_X_FONT_BANNING */ static gint SingleByteConvert(nsFontCharSetInfo* aSelf, XFontStruct* aFont, const PRUnichar* aSrcBuf, PRInt32 aSrcLen, char* aDestBuf, PRInt32 aDestLen); static gint DoubleByteConvert(nsFontCharSetInfo* aSelf, XFontStruct* aFont, const PRUnichar* aSrcBuf, PRInt32 aSrcLen, char* aDestBuf, PRInt32 aDestLen); static gint ISO10646Convert(nsFontCharSetInfo* aSelf, XFontStruct* aFont, const PRUnichar* aSrcBuf, PRInt32 aSrcLen, char* aDestBuf, PRInt32 aDestLen); static nsFontCharSetInfo Unknown = { nsnull }; static nsFontCharSetInfo Special = { nsnull }; #ifdef MOZ_ENABLE_FREETYPE2 static nsFontCharSetInfo USASCII = { "us-ascii", SingleByteConvert, 0, TT_OS2_CPR1_LATIN1 | TT_OS2_CPR1_MAC_ROMAN, TT_OS2_CPR2_CA_FRENCH | TT_OS2_CPR2_PORTUGESE | TT_OS2_CPR2_WE_LATIN1 | TT_OS2_CPR2_US }; static nsFontCharSetInfo ISO88591 = { "ISO-8859-1", SingleByteConvert, 0, TT_OS2_CPR1_LATIN1 | TT_OS2_CPR1_MAC_ROMAN, TT_OS2_CPR2_CA_FRENCH | TT_OS2_CPR2_PORTUGESE | TT_OS2_CPR2_WE_LATIN1 | TT_OS2_CPR2_US }; static nsFontCharSetInfo ISO88592 = { "ISO-8859-2", SingleByteConvert, 0, TT_OS2_CPR1_LATIN2, TT_OS2_CPR2_LATIN2 }; static nsFontCharSetInfo ISO88593 = { "ISO-8859-3", SingleByteConvert, 0, TT_OS2_CPR1_TURKISH, TT_OS2_CPR2_TURKISH }; static nsFontCharSetInfo ISO88594 = { "ISO-8859-4", SingleByteConvert, 0, TT_OS2_CPR1_BALTIC, TT_OS2_CPR2_BALTIC }; static nsFontCharSetInfo ISO88595 = { "ISO-8859-5", SingleByteConvert, 0, TT_OS2_CPR1_CYRILLIC, TT_OS2_CPR2_RUSSIAN | TT_OS2_CPR2_CYRILLIC }; static nsFontCharSetInfo ISO88596 = { "ISO-8859-6", SingleByteConvert, 0, TT_OS2_CPR1_ARABIC, TT_OS2_CPR2_ARABIC | TT_OS2_CPR2_ARABIC_708 }; static nsFontCharSetInfo ISO885968x = { "x-iso-8859-6-8-x", SingleByteConvert, 0, TT_OS2_CPR1_ARABIC, TT_OS2_CPR2_ARABIC | TT_OS2_CPR2_ARABIC_708 }; static nsFontCharSetInfo ISO8859616 = { "x-iso-8859-6-16", SingleByteConvert, 0, TT_OS2_CPR1_ARABIC, TT_OS2_CPR2_ARABIC | TT_OS2_CPR2_ARABIC_708 }; static nsFontCharSetInfo IBM1046 = { "x-IBM1046", SingleByteConvert, 0, TT_OS2_CPR1_ARABIC, TT_OS2_CPR2_ARABIC | TT_OS2_CPR2_ARABIC_708 }; static nsFontCharSetInfo ISO88597 = { "ISO-8859-7", SingleByteConvert, 0, TT_OS2_CPR1_GREEK, TT_OS2_CPR2_GREEK | TT_OS2_CPR2_GREEK_437G }; static nsFontCharSetInfo ISO88598 = { "ISO-8859-8", SingleByteConvert, 0, TT_OS2_CPR1_HEBREW, TT_OS2_CPR2_HEBREW }; // change from // { "ISO-8859-8", SingleByteConvertReverse, 0, 0, 0 }; // untill we fix the layout and ensure we only call this with pure RTL text static nsFontCharSetInfo ISO88599 = { "ISO-8859-9", SingleByteConvert, 0, TT_OS2_CPR1_TURKISH, TT_OS2_CPR2_TURKISH }; // no support for iso-8859-10 (Nordic/Icelandic) currently // static nsFontCharSetInfo ISO885910 = // { "ISO-8859-10", SingleByteConvert, 0, // 0, TT_OS2_CPR2_NORDIC | TT_OS2_CPR2_ICELANDIC }; // no support for iso-8859-12 (Vietnamese) currently // static nsFontCharSetInfo ISO885912 = // { "ISO-8859-12", SingleByteConvert, 0, // TT_OS2_CPR1_VIETNAMESE, 0 }; static nsFontCharSetInfo ISO885913 = { "ISO-8859-13", SingleByteConvert, 0, TT_OS2_CPR1_BALTIC, TT_OS2_CPR2_BALTIC }; static nsFontCharSetInfo ISO885915 = { "ISO-8859-15", SingleByteConvert, 0, TT_OS2_CPR1_LATIN2, TT_OS2_CPR2_LATIN2 }; static nsFontCharSetInfo JISX0201 = { "jis_0201", SingleByteConvert, 1, TT_OS2_CPR1_JAPANESE, 0 }; static nsFontCharSetInfo KOI8R = { "KOI8-R", SingleByteConvert, 0, TT_OS2_CPR1_CYRILLIC, TT_OS2_CPR2_RUSSIAN | TT_OS2_CPR2_CYRILLIC }; static nsFontCharSetInfo KOI8U = { "KOI8-U", SingleByteConvert, 0, TT_OS2_CPR1_CYRILLIC, TT_OS2_CPR2_RUSSIAN | TT_OS2_CPR2_CYRILLIC }; static nsFontCharSetInfo TIS6202 = /* Added to support thai context sensitive shaping if * CTL extension is is in force */ #ifdef SUNCTL { "tis620-2", SingleByteConvert, 0, TT_OS2_CPR1_THAI, 0 }; #else { "windows-874", SingleByteConvert, 0, TT_OS2_CPR1_THAI, 0 }; #endif /* SUNCTL */ static nsFontCharSetInfo TIS620 = { "TIS-620", SingleByteConvert, 0, TT_OS2_CPR1_THAI, 0 }; static nsFontCharSetInfo ISO885911 = { "ISO-8859-11", SingleByteConvert, 0, TT_OS2_CPR1_THAI, 0 }; static nsFontCharSetInfo Big5 = { "x-x-big5", DoubleByteConvert, 1, TT_OS2_CPR1_CHINESE_TRAD, 0 }; // a kludge to distinguish zh-TW only fonts in Big5 (such as hpbig5-) // from zh-TW/zh-HK common fonts in Big5 (such as big5-1) static nsFontCharSetInfo Big5TWHK = { "x-x-big5", DoubleByteConvert, 1, TT_OS2_CPR1_CHINESE_TRAD, 0 }; static nsFontCharSetInfo CNS116431 = { "x-cns-11643-1", DoubleByteConvert, 1, TT_OS2_CPR1_CHINESE_TRAD, 0 }; static nsFontCharSetInfo CNS116432 = { "x-cns-11643-2", DoubleByteConvert, 1, TT_OS2_CPR1_CHINESE_TRAD, 0 }; static nsFontCharSetInfo CNS116433 = { "x-cns-11643-3", DoubleByteConvert, 1, TT_OS2_CPR1_CHINESE_TRAD, 0 }; static nsFontCharSetInfo CNS116434 = { "x-cns-11643-4", DoubleByteConvert, 1, TT_OS2_CPR1_CHINESE_TRAD, 0 }; static nsFontCharSetInfo CNS116435 = { "x-cns-11643-5", DoubleByteConvert, 1, TT_OS2_CPR1_CHINESE_TRAD, 0 }; static nsFontCharSetInfo CNS116436 = { "x-cns-11643-6", DoubleByteConvert, 1, TT_OS2_CPR1_CHINESE_TRAD, 0 }; static nsFontCharSetInfo CNS116437 = { "x-cns-11643-7", DoubleByteConvert, 1, TT_OS2_CPR1_CHINESE_TRAD, 0 }; static nsFontCharSetInfo GB2312 = { "gb_2312-80", DoubleByteConvert, 1, TT_OS2_CPR1_CHINESE_SIMP, 0 }; static nsFontCharSetInfo GB18030_0 = { "gb18030.2000-0", DoubleByteConvert, 1, TT_OS2_CPR1_CHINESE_SIMP, 0 }; static nsFontCharSetInfo GB18030_1 = { "gb18030.2000-1", DoubleByteConvert, 1, TT_OS2_CPR1_CHINESE_SIMP, 0 }; static nsFontCharSetInfo GBK = { "x-gbk-noascii", DoubleByteConvert, 1, TT_OS2_CPR1_CHINESE_SIMP, 0 }; static nsFontCharSetInfo HKSCS = { "hkscs-1", DoubleByteConvert, 1, TT_OS2_CPR1_CHINESE_TRAD, 0 }; static nsFontCharSetInfo JISX0208 = { "jis_0208-1983", DoubleByteConvert, 1, TT_OS2_CPR1_JAPANESE, 0 }; static nsFontCharSetInfo JISX0212 = { "jis_0212-1990", DoubleByteConvert, 1, TT_OS2_CPR1_JAPANESE, 0 }; static nsFontCharSetInfo KSC5601 = { "ks_c_5601-1987", DoubleByteConvert, 1, TT_OS2_CPR1_KO_WANSUNG | TT_OS2_CPR1_KO_JOHAB, 0 }; static nsFontCharSetInfo X11Johab = { "x-x11johab", DoubleByteConvert, 1, TT_OS2_CPR1_KO_WANSUNG | TT_OS2_CPR1_KO_JOHAB, 0 }; static nsFontCharSetInfo JohabNoAscii = { "x-johab-noascii", DoubleByteConvert, 1, TT_OS2_CPR1_KO_WANSUNG | TT_OS2_CPR1_KO_JOHAB, 0 }; static nsFontCharSetInfo JamoTTF = { "x-koreanjamo-0", DoubleByteConvert, 1, TT_OS2_CPR1_KO_WANSUNG | TT_OS2_CPR1_KO_JOHAB, 0 }; static nsFontCharSetInfo TamilTTF = { "x-tamilttf-0", DoubleByteConvert, 0, 0, 0 }; static nsFontCharSetInfo CP1250 = { "windows-1250", SingleByteConvert, 0, TT_OS2_CPR1_LATIN2, TT_OS2_CPR2_LATIN2 }; static nsFontCharSetInfo CP1251 = { "windows-1251", SingleByteConvert, 0, TT_OS2_CPR1_CYRILLIC, TT_OS2_CPR2_RUSSIAN }; static nsFontCharSetInfo CP1252 = { "windows-1252", SingleByteConvert, 0, TT_OS2_CPR1_LATIN1 | TT_OS2_CPR1_MAC_ROMAN, TT_OS2_CPR2_CA_FRENCH | TT_OS2_CPR2_PORTUGESE | TT_OS2_CPR2_WE_LATIN1 | TT_OS2_CPR2_US }; static nsFontCharSetInfo CP1253 = { "windows-1253", SingleByteConvert, 0, TT_OS2_CPR1_GREEK, TT_OS2_CPR2_GREEK | TT_OS2_CPR2_GREEK_437G }; static nsFontCharSetInfo CP1257 = { "windows-1257", SingleByteConvert, 0, TT_OS2_CPR1_BALTIC, TT_OS2_CPR2_BALTIC }; #ifdef SUNCTL /* Hindi range currently unsupported in FT2 range. Change TT* once we arrive at a way to identify hindi */ static nsFontCharSetInfo SunIndic = { "x-sun-unicode-india-0", DoubleByteConvert, 0, 0, 0 }; #endif /* SUNCTL */ static nsFontCharSetInfo ISO106461 = { nsnull, ISO10646Convert, 1, 0xFFFFFFFF, 0xFFFFFFFF }; static nsFontCharSetInfo AdobeSymbol = { "Adobe-Symbol-Encoding", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0 }; static nsFontCharSetInfo AdobeEuro = { "x-adobe-euro", SingleByteConvert, 0, 0, 0 }; #ifdef MOZ_MATHML static nsFontCharSetInfo CMCMEX = { "x-t1-cmex", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0}; static nsFontCharSetInfo CMCMSY = { "x-t1-cmsy", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0}; static nsFontCharSetInfo CMCMR = { "x-t1-cmr", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0}; static nsFontCharSetInfo CMCMMI = { "x-t1-cmmi", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0}; static nsFontCharSetInfo Mathematica1 = { "x-mathematica1", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0}; static nsFontCharSetInfo Mathematica2 = { "x-mathematica2", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0}; static nsFontCharSetInfo Mathematica3 = { "x-mathematica3", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0}; static nsFontCharSetInfo Mathematica4 = { "x-mathematica4", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0}; static nsFontCharSetInfo Mathematica5 = { "x-mathematica5", SingleByteConvert, 0, TT_OS2_CPR1_SYMBOL, 0}; #endif /* MATHML */ #else static nsFontCharSetInfo USASCII = { "us-ascii", SingleByteConvert, 0 }; static nsFontCharSetInfo ISO88591 = { "ISO-8859-1", SingleByteConvert, 0 }; static nsFontCharSetInfo ISO88592 = { "ISO-8859-2", SingleByteConvert, 0 }; static nsFontCharSetInfo ISO88593 = { "ISO-8859-3", SingleByteConvert, 0 }; static nsFontCharSetInfo ISO88594 = { "ISO-8859-4", SingleByteConvert, 0 }; static nsFontCharSetInfo ISO88595 = { "ISO-8859-5", SingleByteConvert, 0 }; static nsFontCharSetInfo ISO88596 = { "ISO-8859-6", SingleByteConvert, 0 }; static nsFontCharSetInfo ISO885968x = { "x-iso-8859-6-8-x", SingleByteConvert, 0 }; static nsFontCharSetInfo ISO8859616 = { "x-iso-8859-6-16", SingleByteConvert, 0 }; static nsFontCharSetInfo IBM1046 = { "x-IBM1046", SingleByteConvert, 0 }; static nsFontCharSetInfo ISO88597 = { "ISO-8859-7", SingleByteConvert, 0 }; static nsFontCharSetInfo ISO88598 = { "ISO-8859-8", SingleByteConvert, 0 }; // change from // { "ISO-8859-8", SingleByteConvertReverse, 0, 0, 0 }; // untill we fix the layout and ensure we only call this with pure RTL text static nsFontCharSetInfo ISO88599 = { "ISO-8859-9", SingleByteConvert, 0 }; // no support for iso-8859-10 (Nordic/Icelandic) currently // static nsFontCharSetInfo ISO885910 = // { "ISO-8859-10", SingleByteConvert, 0, // 0, TT_OS2_CPR2_NORDIC | TT_OS2_CPR2_ICELANDIC }; // no support for iso-8859-12 (Vietnamese) currently // static nsFontCharSetInfo ISO885912 = // { "ISO-8859-12", SingleByteConvert, 0, // TT_OS2_CPR1_VIETNAMESE, 0 }; static nsFontCharSetInfo ISO885913 = { "ISO-8859-13", SingleByteConvert, 0 }; static nsFontCharSetInfo ISO885915 = { "ISO-8859-15", SingleByteConvert, 0 }; static nsFontCharSetInfo JISX0201 = { "jis_0201", SingleByteConvert, 1 }; static nsFontCharSetInfo KOI8R = { "KOI8-R", SingleByteConvert, 0 }; static nsFontCharSetInfo KOI8U = { "KOI8-U", SingleByteConvert, 0 }; static nsFontCharSetInfo TIS6202 = /* Added to support thai context sensitive shaping if * CTL extension is is in force */ #ifdef SUNCTL { "tis620-2", SingleByteConvert, 0 }; #else { "windows-874", SingleByteConvert, 0 }; #endif /* SUNCTL */ static nsFontCharSetInfo TIS620 = { "TIS-620", SingleByteConvert, 0 }; static nsFontCharSetInfo ISO885911 = { "ISO-8859-11", SingleByteConvert, 0 }; static nsFontCharSetInfo Big5 = { "x-x-big5", DoubleByteConvert, 1 }; // a kludge to distinguish zh-TW only fonts in Big5 (such as hpbig5-) // from zh-TW/zh-HK common fonts in Big5 (such as big5-1) static nsFontCharSetInfo Big5TWHK = { "x-x-big5", DoubleByteConvert, 1 }; static nsFontCharSetInfo CNS116431 = { "x-cns-11643-1", DoubleByteConvert, 1 }; static nsFontCharSetInfo CNS116432 = { "x-cns-11643-2", DoubleByteConvert, 1 }; static nsFontCharSetInfo CNS116433 = { "x-cns-11643-3", DoubleByteConvert, 1 }; static nsFontCharSetInfo CNS116434 = { "x-cns-11643-4", DoubleByteConvert, 1 }; static nsFontCharSetInfo CNS116435 = { "x-cns-11643-5", DoubleByteConvert, 1 }; static nsFontCharSetInfo CNS116436 = { "x-cns-11643-6", DoubleByteConvert, 1 }; static nsFontCharSetInfo CNS116437 = { "x-cns-11643-7", DoubleByteConvert, 1 }; static nsFontCharSetInfo GB2312 = { "gb_2312-80", DoubleByteConvert, 1 }; static nsFontCharSetInfo GB18030_0 = { "gb18030.2000-0", DoubleByteConvert, 1 }; static nsFontCharSetInfo GB18030_1 = { "gb18030.2000-1", DoubleByteConvert, 1 }; static nsFontCharSetInfo GBK = { "x-gbk-noascii", DoubleByteConvert, 1 }; static nsFontCharSetInfo HKSCS = { "hkscs-1", DoubleByteConvert, 1 }; static nsFontCharSetInfo JISX0208 = { "jis_0208-1983", DoubleByteConvert, 1 }; static nsFontCharSetInfo JISX0212 = { "jis_0212-1990", DoubleByteConvert, 1 }; static nsFontCharSetInfo KSC5601 = { "ks_c_5601-1987", DoubleByteConvert, 1 }; static nsFontCharSetInfo X11Johab = { "x-x11johab", DoubleByteConvert, 1 }; static nsFontCharSetInfo JohabNoAscii = { "x-johab-noascii", DoubleByteConvert, 1 }; static nsFontCharSetInfo JamoTTF = { "x-koreanjamo-0", DoubleByteConvert, 1 }; static nsFontCharSetInfo TamilTTF = { "x-tamilttf-0", DoubleByteConvert, 0 }; static nsFontCharSetInfo CP1250 = { "windows-1250", SingleByteConvert, 0 }; static nsFontCharSetInfo CP1251 = { "windows-1251", SingleByteConvert, 0 }; static nsFontCharSetInfo CP1252 = { "windows-1252", SingleByteConvert, 0 }; static nsFontCharSetInfo CP1253 = { "windows-1253", SingleByteConvert, 0 }; static nsFontCharSetInfo CP1257 = { "windows-1257", SingleByteConvert, 0 }; #ifdef SUNCTL /* Hindi range currently unsupported in FT2 range. Change TT* once we arrive at a way to identify hindi */ static nsFontCharSetInfo SunIndic = { "x-sun-unicode-india-0", DoubleByteConvert, 0 }; #endif /* SUNCTL */ static nsFontCharSetInfo ISO106461 = { nsnull, ISO10646Convert, 1}; static nsFontCharSetInfo AdobeSymbol = { "Adobe-Symbol-Encoding", SingleByteConvert, 0 }; static nsFontCharSetInfo AdobeEuro = { "x-adobe-euro", SingleByteConvert, 0 }; #ifdef MOZ_MATHML static nsFontCharSetInfo CMCMEX = { "x-t1-cmex", SingleByteConvert, 0}; static nsFontCharSetInfo CMCMSY = { "x-t1-cmsy", SingleByteConvert, 0}; static nsFontCharSetInfo CMCMR = { "x-t1-cmr", SingleByteConvert, 0}; static nsFontCharSetInfo CMCMMI = { "x-t1-cmmi", SingleByteConvert, 0}; static nsFontCharSetInfo Mathematica1 = { "x-mathematica1", SingleByteConvert, 0}; static nsFontCharSetInfo Mathematica2 = { "x-mathematica2", SingleByteConvert, 0}; static nsFontCharSetInfo Mathematica3 = { "x-mathematica3", SingleByteConvert, 0}; static nsFontCharSetInfo Mathematica4 = { "x-mathematica4", SingleByteConvert, 0}; static nsFontCharSetInfo Mathematica5 = { "x-mathematica5", SingleByteConvert, 0}; #endif /* MATHML */ #endif /* FREETYPE2 */ static nsFontLangGroup FLG_WESTERN = { "x-western", nsnull }; static nsFontLangGroup FLG_RUSSIAN = { "x-cyrillic", nsnull }; static nsFontLangGroup FLG_BALTIC = { "x-baltic", nsnull }; static nsFontLangGroup FLG_CE = { "x-central-euro",nsnull }; static nsFontLangGroup FLG_GREEK = { "el", nsnull }; static nsFontLangGroup FLG_TURKISH = { "tr", nsnull }; static nsFontLangGroup FLG_HEBREW = { "he", nsnull }; static nsFontLangGroup FLG_ARABIC = { "ar", nsnull }; static nsFontLangGroup FLG_THAI = { "th", nsnull }; static nsFontLangGroup FLG_ZHCN = { "zh-CN", nsnull }; static nsFontLangGroup FLG_ZHTW = { "zh-TW", nsnull }; static nsFontLangGroup FLG_ZHHK = { "zh-HK", nsnull }; static nsFontLangGroup FLG_ZHTWHK = { "x-zh-TWHK", nsnull }; // TW + HK static nsFontLangGroup FLG_JA = { "ja", nsnull }; static nsFontLangGroup FLG_KO = { "ko", nsnull }; #ifdef SUNCTL static nsFontLangGroup FLG_INDIC = { "x-devanagari", nsnull }; #endif static nsFontLangGroup FLG_TAMIL = { "x-tamil", nsnull }; static nsFontLangGroup FLG_NONE = { nsnull, nsnull }; /* * Normally, the charset of an X font can be determined simply by looking at * the last 2 fields of the long XLFD font name (CHARSET_REGISTRY and * CHARSET_ENCODING). However, there are a number of special cases: * * Sometimes, X server vendors use the same name to mean different things. For * example, IRIX uses "cns11643-1" to mean the 2nd plane of CNS 11643, while * Solaris uses that name for the 1st plane. * * Some X server vendors use certain names for something completely different. * For example, some Solaris fonts say "gb2312.1980-0" but are actually ASCII * fonts. These cases can be detected by looking at the POINT_SIZE and * AVERAGE_WIDTH fields. If the average width is half the point size, this is * an ASCII font, not GB 2312. * * Some fonts say "fontspecific" in the CHARSET_ENCODING field. Their charsets * depend on the FAMILY_NAME. For example, the following is a "Symbol" font: * * -adobe-symbol-medium-r-normal--17-120-100-100-p-95-adobe-fontspecific * * Some vendors use one name to mean 2 different things, depending on the font. * For example, AIX has some "ksc5601.1987-0" fonts that require the 8th bit of * both bytes to be zero, while other fonts require them to be set to one. * These cases can be distinguished by looking at the FOUNDRY field, but a * better way is to look at XFontStruct.min_byte1. */ static nsFontCharSetMap gCharSetMap[] = { { "-ascii", &FLG_NONE, &Unknown }, { "-ibm pc", &FLG_NONE, &Unknown }, { "adobe-fontspecific", &FLG_NONE, &Special }, { "ansi-1251", &FLG_RUSSIAN, &CP1251 }, // On Solaris, big5-0 is used for ASCII-only fonts while in XFree86, // it's for Big5 fonts without US-ASCII. When a non-Solaris binary // is displayed on a Solaris X server, this would break. #ifndef SOLARIS { "big5-0", &FLG_ZHTWHK, &Big5TWHK }, // for both TW and HK #else { "big5-0", &FLG_ZHTW, &USASCII }, #endif { "big5-1", &FLG_ZHTWHK, &Big5TWHK }, // ditto { "big5.et-0", &FLG_ZHTW, &Big5 }, { "big5.et.ext-0", &FLG_ZHTW, &Big5 }, { "big5.etext-0", &FLG_ZHTW, &Big5 }, { "big5.hku-0", &FLG_ZHTW, &Big5 }, { "big5.hku-1", &FLG_ZHTW, &Big5 }, { "big5.pc-0", &FLG_ZHTW, &Big5 }, { "big5.shift-0", &FLG_ZHTW, &Big5 }, { "big5hkscs-0", &FLG_ZHHK, &HKSCS }, { "cns11643.1986-1", &FLG_ZHTW, &CNS116431 }, { "cns11643.1986-2", &FLG_ZHTW, &CNS116432 }, { "cns11643.1992-1", &FLG_ZHTW, &CNS116431 }, { "cns11643.1992.1-0", &FLG_ZHTW, &CNS116431 }, { "cns11643.1992-12", &FLG_NONE, &Unknown }, { "cns11643.1992.2-0", &FLG_ZHTW, &CNS116432 }, { "cns11643.1992-2", &FLG_ZHTW, &CNS116432 }, { "cns11643.1992-3", &FLG_ZHTW, &CNS116433 }, { "cns11643.1992.3-0", &FLG_ZHTW, &CNS116433 }, { "cns11643.1992.4-0", &FLG_ZHTW, &CNS116434 }, { "cns11643.1992-4", &FLG_ZHTW, &CNS116434 }, { "cns11643.1992.5-0", &FLG_ZHTW, &CNS116435 }, { "cns11643.1992-5", &FLG_ZHTW, &CNS116435 }, { "cns11643.1992.6-0", &FLG_ZHTW, &CNS116436 }, { "cns11643.1992-6", &FLG_ZHTW, &CNS116436 }, { "cns11643.1992.7-0", &FLG_ZHTW, &CNS116437 }, { "cns11643.1992-7", &FLG_ZHTW, &CNS116437 }, { "cns11643-1", &FLG_ZHTW, &CNS116431 }, { "cns11643-2", &FLG_ZHTW, &CNS116432 }, { "cns11643-3", &FLG_ZHTW, &CNS116433 }, { "cns11643-4", &FLG_ZHTW, &CNS116434 }, { "cns11643-5", &FLG_ZHTW, &CNS116435 }, { "cns11643-6", &FLG_ZHTW, &CNS116436 }, { "cns11643-7", &FLG_ZHTW, &CNS116437 }, { "cp1251-1", &FLG_RUSSIAN, &CP1251 }, { "dec-dectech", &FLG_NONE, &Unknown }, { "dtsymbol-1", &FLG_NONE, &Unknown }, { "fontspecific-0", &FLG_NONE, &Unknown }, { "gb2312.1980-0", &FLG_ZHCN, &GB2312 }, { "gb2312.1980-1", &FLG_ZHCN, &GB2312 }, { "gb13000.1993-1", &FLG_ZHCN, &GBK }, { "gb18030.2000-0", &FLG_ZHCN, &GB18030_0 }, { "gb18030.2000-1", &FLG_ZHCN, &GB18030_1 }, { "gbk-0", &FLG_ZHCN, &GBK }, { "gbk1988.1989-0", &FLG_ZHCN, &USASCII }, { "hkscs-1", &FLG_ZHHK, &HKSCS }, { "hp-japanese15", &FLG_NONE, &Unknown }, { "hp-japaneseeuc", &FLG_NONE, &Unknown }, { "hp-roman8", &FLG_NONE, &Unknown }, { "hp-schinese15", &FLG_NONE, &Unknown }, { "hp-tchinese15", &FLG_NONE, &Unknown }, { "hp-tchinesebig5", &FLG_ZHTW, &Big5 }, { "hp-wa", &FLG_NONE, &Unknown }, { "hpbig5-", &FLG_ZHTW, &Big5 }, { "hphkbig5-", &FLG_ZHHK, &HKSCS }, { "hproc16-", &FLG_NONE, &Unknown }, { "ibm-1046", &FLG_ARABIC, &IBM1046 }, { "ibm-1252", &FLG_NONE, &Unknown }, { "ibm-850", &FLG_NONE, &Unknown }, { "ibm-fontspecific", &FLG_NONE, &Unknown }, { "ibm-sbdcn", &FLG_NONE, &Unknown }, { "ibm-sbdtw", &FLG_NONE, &Unknown }, { "ibm-special", &FLG_NONE, &Unknown }, { "ibm-udccn", &FLG_NONE, &Unknown }, { "ibm-udcjp", &FLG_NONE, &Unknown }, { "ibm-udctw", &FLG_NONE, &Unknown }, { "iso646.1991-irv", &FLG_NONE, &Unknown }, { "iso8859-1", &FLG_WESTERN, &ISO88591 }, { "iso8859-13", &FLG_BALTIC, &ISO885913 }, { "iso8859-15", &FLG_WESTERN, &ISO885915 }, { "iso8859-1@cn", &FLG_NONE, &Unknown }, { "iso8859-1@kr", &FLG_NONE, &Unknown }, { "iso8859-1@tw", &FLG_NONE, &Unknown }, { "iso8859-1@zh", &FLG_NONE, &Unknown }, { "iso8859-2", &FLG_CE, &ISO88592 }, { "iso8859-3", &FLG_WESTERN, &ISO88593 }, { "iso8859-4", &FLG_BALTIC, &ISO88594 }, { "iso8859-5", &FLG_RUSSIAN, &ISO88595 }, { "iso8859-6", &FLG_ARABIC, &ISO88596 }, { "iso8859-6.8x", &FLG_ARABIC, &ISO885968x }, { "iso8859-6.16" , &FLG_ARABIC, &ISO8859616 }, { "iso8859-7", &FLG_GREEK, &ISO88597 }, { "iso8859-8", &FLG_HEBREW, &ISO88598 }, { "iso8859-9", &FLG_TURKISH, &ISO88599 }, { "iso10646-1", &FLG_NONE, &ISO106461 }, { "jisx0201.1976-0", &FLG_JA, &JISX0201 }, { "jisx0201.1976-1", &FLG_JA, &JISX0201 }, { "jisx0208.1983-0", &FLG_JA, &JISX0208 }, { "jisx0208.1990-0", &FLG_JA, &JISX0208 }, { "jisx0212.1990-0", &FLG_JA, &JISX0212 }, { "koi8-r", &FLG_RUSSIAN, &KOI8R }, { "koi8-u", &FLG_RUSSIAN, &KOI8U }, { "johab-1", &FLG_KO, &X11Johab }, { "johabs-1", &FLG_KO, &X11Johab }, { "johabsh-1", &FLG_KO, &X11Johab }, { "ksc5601.1987-0", &FLG_KO, &KSC5601 }, // we can handle GR fonts with GL encoders (KSC5601 and GB2312) // See |DoubleByteConvert|. { "ksc5601.1987-1", &FLG_KO, &KSC5601 }, { "ksc5601.1992-3", &FLG_KO, &JohabNoAscii }, { "koreanjamo-0", &FLG_KO, &JamoTTF }, { "microsoft-cp1250", &FLG_CE, &CP1250 }, { "microsoft-cp1251", &FLG_RUSSIAN, &CP1251 }, { "microsoft-cp1252", &FLG_WESTERN, &CP1252 }, { "microsoft-cp1253", &FLG_GREEK, &CP1253 }, { "microsoft-cp1257", &FLG_BALTIC, &CP1257 }, { "misc-fontspecific", &FLG_NONE, &Unknown }, { "sgi-fontspecific", &FLG_NONE, &Unknown }, { "sun-fontspecific", &FLG_NONE, &Unknown }, { "sunolcursor-1", &FLG_NONE, &Unknown }, { "sunolglyph-1", &FLG_NONE, &Unknown }, { "symbol-fontspecific",&FLG_NONE, &Special }, { "tis620.2529-1", &FLG_THAI, &TIS620 }, { "tis620.2533-0", &FLG_THAI, &TIS620 }, { "tis620.2533-1", &FLG_THAI, &TIS620 }, { "tis620-0", &FLG_THAI, &TIS620 }, { "tis620-2", &FLG_THAI, &TIS6202 }, { "iso8859-11", &FLG_THAI, &ISO885911 }, { "ucs2.cjk-0", &FLG_NONE, &ISO106461 }, { "ucs2.cjk_china-0", &FLG_ZHCN, &ISO106461 }, { "iso10646.2000-cn", &FLG_ZHCN, &ISO106461 }, // HP/UX { "ucs2.cjk_japan-0", &FLG_JA, &ISO106461 }, { "ucs2.cjk_korea-0", &FLG_KO, &ISO106461 }, { "korean.ucs2-0", &FLG_KO, &ISO106461 }, // HP/UX { "ucs2.cjk_taiwan-0", &FLG_ZHTW, &ISO106461 }, { "ucs2.thai-0", &FLG_THAI, &ISO106461 }, { "tamilttf-0", &FLG_TAMIL, &TamilTTF }, #ifdef SUNCTL { "sun.unicode.india-0",&FLG_INDIC, &SunIndic }, #endif /* SUNCTL */ { nsnull, nsnull, nsnull } }; static nsFontFamilyName gFamilyNameTable[] = { { "arial", "helvetica" }, { "courier new", "courier" }, { "times new roman", "times" }, #ifdef MOZ_MATHML { "cmex", "cmex10" }, { "cmsy", "cmsy10" }, { "-moz-math-text", "times" }, { "-moz-math-symbol", "symbol" }, #endif { nsnull, nsnull } }; static nsFontCharSetMap gNoneCharSetMap[] = { { nsnull }, }; static nsFontCharSetMap gSpecialCharSetMap[] = { { "symbol-adobe-fontspecific", &FLG_NONE, &AdobeSymbol }, { "euromono-adobe-fontspecific", &FLG_NONE, &AdobeEuro }, { "eurosans-adobe-fontspecific", &FLG_NONE, &AdobeEuro }, { "euroserif-adobe-fontspecific", &FLG_NONE, &AdobeEuro }, #ifdef MOZ_MATHML { "cmex10-adobe-fontspecific", &FLG_NONE, &CMCMEX }, { "cmsy10-adobe-fontspecific", &FLG_NONE, &CMCMSY }, { "cmr10-adobe-fontspecific", &FLG_NONE, &CMCMR }, { "cmmi10-adobe-fontspecific", &FLG_NONE, &CMCMMI }, { "math1-adobe-fontspecific", &FLG_NONE, &Mathematica1 }, { "math2-adobe-fontspecific", &FLG_NONE, &Mathematica2 }, { "math3-adobe-fontspecific", &FLG_NONE, &Mathematica3 }, { "math4-adobe-fontspecific", &FLG_NONE, &Mathematica4 }, { "math5-adobe-fontspecific", &FLG_NONE, &Mathematica5 }, { "math1mono-adobe-fontspecific", &FLG_NONE, &Mathematica1 }, { "math2mono-adobe-fontspecific", &FLG_NONE, &Mathematica2 }, { "math3mono-adobe-fontspecific", &FLG_NONE, &Mathematica3 }, { "math4mono-adobe-fontspecific", &FLG_NONE, &Mathematica4 }, { "math5mono-adobe-fontspecific", &FLG_NONE, &Mathematica5 }, #endif { nsnull, nsnull } }; static nsFontPropertyName gStretchNames[] = { { "block", 5 }, // XXX { "bold", 7 }, // XXX { "double wide", 9 }, { "medium", 5 }, { "narrow", 3 }, { "normal", 5 }, { "semicondensed", 4 }, { "wide", 7 }, { nsnull, 0 } }; static nsFontPropertyName gWeightNames[] = { { "black", 900 }, { "bold", 700 }, { "book", 400 }, { "demi", 600 }, { "demibold", 600 }, { "light", 300 }, { "medium", 400 }, { "regular", 400 }, { nsnull, 0 } }; static char* atomToName(nsIAtom* aAtom) { const char *namePRU; aAtom->GetUTF8String(&namePRU); return ToNewCString(nsDependentCString(namePRU)); } static PRUint16* gUserDefinedCCMap = nsnull; static PRUint16* gEmptyCCMap = nsnull; // // smart quotes (and other special chars) in Asian (double byte) // fonts are too large to use is western fonts. // Here we define those characters. // XXX: This array can (and need, for performance) be made |const| when // GTK port of gfx gets sync'd with Xlib port for multiple device contexts. #include "dbyte_special_chars.ccmap" DEFINE_CCMAP(gDoubleByteSpecialCharsCCMap, /* nothing */); static PRBool FreeCharSetMap(nsHashKey* aKey, void* aData, void* aClosure) { nsFontCharSetMap* charsetMap = (nsFontCharSetMap*) aData; NS_IF_RELEASE(charsetMap->mInfo->mConverter); NS_IF_RELEASE(charsetMap->mInfo->mLangGroup); FreeCCMap(charsetMap->mInfo->mCCMap); return PR_TRUE; } static PRBool FreeFamily(nsHashKey* aKey, void* aData, void* aClosure) { delete (nsFontFamily*) aData; return PR_TRUE; } static void FreeStretch(nsFontStretch* aStretch) { PR_smprintf_free(aStretch->mScalable); for (PRInt32 count = aStretch->mScaledFonts.Count()-1; count >= 0; --count) { nsFontGTK *font = (nsFontGTK*)aStretch->mScaledFonts.ElementAt(count); if (font) delete font; } // aStretch->mScaledFonts.Clear(); handled by delete of aStretch for (int i = 0; i < aStretch->mSizesCount; i++) { delete aStretch->mSizes[i]; } delete [] aStretch->mSizes; delete aStretch; } static void FreeWeight(nsFontWeight* aWeight) { for (int i = 0; i < 9; i++) { if (aWeight->mStretches[i]) { for (int j = i + 1; j < 9; j++) { if (aWeight->mStretches[j] == aWeight->mStretches[i]) { aWeight->mStretches[j] = nsnull; } } FreeStretch(aWeight->mStretches[i]); } } delete aWeight; } static void FreeStyle(nsFontStyle* aStyle) { for (int i = 0; i < 9; i++) { if (aStyle->mWeights[i]) { for (int j = i + 1; j < 9; j++) { if (aStyle->mWeights[j] == aStyle->mWeights[i]) { aStyle->mWeights[j] = nsnull; } } FreeWeight(aStyle->mWeights[i]); } } delete aStyle; } PRBool FreeNode(nsHashKey* aKey, void* aData, void* aClosure) { nsFontNode* node = (nsFontNode*) aData; for (int i = 0; i < 3; i++) { if (node->mStyles[i]) { for (int j = i + 1; j < 3; j++) { if (node->mStyles[j] == node->mStyles[i]) { node->mStyles[j] = nsnull; } } FreeStyle(node->mStyles[i]); } } delete node; return PR_TRUE; } static PRBool FreeNodeArray(nsHashKey* aKey, void* aData, void* aClosure) { nsFontNodeArray* nodes = (nsFontNodeArray*) aData; delete nodes; return PR_TRUE; } static void FreeGlobals(void) { // XXX complete this gInitialized = 0; #ifdef MOZ_ENABLE_FREETYPE2 nsFT2FontNode::FreeGlobals(); #endif #ifdef ENABLE_X_FONT_BANNING if (gFontRejectRegEx) { regfree(gFontRejectRegEx); delete gFontRejectRegEx; gFontRejectRegEx = nsnull; } if (gFontAcceptRegEx) { regfree(gFontAcceptRegEx); delete gFontAcceptRegEx; gFontAcceptRegEx = nsnull; } #endif /* ENABLE_X_FONT_BANNING */ nsXFontAAScaledBitmap::FreeGlobals(); nsX11AlphaBlendFreeGlobals(); if (gAliases) { delete gAliases; gAliases = nsnull; } NS_IF_RELEASE(gCharSetManager); if (gCharSetMaps) { gCharSetMaps->Reset(FreeCharSetMap, nsnull); delete gCharSetMaps; gCharSetMaps = nsnull; } if (gFamilies) { gFamilies->Reset(FreeFamily, nsnull); delete gFamilies; gFamilies = nsnull; } if (gGlobalList) { delete gGlobalList; gGlobalList = nsnull; } if (gCachedFFRESearches) { gCachedFFRESearches->Reset(FreeNodeArray, nsnull); delete gCachedFFRESearches; gCachedFFRESearches = nsnull; } if (gFFRENodes) { gFFRENodes->Reset(FreeNode, nsnull); delete gFFRENodes; gFFRENodes = nsnull; } if (gAFRENodes) { gAFRENodes->Reset(FreeNode, nsnull); delete gAFRENodes; gAFRENodes = nsnull; } NS_IF_RELEASE(gPref); if (gSpecialCharSets) { gSpecialCharSets->Reset(FreeCharSetMap, nsnull); delete gSpecialCharSets; gSpecialCharSets = nsnull; } if (gStretches) { delete gStretches; gStretches = nsnull; } NS_IF_RELEASE(gUnicode); NS_IF_RELEASE(gUserDefined); NS_IF_RELEASE(gZHTW); NS_IF_RELEASE(gZHHK); NS_IF_RELEASE(gZHTWHK); NS_IF_RELEASE(gUserDefinedConverter); NS_IF_RELEASE(gUsersLocale); NS_IF_RELEASE(gWesternLocale); NS_IF_RELEASE(gFontSubConverter); if (gWeights) { delete gWeights; gWeights = nsnull; } nsFontCharSetMap* charSetMap; for (charSetMap=gCharSetMap; charSetMap->mFontLangGroup; charSetMap++) { NS_IF_RELEASE(charSetMap->mFontLangGroup->mFontLangGroupAtom); charSetMap->mFontLangGroup->mFontLangGroupAtom = nsnull; } FreeCCMap(gUserDefinedCCMap); FreeCCMap(gEmptyCCMap); } /* * Initialize all the font lookup hash tables and other globals */ static nsresult InitGlobals(nsIDeviceContext *aDevice) { #ifdef NS_FONT_DEBUG /* First check gfx/src/gtk/-specific env var "NS_FONT_DEBUG_GTK", * then the more general "NS_FONT_DEBUG" if "NS_FONT_DEBUG_GTK" * is not present */ const char *debug = PR_GetEnv("NS_FONT_DEBUG_GTK"); if (!debug) { debug = PR_GetEnv("NS_FONT_DEBUG"); } if (debug) { PR_sscanf(debug, "%lX", &gFontDebug); } #endif /* NS_FONT_DEBUG */ NS_ENSURE_TRUE(nsnull != aDevice, NS_ERROR_NULL_POINTER); aDevice->GetCanonicalPixelScale(gDevScale); CallGetService(kCharSetManagerCID, &gCharSetManager); if (!gCharSetManager) { FreeGlobals(); return NS_ERROR_FAILURE; } CallGetService(kPrefCID, &gPref); if (!gPref) { FreeGlobals(); return NS_ERROR_FAILURE; } nsCompressedCharMap empty_ccmapObj; gEmptyCCMap = empty_ccmapObj.NewCCMap(); if (!gEmptyCCMap) return NS_ERROR_OUT_OF_MEMORY; // get the "disable double byte font special chars" setting PRBool val = PR_TRUE; nsresult rv = gPref->GetBoolPref("font.allow_double_byte_special_chars", &val); if (NS_SUCCEEDED(rv)) gAllowDoubleByteSpecialChars = val; PRInt32 scale_minimum = 0; rv = gPref->GetIntPref("font.scale.outline.min", &scale_minimum); if (NS_SUCCEEDED(rv)) { gOutlineScaleMinimum = scale_minimum; SIZE_FONT_PRINTF(("gOutlineScaleMinimum = %d", gOutlineScaleMinimum)); } val = PR_TRUE; rv = gPref->GetBoolPref("font.scale.aa_bitmap.enable", &val); if (NS_SUCCEEDED(rv)) { gAABitmapScaleEnabled = val; SIZE_FONT_PRINTF(("gAABitmapScaleEnabled = %d", gAABitmapScaleEnabled)); } val = PR_FALSE; rv = gPref->GetBoolPref("font.scale.aa_bitmap.always", &val); if (NS_SUCCEEDED(rv)) { gAABitmapScaleAlways = val; SIZE_FONT_PRINTF(("gAABitmapScaleAlways = %d", gAABitmapScaleAlways)); } rv = gPref->GetIntPref("font.scale.aa_bitmap.min", &scale_minimum); if (NS_SUCCEEDED(rv)) { gAABitmapScaleMinimum = scale_minimum; SIZE_FONT_PRINTF(("gAABitmapScaleMinimum = %d", gAABitmapScaleMinimum)); } PRInt32 percent = 0; rv = gPref->GetIntPref("font.scale.aa_bitmap.undersize", &percent); if ((NS_SUCCEEDED(rv)) && (percent)) { gAABitmapUndersize = percent/100.0; SIZE_FONT_PRINTF(("gAABitmapUndersize = %g", gAABitmapUndersize)); } percent = 0; rv = gPref->GetIntPref("font.scale.aa_bitmap.oversize", &percent); if ((NS_SUCCEEDED(rv)) && (percent)) { gAABitmapOversize = percent/100.0; SIZE_FONT_PRINTF(("gAABitmapOversize = %g", gAABitmapOversize)); } PRInt32 int_val = 0; rv = gPref->GetIntPref("font.scale.aa_bitmap.dark_text.min", &int_val); if (NS_SUCCEEDED(rv)) { gAASBDarkTextMinValue = int_val; SIZE_FONT_PRINTF(("gAASBDarkTextMinValue = %d", gAASBDarkTextMinValue)); } nsXPIDLCString str; rv = gPref->GetCharPref("font.scale.aa_bitmap.dark_text.gain", getter_Copies(str)); if (NS_SUCCEEDED(rv)) { gAASBDarkTextGain = atof(str.get()); SIZE_FONT_PRINTF(("gAASBDarkTextGain = %g", gAASBDarkTextGain)); } int_val = 0; rv = gPref->GetIntPref("font.scale.aa_bitmap.light_text.min", &int_val); if (NS_SUCCEEDED(rv)) { gAASBLightTextMinValue = int_val; SIZE_FONT_PRINTF(("gAASBLightTextMinValue = %d", gAASBLightTextMinValue)); } rv = gPref->GetCharPref("font.scale.aa_bitmap.light_text.gain", getter_Copies(str)); if (NS_SUCCEEDED(rv)) { gAASBLightTextGain = atof(str.get()); SIZE_FONT_PRINTF(("gAASBLightTextGain = %g", gAASBLightTextGain)); } rv = gPref->GetIntPref("font.scale.bitmap.min", &scale_minimum); if (NS_SUCCEEDED(rv)) { gBitmapScaleMinimum = scale_minimum; SIZE_FONT_PRINTF(("gBitmapScaleMinimum = %d", gBitmapScaleMinimum)); } percent = 0; gPref->GetIntPref("font.scale.bitmap.oversize", &percent); if (percent) { gBitmapOversize = percent/100.0; SIZE_FONT_PRINTF(("gBitmapOversize = %g", gBitmapOversize)); } percent = 0; gPref->GetIntPref("font.scale.bitmap.undersize", &percent); if (percent) { gBitmapUndersize = percent/100.0; SIZE_FONT_PRINTF(("gBitmapUndersize = %g", gBitmapUndersize)); } PRBool force_outline_scaled_fonts = gForceOutlineScaledFonts; rv = gPref->GetBoolPref("font.x11.force_outline_scaled_fonts", &force_outline_scaled_fonts); if (NS_SUCCEEDED(rv)) { gForceOutlineScaledFonts = force_outline_scaled_fonts; } PRBool scale_bitmap_fonts_with_devscale = gScaleBitmapFontsWithDevScale; rv = gPref->GetBoolPref("font.x11.scale_bitmap_fonts_with_devscale", &scale_bitmap_fonts_with_devscale); if (NS_SUCCEEDED(rv)) { gScaleBitmapFontsWithDevScale = scale_bitmap_fonts_with_devscale; } gFFRENodes = new nsHashtable(); if (!gFFRENodes) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } gAFRENodes = new nsHashtable(); if (!gAFRENodes) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } gCachedFFRESearches = new nsHashtable(); if (!gCachedFFRESearches) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } gFamilies = new nsHashtable(); if (!gFamilies) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } gAliases = new nsHashtable(); if (!gAliases) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } nsFontFamilyName* f = gFamilyNameTable; while (f->mName) { nsCStringKey key(f->mName); gAliases->Put(&key, (void *)f->mXName); f++; } gWeights = new nsHashtable(); if (!gWeights) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } nsFontPropertyName* p = gWeightNames; while (p->mName) { nsCStringKey key(p->mName); gWeights->Put(&key, (void*) p->mValue); p++; } gStretches = new nsHashtable(); if (!gStretches) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } p = gStretchNames; while (p->mName) { nsCStringKey key(p->mName); gStretches->Put(&key, (void*) p->mValue); p++; } gCharSetMaps = new nsHashtable(); if (!gCharSetMaps) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } nsFontCharSetMap* charSetMap = gCharSetMap; while (charSetMap->mName) { nsCStringKey key(charSetMap->mName); gCharSetMaps->Put(&key, charSetMap); charSetMap++; } gSpecialCharSets = new nsHashtable(); if (!gSpecialCharSets) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } nsFontCharSetMap* specialCharSetMap = gSpecialCharSetMap; while (specialCharSetMap->mName) { nsCStringKey key(specialCharSetMap->mName); gSpecialCharSets->Put(&key, specialCharSetMap); specialCharSetMap++; } gUnicode = NS_NewAtom("x-unicode"); if (!gUnicode) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } gUserDefined = NS_NewAtom(USER_DEFINED); if (!gUserDefined) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } gZHTW = NS_NewAtom("zh-TW"); if (!gZHTW) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } gZHHK = NS_NewAtom("zh-HK"); if (!gZHHK) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } gZHTWHK = NS_NewAtom("x-zh-TWHK"); if (!gZHTWHK) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } // the user's locale nsCOMPtr langService; langService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID); if (langService) { NS_IF_ADDREF(gUsersLocale = langService->GetLocaleLanguageGroup()); } if (!gUsersLocale) { gUsersLocale = NS_NewAtom("x-western"); } gWesternLocale = NS_NewAtom("x-western"); if (!gUsersLocale) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } rv = nsX11AlphaBlendInitGlobals(GDK_DISPLAY()); if (NS_FAILED(rv) || (!nsX11AlphaBlend::CanAntiAlias())) { gAABitmapScaleEnabled = PR_FALSE; } if (gAABitmapScaleEnabled) { gAABitmapScaleEnabled = nsXFontAAScaledBitmap::InitGlobals(GDK_DISPLAY(), DefaultScreen(GDK_DISPLAY())); } #ifdef ENABLE_X_FONT_BANNING /* get the font banning pattern */ nsXPIDLCString fbpattern; rv = gPref->GetCharPref("font.x11.rejectfontpattern", getter_Copies(fbpattern)); if (NS_SUCCEEDED(rv)) { gFontRejectRegEx = new regex_t; if (!gFontRejectRegEx) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } /* Compile the pattern - and return an error if we get an invalid pattern... */ if (regcomp(gFontRejectRegEx, fbpattern.get(), REG_EXTENDED|REG_NOSUB) != REG_OK) { PR_LOG(FontMetricsGTKLM, PR_LOG_DEBUG, ("Invalid rejectfontpattern '%s'\n", fbpattern.get())); BANNED_FONT_PRINTF(("Invalid font.x11.rejectfontpattern '%s'", fbpattern.get())); delete gFontRejectRegEx; gFontRejectRegEx = nsnull; FreeGlobals(); return NS_ERROR_INVALID_ARG; } } rv = gPref->GetCharPref("font.x11.acceptfontpattern", getter_Copies(fbpattern)); if (NS_SUCCEEDED(rv)) { gFontAcceptRegEx = new regex_t; if (!gFontAcceptRegEx) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } /* Compile the pattern - and return an error if we get an invalid pattern... */ if (regcomp(gFontAcceptRegEx, fbpattern.get(), REG_EXTENDED|REG_NOSUB) != REG_OK) { PR_LOG(FontMetricsGTKLM, PR_LOG_DEBUG, ("Invalid acceptfontpattern '%s'\n", fbpattern.get())); BANNED_FONT_PRINTF(("Invalid font.x11.acceptfontpattern '%s'", fbpattern.get())); delete gFontAcceptRegEx; gFontAcceptRegEx = nsnull; FreeGlobals(); return NS_ERROR_INVALID_ARG; } } #endif /* ENABLE_X_FONT_BANNING */ #ifdef MOZ_ENABLE_FREETYPE2 rv = nsFT2FontNode::InitGlobals(); if (NS_FAILED(rv)) { FreeGlobals(); return NS_ERROR_OUT_OF_MEMORY; } #endif gInitialized = 1; return NS_OK; } // do the 8 to 16 bit conversion on the stack // if the data is less than this size #define WIDEN_8_TO_16_BUF_SIZE 1024 // handle 8 bit data with a 16 bit font gint Widen8To16AndMove(const gchar *char_p, gint char_len, XChar2b *xchar2b_p) { int i; for (i=0; ibyte1 = 0; (xchar2b_p++)->byte2 = *char_p++; } return(char_len*2); } // handle 8 bit data with a 16 bit font gint Widen8To16AndGetWidth (nsXFont *xFont, const gchar *text, gint text_length) { NS_ASSERTION(!xFont->IsSingleByte(),"wrong string/font size"); XChar2b buf[WIDEN_8_TO_16_BUF_SIZE]; XChar2b *p = buf; int uchar_size; gint rawWidth; if (text_length > WIDEN_8_TO_16_BUF_SIZE) { p = (XChar2b*)PR_Malloc(text_length*sizeof(XChar2b)); if (!p) return(0); // handle malloc failure } uchar_size = Widen8To16AndMove(text, text_length, p); rawWidth = xFont->TextWidth16(p, uchar_size/2); if (text_length > WIDEN_8_TO_16_BUF_SIZE) { PR_Free((char*)p); } return(rawWidth); } void Widen8To16AndDraw (GdkDrawable *drawable, nsXFont *xFont, GdkGC *gc, gint x, gint y, const gchar *text, gint text_length) { NS_ASSERTION(!xFont->IsSingleByte(),"wrong string/font size"); XChar2b buf[WIDEN_8_TO_16_BUF_SIZE]; XChar2b *p = buf; int uchar_size; if (text_length > WIDEN_8_TO_16_BUF_SIZE) { p = (XChar2b*)PR_Malloc(text_length*sizeof(XChar2b)); if (!p) return; // handle malloc failure } uchar_size = Widen8To16AndMove(text, text_length, p); xFont->DrawText16(drawable, gc, x, y, p, uchar_size/2); if (text_length > WIDEN_8_TO_16_BUF_SIZE) { PR_Free((char*)p); } } #ifdef MOZ_MATHML void Widen8To16AndGetTextExtents (nsXFont *xFont, const gchar *text, gint text_length, gint *lbearing, gint *rbearing, gint *width, gint *ascent, gint *descent) { NS_ASSERTION(!xFont->IsSingleByte(),"wrong string/font size"); XChar2b buf[WIDEN_8_TO_16_BUF_SIZE]; XChar2b *p = buf; int uchar_size; if (text_length > WIDEN_8_TO_16_BUF_SIZE) { p = (XChar2b*)PR_Malloc(text_length*sizeof(XChar2b)); if (!p) { // handle malloc failure *lbearing = 0; *rbearing = 0; *width = 0; *ascent = 0; *descent = 0; return; } } uchar_size = Widen8To16AndMove(text, text_length, p); xFont->TextExtents16(p, uchar_size/2, lbearing, rbearing, width, ascent, descent); if (text_length > WIDEN_8_TO_16_BUF_SIZE) { PR_Free((char*)p); } } #endif /* MOZ_MATHML */ nsFontMetricsGTK::nsFontMetricsGTK() : mFonts() // I'm not sure what the common size is here - I generally // see 2-5 entries. For now, punt and let it be allocated later. We can't // make it an nsAutoVoidArray since it's a cString array. // XXX mFontIsGeneric will generally need to be the same size; right now // it's an nsAutoVoidArray. If the average is under 8, that's ok. { gFontMetricsGTKCount++; } nsFontMetricsGTK::~nsFontMetricsGTK() { // do not free mGeneric here if (mLoadedFonts) { PR_Free(mLoadedFonts); mLoadedFonts = nsnull; } if (mSubstituteFont) { delete mSubstituteFont; mSubstituteFont = nsnull; } mWesternFont = nsnull; mCurrentFont = nsnull; if (mDeviceContext) { // Notify our device context that owns us so that it can update its font cache mDeviceContext->FontMetricsDeleted(this); mDeviceContext = nsnull; } if (!--gFontMetricsGTKCount) { FreeGlobals(); } } NS_IMPL_ISUPPORTS1(nsFontMetricsGTK, nsIFontMetrics) static PRBool IsASCIIFontName(const nsString& aName) { PRUint32 len = aName.Length(); const PRUnichar* str = aName.get(); for (PRUint32 i = 0; i < len; i++) { /* * X font names are printable ASCII, ignore others (for now) */ if ((str[i] < 0x20) || (str[i] > 0x7E)) { return PR_FALSE; } } return PR_TRUE; } static PRBool FontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData) { #ifdef REALLY_NOISY_FONTS printf("font = '"); fputs(NS_LossyConvertUCS2toASCII(aFamily).get(), stdout); printf("'\n"); #endif if (!IsASCIIFontName(aFamily)) { return PR_TRUE; // skip and continue } nsCAutoString name; name.AssignWithConversion(aFamily.get()); ToLowerCase(name); nsFontMetricsGTK* metrics = (nsFontMetricsGTK*) aData; metrics->mFonts.AppendCString(name); metrics->mFontIsGeneric.AppendElement((void*) aGeneric); if (aGeneric) { metrics->mGeneric = metrics->mFonts.CStringAt(metrics->mFonts.Count() - 1); return PR_FALSE; // stop } return PR_TRUE; // continue } NS_IMETHODIMP nsFontMetricsGTK::Init(const nsFont& aFont, nsIAtom* aLangGroup, nsIDeviceContext* aContext) { NS_ASSERTION(!(nsnull == aContext), "attempt to init fontmetrics with null device context"); nsresult res = NS_OK; mDocConverterType = nsnull; if (!gInitialized) { res = InitGlobals(aContext); if (NS_FAILED(res)) return res; } mFont = aFont; mLangGroup = aLangGroup; mDeviceContext = aContext; float app2dev; app2dev = mDeviceContext->AppUnitsToDevUnits(); mPixelSize = NSToIntRound(app2dev * mFont.size); // Make sure to clamp the pixel size to something reasonable so we // don't make the X server blow up. mPixelSize = PR_MIN(gdk_screen_height() * FONT_MAX_FONT_SCALE, mPixelSize); mStretchIndex = 4; // normal mStyleIndex = mFont.style; mFont.EnumerateFamilies(FontEnumCallback, this); nsXPIDLCString value; const char* langGroup; mLangGroup->GetUTF8String(&langGroup); if (!mGeneric) { nsCAutoString name("font.default."); name.Append(langGroup); gPref->CopyCharPref(name.get(), getter_Copies(value)); if (value.get()) { mDefaultFont = value.get(); } else { mDefaultFont = "serif"; } mGeneric = &mDefaultFont; } if (mLangGroup) { nsCAutoString name("font.min-size."); if (mGeneric->Equals("monospace")) { name.Append("fixed"); } else { name.Append("variable"); } name.Append(char('.')); name.Append(langGroup); PRInt32 minimum = 0; res = gPref->GetIntPref(name.get(), &minimum); if (NS_FAILED(res)) { gPref->GetDefaultIntPref(name.get(), &minimum); } if (minimum < 0) { minimum = 0; } if (mPixelSize < minimum) { mPixelSize = minimum; } } if (mLangGroup.get() == gUserDefined) { if (!gUserDefinedConverter) { res = gCharSetManager->GetUnicodeEncoderRaw("x-user-defined", &gUserDefinedConverter); if (NS_FAILED(res)) { return res; } res = gUserDefinedConverter->SetOutputErrorBehavior( gUserDefinedConverter->kOnError_Replace, nsnull, '?'); nsCOMPtr mapper = do_QueryInterface(gUserDefinedConverter); if (mapper) { gUserDefinedCCMap = MapperToCCMap(mapper); if (!gUserDefinedCCMap) return NS_ERROR_OUT_OF_MEMORY; } } nsCAutoString name("font.name."); name.Append(*mGeneric); name.Append(char('.')); name.Append(USER_DEFINED); gPref->CopyCharPref(name.get(), getter_Copies(value)); if (value.get()) { mUserDefined = value.get(); mIsUserDefined = 1; } } mWesternFont = FindFont('a'); if (!mWesternFont) { return NS_ERROR_FAILURE; } mCurrentFont = mWesternFont; RealizeFont(); return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::Destroy() { mDeviceContext = nsnull; return NS_OK; } void nsFontMetricsGTK::RealizeFont() { float f; f = mDeviceContext->DevUnitsToAppUnits(); #ifdef MOZ_ENABLE_FREETYPE2 if (mWesternFont->IsFreeTypeFont()) { nsFreeTypeFont *ft = (nsFreeTypeFont *)mWesternFont; if (!ft) return; // now that there are multiple font types (eg: core X fonts // and TrueType fonts) there should be a common set of methods // to get the metrics info from the font object. These methods // probably should be virtual functions defined in nsFontGTK. int lineSpacing = ft->ascent() + ft->descent(); if (lineSpacing > mWesternFont->mSize) { mLeading = nscoord((lineSpacing - mWesternFont->mSize) * f); } else { mLeading = 0; } mEmHeight = PR_MAX(1, nscoord(mWesternFont->mSize * f)); mEmAscent = nscoord(ft->ascent() * mWesternFont->mSize * f / lineSpacing); mEmDescent = mEmHeight - mEmAscent; mMaxHeight = nscoord((ft->max_ascent() + ft->max_descent()) * f); mMaxAscent = nscoord(ft->max_ascent() * f) ; mMaxDescent = nscoord(ft->max_descent() * f); mMaxAdvance = nscoord(ft->max_width() * f); // X may screw up if we try to measure/draw more than 32767 pixels in // one operation mMaxStringLength = (PRInt32)floor(32767.0/ft->max_width()); mMaxStringLength = PR_MAX(1, mMaxStringLength); // 56% of ascent, best guess for non-true type mXHeight = NSToCoordRound((float) ft->ascent()* f * 0.56f); PRUnichar space = (PRUnichar)' '; mSpaceWidth = NSToCoordRound(ft->GetWidth(&space, 1) * f); PRUnichar averageX = (PRUnichar)'x'; mAveCharWidth = NSToCoordRound(ft->GetWidth(&averageX, 1) * f); unsigned long pr = 0; if (ft->getXHeight(pr)) { mXHeight = nscoord(pr * f); } float height; long val; if (ft->underlinePosition(val)) { /* this will only be provided from adobe .afm fonts and TrueType * fonts served by xfsft (not xfstt!) */ mUnderlineOffset = -NSToIntRound(val * f); } else { height = ft->ascent() + ft->descent(); mUnderlineOffset = -NSToIntRound(MAX (1, floor (0.1 * height + 0.5)) * f); } if (ft->underline_thickness(pr)) { /* this will only be provided from adobe .afm fonts */ mUnderlineSize = nscoord(MAX(f, NSToIntRound(pr * f))); } else { height = ft->ascent() + ft->descent(); mUnderlineSize = NSToIntRound(MAX(1, floor (0.05 * height + 0.5)) * f); } if (ft->superscript_y(val)) { mSuperscriptOffset = nscoord(MAX(f, NSToIntRound(val * f))); } else { mSuperscriptOffset = mXHeight; } if (ft->subscript_y(val)) { mSubscriptOffset = nscoord(MAX(f, NSToIntRound(val * f))); } else { mSubscriptOffset = mXHeight; } /* need better way to calculate this */ mStrikeoutOffset = NSToCoordRound(mXHeight / 2.0); mStrikeoutSize = mUnderlineSize; return; } #endif nsXFont *xFont = mWesternFont->GetXFont(); XFontStruct *fontInfo = xFont->GetXFontStruct(); f = mDeviceContext->DevUnitsToAppUnits(); nscoord lineSpacing = nscoord((fontInfo->ascent + fontInfo->descent) * f); mEmHeight = PR_MAX(1, nscoord(mWesternFont->mSize * f)); if (lineSpacing > mEmHeight) { mLeading = lineSpacing - mEmHeight; } else { mLeading = 0; } mMaxHeight = nscoord((fontInfo->ascent + fontInfo->descent) * f); mMaxAscent = nscoord(fontInfo->ascent * f); mMaxDescent = nscoord(fontInfo->descent * f); if (lineSpacing == 0) { mEmAscent = mEmHeight; } else { mEmAscent = nscoord(mMaxAscent * mEmHeight / lineSpacing); } mEmDescent = mEmHeight - mEmAscent; mMaxAdvance = nscoord(fontInfo->max_bounds.width * f); // X may screw up if we try to measure/draw more than 32767 pixels in // one operation. mMaxStringLength = (PRInt32)floor(32767.0/fontInfo->max_bounds.width); mMaxStringLength = PR_MAX(1, mMaxStringLength); gint rawWidth, rawAverage; if ((fontInfo->min_byte1 == 0) && (fontInfo->max_byte1 == 0)) { rawWidth = xFont->TextWidth8(" ", 1); rawAverage = xFont->TextWidth8("x", 1); } else { XChar2b _16bit_space, _16bit_x; _16bit_space.byte1 = 0; _16bit_space.byte2 = ' '; _16bit_x.byte1 = 0; _16bit_x.byte2 = 'x'; rawWidth = xFont->TextWidth16(&_16bit_space, sizeof(_16bit_space)/2); rawAverage = xFont->TextWidth16(&_16bit_x, sizeof( _16bit_x)/2); } mSpaceWidth = NSToCoordRound(rawWidth * f); mAveCharWidth = NSToCoordRound(rawAverage * f); unsigned long pr = 0; if (xFont->GetXFontProperty(XA_X_HEIGHT, &pr) && pr != 0 && pr < 0x00ffffff) // Bug 43214: arbitrary to exclude garbage values { mXHeight = nscoord(pr * f); #ifdef REALLY_NOISY_FONTS printf("xHeight=%d\n", mXHeight); #endif } else { // 56% of ascent, best guess for non-true type mXHeight = NSToCoordRound((float) fontInfo->ascent* f * 0.56f); } if (xFont->GetXFontProperty(XA_UNDERLINE_POSITION, &pr)) { /* this will only be provided from adobe .afm fonts and TrueType * fonts served by xfsft (not xfstt!) */ mUnderlineOffset = -NSToIntRound(pr * f); #ifdef REALLY_NOISY_FONTS printf("underlineOffset=%d\n", mUnderlineOffset); #endif } else { /* this may need to be different than one for those weird asian fonts */ float height; height = fontInfo->ascent + fontInfo->descent; mUnderlineOffset = -NSToIntRound(MAX (1, floor (0.1 * height + 0.5)) * f); } if (xFont->GetXFontProperty(XA_UNDERLINE_THICKNESS, &pr)) { /* this will only be provided from adobe .afm fonts */ mUnderlineSize = nscoord(MAX(f, NSToIntRound(pr * f))); #ifdef REALLY_NOISY_FONTS printf("underlineSize=%d\n", mUnderlineSize); #endif } else { float height; height = fontInfo->ascent + fontInfo->descent; mUnderlineSize = NSToIntRound(MAX(1, floor (0.05 * height + 0.5)) * f); } if (xFont->GetXFontProperty(XA_SUPERSCRIPT_Y, &pr)) { mSuperscriptOffset = nscoord(MAX(f, NSToIntRound(pr * f))); #ifdef REALLY_NOISY_FONTS printf("superscriptOffset=%d\n", mSuperscriptOffset); #endif } else { mSuperscriptOffset = mXHeight; } if (xFont->GetXFontProperty(XA_SUBSCRIPT_Y, &pr)) { mSubscriptOffset = nscoord(MAX(f, NSToIntRound(pr * f))); #ifdef REALLY_NOISY_FONTS printf("subscriptOffset=%d\n", mSubscriptOffset); #endif } else { mSubscriptOffset = mXHeight; } /* need better way to calculate this */ mStrikeoutOffset = NSToCoordRound(mXHeight / 2.0); mStrikeoutSize = mUnderlineSize; } NS_IMETHODIMP nsFontMetricsGTK::GetXHeight(nscoord& aResult) { aResult = mXHeight; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetSuperscriptOffset(nscoord& aResult) { aResult = mSuperscriptOffset; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetSubscriptOffset(nscoord& aResult) { aResult = mSubscriptOffset; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetStrikeout(nscoord& aOffset, nscoord& aSize) { aOffset = mStrikeoutOffset; aSize = mStrikeoutSize; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetUnderline(nscoord& aOffset, nscoord& aSize) { aOffset = mUnderlineOffset; aSize = mUnderlineSize; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetHeight(nscoord &aHeight) { aHeight = mMaxHeight; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetNormalLineHeight(nscoord &aHeight) { aHeight = mEmHeight + mLeading; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetLeading(nscoord &aLeading) { aLeading = mLeading; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetEmHeight(nscoord &aHeight) { aHeight = mEmHeight; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetEmAscent(nscoord &aAscent) { aAscent = mEmAscent; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetEmDescent(nscoord &aDescent) { aDescent = mEmDescent; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetMaxHeight(nscoord &aHeight) { aHeight = mMaxHeight; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetMaxAscent(nscoord &aAscent) { aAscent = mMaxAscent; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetMaxDescent(nscoord &aDescent) { aDescent = mMaxDescent; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetMaxAdvance(nscoord &aAdvance) { aAdvance = mMaxAdvance; return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetAveCharWidth(nscoord &aAveCharWidth) { aAveCharWidth = mAveCharWidth; return NS_OK; } PRInt32 nsFontMetricsGTK::GetMaxStringLength() { return mMaxStringLength; } NS_IMETHODIMP nsFontMetricsGTK::GetLangGroup(nsIAtom** aLangGroup) { if (!aLangGroup) { return NS_ERROR_NULL_POINTER; } *aLangGroup = mLangGroup; NS_IF_ADDREF(*aLangGroup); return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetFontHandle(nsFontHandle &aHandle) { return NS_ERROR_NOT_IMPLEMENTED; } nsFontGTK* nsFontMetricsGTK::LocateFont(PRUint32 aChar, PRInt32 & aCount) { nsFontGTK *font; PRInt32 i; // see if one of our loaded fonts can represent the character for (i = 0; i < aCount; ++i) { font = (nsFontGTK*)mLoadedFonts[i]; if (CCMAP_HAS_CHAR_EXT(font->mCCMap, aChar)) return font; } font = FindFont(aChar); aCount = mLoadedFontsCount; // update since FindFont() can change it return font; } nsresult nsFontMetricsGTK::ResolveForwards(const PRUnichar *aString, PRUint32 aLength, nsFontSwitchCallbackGTK aFunc, void *aData) { NS_ASSERTION(aString || !aLength, "invalid call"); const PRUnichar* firstChar = aString; const PRUnichar* currChar = firstChar; const PRUnichar* lastChar = aString + aLength; nsFontGTK* currFont; nsFontGTK* nextFont; PRInt32 count; nsFontSwitchGTK fontSwitch; if (firstChar == lastChar) return NS_OK; count = mLoadedFontsCount; if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) { currFont = LocateFont(SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count); currChar += 2; } else { currFont = LocateFont(*currChar, count); ++currChar; } //This if block is meant to speedup the process in normal situation, when //most characters can be found in first font if (currFont == mLoadedFonts[0]) { while (currChar < lastChar && CCMAP_HAS_CHAR_EXT(currFont->mCCMap,*currChar)) ++currChar; fontSwitch.mFontGTK = currFont; if (!(*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData)) return NS_OK; if (currChar == lastChar) return NS_OK; // continue with the next substring, re-using the available loaded fonts firstChar = currChar; if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) { currFont = LocateFont(SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count); currChar += 2; } else { currFont = LocateFont(*currChar, count); ++currChar; } } // see if we can keep the same font for adjacent characters PRInt32 lastCharLen; while (currChar < lastChar) { if (IS_HIGH_SURROGATE(*currChar) && (currChar+1) < lastChar && IS_LOW_SURROGATE(*(currChar+1))) { nextFont = LocateFont(SURROGATE_TO_UCS4(*currChar, *(currChar+1)), count); lastCharLen = 2; } else { nextFont = LocateFont(*currChar, count); lastCharLen = 1; } if (nextFont != currFont) { // We have a substring that can be represented with the same font, and // we are about to switch fonts, it is time to notify our caller. fontSwitch.mFontGTK = currFont; if (!(*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData)) return NS_OK; // continue with the next substring, re-using the available loaded fonts firstChar = currChar; currFont = nextFont; // use the font found earlier for the char } currChar += lastCharLen; } //do it for last part of the string fontSwitch.mFontGTK = currFont; (*aFunc)(&fontSwitch, firstChar, currChar - firstChar, aData); return NS_OK; } NS_IMETHODIMP nsFontMetricsGTK::GetSpaceWidth(nscoord &aSpaceWidth) { aSpaceWidth = mSpaceWidth; return NS_OK; } /* * CSS2 "font properties": * font-family * font-style * font-variant * font-weight * font-stretch * font-size * font-size-adjust * font */ /* * CSS2 "font descriptors": * font-family * font-style * font-variant * font-weight * font-stretch * font-size * unicode-range * units-per-em * src * panose-1 * stemv * stemh * slope * cap-height * x-height * ascent * descent * widths * bbox * definition-src * baseline * centerline * mathline * topline */ /* * XLFD 1.5 "FontName fields": * FOUNDRY * FAMILY_NAME * WEIGHT_NAME * SLANT * SETWIDTH_NAME * ADD_STYLE_NAME * PIXEL_SIZE * POINT_SIZE * RESOLUTION_X * RESOLUTION_Y * SPACING * AVERAGE_WIDTH * CHARSET_REGISTRY * CHARSET_ENCODING * XLFD example: * -adobe-times-medium-r-normal--17-120-100-100-p-84-iso8859-1 */ /* * XLFD 1.5 "font properties": * FOUNDRY * FAMILY_NAME * WEIGHT_NAME * SLANT * SETWIDTH_NAME * ADD_STYLE_NAME * PIXEL_SIZE * POINT_SIZE * RESOLUTION_X * RESOLUTION_Y * SPACING * AVERAGE_WIDTH * CHARSET_REGISTRY * CHARSET_ENCODING * MIN_SPACE * NORM_SPACE * MAX_SPACE * END_SPACE * AVG_CAPITAL_WIDTH * AVG_LOWERCASE_WIDTH * QUAD_WIDTH * FIGURE_WIDTH * SUPERSCRIPT_X * SUPERSCRIPT_Y * SUBSCRIPT_X * SUBSCRIPT_Y * SUPERSCRIPT_SIZE * SUBSCRIPT_SIZE * SMALL_CAP_SIZE * UNDERLINE_POSITION * UNDERLINE_THICKNESS * STRIKEOUT_ASCENT * STRIKEOUT_DESCENT * ITALIC_ANGLE * CAP_HEIGHT * X_HEIGHT * RELATIVE_SETWIDTH * RELATIVE_WEIGHT * WEIGHT * RESOLUTION * FONT * FACE_NAME * FULL_NAME * COPYRIGHT * NOTICE * DESTINATION * FONT_TYPE * FONT_VERSION * RASTERIZER_NAME * RASTERIZER_VERSION * RAW_ASCENT * RAW_DESCENT * RAW_* * AXIS_NAMES * AXIS_LIMITS * AXIS_TYPES */ /* * XLFD 1.5 BDF 2.1 properties: * FONT_ASCENT * FONT_DESCENT * DEFAULT_CHAR */ /* * CSS2 algorithm, in the following order: * font-family: FAMILY_NAME (and FOUNDRY? (XXX)) * font-style: SLANT (XXX: XLFD's RI and RO) * font-variant: implemented in mozilla/layout/html/base/src/nsTextFrame.cpp * font-weight: RELATIVE_WEIGHT (XXX), WEIGHT (XXX), WEIGHT_NAME * font-size: XFontStruct.max_bounds.ascent + descent * * The following property is not specified in the algorithm spec. It will be * inserted between the font-weight and font-size steps for now: * font-stretch: RELATIVE_SETWIDTH (XXX), SETWIDTH_NAME */ /* * XXX: Things to investigate in the future: * ADD_STYLE_NAME font-family's serif and sans-serif * SPACING font-family's monospace; however, there are very few * proportional fonts in non-Latin-1 charsets, so beware in * font prefs dialog * AVERAGE_WIDTH none (see SETWIDTH_NAME) */ static gint SingleByteConvert(nsFontCharSetInfo* aSelf, XFontStruct* aFont, const PRUnichar* aSrcBuf, PRInt32 aSrcLen, char* aDestBuf, PRInt32 aDestLen) { gint count = 0; if (aSelf->mConverter) { aSelf->mConverter->Convert(aSrcBuf, &aSrcLen, aDestBuf, &aDestLen); count = aDestLen; } return count; } /* static void ReverseBuffer(char* aBuf, gint count) { char *head, *tail, *med; head = aBuf; tail = &aBuf[count-1]; med = &aBuf[count/2]; while(head < med) { char tmp = *head; *head++ = *tail; *tail-- = tmp; } } */ // the following code assume all the PRUnichar is draw in the same // direction- left to right, without mixing with characters which should // draw from right to left. This mean it should not be used untill the // upper level code resolve bi-di and ensure this assumption. otherwise // it may break non-bidi pages on a system which have hebrew/arabic fonts /* static gint SingleByteConvertReverse(nsFontCharSetInfo* aSelf, const PRUnichar* aSrcBuf, PRInt32 aSrcLen, char* aDestBuf, PRInt32 aDestLen) { gint count = SingleByteConvert(aSelf, aSrcBuf, aSrcLen, aDestBuf, aDestLen); ReverseBuffer(aDestBuf, count); return count; } */ static gint DoubleByteConvert(nsFontCharSetInfo* aSelf, XFontStruct* aFont, const PRUnichar* aSrcBuf, PRInt32 aSrcLen, char* aDestBuf, PRInt32 aDestLen) { gint count; if (aSelf->mConverter) { aSelf->mConverter->Convert(aSrcBuf, &aSrcLen, aDestBuf, &aDestLen); count = aDestLen; if (count > 0) { if ((aDestBuf[0] & 0x80) && (!(aFont->max_byte1 & 0x80))) { for (PRInt32 i = 0; i < aDestLen; i++) { aDestBuf[i] &= 0x7F; } } // We're using a GL encoder (KSC5601 or GB2312) but the font is a GR font. // (ksc5601.1987-1 or gb2312.1980-1) else if ((!(aDestBuf[0] & 0x80)) && (aFont->min_byte1 & 0x80)) { for (PRInt32 i = 0; i < aDestLen; i++) { aDestBuf[i] |= 0x80; } } } } else { count = 0; } return count; } static gint ISO10646Convert(nsFontCharSetInfo* aSelf, XFontStruct* aFont, const PRUnichar* aSrcBuf, PRInt32 aSrcLen, char* aDestBuf, PRInt32 aDestLen) { aDestLen /= 2; if (aSrcLen > aDestLen) { aSrcLen = aDestLen; } if (aSrcLen < 0) { aSrcLen = 0; } XChar2b* dest = (XChar2b*) aDestBuf; for (PRInt32 i = 0; i < aSrcLen; i++) { dest[i].byte1 = (aSrcBuf[i] >> 8); dest[i].byte2 = (aSrcBuf[i] & 0xFF); } return (gint) aSrcLen * 2; } #ifdef DEBUG static void CheckMap(nsFontCharSetMap* aEntry) { while (aEntry->mName) { if (aEntry->mInfo->mCharSet) { // used to use NS_NewAtom?? nsresult res; nsCOMPtr converter; res = gCharSetManager->GetUnicodeEncoderRaw(aEntry->mInfo->mCharSet, getter_AddRefs(converter)); if (NS_FAILED(res)) { printf("=== %s failed (%s)\n", aEntry->mInfo->mCharSet, __FILE__); } } aEntry++; } } static void CheckSelf(void) { CheckMap(gCharSetMap); #ifdef MOZ_MATHML // For this to pass, the ucvmath module must be built as well CheckMap(gSpecialCharSetMap); #endif } #endif /* DEBUG */ static PRBool SetUpFontCharSetInfo(nsFontCharSetInfo* aSelf) { #ifdef DEBUG static int checkedSelf = 0; if (!checkedSelf) { CheckSelf(); checkedSelf = 1; } #endif nsresult res; // used NS_NewAtom before?? nsIUnicodeEncoder* converter = nsnull; res = gCharSetManager->GetUnicodeEncoderRaw(aSelf->mCharSet, &converter); if (NS_SUCCEEDED(res)) { aSelf->mConverter = converter; res = converter->SetOutputErrorBehavior(converter->kOnError_Replace, nsnull, '?'); nsCOMPtr mapper = do_QueryInterface(converter); if (mapper) { aSelf->mCCMap = MapperToCCMap(mapper); if (aSelf->mCCMap) { #ifdef DEBUG_bzbarsky NS_WARNING(nsPrintfCString("\n\ncharset = %s", aSelf->mCharSet).get()); #endif /* DEBUG */ /* * We used to disable special characters like smart quotes * in CJK fonts because if they are quite a bit larger than * western glyphs and we did not want glyph fill-in to use them * in single byte documents. * * Now, single byte documents find these special chars before * the CJK fonts are searched so this is no longer needed * but is useful when trying to determine which font(s) the * special chars are found in. */ if ((aSelf->Convert == DoubleByteConvert) && (!gAllowDoubleByteSpecialChars)) { PRUint16* ccmap = aSelf->mCCMap; PRUint32 page = CCMAP_BEGIN_AT_START_OF_MAP; const PRUint16* specialmap = gDoubleByteSpecialCharsCCMap; while (NextNonEmptyCCMapPage(specialmap, &page)) { PRUint32 pagechar = page; for (int i=0; i < CCMAP_BITS_PER_PAGE; i++) { if (CCMAP_HAS_CHAR(specialmap, pagechar)) CCMAP_UNSET_CHAR(ccmap, pagechar); pagechar++; } } } return PR_TRUE; } } else { NS_WARNING("cannot get nsICharRepresentable"); } } else { NS_WARNING("cannot get Unicode converter"); } // // always try to return a map even if it is empty // nsCompressedCharMap empty_ccmapObj; aSelf->mCCMap = empty_ccmapObj.NewCCMap(); // return false if unable to alloc a map if (aSelf->mCCMap == nsnull) return PR_FALSE; return PR_TRUE; } #undef DEBUG_DUMP_TREE #ifdef DEBUG_DUMP_TREE static char* gDumpStyles[3] = { "normal", "italic", "oblique" }; static PRIntn DumpCharSet(PLHashEntry* he, PRIntn i, void* arg) { printf(" %s\n", (char*) he->key); nsFontCharSet* charSet = (nsFontCharSet*) he->value; for (int sizeIndex = 0; sizeIndex < charSet->mSizesCount; sizeIndex++) { nsFontGTK* size = &charSet->mSizes[sizeIndex]; printf(" %d %s\n", size->mSize, size->mName); } return HT_ENUMERATE_NEXT; } static void DumpFamily(nsFontFamily* aFamily) { for (int styleIndex = 0; styleIndex < 3; styleIndex++) { nsFontStyle* style = aFamily->mStyles[styleIndex]; if (style) { printf(" style: %s\n", gDumpStyles[styleIndex]); for (int weightIndex = 0; weightIndex < 8; weightIndex++) { nsFontWeight* weight = style->mWeights[weightIndex]; if (weight) { printf(" weight: %d\n", (weightIndex + 1) * 100); for (int stretchIndex = 0; stretchIndex < 9; stretchIndex++) { nsFontStretch* stretch = weight->mStretches[stretchIndex]; if (stretch) { printf(" stretch: %d\n", stretchIndex + 1); PL_HashTableEnumerateEntries(stretch->mCharSets, DumpCharSet, nsnull); } } } } } } } // this existing debug code was broken and I have partly fixed it static PRBool DumpFamilyEnum(nsHashKey* hashKey, void *aData, void* closure) { printf("family: %s\n", NS_LossyConvertUCS2toASCII(*NS_STATIC_CAST(nsString*,he->key))); nsFontFamily* family = (nsFontFamily*) he->value; DumpFamily(family); return HT_ENUMERATE_NEXT; } static void DumpTree(void) { gFamilies->Enumerate(DumpFamilyEnum, nsnull); } #endif /* DEBUG_DUMP_TREE */ struct nsFontSearch { nsFontMetricsGTK* mMetrics; PRUint32 mChar; nsFontGTK* mFont; }; #if 0 static void GetUnderlineInfo(nsXFont* aFont, unsigned long* aPositionX2, unsigned long* aThickness) { /* * XLFD 1.5 says underline position defaults descent/2. * Hence we return position*2 to avoid rounding error. */ if (aFont->GetXFontProperty(XA_UNDERLINE_POSITION, aPositionX2)) { *aPositionX2 *= 2; } else { *aPositionX2 = aFont->max_bounds.descent; } /* * XLFD 1.5 says underline thickness defaults to cap stem width. * We don't know what that is, so we just take the thickness of "_". * This way, we get thicker underlines for bold fonts. */ if (!xFont->GetXFontProperty(XA_UNDERLINE_THICKNESS, aThickness)) { int dir, ascent, descent; XCharStruct overall; XTextExtents(aFont, "_", 1, &dir, &ascent, &descent, &overall); *aThickness = (overall.ascent + overall.descent); } } #endif /* 0 */ static PRUint16* GetMapFor10646Font(XFontStruct* aFont) { if (!aFont->per_char) return nsnull; nsCompressedCharMap ccmapObj; PRInt32 minByte1 = aFont->min_byte1; PRInt32 maxByte1 = aFont->max_byte1; PRInt32 minByte2 = aFont->min_char_or_byte2; PRInt32 maxByte2 = aFont->max_char_or_byte2; PRInt32 charsPerRow = maxByte2 - minByte2 + 1; for (PRInt32 row = minByte1; row <= maxByte1; row++) { PRInt32 offset = (((row - minByte1) * charsPerRow) - minByte2); for (PRInt32 cell = minByte2; cell <= maxByte2; cell++) { XCharStruct* bounds = &aFont->per_char[offset + cell]; // From Section 8.5 Font Metrics in the Xlib programming manual: // A nonexistent character is represented with all members of its XCharStruct set to zero. if (bounds->ascent || bounds->descent || bounds->lbearing || bounds->rbearing || bounds->width || bounds->attributes) { ccmapObj.SetChar((row << 8) | cell); } } } PRUint16 *ccmap = ccmapObj.NewCCMap(); return ccmap; } PRBool nsFontGTK::IsEmptyFont(XFontStruct* xFont) { // // scan and see if we can find at least one glyph // if (xFont->per_char) { PRInt32 minByte1 = xFont->min_byte1; PRInt32 maxByte1 = xFont->max_byte1; PRInt32 minByte2 = xFont->min_char_or_byte2; PRInt32 maxByte2 = xFont->max_char_or_byte2; PRInt32 charsPerRow = maxByte2 - minByte2 + 1; for (PRInt32 row = minByte1; row <= maxByte1; row++) { PRInt32 offset = (((row - minByte1) * charsPerRow) - minByte2); for (PRInt32 cell = minByte2; cell <= maxByte2; cell++) { XCharStruct* bounds = &xFont->per_char[offset + cell]; if (bounds->ascent || bounds->descent) { return PR_FALSE; } } } } return PR_TRUE; } void nsFontGTK::LoadFont(void) { if (mAlreadyCalledLoadFont) { return; } mAlreadyCalledLoadFont = PR_TRUE; GdkFont* gdkFont; NS_ASSERTION(!mFont, "mFont should not be loaded"); if (mAABaseSize==0) { NS_ASSERTION(!mFontHolder, "mFontHolder should not be loaded"); gdk_error_trap_push(); gdkFont = ::gdk_font_load(mName); gdk_error_trap_pop(); if (!gdkFont) return; mXFont = new nsXFontNormal(gdkFont); } else { NS_ASSERTION(mFontHolder, "mFontHolder should be loaded"); gdkFont = mFontHolder; mXFont = new nsXFontAAScaledBitmap(GDK_DISPLAY(), DefaultScreen(GDK_DISPLAY()), gdkFont, mSize, mAABaseSize); } NS_ASSERTION(mXFont,"failed to load mXFont"); if (!mXFont) return; if (!mXFont->LoadFont()) { delete mXFont; mXFont = nsnull; return; } if (gdkFont) { XFontStruct* xFont = mXFont->GetXFontStruct(); XFontStruct* xFont_with_per_char; if (mAABaseSize==0) xFont_with_per_char = xFont; else xFont_with_per_char = (XFontStruct *)GDK_FONT_XFONT(mFontHolder); mMaxAscent = xFont->ascent; mMaxDescent = xFont->descent; if (mCharSetInfo == &ISO106461) { mCCMap = GetMapFor10646Font(xFont_with_per_char); if (!mCCMap) { mXFont->UnloadFont(); mXFont = nsnull; ::gdk_font_unref(gdkFont); mFontHolder = nsnull; return; } } // // since we are very close to a release point // limit the risk of this fix // please remove soon // // Redhat 6.2 Japanese has invalid jisx201 fonts // Solaris 2.6 has invalid cns11643 fonts for planes 4-7 if ((mCharSetInfo == &JISX0201) || (mCharSetInfo == &CNS116434) || (mCharSetInfo == &CNS116435) || (mCharSetInfo == &CNS116436) || (mCharSetInfo == &CNS116437) ) { if (IsEmptyFont(xFont_with_per_char)) { #ifdef NS_FONT_DEBUG_LOAD_FONT if (gFontDebug & NS_FONT_DEBUG_LOAD_FONT) { printf("\n"); printf("***************************************\n"); printf("invalid font \"%s\", %s %d\n", mName, __FILE__, __LINE__); printf("***************************************\n"); printf("\n"); } #endif mXFont->UnloadFont(); mXFont = nsnull; ::gdk_font_unref(gdkFont); mFontHolder = nsnull; return; } } mFont = gdkFont; #ifdef NS_FONT_DEBUG_LOAD_FONT if (gFontDebug & NS_FONT_DEBUG_LOAD_FONT) { printf("loaded %s\n", mName); } #endif } #ifdef NS_FONT_DEBUG_LOAD_FONT else if (gFontDebug & NS_FONT_DEBUG_LOAD_FONT) { printf("cannot load %s\n", mName); } #endif } GdkFont* nsFontGTK::GetGDKFont(void) { return mFont; } nsXFont* nsFontGTK::GetXFont(void) { return mXFont; } PRBool nsFontGTK::GetXFontIs10646(void) { return ((PRBool) (mCharSetInfo == &ISO106461)); } PRBool nsFontGTK::IsFreeTypeFont(void) { return PR_FALSE; } MOZ_DECL_CTOR_COUNTER(nsFontGTK) nsFontGTK::nsFontGTK() { MOZ_COUNT_CTOR(nsFontGTK); } nsFontGTK::~nsFontGTK() { MOZ_COUNT_DTOR(nsFontGTK); if (mXFont) { delete mXFont; } if (mFont && (mAABaseSize==0)) { gdk_font_unref(mFont); } if (mCharSetInfo == &ISO106461) { FreeCCMap(mCCMap); } if (mName) { PR_smprintf_free(mName); } } class nsFontGTKNormal : public nsFontGTK { public: nsFontGTKNormal(); nsFontGTKNormal(nsFontGTK*); virtual ~nsFontGTKNormal(); virtual gint GetWidth(const PRUnichar* aString, PRUint32 aLength); virtual gint DrawString(nsRenderingContextGTK* aContext, nsDrawingSurfaceGTK* aSurface, nscoord aX, nscoord aY, const PRUnichar* aString, PRUint32 aLength); #ifdef MOZ_MATHML virtual nsresult GetBoundingMetrics(const PRUnichar* aString, PRUint32 aLength, nsBoundingMetrics& aBoundingMetrics); #endif }; nsFontGTKNormal::nsFontGTKNormal() { mFontHolder = nsnull; } nsFontGTKNormal::nsFontGTKNormal(nsFontGTK *aFont) { mAABaseSize = aFont->mSize; mFontHolder = aFont->GetGDKFont(); if (!mFontHolder) { aFont->LoadFont(); mFontHolder = aFont->GetGDKFont(); } NS_ASSERTION(mFontHolder, "font to copy not loaded"); if (mFontHolder) ::gdk_font_ref(mFontHolder); } nsFontGTKNormal::~nsFontGTKNormal() { if (mFontHolder) ::gdk_font_unref(mFontHolder); } gint nsFontGTKNormal::GetWidth(const PRUnichar* aString, PRUint32 aLength) { if (!mFont) { LoadFont(); if (!mFont) { return 0; } } XChar2b buf[512]; char* p; PRInt32 bufLen; ENCODER_BUFFER_ALLOC_IF_NEEDED(p, mCharSetInfo->mConverter, aString, aLength, buf, sizeof(buf), bufLen); gint len = mCharSetInfo->Convert(mCharSetInfo, mXFont->GetXFontStruct(), aString, aLength, p, bufLen); gint outWidth; if (mXFont->IsSingleByte()) outWidth = mXFont->TextWidth8(p, len); else outWidth = mXFont->TextWidth16((const XChar2b*)p, len/2); ENCODER_BUFFER_FREE_IF_NEEDED(p, buf); return outWidth; } gint nsFontGTKNormal::DrawString(nsRenderingContextGTK* aContext, nsDrawingSurfaceGTK* aSurface, nscoord aX, nscoord aY, const PRUnichar* aString, PRUint32 aLength) { if (!mFont) { LoadFont(); if (!mFont) { return 0; } } XChar2b buf[512]; char* p; PRInt32 bufLen; ENCODER_BUFFER_ALLOC_IF_NEEDED(p, mCharSetInfo->mConverter, aString, aLength, buf, sizeof(buf), bufLen); gint len = mCharSetInfo->Convert(mCharSetInfo, mXFont->GetXFontStruct(), aString, aLength, p, bufLen); GdkGC *gc = aContext->GetGC(); gint outWidth; if (mXFont->IsSingleByte()) { mXFont->DrawText8(aSurface->GetDrawable(), gc, aX, aY + mBaselineAdjust, p, len); outWidth = mXFont->TextWidth8(p, len); } else { mXFont->DrawText16(aSurface->GetDrawable(), gc, aX, aY + mBaselineAdjust, (const XChar2b*)p, len/2); outWidth = mXFont->TextWidth16((const XChar2b*)p, len/2); } gdk_gc_unref(gc); ENCODER_BUFFER_FREE_IF_NEEDED(p, buf); return outWidth; } #ifdef MOZ_MATHML // bounding metrics for a string // remember returned values are not in app units nsresult nsFontGTKNormal::GetBoundingMetrics (const PRUnichar* aString, PRUint32 aLength, nsBoundingMetrics& aBoundingMetrics) { aBoundingMetrics.Clear(); if (!mFont) { LoadFont(); if (!mFont) { return NS_ERROR_FAILURE; } } if (aString && 0 < aLength) { XFontStruct *fontInfo = mXFont->GetXFontStruct(); XChar2b buf[512]; char* p; PRInt32 bufLen; ENCODER_BUFFER_ALLOC_IF_NEEDED(p, mCharSetInfo->mConverter, aString, aLength, buf, sizeof(buf), bufLen); gint len = mCharSetInfo->Convert(mCharSetInfo, fontInfo, aString, aLength, p, bufLen); if (mXFont->IsSingleByte()) { mXFont->TextExtents8(p, len, &aBoundingMetrics.leftBearing, &aBoundingMetrics.rightBearing, &aBoundingMetrics.width, &aBoundingMetrics.ascent, &aBoundingMetrics.descent); } else { mXFont->TextExtents16((const XChar2b*)p, len, &aBoundingMetrics.leftBearing, &aBoundingMetrics.rightBearing, &aBoundingMetrics.width, &aBoundingMetrics.ascent, &aBoundingMetrics.descent); } ENCODER_BUFFER_FREE_IF_NEEDED(p, buf); } return NS_OK; } #endif class nsFontGTKSubstitute : public nsFontGTK { public: nsFontGTKSubstitute(nsFontGTK* aFont); virtual ~nsFontGTKSubstitute(); virtual GdkFont* GetGDKFont(void); virtual nsXFont* GetXFont(void); virtual PRBool GetXFontIs10646(void); virtual gint GetWidth(const PRUnichar* aString, PRUint32 aLength); virtual gint DrawString(nsRenderingContextGTK* aContext, nsDrawingSurfaceGTK* aSurface, nscoord aX, nscoord aY, const PRUnichar* aString, PRUint32 aLength); #ifdef MOZ_MATHML virtual nsresult GetBoundingMetrics(const PRUnichar* aString, PRUint32 aLength, nsBoundingMetrics& aBoundingMetrics); #endif virtual PRUint32 Convert(const PRUnichar* aSrc, PRUint32 aSrcLen, PRUnichar* aDest, PRUint32 aDestLen); nsFontGTK* mSubstituteFont; }; nsFontGTKSubstitute::nsFontGTKSubstitute(nsFontGTK* aFont) { mSubstituteFont = aFont; } nsFontGTKSubstitute::~nsFontGTKSubstitute() { // Do not free mSubstituteFont here. It is owned by somebody else. } PRUint32 nsFontGTKSubstitute::Convert(const PRUnichar* aSrc, PRUint32 aSrcLen, PRUnichar* aDest, PRUint32 aDestLen) { nsresult res; if (!gFontSubConverter) { CallCreateInstance(kSaveAsCharsetCID, &gFontSubConverter); if (gFontSubConverter) { res = gFontSubConverter->Init("ISO-8859-1", nsISaveAsCharset::attr_FallbackQuestionMark + nsISaveAsCharset::attr_EntityAfterCharsetConv + nsISaveAsCharset::attr_IgnoreIgnorables, nsIEntityConverter::transliterate); if (NS_FAILED(res)) { NS_RELEASE(gFontSubConverter); } } } if (gFontSubConverter) { nsAutoString tmp(aSrc, aSrcLen); char* conv = nsnull; res = gFontSubConverter->Convert(tmp.get(), &conv); if (NS_SUCCEEDED(res) && conv) { char* p = conv; PRUint32 i; for (i = 0; i < aDestLen; i++) { if (*p) { aDest[i] = *p; } else { break; } p++; } nsMemory::Free(conv); conv = nsnull; return i; } } if (aSrcLen > aDestLen) { aSrcLen = aDestLen; } for (PRUint32 i = 0; i < aSrcLen; i++) { aDest[i] = '?'; } return aSrcLen; } gint nsFontGTKSubstitute::GetWidth(const PRUnichar* aString, PRUint32 aLength) { PRUnichar buf[512]; PRUnichar *p = buf; PRUint32 bufLen = sizeof(buf)/sizeof(PRUnichar); if ((aLength*2) > bufLen) { PRUnichar *tmp; tmp = (PRUnichar*)nsMemory::Alloc(sizeof(PRUnichar) * (aLength*2)); if (tmp) { p = tmp; bufLen = (aLength*2); } } PRUint32 len = Convert(aString, aLength, p, bufLen); gint outWidth = mSubstituteFont->GetWidth(p, len); if (p != buf) nsMemory::Free(p); return outWidth; } gint nsFontGTKSubstitute::DrawString(nsRenderingContextGTK* aContext, nsDrawingSurfaceGTK* aSurface, nscoord aX, nscoord aY, const PRUnichar* aString, PRUint32 aLength) { PRUnichar buf[512]; PRUnichar *p = buf; PRUint32 bufLen = sizeof(buf)/sizeof(PRUnichar); if ((aLength*2) > bufLen) { PRUnichar *tmp; tmp = (PRUnichar*)nsMemory::Alloc(sizeof(PRUnichar) * (aLength*2)); if (tmp) { p = tmp; bufLen = (aLength*2); } } PRUint32 len = Convert(aString, aLength, p, bufLen); gint outWidth = mSubstituteFont->DrawString(aContext, aSurface, aX, aY, p, len); if (p != buf) nsMemory::Free(p); return outWidth; } #ifdef MOZ_MATHML // bounding metrics for a string // remember returned values are not in app units nsresult nsFontGTKSubstitute::GetBoundingMetrics(const PRUnichar* aString, PRUint32 aLength, nsBoundingMetrics& aBoundingMetrics) { PRUnichar buf[512]; PRUnichar *p = buf; PRUint32 bufLen = sizeof(buf)/sizeof(PRUnichar); if ((aLength*2) > bufLen) { PRUnichar *tmp; tmp = (PRUnichar*)nsMemory::Alloc(sizeof(PRUnichar) * (aLength*2)); if (tmp) { p = tmp; bufLen = (aLength*2); } } PRUint32 len = Convert(aString, aLength, p, bufLen); nsresult res = mSubstituteFont->GetBoundingMetrics(p, len, aBoundingMetrics); if (p != buf) nsMemory::Free(p); return res; } #endif GdkFont* nsFontGTKSubstitute::GetGDKFont(void) { return mSubstituteFont->GetGDKFont(); } nsXFont* nsFontGTKSubstitute::GetXFont(void) { return mSubstituteFont->GetXFont(); } PRBool nsFontGTKSubstitute::GetXFontIs10646(void) { return mSubstituteFont->GetXFontIs10646(); } class nsFontGTKUserDefined : public nsFontGTK { public: nsFontGTKUserDefined(); virtual ~nsFontGTKUserDefined(); virtual PRBool Init(nsFontGTK* aFont); virtual gint GetWidth(const PRUnichar* aString, PRUint32 aLength); virtual gint DrawString(nsRenderingContextGTK* aContext, nsDrawingSurfaceGTK* aSurface, nscoord aX, nscoord aY, const PRUnichar* aString, PRUint32 aLength); #ifdef MOZ_MATHML virtual nsresult GetBoundingMetrics(const PRUnichar* aString, PRUint32 aLength, nsBoundingMetrics& aBoundingMetrics); #endif virtual PRUint32 Convert(const PRUnichar* aSrc, PRInt32 aSrcLen, char* aDest, PRInt32 aDestLen); }; nsFontGTKUserDefined::nsFontGTKUserDefined() { } nsFontGTKUserDefined::~nsFontGTKUserDefined() { // Do not free mFont here. It is owned by somebody else. } PRBool nsFontGTKUserDefined::Init(nsFontGTK* aFont) { if (!aFont->GetXFont()) { aFont->LoadFont(); if (!aFont->GetXFont()) { mCCMap = gEmptyCCMap; return PR_FALSE; } } mXFont = aFont->GetXFont(); mCCMap = gUserDefinedCCMap; mName = aFont->mName; return PR_TRUE; } PRUint32 nsFontGTKUserDefined::Convert(const PRUnichar* aSrc, PRInt32 aSrcLen, char* aDest, PRInt32 aDestLen) { if (aSrcLen > aDestLen) { aSrcLen = aDestLen; } gUserDefinedConverter->Convert(aSrc, &aSrcLen, aDest, &aDestLen); return aSrcLen; } gint nsFontGTKUserDefined::GetWidth(const PRUnichar* aString, PRUint32 aLength) { char buf[1024]; char* p; PRInt32 bufLen; ENCODER_BUFFER_ALLOC_IF_NEEDED(p, gUserDefinedConverter, aString, aLength, buf, sizeof(buf), bufLen); PRUint32 len = Convert(aString, aLength, p, bufLen); gint outWidth; if (mXFont->IsSingleByte()) outWidth = mXFont->TextWidth8(p, len); else outWidth = mXFont->TextWidth16((const XChar2b*)p, len/2); ENCODER_BUFFER_FREE_IF_NEEDED(p, buf); return outWidth; } gint nsFontGTKUserDefined::DrawString(nsRenderingContextGTK* aContext, nsDrawingSurfaceGTK* aSurface, nscoord aX, nscoord aY, const PRUnichar* aString, PRUint32 aLength) { char buf[1024]; char* p; PRInt32 bufLen; ENCODER_BUFFER_ALLOC_IF_NEEDED(p, gUserDefinedConverter, aString, aLength, buf, sizeof(buf), bufLen); PRUint32 len = Convert(aString, aLength, p, bufLen); GdkGC *gc = aContext->GetGC(); gint outWidth; if (mXFont->IsSingleByte()) { mXFont->DrawText8(aSurface->GetDrawable(), gc, aX, aY + mBaselineAdjust, p, len); outWidth = mXFont->TextWidth8(p, len); } else { mXFont->DrawText16(aSurface->GetDrawable(), gc, aX, aY + mBaselineAdjust, (const XChar2b*)p, len); outWidth = mXFont->TextWidth16((const XChar2b*)p, len/2); } gdk_gc_unref(gc); ENCODER_BUFFER_FREE_IF_NEEDED(p, buf); return outWidth; } #ifdef MOZ_MATHML // bounding metrics for a string // remember returned values are not in app units nsresult nsFontGTKUserDefined::GetBoundingMetrics(const PRUnichar* aString, PRUint32 aLength, nsBoundingMetrics& aBoundingMetrics) { aBoundingMetrics.Clear(); if (aString && 0 < aLength) { char buf[1024]; char* p; PRInt32 bufLen; ENCODER_BUFFER_ALLOC_IF_NEEDED(p, gUserDefinedConverter, aString, aLength, buf, sizeof(buf), bufLen); PRUint32 len = Convert(aString, aLength, p, bufLen); if (mXFont->IsSingleByte()) { mXFont->TextExtents8(p, len, &aBoundingMetrics.leftBearing, &aBoundingMetrics.rightBearing, &aBoundingMetrics.width, &aBoundingMetrics.ascent, &aBoundingMetrics.descent); } else { mXFont->TextExtents16((const XChar2b*)p, len, &aBoundingMetrics.leftBearing, &aBoundingMetrics.rightBearing, &aBoundingMetrics.width, &aBoundingMetrics.ascent, &aBoundingMetrics.descent); } ENCODER_BUFFER_FREE_IF_NEEDED(p, buf); } return NS_OK; } #endif nsFontGTK* nsFontMetricsGTK::AddToLoadedFontsList(nsFontGTK* aFont) { if (mLoadedFontsCount == mLoadedFontsAlloc) { int newSize; if (mLoadedFontsAlloc) { newSize = (2 * mLoadedFontsAlloc); } else { newSize = 1; } nsFontGTK** newPointer = (nsFontGTK**) PR_Realloc(mLoadedFonts, newSize * sizeof(nsFontGTK*)); if (newPointer) { mLoadedFonts = newPointer; mLoadedFontsAlloc = newSize; } else { return nsnull; } } mLoadedFonts[mLoadedFontsCount++] = aFont; return aFont; } // define a size such that a scaled font would always be closer // to the desired size than this #define NOT_FOUND_FONT_SIZE 1000*1000*1000 nsFontGTK* nsFontMetricsGTK::FindNearestSize(nsFontStretch* aStretch, PRUint16 aSize) { nsFontGTK* font = nsnull; if (aStretch->mSizes) { nsFontGTK** begin = aStretch->mSizes; nsFontGTK** end = &aStretch->mSizes[aStretch->mSizesCount]; nsFontGTK** s; // scan the list of sizes for (s = begin; s < end; s++) { // stop when we hit or overshoot the size if ((*s)->mSize >= aSize) { break; } } // backup if we hit the end of the list if (s == end) { s--; } else if (s != begin) { // if we overshot pick the closest size if (((*s)->mSize - aSize) >= (aSize - (*(s - 1))->mSize)) { s--; } } // this is the nearest bitmap font font = *s; } return font; } static PRBool SetFontCharsetInfo(nsFontGTK *aFont, nsFontCharSetInfo* aCharSet, PRUint32 aChar) { if (aCharSet->mCharSet) { aFont->mCCMap = aCharSet->mCCMap; // check that the font is not empty if (CCMAP_HAS_CHAR_EXT(aFont->mCCMap, aChar)) { aFont->LoadFont(); if (!aFont->GetXFont()) { return PR_FALSE; } } } else { if (aCharSet == &ISO106461) { aFont->LoadFont(); if (!aFont->GetXFont()) { return PR_FALSE; } } } return PR_TRUE; } static nsFontGTK* SetupUserDefinedFont(nsFontGTK *aFont) { if (!aFont->mUserDefinedFont) { aFont->mUserDefinedFont = new nsFontGTKUserDefined(); if (!aFont->mUserDefinedFont) { return nsnull; } if (!aFont->mUserDefinedFont->Init(aFont)) { return nsnull; } } return aFont->mUserDefinedFont; } nsFontGTK* nsFontMetricsGTK::GetAASBBaseFont(nsFontStretch* aStretch, nsFontCharSetInfo* aCharSet) { nsFontGTK* base_aafont; PRInt32 scale_size; PRUint32 aa_target_size; scale_size = PR_MAX(mPixelSize, aCharSet->mAABitmapScaleMin); aa_target_size = MAX((scale_size*2), 16); base_aafont = FindNearestSize(aStretch, aa_target_size); NS_ASSERTION(base_aafont, "failed to find a base font for Anti-Aliased bitmap Scaling"); return base_aafont; } nsFontGTK* nsFontMetricsGTK::PickASizeAndLoad(nsFontStretch* aStretch, nsFontCharSetInfo* aCharSet, PRUint32 aChar, const char *aName) { #ifdef MOZ_ENABLE_FREETYPE2 if (aStretch->mFreeTypeFaceID) { //FREETYPE_FONT_PRINTF(("mFreeTypeFaceID = 0x%p", aStretch->mFreeTypeFaceID)); nsFreeTypeFont *ftfont = nsFreeTypeFont::NewFont(aStretch->mFreeTypeFaceID, mPixelSize, aName); if (!ftfont) { FREETYPE_FONT_PRINTF(("failed to create font")); return nsnull; } //FREETYPE_FONT_PRINTF(("created ftfont")); /* * XXX Instead of passing pixel size, we ought to take underline * into account. (Extra space for underline for Asian fonts.) */ ftfont->mName = PR_smprintf("%s", aName); if (!ftfont->mName) { FREETYPE_FONT_PRINTF(("failed to create mName")); delete ftfont; return nsnull; } SetCharsetLangGroup(aCharSet); ftfont->mSize = mPixelSize; ftfont->LoadFont(); ftfont->mCharSetInfo = &ISO106461; //FREETYPE_FONT_PRINTF(("add the ftfont")); return AddToLoadedFontsList(ftfont); } if (IS_SURROGATE(aChar)) { // SURROGATE is only supported by FreeType return nsnull; } #endif PRBool use_scaled_font = PR_FALSE; PRBool have_nearly_rightsized_bitmap = PR_FALSE; nsFontGTK* base_aafont = nsnull; PRInt32 bitmap_size = NOT_FOUND_FONT_SIZE; PRInt32 scale_size = mPixelSize; nsFontGTK* font = FindNearestSize(aStretch, mPixelSize); if (font) { bitmap_size = font->mSize; if ( (bitmap_size >= mPixelSize-(mPixelSize/10)) && (bitmap_size <= mPixelSize+(mPixelSize/10))) // When the size of a hand tuned font is close to the desired size // favor it over outline scaled font have_nearly_rightsized_bitmap = PR_TRUE; } // // If the user says always try to aasb (anti alias scaled bitmap) scale // if (gAABitmapScaleEnabled && aCharSet->mAABitmapScaleAlways) { base_aafont = GetAASBBaseFont(aStretch, aCharSet); if (base_aafont) { use_scaled_font = PR_TRUE; SIZE_FONT_PRINTF(("anti-aliased bitmap scaled font: %s\n" " desired=%d, aa-scaled=%d, bitmap=%d, " "aa_bitmap=%d", aName, mPixelSize, scale_size, bitmap_size, base_aafont->mSize)); } } // // if not already aasb scaling and // if we do not have a bitmap that is nearly the correct size // if (!use_scaled_font && !have_nearly_rightsized_bitmap) { // check if we can use an outline scaled font if (aStretch->mOutlineScaled) { scale_size = PR_MAX(mPixelSize, aCharSet->mOutlineScaleMin); if (PR_ABS(mPixelSize-scale_size) < PR_ABS(mPixelSize-bitmap_size)) { use_scaled_font = 1; SIZE_FONT_PRINTF(("outline font:______ %s\n" " desired=%d, scaled=%d, bitmap=%d", aStretch->mScalable, mPixelSize, scale_size, (bitmap_size=NOT_FOUND_FONT_SIZE?0:bitmap_size))); } } // see if we can aasb (anti alias scaled bitmap) if (!use_scaled_font && (bitmap_sizemAABitmapScaleMin); double ratio = (bitmap_size / ((double) mPixelSize)); if ( (ratio < aCharSet->mAABitmapUndersize) || (ratio > aCharSet->mAABitmapOversize)) { // // Try to get a size font to scale that is 2x larger // (but at least 16 pixel) // base_aafont = GetAASBBaseFont(aStretch, aCharSet); if (base_aafont) { use_scaled_font = PR_TRUE; SIZE_FONT_PRINTF(("anti-aliased bitmap scaled font: %s\n" " desired=%d, aa-scaled=%d, bitmap=%d, " "aa_bitmap=%d", aName, mPixelSize, scale_size, bitmap_size, base_aafont->mSize)); } } } // last resort: consider a bitmap scaled font (ugly!) if (!use_scaled_font && aStretch->mScalable) { scale_size = PR_MAX(mPixelSize, aCharSet->mBitmapScaleMin); double ratio = (bitmap_size / ((double) mPixelSize)); if ((ratio < aCharSet->mBitmapUndersize) || (ratio > aCharSet->mBitmapOversize)) { if ((PR_ABS(mPixelSize-scale_size) < PR_ABS(mPixelSize-bitmap_size))) { use_scaled_font = 1; SIZE_FONT_PRINTF(("bitmap scaled font: %s\n" " desired=%d, scaled=%d, bitmap=%d", aStretch->mScalable, mPixelSize, scale_size, (bitmap_size=NOT_FOUND_FONT_SIZE?0:bitmap_size))); } } } } NS_ASSERTION((bitmap_sizemScaledFonts.Count(); nsFontGTK* p = nsnull; for (i = 0; i < n; i++) { p = (nsFontGTK*) aStretch->mScaledFonts.ElementAt(i); if (p->mSize == scale_size) { break; } } if (i == n) { if (base_aafont) { // setup the base font if (!SetFontCharsetInfo(base_aafont, aCharSet, aChar)) return nsnull; if (mIsUserDefined) { base_aafont = SetupUserDefinedFont(base_aafont); if (!base_aafont) return nsnull; } font = new nsFontGTKNormal(base_aafont); } else font = new nsFontGTKNormal; if (font) { /* * XXX Instead of passing pixel size, we ought to take underline * into account. (Extra space for underline for Asian fonts.) */ if (base_aafont) { font->mName = PR_smprintf("%s", base_aafont->mName); font->mAABaseSize = base_aafont->mSize; } else { font->mName = PR_smprintf(aStretch->mScalable, scale_size); font->mAABaseSize = 0; } if (!font->mName) { delete font; return nsnull; } font->mSize = scale_size; font->mCharSetInfo = aCharSet; aStretch->mScaledFonts.AppendElement(font); } else { return nsnull; } } else { font = p; } } if (!SetFontCharsetInfo(font, aCharSet, aChar)) return nsnull; if (mIsUserDefined) { font = SetupUserDefinedFont(font); if (!font) return nsnull; } return AddToLoadedFontsList(font); } nsresult nsFontMetricsGTK::GetWidth (const char* aString, PRUint32 aLength, nscoord& aWidth, nsRenderingContextGTK *aContext) { if (aLength == 0) { aWidth = 0; return NS_OK; } nsXFont *xFont = mCurrentFont->GetXFont(); gint rawWidth; if (mCurrentFont->IsFreeTypeFont()) { // this function is only supposed to be called for ascii data rawWidth = mCurrentFont-> GetWidth(NS_ConvertASCIItoUTF16(aString, aLength).get(), aLength); } else if (!mCurrentFont->GetXFontIs10646()) { NS_ASSERTION(xFont->IsSingleByte(),"wrong string/font size"); // 8 bit data with an 8 bit font rawWidth = xFont->TextWidth8(aString, aLength); } else { NS_ASSERTION(!xFont->IsSingleByte(),"wrong string/font size"); // we have 8 bit data but a 16 bit font rawWidth = Widen8To16AndGetWidth (mCurrentFont->GetXFont(), aString, aLength); } float f; f = mDeviceContext->DevUnitsToAppUnits(); aWidth = NSToCoordRound(rawWidth * f); return NS_OK; } nsresult nsFontMetricsGTK::GetWidth (const PRUnichar* aString, PRUint32 aLength, nscoord& aWidth, PRInt32* aFontID, nsRenderingContextGTK *aContext) { if (aLength == 0) { aWidth = 0; return NS_OK; } nsFontGTK* prevFont = nsnull; gint rawWidth = 0; PRUint32 start = 0; PRUint32 i; PRUint32 extraSurrogateLength; for (i = 0; i < aLength; i+=1+extraSurrogateLength) { PRUint32 c = aString[i]; extraSurrogateLength=0; if(i < aLength-1 && IS_HIGH_SURROGATE(c) && IS_LOW_SURROGATE(aString[i+1])) { // if surrogate, make UCS4 code point from high aString[i] and // low surrogate aString[i+1] c = SURROGATE_TO_UCS4(c, aString[i+1]); // skip aString[i+1], it is already used as low surrogate extraSurrogateLength = 1; } nsFontGTK* currFont = nsnull; nsFontGTK** font = mLoadedFonts; nsFontGTK** end = &mLoadedFonts[mLoadedFontsCount]; while (font < end) { if (CCMAP_HAS_CHAR_EXT((*font)->mCCMap, c)) { currFont = *font; goto FoundFont; // for speed -- avoid "if" statement } font++; } currFont = FindFont(c); FoundFont: // XXX avoid this test by duplicating code -- erik if (prevFont) { if (currFont != prevFont) { rawWidth += prevFont->GetWidth(&aString[start], i - start); prevFont = currFont; start = i; } } else { prevFont = currFont; start = i; } } if (prevFont) { rawWidth += prevFont->GetWidth(&aString[start], i - start); } float f; f = mDeviceContext->DevUnitsToAppUnits(); aWidth = NSToCoordRound(rawWidth * f); if (nsnull != aFontID) *aFontID = 0; return NS_OK; } nsresult nsFontMetricsGTK::DrawString(const char *aString, PRUint32 aLength, nscoord aX, nscoord aY, const nscoord* aSpacing, nsRenderingContextGTK *aContext, nsDrawingSurfaceGTK *aSurface) { if (!aLength) return NS_ERROR_FAILURE; nsresult rv = NS_OK; g_return_val_if_fail(aString != NULL, NS_ERROR_FAILURE); g_return_val_if_fail(mCurrentFont != NULL, NS_ERROR_FAILURE); nscoord x = aX; nscoord y = aY; aContext->UpdateGC(); nsXFont *xFont = mCurrentFont->GetXFont(); // Get the gc - note that we have to unref this later GdkGC *gc = aContext->GetGC(); if (nsnull != aSpacing) { // Render the string, one character at a time... const char* end = aString + aLength; while (aString < end) { char ch = *aString++; nscoord xx = x; nscoord yy = y; aContext->GetTranMatrix()->TransformCoord(&xx, &yy); if (mCurrentFont->IsFreeTypeFont()) { // this function is only supposed to be called for ascii data rv = mCurrentFont->DrawString(aContext, aSurface, xx, yy, NS_ConvertASCIItoUTF16(aString, aLength).get(), aLength); } else if (!mCurrentFont->GetXFontIs10646()) { // 8 bit data with an 8 bit font NS_ASSERTION(xFont->IsSingleByte(),"wrong string/font size"); xFont->DrawText8(aSurface->GetDrawable(), gc, xx, yy, &ch, 1); } else { // we have 8 bit data but a 16 bit font NS_ASSERTION(!xFont->IsSingleByte(),"wrong string/font size"); Widen8To16AndDraw(aSurface->GetDrawable(), xFont, gc, xx, yy, &ch, 1); } x += *aSpacing++; } } else { aContext->GetTranMatrix()->TransformCoord(&x, &y); if (mCurrentFont->IsFreeTypeFont()) { // this function is only supposed to be called for ascii data rv = mCurrentFont->DrawString(aContext, aSurface, x, y, NS_ConvertASCIItoUTF16(aString, aLength).get(), aLength); } else if (!mCurrentFont->GetXFontIs10646()) { // keep 8 bit path fast // 8 bit data with an 8 bit font NS_ASSERTION(xFont->IsSingleByte(),"wrong string/font size"); xFont->DrawText8(aSurface->GetDrawable(), gc, x, y, aString, aLength); } else { // we have 8 bit data but a 16 bit font NS_ASSERTION(!xFont->IsSingleByte(),"wrong string/font size"); Widen8To16AndDraw(aSurface->GetDrawable(), xFont, gc, x, y, aString, aLength); } } gdk_gc_unref(gc); return rv; } nsresult nsFontMetricsGTK::DrawString(const PRUnichar* aString, PRUint32 aLength, nscoord aX, nscoord aY, PRInt32 aFontID, const nscoord* aSpacing, nsRenderingContextGTK *aContext, nsDrawingSurfaceGTK *aSurface) { if (!aLength) return NS_ERROR_FAILURE; g_return_val_if_fail(aSurface != NULL, NS_ERROR_FAILURE); g_return_val_if_fail(aString != NULL, NS_ERROR_FAILURE); nscoord x = aX; nscoord y = aY; aContext->GetTranMatrix()->TransformCoord(&x, &y); nsFontGTK* prevFont = nsnull; PRUint32 start = 0; PRUint32 i; PRUint32 extraSurrogateLength; for (i = 0; i < aLength; i+=1+extraSurrogateLength) { PRUint32 c = aString[i]; extraSurrogateLength=0; if(i < aLength-1 && IS_HIGH_SURROGATE(c) && IS_LOW_SURROGATE(aString[i+1])) { // if surrogate, make UCS4 code point from high aString[i] and // low surrogate aString[i+1] c = SURROGATE_TO_UCS4(c, aString[i+1]); // skip aString[i+1], it is already used as low surrogate extraSurrogateLength = 1; } nsFontGTK* currFont = nsnull; nsFontGTK** font = mLoadedFonts; nsFontGTK** lastFont = &mLoadedFonts[mLoadedFontsCount]; while (font < lastFont) { if (CCMAP_HAS_CHAR_EXT((*font)->mCCMap, c)) { currFont = *font; goto FoundFont; // for speed -- avoid "if" statement } font++; } currFont = FindFont(c); FoundFont: // XXX avoid this test by duplicating code -- erik if (prevFont) { if (currFont != prevFont) { if (aSpacing) { const PRUnichar* str = &aString[start]; const PRUnichar* end = &aString[i]; // save off mCurrentFont and set it so that we // cache the GC's font correctly nsFontGTK *oldFont = mCurrentFont; mCurrentFont = prevFont; aContext->UpdateGC(); while (str < end) { x = aX; y = aY; aContext->GetTranMatrix()->TransformCoord(&x, &y); prevFont->DrawString(aContext, aSurface, x, y, str, 1); aX += *aSpacing++; str++; } mCurrentFont = oldFont; } else { nsFontGTK *oldFont = mCurrentFont; mCurrentFont = prevFont; aContext->UpdateGC(); x += prevFont->DrawString(aContext, aSurface, x, y, &aString[start], i - start); mCurrentFont = oldFont; } prevFont = currFont; start = i; } } else { prevFont = currFont; start = i; } } if (prevFont) { nsFontGTK *oldFont = mCurrentFont; mCurrentFont = prevFont; aContext->UpdateGC(); if (aSpacing) { const PRUnichar* str = &aString[start]; const PRUnichar* end = &aString[i]; while (str < end) { x = aX; y = aY; aContext->GetTranMatrix()->TransformCoord(&x, &y); prevFont->DrawString(aContext, aSurface, x, y, str, 1); aX += *aSpacing++; str++; } } else { prevFont->DrawString(aContext, aSurface, x, y, &aString[start], i - start); } mCurrentFont = oldFont; } return NS_OK; } #ifdef MOZ_MATHML nsresult nsFontMetricsGTK::GetBoundingMetrics(const char *aString, PRUint32 aLength, nsBoundingMetrics &aBoundingMetrics, nsRenderingContextGTK *aContext) { aBoundingMetrics.Clear(); if (!aString || !aLength) return NS_ERROR_FAILURE; nsresult rv = NS_OK; nsXFont *xFont = mCurrentFont->GetXFont(); if (mCurrentFont->IsFreeTypeFont()) { // this function is only supposed to be called for ascii data rv = mCurrentFont->GetBoundingMetrics( NS_ConvertASCIItoUTF16(aString, aLength).get(), aLength, aBoundingMetrics); } else if (!mCurrentFont->GetXFontIs10646()) { // 8 bit data with an 8 bit font NS_ASSERTION(xFont->IsSingleByte(),"wrong string/font size"); xFont->TextExtents8(aString, aLength, &aBoundingMetrics.leftBearing, &aBoundingMetrics.rightBearing, &aBoundingMetrics.width, &aBoundingMetrics.ascent, &aBoundingMetrics.descent); } else { // we have 8 bit data but a 16 bit font NS_ASSERTION(!xFont->IsSingleByte(),"wrong string/font size"); Widen8To16AndGetTextExtents (mCurrentFont->GetXFont(), aString, aLength, &aBoundingMetrics.leftBearing, &aBoundingMetrics.rightBearing, &aBoundingMetrics.width, &aBoundingMetrics.ascent, &aBoundingMetrics.descent); } float P2T; P2T = mDeviceContext->DevUnitsToAppUnits(); aBoundingMetrics.leftBearing = NSToCoordRound(aBoundingMetrics.leftBearing * P2T); aBoundingMetrics.rightBearing = NSToCoordRound(aBoundingMetrics.rightBearing * P2T); aBoundingMetrics.width = NSToCoordRound(aBoundingMetrics.width * P2T); aBoundingMetrics.ascent = NSToCoordRound(aBoundingMetrics.ascent * P2T); aBoundingMetrics.descent = NSToCoordRound(aBoundingMetrics.descent * P2T); return rv; } nsresult nsFontMetricsGTK::GetBoundingMetrics(const PRUnichar *aString, PRUint32 aLength, nsBoundingMetrics &aBoundingMetrics, PRInt32 *aFontID, nsRenderingContextGTK *aContext) { aBoundingMetrics.Clear(); if (!aString || !aLength) return NS_ERROR_FAILURE; nsFontGTK* prevFont = nsnull; nsBoundingMetrics rawbm; PRBool firstTime = PR_TRUE; PRUint32 start = 0; PRUint32 i; PRUint32 extraSurrogateLength; for (i = 0; i < aLength; i+=1+extraSurrogateLength) { PRUint32 c = aString[i]; extraSurrogateLength=0; if(i < aLength-1 && IS_HIGH_SURROGATE(c) && IS_LOW_SURROGATE(aString[i+1])) { // if surrogate, make UCS4 code point from high aString[i] and // low surrogate aString[i+1] c = SURROGATE_TO_UCS4(c, aString[i+1]); // skip aString[i+1], it is already used as low surrogate extraSurrogateLength = 1; } nsFontGTK* currFont = nsnull; nsFontGTK** font = mLoadedFonts; nsFontGTK** end = &mLoadedFonts[mLoadedFontsCount]; while (font < end) { if (CCMAP_HAS_CHAR_EXT((*font)->mCCMap, c)) { currFont = *font; goto FoundFont; // for speed -- avoid "if" statement } font++; } currFont = FindFont(c); FoundFont: // XXX avoid this test by duplicating code -- erik if (prevFont) { if (currFont != prevFont) { prevFont->GetBoundingMetrics((const PRUnichar*)&aString[start], i - start, rawbm); if (firstTime) { firstTime = PR_FALSE; aBoundingMetrics = rawbm; } else { aBoundingMetrics += rawbm; } prevFont = currFont; start = i; } } else { prevFont = currFont; start = i; } } if (prevFont) { prevFont->GetBoundingMetrics((const PRUnichar*) &aString[start], i - start, rawbm); if (firstTime) aBoundingMetrics = rawbm; else aBoundingMetrics += rawbm; } // convert to app units float P2T; P2T = mDeviceContext->DevUnitsToAppUnits(); aBoundingMetrics.leftBearing = NSToCoordRound(aBoundingMetrics.leftBearing * P2T); aBoundingMetrics.rightBearing = NSToCoordRound(aBoundingMetrics.rightBearing * P2T); aBoundingMetrics.width = NSToCoordRound(aBoundingMetrics.width * P2T); aBoundingMetrics.ascent = NSToCoordRound(aBoundingMetrics.ascent * P2T); aBoundingMetrics.descent = NSToCoordRound(aBoundingMetrics.descent * P2T); if (nsnull != aFontID) *aFontID = 0; return NS_OK; } #endif /* MOZ_MATHML */ nsresult nsFontMetricsGTK::GetTextDimensions (const PRUnichar* aString, PRUint32 aLength, nsTextDimensions& aDimensions, PRInt32* aFontID, nsRenderingContextGTK *aContext) { aDimensions.Clear(); if (!aString || !aLength) return NS_ERROR_FAILURE; nsFontGTK* prevFont = nsnull; gint rawWidth = 0, rawAscent = 0, rawDescent = 0; PRUint32 start = 0; PRUint32 i; PRUint32 extraSurrogateLength; for (i = 0; i < aLength; i+=1+extraSurrogateLength) { PRUint32 c = aString[i]; extraSurrogateLength=0; if(i < aLength-1 && IS_HIGH_SURROGATE(c) && IS_LOW_SURROGATE(aString[i+1])) { // if surrogate, make UCS4 code point from high aString[i] and // low surrogate aString[i+1] c = SURROGATE_TO_UCS4(c, aString[i+1]); // skip aString[i+1], it is already used as low surrogate extraSurrogateLength = 1; } nsFontGTK* currFont = nsnull; nsFontGTK** font = mLoadedFonts; nsFontGTK** end = &mLoadedFonts[mLoadedFontsCount]; while (font < end) { if (CCMAP_HAS_CHAR_EXT((*font)->mCCMap, c)) { currFont = *font; goto FoundFont; // for speed -- avoid "if" statement } font++; } currFont = FindFont(c); FoundFont: // XXX avoid this test by duplicating code -- erik if (prevFont) { if (currFont != prevFont) { rawWidth += prevFont->GetWidth(&aString[start], i - start); if (rawAscent < prevFont->mMaxAscent) rawAscent = prevFont->mMaxAscent; if (rawDescent < prevFont->mMaxDescent) rawDescent = prevFont->mMaxDescent; prevFont = currFont; start = i; } } else { prevFont = currFont; start = i; } } if (prevFont) { rawWidth += prevFont->GetWidth(&aString[start], i - start); if (rawAscent < prevFont->mMaxAscent) rawAscent = prevFont->mMaxAscent; if (rawDescent < prevFont->mMaxDescent) rawDescent = prevFont->mMaxDescent; } float P2T; P2T = mDeviceContext->DevUnitsToAppUnits(); aDimensions.width = NSToCoordRound(rawWidth * P2T); aDimensions.ascent = NSToCoordRound(rawAscent * P2T); aDimensions.descent = NSToCoordRound(rawDescent * P2T); if (nsnull != aFontID) *aFontID = 0; return NS_OK; } nsresult nsFontMetricsGTK::GetTextDimensions (const char* aString, PRInt32 aLength, PRInt32 aAvailWidth, PRInt32* aBreaks, PRInt32 aNumBreaks, nsTextDimensions& aDimensions, PRInt32& aNumCharsFit, nsTextDimensions& aLastWordDimensions, PRInt32* aFontID, nsRenderingContextGTK *aContext) { NS_PRECONDITION(aBreaks[aNumBreaks - 1] == aLength, "invalid break array"); // If we need to back up this state represents the last place // we could break. We can use this to avoid remeasuring text PRInt32 prevBreakState_BreakIndex = -1; // not known // (hasn't been computed) nscoord prevBreakState_Width = 0; // accumulated width to this point // Initialize OUT parameters GetMaxAscent(aLastWordDimensions.ascent); GetMaxDescent(aLastWordDimensions.descent); aLastWordDimensions.width = -1; aNumCharsFit = 0; // Iterate each character in the string and determine which font to use nscoord width = 0; PRInt32 start = 0; nscoord aveCharWidth; GetAveCharWidth(aveCharWidth); while (start < aLength) { // Estimate how many characters will fit. Do that by // diving the available space by the average character // width. Make sure the estimated number of characters is // at least 1 PRInt32 estimatedNumChars = 0; if (aveCharWidth > 0) estimatedNumChars = (aAvailWidth - width) / aveCharWidth; if (estimatedNumChars < 1) estimatedNumChars = 1; // Find the nearest break offset PRInt32 estimatedBreakOffset = start + estimatedNumChars; PRInt32 breakIndex; nscoord numChars; // Find the nearest place to break that is less than or equal to // the estimated break offset if (aLength <= estimatedBreakOffset) { // All the characters should fit numChars = aLength - start; breakIndex = aNumBreaks - 1; } else { breakIndex = prevBreakState_BreakIndex; while (((breakIndex + 1) < aNumBreaks) && (aBreaks[breakIndex + 1] <= estimatedBreakOffset)) { ++breakIndex; } if (breakIndex == prevBreakState_BreakIndex) { ++breakIndex; // make sure we advanced past the // previous break index } numChars = aBreaks[breakIndex] - start; } // Measure the text nscoord twWidth = 0; if ((1 == numChars) && (aString[start] == ' ')) GetSpaceWidth(twWidth); else if (numChars > 0) GetWidth(&aString[start], numChars, twWidth, aContext); // See if the text fits PRBool textFits = (twWidth + width) <= aAvailWidth; // If the text fits then update the width and the number of // characters that fit if (textFits) { aNumCharsFit += numChars; width += twWidth; start += numChars; // This is a good spot to back up to if we need to so remember // this state prevBreakState_BreakIndex = breakIndex; prevBreakState_Width = width; } else { // See if we can just back up to the previous saved // state and not have to measure any text if (prevBreakState_BreakIndex > 0) { // If the previous break index is just before the // current break index then we can use it if (prevBreakState_BreakIndex == (breakIndex - 1)) { aNumCharsFit = aBreaks[prevBreakState_BreakIndex]; width = prevBreakState_Width; break; } } // We can't just revert to the previous break state if (0 == breakIndex) { // There's no place to back up to, so even though // the text doesn't fit return it anyway aNumCharsFit += numChars; width += twWidth; break; } // Repeatedly back up until we get to where the text // fits or we're all the way back to the first word width += twWidth; while ((breakIndex >= 1) && (width > aAvailWidth)) { twWidth = 0; start = aBreaks[breakIndex - 1]; numChars = aBreaks[breakIndex] - start; if ((1 == numChars) && (aString[start] == ' ')) GetSpaceWidth(twWidth); else if (numChars > 0) GetWidth(&aString[start], numChars, twWidth, aContext); width -= twWidth; aNumCharsFit = start; breakIndex--; } break; } } aDimensions.width = width; GetMaxAscent(aDimensions.ascent); GetMaxDescent(aDimensions.descent); return NS_OK; } struct BreakGetTextDimensionsData { float mP2T; // IN PRInt32 mAvailWidth; // IN PRInt32* mBreaks; // IN PRInt32 mNumBreaks; // IN nscoord mSpaceWidth; // IN nscoord mAveCharWidth; // IN PRInt32 mEstimatedNumChars; // IN (running -- to handle the edge case of one word) PRInt32 mNumCharsFit; // IN/OUT -- accumulated number of chars that fit so far nscoord mWidth; // IN/OUT -- accumulated width so far // If we need to back up, this state represents the last place // we could break. We can use this to avoid remeasuring text PRInt32 mPrevBreakState_BreakIndex; // IN/OUT, initialized as -1, i.e., not yet computed nscoord mPrevBreakState_Width; // IN/OUT, initialized as 0 // Remember the fonts that we use so that we can deal with // line-breaking in-between fonts later. mOffsets[0] is also used // to initialize the current offset from where to start measuring nsVoidArray* mFonts; // IN/OUT nsVoidArray* mOffsets; // IN/OUT }; static PRBool PR_CALLBACK do_BreakGetTextDimensions(const nsFontSwitchGTK *aFontSwitch, const PRUnichar* aSubstring, PRUint32 aSubstringLength, void* aData) { nsFontGTK* fontGTK = aFontSwitch->mFontGTK; // Make sure the font is selected BreakGetTextDimensionsData* data = (BreakGetTextDimensionsData*)aData; // Our current state relative to the _full_ string... // This allows emulation of the previous code... const PRUnichar* pstr = (const PRUnichar*)data->mOffsets->ElementAt(0); PRInt32 numCharsFit = data->mNumCharsFit; nscoord width = data->mWidth; PRInt32 start = (PRInt32)(aSubstring - pstr); PRInt32 end = start + aSubstringLength; PRBool allDone = PR_FALSE; while (start < end) { // Estimate how many characters will fit. Do that by dividing the // available space by the average character width PRInt32 estimatedNumChars = data->mEstimatedNumChars; if (!estimatedNumChars && data->mAveCharWidth > 0) { estimatedNumChars = (data->mAvailWidth - width) / data->mAveCharWidth; } // Make sure the estimated number of characters is at least 1 if (estimatedNumChars < 1) { estimatedNumChars = 1; } // Find the nearest break offset PRInt32 estimatedBreakOffset = start + estimatedNumChars; PRInt32 breakIndex = -1; // not yet computed PRBool inMiddleOfSegment = PR_FALSE; nscoord numChars; // Avoid scanning the break array in the case where we think all // the text should fit if (end <= estimatedBreakOffset) { // Everything should fit numChars = end - start; } else { // Find the nearest place to break that is less than or equal to // the estimated break offset breakIndex = data->mPrevBreakState_BreakIndex; while (breakIndex + 1 < data->mNumBreaks && data->mBreaks[breakIndex + 1] <= estimatedBreakOffset) { ++breakIndex; } if (breakIndex == -1) breakIndex = 0; // We found a place to break that is before the estimated break // offset. Where we break depends on whether the text crosses a // segment boundary if (start < data->mBreaks[breakIndex]) { // The text crosses at least one segment boundary so measure to the // break point just before the estimated break offset numChars = PR_MIN(data->mBreaks[breakIndex], end) - start; } else { // See whether there is another segment boundary between this one // and the end of the text if ((breakIndex < (data->mNumBreaks - 1)) && (data->mBreaks[breakIndex] < end)) { ++breakIndex; numChars = PR_MIN(data->mBreaks[breakIndex], end) - start; } else { NS_ASSERTION(end != data->mBreaks[breakIndex], "don't expect to be at segment boundary"); // The text is all within the same segment numChars = end - start; // Remember we're in the middle of a segment and not between // two segments inMiddleOfSegment = PR_TRUE; } } } // Measure the text nscoord twWidth, pxWidth; if ((1 == numChars) && (pstr[start] == ' ')) { twWidth = data->mSpaceWidth; } else { pxWidth = fontGTK->GetWidth(&pstr[start], numChars); twWidth = NSToCoordRound(float(pxWidth) * data->mP2T); } // See if the text fits PRBool textFits = (twWidth + width) <= data->mAvailWidth; // If the text fits then update the width and the number of // characters that fit if (textFits) { numCharsFit += numChars; width += twWidth; // If we computed the break index and we're not in the middle // of a segment then this is a spot that we can back up to if // we need to, so remember this state if ((breakIndex != -1) && !inMiddleOfSegment) { data->mPrevBreakState_BreakIndex = breakIndex; data->mPrevBreakState_Width = width; } } else { // The text didn't fit. If we're out of room then we're all done allDone = PR_TRUE; // See if we can just back up to the previous saved state and not // have to measure any text if (data->mPrevBreakState_BreakIndex != -1) { PRBool canBackup; // If we're in the middle of a word then the break index // must be the same if we can use it. If we're at a segment // boundary, then if the saved state is for the previous // break index then we can use it if (inMiddleOfSegment) { canBackup = data->mPrevBreakState_BreakIndex == breakIndex; } else { canBackup = data->mPrevBreakState_BreakIndex == (breakIndex - 1); } if (canBackup) { numCharsFit = data->mBreaks[data->mPrevBreakState_BreakIndex]; width = data->mPrevBreakState_Width; break; } } // We can't just revert to the previous break state. Find the break // index just before the end of the text end = start + numChars; breakIndex = 0; if (data->mBreaks[breakIndex] < end) { while ((breakIndex + 1 < data->mNumBreaks) && (data->mBreaks[breakIndex + 1] < end)) { ++breakIndex; } } if ((0 == breakIndex) && (end <= data->mBreaks[0])) { // There's no place to back up to, so even though the text doesn't fit // return it anyway numCharsFit += numChars; width += twWidth; // Edge case of one word: it could be that we just measured a fragment of the // first word and its remainder involves other fonts, so we want to keep going // until we at least measure the entire first word if (numCharsFit < data->mBreaks[0]) { allDone = PR_FALSE; // From now on we don't care anymore what is the _real_ estimated // number of characters that fits. Rather, we have no where to break // and have to measure one word fully, but the real estimate is less // than that one word. However, since the other bits of code rely on // what is in "data->mEstimatedNumChars", we want to override // "data->mEstimatedNumChars" and pass in what _has_ to be measured // so that it is transparent to the other bits that depend on it. data->mEstimatedNumChars = data->mBreaks[0] - numCharsFit; start += numChars; } break; } // Repeatedly back up until we get to where the text fits or we're // all the way back to the first word width += twWidth; while ((breakIndex >= 0) && (width > data->mAvailWidth)) { start = data->mBreaks[breakIndex]; numChars = end - start; numCharsFit = start; if ((1 == numChars) && (pstr[start] == ' ')) { width -= data->mSpaceWidth; } else if (pstr + start >= aSubstring) { // The entire fragment to chop is within the current font. pxWidth = fontGTK->GetWidth(&pstr[start], numChars); width -= NSToCoordRound(float(pxWidth) * data->mP2T); } else { // The fragment that we want to chop extends back into previous fonts. // We need to reverse into previous fonts. Fortunately, // data->mFonts[] and data->mOffsets[] tell us which fonts are used // and when. end = data->mNumCharsFit; // same as aSubstring - pstr data->mNumCharsFit = numCharsFit; // has got shorter... PRInt32 k = data->mFonts->Count() - 1; for ( ; k >= 0 && start < end; --k, end -= numChars) { fontGTK = (nsFontGTK*)data->mFonts->ElementAt(k); const PRUnichar* ps = (const PRUnichar*)data->mOffsets->ElementAt(k); if (ps < pstr + start) ps = pstr + start; numChars = pstr + end - ps; NS_ASSERTION(numChars > 0, "empty string"); pxWidth = fontGTK->GetWidth(ps, numChars); data->mWidth -= NSToCoordRound(float(pxWidth) * data->mP2T); // By construction, mFonts[k] is the last font, and // mOffsets[k+1] is the last offset. data->mFonts->RemoveElementAt(k); data->mOffsets->RemoveElementAt(k+1); } // We are done, update the data now because we won't do it later. // The |if (data->mNumCharsFit != numCharsFit)| won't apply below data->mFonts->AppendElement(fontGTK); data->mOffsets->AppendElement((void*)&pstr[numCharsFit]); break; } --breakIndex; end = start; } } start += numChars; } #ifdef DEBUG_rbs NS_ASSERTION(allDone || start == end, "internal error"); NS_ASSERTION(allDone || data->mNumCharsFit != numCharsFit, "internal error"); #endif /* DEBUG_rbs */ if (data->mNumCharsFit != numCharsFit) { // some text was actually retained data->mWidth = width; data->mNumCharsFit = numCharsFit; data->mFonts->AppendElement(fontGTK); data->mOffsets->AppendElement((void*)&pstr[numCharsFit]); } if (allDone) { // stop now return PR_FALSE; } return PR_TRUE; // don't stop if we still need to measure more characters } nsresult nsFontMetricsGTK::GetTextDimensions (const PRUnichar* aString, PRInt32 aLength, PRInt32 aAvailWidth, PRInt32* aBreaks, PRInt32 aNumBreaks, nsTextDimensions& aDimensions, PRInt32& aNumCharsFit, nsTextDimensions& aLastWordDimensions, PRInt32* aFontID, nsRenderingContextGTK *aContext) { nscoord spaceWidth, aveCharWidth; GetSpaceWidth(spaceWidth); GetAveCharWidth(aveCharWidth); // Note: aBreaks[] is supplied to us so that the first word is // located at aString[0 .. aBreaks[0]-1] and more generally, the // k-th word is located at aString[aBreaks[k-1] // .. aBreaks[k]-1]. Whitespace can be included and each of them // counts as a word in its own right. // Upon completion of glyph resolution, characters that can be // represented with fonts[i] are at offsets[i] .. offsets[i+1]-1 nsAutoVoidArray fonts, offsets; offsets.AppendElement((void*)aString); float f; f = mDeviceContext->DevUnitsToAppUnits(); BreakGetTextDimensionsData data = { f, aAvailWidth, aBreaks, aNumBreaks, spaceWidth, aveCharWidth, 0, 0, 0, -1, 0, &fonts, &offsets }; ResolveForwards(aString, aLength, do_BreakGetTextDimensions, &data); if (aFontID) *aFontID = 0; aNumCharsFit = data.mNumCharsFit; aDimensions.width = data.mWidth; /////////////////// // Post-processing for the ascent and descent: // // The width of the last word is included in the final width, but // its ascent and descent are kept aside for the moment. The // problem is that line-breaking may occur _before_ the last word, // and we don't want its ascent and descent to interfere. We can // re-measure the last word and substract its width // later. However, we need a special care for the ascent and // descent at the break-point. The idea is to keep the ascent and // descent of the last word separate, and let layout consider them // later when it has determined that line-breaking doesn't occur // before the last word. // // Therefore, there are two things to do: // 1. Determine the ascent and descent up to where line-breaking may occur. // 2. Determine the ascent and descent of the remainder. For // efficiency however, it is okay to bail out early if there is // only one font (in this case, the height of the last word has no // special effect on the total height). // aLastWordDimensions.width should be set to -1 to reply that we // don't know the width of the last word since we measure multiple // words aLastWordDimensions.Clear(); aLastWordDimensions.width = -1; PRInt32 count = fonts.Count(); if (!count) return NS_OK; nsFontGTK* fontGTK = (nsFontGTK*)fonts[0]; NS_ASSERTION(fontGTK, "internal error in do_BreakGetTextDimensions"); aDimensions.ascent = fontGTK->mMaxAscent; aDimensions.descent = fontGTK->mMaxDescent; // fast path - normal case, quick return if there is only one font if (count == 1) return NS_OK; // get the last break index. // If there is only one word, we end up with lastBreakIndex = // 0. We don't need to worry about aLastWordDimensions in this // case too. But if we didn't return earlier, it would mean that // the unique word needs several fonts and we will still have to // loop over the fonts to return the final height PRInt32 lastBreakIndex = 0; while (aBreaks[lastBreakIndex] < aNumCharsFit) ++lastBreakIndex; const PRUnichar* lastWord = (lastBreakIndex > 0) ? aString + aBreaks[lastBreakIndex-1] : aString + aNumCharsFit; // let it point outside to play nice // with the loop // now get the desired ascent and descent information... this is // however a very fast loop of the order of the number of // additional fonts PRInt32 currFont = 0; const PRUnichar* pstr = aString; const PRUnichar* last = aString + aNumCharsFit; while (pstr < last) { fontGTK = (nsFontGTK*)fonts[currFont]; PRUnichar* nextOffset = (PRUnichar*)offsets[++currFont]; // For consistent word-wrapping, we are going to handle the // whitespace character with special care because a whitespace // character can come from a font different from that of the // previous word. If 'x', 'y', 'z', are Unicode points that // require different fonts, we want 'xyz
' and 'xyz
' // to have the same height because it gives a more stable // rendering, especially when the window is resized at the // edge of the word. // If we don't do this, a 'tall' trailing whitespace, i.e., if // the whitespace happens to come from a font with a bigger // ascent and/or descent than all current fonts on the line, // this can cause the next lines to be shifted down when the // window is slowly resized to fit that whitespace. if (*pstr == ' ') { // skip pass the whitespace to ignore the height that it // may contribute ++pstr; // get out if we reached the end if (pstr == last) { break; } // switch to the next font if we just passed the current font if (pstr == nextOffset) { fontGTK = (nsFontGTK*)fonts[currFont]; nextOffset = (PRUnichar*)offsets[++currFont]; } } // see if the last word intersects with the current font // (we are testing for 'nextOffset-1 >= lastWord' since the // current font ends at nextOffset-1) if (nextOffset > lastWord) { if (aLastWordDimensions.ascent < fontGTK->mMaxAscent) { aLastWordDimensions.ascent = fontGTK->mMaxAscent; } if (aLastWordDimensions.descent < fontGTK->mMaxDescent) { aLastWordDimensions.descent = fontGTK->mMaxDescent; } } // see if we have not reached the last word yet if (pstr < lastWord) { if (aDimensions.ascent < fontGTK->mMaxAscent) { aDimensions.ascent = fontGTK->mMaxAscent; } if (aDimensions.descent < fontGTK->mMaxDescent) { aDimensions.descent = fontGTK->mMaxDescent; } } // advance to where the next font starts pstr = nextOffset; } return NS_OK; } GdkFont * nsFontMetricsGTK::GetCurrentGDKFont(void) { return mCurrentFont->GetGDKFont(); } nsresult nsFontMetricsGTK::SetRightToLeftText(PRBool aIsRTL) { return NS_OK; } PRBool nsFontMetricsGTK::GetRightToLeftText() { return PR_FALSE; } nsresult nsFontMetricsGTK::GetClusterInfo(const PRUnichar *aText, PRUint32 aLength, PRUint8 *aClusterStarts) { return NS_ERROR_NOT_IMPLEMENTED; } PRInt32 nsFontMetricsGTK::GetPosition(const PRUnichar *aText, PRUint32 aLength, nsPoint aPt) { return -1; } nsresult nsFontMetricsGTK::GetRangeWidth(const PRUnichar *aText, PRUint32 aLength, PRUint32 aStart, PRUint32 aEnd, PRUint32 &aWidth) { return NS_ERROR_NOT_IMPLEMENTED; } nsresult nsFontMetricsGTK::GetRangeWidth(const char *aText, PRUint32 aLength, PRUint32 aStart, PRUint32 aEnd, PRUint32 &aWidth) { return NS_ERROR_NOT_IMPLEMENTED; } PR_BEGIN_EXTERN_C static int CompareSizes(const void* aArg1, const void* aArg2, void *data) { return (*((nsFontGTK**) aArg1))->mSize - (*((nsFontGTK**) aArg2))->mSize; } PR_END_EXTERN_C void nsFontStretch::SortSizes(void) { NS_QuickSort(mSizes, mSizesCount, sizeof(*mSizes), CompareSizes, NULL); } void nsFontWeight::FillStretchHoles(void) { int i, j; for (i = 0; i < 9; i++) { if (mStretches[i]) { mStretches[i]->SortSizes(); } } if (!mStretches[4]) { for (i = 5; i < 9; i++) { if (mStretches[i]) { mStretches[4] = mStretches[i]; break; } } if (!mStretches[4]) { for (i = 3; i >= 0; i--) { if (mStretches[i]) { mStretches[4] = mStretches[i]; break; } } } } for (i = 5; i < 9; i++) { if (!mStretches[i]) { for (j = i + 1; j < 9; j++) { if (mStretches[j]) { mStretches[i] = mStretches[j]; break; } } if (!mStretches[i]) { for (j = i - 1; j >= 0; j--) { if (mStretches[j]) { mStretches[i] = mStretches[j]; break; } } } } } for (i = 3; i >= 0; i--) { if (!mStretches[i]) { for (j = i - 1; j >= 0; j--) { if (mStretches[j]) { mStretches[i] = mStretches[j]; break; } } if (!mStretches[i]) { for (j = i + 1; j < 9; j++) { if (mStretches[j]) { mStretches[i] = mStretches[j]; break; } } } } } } void nsFontStyle::FillWeightHoles(void) { int i, j; for (i = 0; i < 9; i++) { if (mWeights[i]) { mWeights[i]->FillStretchHoles(); } } if (!mWeights[3]) { for (i = 4; i < 9; i++) { if (mWeights[i]) { mWeights[3] = mWeights[i]; break; } } if (!mWeights[3]) { for (i = 2; i >= 0; i--) { if (mWeights[i]) { mWeights[3] = mWeights[i]; break; } } } } // CSS2, section 15.5.1 if (!mWeights[4]) { mWeights[4] = mWeights[3]; } for (i = 5; i < 9; i++) { if (!mWeights[i]) { for (j = i + 1; j < 9; j++) { if (mWeights[j]) { mWeights[i] = mWeights[j]; break; } } if (!mWeights[i]) { for (j = i - 1; j >= 0; j--) { if (mWeights[j]) { mWeights[i] = mWeights[j]; break; } } } } } for (i = 2; i >= 0; i--) { if (!mWeights[i]) { for (j = i - 1; j >= 0; j--) { if (mWeights[j]) { mWeights[i] = mWeights[j]; break; } } if (!mWeights[i]) { for (j = i + 1; j < 9; j++) { if (mWeights[j]) { mWeights[i] = mWeights[j]; break; } } } } } } void nsFontNode::FillStyleHoles(void) { if (mHolesFilled) { return; } mHolesFilled = 1; #ifdef DEBUG_DUMP_TREE DumpFamily(this); #endif for (int i = 0; i < 3; i++) { if (mStyles[i]) { mStyles[i]->FillWeightHoles(); } } // XXX If both italic and oblique exist, there is probably something // wrong. Try counting the fonts, and removing the one that has less. if (!mStyles[NS_FONT_STYLE_NORMAL]) { if (mStyles[NS_FONT_STYLE_ITALIC]) { mStyles[NS_FONT_STYLE_NORMAL] = mStyles[NS_FONT_STYLE_ITALIC]; } else { mStyles[NS_FONT_STYLE_NORMAL] = mStyles[NS_FONT_STYLE_OBLIQUE]; } } if (!mStyles[NS_FONT_STYLE_ITALIC]) { if (mStyles[NS_FONT_STYLE_OBLIQUE]) { mStyles[NS_FONT_STYLE_ITALIC] = mStyles[NS_FONT_STYLE_OBLIQUE]; } else { mStyles[NS_FONT_STYLE_ITALIC] = mStyles[NS_FONT_STYLE_NORMAL]; } } if (!mStyles[NS_FONT_STYLE_OBLIQUE]) { if (mStyles[NS_FONT_STYLE_ITALIC]) { mStyles[NS_FONT_STYLE_OBLIQUE] = mStyles[NS_FONT_STYLE_ITALIC]; } else { mStyles[NS_FONT_STYLE_OBLIQUE] = mStyles[NS_FONT_STYLE_NORMAL]; } } #ifdef DEBUG_DUMP_TREE DumpFamily(this); #endif } static void SetCharsetLangGroup(nsFontCharSetInfo* aCharSetInfo) { if (!aCharSetInfo->mCharSet || aCharSetInfo->mLangGroup) return; nsresult res; res = gCharSetManager->GetCharsetLangGroupRaw(aCharSetInfo->mCharSet, &aCharSetInfo->mLangGroup); if (NS_FAILED(res)) { aCharSetInfo->mLangGroup = NS_NewAtom(""); #ifdef NOISY_FONTS printf("=== cannot get lang group for %s\n", aCharSetInfo->mCharSet); #endif } } #define GET_WEIGHT_INDEX(index, weight) \ do { \ (index) = WEIGHT_INDEX(weight); \ if ((index) < 0) { \ (index) = 0; \ } \ else if ((index) > 8) { \ (index) = 8; \ } \ } while (0) nsFontGTK* nsFontMetricsGTK::SearchNode(nsFontNode* aNode, PRUint32 aChar) { if (aNode->mDummy) { return nsnull; } nsFontCharSetInfo* charSetInfo = aNode->mCharSetInfo; /* * mCharSet is set if we know which glyphs will be found in these fonts. * If mCCMap has already been created for this charset, we compare it with * the mCCMaps of the previously loaded fonts. If it is the same as any of * the previous ones, we return nsnull because there is no point in * loading a font with the same map. */ if (charSetInfo->mCharSet) { // if SURROGATE char, ignore charSetInfo->mCCMap checking // because the exact ccmap is never created before loading // NEED TO FIX: need better way if (IS_SURROGATE(aChar) ) { goto check_done; } PRUint16* ccmap = charSetInfo->mCCMap; if (ccmap) { for (int i = 0; i < mLoadedFontsCount; i++) { if (mLoadedFonts[i]->mCCMap == ccmap) { return nsnull; } } } else { if (!SetUpFontCharSetInfo(charSetInfo)) return nsnull; } } else { if ((!mIsUserDefined) && (charSetInfo == &Unknown)) { return nsnull; } } check_done: aNode->FillStyleHoles(); nsFontStyle* style = aNode->mStyles[mStyleIndex]; nsFontWeight** weights = style->mWeights; int weight = mFont.weight; int steps = (weight % 100); int weightIndex; if (steps) { if (steps < 10) { int base = (weight - steps); GET_WEIGHT_INDEX(weightIndex, base); while (steps--) { nsFontWeight* prev = weights[weightIndex]; for (weightIndex++; weightIndex < 9; weightIndex++) { if (weights[weightIndex] != prev) { break; } } if (weightIndex >= 9) { weightIndex = 8; } } } else if (steps > 90) { steps = (100 - steps); int base = (weight + steps); GET_WEIGHT_INDEX(weightIndex, base); while (steps--) { nsFontWeight* prev = weights[weightIndex]; for (weightIndex--; weightIndex >= 0; weightIndex--) { if (weights[weightIndex] != prev) { break; } } if (weightIndex < 0) { weightIndex = 0; } } } else { GET_WEIGHT_INDEX(weightIndex, weight); } } else { GET_WEIGHT_INDEX(weightIndex, weight); } FIND_FONT_PRINTF((" load font %s", aNode->mName.get())); return PickASizeAndLoad(weights[weightIndex]->mStretches[mStretchIndex], charSetInfo, aChar, aNode->mName.get()); } static void SetFontLangGroupInfo(nsFontCharSetMap* aCharSetMap) { nsFontLangGroup *fontLangGroup = aCharSetMap->mFontLangGroup; if (!fontLangGroup) return; // get the atom for mFontLangGroup->mFontLangGroupName so we can // apply fontLangGroup operations to it // eg: search for related groups, check for scaling prefs const char *langGroup = fontLangGroup->mFontLangGroupName; // the font.scale prefs don't make sense without a langGroup // XXX FIX!!! if langGroup is "", we need to bypass the font.scale stuff if (!langGroup) langGroup = ""; if (!fontLangGroup->mFontLangGroupAtom) { fontLangGroup->mFontLangGroupAtom = NS_NewAtom(langGroup); } // hack : map 'x-zh-TWHK' to 'zh-TW' when retrieving font scaling-control // preferences via |Get*Pref|. // XXX : This would make the font scaling controls for // zh-HK NOT work if a font common to zh-TW and zh-HK (e.g. big5-0) // were chosen for zh-HK. An alternative would be to make it // locale-dependent. Even with that, it'd work only under zh-HK locale. if (fontLangGroup->mFontLangGroupAtom == gZHTWHK) { langGroup = "zh-TW"; } // get the font scaling controls nsFontCharSetInfo *charSetInfo = aCharSetMap->mInfo; if (!charSetInfo->mInitedSizeInfo) { charSetInfo->mInitedSizeInfo = PR_TRUE; nsCAutoString name; nsresult rv; name.Assign("font.scale.outline.min."); name.Append(langGroup); rv = gPref->GetIntPref(name.get(), &charSetInfo->mOutlineScaleMin); if (NS_SUCCEEDED(rv)) SIZE_FONT_PRINTF(("%s = %d", name.get(), charSetInfo->mOutlineScaleMin)); else charSetInfo->mOutlineScaleMin = gOutlineScaleMinimum; name.Assign("font.scale.aa_bitmap.min."); name.Append(langGroup); rv = gPref->GetIntPref(name.get(), &charSetInfo->mAABitmapScaleMin); if (NS_SUCCEEDED(rv)) SIZE_FONT_PRINTF(("%s = %d", name.get(), charSetInfo->mAABitmapScaleMin)); else charSetInfo->mAABitmapScaleMin = gAABitmapScaleMinimum; name.Assign("font.scale.bitmap.min."); name.Append(langGroup); rv = gPref->GetIntPref(name.get(), &charSetInfo->mBitmapScaleMin); if (NS_SUCCEEDED(rv)) SIZE_FONT_PRINTF(("%s = %d", name.get(), charSetInfo->mBitmapScaleMin)); else charSetInfo->mBitmapScaleMin = gBitmapScaleMinimum; name.Assign("font.scale.aa_bitmap.oversize."); name.Append(langGroup); PRInt32 percent = 0; rv = gPref->GetIntPref(name.get(), &percent); if (NS_SUCCEEDED(rv)) { charSetInfo->mAABitmapOversize = percent/100.0; SIZE_FONT_PRINTF(("%s = %g", name.get(), charSetInfo->mAABitmapOversize)); } else charSetInfo->mAABitmapOversize = gAABitmapOversize; percent = 0; name.Assign("font.scale.aa_bitmap.undersize."); name.Append(langGroup); rv = gPref->GetIntPref(name.get(), &percent); if (NS_SUCCEEDED(rv)) { charSetInfo->mAABitmapUndersize = percent/100.0; SIZE_FONT_PRINTF(("%s = %g", name.get(),charSetInfo->mAABitmapUndersize)); } else charSetInfo->mAABitmapUndersize = gAABitmapUndersize; PRBool val = PR_TRUE; name.Assign("font.scale.aa_bitmap.always."); name.Append(langGroup); rv = gPref->GetBoolPref(name.get(), &val); if (NS_SUCCEEDED(rv)) { charSetInfo->mAABitmapScaleAlways = val; SIZE_FONT_PRINTF(("%s = %d", name.get(),charSetInfo->mAABitmapScaleAlways)); } else charSetInfo->mAABitmapScaleAlways = gAABitmapScaleAlways; percent = 0; name.Assign("font.scale.bitmap.oversize."); name.Append(langGroup); rv = gPref->GetIntPref(name.get(), &percent); if (NS_SUCCEEDED(rv)) { charSetInfo->mBitmapOversize = percent/100.0; SIZE_FONT_PRINTF(("%s = %g", name.get(), charSetInfo->mBitmapOversize)); } else charSetInfo->mBitmapOversize = gBitmapOversize; percent = 0; name.Assign("font.scale.bitmap.undersize."); name.Append(langGroup); rv = gPref->GetIntPref(name.get(), &percent); if (NS_SUCCEEDED(rv)) { charSetInfo->mBitmapUndersize = percent/100.0; SIZE_FONT_PRINTF(("%s = %g", name.get(), charSetInfo->mBitmapUndersize)); } else charSetInfo->mBitmapUndersize = gBitmapUndersize; } } static nsFontStyle* NodeGetStyle(nsFontNode* aNode, int aStyleIndex) { nsFontStyle* style = aNode->mStyles[aStyleIndex]; if (!style) { style = new nsFontStyle; if (!style) { return nsnull; } aNode->mStyles[aStyleIndex] = style; } return style; } static nsFontWeight* NodeGetWeight(nsFontStyle* aStyle, int aWeightIndex) { nsFontWeight* weight = aStyle->mWeights[aWeightIndex]; if (!weight) { weight = new nsFontWeight; if (!weight) { return nsnull; } aStyle->mWeights[aWeightIndex] = weight; } return weight; } static nsFontStretch* NodeGetStretch(nsFontWeight* aWeight, int aStretchIndex) { nsFontStretch* stretch = aWeight->mStretches[aStretchIndex]; if (!stretch) { stretch = new nsFontStretch; if (!stretch) { return nsnull; } aWeight->mStretches[aStretchIndex] = stretch; } return stretch; } static PRBool NodeAddScalable(nsFontStretch* aStretch, PRBool aOutlineScaled, const char *aDashFoundry, const char *aFamily, const char *aWeight, const char * aSlant, const char *aWidth, const char *aStyle, const char *aSpacing, const char *aCharSet) { // if we have both an outline scaled font and a bitmap // scaled font pick the outline scaled font if ((aStretch->mScalable) && (!aStretch->mOutlineScaled) && (aOutlineScaled)) { PR_smprintf_free(aStretch->mScalable); aStretch->mScalable = nsnull; } if (!aStretch->mScalable) { aStretch->mOutlineScaled = aOutlineScaled; if (aOutlineScaled) { aStretch->mScalable = PR_smprintf("%s-%s-%s-%s-%s-%s-%%d-*-0-0-%s-*-%s", aDashFoundry, aFamily, aWeight, aSlant, aWidth, aStyle, aSpacing, aCharSet); if (!aStretch->mScalable) return PR_FALSE; } else { aStretch->mScalable = PR_smprintf("%s-%s-%s-%s-%s-%s-%%d-*-*-*-%s-*-%s", aDashFoundry, aFamily, aWeight, aSlant, aWidth, aStyle, aSpacing, aCharSet); if (!aStretch->mScalable) return PR_FALSE; } } return PR_TRUE; } static PRBool NodeAddSize(nsFontStretch* aStretch, int aPixelSize, int aPointSize, float scaler, int aResX, int aResY, const char *aDashFoundry, const char *aFamily, const char *aWeight, const char * aSlant, const char *aWidth, const char *aStyle, const char *aSpacing, const char *aCharSet, nsFontCharSetInfo* aCharSetInfo) { if (scaler!=1.0f) { aPixelSize = int(float(aPixelSize) * scaler); aPointSize = int(float(aPointSize) * scaler); aResX = 0; aResY = 0; } PRBool haveSize = PR_FALSE; if (aStretch->mSizesCount) { nsFontGTK** end = &aStretch->mSizes[aStretch->mSizesCount]; nsFontGTK** s; for (s = aStretch->mSizes; s < end; s++) { if ((*s)->mSize == aPixelSize) { haveSize = PR_TRUE; break; } } } if (!haveSize) { if (aStretch->mSizesCount == aStretch->mSizesAlloc) { int newSize = 2 * (aStretch->mSizesAlloc ? aStretch->mSizesAlloc : 1); nsFontGTK** newSizes = new nsFontGTK*[newSize]; if (!newSizes) return PR_FALSE; for (int j = aStretch->mSizesAlloc - 1; j >= 0; j--) { newSizes[j] = aStretch->mSizes[j]; } aStretch->mSizesAlloc = newSize; delete [] aStretch->mSizes; aStretch->mSizes = newSizes; } char *name = PR_smprintf("%s-%s-%s-%s-%s-%s-%d-%d-%d-%d-%s-*-%s", aDashFoundry, aFamily, aWeight, aSlant, aWidth, aStyle, aPixelSize, aPointSize, aResX, aResY, aSpacing, aCharSet); if (!name) { return PR_FALSE; } nsFontGTK* size = new nsFontGTKNormal(); if (!size) { return PR_FALSE; } aStretch->mSizes[aStretch->mSizesCount++] = size; size->mName = name; // size->mFont is initialized in the constructor size->mSize = aPixelSize; size->mBaselineAdjust = 0; size->mCCMap = nsnull; size->mCharSetInfo = aCharSetInfo; } return PR_TRUE; } static void GetFontNames(const char* aPattern, PRBool aAnyFoundry, PRBool aOnlyOutlineScaledFonts, nsFontNodeArray* aNodes) { #ifdef NS_FONT_DEBUG_CALL_TRACE if (gFontDebug & NS_FONT_DEBUG_CALL_TRACE) { printf("GetFontNames %s\n", aPattern); } #endif #ifdef MOZ_ENABLE_FREETYPE2 // get FreeType fonts nsFT2FontNode::GetFontNames(aPattern, aNodes); #endif nsCAutoString previousNodeName; nsHashtable* node_hash; if (aAnyFoundry) { NS_ASSERTION(aPattern[1] == '*', "invalid 'anyFoundry' pattern"); node_hash = gAFRENodes; } else { node_hash = gFFRENodes; } #ifdef ENABLE_X_FONT_BANNING int screen_xres, screen_yres; /* Get Xserver DPI. * We cannot use Mozilla's API here because it may "override" the DPI * got from the Xserver via prefs. But we want to filter ("ban") fonts * we get from the Xserver which _it_(=Xserver) has "choosen" for us * using its DPI value ... */ screen_xres = int((float(::gdk_screen_width()) / (float(::gdk_screen_width_mm()) / 25.4f)) + 0.5f); screen_yres = int((float(::gdk_screen_height()) / (float(::gdk_screen_height_mm()) / 25.4f)) + 0.5f); #endif /* ENABLE_X_FONT_BANNING */ BANNED_FONT_PRINTF(("Loading font '%s'", aPattern)); /* * We do not use XListFontsWithInfo here, because it is very expensive. * Instead, we get that info at the time when we actually load the font. */ int count; char** list = ::XListFonts(GDK_DISPLAY(), aPattern, INT_MAX, &count); if ((!list) || (count < 1)) { return; } for (int i = 0; i < count; i++) { char name[256]; /* X11 font names are never larger than 255 chars */ strcpy(name, list[i]); /* Check if we can handle the font name ('*' and '?' are only valid in * input patterns passed as argument to |XListFont()|&co. but _not_ in * font names returned by these functions (see bug 136743 ("xlib complains * a lot about fonts with '*' in the XLFD string"))) */ if ((!name) || (name[0] != '-') || (PL_strpbrk(name, "*?") != nsnull)) { continue; } char buf[512]; PL_strncpyz(buf, name, sizeof(buf)); char *fName = buf; char* p = name + 1; int scalable = 0; PRBool outline_scaled = PR_FALSE; int resX = -1, resY = -1; #ifdef FIND_FIELD #undef FIND_FIELD #endif #define FIND_FIELD(var) \ char* var = p; \ while ((*p) && ((*p) != '-')) { \ p++; \ } \ if (*p) { \ *p++ = 0; \ } \ else { \ continue; \ } #ifdef SKIP_FIELD #undef SKIP_FIELD #endif #define SKIP_FIELD(var) \ while ((*p) && ((*p) != '-')) { \ p++; \ } \ if (*p) { \ p++; \ } \ else { \ continue; \ } FIND_FIELD(foundry); // XXX What to do about the many Applix fonts that start with "ax"? FIND_FIELD(familyName); FIND_FIELD(weightName); FIND_FIELD(slant); FIND_FIELD(setWidth); FIND_FIELD(addStyle); FIND_FIELD(pixelSize); if (pixelSize[0] == '0') { scalable = 1; } FIND_FIELD(pointSize); if (pointSize[0] == '0') { scalable = 1; } FIND_FIELD(resolutionX); resX = atoi(resolutionX); NS_ASSERTION(!(resolutionX[0] != '0' && resX == 0), "atoi(resolutionX) failure."); if (resolutionX[0] == '0') { scalable = 1; } FIND_FIELD(resolutionY); resY = atoi(resolutionY); NS_ASSERTION(!(resolutionY[0] != '0' && resY == 0), "atoi(resolutionY) failure."); if (resolutionY[0] == '0') { scalable = 1; } // check if bitmap non-scaled font if ((pixelSize[0] != '0') || (pointSize[0] != '0')) { SCALED_FONT_PRINTF(("bitmap (non-scaled) font: %s", fName)); } // check if bitmap scaled font else if ((pixelSize[0] == '0') && (pointSize[0] == '0') && (resolutionX[0] != '0') && (resolutionY[0] != '0')) { SCALED_FONT_PRINTF(("bitmap scaled font: %s", fName)); } // check if outline scaled font else if ((pixelSize[0] == '0') && (pointSize[0] == '0') && (resolutionX[0] == '0') && (resolutionY[0] == '0')) { outline_scaled = PR_TRUE; SCALED_FONT_PRINTF(("outline scaled font: %s", fName)); } else { SCALED_FONT_PRINTF(("unexpected font values: %s", fName)); SCALED_FONT_PRINTF((" pixelSize[0] = %c", pixelSize[0])); SCALED_FONT_PRINTF((" pointSize[0] = %c", pointSize[0])); SCALED_FONT_PRINTF((" resolutionX[0] = %c", resolutionX[0])); SCALED_FONT_PRINTF((" resolutionY[0] = %c", resolutionY[0])); static PRBool already_complained = PR_FALSE; // only complaing once if (!already_complained) { already_complained = PR_TRUE; NS_ASSERTION(pixelSize[0] == '0', "font scaler type test failed"); NS_ASSERTION(pointSize[0] == '0', "font scaler type test failed"); NS_ASSERTION(resolutionX[0] == '0', "font scaler type test failed"); NS_ASSERTION(resolutionY[0] == '0', "font scaler type test failed"); } } FIND_FIELD(spacing); FIND_FIELD(averageWidth); if (averageWidth[0] == '0') { scalable = 1; /* Workaround for bug 103159 ("sorting fonts by foundry names cause font * size of css ignored in some cases"). * Hardcoded font ban until bug 104075 ("need X font banning") has been * implemented. See http://bugzilla.mozilla.org/show_bug.cgi?id=94327#c34 * for additional comments... */ #ifndef DISABLE_WORKAROUND_FOR_BUG_103159 // skip 'mysterious' and 'spurious' cases like // -adobe-times-medium-r-normal--17-120-100-100-p-0-iso8859-9 if ((pixelSize[0] != '0' || pointSize[0] != 0) && (outline_scaled == PR_FALSE)) { PR_LOG(FontMetricsGTKLM, PR_LOG_DEBUG, ("rejecting font '%s' (via hardcoded workaround for bug 103159)\n", list[i])); BANNED_FONT_PRINTF(("rejecting font '%s' (via hardcoded workaround for bug 103159)", list[i])); continue; } #endif /* DISABLE_WORKAROUND_FOR_BUG_103159 */ } char* charSetName = p; // CHARSET_REGISTRY & CHARSET_ENCODING if (!*charSetName) { continue; } if (aOnlyOutlineScaledFonts && (outline_scaled == PR_FALSE)) { continue; } #ifdef ENABLE_X_FONT_BANNING #define BOOL2STR(b) ((b)?("true"):("false")) if (gFontRejectRegEx || gFontAcceptRegEx) { char fmatchbuf[512]; /* See sprintf() below. */ sprintf(fmatchbuf, "fname=%s;scalable=%s;outline_scaled=%s;xdisplay=%s;xdpy=%d;ydpy=%d;xdevice=%s", list[i], /* full font name */ BOOL2STR(scalable), BOOL2STR(outline_scaled), XDisplayString(GDK_DISPLAY()), screen_xres, screen_yres, "display" /* Xlib gfx supports other devices like "printer", too - DO NOT REMOVE! */ ); #undef BOOL2STR if (gFontRejectRegEx) { /* reject font if reject pattern matches it... */ if (regexec(gFontRejectRegEx, fmatchbuf, 0, nsnull, 0) == REG_OK) { PR_LOG(FontMetricsGTKLM, PR_LOG_DEBUG, ("rejecting font '%s' (via reject pattern)\n", fmatchbuf)); BANNED_FONT_PRINTF(("rejecting font '%s' (via reject pattern)", fmatchbuf)); continue; } } if (gFontAcceptRegEx) { if (regexec(gFontAcceptRegEx, fmatchbuf, 0, nsnull, 0) == REG_NOMATCH) { PR_LOG(FontMetricsGTKLM, PR_LOG_DEBUG, ("rejecting font '%s' (via accept pattern)\n", fmatchbuf)); BANNED_FONT_PRINTF(("rejecting font '%s' (via accept pattern)", fmatchbuf)); continue; } } } #endif /* ENABLE_X_FONT_BANNING */ nsFontCharSetMap *charSetMap = GetCharSetMap(charSetName); nsFontCharSetInfo* charSetInfo = charSetMap->mInfo; // indirection for font specific charset encoding if (charSetInfo == &Special) { nsCAutoString familyCharSetName(familyName); familyCharSetName.Append('-'); familyCharSetName.Append(charSetName); nsCStringKey familyCharSetKey(familyCharSetName); charSetMap = NS_STATIC_CAST(nsFontCharSetMap*, gSpecialCharSets->Get(&familyCharSetKey)); if (!charSetMap) charSetMap = gNoneCharSetMap; charSetInfo = charSetMap->mInfo; } if (!charSetInfo) { #ifdef NOISY_FONTS printf("cannot find charset %s\n", charSetName); #endif charSetInfo = &Unknown; } SetCharsetLangGroup(charSetInfo); SetFontLangGroupInfo(charSetMap); nsCAutoString nodeName; if (aAnyFoundry) nodeName.Assign('*'); else nodeName.Assign(foundry); nodeName.Append('-'); nodeName.Append(familyName); nodeName.Append('-'); nodeName.Append(charSetName); nsCStringKey key(nodeName); nsFontNode* node = (nsFontNode*) node_hash->Get(&key); if (!node) { node = new nsFontNode; if (!node) { continue; } node_hash->Put(&key, node); node->mName = nodeName; node->mCharSetInfo = charSetInfo; } int found = 0; if (nodeName == previousNodeName) { found = 1; } else { found = (aNodes->IndexOf(node) >= 0); } previousNodeName = nodeName; if (!found) { aNodes->AppendElement(node); } int styleIndex; // XXX This does not cover the full XLFD spec for SLANT. switch (slant[0]) { case 'i': styleIndex = NS_FONT_STYLE_ITALIC; break; case 'o': styleIndex = NS_FONT_STYLE_OBLIQUE; break; case 'r': default: styleIndex = NS_FONT_STYLE_NORMAL; break; } nsFontStyle* style = NodeGetStyle(node, styleIndex); if (!style) continue; nsCStringKey weightKey(weightName); int weightNumber = NS_PTR_TO_INT32(gWeights->Get(&weightKey)); if (!weightNumber) { #ifdef NOISY_FONTS printf("cannot find weight %s\n", weightName); #endif weightNumber = NS_FONT_WEIGHT_NORMAL; } int weightIndex = WEIGHT_INDEX(weightNumber); nsFontWeight* weight = NodeGetWeight(style, weightIndex); if (!weight) continue; nsCStringKey setWidthKey(setWidth); int stretchIndex = NS_PTR_TO_INT32(gStretches->Get(&setWidthKey)); if (!stretchIndex) { #ifdef NOISY_FONTS printf("cannot find stretch %s\n", setWidth); #endif stretchIndex = 5; } stretchIndex--; nsFontStretch* stretch = NodeGetStretch(weight, stretchIndex); if (!stretch) continue; if (scalable) { if (!NodeAddScalable(stretch, outline_scaled, name, familyName, weightName, slant, setWidth, addStyle, spacing, charSetName)) continue; } // get pixel size before the string is changed int pixels, points; pixels = atoi(pixelSize); points = atoi(pointSize); if (pixels) { if (gScaleBitmapFontsWithDevScale && (gDevScale > 1.0f)) { /* Add a font size which is exactly scaled as the scaling factor ... */ if (!NodeAddSize(stretch, pixels, points, gDevScale, resX, resY, name, familyName, weightName, slant, setWidth, addStyle, spacing, charSetName, charSetInfo)) continue; /* ... and offer a range of scaled fonts with integer scaling factors * (we're taking half steps between integers, too - to avoid too big * steps between font sizes) */ float minScaler = PR_MAX(gDevScale / 2.0f, 1.5f), maxScaler = gDevScale * 2.f, scaler; for( scaler = minScaler ; scaler <= maxScaler ; scaler += 0.5f ) { if (!NodeAddSize(stretch, pixels, points, scaler, resX, resY, name, familyName, weightName, slant, setWidth, addStyle, spacing, charSetName, charSetInfo)) break; } if (scaler <= maxScaler) { continue; /* |NodeAddSize| returned an error in the loop above... */ } } else { if (!NodeAddSize(stretch, pixels, points, 1.0f, resX, resY, name, familyName, weightName, slant, setWidth, addStyle, spacing, charSetName, charSetInfo)) continue; } } } XFreeFontNames(list); #ifdef DEBUG_DUMP_TREE DumpTree(); #endif } static nsresult GetAllFontNames(void) { if (!gGlobalList) { // This may well expand further (families * sizes * styles?), but it's // only created once. gGlobalList = new nsFontNodeArray; if (!gGlobalList) { return NS_ERROR_OUT_OF_MEMORY; } /* Using "-*" instead of the full-qualified "-*-*-*-*-*-*-*-*-*-*-*-*-*-*" * because it's faster and "smarter" - see bug 34242 for details. */ GetFontNames("-*", PR_FALSE, PR_FALSE, gGlobalList); } return NS_OK; } static nsFontFamily* FindFamily(nsCString* aName) { nsCStringKey key(*aName); nsFontFamily* family = (nsFontFamily*) gFamilies->Get(&key); if (!family) { family = new nsFontFamily(); if (family) { char pattern[256]; PR_snprintf(pattern, sizeof(pattern), "-*-%s-*-*-*-*-*-*-*-*-*-*-*-*", aName->get()); GetFontNames(pattern, PR_TRUE, gForceOutlineScaledFonts, &family->mNodes); gFamilies->Put(&key, family); } } return family; } nsresult nsFontMetricsGTK::FamilyExists(nsIDeviceContext *aDevice, const nsString& aName) { if (!gInitialized) { nsresult res = InitGlobals(aDevice); if (NS_FAILED(res)) return res; } if (!IsASCIIFontName(aName)) { return NS_ERROR_FAILURE; } nsCAutoString name; name.AssignWithConversion(aName.get()); ToLowerCase(name); nsFontFamily* family = FindFamily(&name); if (family && family->mNodes.Count()) { return NS_OK; } return NS_ERROR_FAILURE; } PRUint32 nsFontMetricsGTK::GetHints(void) { PRUint32 result = 0; /* We can't enable fast text measuring (yet) on platforms which * force natural alignment of datatypes (see * http://bugzilla.mozilla.org/show_bug.cgi?id=36146#c46) ... ;-( */ #ifndef CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT #if defined(__i386) #define CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT 1 #endif /* __i386 */ #endif /* !CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT */ static PRBool enable_fast_measure; static PRBool getenv_done = PR_FALSE; /* Check for the env vars "MOZILLA_GFX_ENABLE_FAST_MEASURE" and * "MOZILLA_GFX_DISABLE_FAST_MEASURE" to enable/disable fast text * measuring (for debugging the feature and doing regression tests). * This code will be removed one all issues around this new feature * have been fixed. */ if (!getenv_done) { #ifdef CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT enable_fast_measure = PR_TRUE; #else enable_fast_measure = PR_FALSE; #endif /* CPU_DOES_NOT_REQUIRE_NATURAL_ALIGNMENT */ if (PR_GetEnv("MOZILLA_GFX_ENABLE_FAST_MEASURE")) enable_fast_measure = PR_TRUE; if (PR_GetEnv("MOZILLA_GFX_DISABLE_FAST_MEASURE")) enable_fast_measure = PR_FALSE; getenv_done = PR_TRUE; } if (enable_fast_measure) { // We have GetTextDimensions() result |= NS_RENDERING_HINT_FAST_MEASURE; } return result; } // // convert a FFRE (Foundry-Family-Registry-Encoding) To XLFD Pattern // static void FFREToXLFDPattern(nsACString &aFFREName, nsACString &oPattern) { PRInt32 charsetHyphen; oPattern.Append("-"); oPattern.Append(aFFREName); /* Search for the 3rd appearance of '-' */ charsetHyphen = oPattern.FindChar('-'); charsetHyphen = oPattern.FindChar('-', charsetHyphen + 1); charsetHyphen = oPattern.FindChar('-', charsetHyphen + 1); oPattern.Insert("-*-*-*-*-*-*-*-*-*-*", charsetHyphen); } // // substitute the charset in a FFRE (Foundry-Family-Registry-Encoding) // static void FFRESubstituteCharset(nsACString &aFFREName, const char *aReplacementCharset) { PRInt32 charsetHyphen = aFFREName.FindChar('-'); charsetHyphen = aFFREName.FindChar('-', charsetHyphen + 1); aFFREName.Truncate(charsetHyphen+1); aFFREName.Append(aReplacementCharset); } // // substitute the encoding in a FFRE (Foundry-Family-Registry-Encoding) // static void FFRESubstituteEncoding(nsACString &aFFREName, const char *aReplacementEncoding) { PRInt32 encodingHyphen; /* Search for the 3rd apperance of '-' */ encodingHyphen = aFFREName.FindChar('-'); encodingHyphen = aFFREName.FindChar('-', encodingHyphen + 1); encodingHyphen = aFFREName.FindChar('-', encodingHyphen + 1); aFFREName.Truncate(encodingHyphen+1); aFFREName.Append(aReplacementEncoding); } nsFontGTK* nsFontMetricsGTK::TryNodes(nsACString &aFFREName, PRUint32 aChar) { const nsPromiseFlatCString& FFREName = PromiseFlatCString(aFFREName); FIND_FONT_PRINTF((" TryNodes aFFREName = %s", FFREName.get())); nsCStringKey key(FFREName); PRBool anyFoundry = (FFREName.First() == '*'); nsFontNodeArray* nodes = (nsFontNodeArray*) gCachedFFRESearches->Get(&key); if (!nodes) { nsCAutoString pattern; FFREToXLFDPattern(aFFREName, pattern); nodes = new nsFontNodeArray; if (!nodes) return nsnull; GetFontNames(pattern.get(), anyFoundry, gForceOutlineScaledFonts, nodes); gCachedFFRESearches->Put(&key, nodes); } int i, cnt = nodes->Count(); for (i=0; iGetElement(i); nsFontGTK * font; font = SearchNode(node, aChar); if (font && font->SupportsChar(aChar)) return font; } return nsnull; } nsFontGTK* nsFontMetricsGTK::TryNode(nsCString* aName, PRUint32 aChar) { FIND_FONT_PRINTF((" TryNode aName = %s", (*aName).get())); // // check the specified font (foundry-family-registry-encoding) // if (aName->IsEmpty()) { return nsnull; } nsFontGTK* font; nsCStringKey key(*aName); nsFontNode* node = (nsFontNode*) gFFRENodes->Get(&key); if (!node) { nsCAutoString pattern; FFREToXLFDPattern(*aName, pattern); nsFontNodeArray nodes; GetFontNames(pattern.get(), PR_FALSE, gForceOutlineScaledFonts, &nodes); // no need to call gFFRENodes->Put() since GetFontNames already did if (nodes.Count() > 0) { // This assertion is not spurious; when searching for an FFRE // like -*-courier-iso8859-1 TryNodes should be called not TryNode NS_ASSERTION((nodes.Count() == 1), "unexpected number of nodes"); node = nodes.GetElement(0); } else { // add a dummy node to the hash table to avoid calling XListFonts again node = new nsFontNode(); if (!node) { return nsnull; } gFFRENodes->Put(&key, node); node->mDummy = 1; } } if (node) { font = SearchNode(node, aChar); if (font && font->SupportsChar(aChar)) return font; } // // do not check related sub-planes for UserDefined // if (mIsUserDefined) { return nsnull; } // // check related sub-planes (wild-card the encoding) // nsCAutoString ffreName(*aName); FFRESubstituteEncoding(ffreName, "*"); FIND_FONT_PRINTF((" TrySubplane: wild-card the encoding")); font = TryNodes(ffreName, aChar); if (font) { NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char"); return font; } return nsnull; } nsFontGTK* nsFontMetricsGTK::TryLangGroup(nsIAtom* aLangGroup, nsCString* aName, PRUint32 aChar) { // // for this family check related registry-encoding (for the language) // FIND_FONT_PRINTF((" TryLangGroup lang group = %s, aName = %s", atomToName(aLangGroup), (*aName).get())); if (aName->IsEmpty()) { return nsnull; } nsFontGTK* font = FindLangGroupFont(aLangGroup, aChar, aName); return font; } nsFontGTK* nsFontMetricsGTK::TryFamily(nsCString* aName, PRUint32 aChar) { // // check the patterh "*-familyname-registry-encoding" for language // nsFontFamily* family = FindFamily(aName); if (family) { // try family name of language group first nsCAutoString FFREName("*-"); FFREName.Append(*aName); FFREName.Append("-*-*"); FIND_FONT_PRINTF((" TryFamily %s with lang group = %s", (*aName).get(), atomToName(mLangGroup))); nsFontGTK* font = TryLangGroup(mLangGroup, &FFREName, aChar); if(font) { return font; } // then try family name regardless of language group nsFontNodeArray* nodes = &family->mNodes; PRInt32 n = nodes->Count(); for (PRInt32 i = 0; i < n; i++) { FIND_FONT_PRINTF((" TryFamily %s", nodes->GetElement(i)->mName.get())); nsFontGTK* font = SearchNode(nodes->GetElement(i), aChar); if (font && font->SupportsChar(aChar)) { return font; } } } return nsnull; } nsFontGTK* nsFontMetricsGTK::TryAliases(nsCString* aAlias, PRUint32 aChar) { nsCStringKey key(*aAlias); char* name = (char*) gAliases->Get(&key); if (name) { nsCAutoString str(name); return TryFamily(&str, aChar); } return nsnull; } nsFontGTK* nsFontMetricsGTK::FindUserDefinedFont(PRUint32 aChar) { if (mIsUserDefined) { FIND_FONT_PRINTF((" FindUserDefinedFont")); nsFontGTK* font = TryNode(&mUserDefined, aChar); mIsUserDefined = PR_FALSE; if (font) { NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char"); return font; } } return nsnull; } nsFontGTK* nsFontMetricsGTK::FindStyleSheetSpecificFont(PRUint32 aChar) { FIND_FONT_PRINTF((" FindStyleSheetSpecificFont")); while (mFontsIndex < mFonts.Count()) { if (mFontIsGeneric[mFontsIndex]) { return nsnull; } nsCString* familyName = mFonts.CStringAt(mFontsIndex); /* * count hyphens * XXX It might be good to try to pre-cache this information instead * XXX of recalculating it on every font access! */ const char* str = familyName->get(); FIND_FONT_PRINTF((" familyName = %s", str)); PRUint32 len = familyName->Length(); int hyphens = 0; for (PRUint32 i = 0; i < len; i++) { if (str[i] == '-') { hyphens++; } } /* * if there are 3 hyphens, the name is in FFRE form * (foundry-family-registry-encoding) * ie: something like this: * * adobe-times-iso8859-1 * * otherwise it is something like * * times new roman */ nsFontGTK* font; if (hyphens == 3) { font = TryNode(familyName, aChar); if (font) { NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char"); return font; } } else { font = TryFamily(familyName, aChar); if (font) { NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char"); return font; } font = TryAliases(familyName, aChar); if (font) { NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char"); return font; } } // bug 42917: increment only after all of the above fails mFontsIndex++; } return nsnull; } static void PrefEnumCallback(const char* aName, void* aClosure) { nsFontSearch* s = (nsFontSearch*) aClosure; if (s->mFont) { NS_ASSERTION(s->mFont->SupportsChar(s->mChar), "font supposed to support this char"); return; } nsXPIDLCString value; gPref->CopyCharPref(aName, getter_Copies(value)); nsCAutoString name; if (value.get()) { name = value; FIND_FONT_PRINTF((" PrefEnumCallback")); s->mFont = s->mMetrics->TryNode(&name, s->mChar); if (s->mFont) { NS_ASSERTION(s->mFont->SupportsChar(s->mChar), "font supposed to support this char"); return; } s->mFont = s->mMetrics->TryLangGroup(s->mMetrics->mLangGroup, &name, s->mChar); if (s->mFont) { NS_ASSERTION(s->mFont->SupportsChar(s->mChar), "font supposed to support this char"); return; } } gPref->CopyDefaultCharPref(aName, getter_Copies(value)); if (value.get() && (!name.Equals(value))) { name = value; FIND_FONT_PRINTF((" PrefEnumCallback:default")); s->mFont = s->mMetrics->TryNode(&name, s->mChar); if (s->mFont) { NS_ASSERTION(s->mFont->SupportsChar(s->mChar), "font supposed to support this char"); return; } s->mFont = s->mMetrics->TryLangGroup(s->mMetrics->mLangGroup, &name, s->mChar); NS_ASSERTION(s->mFont ? s->mFont->SupportsChar(s->mChar) : 1, "font supposed to support this char"); } } nsFontGTK* nsFontMetricsGTK::FindStyleSheetGenericFont(PRUint32 aChar) { FIND_FONT_PRINTF((" FindStyleSheetGenericFont")); nsFontGTK* font; if (mTriedAllGenerics) { return nsnull; } // // find font based on document's lang group // font = FindLangGroupPrefFont(mLangGroup, aChar); if (font) { NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char"); return font; } // // Asian smart quote glyphs are much too large for western // documents so if this is a single byte document add a // special "font" to tranliterate those chars rather than // possibly find them in double byte fonts // // (risk management: since we are close to a ship point we have a // control (gAllowDoubleByteSpecialChars) to disable this new feature) // if (gAllowDoubleByteSpecialChars) { if (!mDocConverterType) { if (mLoadedFontsCount) { FIND_FONT_PRINTF(("just use the 1st converter type")); nsFontGTK* first_font = mLoadedFonts[0]; if (first_font->mCharSetInfo) { mDocConverterType = first_font->mCharSetInfo->Convert; if (mDocConverterType == SingleByteConvert ) { FIND_FONT_PRINTF(("single byte converter for %s", atomToName(mLangGroup))); } else { FIND_FONT_PRINTF(("double byte converter for %s", atomToName(mLangGroup))); } } } if (!mDocConverterType) { mDocConverterType = SingleByteConvert; } if (mDocConverterType == SingleByteConvert) { // before we put in the transliterator to disable double byte special chars // add the x-western font before the early transliterator // to get the EURO sign (hack) nsFontGTK* western_font = nsnull; if (mLangGroup != gWesternLocale) western_font = FindLangGroupPrefFont(gWesternLocale, aChar); // add the symbol font before the early transliterator // to get the bullet (hack) nsCAutoString symbol_ffre("*-symbol-adobe-fontspecific"); nsFontGTK* symbol_font = TryNodes(symbol_ffre, 0x0030); // Add the Adobe Euro fonts before the early transliterator nsCAutoString euro_ffre("*-euro*-adobe-fontspecific"); nsFontGTK* euro_font = TryNodes(euro_ffre, 0x20AC); // add the early transliterator // to avoid getting Japanese "special chars" such as smart // since they are very oversized compared to western fonts nsFontGTK* sub_font = FindSubstituteFont(aChar); NS_ASSERTION(sub_font, "failed to get a special chars substitute font"); if (sub_font) { sub_font->mCCMap = gDoubleByteSpecialCharsCCMap; AddToLoadedFontsList(sub_font); } if (western_font && CCMAP_HAS_CHAR_EXT(western_font->mCCMap, aChar)) { return western_font; } else if (symbol_font && CCMAP_HAS_CHAR_EXT(symbol_font->mCCMap, aChar)) { return symbol_font; } else if (euro_font && CCMAP_HAS_CHAR_EXT(euro_font->mCCMap, aChar)) { return euro_font; } else if (sub_font && CCMAP_HAS_CHAR_EXT(sub_font->mCCMap, aChar)) { FIND_FONT_PRINTF((" transliterate special chars for single byte docs")); return sub_font; } } } } // // find font based on user's locale's lang group // if different from documents locale if (gUsersLocale != mLangGroup) { FIND_FONT_PRINTF((" find font based on user's locale's lang group")); font = FindLangGroupPrefFont(gUsersLocale, aChar); if (font) { NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char"); return font; } } // // Search all font prefs for generic // nsCAutoString prefix("font.name."); prefix.Append(*mGeneric); nsFontSearch search = { this, aChar, nsnull }; FIND_FONT_PRINTF((" Search all font prefs for generic")); gPref->EnumerateChildren(prefix.get(), PrefEnumCallback, &search); if (search.mFont) { NS_ASSERTION(search.mFont->SupportsChar(aChar), "font supposed to support this char"); return search.mFont; } // // Search all font prefs // // find based on all prefs (no generic part (eg: sans-serif)) nsCAutoString allPrefs("font.name."); search.mFont = nsnull; FIND_FONT_PRINTF((" Search all font prefs")); gPref->EnumerateChildren(allPrefs.get(), PrefEnumCallback, &search); if (search.mFont) { NS_ASSERTION(search.mFont->SupportsChar(aChar), "font supposed to support this char"); return search.mFont; } mTriedAllGenerics = 1; return nsnull; } nsFontGTK* nsFontMetricsGTK::FindAnyFont(PRUint32 aChar) { FIND_FONT_PRINTF((" FindAnyFont")); // XXX If we get to this point, that means that we have exhausted all the // families in the lists. Maybe we should try a list of fonts that are // specific to the vendor of the X server here. Because XListFonts for the // whole list is very expensive on some Unixes. /* * Try all the fonts on the system. */ nsresult res = GetAllFontNames(); if (NS_FAILED(res)) { return nsnull; } PRInt32 n = gGlobalList->Count(); for (PRInt32 i = 0; i < n; i++) { nsFontGTK* font = SearchNode(gGlobalList->GetElement(i), aChar); if (font && font->SupportsChar(aChar)) { // XXX We should probably write this family name out to disk, so that // we can use it next time. I.e. prefs file or something. return font; } } // future work: // to properly support the substitute font we // need to indicate here that all fonts have been tried return nsnull; } nsFontGTK* nsFontMetricsGTK::FindSubstituteFont(PRUint32 aChar) { if (!mSubstituteFont) { for (int i = 0; i < mLoadedFontsCount; i++) { if (CCMAP_HAS_CHAR_EXT(mLoadedFonts[i]->mCCMap, 'a')) { mSubstituteFont = new nsFontGTKSubstitute(mLoadedFonts[i]); break; } } // Currently the substitute font does not have a glyph map. // This means that even if we have already checked all fonts // for a particular character the mLoadedFonts will not know it. // Thus we reparse *all* font glyph maps every time we see // a character that ends up using a substitute font. // future work: // create an empty mCCMap and every time we determine a // character will get its "glyph" from the substitute font // mark that character in the mCCMap. } // mark the mCCMap to indicate that this character has a "glyph" // If we know that mLoadedFonts has every font's glyph map loaded // then we can now set all the bit in the substitute font's glyph map // and thus direct all umapped characters to the substitute // font (without the font search). // if tried all glyphs { // create a substitute font with all bits set // set all bits in mCCMap // } return mSubstituteFont; } // // find font based on lang group // nsFontGTK* nsFontMetricsGTK::FindLangGroupPrefFont(nsIAtom* aLangGroup, PRUint32 aChar) { nsFontGTK* font; // // get the font specified in prefs // nsCAutoString prefix("font.name."); prefix.Append(*mGeneric); if (aLangGroup) { // check user set pref nsCAutoString pref = prefix; pref.Append(char('.')); const char* langGroup = nsnull; aLangGroup->GetUTF8String(&langGroup); pref.Append(langGroup); nsXPIDLCString value; gPref->CopyCharPref(pref.get(), getter_Copies(value)); nsCAutoString str; nsCAutoString str_user; if (value.get()) { str = value.get(); str_user = value.get(); FIND_FONT_PRINTF((" user pref %s = %s", pref.get(), str.get())); font = TryNode(&str, aChar); if (font) { NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char"); return font; } font = TryLangGroup(aLangGroup, &str, aChar); if (font) { NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char"); return font; } } // check factory set pref gPref->CopyDefaultCharPref(pref.get(), getter_Copies(value)); if (value.get()) { str = value.get(); // check if we already tried this name if (str != str_user) { FIND_FONT_PRINTF((" default pref %s = %s", pref.get(), str.get())); font = TryNode(&str, aChar); if (font) { NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char"); return font; } font = TryLangGroup(aLangGroup, &str, aChar); if (font) { NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char"); return font; } } } } // // find any style font based on lang group // FIND_FONT_PRINTF((" find font based on lang group")); font = FindLangGroupFont(aLangGroup, aChar, nsnull); if (font) { NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char"); return font; } return nsnull; } nsFontGTK* nsFontMetricsGTK::FindLangGroupFont(nsIAtom* aLangGroup, PRUint32 aChar, nsCString *aName) { nsFontGTK* font; FIND_FONT_PRINTF((" lang group = %s", atomToName(aLangGroup))); // scan gCharSetMap for encodings with matching lang groups nsFontCharSetMap* charSetMap; for (charSetMap=gCharSetMap; charSetMap->mName; charSetMap++) { nsFontLangGroup* fontLangGroup = charSetMap->mFontLangGroup; if ((!fontLangGroup) || (!fontLangGroup->mFontLangGroupName)) { continue; } if (!charSetMap->mInfo->mLangGroup) { SetCharsetLangGroup(charSetMap->mInfo); } if (!fontLangGroup->mFontLangGroupAtom) { SetFontLangGroupInfo(charSetMap); } // if font's langGroup is different from requested langGroup, continue. // An exception is that font's langGroup ZHTWHK is regarded as matching // both ZHTW and ZHHK (Freetype2 and Solaris). if ((aLangGroup != fontLangGroup->mFontLangGroupAtom) && (aLangGroup != charSetMap->mInfo->mLangGroup) && (fontLangGroup->mFontLangGroupAtom != gZHTWHK || (aLangGroup != gZHHK && aLangGroup != gZHTW))) { continue; } // look for a font with this charset (registry-encoding) & char // nsCAutoString ffreName; if(aName) { // if aName was specified so call TryNode() not TryNodes() ffreName.Assign(*aName); FFRESubstituteCharset(ffreName, charSetMap->mName); FIND_FONT_PRINTF((" %s ffre = %s", charSetMap->mName, ffreName.get())); if(aName->First() == '*') { // called from TryFamily() font = TryNodes(ffreName, aChar); } else { font = TryNode(&ffreName, aChar); } NS_ASSERTION(font ? font->SupportsChar(aChar) : 1, "font supposed to support this char"); } else { // no name was specified so call TryNodes() for this charset ffreName.Assign("*-*-*-*"); FFRESubstituteCharset(ffreName, charSetMap->mName); FIND_FONT_PRINTF((" %s ffre = %s", charSetMap->mName, ffreName.get())); font = TryNodes(ffreName, aChar); NS_ASSERTION(font ? font->SupportsChar(aChar) : 1, "font supposed to support this char"); } if (font) { NS_ASSERTION(font->SupportsChar(aChar), "font supposed to support this char"); return font; } } return nsnull; } /* * First we try to load the user-defined font, if the user-defined charset * has been selected in the menu. * * Next, we try the fonts listed in the font-family property (FindStyleSheetSpecificFont). * * Next, we try any CSS generic font encountered in the font-family list and * all of the fonts specified by the user for the generic (FindStyleSheetGenericFont). * * Next, we try all of the fonts on the system (FindAnyFont). This is * expensive on some Unixes. * * Finally, we try to create a substitute font that offers substitute glyphs * for the characters (FindSubstituteFont). */ nsFontGTK* nsFontMetricsGTK::FindFont(PRUint32 aChar) { FIND_FONT_PRINTF(("\nFindFont(%c/0x%04x)", aChar, aChar)); // If this is is the 'unknown' char (ie: converter could not // convert it) there is no sense in searching any further for // a font. Just returing mWesternFont if (aChar == UCS2_NOMAPPING) { FIND_FONT_PRINTF((" ignore the 'UCS2_NOMAPPING' character, return mWesternFont")); return mWesternFont; } nsFontGTK* font = FindUserDefinedFont(aChar); if (!font) { font = FindStyleSheetSpecificFont(aChar); if (!font) { font = FindStyleSheetGenericFont(aChar); if (!font) { font = FindAnyFont(aChar); if (!font) { font = FindSubstituteFont(aChar); } } } } #ifdef NS_FONT_DEBUG_CALL_TRACE if (gFontDebug & NS_FONT_DEBUG_CALL_TRACE) { printf("FindFont(%04X)[", aChar); for (PRInt32 i = 0; i < mFonts.Count(); i++) { printf("%s, ", mFonts.CStringAt(i)->get()); } printf("]\nreturns "); if (font) { printf("%s\n", font->mName ? font->mName : "(substitute)"); } else { printf("NULL\n"); } } #endif return font; } // The Font Enumerator nsFontEnumeratorGTK::nsFontEnumeratorGTK() { } NS_IMPL_ISUPPORTS1(nsFontEnumeratorGTK, nsIFontEnumerator) typedef struct EnumerateNodeInfo { PRUnichar** mArray; int mIndex; nsIAtom* mLangGroup; } EnumerateNodeInfo; static PRIntn EnumerateNode(void* aElement, void* aData) { nsFontNode* node = (nsFontNode*) aElement; EnumerateNodeInfo* info = (EnumerateNodeInfo*) aData; if (info->mLangGroup != gUserDefined) { if (node->mCharSetInfo == &Unknown) { return PR_TRUE; // continue } else if (info->mLangGroup != gUnicode) { // if font's langGroup is different from requested langGroup, continue. // An exception is that font's langGroup ZHTWHK is regarded as matching // both ZHTW and ZHHK (Freetype2 and Solaris). if (node->mCharSetInfo->mLangGroup != info->mLangGroup && (node->mCharSetInfo->mLangGroup != gZHTWHK || (info->mLangGroup != gZHHK && info->mLangGroup != gZHTW))) { return PR_TRUE; // continue } } // else { // if (lang == add-style-field) { // consider it part of the lang group // } // else if (a Unicode font reports its lang group) { // consider it part of the lang group // } // else if (lang's ranges in list of ranges) { // consider it part of the lang group // // Note: at present we have no way to do this test but we // // could in the future and this would be the place to enable // // to make the font show up in the preferences dialog // } // } } PRUnichar** array = info->mArray; int j = info->mIndex; PRUnichar* str = ToNewUnicode(node->mName); if (!str) { for (j = j - 1; j >= 0; j--) { nsMemory::Free(array[j]); } info->mIndex = 0; return PR_FALSE; // stop } array[j] = str; info->mIndex++; return PR_TRUE; // continue } PR_BEGIN_EXTERN_C static int CompareFontNames(const void* aArg1, const void* aArg2, void* aClosure) { const PRUnichar* str1 = *((const PRUnichar**) aArg1); const PRUnichar* str2 = *((const PRUnichar**) aArg2); // XXX add nsICollation stuff return nsCRT::strcmp(str1, str2); } PR_END_EXTERN_C static nsresult EnumFonts(nsIAtom* aLangGroup, const char* aGeneric, PRUint32* aCount, PRUnichar*** aResult) { nsresult res = GetAllFontNames(); if (NS_FAILED(res)) { return res; } PRUnichar** array = (PRUnichar**) nsMemory::Alloc(gGlobalList->Count() * sizeof(PRUnichar*)); if (!array) { return NS_ERROR_OUT_OF_MEMORY; } EnumerateNodeInfo info = { array, 0, aLangGroup }; if (!gGlobalList->EnumerateForwards(EnumerateNode, &info)) { nsMemory::Free(array); return NS_ERROR_OUT_OF_MEMORY; } NS_QuickSort(array, info.mIndex, sizeof(PRUnichar*), CompareFontNames, nsnull); *aCount = info.mIndex; if (*aCount) { *aResult = array; } else { nsMemory::Free(array); } return NS_OK; } NS_IMETHODIMP nsFontEnumeratorGTK::EnumerateAllFonts(PRUint32* aCount, PRUnichar*** aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = nsnull; NS_ENSURE_ARG_POINTER(aCount); *aCount = 0; return EnumFonts(nsnull, nsnull, aCount, aResult); } NS_IMETHODIMP nsFontEnumeratorGTK::EnumerateFonts(const char* aLangGroup, const char* aGeneric, PRUint32* aCount, PRUnichar*** aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = nsnull; NS_ENSURE_ARG_POINTER(aCount); *aCount = 0; // aLangGroup=null or "" means any (i.e., don't care) // aGeneric=null or "" means any (i.e, don't care) nsCOMPtr langGroup; if (aLangGroup && *aLangGroup) langGroup = do_GetAtom(aLangGroup); const char* generic = nsnull; if (aGeneric && *aGeneric) generic = aGeneric; // XXX still need to implement aLangGroup and aGeneric return EnumFonts(langGroup, generic, aCount, aResult); } NS_IMETHODIMP nsFontEnumeratorGTK::HaveFontFor(const char* aLangGroup, PRBool* aResult) { NS_ENSURE_ARG_POINTER(aResult); *aResult = PR_FALSE; NS_ENSURE_ARG_POINTER(aLangGroup); *aResult = PR_TRUE; // always return true for now. // Finish me - ftang return NS_OK; } NS_IMETHODIMP nsFontEnumeratorGTK::GetDefaultFont(const char *aLangGroup, const char *aGeneric, PRUnichar **aResult) { // aLangGroup=null or "" means any (i.e., don't care) // aGeneric=null or "" means any (i.e, don't care) NS_ENSURE_ARG_POINTER(aResult); *aResult = nsnull; return NS_OK; } NS_IMETHODIMP nsFontEnumeratorGTK::UpdateFontList(PRBool *updateFontList) { *updateFontList = PR_FALSE; // always return false for now return NS_OK; } nsFontCharSetMap * GetCharSetMap(const char *aCharSetName) { nsCStringKey charSetKey(aCharSetName); nsFontCharSetMap* charSetMap = (nsFontCharSetMap*) gCharSetMaps->Get(&charSetKey); if (!charSetMap) charSetMap = gNoneCharSetMap; return charSetMap; } #ifdef MOZ_ENABLE_FREETYPE2 void CharSetNameToCodeRangeBits(const char *aCharset, PRUint32 *aCodeRange1, PRUint32 *aCodeRange2) { nsFontCharSetMap *charSetMap = GetCharSetMap(aCharset); nsFontCharSetInfo* charSetInfo = charSetMap->mInfo; *aCodeRange1 = charSetInfo->mCodeRange1Bits; *aCodeRange2 = charSetInfo->mCodeRange2Bits; } #endif