#include "stdafx.h"

#include <ft2build.h>
#include FT_OUTLINE_H
#include FT_SIZES_H
#include FT_GLYPH_H
#include FT_TRUETYPE_IDS_H
#include FT_TRUETYPE_TABLES_H
#include FT_XFREE86_H
#include FT_ADVANCES_H

#include <math.h>

#include "FT_FontEngine.h"
#include "FT_FontFile.h"
#include "FT_Font.h"

#include "FontPath.h"
#include "FontConsts.h"
#include "GlyphString.h"

#define LOAD_MODE FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_BITMAP | FT_LOAD_LINEAR_DESIGN
#define REND_MODE FT_RENDER_MODE_NORMAL

//-------------------------------------------------------------------------------------------------------------------------------
struct TFreeTypeFontPath 
{
	CFontPath *pPath;
	//double dTextScale;
	BOOL       bNeedClose;
};

static int GlyphPathMoveTo(const FT_Vector *pPoint, void *pPath) 
{
	TFreeTypeFontPath *pGlyphPath = (TFreeTypeFontPath *)pPath;

	if ( pGlyphPath->bNeedClose ) 
	{
		pGlyphPath->pPath->Close();
		pGlyphPath->bNeedClose = FALSE;
	}
	pGlyphPath->pPath->MoveTo( (double)pPoint->x /** pGlyphPath->dTextScale*/ / 64.0, (double)pPoint->y /** pGlyphPath->dTextScale*/ / 64.0 );
	return 0;
}

static int GlyphPathLineTo(const FT_Vector *pPoint, void *pPath) 
{
	TFreeTypeFontPath *pGlyphPath = (TFreeTypeFontPath *)pPath;

	pGlyphPath->pPath->LineTo( (double)pPoint->x /** pGlyphPath->dTextScale*/ / 64.0, (double)pPoint->y /** pGlyphPath->dTextScale*/ / 64.0 );
	pGlyphPath->bNeedClose = TRUE;
	return 0;
}

static int GlyphPathConicTo(const FT_Vector *pControlPoint, const FT_Vector *pEndPoint, void *pPath) 
{
	TFreeTypeFontPath *pGlyphPath = (TFreeTypeFontPath *)pPath;

	double dX0, dY0;

	if ( !pGlyphPath->pPath->GetCurPoint( &dX0, &dY0 ) ) 
	{
		return 0;
	}

	double dXc = (double)pControlPoint->x /** pGlyphPath->dTextScale*/ / 64.0;
	double dYc = (double)pControlPoint->y /** pGlyphPath->dTextScale*/ / 64.0;
	double dX3 = (double)pEndPoint->x /** pGlyphPath->dTextScale*/ / 64.0;
	double dY3 = (double)pEndPoint->y /** pGlyphPath->dTextScale*/ / 64.0;

	// Строим кривую Безье второго порядка, с помощью кривой Безье третего порядка. Если p0, pC, p3 -
	// начальная, контрольная и конечная точки, соответственно, для кривой Безье второго порядка. Тогда 
	// для этой же кривой, рассматриваемой как кривая Безье третьего порядка, точки p0, p1, p2, p3 будут
	// начальной, две контрольные, конечная точки. Где p1 и p2 рассчитываются по следующим формулам:
	//     p1 = (1/3) * (p0 + 2pС)
	//     p2 = (1/3) * (2pС + p3)

	double dX1 = (double)(1.0 / 3.0) * (dX0 + (double)2 * dXc);
	double dY1 = (double)(1.0 / 3.0) * (dY0 + (double)2 * dYc);
	double dX2 = (double)(1.0 / 3.0) * ((double)2 * dXc + dX3);
	double dY2 = (double)(1.0 / 3.0) * ((double)2 * dYc + dY3);

	pGlyphPath->pPath->CurveTo( dX1, dY1, dX2, dY2, dX3, dY3 );
	pGlyphPath->bNeedClose = TRUE;
	return 0;
}

static int GlyphPathCubicTo(const FT_Vector *pFirstControlPoint, const FT_Vector *pSecondControlPoint, const FT_Vector *pEndPoint, void *pPath) 
{
	TFreeTypeFontPath *pGlyphPath = (TFreeTypeFontPath *)pPath;

	double dX1 = (double)pFirstControlPoint->x /** pGlyphPath->dTextScale*/ / 64.0;
	double dY1 = (double)pFirstControlPoint->y /** pGlyphPath->dTextScale*/ / 64.0;
	double dX2 = (double)pSecondControlPoint->x /** pGlyphPath->dTextScale*/ / 64.0;
	double dY2 = (double)pSecondControlPoint->y /** pGlyphPath->dTextScale*/ / 64.0;
	double dX3 = (double)pEndPoint->x /** pGlyphPath->dTextScale*/ / 64.0;
	double dY3 = (double)pEndPoint->y /** pGlyphPath->dTextScale*/ / 64.0;

	pGlyphPath->pPath->CurveTo( dX1, dY1, dX2, dY2, dX3, dY3 );
	pGlyphPath->bNeedClose = TRUE;
	return 0;
}

//-------------------------------------------------------------------------------------------------------------------------------
// CFreeTypeFont
//-------------------------------------------------------------------------------------------------------------------------------

CFreeTypeFont::CFreeTypeFont(CFreeTypeFontFile *pFontFile):
CFont(pFontFile, pFontFile->m_pEngine->m_bAntiAliasing, pFontFile->m_pEngine->m_bUseKerning)
{
	m_dUnitsKoef   = 1.0;
	m_nDefaultChar = -1;

	GetDefaultChar();
	m_nSymbolic = GetSymbolicCmapIndex();

	FT_Face pFace = pFontFile->m_pFace;
	if ( FT_New_Size( pFace, &m_pSize ) ) 
	{
		return;
	}
	pFace->size = m_pSize;

	// Выставляем размер шрифта (dSize) и DPI 
	if ( FT_Set_Char_Size( pFace, 0, (int)m_fSize * 64, 0, 0 ) ) 
	{
		return;
	}

	// В конструкторе CFont должно быть m_fSize = 1.0f, поэтому и m_dUnitsKoef = 1.0
	ResetFontMatrix();
	ResetTextMatrix();

	if ( TRUE == m_bUseKerning )
	{
		m_bUseKerning = ( FT_HAS_KERNING( pFace ) > 0 ? TRUE : FALSE );
	}

	InitSizesCache();
	::memset( m_arrCacheSizesIndexs, 0xFF, FONT_CACHE_SIZES_INDEXES_SIZE_2 );

	m_bStringGID = FALSE;
}

CFreeTypeFont::~CFreeTypeFont() 
{
	ClearSizesCache();
}

BOOL CFreeTypeFont::GetGlyph(int nCode, int nFracX, int nFracY, TGlyphBitmap *pBitmap) 
{
	return CFont::GetGlyph( nCode, nFracX, 0, pBitmap );
}

BOOL CFreeTypeFont::MakeGlyph(int nCode, int nFracX, int nFracY, TGlyphBitmap *pBitmap) 
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	FT_Int unGID = SetCMapForCharCode2( nCode );
	if ( unGID <= 0 )
		return FALSE;

	pFontFile->m_pFace->size = m_pSize;

	FT_Vector oOffset;
	oOffset.x = (FT_Pos)(int)( (double)nFracX * FONT_FRACTION_MULT * 64 );
	oOffset.y = 0;

	FT_GlyphSlot pGlyphSlot = pFontFile->m_pFace->glyph;

	// TO DO: Пропустить нулевой (".notdef") символ в TrueType

	if ( m_pDefaultFont )
		((CFreeTypeFont*)m_pDefaultFont)->UpdateMatrix2();
	UpdateMatrix2();

	if ( FT_Load_Glyph( pFontFile->m_pFace, unGID, LOAD_MODE ) ) 
	{
		return FALSE;
	}

	if ( FT_Render_Glyph( pGlyphSlot, REND_MODE /*m_bAntiAliasing ? ft_render_mode_normal : ft_render_mode_mono*/ ) ) 
	{
		return FALSE;
	}

	pBitmap->nX      = pGlyphSlot->bitmap_left;
	pBitmap->nY      = pGlyphSlot->bitmap_top;
	pBitmap->nWidth  = pGlyphSlot->bitmap.width;
	pBitmap->nHeight = pGlyphSlot->bitmap.rows;
	pBitmap->bAA     = m_bAntiAliasing;

	int nRowSize = 0;
	if ( m_bAntiAliasing ) 
	{
		if ( m_bNeedDoBold )
			pBitmap->nWidth++;

		nRowSize = pBitmap->nWidth;
	} 
	else 
	{
		nRowSize = (pBitmap->nWidth + 7) >> 3;
	}

	pBitmap->pData = (unsigned char *)malloc( nRowSize * pBitmap->nHeight );
	pBitmap->bFreeData = TRUE;

	int nIndex;
	unsigned char *pDstBuffer, *pSrcBuffer;

	if ( !m_bNeedDoBold || !m_bAntiAliasing )
	{
		for ( nIndex = 0, pDstBuffer = pBitmap->pData, pSrcBuffer = pGlyphSlot->bitmap.buffer; nIndex < pBitmap->nHeight; ++nIndex, pDstBuffer += nRowSize, pSrcBuffer += pGlyphSlot->bitmap.pitch ) 
		{
			memcpy( pDstBuffer, pSrcBuffer, nRowSize );
		}
	}
	else
	{
		int nY, nX;
		for ( nY = 0, pDstBuffer = pBitmap->pData, pSrcBuffer = pGlyphSlot->bitmap.buffer; nY < pBitmap->nHeight; ++nY, pDstBuffer += nRowSize, pSrcBuffer += pGlyphSlot->bitmap.pitch ) 
		{
			for ( nX = pBitmap->nWidth - 1; nX >= 0; nX-- )
			{
				if ( 0 != nX )
				{
					int nFirstByte, nSecondByte;

					if ( pBitmap->nWidth - 1 == nX )
						nFirstByte = 0;
					else
						nFirstByte = pSrcBuffer[nX];

					nSecondByte = pSrcBuffer[nX - 1];

					pDstBuffer[nX] = min( 255, nFirstByte + nSecondByte);

				}
				else
				{
					pDstBuffer[nX] = pSrcBuffer[nX];
				}
			}
		}

	}

	return TRUE;
}

BOOL CFreeTypeFont::GetString(CGlyphString *pString)
{
	if ( pString->GetLength() <= 0 )
		return TRUE;

	unsigned int unPrevGID = 0;
	float fPenX = 0, fPenY = 0;

	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)(m_pFontFile);
	FT_Face pSrcFace = pFontFile->m_pFace;

	FT_Face pDefFace = NULL;
	CFreeTypeFont *pDefFont = NULL;
	if ( m_pDefaultFont )
	{
		pDefFont = (CFreeTypeFont*)m_pDefaultFont;
		pDefFace = ((CFreeTypeFontFile *)(pDefFont->m_pFontFile))->m_pFace;
	}

	// Сначала мы все рассчитываем исходя только из матрицы шрифта FontMatrix
	if ( pDefFont )
		pDefFont->UpdateMatrix1();
	UpdateMatrix1();

	for ( int nIndex = 0; nIndex < pString->GetLength(); nIndex++ )
	{
		FT_Face pFace = pSrcFace;
		TGlyph *pCurGlyph = pString->GetAt( nIndex );

		unsigned short ushUnicode = (unsigned short)pCurGlyph->lUnicode;
		int nCacheIndex = FindInSizesCache( ushUnicode );
		unsigned int unGID = 0;

		if ( 0xFFFF == nCacheIndex )
		{
			int nCMapIndex = 0;
			unGID = (unsigned int)SetCMapForCharCode( ushUnicode, &nCMapIndex );
			TFontCacheSizes oSizes;
			oSizes.ushUnicode = ushUnicode;

			if ( !( ( unGID > 0 ) || ( -1 != m_nSymbolic && ( ushUnicode < 0xF000 )  && 0 < ( unGID = (unsigned int)SetCMapForCharCode( ushUnicode + 0xF000, &nCMapIndex ) ) ) ) )
			{
				// Пробуем загрузить через стандартный шрифт
				if ( FALSE == m_bUseDefaultFont || NULL == pDefFont || 0 >= ( unGID = pDefFont->SetCMapForCharCode( ushUnicode, &nCMapIndex ) )  )
				{
					if ( m_nDefaultChar < 0 )
					{
						oSizes.ushGID    = -1;
						oSizes.eState    = glyphstateMiss;
						oSizes.fAdvanceX = (float)(pSrcFace->size->metrics.max_advance >> 6) / 2.f;

						// В стандартном шрифте данный символ тоже не рисуется
						pString->SetStartPoint( nIndex, fPenX, fPenY );
						pString->SetBBox( nIndex, 0, 0, 0, 0 );
						pString->SetState( nIndex, glyphstateMiss );

						FT_Fixed lAdv = 0;

						fPenX += oSizes.fAdvanceX + m_fCharSpacing;
						unPrevGID = 0;

						continue;
					}
					else
					{
						unGID = m_nDefaultChar;
						oSizes.eState = glyphstateNormal;
						pString->SetState( nIndex, glyphstateNormal );
						pFace = pSrcFace;
					}
				}
				else
				{
					oSizes.eState = glyphstateDeafault;

					pString->SetState( nIndex, glyphstateDeafault );
					pFace = pDefFace;
				}
			}
			else
			{
				oSizes.eState = glyphstateNormal;

				pString->SetState( nIndex, glyphstateNormal );
				pFace = pSrcFace;
			}
			oSizes.ushGID     = unGID;
			oSizes.nCMapIndex = nCMapIndex;

			if ( m_bUseKerning && unPrevGID && ( nIndex >= 0 && pString->GetAt( nIndex )->eState == pString->GetAt( nIndex - 1 )->eState ) )
			{
				FT_Vector oDelta;
				FT_Get_Kerning( pFace, unPrevGID, unGID, FT_KERNING_DEFAULT, &oDelta );
				fPenX += (float)(oDelta.x >> 6);
			}

			float fX = pString->m_fX + fPenX;
			float fY = pString->m_fY + fPenY;

			// Начальную точку рассчитываем сразу исходя из глобальной матрицы 
			float fXX = (float)(pString->m_arrCTM[4] + fX * pString->m_arrCTM[0] + fY * pString->m_arrCTM[2] - pString->m_fX );
			float fYY = (float)(pString->m_arrCTM[5] + fX * pString->m_arrCTM[1] + fY * pString->m_arrCTM[3] - pString->m_fY );

			pString->SetStartPoint( nIndex, fXX, fYY);

			if ( FT_Load_Glyph( pFace, unGID, LOAD_MODE ) )
			{
				pString->SetStartPoint( nIndex, -0xFFFF, -0xFFFF );
				pString->SetState( nIndex, glyphstateMiss );
				continue;
			}

			FT_Glyph pGlyph = NULL;
			if ( FT_Get_Glyph( pFace->glyph, &pGlyph ) )
			{
				pString->SetStartPoint( nIndex, -0xFFFF, -0xFFFF );
				pString->SetState( nIndex, glyphstateMiss );
				continue;
			}

			FT_BBox oBBox;
			FT_Glyph_Get_CBox( pGlyph, ft_glyph_bbox_gridfit, &oBBox );
			FT_Done_Glyph( pGlyph );

			oSizes.fAdvanceX = (float)(pFace->glyph->linearHoriAdvance * m_dUnitsKoef / pFace->units_per_EM);
			oSizes.oBBox.fMinX = (float)(oBBox.xMin >> 6);
			oSizes.oBBox.fMaxX = (float)(oBBox.xMax >> 6);
			oSizes.oBBox.fMinY = (float)(oBBox.yMin >> 6);
			oSizes.oBBox.fMaxY = (float)(oBBox.yMax >> 6);

			oSizes.oMetrics.fHeight       = (float)(pFace->glyph->metrics.height       >> 6);
			oSizes.oMetrics.fHoriAdvance  = (float)(pFace->glyph->metrics.horiAdvance  >> 6);
			oSizes.oMetrics.fHoriBearingX = (float)(pFace->glyph->metrics.horiBearingX >> 6);
			oSizes.oMetrics.fHoriBearingY = (float)(pFace->glyph->metrics.horiBearingY >> 6);
			oSizes.oMetrics.fVertAdvance  = (float)(pFace->glyph->metrics.vertAdvance  >> 6);
			oSizes.oMetrics.fVertBearingX = (float)(pFace->glyph->metrics.vertBearingX >> 6);
			oSizes.oMetrics.fVertBearingY = (float)(pFace->glyph->metrics.vertBearingY >> 6);
			oSizes.oMetrics.fWidth        = (float)(pFace->glyph->metrics.width        >> 6);

			oSizes.bBitmap = false;
			oSizes.oBitmap.nX        = 0;
			oSizes.oBitmap.nY        = 0;
			oSizes.oBitmap.nHeight   = 0;
			oSizes.oBitmap.nWidth    = 0;
			oSizes.oBitmap.bFreeData = FALSE;
			oSizes.oBitmap.pData     = NULL;
			oSizes.oBitmap.bAA       = FALSE;

			pString->SetMetrics( nIndex, oSizes.oMetrics.fWidth, oSizes.oMetrics.fHeight, oSizes.oMetrics.fHoriAdvance, oSizes.oMetrics.fHoriBearingX, oSizes.oMetrics.fHoriBearingY, oSizes.oMetrics.fVertAdvance, oSizes.oMetrics.fVertBearingX, oSizes.oMetrics.fVertBearingY );
			pString->SetBBox( nIndex, oSizes.oBBox.fMinX, oSizes.oBBox.fMaxY, oSizes.oBBox.fMaxX, oSizes.oBBox.fMinY );
			//pString->SetBBox( nIndex, (float)(oBBox.xMin >> 6), (float)(oBBox.yMax >> 6), (float)(oBBox.xMax >> 6), (float)(oBBox.yMin >> 6) );

			fPenX    += oSizes.fAdvanceX + m_fCharSpacing;//(float)(pFace->glyph->advance.x >> 6);
			if ( m_bNeedDoBold )
			{
				// Когда текст делаем жирным сами, то мы увеличиваем расстояние на 1 пиксель в ширину (независимо от DPI и размера текста всегда 1 пиксель)
				fPenX += 1;
			}
			AddToSizesCache( oSizes );
		}
		else
		{
			TFontCacheSizes oSizes = m_oCacheSizes.Get(nCacheIndex);

			int nCMapIndex     = oSizes.nCMapIndex;
			unGID              = oSizes.ushGID;
			EGlyphState eState = oSizes.eState;

			if ( glyphstateMiss == eState )
			{
				pString->SetStartPoint( nIndex, fPenX, fPenY );
				pString->SetBBox( nIndex, 0, 0, 0, 0 );
				pString->SetState( nIndex, glyphstateMiss );

				FT_Fixed lAdv = 0;

				fPenX += oSizes.fAdvanceX + m_fCharSpacing;		
				unPrevGID = 0;

				continue;
			}
			else if ( glyphstateDeafault == eState )
			{
				pString->SetState( nIndex, glyphstateDeafault );
				pFace = pDefFace;
			}
			else // if ( glyphstateNormal == eState )
			{
				pString->SetState( nIndex, glyphstateNormal );
				pFace = pSrcFace;
			}

			if ( 0 != pFace->num_charmaps )
			{
				int nCurCMapIndex = FT_Get_Charmap_Index( pFace->charmap );
				if ( nCurCMapIndex != nCMapIndex )
				{
					nCMapIndex = max( 0, nCMapIndex );
					FT_Set_Charmap( pFace, pFace->charmaps[nCMapIndex] );
				}
			}

			if ( m_bUseKerning && unPrevGID && ( nIndex >= 0 && pString->GetAt( nIndex )->eState == pString->GetAt( nIndex - 1 )->eState ) )
			{
				FT_Vector oDelta;
				FT_Get_Kerning( pFace, unPrevGID, unGID, FT_KERNING_DEFAULT, &oDelta );
				fPenX += (float)(oDelta.x >> 6);
			}

			float fX = pString->m_fX + fPenX;
			float fY = pString->m_fY + fPenY;

			// Начальную точку рассчитываем сразу исходя из глобальной матрицы 
			float fXX = (float)(pString->m_arrCTM[4] + fX * pString->m_arrCTM[0] + fY * pString->m_arrCTM[2] - pString->m_fX );
			float fYY = (float)(pString->m_arrCTM[5] + fX * pString->m_arrCTM[1] + fY * pString->m_arrCTM[3] - pString->m_fY );

			pString->SetStartPoint( nIndex, fXX, fYY);

			pString->SetMetrics( nIndex, oSizes.oMetrics.fWidth, oSizes.oMetrics.fHeight, oSizes.oMetrics.fHoriAdvance, oSizes.oMetrics.fHoriBearingX, oSizes.oMetrics.fHoriBearingY, oSizes.oMetrics.fVertAdvance, oSizes.oMetrics.fVertBearingX, oSizes.oMetrics.fVertBearingY );
			pString->SetBBox( nIndex, oSizes.oBBox.fMinX, oSizes.oBBox.fMaxY, oSizes.oBBox.fMaxX, oSizes.oBBox.fMinY );
			fPenX += oSizes.fAdvanceX + m_fCharSpacing;

			if ( m_bNeedDoBold )
			{
				// Когда текст делаем жирным сами, то мы увеличиваем расстояние на 1 пиксель в ширину (независимо от DPI и размера текста всегда 1 пиксель)
				fPenX += 1;
			}

			oSizes.bBitmap = false;
			oSizes.oBitmap.nX        = 0;
			oSizes.oBitmap.nY        = 0;
			oSizes.oBitmap.nHeight   = 0;
			oSizes.oBitmap.nWidth    = 0;
			oSizes.oBitmap.bFreeData = FALSE;
			oSizes.oBitmap.pData     = NULL;
			oSizes.oBitmap.bAA       = FALSE;
			//pCurGlyph->bBitmap = m_oCacheSizes.Get(nCacheIndex).bBitmap;//m_arrCacheSizes[nCacheIndex].bBitmap;
			//pCurGlyph->oBitmap = m_oCacheSizes.Get(nCacheIndex).oBitmap;//m_arrCacheSizes[nCacheIndex].oBitmap;

		}
		unPrevGID = unGID;
	}

	pString->m_fEndX = fPenX + pString->m_fX;
	pString->m_fEndY = fPenY + pString->m_fY;

	if ( pDefFont )
		pDefFont->UpdateMatrix2();
	UpdateMatrix2();

	return TRUE;
}
BOOL CFreeTypeFont::GetString2(CGlyphString *pString)
{
	if ( pString->GetLength() <= 0 )
		return TRUE;

	unsigned int unPrevGID = 0;
	float fPenX = 0, fPenY = 0;

	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)(m_pFontFile);
	FT_Face pSrcFace = pFontFile->m_pFace;

	FT_Face pDefFace = NULL;
	CFreeTypeFont *pDefFont = NULL;
	if ( m_pDefaultFont )
	{
		pDefFont = (CFreeTypeFont*)m_pDefaultFont;
		pDefFace = ((CFreeTypeFontFile *)(pDefFont->m_pFontFile))->m_pFace;
	}

	for ( int nIndex = 0; nIndex < pString->GetLength(); nIndex++ )
	{
		// Сначала мы все рассчитываем исходя только из матрицы шрифта FontMatrix
		if ( pDefFont )
			pDefFont->UpdateMatrix1();
		UpdateMatrix1();

		FT_Face pFace = pSrcFace;
		TGlyph *pCurGlyph = pString->GetAt( nIndex );

		unsigned short ushUnicode = (unsigned short)pCurGlyph->lUnicode;
		int nCacheIndex = FindInSizesCache( ushUnicode );
		unsigned int unGID = 0;
		
		if ( 0xFFFF == nCacheIndex )
		{
			int nCMapIndex = 0;
			unGID = (unsigned int)SetCMapForCharCode( ushUnicode, &nCMapIndex );
			TFontCacheSizes oSizes;
			oSizes.ushUnicode = ushUnicode;

			if ( !( ( unGID > 0 ) || ( -1 != m_nSymbolic && ( ushUnicode < 0xF000 )  && 0 < ( unGID = (unsigned int)SetCMapForCharCode( ushUnicode + 0xF000, &nCMapIndex ) ) ) ) )
			{
				// Пробуем загрузить через стандартный шрифт
				if ( FALSE == m_bUseDefaultFont || NULL == pDefFont || 0 >= ( unGID = pDefFont->SetCMapForCharCode( ushUnicode, &nCMapIndex ) )  )
				{
					if ( m_nDefaultChar < 0 )
					{
						oSizes.ushGID    = -1;
						oSizes.eState    = glyphstateMiss;
						oSizes.fAdvanceX = (float)(pSrcFace->size->metrics.max_advance >> 6) / 2.f;

						// В стандартном шрифте данный символ тоже не рисуется
						pString->SetStartPoint( nIndex, fPenX, fPenY );
						pString->SetBBox( nIndex, 0, 0, 0, 0 );
						pString->SetState( nIndex, glyphstateMiss );

						FT_Fixed lAdv = 0;

						fPenX += oSizes.fAdvanceX + m_fCharSpacing;		
						continue;
					}
					else
					{
						unGID = m_nDefaultChar;
						oSizes.eState = glyphstateNormal;

						pString->SetState( nIndex, glyphstateNormal );
						pFace = pSrcFace;
					}
				}
				else
				{
					oSizes.eState = glyphstateDeafault;

					pString->SetState( nIndex, glyphstateDeafault );
					pFace = pDefFace;
				}
			}
			else
			{
				oSizes.eState = glyphstateNormal;

				pString->SetState( nIndex, glyphstateNormal );
				pFace = pSrcFace;
			}
			oSizes.ushGID     = unGID;
			oSizes.nCMapIndex = nCMapIndex;

			if ( m_bUseKerning && unPrevGID && ( nIndex >= 0 && pString->GetAt( nIndex )->eState == pString->GetAt( nIndex - 1 )->eState ) )
			{
				FT_Vector oDelta;
				FT_Get_Kerning( pFace, unPrevGID, unGID, FT_KERNING_DEFAULT, &oDelta );
				fPenX += (float)(oDelta.x >> 6);
			}

			float fX = pString->m_fX + fPenX;
			float fY = pString->m_fY + fPenY;

			// Начальную точку рассчитываем сразу исходя из глобальной матрицы 
			float fXX = (float)(pString->m_arrCTM[4] + fX * pString->m_arrCTM[0] + fY * pString->m_arrCTM[2] - pString->m_fX );
			float fYY = (float)(pString->m_arrCTM[5] + fX * pString->m_arrCTM[1] + fY * pString->m_arrCTM[3] - pString->m_fY );

			pString->SetStartPoint( nIndex, fXX, fYY);

			if ( pDefFont )
				pDefFont->UpdateMatrix2();
			UpdateMatrix2();

			if ( FT_Load_Glyph( pFace, unGID, LOAD_MODE ) )
			{
				pString->SetStartPoint( nIndex, -0xFFFF, -0xFFFF );
				pString->SetState( nIndex, glyphstateMiss );
				continue;
			}

			FT_Glyph pGlyph = NULL;
			if ( FT_Get_Glyph( pFace->glyph, &pGlyph ) )
			{
				pString->SetStartPoint( nIndex, -0xFFFF, -0xFFFF );
				pString->SetState( nIndex, glyphstateMiss );
				continue;
			}

			FT_BBox oBBox;
			FT_Glyph_Get_CBox( pGlyph, ft_glyph_bbox_gridfit, &oBBox );
			FT_Done_Glyph( pGlyph );

			float fAdvX = (float)(pFace->glyph->linearHoriAdvance * m_dUnitsKoef / pFace->units_per_EM );
			oSizes.fAdvanceX = fAdvX;
			oSizes.oBBox.fMinX = (float)(oBBox.xMin >> 6);
			oSizes.oBBox.fMaxX = (float)(oBBox.xMax >> 6);
			oSizes.oBBox.fMinY = (float)(oBBox.yMin >> 6);
			oSizes.oBBox.fMaxY = (float)(oBBox.yMax >> 6);

			oSizes.oMetrics.fHeight       = (float)(pFace->glyph->metrics.height       >> 6);
			oSizes.oMetrics.fHoriAdvance  = (float)(pFace->glyph->metrics.horiAdvance  >> 6);
			oSizes.oMetrics.fHoriBearingX = (float)(pFace->glyph->metrics.horiBearingX >> 6);
			oSizes.oMetrics.fHoriBearingY = (float)(pFace->glyph->metrics.horiBearingY >> 6);
			oSizes.oMetrics.fVertAdvance  = (float)(pFace->glyph->metrics.vertAdvance  >> 6);
			oSizes.oMetrics.fVertBearingX = (float)(pFace->glyph->metrics.vertBearingX >> 6);
			oSizes.oMetrics.fVertBearingY = (float)(pFace->glyph->metrics.vertBearingY >> 6);
			oSizes.oMetrics.fWidth        = (float)(pFace->glyph->metrics.width        >> 6);

			pString->SetMetrics( nIndex, oSizes.oMetrics.fWidth, oSizes.oMetrics.fHeight, oSizes.oMetrics.fHoriAdvance, oSizes.oMetrics.fHoriBearingX, oSizes.oMetrics.fHoriBearingY, oSizes.oMetrics.fVertAdvance, oSizes.oMetrics.fVertBearingX, oSizes.oMetrics.fVertBearingY );
			pString->SetBBox( nIndex, oSizes.oBBox.fMinX, oSizes.oBBox.fMaxY, oSizes.oBBox.fMaxX, oSizes.oBBox.fMinY );

			fPenX  += oSizes.fAdvanceX + m_fCharSpacing;
			if ( m_bNeedDoBold )
			{
				// Когда текст делаем жирным сами, то мы увеличиваем расстояние на 1 пиксель в ширину (независимо от DPI и размера текста всегда 1 пиксель)
				fPenX += 1;
			}

			pCurGlyph->bBitmap  = true;
			FT_GlyphSlot pGlyphSlot = pFace->glyph;
			if ( FT_Render_Glyph( pGlyphSlot, REND_MODE/*m_bAntiAliasing ? ft_render_mode_normal : ft_render_mode_mono*/ ) ) 
			{
				return FALSE;
			}

			TGlyphBitmap *pBitmap = &(pCurGlyph->oBitmap);
			pBitmap->nX      = pGlyphSlot->bitmap_left;
			pBitmap->nY      = pGlyphSlot->bitmap_top;
			pBitmap->nWidth  = pGlyphSlot->bitmap.width;
			pBitmap->nHeight = pGlyphSlot->bitmap.rows;
			pBitmap->bAA     = m_bAntiAliasing;

			int nRowSize = 0;
			if ( m_bAntiAliasing ) 
			{
				if ( m_bNeedDoBold )
					pBitmap->nWidth++;

				nRowSize = pBitmap->nWidth;
			} 
			else 
			{
				nRowSize = (pBitmap->nWidth + 7) >> 3;
			}

			if (0 != (nRowSize * pBitmap->nHeight))
				pBitmap->pData = (unsigned char *)malloc( nRowSize * pBitmap->nHeight );
			else
				pBitmap->pData = NULL;

			pBitmap->bFreeData = FALSE; // Все удаляется в кэше (во время очистки или замены)

			int nIndex2;
			unsigned char *pDstBuffer, *pSrcBuffer;

			if (NULL != pBitmap->pData)
			{
				//double dKoef = ( 255 + 10 ) / (double)255;
				if ( !m_bNeedDoBold || !m_bAntiAliasing )
				{
					for ( nIndex2 = 0, pDstBuffer = pBitmap->pData, pSrcBuffer = pGlyphSlot->bitmap.buffer; nIndex2 < pBitmap->nHeight; ++nIndex2, pDstBuffer += nRowSize, pSrcBuffer += pGlyphSlot->bitmap.pitch ) 
					{
						//for ( int nX = 0; nX < nRowSize; nX++ )
						//{
						//	pDstBuffer[nX] = min( 255, ( pSrcBuffer[nX]) * dKoef );
						//}
						memcpy( pDstBuffer, pSrcBuffer, nRowSize );
					}
				}
				else
				{
					int nY, nX;
					for ( nY = 0, pDstBuffer = pBitmap->pData, pSrcBuffer = pGlyphSlot->bitmap.buffer; nY < pBitmap->nHeight; ++nY, pDstBuffer += nRowSize, pSrcBuffer += pGlyphSlot->bitmap.pitch ) 
					{
						for ( nX = pBitmap->nWidth - 1; nX >= 0; nX-- )
						{
							if ( 0 != nX )
							{
								int nFirstByte, nSecondByte;

								if ( pBitmap->nWidth - 1 == nX )
									nFirstByte = 0;
								else
									nFirstByte = pSrcBuffer[nX];

								nSecondByte = pSrcBuffer[nX - 1];

								pDstBuffer[nX] = min( 255, nFirstByte + nSecondByte);

							}
							else
							{
								pDstBuffer[nX] = pSrcBuffer[nX];
							}
						}
					}

				}
			}

			oSizes.bBitmap			 = pCurGlyph->bBitmap;
			oSizes.oBitmap.bAA       = pBitmap->bAA;
			oSizes.oBitmap.bFreeData = pBitmap->bFreeData;
			oSizes.oBitmap.nX        = pBitmap->nX;
			oSizes.oBitmap.nY        = pBitmap->nY;
			oSizes.oBitmap.nWidth    = pBitmap->nWidth;
			oSizes.oBitmap.nHeight   = pBitmap->nHeight;
			oSizes.oBitmap.pData     = pBitmap->pData;

			AddToSizesCache( oSizes );
		}
		else
		{
			TFontCacheSizes oSizes = m_oCacheSizes.Get(nCacheIndex);

			int nCMapIndex     = oSizes.nCMapIndex;
			unGID              = oSizes.ushGID;
			EGlyphState eState = oSizes.eState;

			if ( glyphstateMiss == eState )
			{
				pString->SetStartPoint( nIndex, fPenX, fPenY );
				pString->SetBBox( nIndex, 0, 0, 0, 0 );
				pString->SetState( nIndex, glyphstateMiss );

				FT_Fixed lAdv = 0;

				fPenX += oSizes.fAdvanceX + m_fCharSpacing;		
				unPrevGID = 0;

				continue;
			}
			else if ( glyphstateDeafault == eState )
			{
				pString->SetState( nIndex, glyphstateDeafault );
				pFace = pDefFace;
			}
			else // if ( glyphstateNormal == eState )
			{
				pString->SetState( nIndex, glyphstateNormal );
				pFace = pSrcFace;
			}

			if ( 0 != pFace->num_charmaps )
			{
				int nCurCMapIndex = FT_Get_Charmap_Index( pFace->charmap );
				if ( nCurCMapIndex != nCMapIndex )
				{
					nCMapIndex = max( 0, nCMapIndex );
					FT_Set_Charmap( pFace, pFace->charmaps[nCMapIndex] );
				}
			}

			if ( m_bUseKerning && unPrevGID && ( nIndex >= 0 && pString->GetAt( nIndex )->eState == pString->GetAt( nIndex - 1 )->eState ) )
			{
				FT_Vector oDelta;
				FT_Get_Kerning( pFace, unPrevGID, unGID, FT_KERNING_DEFAULT, &oDelta );
				fPenX += (float)(oDelta.x >> 6);
			}

			float fX = pString->m_fX + fPenX;
			float fY = pString->m_fY + fPenY;

			// Начальную точку рассчитываем сразу исходя из глобальной матрицы 
			float fXX = (float)(pString->m_arrCTM[4] + fX * pString->m_arrCTM[0] + fY * pString->m_arrCTM[2] - pString->m_fX );
			float fYY = (float)(pString->m_arrCTM[5] + fX * pString->m_arrCTM[1] + fY * pString->m_arrCTM[3] - pString->m_fY );

			pString->SetStartPoint( nIndex, fXX, fYY);

			pString->SetMetrics( nIndex, oSizes.oMetrics.fWidth, oSizes.oMetrics.fHeight, oSizes.oMetrics.fHoriAdvance, oSizes.oMetrics.fHoriBearingX, oSizes.oMetrics.fHoriBearingY, oSizes.oMetrics.fVertAdvance, oSizes.oMetrics.fVertBearingX, oSizes.oMetrics.fVertBearingY );
			pString->SetBBox( nIndex, oSizes.oBBox.fMinX, oSizes.oBBox.fMaxY, oSizes.oBBox.fMaxX, oSizes.oBBox.fMinY );
			fPenX  += oSizes.fAdvanceX + m_fCharSpacing;

			if ( m_bNeedDoBold )
			{
				// Когда текст делаем жирным сами, то мы увеличиваем расстояние на 1 пиксель в ширину (независимо от DPI и размера текста всегда 1 пиксель)
				fPenX += 1;
			}

			pCurGlyph->bBitmap = oSizes.bBitmap;// m_oCacheSizes.Get(nCacheIndex).bBitmap;
			pCurGlyph->oBitmap = oSizes.oBitmap;// m_oCacheSizes.Get(nCacheIndex).oBitmap;
		}
		unPrevGID = unGID;
	}

	pString->m_fEndX = fPenX + pString->m_fX;
	pString->m_fEndY = fPenY + pString->m_fY;

	if ( pDefFont )
		pDefFont->UpdateMatrix2();
	UpdateMatrix2();


	return TRUE;
}
CFontPath *CFreeTypeFont::GetGlyphPath(int nCode) 
{ 
	FT_UInt unGID = SetCMapForCharCode2( nCode );
	if ( unGID <= 0 )
		return NULL;

	static FT_Outline_Funcs pOutlineFuncs = 
	{
#if FREETYPE_MINOR <= 1
		(int (*)(FT_Vector *, void *))&GlyphPathMoveTo,
		(int (*)(FT_Vector *, void *))&GlyphPathLineTo,
		(int (*)(FT_Vector *, FT_Vector *, void *))&GlyphPathConicTo,
		(int (*)(FT_Vector *, FT_Vector *, FT_Vector *, void *))&GlyphPathCubicTo,
#else
		&GlyphPathMoveTo,
		&GlyphPathLineTo,
		&GlyphPathConicTo,
		&GlyphPathCubicTo,
#endif
		0, 0
	};

	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;
	pFontFile->m_pFace->size = m_pSize;

	//FT_Set_Transform( pFontFile->m_pFace, &m_oTextMatrix, NULL );
	FT_GlyphSlot oSlot = pFontFile->m_pFace->glyph;

	// TO DO: Пропустить нулевой (".notdef") символ в TrueType

	if ( FT_Load_Glyph( pFontFile->m_pFace, unGID, FT_LOAD_NO_BITMAP ) ) 
	{
		return NULL;
	}

	FT_Glyph oGlyph;
	if ( FT_Get_Glyph( oSlot, &oGlyph ) ) 
	{
		return NULL;
	}

	TFreeTypeFontPath oGlyphPath;
	oGlyphPath.pPath      = new CFontPath();
	//oGlyphPath.dTextScale = m_dTextScale;
	oGlyphPath.bNeedClose = FALSE;

	FT_Outline_Decompose( &((FT_OutlineGlyph)oGlyph)->outline, &pOutlineFuncs, &oGlyphPath );

	if ( oGlyphPath.bNeedClose ) 
	{
		oGlyphPath.pPath->Close();
	}

	FT_Done_Glyph( oGlyph );
	return oGlyphPath.pPath;
}
short CFreeTypeFont::GetAscender()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	return pFontFile->m_pFace->ascender;
}

short CFreeTypeFont::GetDescender()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	return pFontFile->m_pFace->descender;
}

unsigned short CFreeTypeFont::GetUnitsPerEm()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	return pFontFile->m_pFace->units_per_EM;
}

short CFreeTypeFont::GetLineSpacing()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	return pFontFile->m_pFace->height;
}

char *CFreeTypeFont::GetFamilyName()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	return pFontFile->m_pFace->family_name;
}

long CFreeTypeFont::GetFacesCount()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	return pFontFile->m_pFace->num_faces;
}

long CFreeTypeFont::GetFaceIndex()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	return pFontFile->m_pFace->face_index;
}

long CFreeTypeFont::GetGlyphsCount()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	return pFontFile->m_pFace->num_glyphs;
}

char *CFreeTypeFont::GetStyleName()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	return pFontFile->m_pFace->style_name;
}

short CFreeTypeFont::GetUnderlinePosition()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	return pFontFile->m_pFace->underline_position;	
}
short CFreeTypeFont::GetUnderlineThickness()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	return pFontFile->m_pFace->underline_thickness;	
}
short CFreeTypeFont::GetMaxAdvanceWidth()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	return pFontFile->m_pFace->max_advance_width;
}
short CFreeTypeFont::GetMaxAdvanceHeight()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	return pFontFile->m_pFace->max_advance_height;
}
void CFreeTypeFont::GetBBox(long *plMinX, long *plMinY, long *plMaxX, long *plMaxY)
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	*plMinX = pFontFile->m_pFace->bbox.xMin;
	*plMinY = pFontFile->m_pFace->bbox.yMin;
	*plMaxX = pFontFile->m_pFace->bbox.xMax;
	*plMaxY = pFontFile->m_pFace->bbox.yMax;
}
int CFreeTypeFont::SetCMapForCharCode(long lUnicode, int *pnCMapIndex)
{
	*pnCMapIndex = -1;

	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;
	FT_Face pFace = pFontFile->m_pFace;

	if ( m_bStringGID || 0 == pFace->num_charmaps )
		return lUnicode;

	int nCharIndex = 0;

	if ( !pFace )
		return nCharIndex;

	for ( int nIndex = 0; nIndex < pFace->num_charmaps; nIndex++ )
	{
		FT_CharMap pCharMap = pFace->charmaps[nIndex];

		if ( FT_Set_Charmap( pFace, pCharMap ) )
			continue;

		FT_Encoding pEncoding = pCharMap->encoding;

		if ( FT_ENCODING_UNICODE == pEncoding )
		{
			if ( nCharIndex = FT_Get_Char_Index( pFace, lUnicode ) )
			{
				*pnCMapIndex = nIndex;
				return nCharIndex;
			}
		}
		else if ( FT_ENCODING_NONE == pEncoding || FT_ENCODING_MS_SYMBOL == pEncoding || FT_ENCODING_APPLE_ROMAN == pEncoding )
		{
			FT_ULong  charcode;
			FT_UInt   gindex;


			charcode = FT_Get_First_Char( pFace, &gindex );                   
			while ( gindex != 0 )                                            
			{                                                               
				charcode = FT_Get_Next_Char( pFace, charcode, &gindex );       
				if ( charcode == lUnicode )
				{
					nCharIndex = gindex;
					*pnCMapIndex = nIndex;
					break;
				}
			}                                                               

			if ( nCharIndex = FT_Get_Char_Index( pFace, lUnicode ) )
			{
				*pnCMapIndex = nIndex;
			}
		}

		//else if ( FT_ENCODING_ADOBE_STANDARD == pEncoding )
		//{
		//	if ( nCharIndex = FT_Get_Char_Index( pFace, lUnicode ) )
		//		return nCharIndex;

		//}
		//else if ( FT_ENCODING_ADOBE_CUSTOM == pEncoding )
		//{
		//	if ( nCharIndex = FT_Get_Char_Index( pFace, lUnicode ) )
		//		return nCharIndex;

		//}
		//else if ( FT_ENCODING_ADOBE_EXPERT == pEncoding )
		//{
		//	if ( nCharIndex = FT_Get_Char_Index( pFace, lUnicode ) )
		//		return nCharIndex;

		//}
	}

	return nCharIndex;
}

int CFreeTypeFont::SetCMapForCharCode2(long lUnicode)
{
	if ( m_bStringGID )
		return lUnicode;

	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	FT_Int unGID;
	int nCMapIndex = 0;
	int nCacheIndex = m_arrCacheSizesIndexs[(unsigned short)lUnicode];
	if ( 0xFFFF == nCacheIndex )
	{
		return unGID = SetCMapForCharCode( lUnicode, &nCMapIndex );
	}
	else
	{
		FT_Face pFace = pFontFile->m_pFace;
		TFontCacheSizes oSizes = m_oCacheSizes.Get(nCacheIndex);
		unGID = oSizes.ushGID;
		nCMapIndex = oSizes.nCMapIndex;
		if ( 0 != pFace->num_charmaps )
		{
			int nCurCMapIndex = FT_Get_Charmap_Index( pFace->charmap );
			if ( nCurCMapIndex != nCMapIndex )
			{
				nCMapIndex = max( 0, nCMapIndex );
				FT_Set_Charmap( pFace, pFace->charmaps[nCMapIndex] );
			}
		}
	}

	return unGID;
}
unsigned long CFreeTypeFont::GetCodeByGID(unsigned short unGID)
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;
	FT_Face pFace = pFontFile->m_pFace;

	if ( 0 == pFace->num_charmaps )
		return unGID;

	int nCharCode = 0;

	if ( !pFace )
		return nCharCode;

	for ( int nIndex = 0; nIndex < pFace->num_charmaps; nIndex++ )
	{
		FT_CharMap pCharMap = pFace->charmaps[nIndex];

		if ( FT_Set_Charmap( pFace, pCharMap ) )
			continue;

		FT_ULong unCharCode;
        FT_UInt  unCurGID;         
        
		unCharCode = FT_Get_First_Char( pFace, &unCurGID );

        while ( unCurGID != 0 )                                            
        {                                                                
			if ( unGID == unCurGID )
				return unCharCode;

			unCharCode = FT_Get_Next_Char( pFace, unCharCode, &unCurGID );        
        }                                                                
	}

	return 0;
}
void CFreeTypeFont::GetPanose(char **ppPanose)
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;
	FT_Face pFace = pFontFile->m_pFace;

	TT_OS2 *pTable = (TT_OS2 *)FT_Get_Sfnt_Table( pFace, ft_sfnt_os2 );

	::memset( *ppPanose, 0x00, 10 );
	if ( NULL == pTable )
		return;

	::memcpy( *ppPanose, pTable->panose, 10 );
}
bool CFreeTypeFont::IsFixedWidth()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	return FT_IS_FIXED_WIDTH( pFontFile->m_pFace ) != 0;
}
bool CFreeTypeFont::IsCharAvailable(long lUnicode)
{
	return ( SetCMapForCharCode2( lUnicode ) > 0 );
}
void CFreeTypeFont::SetSizeAndDpi(float fSize, unsigned int unHorDpi, unsigned int unVerDpi)
{
	if ( m_pDefaultFont )
	{
		m_pDefaultFont->SetSizeAndDpi( fSize, unHorDpi, unVerDpi );
	}

	CFont::ClearCache();
	ClearSizesCache();

	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;
	FT_Face pFace = pFontFile->m_pFace;

	float fOldSize = m_fSize;
	float fNewSize = fSize;
	float fKoef    = fNewSize / fOldSize;

	if ( fKoef > 1.001 || fKoef < 0.999 || unHorDpi != m_unHorDpi || unVerDpi != m_unVerDpi )
	{
		m_unHorDpi = unHorDpi;
		m_unVerDpi = unVerDpi;

		if ( fKoef > 1.001 || fKoef < 0.999 )
		{
			m_fSize    = fNewSize;
			UpdateMatrix0();
		}

		m_dUnitsKoef = m_unHorDpi / 72.0 * m_fSize;

		// Выставляем размер шрифта (dSize) и DPI 
		if ( FT_Set_Char_Size( pFace, 0, (int)(fNewSize * 64), unHorDpi, unVerDpi ) ) 
		{
			return;
		}
	}
}
const char *CFreeTypeFont::GetFontFormat()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;
	FT_Face pFace = pFontFile->m_pFace;

	return FT_Get_X11_Font_Format( pFace );
}

int CFreeTypeFont::IsUnicodeRangeAvailable(unsigned long ulBit, unsigned int un4ByteIndex)
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;
	FT_Face pFace = pFontFile->m_pFace;

	TT_OS2 *pOs2 = (TT_OS2 *)FT_Get_Sfnt_Table( pFace, ft_sfnt_os2 );
	if ( NULL == pOs2 || 0xFFFF == pOs2->version )
		return -1;

	int nResult = 0;

	unsigned long ulMult = 1;
	for ( unsigned long ulIndex = 0; ulIndex < ulBit; ulIndex++ )
		ulMult <<= 1;

	switch(un4ByteIndex)
	{
	case 0: if ( pOs2->ulUnicodeRange1  & ulMult ) nResult = 1; break;
	case 1: if ( pOs2->ulUnicodeRange2  & ulMult ) nResult = 1; break;
	case 2: if ( pOs2->ulUnicodeRange3  & ulMult ) nResult = 1; break;
	case 3: if ( pOs2->ulUnicodeRange4  & ulMult ) nResult = 1; break;
	case 4: if ( pOs2->ulCodePageRange1 & ulMult ) nResult = 1; break;
	case 5: if ( pOs2->ulCodePageRange2 & ulMult ) nResult = 1; break;
	}

	// Специальная ветка для случаев, когда charset может быть задан не через значения
	// ulCodePageRange, а непосредственно через тип Cmap.
	
	//  Charset Name       Charset Value(hex)  Codepage number   Platform_ID   Encoding_ID   Description
	//  -------------------------------------------------------------------------------------------------
	//
	//  SYMBOL_CHARSET            2 (x02)                             3            0           Symbol
	//  SHIFTJIS_CHARSET        128 (x80)             932             3            2           ShiftJIS
	//  GB2313_CHARSET          134 (x86)             936             3            3           PRC
	//  CHINESEBIG5_CHARSET     136 (x88)             950             3            4           Big5
	//  HANGEUL_CHARSET         129 (x81)             949             3            5           Wansung
	//  JOHAB_CHARSET	        130 (x82)            1361             3            6           Johab

	if ( 4 == un4ByteIndex && 0 == nResult )
	{
		for( int nIndex = 0; nIndex < pFace->num_charmaps; nIndex++ )
		{
			// Symbol
			if ( 31 == ulBit && 0 == pFace->charmaps[nIndex]->encoding_id && 3 == pFace->charmaps[nIndex]->platform_id )
			{
				nResult = 1;
				break;
			}

			// ShiftJIS
			if ( 17 == ulBit && 2 == pFace->charmaps[nIndex]->encoding_id && 3 == pFace->charmaps[nIndex]->platform_id )
			{
				nResult = 1;
				break;
			}

			// PRC
			if ( 18 == ulBit && 3 == pFace->charmaps[nIndex]->encoding_id && 3 == pFace->charmaps[nIndex]->platform_id )
			{
				nResult = 1;
				break;
			}

			// Big5
			if ( 20 == ulBit && 4 == pFace->charmaps[nIndex]->encoding_id && 3 == pFace->charmaps[nIndex]->platform_id )
			{
				nResult = 1;
				break;
			}

			// Wansung
			if ( 19 == ulBit && 5 == pFace->charmaps[nIndex]->encoding_id && 3 == pFace->charmaps[nIndex]->platform_id )
			{
				nResult = 1;
				break;
			}

			// Johab
			if ( 21 == ulBit && 6 == pFace->charmaps[nIndex]->encoding_id && 3 == pFace->charmaps[nIndex]->platform_id )
			{
				nResult = 1;
				break;
			}
		}
	}

	return nResult;
}
unsigned short CFreeTypeFont::GetNameIndex(char *sName)
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;
	FT_Face pFace = pFontFile->m_pFace;

	unsigned int unGID = FT_Get_Name_Index( pFace, sName );
	return unGID;
}
void CFreeTypeFont::ResetFontMatrix()
{
	if ( m_pDefaultFont )
	{
		m_pDefaultFont->ResetFontMatrix();
	}

	if ( m_bNeedDoItalic )
	{
		m_arrdFontMatrix[0] = 1;
		m_arrdFontMatrix[1] = 0;
		m_arrdFontMatrix[2] = FONT_ITALIC_ANGLE;
		m_arrdFontMatrix[3] = 1;
		m_arrdFontMatrix[4] = 0;
		m_arrdFontMatrix[5] = 0;
	}
	else
	{
		m_arrdFontMatrix[0] = 1;
		m_arrdFontMatrix[1] = 0;
		m_arrdFontMatrix[2] = 0;
		m_arrdFontMatrix[3] = 1;
		m_arrdFontMatrix[4] = 0;
		m_arrdFontMatrix[5] = 0;
	}

	UpdateMatrix0();

	//ClearSizesCache();
}
void CFreeTypeFont::ApplyTransform(float fA, float fB, float fC, float fD, float fE, float fF)
{
	if ( m_pDefaultFont )
	{
		m_pDefaultFont->ApplyTransform( fA, fB, fC, fD, fE, fF );
	}

	double arrTemp[6] = { m_arrdFontMatrix[0], m_arrdFontMatrix[1], m_arrdFontMatrix[2], m_arrdFontMatrix[3] };

	m_arrdFontMatrix[0] = arrTemp[0] * fA + arrTemp[1] * fC;
	m_arrdFontMatrix[1] = arrTemp[0] * fB + arrTemp[1] * fD;
	m_arrdFontMatrix[2] = arrTemp[2] * fA + arrTemp[3] * fC;
	m_arrdFontMatrix[3] = arrTemp[2] * fB + arrTemp[3] * fD;
	m_arrdFontMatrix[4] = arrTemp[4] * fA + arrTemp[5] * fC + fE;
	m_arrdFontMatrix[5] = arrTemp[4] * fB + arrTemp[5] * fD + fF;

	UpdateMatrix0();

	//ClearSizesCache();
}
void CFreeTypeFont::SetFontMatrix(float fA, float fB, float fC, float fD, float fE, float fF)
{
	if ( m_pDefaultFont )
	{
		m_pDefaultFont->SetFontMatrix( fA, fB, fC, fD, fE, fF );
	}

	if ( m_bNeedDoItalic ) 
	{
		m_arrdFontMatrix[0] = fA;
		m_arrdFontMatrix[1] = fB;
		m_arrdFontMatrix[2] = fC + fA * FONT_ITALIC_ANGLE;
		m_arrdFontMatrix[3] = fD + fB * FONT_ITALIC_ANGLE;
		m_arrdFontMatrix[4] = fE;
		m_arrdFontMatrix[5] = fF;
	}
	else
	{
		m_arrdFontMatrix[0] = fA;
		m_arrdFontMatrix[1] = fB;
		m_arrdFontMatrix[2] = fC;
		m_arrdFontMatrix[3] = fD;
		m_arrdFontMatrix[4] = fE;
		m_arrdFontMatrix[5] = fF;
	}

	ClearSizesCache();
}

void CFreeTypeFont::SetTextMatrix(float fA, float fB, float fC, float fD, float fE, float fF)
{
	if ( m_pDefaultFont )
	{
		m_pDefaultFont->SetTextMatrix( fA, fB, fC, fD, fE, fF );
	}

	m_arrdTextMatrix[0] =  fA;
	m_arrdTextMatrix[1] = -fB;
	m_arrdTextMatrix[2] = -fC;
	m_arrdTextMatrix[3] =  fD;
	m_arrdTextMatrix[4] =  fE;
	m_arrdTextMatrix[5] =  fF;

	ClearSizesCache();
}

void CFreeTypeFont::ResetTextMatrix()
{
	if ( m_pDefaultFont )
	{
		m_pDefaultFont->ResetTextMatrix();
	}

	m_arrdTextMatrix[0] =  1;
	m_arrdTextMatrix[1] = -0;
	m_arrdTextMatrix[2] = -0; // 
	m_arrdTextMatrix[3] =  1; // 
	m_arrdTextMatrix[4] =  0;
	m_arrdTextMatrix[5] =  0;

	//ClearSizesCache();
}
inline void CFreeTypeFont::UpdateMatrix0()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;
	FT_Face pFace = pFontFile->m_pFace;

	double dSize = m_fSize;

	m_dTextScale = sqrt( m_arrdTextMatrix[2] * m_arrdTextMatrix[2] + m_arrdTextMatrix[3] * m_arrdTextMatrix[3] );

	double dDiv = pFace->bbox.xMax > 20000 ? 65536 : 1;

	// Вычисляем BBox

	if ( pFace->units_per_EM == 0 )
		pFace->units_per_EM = 2048;

	int nX = (int)((m_arrdFontMatrix[0] * pFace->bbox.xMin + m_arrdFontMatrix[2] * pFace->bbox.yMin) * dSize / (dDiv * pFace->units_per_EM));
	m_nMinX = m_nMaxX = nX;

	int nY = (int)((m_arrdFontMatrix[1] * pFace->bbox.xMin + m_arrdFontMatrix[3] * pFace->bbox.yMin) * dSize / (dDiv * pFace->units_per_EM));
	m_nMinY = m_nMaxY = nY;

	nX = (int)((m_arrdFontMatrix[0] * pFace->bbox.xMin + m_arrdFontMatrix[2] * pFace->bbox.yMax) * dSize / (dDiv * pFace->units_per_EM));

	if ( nX < m_nMinX ) 
	{
		m_nMinX = nX;
	} 
	else if ( nX > m_nMaxX ) 
	{
		m_nMaxX = nX;
	}

	nY = (int)((m_arrdFontMatrix[1] * pFace->bbox.xMin + m_arrdFontMatrix[3] * pFace->bbox.yMax) * dSize / (dDiv * pFace->units_per_EM));

	if ( nY < m_nMinY ) 
	{
		m_nMinY = nY;
	} 
	else if ( nY > m_nMaxY ) 
	{
		m_nMaxY = nY;
	}

	nX = (int)((m_arrdFontMatrix[0] * pFace->bbox.xMax + m_arrdFontMatrix[2] * pFace->bbox.yMin) * dSize / (dDiv * pFace->units_per_EM));
	if ( nX < m_nMinX ) 
	{
		m_nMinX = nX;
	} 
	else if ( nX > m_nMaxX ) 
	{
		m_nMaxX = nX;
	}

	nY = (int)((m_arrdFontMatrix[1] * pFace->bbox.xMax + m_arrdFontMatrix[3] * pFace->bbox.yMin) * dSize / (dDiv * pFace->units_per_EM));
	if ( nY < m_nMinY ) 
	{
		m_nMinY = nY;
	} 
	else if ( nY > m_nMaxY ) 
	{
		m_nMaxY = nY;
	}

	nX = (int)((m_arrdFontMatrix[0] * pFace->bbox.xMax + m_arrdFontMatrix[2] * pFace->bbox.yMax) * dSize / (dDiv * pFace->units_per_EM));
	if ( nX < m_nMinX ) 
	{
		m_nMinX = nX;
	} 
	else if ( nX > m_nMaxX ) 
	{
		m_nMaxX = nX;
	}

	nY = (int)((m_arrdFontMatrix[1] * pFace->bbox.xMax + m_arrdFontMatrix[3] * pFace->bbox.yMax) * dSize / (dDiv * pFace->units_per_EM));
	if ( nY < m_nMinY ) 
	{
		m_nMinY = nY;
	} 
	else if ( nY > m_nMaxY ) 
	{
		m_nMaxY = nY;
	}

	// This is a kludge: some buggy PDF generators embed fonts with  zero bounding boxes.
	if ( m_nMaxX == m_nMinX ) 
	{
		m_nMinX = 0;
		m_nMaxX = (int)dSize;
	}

	if ( m_nMaxY == m_nMinY ) 
	{
		m_nMinY = 0;
		m_nMaxY = (int)((double)1.2 * dSize);
	}

	// Вычислим матрицу преобразования (FontMatrix)
	m_oFontMatrix.xx = (FT_Fixed)(m_arrdFontMatrix[0] * 65536);
	m_oFontMatrix.yx = (FT_Fixed)(m_arrdFontMatrix[1] * 65536);
	m_oFontMatrix.xy = (FT_Fixed)(m_arrdFontMatrix[2] * 65536);
	m_oFontMatrix.yy = (FT_Fixed)(m_arrdFontMatrix[3] * 65536);

	m_oTextMatrix.xx = (FT_Fixed)((m_arrdTextMatrix[0] / m_dTextScale) * 65536);
	m_oTextMatrix.yx = (FT_Fixed)((m_arrdTextMatrix[1] / m_dTextScale) * 65536);
	m_oTextMatrix.xy = (FT_Fixed)((m_arrdTextMatrix[2] / m_dTextScale) * 65536);
	m_oTextMatrix.yy = (FT_Fixed)((m_arrdTextMatrix[3] / m_dTextScale) * 65536);

	FT_Set_Transform( pFontFile->m_pFace, &m_oFontMatrix, NULL );
}

inline void CFreeTypeFont::UpdateMatrix1()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	m_oFontMatrix.xx = (FT_Fixed)(m_arrdFontMatrix[0] * 65536);
	m_oFontMatrix.yx = (FT_Fixed)(m_arrdFontMatrix[1] * 65536);
	m_oFontMatrix.xy = (FT_Fixed)(m_arrdFontMatrix[2] * 65536);
	m_oFontMatrix.yy = (FT_Fixed)(m_arrdFontMatrix[3] * 65536);

	FT_Set_Transform( pFontFile->m_pFace, &m_oFontMatrix, NULL );
}

inline void CFreeTypeFont::UpdateMatrix2()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;

	m_oFontMatrix.xx = (FT_Fixed)(( m_arrdFontMatrix[0] * m_arrdTextMatrix[0] + m_arrdFontMatrix[1] * m_arrdTextMatrix[2] ) * 65536);
	m_oFontMatrix.yx = (FT_Fixed)(( m_arrdFontMatrix[0] * m_arrdTextMatrix[1] + m_arrdFontMatrix[1] * m_arrdTextMatrix[3] ) * 65536);
	m_oFontMatrix.xy = (FT_Fixed)(( m_arrdFontMatrix[2] * m_arrdTextMatrix[0] + m_arrdFontMatrix[3] * m_arrdTextMatrix[2] ) * 65536);
	m_oFontMatrix.yy = (FT_Fixed)(( m_arrdFontMatrix[2] * m_arrdTextMatrix[1] + m_arrdFontMatrix[3] * m_arrdTextMatrix[3] ) * 65536);

	FT_Set_Transform( pFontFile->m_pFace, &m_oFontMatrix, NULL );
}

void CFreeTypeFont::InitSizesCache()
{
	m_oCacheSizes.Init();
}
void CFreeTypeFont::ClearSizesCache()
{
	m_oCacheSizes.Clear();
	::memset( m_arrCacheSizesIndexs, 0xFF, FONT_CACHE_SIZES_INDEXES_SIZE_2 );
}
void CFreeTypeFont::AddToSizesCache(TFontCacheSizes oSizes)
{
	m_arrCacheSizesIndexs[oSizes.ushUnicode] = m_oCacheSizes.Add( oSizes );
}
__forceinline int CFreeTypeFont::FindInSizesCache(int nCode)
{
	return m_arrCacheSizesIndexs[nCode];
}
void CFreeTypeFont::SetStringGID(BOOL bGID)
{
	if ( m_bStringGID == bGID )
		return;

	ClearSizesCache();
	m_bStringGID = bGID;
}
BOOL CFreeTypeFont::GetStringGID()
{
	return m_bStringGID;
}
int CFreeTypeFont::GetDefaultChar()
{
	FT_Face pFace = ((CFreeTypeFontFile *)m_pFontFile)->m_pFace;

	TT_OS2 *pTable = (TT_OS2 *)FT_Get_Sfnt_Table( pFace, ft_sfnt_os2 );

	if ( NULL == pTable )
	{
		return (m_nDefaultChar = -1);
	}

	return (m_nDefaultChar = pTable->usDefaultChar);
}
int CFreeTypeFont::GetSymbolicCmapIndex()
{
	CFreeTypeFontFile *pFontFile = (CFreeTypeFontFile *)m_pFontFile;
	FT_Face pFace = pFontFile->m_pFace;

	TT_OS2 *pOs2 = (TT_OS2 *)FT_Get_Sfnt_Table( pFace, ft_sfnt_os2 );
	if ( NULL == pOs2 || 0xFFFF == pOs2->version )
		return -1;

	// Проверяем установлен ли 31 бит
	if ( !( pOs2->ulCodePageRange1 & 0x80000000 ) && !( pOs2->ulCodePageRange1 == 0 && pOs2->ulCodePageRange2 == 0 ) )
		return -1;


	for( int nIndex = 0; nIndex < pFace->num_charmaps; nIndex++ )
	{
		// Symbol
		if ( 0 == pFace->charmaps[nIndex]->encoding_id && 3 == pFace->charmaps[nIndex]->platform_id )
			return nIndex;
	}

	return -1;
}