848 lines
25 KiB
C++
848 lines
25 KiB
C++
|
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
//=====================================================================================//
|
||
|
|
||
|
#include <locale.h>
|
||
|
#include "vgui_surfacelib/BitmapFont.h"
|
||
|
#include "vgui_surfacelib/fontmanager.h"
|
||
|
#include "convar.h"
|
||
|
#include <vgui/ISurface.h>
|
||
|
#include <tier0/dbg.h>
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include <tier0/memdbgon.h>
|
||
|
|
||
|
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?" );
|
||
|
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));
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CFontManager::GetLanguage()
|
||
|
{
|
||
|
return m_szLanguage;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Destructor
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CFontManager::~CFontManager()
|
||
|
{
|
||
|
ClearAllFonts();
|
||
|
#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();
|
||
|
|
||
|
m_FontAmalgams.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 bForceSingleFont = false;
|
||
|
if ( IsX360() )
|
||
|
{
|
||
|
//-----//
|
||
|
// 360 //
|
||
|
//-----//
|
||
|
|
||
|
// AV - The 360 must use the same size font for 0-255 and 256-0xFFFF regardless of the font since the
|
||
|
// fontAmalgam can only deal with a consistent font height for all fonts in a single amalgam. We
|
||
|
// could change this if we forced all fonts within a single amalgam to have the same height with
|
||
|
// the font baselines aligned, but even then the fonts wouldn't look great, because different
|
||
|
// fonts set to the same size don't necessarily have the same height visually. We need to revisit
|
||
|
// this before shipping l4d2 on the PC!
|
||
|
bForceSingleFont = true;
|
||
|
|
||
|
// 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" ) || !V_stricmp( windowsFontName, "courier new" ) )
|
||
|
{
|
||
|
// toolbox stays as-is
|
||
|
// courier new is an internal debug font, not part of customer UI, need it stay as is
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bool bUseFallback = false;
|
||
|
if ( !V_stricmp( m_szLanguage, "portuguese" ) ||
|
||
|
!V_stricmp( m_szLanguage, "polish" ) )
|
||
|
{
|
||
|
static ConVarRef mat_xbox_iswidescreen( "mat_xbox_iswidescreen" );
|
||
|
static ConVarRef mat_xbox_ishidef( "mat_xbox_ishidef" );
|
||
|
|
||
|
// we can support these languages with our desired fonts in hidef/widescreen modes only
|
||
|
// we must fallback to the more legible font in the lowdef or non-widescreen
|
||
|
bUseFallback = !( mat_xbox_iswidescreen.GetBool() && mat_xbox_ishidef.GetBool() );
|
||
|
}
|
||
|
|
||
|
if ( bUseFallback ||
|
||
|
!V_stricmp( m_szLanguage, "japanese" ) ||
|
||
|
!V_stricmp( m_szLanguage, "korean" ) ||
|
||
|
!V_stricmp( m_szLanguage, "schinese" ) ||
|
||
|
!V_stricmp( m_szLanguage, "tchinese" ) ||
|
||
|
!V_stricmp( m_szLanguage, "russian" ) )
|
||
|
{
|
||
|
// these languages must use the font that has their glyphs
|
||
|
// these language require a high degree of legibility
|
||
|
windowsFontName = GetForeignFallbackFontName();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//----//
|
||
|
// PC //
|
||
|
//----//
|
||
|
|
||
|
// AV - The PC has the same issues caused by multiple fonts in a single amalgam with different font
|
||
|
// heights...see comment above. Given the available languages in Steam, the languages below
|
||
|
// were illegible at 1024x768. Resolutions of 800x600 and 640x480 are a complete mess for all
|
||
|
// languages, including English. We probably need to fallback to Tahoma for all languages when
|
||
|
// the vertical resolution < 720. This will probably be the next check-in, but we need to evaluate
|
||
|
// this further tomorrow.
|
||
|
|
||
|
if ( !V_stricmp( windowsFontName, "toolbox" ) || !V_stricmp( windowsFontName, "courier new" ) )
|
||
|
{
|
||
|
// toolbox stays as-is
|
||
|
// courier new is an internal debug font, not part of customer UI, need it stay as is
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// These languages are illegible @ vertical resolutions <= 768
|
||
|
if ( !V_stricmp( m_szLanguage, "korean" ) ||
|
||
|
!V_stricmp( m_szLanguage, "schinese" ) ||
|
||
|
!V_stricmp( m_szLanguage, "tchinese" ) ||
|
||
|
!V_stricmp( m_szLanguage, "russian" ) ||
|
||
|
!V_stricmp( m_szLanguage, "thai" ) ||
|
||
|
!V_stricmp( m_szLanguage, "japanese" ) ||
|
||
|
!V_stricmp( m_szLanguage, "czech" ) )
|
||
|
{
|
||
|
windowsFontName = GetForeignFallbackFontName();
|
||
|
bForceSingleFont = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// AV - If we actually want to support multiple fonts within an amalgam, we need a change here. Currently,
|
||
|
// the code will use winFont for 0-255 and pExtendedFont for 256-0xFFFF! But since the functions for
|
||
|
// getting the font height from the amalgam can only return one height, the heights of the two fonts
|
||
|
// need to be identical. This isn't trivial because even if we query both fonts for their height
|
||
|
// first, that won't force their baselines within the font pages to be aligned. So we would have to
|
||
|
// do something much more complicated where we loop over all characters in each font to find the
|
||
|
// absolute ascent and descent above/below the baseline and then when we create the font, align both
|
||
|
// fonts to the shared baseline with a shared height. But even with the font baselines aligned with a
|
||
|
// shared height, the fonts still wouldn't look great, because different fonts set to the same size
|
||
|
// don't necessarily have the same height visually. We need to revisit this before shipping l4d2 on the PC!
|
||
|
// And there are still issues with what I'm suggesting here because when we ask the font API what
|
||
|
// the maxHeight, maxAscent, maxDescent is, we get inconsistent results, so I'm not even sure we could
|
||
|
// successfully align the baseline of two fonts in the font pages.
|
||
|
|
||
|
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 ( bForceSingleFont || 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();
|
||
|
#ifdef LINUX
|
||
|
int memSize = 0;
|
||
|
void *pchFontData = pFontDataHelper( windowsFontName, memSize );
|
||
|
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];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// failed to create, remove
|
||
|
delete m_Win32Fonts[i];
|
||
|
m_Win32Fonts.Remove(i);
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#endif
|
||
|
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;
|
||
|
}
|
||
|
#ifdef LINUX
|
||
|
}
|
||
|
#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 & 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 );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// 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)
|
||
|
{
|
||
|
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 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 ) & 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 ) & 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;
|
||
|
|
||
|
// AV - Calling GetFontTall() for an amalgam with multiple fonts will return
|
||
|
// the font height of the first font only! We should be doing something like:
|
||
|
//
|
||
|
// tall = FontManager().GetFontForChar( font, text[0] )->GetHeight();
|
||
|
//
|
||
|
// but that's a little hacky since we're only looking at the first character!
|
||
|
// Same goes for the calls to GetFontTall() in the for loop below
|
||
|
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')
|
||
|
{
|
||
|
// AV - See note above about calling this instead: tall += FontManager().GetFontForChar( font, text[0] )->GetHeight();
|
||
|
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;
|
||
|
};
|
||
|
|
||
|
const char *g_szValidAsianFonts[] = {
|
||
|
#ifdef WIN32
|
||
|
"Marlett",
|
||
|
#else
|
||
|
"Helvetica",
|
||
|
#endif
|
||
|
NULL };
|
||
|
|
||
|
// list of how fonts fallback
|
||
|
FallbackFont_t g_FallbackFonts[] =
|
||
|
{
|
||
|
{ "Times New Roman", "Courier New" },
|
||
|
{ "Courier New", "Courier" },
|
||
|
{ "Verdana", "Arial" },
|
||
|
{ "Trebuchet MS", "Arial" },
|
||
|
#ifdef WIN32
|
||
|
{ "Tahoma", NULL },
|
||
|
{ NULL, "Tahoma" }, // every other font falls back to this
|
||
|
#else
|
||
|
{ "Tahoma", "Helvetica" },
|
||
|
{ "Helvetica", NULL },
|
||
|
{ NULL, "Helvetica" } // every other font falls back to this
|
||
|
|
||
|
#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;
|
||
|
}
|
||
|
|
||
|
struct Win98ForeignFallbackFont_t
|
||
|
{
|
||
|
const char *language;
|
||
|
const char *fallbackFont;
|
||
|
};
|
||
|
|
||
|
// list of how fonts fallback
|
||
|
Win98ForeignFallbackFont_t g_Win98ForeignFallbackFonts[] =
|
||
|
{
|
||
|
{ "russian", "system" },
|
||
|
{ "japanese", "win98japanese" },
|
||
|
{ "thai", "system" },
|
||
|
#ifdef WIN32
|
||
|
{ NULL, "Tahoma" }, // every other font falls back to this
|
||
|
#else
|
||
|
{ NULL, "Helvetica" }, // every other font falls back to this
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: specialized fonts
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CFontManager::GetForeignFallbackFontName()
|
||
|
{
|
||
|
#ifdef WIN32
|
||
|
if ( s_bSupportsUnicode )
|
||
|
{
|
||
|
if ( IsX360() )
|
||
|
{
|
||
|
return "arial unicode ms";
|
||
|
}
|
||
|
|
||
|
// tahoma has all the necessary characters for asian/russian languages for winXP/2K+
|
||
|
return "Tahoma";
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int i;
|
||
|
for (i = 0; g_Win98ForeignFallbackFonts[i].language != NULL; i++)
|
||
|
{
|
||
|
if (!stricmp(g_Win98ForeignFallbackFonts[i].language, m_szLanguage))
|
||
|
return g_Win98ForeignFallbackFonts[i].fallbackFont;
|
||
|
}
|
||
|
|
||
|
// the ultimate fallback
|
||
|
return g_Win98ForeignFallbackFonts[i].fallbackFont;
|
||
|
}
|
||
|
|
||
|
#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;
|
||
|
|
||
|
pFont->GetKernedCharWidth( ch, chBefore, chAfter, wide, flabcA, flabcC );
|
||
|
}
|
||
|
|
||
|
|
||
|
#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
|
||
|
|