//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=====================================================================================// #include #include "vgui_surfacelib/BitmapFont.h" #include "vgui_surfacelib/FontManager.h" #include #include // memdbgon must be the last include file in a .cpp file!!! #include static CFontManager s_FontManager; #ifdef WIN32 extern bool s_bSupportsUnicode; #endif #if !defined( _X360 ) #define MAX_INITIAL_FONTS 100 #else #define MAX_INITIAL_FONTS 1 #endif //----------------------------------------------------------------------------- // Purpose: singleton accessor //----------------------------------------------------------------------------- CFontManager &FontManager() { return s_FontManager; } //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- CFontManager::CFontManager() { // add a single empty font, to act as an invalid font handle 0 m_FontAmalgams.EnsureCapacity( MAX_INITIAL_FONTS ); m_FontAmalgams.AddToTail(); m_Win32Fonts.EnsureCapacity( MAX_INITIAL_FONTS ); #ifdef LINUX FT_Error error = FT_Init_FreeType( &library ); if ( error ) Error( "Unable to initalize freetype library, is it installed?" ); m_pFontDataHelper = NULL; #endif // setup our text locale setlocale( LC_CTYPE, "" ); setlocale( LC_TIME, "" ); setlocale( LC_COLLATE, "" ); setlocale( LC_MONETARY, "" ); m_pFileSystem = NULL; m_pMaterialSystem = NULL; } //----------------------------------------------------------------------------- // Purpose: language setting for font fallbacks //----------------------------------------------------------------------------- void CFontManager::SetLanguage(const char *language) { Q_strncpy(m_szLanguage, language, sizeof(m_szLanguage)); } //----------------------------------------------------------------------------- // Purpose: Destructor //----------------------------------------------------------------------------- CFontManager::~CFontManager() { ClearAllFonts(); m_FontAmalgams.RemoveAll(); #ifdef LINUX FT_Done_FreeType( library ); #endif } //----------------------------------------------------------------------------- // Purpose: frees the fonts //----------------------------------------------------------------------------- void CFontManager::ClearAllFonts() { // free the fonts for (int i = 0; i < m_Win32Fonts.Count(); i++) { delete m_Win32Fonts[i]; } m_Win32Fonts.RemoveAll(); for (int i = 0; i < m_FontAmalgams.Count(); i++) { m_FontAmalgams[i].RemoveAll(); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- vgui::HFont CFontManager::CreateFont() { int i = m_FontAmalgams.AddToTail(); return i; } //----------------------------------------------------------------------------- // Purpose: Sets the valid glyph ranges for a font created by CreateFont() //----------------------------------------------------------------------------- bool CFontManager::SetFontGlyphSet(HFont font, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags) { return SetFontGlyphSet( font, windowsFontName, tall, weight, blur, scanlines, flags, 0, 0); } //----------------------------------------------------------------------------- // Purpose: Sets the valid glyph ranges for a font created by CreateFont() //----------------------------------------------------------------------------- bool CFontManager::SetFontGlyphSet(HFont font, const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags, int nRangeMin, int nRangeMax) { // ignore all but the first font added // need to rev vgui versions and change the name of this function if ( m_FontAmalgams[font].GetCount() > 0 ) { // clear any existing fonts m_FontAmalgams[font].RemoveAll(); } bool bForceSingleFontForXbox = false; if ( IsX360() ) { // discovered xbox only allows glyphs from these languages from the foreign fallback font // prefer to have the entire range of chars from the font so UI doesn't suffer from glyph disparity if ( !V_stricmp( windowsFontName, "toolbox" ) ) { // only the toolbox font is allowed to pass } else { if ( !V_stricmp( m_szLanguage, "polish" ) || !V_stricmp( m_szLanguage, "russian" ) || !V_stricmp( m_szLanguage, "japanese" ) || !V_stricmp( m_szLanguage, "korean" ) || !V_stricmp( m_szLanguage, "portuguese" ) || !V_stricmp( m_szLanguage, "schinese" ) || !V_stricmp( m_szLanguage, "tchinese" ) ) { windowsFontName = GetForeignFallbackFontName(); bForceSingleFontForXbox = true; } } } font_t *winFont = CreateOrFindWin32Font( windowsFontName, tall, weight, blur, scanlines, flags ); // cycle until valid english/extended font support has been created do { // add to the amalgam if ( bForceSingleFontForXbox || IsFontForeignLanguageCapable( windowsFontName ) ) { if ( winFont ) { // font supports the full range of characters m_FontAmalgams[font].AddFont( winFont, 0x0000, 0xFFFF ); return true; } } else { // font cannot provide glyphs and just supports the normal range // redirect to a font that can supply glyps const char *localizedFontName = GetForeignFallbackFontName(); if ( winFont && !stricmp( localizedFontName, windowsFontName ) ) { // it's the same font and can support the full range m_FontAmalgams[font].AddFont( winFont, 0x0000, 0xFFFF ); return true; } // create the extended support font font_t *pExtendedFont = CreateOrFindWin32Font( localizedFontName, tall, weight, blur, scanlines, flags ); if ( winFont && pExtendedFont ) { // use the normal font for english characters, and the extended font for the rest int nMin = 0x0000, nMax = 0x00FF; // did we specify a range? if ( nRangeMin > 0 || nRangeMax > 0 ) { nMin = nRangeMin; nMax = nRangeMax; // make sure they're in the correct order if ( nMin > nMax ) { int nTemp = nMin; nMin = nMax; nMax = nTemp; } } if ( nMin > 0 ) { m_FontAmalgams[font].AddFont( pExtendedFont, 0x0000, nMin - 1 ); } m_FontAmalgams[font].AddFont( winFont, nMin, nMax ); if ( nMax < 0xFFFF ) { m_FontAmalgams[font].AddFont( pExtendedFont, nMax + 1, 0xFFFF ); } return true; } else if ( pExtendedFont ) { // the normal font failed to create // just use the extended font for the full range m_FontAmalgams[font].AddFont( pExtendedFont, 0x0000, 0xFFFF ); return true; } } // no valid font has been created, so fallback to a different font and try again } while ( NULL != ( windowsFontName = GetFallbackFontName( windowsFontName ) ) ); // nothing successfully created return false; } //----------------------------------------------------------------------------- // Purpose: adds glyphs to a font created by CreateFont() //----------------------------------------------------------------------------- bool CFontManager::SetBitmapFontGlyphSet(HFont font, const char *windowsFontName, float scalex, float scaley, int flags) { if ( m_FontAmalgams[font].GetCount() > 0 ) { // clear any existing fonts m_FontAmalgams[font].RemoveAll(); } CBitmapFont *winFont = CreateOrFindBitmapFont( windowsFontName, scalex, scaley, flags ); if ( winFont ) { // bitmap fonts are only 0-255 m_FontAmalgams[font].AddFont( winFont, 0x0000, 0x00FF ); return true; } // nothing successfully created return false; } //----------------------------------------------------------------------------- // Purpose: Creates a new win32 font, or reuses one if possible //----------------------------------------------------------------------------- font_t *CFontManager::CreateOrFindWin32Font(const char *windowsFontName, int tall, int weight, int blur, int scanlines, int flags) { // see if we already have the win32 font font_t *winFont = NULL; int i; for (i = 0; i < m_Win32Fonts.Count(); i++) { if (m_Win32Fonts[i]->IsEqualTo(windowsFontName, tall, weight, blur, scanlines, flags)) { winFont = m_Win32Fonts[i]; break; } } // create the new win32font if we didn't find it if (!winFont) { MEM_ALLOC_CREDIT(); i = m_Win32Fonts.AddToTail(); m_Win32Fonts[i] = NULL; #ifdef LINUX int memSize = 0; void *pchFontData = m_pFontDataHelper( windowsFontName, memSize, NULL ); if( !pchFontData ) { // If we didn't find the font data in the font cache, then get the font filename... char *filename = CLinuxFont::GetFontFileName( windowsFontName, flags ); if( filename ) { // ... and try to add it to the font cache. pchFontData = m_pFontDataHelper( windowsFontName, memSize, filename ); free( filename ); } } if ( pchFontData ) { m_Win32Fonts[i] = new font_t(); if (m_Win32Fonts[i]->CreateFromMemory( windowsFontName, pchFontData, memSize, tall, weight, blur, scanlines, flags)) { // add to the list winFont = m_Win32Fonts[i]; } } if( !winFont ) { // failed to create, remove if ( m_Win32Fonts[i] ) delete m_Win32Fonts[i]; m_Win32Fonts.Remove(i); return NULL; } #else m_Win32Fonts[i] = new font_t(); if (m_Win32Fonts[i]->Create(windowsFontName, tall, weight, blur, scanlines, flags)) { // add to the list winFont = m_Win32Fonts[i]; } else { // failed to create, remove delete m_Win32Fonts[i]; m_Win32Fonts.Remove(i); return NULL; } #endif } return winFont; } //----------------------------------------------------------------------------- // Purpose: Creates a new win32 font, or reuses one if possible //----------------------------------------------------------------------------- CBitmapFont *CFontManager::CreateOrFindBitmapFont(const char *windowsFontName, float scalex, float scaley, int flags) { // see if we already have the font CBitmapFont *winFont = NULL; int i; for ( i = 0; i < m_Win32Fonts.Count(); i++ ) { font_t *font = m_Win32Fonts[i]; // Only looking for bitmap fonts int testflags = font->GetFlags(); if ( !( testflags & vgui::ISurface::FONTFLAG_BITMAP ) ) { continue; } CBitmapFont *bitmapFont = reinterpret_cast< CBitmapFont* >( font ); if ( bitmapFont->IsEqualTo( windowsFontName, scalex, scaley, flags ) ) { winFont = bitmapFont; break; } } // create the font if we didn't find it if ( !winFont ) { MEM_ALLOC_CREDIT(); i = m_Win32Fonts.AddToTail(); CBitmapFont *bitmapFont = new CBitmapFont(); if ( bitmapFont->Create( windowsFontName, scalex, scaley, flags ) ) { // add to the list m_Win32Fonts[i] = bitmapFont; winFont = bitmapFont; } else { // failed to create, remove delete bitmapFont; m_Win32Fonts.Remove(i); return NULL; } } return winFont; } //----------------------------------------------------------------------------- // Purpose: sets the scale of a bitmap font //----------------------------------------------------------------------------- void CFontManager::SetFontScale(vgui::HFont font, float sx, float sy) { m_FontAmalgams[font].SetFontScale( sx, sy ); } const char *CFontManager::GetFontName( HFont font ) { // ignore the amalgam of disparate char ranges, assume the first font return m_FontAmalgams[font].GetFontName( 0 ); } const char *CFontManager::GetFontFamilyName( HFont font ) { return m_FontAmalgams[font].GetFontFamilyName( 0 ); } //----------------------------------------------------------------------------- // Purpose: gets the windows font for the particular font in the amalgam //----------------------------------------------------------------------------- font_t *CFontManager::GetFontForChar(vgui::HFont font, wchar_t wch) { return m_FontAmalgams[font].GetFontForChar(wch); } //----------------------------------------------------------------------------- // Purpose: returns the abc widths of a single character //----------------------------------------------------------------------------- void CFontManager::GetCharABCwide(HFont font, int ch, int &a, int &b, int &c) { if ( !m_FontAmalgams.IsValidIndex( font ) ) { a = b = c = 0; return; } font_t *winFont = m_FontAmalgams[font].GetFontForChar(ch); if (winFont) { winFont->GetCharABCWidths(ch, a, b, c); } else { // no font for this range, just use the default width a = c = 0; b = m_FontAmalgams[font].GetFontMaxWidth(); } } //----------------------------------------------------------------------------- // Purpose: returns the max height of a font //----------------------------------------------------------------------------- int CFontManager::GetFontTall(HFont font) { return m_FontAmalgams[font].GetFontHeight(); } //----------------------------------------------------------------------------- // Purpose: returns requested height of the font //----------------------------------------------------------------------------- int CFontManager::GetFontTallRequested(HFont font) { return m_FontAmalgams[font].GetFontHeightRequested(); } //----------------------------------------------------------------------------- // Purpose: returns the ascent of a font //----------------------------------------------------------------------------- int CFontManager::GetFontAscent(HFont font, wchar_t wch) { font_t *winFont = m_FontAmalgams[font].GetFontForChar(wch); if ( winFont ) { return winFont->GetAscent(); } else { return 0; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CFontManager::IsFontAdditive(HFont font) { return ( m_FontAmalgams[font].GetFlags( 0 ) & vgui::ISurface::FONTFLAG_ADDITIVE ) ? true : false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CFontManager::IsBitmapFont(HFont font) { // A FontAmalgam is either some number of non-bitmap fonts, or a single bitmap font - so this check is valid return ( m_FontAmalgams[font].GetFlags( 0 ) & vgui::ISurface::FONTFLAG_BITMAP ) ? true : false; } //----------------------------------------------------------------------------- // Purpose: returns the pixel width of a single character //----------------------------------------------------------------------------- int CFontManager::GetCharacterWidth(HFont font, int ch) { if ( !iswcntrl( ch ) ) { int a, b, c; GetCharABCwide(font, ch, a, b, c); return (a + b + c); } return 0; } //----------------------------------------------------------------------------- // Purpose: returns the area of a text string, including newlines //----------------------------------------------------------------------------- void CFontManager::GetTextSize(HFont font, const wchar_t *text, int &wide, int &tall) { wide = 0; tall = 0; if (!text) return; tall = GetFontTall(font); float xx = 0; char chBefore = 0; char chAfter = 0; for (int i = 0; ; i++) { wchar_t ch = text[i]; if (ch == 0) { break; } chAfter = text[i+1]; if (ch == '\n') { tall += GetFontTall(font); xx=0; } else if (ch == '&') { // underscore character, so skip } else { float flWide, flabcA, flabcC; GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA, flabcC ); xx += flWide; if (xx > wide) { wide = ceil(xx); } } chBefore = ch; } } // font validation functions struct FallbackFont_t { const char *font; const char *fallbackFont; }; #ifdef WIN32 const char *g_szValidAsianFonts[] = { "Marlett", NULL }; // list of how fonts fallback FallbackFont_t g_FallbackFonts[] = { { "Times New Roman", "Courier New" }, { "Courier New", "Courier" }, { "Verdana", "Arial" }, { "Trebuchet MS", "Arial" }, { "Tahoma", NULL }, { NULL, "Tahoma" }, // every other font falls back to this }; #elif defined(OSX) static const char *g_szValidAsianFonts[] = { "Apple Symbols", NULL }; // list of how fonts fallback FallbackFont_t g_FallbackFonts[] = { { "Marlett", "Apple Symbols" }, { "Lucida Console", "Lucida Grande" }, { "Tahoma", "Helvetica" }, { "Helvetica", "Monaco" }, { "Monaco", NULL }, { NULL, "Monaco" } // every other font falls back to this }; #elif defined(LINUX) static const char *g_szValidAsianFonts[] = { "Marlett", "WenQuanYi Zen Hei", "unifont", NULL }; // list of how fonts fallback FallbackFont_t g_FallbackFonts[] = { { "DejaVu Sans", NULL }, { NULL, "DejaVu Sans" }, // every other font falls back to this }; #elif defined(_PS3) // list of how fonts fallback FallbackFont_t g_FallbackFonts[] = { { NULL, "Tahoma" }, // every other font falls back to this }; #else #error #endif //----------------------------------------------------------------------------- // Purpose: returns true if the font is in the list of OK asian fonts //----------------------------------------------------------------------------- bool CFontManager::IsFontForeignLanguageCapable(const char *windowsFontName) { if ( IsX360() ) { return false; } for (int i = 0; g_szValidAsianFonts[i] != NULL; i++) { if (!stricmp(g_szValidAsianFonts[i], windowsFontName)) return true; } // typeface isn't supported by asian languages return false; } //----------------------------------------------------------------------------- // Purpose: fallback fonts //----------------------------------------------------------------------------- const char *CFontManager::GetFallbackFontName(const char *windowsFontName) { int i; for ( i = 0; g_FallbackFonts[i].font != NULL; i++ ) { if (!stricmp(g_FallbackFonts[i].font, windowsFontName)) return g_FallbackFonts[i].fallbackFont; } // the ultimate fallback return g_FallbackFonts[i].fallbackFont; } //----------------------------------------------------------------------------- // Purpose: specialized fonts //----------------------------------------------------------------------------- const char *CFontManager::GetForeignFallbackFontName() { #ifdef WIN32 // tahoma has all the necessary characters for asian/russian languages for winXP/2K+ return "Tahoma"; #elif defined(OSX) return "Helvetica"; #elif defined(LINUX) return "WenQuanYi Zen Hei"; #elif defined(_PS3) return "Tahoma"; #else #error #endif } #if defined( _X360 ) bool CFontManager::GetCachedXUIMetrics( const char *pFontName, int tall, int style, XUIFontMetrics *pFontMetrics, XUICharMetrics charMetrics[256] ) { // linear lookup is good enough CUtlSymbol fontSymbol = pFontName; bool bFound = false; int i; for ( i = 0; i < m_XUIMetricCache.Count(); i++ ) { if ( m_XUIMetricCache[i].fontSymbol == fontSymbol && m_XUIMetricCache[i].tall == tall && m_XUIMetricCache[i].style == style ) { bFound = true; break; } } if ( !bFound ) { return false; } // get from the cache *pFontMetrics = m_XUIMetricCache[i].fontMetrics; V_memcpy( charMetrics, m_XUIMetricCache[i].charMetrics, 256 * sizeof( XUICharMetrics ) ); return true; } #endif #if defined( _X360 ) void CFontManager::SetCachedXUIMetrics( const char *pFontName, int tall, int style, XUIFontMetrics *pFontMetrics, XUICharMetrics charMetrics[256] ) { MEM_ALLOC_CREDIT(); int i = m_XUIMetricCache.AddToTail(); m_XUIMetricCache[i].fontSymbol = pFontName; m_XUIMetricCache[i].tall = tall; m_XUIMetricCache[i].style = style; m_XUIMetricCache[i].fontMetrics = *pFontMetrics; V_memcpy( m_XUIMetricCache[i].charMetrics, charMetrics, 256 * sizeof( XUICharMetrics ) ); } #endif void CFontManager::ClearTemporaryFontCache() { #if defined( _X360 ) COM_TimestampedLog( "ClearTemporaryFontCache(): Start" ); m_XUIMetricCache.Purge(); // many fonts are blindly precached by vgui and never used // font will re-open if glyph is actually requested for ( int i = 0; i < m_Win32Fonts.Count(); i++ ) { m_Win32Fonts[i]->CloseResource(); } COM_TimestampedLog( "ClearTemporaryFontCache(): Finish" ); #endif } //----------------------------------------------------------------------------- // Purpose: returns the max height of a font //----------------------------------------------------------------------------- bool CFontManager::GetFontUnderlined( HFont font ) { return m_FontAmalgams[font].GetUnderlined(); } void CFontManager::GetKernedCharWidth( vgui::HFont font, wchar_t ch, wchar_t chBefore, wchar_t chAfter, float &wide, float &flabcA, float &flabcC ) { wide = 0.0f; flabcA = 0.0f; Assert( font != vgui::INVALID_FONT ); if ( font == vgui::INVALID_FONT ) return; font_t *pFont = m_FontAmalgams[font].GetFontForChar(ch); if ( !pFont ) { // no font for this range, just use the default width flabcA = 0.0f; wide = m_FontAmalgams[font].GetFontMaxWidth(); return; } if ( m_FontAmalgams[font].GetFontForChar( chBefore ) != pFont ) chBefore = 0; if ( m_FontAmalgams[font].GetFontForChar( chAfter ) != pFont ) chAfter = 0; #if defined(LINUX) pFont->GetKernedCharWidth( ch, chBefore, chAfter, wide, flabcA, flabcC ); #else pFont->GetKernedCharWidth( ch, chBefore, chAfter, wide, flabcA ); #endif } #ifdef DBGFLAG_VALIDATE //----------------------------------------------------------------------------- // Purpose: Ensure that all of our internal structures are consistent, and // account for all memory that we've allocated. // Input: validator - Our global validator object // pchName - Our name (typically a member var in our container) //----------------------------------------------------------------------------- void CFontManager::Validate( CValidator &validator, char *pchName ) { validator.Push( "CFontManager", this, pchName ); ValidateObj( m_FontAmalgams ); for ( int iFont = 0; iFont < m_FontAmalgams.Count(); iFont++ ) { ValidateObj( m_FontAmalgams[iFont] ); } ValidateObj( m_Win32Fonts ); for ( int iWin32Font = 0; iWin32Font < m_Win32Fonts.Count(); iWin32Font++ ) { ValidatePtr( m_Win32Fonts[ iWin32Font ] ); } validator.Pop(); } #endif // DBGFLAG_VALIDATE