//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*
 *	@doc	INTERNAL
 *
 *	@module FONT.C -- font cache |
 *
 *		includes font cache, char width cache;
 *		create logical font if not in cache, look up
 *		character widths on an as needed basis (this
 *		has been abstracted away into a separate class
 *		so that different char width caching algos can
 *		be tried.) <nl>
 *		
 *	Owner: <nl>
 *		David R. Fulmer <nl>
 *		Christian Fortini <nl>
 *		Jon Matousek <nl>
 *
 *	History: <nl>
 *		7/26/95		jonmat	cleanup and reorganization, factored out
 *					char width caching code into a separate class.
 *
 */								 

#include "_common.h"
#include "_font.h"
#include "_rtfconv.h"	// Needed for GetCodePage
#include <intsafe.h>

#define CLIP_DFA_OVERRIDE   0x40	//  Used to disable Korea & Taiwan font association


ASSERTDATA

// corresponds to yHeightCharPtsMost in richedit.h
#define yHeightCharMost 32760

// NOTE: this is global across all instances in the same process.
static CFontCache *__fc;

WCHAR	lfJapaneseFaceName[LF_FACESIZE];
WCHAR	lfHangulFaceName[LF_FACESIZE];
WCHAR	lfBig5FaceName[LF_FACESIZE];
WCHAR	lfGB2312FaceName[LF_FACESIZE];

/*
 *	InitFontCache ()
 *	
 *	@mfunc
 *		Initializes font cache.
 *
 *	@devnote
 *		This is exists so reinit.cpp doesn't have to know all about the 
 *		font cache.
 */
BOOL InitFontCache()
{
	// GuyBark Jupiter: Beware of oom.

	if(!(__fc = new CFontCache))
	{
	    return FALSE;
	}

    __fc->Init();

    return TRUE;
}

/*
 *	FreeFontCachet ()
 *	
 *	@mfunc
 *		Frees font cache.
 *
 *	@devnote
 *		This is exists so reinit.cpp doesn't have to know all about the 
 *		font cache.
 */
void FreeFontCache()
{
	__fc->Free();
	delete __fc;
	__fc = NULL;
}


/*
 *	CFontCache & fc()
 *	
 *	@func
 *		initialize the global __fc.
 *	@comm
 *		current #defined to store 16 logical fonts and
 *		respective character widths.
 *		
 */
CFontCache & fc()
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "fc");
    return *__fc;
}

// ===================================  CFontCache  ====================================


/*
 *	CFontCache::Init ()
 *	
 *	@mfunc
 *		Initializes font cache.
 *
 *	@devnote
 *		This is not a constructor because something bad seems to happen
 *		if we try to contruct a global object.
 */
void CFontCache::Init ()
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CFontCache::CFontCache");

	_dwAgeNext = 0;
}

/*
 *	CFontCache::Free ()
 *	
 *	@mfunc
 *		Frees resources attached to font cache.
 *
 *	@devnote
 *		This is not a constructor because something bad seems to happen
 *		if we try to call a destructor on a global object.
 */
void CFontCache::Free ()
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CFontCache::~CFontCache");
}


/*
 *	CCcs* CFontCache::GetCcs(hdc, pcf, lZoomNumerator, lZoomDenominator, yPixelsPerInch)
 *	
 *	@mfunc
 *		Search the font cache for a matching logical font and return it.
 *		If a match is not found in the cache,  create one.
 *
 *	@rdesc
 *		A logical font matching the given CHARFORMAT info.
 */
CCcs* CFontCache::GetCcs(
	HDC			hdc,				//@parm HDC into which font will be selected
	const CCharFormat *const pcf,	//@parm description of desired logical font
	const LONG lZoomNumerator,		//@parm Zoom numerator for getting display font
	const LONG lZoomDenominator,	//@parm Zoom denominator for getting
	const LONG yPerInch)			//@parm Y pixels per inch
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CFontCache::GetCcs");
	CLock	lock;

									//  display font
	const CCcs * const	pccsMost = &_rgccs[cccsMost - 1];
	CCcs *				pccs;
    LONG				lfHeight;

    BYTE				bCrc;
	SHORT				hashKey;
	// Duplicate the format structure because we might need to change some of the
	// values by the zoom factor 
	// and in the case of sub superscript
	CCharFormat cf = *pcf;


	//FUTURE igorzv
	//Take subscript size, subscript offset, superscript offset, superscript size 
	// from the OUTLINETEXMETRIC


	// change cf.yHeight in the case of sub superscript
	if (cf.dwEffects & (CFE_SUPERSCRIPT | CFE_SUBSCRIPT))
	{  
	     if (cf.dwEffects & CFE_SUBSCRIPT)
		 {  
		 	cf.yOffset-=cf.yHeight/5;
		 }
		 else
		 {
 		 	cf.yOffset += cf.yHeight/2;	 
		 }
	 	 cf.yHeight = 2*cf.yHeight/3;
	}					   

	// We only adjust the size if we need to.
	if (lZoomNumerator != lZoomDenominator)
	{
		cf.yHeight = MulDiv(cf.yHeight, lZoomNumerator, lZoomDenominator);
		cf.yOffset = MulDiv(cf.yOffset, lZoomNumerator, lZoomDenominator);
	}

	// calculate lfHeight used for Compare().
	lfHeight = -MulDiv(cf.yHeight, yPerInch, LY_PER_INCH);

	bCrc = cf.bCRC;

	Assert(0 != bCrc);								// Wasn't computed?

	if(!lfHeight)
		lfHeight--;	// round error, make this a minimum legal height of -1.

	// check our hash before going sequential.
	hashKey = bCrc & QUICKCRCSEARCHSIZE;
	if ( bCrc == quickCrcSearch[hashKey].bCrc )
	{
		pccs = quickCrcSearch[hashKey].pccs;
		if(pccs && pccs->_bCrc == bCrc && pccs->_fValid )
		{
	        if(pccs->Compare( &cf, lfHeight ))
			{
                goto matched;
			}
		}
	}
	quickCrcSearch[hashKey].bCrc = bCrc;

	// squentially search ccs for same character format
	for(pccs = &_rgccs[0]; pccs <= pccsMost; pccs++)
	{
		if( pccs->_bCrc == bCrc && pccs->_fValid )
		{
	        if(!pccs->Compare( &cf, lfHeight ))
                continue;
		matched:
			//$ FUTURE: make this work even with wrap around of dwAgeNext
			// Mark as most recently used if it isn't already in use.
			if(pccs->_dwAge != _dwAgeNext - 1)
				pccs->_dwAge = _dwAgeNext++;
			pccs->_dwRefCount++;		// bump up ref. count

			// setup the font to be used for this hdc.
			pccs->_hdc = hdc;

			// the same font can be used at different offsets.
			pccs->_yOffset = cf.yOffset ? (cf.yOffset * yPerInch / LY_PER_INCH) : 0;

			quickCrcSearch[hashKey].pccs = pccs;

			return pccs;
		}
	}

	// we did not find a match, init a new font cache.
	pccs = GrabInitNewCcs ( hdc, &cf, yPerInch );

	quickCrcSearch[hashKey].pccs = pccs;

	return pccs;
}

/*
 *	CCcs* CFontCache::GrabInitNewCcs(hdc, pcf)
 *	
 *	@mfunc
 *		create a logical font and store it in our cache.
 *		
 */
CCcs* CFontCache::GrabInitNewCcs(
	HDC hdc,						//@parm HDC into which font will be selected
	const CCharFormat * const pcf,	//@parm description of desired logical font
	const LONG yPerInch)			//@parm Y Pixels per Inch
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CFontCache::GrabInitNewCcs");

	DWORD				dwAgeOldest = 0xffffffff;
	CCcs *				pccs;
	const CCcs * const	pccsMost = &_rgccs[cccsMost - 1];
	CCcs *				pccsOldest = NULL;

	// look for unused entry and oldest in use entry
	for(pccs = &_rgccs[0]; pccs <= pccsMost && pccs->_fValid; pccs++)
		if(pccs->_dwRefCount == 0 && pccs->_dwAge < dwAgeOldest)
		{
			dwAgeOldest = pccs->_dwAge;
			pccsOldest = pccs;
		}

	if(pccs > pccsMost)		// Didn't find an unused entry, use oldest entry

	{
		pccs = pccsOldest;
		if(!pccs)
		{
			AssertSz(FALSE, "CFontCache::GrabInitNewCcs oldest entry is NULL");
			return NULL;
		}
	}

	pccs->_pfc = this;
	
	// Initialize new CCcs
	if(!pccs->Init(hdc, pcf, yPerInch) )
	{
		return NULL;
	}

	pccs->_dwRefCount++;

	return pccs;
}

// =============================  CCcs  class  ===================================================


/*
 *	BOOL CCcs::Init()
 *	
 *	@mfunc
 *		Init one font cache object. The global font cache stores
 *		individual CCcs objects. 
 */
BOOL CCcs::Init (
	HDC hdc,						//@parm HDC into which font will be selected
	const CCharFormat * const pcf,	//@parm description of desired logical font
	const LONG yPerInch)			//@parm Y pixels per inch
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::Init");

	if ( _fValid ) Free();				// recycle already in-use fonts.


	if( MakeFont(hdc, pcf, yPerInch) )
	{
		_bCrc = pcf->bCRC;
		_yCfHeight = pcf->yHeight;

		Assert(0 != _bCrc);

		if( pcf->yOffset )				// offset for super/sub script.
		{
			_yOffset = pcf->yOffset * yPerInch / LY_PER_INCH;
		}
		else
			_yOffset = 0;
	
		_dwAge = _pfc->_dwAgeNext++;

		_fValid = TRUE;			// successfully created a new font cache.

	}

	return _fValid;
}

/*
 *	void CCcs::Free ()
 *	
 *	@mfunc
 *		Free any dynamic memory allocated by an individual font's cache.
 *		
 */
void CCcs::Free ()
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::Free");

	Assert(_fValid);

	_widths.Free();

	if(_hfont)
		DestroyFont();

	_fValid = FALSE;
	_dwRefCount = 0;
}

/*
 *	BOOL CCcs::CheckFillWidths ()
 *	
 *	@mfunc
 *		Check existence, load nonexistent width information.
 */
BOOL CCcs::CheckFillWidths (
	TCHAR ch, 			//@parm	the TCHAR character in question.
	LONG &rlWidth )	 	//@parm the width to use
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::CheckFillWidths");

	if ( !_widths.CheckWidth(ch, rlWidth) )
	{
		return FillWidths(ch, rlWidth);
	}

	return TRUE;
}

/* 	
 *	CCcs::FillWidths (ch, rlWidth)
 *
 *	@mfunc
 *		Fill in this CCcs with metrics info for given device
 *
 *	@rdesc
 *		TRUE if OK, FALSE if failed
 */
BOOL CCcs::FillWidths(
	TCHAR ch, 		//@parm The TCHAR character we need a width for.
	LONG &rlWidth)	//@parm the width of the character
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::FillWidths");

	BOOL		fRes = FALSE;
	HFONT		hfontOld;

	AssertSz(_hfont, "CCcs::Fill - CCcs has no font");


	//	The mapping mode for the HDC is set before we get here.
	hfontOld = (HFONT)GetCurrentObject( _hdc, OBJ_FONT );
    if ( hfontOld == _hfont || (hfontOld = (HFONT)SelectObject(_hdc, _hfont)) )
    {
		// fill up the width info.
		fRes = _widths.FillWidth ( _hdc, ch, _xOverhangAdjust, rlWidth, 
			_wCodePage, _bConvertMode, _xAveCharWidth, _xDefDBCWidth, _fFixPitchFont );
    }
    
	//	restore the original mapping mode and font.
	//
	if(hfontOld && hfontOld != _hfont)
		SelectFont(_hdc, hfontOld);

	return fRes;
}

/* 	
 *	BOOL CCcs::MakeFont(hdc, pcf)
 *
 *	@mfunc
 *		Wrapper, setup for CreateFontIndirect() to create the font to be
 *		selected into the HDC.
 *
 *	@rdesc
 *		TRUE if OK, FALSE if allocation failure 
 */

#define szFontOfChoice TEXT("Arial")

BOOL CCcs::MakeFont(
	HDC hdc,						//@parm HDC into which  font will be selected
	const CCharFormat * const pcf,	//@parm description of desired logical font
	const LONG yPerInch)
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::MakeFont");

	_bConvertMode = pcf->bInternalEffects & CFEI_RUNISDBCS
				  ? CM_LOWBYTE : CM_NONE;

	_hdc = hdc;

	// Computes font height
	AssertSz(pcf->yHeight <= INT_MAX, "It's too big");

	//	Roundoff can result in a height of zero, which is bad.
	//	If this happens, use the minimum legal height.
	_lf.lfHeight = -(MulDiv(pcf->yHeight, yPerInch, LY_PER_INCH));
	if(_lf.lfHeight > 0)
		_lf.lfHeight = -_lf.lfHeight;		//FUTURE: do something more intelligent...
	if(!_lf.lfHeight)
		_lf.lfHeight--;	// round error, make this a minimum legal height of -1.
	_lf.lfWidth			= 0;

	if( pcf->wWeight != 0 )
	{
		_lf.lfWeight = pcf->wWeight;
	}
	else
	{
		_lf.lfWeight	= (pcf->dwEffects & CFE_BOLD) ? FW_BOLD : FW_NORMAL;
		#ifdef MACPORTStyle
		_lf.lfWeight	|= (pcf->dwEffects & CFE_OUTLINE) ? FW_OUTLINE : _lf.lfWeight;
		_lf.lfWeight	|= (pcf->dwEffects & CFE_SHADOW) ? FW_SHADOW : _lf.lfWeight; 
		#endif
	}

	_lf.lfItalic		= (pcf->dwEffects & CFE_ITALIC)	!= 0;
	_lf.lfUnderline		= 0;	
	_lf.lfStrikeOut		= 0;
	_lf.lfCharSet		= _bConvertMode == CM_LOWBYTE ? ANSI_CHARSET : pcf->bCharSet;
	_lf.lfEscapement 	= 0;
	_lf.lfOrientation 	= 0;
	_lf.lfOutPrecision 	= OUT_DEFAULT_PRECIS;
	_lf.lfClipPrecision = CLIP_DEFAULT_PRECIS | CLIP_DFA_OVERRIDE;
	_lf.lfQuality 		= DEFAULT_QUALITY;
	_lf.lfPitchAndFamily = pcf->bPitchAndFamily;

	// If the run is DBCS, that means the font's codepage is not available in
	// this system.  Use the English ANSI codepage instead so we will display
	// ANSI characters correctly.  NOTE: _wCodePage is only used for Win95.
	_wCodePage = (WORD)GetCodePage(_lf.lfCharSet);

	wcscpy(_lf.lfFaceName, pcf->szFaceName);

	_bUnderlineType = CFU_UNDERLINENONE;

	if ((pcf->dwEffects & CFE_UNDERLINE) != 0)
	{
		_bUnderlineType = CFU_UNDERLINE;

		if (pcf->bUnderlineType)
		{
			_bUnderlineType = pcf->bUnderlineType;
		}
	}

	_fStrikeOut = (pcf->dwEffects & CFE_STRIKEOUT) != 0;

	// Reader! A bundle of spagghetti code lies ahead of you!
	// But go on boldly, for these spagghetti are seasoned with 
	// lots of comments, and ... good luck to you...

	BOOL fTweakedCharSet = FALSE;

	HFONT hfontOriginalCharset = NULL;
	BYTE bOriginalCharset = _lf.lfCharSet;
	WCHAR szNewFaceName[LF_FACESIZE];

	if (NULL == GetFontWithMetrics(szNewFaceName))
	{
	    AssertSz(0, "GetFontWithMetrics failed");
	    return FALSE;
    }
    

	if (0 != wcscmp(szNewFaceName, _lf.lfFaceName))					
	{
		BOOL fCorrectFont = FALSE;

		if (_lf.lfCharSet == SYMBOL_CHARSET)					
		{
			// #1. if the face changed, and the specified charset was SYMBOL,
			//     but the face name exists and suports ANSI, we give preference
			//     to the face name

			_lf.lfCharSet = ANSI_CHARSET;
			fTweakedCharSet = TRUE;			

			hfontOriginalCharset = _hfont;
			GetFontWithMetrics(szNewFaceName);

			if (0 == wcscmp(szNewFaceName, _lf.lfFaceName))
				// that's right, ANSI is the asnwer
				fCorrectFont = TRUE;
			else
				// no, fall back by default
				// the charset we got was right
				_lf.lfCharSet = bOriginalCharset;
		}
		else if (_lf.lfCharSet == DEFAULT_CHARSET && _bCharSet == DEFAULT_CHARSET)
		{
			// #2. If we got the "default" font back, we don't know what it means
			// (could be anything) so we veryfy that this guy's not SYMBOL
			// (symbol is never default, but the OS could be lying to us!!!)
			// we would like to veryfy more like whether it actually gave us
			// Japanese instead of ANSI and labeled it "default"...
			// but SYMBOL is the least we can do

			_lf.lfCharSet = SYMBOL_CHARSET;
			wcscpy(_lf.lfFaceName, szNewFaceName);
			fTweakedCharSet = TRUE;			

			hfontOriginalCharset = _hfont;
			GetFontWithMetrics(szNewFaceName);

			if (0 == wcscmp(szNewFaceName, _lf.lfFaceName))
				// that's right, it IS symbol!
				// 'correct' the font to the 'true' one,
				//  and we'll get fMappedToSymbol
				fCorrectFont = TRUE;
				
			// always restore the charset name, we didn't want to
			// question he original choice of charset here
			_lf.lfCharSet = bOriginalCharset;

		}
		else if ( _bConvertMode != CM_LOWBYTE && IsFECharset(_lf.lfCharSet)
			&& (!OnWinNTFE() || !OnWin95FE()))
		{
			if (_bCharSet != _lf.lfCharSet && (VER_PLATFORM_WIN32_WINDOWS == dwPlatformId))
			{
				// on Win95, when rendering to PS driver,
				// it will give us something other than what we asked.
				// We have to try some known font we got from GDI
				switch (_lf.lfCharSet)
				{
					case CHINESEBIG5_CHARSET:
						wcscpy(_lf.lfFaceName, lfBig5FaceName);
						break;

					case SHIFTJIS_CHARSET:
						wcscpy(_lf.lfFaceName, lfJapaneseFaceName);
						break;

					case HANGEUL_CHARSET:
						wcscpy(_lf.lfFaceName, lfHangulFaceName);
						break;

					case GB2312_CHARSET:
						wcscpy(_lf.lfFaceName, lfGB2312FaceName);
						break;
				}
			}
			else
			{
				// this is a FE Font (from Lang pack) on a nonFEsystem
				wcscpy(_lf.lfFaceName, szNewFaceName);
			}
			hfontOriginalCharset = _hfont;		

			GetFontWithMetrics(szNewFaceName);

			if (0 == wcscmp(szNewFaceName, _lf.lfFaceName))
			{
				// that's right, it IS the FE font we want!
				// 'correct' the font to the 'true' one.
				fCorrectFont = TRUE;
//				if (VER_PLATFORM_WIN32_WINDOWS == dwPlatformId)
//				{
					// save up the GDI font names for later printing use
					switch (_lf.lfCharSet)
					{
						case CHINESEBIG5_CHARSET:
							wcscpy(lfBig5FaceName, _lf.lfFaceName);
							break;

						case SHIFTJIS_CHARSET:
							wcscpy(lfJapaneseFaceName, _lf.lfFaceName);
							break;

						case HANGEUL_CHARSET:
							wcscpy(lfHangulFaceName, _lf.lfFaceName);
							break;

						case GB2312_CHARSET:
							wcscpy(lfGB2312FaceName, _lf.lfFaceName);
							break;
					}
//				}
			}
			fTweakedCharSet = TRUE;
		}

		if (hfontOriginalCharset)
		{
		// either keep the old font or the new one		

			if (fCorrectFont)
			{
				DeleteObject(hfontOriginalCharset);
				hfontOriginalCharset = NULL;
			}
			else 
			{
				// fall back to the original font
				DeleteObject(_hfont);

				_hfont = hfontOriginalCharset;
				hfontOriginalCharset = NULL;
				
				GetTextMetrics();
			}
		}
	}

RetryCreateFont:
	{
		// could be that we just plain symply get mapped to symbol. 
		// avoid it
		BOOL fMappedToSymbol =	(_bCharSet == SYMBOL_CHARSET && 
								 _lf.lfCharSet != SYMBOL_CHARSET);

		BOOL fChangedCharset = (_bCharSet != _lf.lfCharSet && 
								_lf.lfCharSet != DEFAULT_CHARSET);

		if (fChangedCharset || fMappedToSymbol)
		{
			// Here, the system did not preserve the font language or mapped 
			// our non-symbol font onto a symbol font,
			// which will look awful when displayed.
			// Giving us a symbol font when we asked for a non-symbol one
			// (default can never be symbol) is very bizzare and means
			// that either the font name is not known or the system
			// has gone complete nuts here.
			// The charset language takes priority over the font name.
			// Hence, I would argue that nothing can be done to save the 
			// situation at this point, and we have to 
			// delete the font name and retry

			// let's tweak it a bit
			fTweakedCharSet = TRUE;

			if (0 == wcscmp(_lf.lfFaceName, szFontOfChoice))
			{
				// we've been here already
				// no font with an appropriate charset is on the system
				// try getting the ANSI one for the original font name
				// next time around, we'll null out the name as well!!
				if (_lf.lfCharSet == ANSI_CHARSET) 
				{
					TRACEINFOSZ("Asking for ANSI ARIAL and not getting it?!");

					// those Win95 guys have definitely outbugged me 
					goto GetOutOfHere;
				}

				DeleteObject(_hfont);
				wcscpy(_lf.lfFaceName, pcf->szFaceName);
				_lf.lfCharSet = ANSI_CHARSET;
				
			}
			else
			{
				DeleteObject(_hfont);
				wcscpy(_lf.lfFaceName, szFontOfChoice);
			}

			GetFontWithMetrics(szNewFaceName);
			goto RetryCreateFont;
		}

    }

GetOutOfHere:
	if(fTweakedCharSet || _bConvertMode == CM_LOWBYTE)
	{
		// we must preserve the original charset value, since it is used in Compare()
		_lf.lfCharSet = bOriginalCharset;
		wcscpy(_lf.lfFaceName, pcf->szFaceName);
	}

	if (hfontOriginalCharset)
		DeleteObject(hfontOriginalCharset);
   
	// if we're really really stuck, just get the system font and hope for the best.
	if( _hfont == 0 )
	{
		_hfont = (HFONT)GetStockObject(SYSTEM_FONT);
	}
	return _hfont != 0;
}

/*
 *	BOOL CCcs::GetFontWithMetrics (szNewFaceName)
 *	
 *	@mfunc
 *		Get metrics used by the measurer and renderer and the new face name.
 */
BOOL CCcs::GetFontWithMetrics (
	WCHAR* szNewFaceName)
{

	// we want to keep _lf untouched as it is used in Compare().
	_hfont = CreateFontIndirect(&_lf);

    if (_hfont)
    {
		// FUTURE (alexgo) if a font was not created then we may want to select 
		//		a default one.
		//		If we do this, then BE SURE not to overwrite the values of _lf as
		//		it is used to match with a pcf in our Compare().
		//
		// get text metrics, in logical units, that are constant for this font,
		// regardless of the hdc in use.
		GetTextMetrics();

		HFONT hfontOld = SelectFont(_hdc, _hfont);
		GetTextFace(_hdc, LF_FACESIZE, szNewFaceName);
		SelectFont(_hdc, hfontOld);
	}

	return (_hfont != NULL);
}

/*
 *	BOOL CCcs::GetTextMetrics ( )
 *	
 *	@mfunc
 *		Get metrics used by the measurer and renderer.
 *
 *	@comm
 *		These are in logical coordinates which are dependent
 *		on the mapping mode and font selected into the hdc.
 */
BOOL CCcs::GetTextMetrics ()
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::GetTextMetrics");

	BOOL		fRes = TRUE;
	HFONT		hfontOld;
	TEXTMETRIC	tm;

	AssertSz(_hfont, "No font has been created.");
	AssertSz(_hfont, "CCcs::Fill - CCcs has no font");

    if ( GetCurrentObject( _hdc, OBJ_FONT ) != _hfont )
    {
	    hfontOld = (HFONT)SelectObject(_hdc, _hfont);

        if(!hfontOld)
        {
 			fRes = FALSE;
        	DestroyFont();
            goto cleanup;
        }
    }
    else
        hfontOld = 0;

	if(!W32->GetTextMetrics(_hdc, &tm))
	{
		fRes = FALSE;
    	DestroyFont();
        goto cleanup;
	}

	// the metrics, in logical units, dependent on the map mode and font.
	_yHeight		= (SHORT) tm.tmHeight;
	_yDescent		= (SHORT) tm.tmDescent;
	_xAveCharWidth	= (SHORT) tm.tmAveCharWidth;
	_xOverhangAdjust= (SHORT) tm.tmOverhang;

	_xOverhang = 0;
	_xUnderhang	= 0;
	if ( _lf.lfItalic )
	{
		_xOverhang =  SHORT ( (tm.tmAscent + 1) >> 2 );
		_xUnderhang =  SHORT ( (tm.tmDescent + 1) >> 2 );
	}

	// if fix pitch, the tm bit is clear
	_fFixPitchFont = !(TMPF_FIXED_PITCH & tm.tmPitchAndFamily);
	_xDefDBCWidth = 0;

	_bCharSet = tm.tmCharSet;

	// If SYMBOL_CHARSET is used, use the A APIs with the low bytes of the
	// characters in the run
	if(_bCharSet == SYMBOL_CHARSET)
		_bConvertMode = CM_LOWBYTE;

	else if (_bConvertMode == CM_NONE)
	{
		_bConvertMode = W32->DetermineConvertMode( tm.tmCharSet );
	}

	CalcUnderlineInfo(&tm);

cleanup:

	if(hfontOld)
		SelectFont(_hdc, hfontOld);

	return fRes;
}

/*
 *	BOOL CCcs::CalcUnderlineInfo ( )
 *	
 *	@mfunc
 *		Calculate underline & strike out offsets
 *
 *	@rdesc
 *		None.
 */
void CCcs::CalcUnderlineInfo(
	TEXTMETRIC *ptm)		//@parm text metric for the font
{
	W32->CalcUnderlineInfo (this, ptm);
}


/* 	
 *	CCcs::DestroyFont
 *
 *	@mfunc
 *		Destroy font handle for this CCcs
 *
 */
VOID CCcs::DestroyFont()
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::DestroyFont");

	// clear out any old font
	if(_hfont)
	{
		DeleteObject(_hfont);
		_hfont = 0;
	}
}

/*
 *	CCcs::Compare (pcf,	lfHeight, fMetafile)
 *
 *	@mfunc
 *		Compares this font cache with the font properties of a 
 *      given CHARFORMAT
 *
 *	@rdesc
 *		FALSE iff did not match exactly.
 */
BOOL CCcs::Compare (
	const CCharFormat * const pcf,	//@parm Description of desired logical font
	LONG	lfHeight)		//@parm	lfHeight as calculated with the given HDC
{
	BOOL result;
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CCcs::Compare");

	result =
			_yCfHeight      == pcf->yHeight &&	// because different mapping modes
			_lf.lfHeight	== lfHeight &&		//  have diff logical coords
        	_lf.lfWeight	== ((pcf->dwEffects & CFE_BOLD) ? FW_BOLD : FW_NORMAL) &&
	        _lf.lfItalic	== ((pcf->dwEffects & CFE_ITALIC) != 0) &&
	        _fStrikeOut == ((pcf->dwEffects & CFE_STRIKEOUT) != 0) &&
			((((pcf->dwEffects & CFE_UNDERLINE) == 0) 
					&& (_bUnderlineType == CFU_UNDERLINENONE))
				|| (((pcf->dwEffects & CFE_UNDERLINE) != 0) 
					&& (_bUnderlineType == pcf->bUnderlineType))) &&
	        _lf.lfCharSet == pcf->bCharSet &&
        	_lf.lfPitchAndFamily == pcf->bPitchAndFamily &&
			((pcf->bInternalEffects & CFEI_RUNISDBCS)
					? _bConvertMode == CM_LOWBYTE : 1);
	// The following call has been known to cause troubles on Win CE
	result = result && !lstrcmp( _lf.lfFaceName, pcf->szFaceName );
	return result;
}

// =========================  WidthCache by jonmat  =========================
/*
 *	CWidthCache::CheckWidth(ch, rlWidth)
 *	
 *	@mfunc
 *		check to see if we have a width for a TCHAR character.
 *
 *	@comm
 *		Used prior to calling FillWidth(). Since FillWidth
 *		may require selecting the map mode and font in the HDC,
 *		checking here first saves time.
 *
 *	@comm
 *		Statistics are maintained to determine when to
 *		expand the cache. The determination is made after a constant
 *		number of calls in order to make calculations faster.
 *
 *	@rdesc
 *		returns TRUE if we have the width of the given TCHAR.
 */
BOOL CWidthCache::CheckWidth (
	const TCHAR ch,  //@parm char, can be Unicode, to check width for.
	LONG &rlWidth )	//@parm the width of the character
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::CheckWidth");

	BOOL	exist;
	INT		i;

	const	CacheEntry * pWidthData = GetEntry ( ch );

	exist = ( ch == pWidthData->ch		// Have we fetched the width?
				&& pWidthData->width );	//  only because we may have ch == 0.

	if( exist )
	{
		rlWidth = pWidthData->width;
	}
	else
	{
		rlWidth = 0;
	}

	i = CACHE_SWITCH(ch);				// Update statistics
	if ( !_fMaxPerformance[i] )			//  if we have not grown to the max...
	{
		_accesses[i]++;
		if ( !exist )					// Only interesting on collision.
		{
			if ( 0 == pWidthData->width )// Test width not ch, 0 is valid ch.
			{
				_cacheUsed[i]++;		// Used another entry.
				AssertSz( _cacheUsed[i] <= _cacheSize[i]+1, "huh?");
			}
			else
				_collisions[i]++;		// We had a collision.

			if ( _accesses[i] >= PERFCHECKEPOCH )
				CheckPerformance(i);	// After some history, tune cache.
		}
	}
#ifdef DEBUG							// Continue to monitor performance
	else
	{
		_accesses[i]++;
		if ( !exist )					// Only interesting on collision.
		{
			if ( 0 == pWidthData->width )// Test width not ch, 0 is valid ch.
			{
				_cacheUsed[i]++;		// Used another entry.
				AssertSz( _cacheUsed[i] <= _cacheSize[i]+1, "huh?");
			}
			else
				_collisions[i]++;		// We had a collision.
		}

		if ( _accesses[i] > PERFCHECKEPOCH )
		{
			_accesses[i] = 0;
			_collisions[i] = 0;
		}
	}
#endif

	return exist;
}

/*
 *	CWidthCache::CheckPerformance(i)
 *	
 *	@mfunc
 *		check performance and increase cache size if deemed necessary.
 *
 *	@devnote
 *		To calculate 25% collision rate, we make use of the fact that
 *		we are only called once every 64 accesses. The inequality is 
 *		100 * collisions / accesses >= 25. By converting from 100ths to
 *		8ths, the ineqaulity becomes (collisions << 3) / accesses >= 2.
 *		Substituting 64 for accesses, this becomes (collisions >> 3) >= 2.
 */
void CWidthCache::CheckPerformance(
	INT i ) //@parm which cache to check.
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::CheckPerformance");

	if ( _fMaxPerformance[i] )			// Exit if already grown to our max.
		return;

	if (								// Grow the cache when
										//  cacheSize > 0 && 75% utilized,
			(  _cacheSize[i] > DEFAULTCACHESIZE &&
			( (_cacheSize[i] >> 1) + (_cacheSize[i] >> 2)) < _cacheUsed[i])
										//  or approx 25% collision rate.
		||  ( _collisions[i] >> COLLISION_SHIFT ) >= 2 )
	{
		GrowCache( &_pWidthCache[i], &_cacheSize[i], &_cacheUsed[i] );
	}
	_collisions[i]	= 0;				// This prevents wraps but makes
	_accesses[i]	= 0;				//  calc a local rate, not global.
										
										// Note if we've max'ed out.
	if ( _cacheSize[i] >= maxCacheSize[i] )
		_fMaxPerformance[i] = TRUE;

	AssertSz( _cacheSize[i] <= maxCacheSize[i], "max must be 2^n-1");
	AssertSz( _cacheUsed[i] <= _cacheSize[i]+1, "huh?");
}

/*
 *	CWidthCache::GrowCache(ppWidthCache, pCacheSize, pCacheUsed)
 *	
 *	@mfunc
 *		Exponentially expand the size of the cache.
 *
 *	@comm
 *		The cache size must be of the form 2^n as we use a
 *		logical & to get the hash MOD by storing 2^n-1 as
 *		the size and using this as the modulo.
 *
 *	@rdesc
 *		Returns TRUE if we were able to allocate the new cache.
 *		All in params are also out params.
 *		
 */
BOOL CWidthCache::GrowCache(
	CacheEntry **ppWidthCache,	//@parm cache
	INT *		pCacheSize,		//@parm cache's respective size.
	INT *		pCacheUsed)		//@parm cache's respective utilization.
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::GrowCache");

	CacheEntry		*pNewWidthCache, *pOldWidthCache, *pWidthData;
	INT 			j, newCacheSize, newCacheUsed;
	TCHAR			ch;
	UINT uT;
	
	j = *pCacheSize;						// Allocate cache of 2^n.
	newCacheSize = max ( INITIALCACHESIZE, (j << 1) + 1);
	if (newCacheSize + 1 < newCacheSize || FAILED(UIntMult(sizeof(CacheEntry), newCacheSize + 1, &uT)))
		return FALSE;
	pNewWidthCache = (CacheEntry *)
			PvAlloc( uT, GMEM_ZEROINIT);

	if ( pNewWidthCache )
	{
		newCacheUsed = 0;
		*pCacheSize = newCacheSize;			// Update out params.
		pOldWidthCache = *ppWidthCache;
		*ppWidthCache = pNewWidthCache;
		for (; j >= 0; j--)					// Move old cache info to new.
		{
			ch = pOldWidthCache[j].ch;
			if ( ch )
			{
				pWidthData			= &pNewWidthCache [ch & newCacheSize];
				if ( 0 == pWidthData->ch )
					newCacheUsed++;			// Used another entry.
				pWidthData->ch		= ch;
				pWidthData->width	= pOldWidthCache[j].width;
			}
		}
		*pCacheUsed = newCacheUsed;			// Update out param.

											// Free old cache.
              											
		if (   pOldWidthCache <  &_defaultWidthCache[0][0]
			|| pOldWidthCache > &_defaultWidthCache[TOTALCACHES-1][(DEFAULTCACHESIZE+1)-1])
			//-1 on _defaultWidthCache indexes because of 0 based array.
		{
			FreePv(pOldWidthCache);
		}
	}

	return NULL != pNewWidthCache;
}

/*
 *	CWidthCache::FillWidth(hdc, ch, xOverhang, rlWidth)
 *	
 *	@mfunc
 *		Call GetCharWidth() to obtain the width of the given char.
 *
 *	@comm
 *		The HDC must be setup with the mapping mode and proper font
 *		selected *before* calling this routine.
 *
 *	@rdesc
 *		Returns TRUE if we were able to obtain the widths.
 *		
 */
BOOL CWidthCache::FillWidth (
	HDC			hdc,		//@parm Current HDC we want font info for.
	const TCHAR	ch,			//@parm Char to obtain width for.
	const SHORT xOverhang,	//@parm Equivalent to GetTextMetrics() tmOverhang.
	LONG &		rlWidth,	//@parm Width of character
	UINT		uiCodePage,	//@parm code page for text	
	BOOL		fANSI,		//@parm indicates font needs to use ANSI call.
	INT			iDefWidth,	//@parm Default width to use if font calc's zero
							//width. (Handles Win95 problem).
	INT			iDBCDefWidth,	//@parm Default width for DBC to use 
								// Handles Win95 Trad Chinese problem.
	BOOL		fFixPitch)		//@parm fix pitch font for DBC to use 
								// Handles Win95 Trad Chinese problem.
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::FillWidth");

	char	ansiChar[2] = {0};
	INT		numOfDBCS = 0;
	CacheEntry * pWidthData = GetEntry ( ch );

	(void) W32->REGetCharWidth( hdc, ch, &pWidthData->width, uiCodePage );

#if 0
	// REVIEW  May not need codepage.
	// TODO

	if (!fANSI && uiCodePage != SYMBOL_CODEPAGE)
		(void) W32->REGetCharWidth( hdc, ch, &pWidthData->width, uiCodePage );
	else
	{
		WORD wDBCS = ch;
		(void) GetCharWidthA( hdc, wDBCS, wDBCS, &pWidthData->width );				
	}

	// Is this needed ????
	// fAnsi case, SYMBOL_CHARSET, or GetCharWidthW failed: try GetCharWidthA 
	if (!fRes || 0 == pWidthData->width)
	{
		if(uiCodePage != SYMBOL_CODEPAGE)
		{
			// Try to convert string
			numOfDBCS = WideCharToMultiByte( uiCodePage, 0, &ch, 1, 
				ansiChar, 2, NULL, NULL);

			if (2 == numOfDBCS)
				wDBCS = (BYTE)ansiChar[0] << 8 | (BYTE)ansiChar[1];

			else if (numOfDBCS)
				wDBCS = (BYTE)ansiChar[0];
		}
		fRes = GetCharWidthA( hdc, wDBCS, wDBCS, &pWidthData->width );				
	}
#endif

	pWidthData->ch = ch;
//	pWidthData->width	-= xOverhang;		// Don't need since we use
											//  GetTextExtentPoint32()
	if (0 >= pWidthData->width)
	{
		// Sometimes GetCharWidth will return a zero length for small
		// characters. When this happens we will use the default width
		// for the font if that is non-zero otherwise we just us 1 because
		// this is the smallest valid value.

		// This code can also be triggered if the overhang is bugger than the
		// width returned by the OS call to get the character width.
		
		// under Win95 Trad. Chinese, there is a bug in the font.
		// It is returning a width of 0 for a few characters (Eg 0x09F8D, 0x81E8)
		// In such case, we need to use 2 * iDefWidth since these are DBCS
		if (0 == iDefWidth)
			pWidthData->width = 1;
		else
			pWidthData->width = (numOfDBCS == 2) ? 
				(iDBCDefWidth ? iDBCDefWidth : 2 * iDefWidth) : iDefWidth;
	}

	rlWidth = pWidthData->width;
	return TRUE;
}

/*
 *	CWidthCache::GetWidth(ch)
 *	
 *	@mfunc
 *		get the width (A+B+C) for the given character.
 *	@comm
 *		we've already called GetCharWidth() at this point.
 *	@rdesc
 *		the width.
 */
INT CWidthCache::GetWidth (
	const TCHAR ch )//@parm char, can be Unicode, to check width for.
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::GetWidth");

	const CacheEntry * pWidthData = GetEntry ( ch );

	AssertSz( pWidthData->ch == ch, "Table not filled in?" );

	return pWidthData->width;
}

/*
 *	CWidthCache::Free()
 *	
 *	@mfunc
 *		Free any dynamic memory allocated by the width cache and prepare
 *		it to be recycled.
 *		
 */
VOID CWidthCache::Free()
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::Free");

	INT i;

	for (i = 0; i < TOTALCACHES; i++ )
	{
		_fMaxPerformance[i] = FALSE;
		_cacheSize[i]		= DEFAULTCACHESIZE;
		_cacheUsed[i]		= 0;
		_collisions[i]		= 0;
		_accesses[i]		= 0;
		if ( _pWidthCache[i] != &_defaultWidthCache[i][0] )
		{
			FreePv(_pWidthCache[i]);
			_pWidthCache[i] = &_defaultWidthCache[i][0];
		}	
		ZeroMemory(_pWidthCache[i], sizeof(CacheEntry)*(DEFAULTCACHESIZE + 1));
	}
}

/*
 *	CWidthCache::CWidthCache()
 *	
 *	@mfunc
 *		Point the caches to the defaults.
 *		
 */
CWidthCache::CWidthCache()
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::CWidthCache");

	INT i;

	for (i = 0; i < TOTALCACHES; i++ )
	{
		_pWidthCache[i] = &_defaultWidthCache[i][0];
	}
}

/*
 *	CWidthCache::~CWidthCache()
 *	
 *	@mfunc
 *		Free any allocated caches.
 *		
 */
CWidthCache::~CWidthCache()
{
	TRACEBEGIN(TRCSUBSYSFONT, TRCSCOPEINTERN, "CWidthCache::~CWidthCache");


	INT i;

	for (i = 0; i < TOTALCACHES; i++ )
	{
		if (_pWidthCache[i] != &_defaultWidthCache[i][0])
			FreePv(_pWidthCache[i]);
	}
}

#if 0 // unused
/*
 *	INT CheckDBChar ( UINT uiCodePage, WCHAR wChar, char *pAnsiChar)
 *	
 *	@func
 *		Determine if the input wChar is converted to a DBC or SBC
 *
 *	@comm
 *		Called when we need to determine the char width and when we render 
 *		the char.  This is mainly used for determining if the Bullet char (0xb7)
 *		has a DBC equivalent.
 *
 *	@rdesc
 *		return 2 if DBCS, otherwise return 0. Also, pAnsiChar contains the DBCS.
 *		
 */
INT	CheckDBChar ( UINT uiCodePage, WCHAR wChar, char *pAnsiChar)
{
	WORD wDBCS=0;
	
	// Convert string	
	if ( uiCodePage )	
		// check if we have a DBC bullet for this codepage		
		wDBCS = WideCharToMultiByte( uiCodePage, 0, &wChar, 1, 		
			pAnsiChar, 2, NULL, NULL);

	if ( 2 != wDBCS )
		*pAnsiChar = (CHAR)wChar;

	return wDBCS;
}
#endif



