1572 lines
40 KiB
C++
1572 lines
40 KiB
C++
/*
|
||
* (c) Copyright Ascensio System SIA 2010-2023
|
||
*
|
||
* This program is a free software product. You can redistribute it and/or
|
||
* modify it under the terms of the GNU Affero General Public License (AGPL)
|
||
* version 3 as published by the Free Software Foundation. In accordance with
|
||
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
|
||
* that Ascensio System SIA expressly excludes the warranty of non-infringement
|
||
* of any third-party rights.
|
||
*
|
||
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
|
||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
|
||
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||
*
|
||
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
|
||
* street, Riga, Latvia, EU, LV-1050.
|
||
*
|
||
* The interactive user interfaces in modified source and object code versions
|
||
* of the Program must display Appropriate Legal Notices, as required under
|
||
* Section 5 of the GNU AGPL version 3.
|
||
*
|
||
* Pursuant to Section 7(b) of the License you must retain the original Product
|
||
* logo when distributing the program. Pursuant to Section 7(e) we decline to
|
||
* grant you any rights under trademark law for use of our trademarks.
|
||
*
|
||
* All the Product's GUI elements, including illustrations and icon sets, as
|
||
* well as technical writing content are licensed under the terms of the
|
||
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
|
||
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||
*
|
||
*/
|
||
#include "FontManager.h"
|
||
#include "internal/ftobjs.h"
|
||
#include "../common/Types.h"
|
||
#include "../common/File.h"
|
||
|
||
#ifdef GetCharWidth
|
||
#undef GetCharWidth
|
||
#endif
|
||
|
||
#ifndef max
|
||
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||
#endif
|
||
|
||
#ifndef min
|
||
#define min(a,b) (((a) < (b)) ? (a) : (b))
|
||
#endif
|
||
|
||
TFontCacheSizes* CCacheGlyphs::Get(const int& code)
|
||
{
|
||
std::map<int, TFontCacheSizes>* map = (m_pFile->m_bStringGID != 0) ? &m_mapGids : &m_mapUnicodes;
|
||
std::map<int, TFontCacheSizes>::iterator iter = map->find(code);
|
||
if (iter != map->end())
|
||
return &iter->second;
|
||
return NULL;
|
||
}
|
||
|
||
void CCacheGlyphs::Add(const TFontCacheSizes& item)
|
||
{
|
||
std::map<int, TFontCacheSizes>* map = (m_pFile->m_bStringGID != 0) ? &m_mapGids : &m_mapUnicodes;
|
||
std::map<int, TFontCacheSizes>::iterator iter = map->find(item.Unicode);
|
||
if (iter == map->end())
|
||
map->insert(std::pair<int, TFontCacheSizes>(item.Unicode, item));
|
||
else
|
||
iter->second = item;
|
||
}
|
||
|
||
void CCacheGlyphs::Clear(bool bIsFree)
|
||
{
|
||
if (bIsFree)
|
||
{
|
||
for (std::map<int, TFontCacheSizes>::iterator i = m_mapGids.begin(); i != m_mapGids.end(); i++)
|
||
i->second.oBitmap.bFreeData = TRUE;
|
||
for (std::map<int, TFontCacheSizes>::iterator i = m_mapUnicodes.begin(); i != m_mapUnicodes.end(); i++)
|
||
i->second.oBitmap.bFreeData = TRUE;
|
||
}
|
||
|
||
m_mapGids.clear();
|
||
m_mapUnicodes.clear();
|
||
}
|
||
|
||
FT_Error FT_Load_Glyph_Wrapper( FT_Face face,
|
||
FT_UInt glyph_index,
|
||
FT_Int32& load_flags, INT& bHintsSupport )
|
||
{
|
||
int nErr = FT_Load_Glyph(face, glyph_index, load_flags);
|
||
|
||
/*
|
||
FT_Err_Invalid_Glyph_Index = 0x10;
|
||
FT_Err_Invalid_Character_Code = 0x11;
|
||
FT_Err_Invalid_Glyph_Format = 0x12;
|
||
FT_Err_Cannot_Render_Glyph = 0x13;
|
||
FT_Err_Invalid_Outline = 0x14;
|
||
FT_Err_Invalid_Composite = 0x15;
|
||
FT_Err_Too_Many_Hints = 0x16;
|
||
FT_Err_Invalid_Pixel_Size = 0x17;
|
||
|
||
FT_Err_Invalid_Handle = 0x20;
|
||
FT_Err_Invalid_Library_Handle = 0x21;
|
||
FT_Err_Invalid_Driver_Handle = 0x22;
|
||
FT_Err_Invalid_Face_Handle = 0x23;
|
||
FT_Err_Invalid_Size_Handle = 0x24;
|
||
FT_Err_Invalid_Slot_Handle = 0x25;
|
||
FT_Err_Invalid_CharMap_Handle = 0x26;
|
||
FT_Err_Invalid_Cache_Handle = 0x27;
|
||
FT_Err_Invalid_Stream_Handle = 0x28;
|
||
|
||
FT_Err_Code_Overflow = 0x83;
|
||
FT_Err_Invalid_Reference = 0x86;
|
||
*/
|
||
|
||
if ((bHintsSupport == TRUE) && ((nErr > 0x10 && nErr < 0x28) || (nErr >= 0x83 && nErr <= 0x8D)))
|
||
{
|
||
int nErr2 = FT_Load_Glyph(face, glyph_index, 40970);
|
||
|
||
if (0 == nErr2)
|
||
{
|
||
bHintsSupport = FALSE;
|
||
load_flags = 40970;
|
||
nErr = 0;
|
||
}
|
||
}
|
||
return nErr;
|
||
}
|
||
|
||
CFontFile::CFontFile()
|
||
{
|
||
m_pStream = NULL;
|
||
m_pFontManager = NULL;
|
||
m_pDefaultFont = NULL;
|
||
ClearCache();
|
||
|
||
memset(m_arrdFontMatrix, 0, 6 * sizeof(double));
|
||
memset(m_arrdTextMatrix, 0, 6 * sizeof(double));
|
||
|
||
m_bAntiAliasing = TRUE;
|
||
m_bUseKerning = FALSE;
|
||
|
||
m_dSize = 0;
|
||
m_unHorDpi = 0;
|
||
m_unVerDpi = 0;
|
||
|
||
m_bNeedDoItalic = FALSE;
|
||
m_bNeedDoBold = FALSE;
|
||
|
||
m_dCharSpacing = 0;
|
||
|
||
m_nMinX = 0;
|
||
m_nMinY = 0;
|
||
m_nMaxX = 0;
|
||
m_nMaxY = 0;
|
||
|
||
m_lFaceIndex = 0;
|
||
|
||
m_pFace = NULL;
|
||
|
||
m_dUnitsKoef = 0;
|
||
m_nDefaultChar = -1;
|
||
m_nSymbolic = -1;
|
||
m_dTextScale = 1.0;
|
||
|
||
m_bStringGID = FALSE;
|
||
|
||
m_nNum_charmaps = 0;
|
||
|
||
m_lAscender = 0;
|
||
m_lDescender = 0;
|
||
m_lLineHeight = 0;
|
||
m_lUnits_Per_Em = 0;
|
||
|
||
m_bUseDefaultFont = FALSE;
|
||
m_bIsNeedUpdateMatrix12 = FALSE;
|
||
|
||
m_bHintsSupport = TRUE;
|
||
|
||
m_oCache.m_pFile = this;
|
||
}
|
||
|
||
CFontFile::~CFontFile()
|
||
{
|
||
RELEASEINTERFACE(m_pStream);
|
||
ClearCache();
|
||
|
||
if (m_pFace)
|
||
FT_Done_Face(m_pFace);
|
||
}
|
||
|
||
void CFontFile::SetDefaultFont(CFontFile* pDefFont)
|
||
{
|
||
m_pDefaultFont = pDefFont;
|
||
}
|
||
|
||
void CFontFile::LoadDefaultCharAndSymbolicCmapIndex()
|
||
{
|
||
m_nDefaultChar = -1;
|
||
m_nSymbolic = -1;
|
||
|
||
TT_OS2* pTable = (TT_OS2*)FT_Get_Sfnt_Table( m_pFace, ft_sfnt_os2 );
|
||
if (NULL == pTable)
|
||
return;
|
||
|
||
m_nDefaultChar = pTable->usDefaultChar;
|
||
|
||
// version
|
||
if (0xFFFF == pTable->version)
|
||
return;
|
||
|
||
FT_ULong ulCodePageRange1 = pTable->ulCodePageRange1;
|
||
FT_ULong ulCodePageRange2 = pTable->ulCodePageRange2;
|
||
if (!(ulCodePageRange1 & 0x80000000) && !(ulCodePageRange1 == 0 && ulCodePageRange2 == 0))
|
||
return;
|
||
|
||
for (int nIndex = 0; nIndex < m_pFace->num_charmaps; ++nIndex)
|
||
{
|
||
// Symbol
|
||
if ( 0 == m_pFace->charmaps[nIndex]->encoding_id && 3 == m_pFace->charmaps[nIndex]->platform_id )
|
||
{
|
||
m_nSymbolic = nIndex;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
void CFontFile::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();
|
||
}
|
||
|
||
void CFontFile::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;
|
||
|
||
CheckTextMatrix();
|
||
}
|
||
|
||
void CFontFile::CheckTextMatrix()
|
||
{
|
||
m_bIsNeedUpdateMatrix12 = true;
|
||
if ((m_arrdTextMatrix[0] == 1) && (m_arrdTextMatrix[1] == 0) && (m_arrdTextMatrix[2] == 0) && (m_arrdTextMatrix[3] == 1))
|
||
{
|
||
m_bIsNeedUpdateMatrix12 = false;
|
||
|
||
if (m_pDefaultFont)
|
||
m_pDefaultFont->UpdateMatrix1();
|
||
|
||
UpdateMatrix1();
|
||
}
|
||
}
|
||
|
||
void CFontFile::UpdateMatrix0()
|
||
{
|
||
double m1 = m_arrdTextMatrix[2];
|
||
double m2 = m_arrdTextMatrix[3];
|
||
m_dTextScale = sqrt(m_arrdTextMatrix[2]*m_arrdTextMatrix[2] + m_arrdTextMatrix[3]*m_arrdTextMatrix[3]);
|
||
|
||
FT_BBox* bbox = &m_pFace->bbox;
|
||
FT_Pos xMin = bbox->xMin;
|
||
FT_Pos yMin = bbox->yMin;
|
||
FT_Pos xMax = bbox->xMax;
|
||
FT_Pos yMax = bbox->yMax;
|
||
|
||
if (m_lUnits_Per_Em == 0)
|
||
m_lUnits_Per_Em = m_pFace->units_per_EM = 2048;
|
||
|
||
int units_per_EM = m_lUnits_Per_Em;
|
||
double dDiv = xMax > 20000 ? 65536 : 1;
|
||
double del = dDiv * units_per_EM;
|
||
|
||
int nX = (int)((m_arrdFontMatrix[0] * xMin + m_arrdFontMatrix[2] * yMin) * m_dSize / del);
|
||
m_nMinX = m_nMaxX = nX;
|
||
|
||
int nY = (int)((m_arrdFontMatrix[1] * xMin + m_arrdFontMatrix[3] * yMin) * m_dSize / del);
|
||
m_nMinY = m_nMaxY = nY;
|
||
|
||
nX = (int)((m_arrdFontMatrix[0] * xMin + m_arrdFontMatrix[2] * yMax) * m_dSize / del);
|
||
|
||
if (nX < m_nMinX)
|
||
m_nMinX = nX;
|
||
else if (nX > m_nMaxX)
|
||
m_nMaxX = nX;
|
||
|
||
nY = (int)((m_arrdFontMatrix[1] * xMin + m_arrdFontMatrix[3] * yMax) * m_dSize / del);
|
||
|
||
if (nY < m_nMinY)
|
||
m_nMinY = nY;
|
||
else if (nY > m_nMaxY)
|
||
m_nMaxY = nY;
|
||
|
||
nX = (int)((m_arrdFontMatrix[0] * xMax + m_arrdFontMatrix[2] * yMin) * m_dSize / del);
|
||
if (nX < m_nMinX)
|
||
m_nMinX = nX;
|
||
else if (nX > m_nMaxX)
|
||
m_nMaxX = nX;
|
||
|
||
nY = (int)((m_arrdFontMatrix[1] * xMax + m_arrdFontMatrix[3] * yMin) * m_dSize / del);
|
||
if (nY < m_nMinY)
|
||
m_nMinY = nY;
|
||
else if (nY > m_nMaxY)
|
||
m_nMaxY = nY;
|
||
|
||
nX = (int)((m_arrdFontMatrix[0] * xMax + m_arrdFontMatrix[2] * yMax) * m_dSize / del);
|
||
if (nX < m_nMinX)
|
||
m_nMinX = nX;
|
||
else if (nX > m_nMaxX)
|
||
m_nMaxX = nX;
|
||
|
||
nY = (int)((m_arrdFontMatrix[1] * xMax + m_arrdFontMatrix[3] * yMax) * m_dSize / del);
|
||
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)(m_dSize);
|
||
}
|
||
|
||
if (m_nMaxY == m_nMinY)
|
||
{
|
||
m_nMinY = 0;
|
||
m_nMaxY = (int)((1.2 * m_dSize));
|
||
}
|
||
|
||
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( m_pFace, &m_oFontMatrix, NULL );
|
||
}
|
||
|
||
void CFontFile::UpdateMatrix1()
|
||
{
|
||
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( m_pFace, &m_oFontMatrix, NULL );
|
||
}
|
||
|
||
void CFontFile::UpdateMatrix2()
|
||
{
|
||
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( m_pFace, &m_oFontMatrix, NULL );
|
||
}
|
||
|
||
void CFontFile::SetSizeAndDpi(double dSize, double unHorDpi, double unVerDpi)
|
||
{
|
||
if (m_pDefaultFont)
|
||
m_pDefaultFont->SetSizeAndDpi(dSize, unHorDpi, unVerDpi);
|
||
|
||
double dOldSize = m_dSize;
|
||
double dNewSize = dSize;
|
||
double fKoef = dNewSize / dOldSize;
|
||
|
||
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_dSize = dNewSize;
|
||
UpdateMatrix0();
|
||
}
|
||
|
||
m_dUnitsKoef = m_unHorDpi / 72.0 * m_dSize;
|
||
|
||
FT_Set_Char_Size(m_pFace, 0, (int)(dNewSize * 64), unHorDpi, unVerDpi);
|
||
ClearCache();
|
||
}
|
||
}
|
||
|
||
void CFontFile::ClearCache()
|
||
{
|
||
// TODO: total memory
|
||
ClearCacheNoAttack(true);
|
||
}
|
||
void CFontFile::ClearCacheNoAttack(bool bIsFree)
|
||
{
|
||
m_oCache.Clear(bIsFree);
|
||
}
|
||
|
||
void CFontFile::Destroy()
|
||
{
|
||
// TODO: total memory
|
||
}
|
||
|
||
bool CFontFile::SetTextMatrix(const double& fA, const double& fB, const double& fC, const double fD, double fE, double fF)
|
||
{
|
||
bool b1 = (m_arrdTextMatrix[0] == fA && m_arrdTextMatrix[1] == -fB && m_arrdTextMatrix[2] == -fC && m_arrdTextMatrix[3] == fD);
|
||
if (b1 && m_arrdTextMatrix[4] == fE && m_arrdTextMatrix[5] == fF)
|
||
return false;
|
||
|
||
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;
|
||
|
||
if (!b1)
|
||
{
|
||
ClearCache();
|
||
}
|
||
CheckTextMatrix();
|
||
return true;
|
||
}
|
||
|
||
void CFontFile::SetFontMatrix(const double& fA, const double& fB, const double& fC, const double fD, double fE, double 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;
|
||
}
|
||
|
||
ClearCache();
|
||
}
|
||
|
||
int CFontFile::GetKerning(UINT unPrevGID, UINT unGID)
|
||
{
|
||
FT_Vector delta;
|
||
FT_Get_Kerning(m_pFace, unPrevGID, unGID, 0, &delta);
|
||
return (delta.x >> 6);
|
||
}
|
||
|
||
void CFontFile::SetStringGID(const INT& bGID)
|
||
{
|
||
if (m_bStringGID == bGID)
|
||
return;
|
||
|
||
//ClearCache();
|
||
m_bStringGID = bGID;
|
||
}
|
||
INT CFontFile::GetStringGID()
|
||
{
|
||
return m_bStringGID;
|
||
}
|
||
void CFontFile::SetUseDefaultFont(const INT& bUse)
|
||
{
|
||
m_bUseDefaultFont = bUse;
|
||
}
|
||
INT CFontFile::GetUseDefaultFont()
|
||
{
|
||
return m_bUseDefaultFont;
|
||
}
|
||
void CFontFile::SetCharSpacing(const double& dCharSpacing)
|
||
{
|
||
m_dCharSpacing = dCharSpacing;
|
||
}
|
||
double CFontFile::GetCharSpacing()
|
||
{
|
||
return m_dCharSpacing;
|
||
}
|
||
|
||
std::string CFontFile::GetStyleName()
|
||
{
|
||
std::string s(m_pFace->style_name);
|
||
return s;
|
||
}
|
||
|
||
void CFontFile::GetPanose(BYTE* pData)
|
||
{
|
||
memset(pData, 0, 10);
|
||
if (!m_pFace)
|
||
return;
|
||
|
||
TT_OS2 *pTable = (TT_OS2 *)FT_Get_Sfnt_Table( m_pFace, ft_sfnt_os2 );
|
||
if ( NULL == pTable )
|
||
return;
|
||
|
||
memcpy( pData, pTable->panose, 10 );
|
||
}
|
||
|
||
bool CFontFile::IsFixedWidth()
|
||
{
|
||
if (!m_pFace)
|
||
return false;
|
||
return FT_IS_FIXED_WIDTH( m_pFace ) != 0;
|
||
}
|
||
|
||
int CFontFile::IsUnicodeRangeAvailable(unsigned long ulBit, unsigned int un4ByteIndex)
|
||
{
|
||
if (!m_pFace)
|
||
return -1;
|
||
|
||
TT_OS2 *pOs2 = (TT_OS2 *)FT_Get_Sfnt_Table( m_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 < m_pFace->num_charmaps; nIndex++ )
|
||
{
|
||
// Symbol
|
||
if ( 31 == ulBit && 0 == m_pFace->charmaps[nIndex]->encoding_id && 3 == m_pFace->charmaps[nIndex]->platform_id )
|
||
{
|
||
nResult = 1;
|
||
break;
|
||
}
|
||
|
||
// ShiftJIS
|
||
if ( 17 == ulBit && 2 == m_pFace->charmaps[nIndex]->encoding_id && 3 == m_pFace->charmaps[nIndex]->platform_id )
|
||
{
|
||
nResult = 1;
|
||
break;
|
||
}
|
||
|
||
// PRC
|
||
if ( 18 == ulBit && 3 == m_pFace->charmaps[nIndex]->encoding_id && 3 == m_pFace->charmaps[nIndex]->platform_id )
|
||
{
|
||
nResult = 1;
|
||
break;
|
||
}
|
||
|
||
// Big5
|
||
if ( 20 == ulBit && 4 == m_pFace->charmaps[nIndex]->encoding_id && 3 == m_pFace->charmaps[nIndex]->platform_id )
|
||
{
|
||
nResult = 1;
|
||
break;
|
||
}
|
||
|
||
// Wansung
|
||
if ( 19 == ulBit && 5 == m_pFace->charmaps[nIndex]->encoding_id && 3 == m_pFace->charmaps[nIndex]->platform_id )
|
||
{
|
||
nResult = 1;
|
||
break;
|
||
}
|
||
|
||
// Johab
|
||
if ( 21 == ulBit && 6 == m_pFace->charmaps[nIndex]->encoding_id && 3 == m_pFace->charmaps[nIndex]->platform_id )
|
||
{
|
||
nResult = 1;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return nResult;
|
||
}
|
||
|
||
void CFontFile::UpdateStyles(const INT& bBold, const INT& bItalic)
|
||
{
|
||
std::string sStyle = GetStyleName();
|
||
|
||
// Смотрим какой стиль у исходного шрифта
|
||
INT bSrcBold = (-1 != sStyle.find("Bold"));
|
||
INT bSrcItalic = (-1 != sStyle.find("Italic"));
|
||
|
||
if (!bBold) // Нам нужен не жирный шрифт
|
||
{
|
||
m_bNeedDoBold = false;
|
||
}
|
||
else if (bBold) // Нам нужно сделать шрифт жирным
|
||
{
|
||
if (bSrcBold)
|
||
{
|
||
// Исходный шрифт уже жирный, поэтому ничего дополнительного делать не надо
|
||
m_bNeedDoBold = false;
|
||
}
|
||
else
|
||
{
|
||
// Иходный шрифт не жирный, поэтому жирность делаем сами
|
||
m_bNeedDoBold = true;
|
||
}
|
||
}
|
||
|
||
if (!bItalic) // Нам нужен не наклонный шрифт
|
||
{
|
||
SetItalic(false);
|
||
}
|
||
else if (bItalic) // Нам нужно сделать наклонный шрифт
|
||
{
|
||
if (bSrcItalic)
|
||
{
|
||
// Исходный шрифт уже наклонный, поэтому ничего дополнительного делать не надо
|
||
SetItalic(false);
|
||
}
|
||
else
|
||
{
|
||
// Иходный шрифт не наклонный, поэтому делаем его наклонным сами
|
||
SetItalic(true);
|
||
}
|
||
}
|
||
}
|
||
|
||
void CFontFile::SetItalic(const INT& value)
|
||
{
|
||
if (m_bNeedDoItalic != value)
|
||
{
|
||
ClearCache();
|
||
m_bNeedDoItalic = value;
|
||
ResetFontMatrix();
|
||
}
|
||
}
|
||
void CFontFile::SetNeedBold(const INT& value)
|
||
{
|
||
if (m_bNeedDoBold != value)
|
||
ClearCache();
|
||
|
||
m_bNeedDoBold = value;
|
||
}
|
||
|
||
int CFontFile::GetAscender()
|
||
{
|
||
return m_lAscender;
|
||
}
|
||
int CFontFile::GetDescender()
|
||
{
|
||
return m_lDescender;
|
||
}
|
||
int CFontFile::GetHeight()
|
||
{
|
||
return m_lLineHeight;
|
||
}
|
||
int CFontFile::Units_Per_Em()
|
||
{
|
||
return m_lUnits_Per_Em;
|
||
}
|
||
|
||
void CFontFile::CheckHintsSupport()
|
||
{
|
||
m_bHintsSupport = TRUE;
|
||
|
||
if (!m_pFace || !m_pFace->driver || !m_pFace->driver->clazz)
|
||
return;
|
||
|
||
std::string sName(m_pFace->driver->clazz->root.module_name);
|
||
|
||
if (sName != "truetype")
|
||
{
|
||
m_bHintsSupport = FALSE;
|
||
return;
|
||
}
|
||
|
||
std::wstring sFamilyName = GetCorrectSfntName(m_pFace->family_name);
|
||
|
||
if (m_sName == L"MS Mincho" || m_sName == L"Castellar")
|
||
m_bHintsSupport = FALSE;
|
||
}
|
||
|
||
// glyph methods
|
||
int CFontFile::SetCMapForCharCode(long lUnicode, int *pnCMapIndex)
|
||
{
|
||
*pnCMapIndex = -1;
|
||
if (!m_pFace)
|
||
return 0;
|
||
|
||
if ( m_bStringGID || 0 == m_pFace->num_charmaps )
|
||
return lUnicode;
|
||
|
||
int nCharIndex = 0;
|
||
|
||
for ( int nIndex = 0; nIndex < m_pFace->num_charmaps; ++nIndex )
|
||
{
|
||
FT_CharMap pCharMap = m_pFace->charmaps[nIndex];
|
||
|
||
if ( FT_Set_Charmap( m_pFace, pCharMap ) )
|
||
continue;
|
||
|
||
FT_Encoding pEncoding = pCharMap->encoding;
|
||
|
||
if ( FT_ENCODING_UNICODE == pEncoding )
|
||
{
|
||
if ( nCharIndex = FT_Get_Char_Index( m_pFace, lUnicode ) )
|
||
{
|
||
*pnCMapIndex = nIndex;
|
||
return nCharIndex;
|
||
}
|
||
}
|
||
else if ( FT_ENCODING_NONE == pEncoding || FT_ENCODING_MS_SYMBOL == pEncoding || FT_ENCODING_APPLE_ROMAN == pEncoding )
|
||
{
|
||
#if 0
|
||
FT_ULong charcode;
|
||
FT_UInt gindex;
|
||
|
||
charcode = FT_Get_First_Char( m_pFace, &gindex );
|
||
while ( gindex != 0 )
|
||
{
|
||
charcode = FT_Get_Next_Char( m_pFace, charcode, &gindex );
|
||
if ( charcode == lUnicode )
|
||
{
|
||
nCharIndex = gindex;
|
||
*pnCMapIndex = nIndex;
|
||
break;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if ( nCharIndex = FT_Get_Char_Index( m_pFace, lUnicode ) )
|
||
{
|
||
*pnCMapIndex = nIndex;
|
||
}
|
||
}
|
||
}
|
||
|
||
return nCharIndex;
|
||
}
|
||
|
||
int CFontFile::SetCMapForCharCode2(long lUnicode)
|
||
{
|
||
if (m_bStringGID)
|
||
return lUnicode;
|
||
|
||
int nCMapIndex = 0;
|
||
TFontCacheSizes* pCachedGlyph = m_oCache.Get(lUnicode);
|
||
if (NULL == pCachedGlyph)
|
||
return SetCMapForCharCode( lUnicode, &nCMapIndex );
|
||
|
||
FT_Int unGID = pCachedGlyph->GID;
|
||
nCMapIndex = pCachedGlyph->nCMapIndex;
|
||
if ( 0 != m_pFace->num_charmaps )
|
||
{
|
||
int nCurCMapIndex = FT_Get_Charmap_Index( m_pFace->charmap );
|
||
if ( nCurCMapIndex != nCMapIndex )
|
||
{
|
||
nCMapIndex = max( 0, nCMapIndex );
|
||
FT_Set_Charmap( m_pFace, m_pFace->charmaps[nCMapIndex] );
|
||
}
|
||
}
|
||
return unGID;
|
||
}
|
||
|
||
TFontCacheSizes CFontFile::CacheGlyph(const int& code, const bool& isRaster, CVectorWorker* pWorker, const bool& isFromPicker)
|
||
{
|
||
TFontCacheSizes oSizes;
|
||
oSizes.Unicode = code;
|
||
oSizes.eState = glyphstateMiss;
|
||
|
||
int nCMapIndex = 0;
|
||
int unGID = m_bStringGID ? code : SetCMapForCharCode(code, &nCMapIndex);
|
||
|
||
if (unGID <= 0 && !m_bStringGID)
|
||
{
|
||
if (-1 != m_nSymbolic && code < 0xF000)
|
||
unGID = SetCMapForCharCode(code + 0xF000, &nCMapIndex);
|
||
}
|
||
oSizes.GID = unGID;
|
||
oSizes.nCMapIndex = nCMapIndex;
|
||
|
||
if (unGID <= 0)
|
||
{
|
||
if (isFromPicker)
|
||
return oSizes;
|
||
|
||
if (!m_bStringGID)
|
||
{
|
||
// пробуем подобрать нужный шрифт
|
||
CFontFile* pPickFile = m_pFontManager->GetFontFileBySymbol(this, code);
|
||
if (!pPickFile)
|
||
return oSizes;
|
||
|
||
TFontCacheSizes oSizesCheck = pPickFile->CacheGlyph(code, isRaster, pWorker, true);
|
||
|
||
// файл - в кэше. а тут нужно удалить
|
||
RELEASEINTERFACE(pPickFile);
|
||
|
||
if (oSizesCheck.eState == glyphstateNormal)
|
||
return oSizesCheck;
|
||
}
|
||
|
||
if (m_nDefaultChar >= 0)
|
||
{
|
||
unGID = m_nDefaultChar;
|
||
oSizes.eState = glyphstateDefault;
|
||
}
|
||
else
|
||
{
|
||
oSizes.fAdvanceX = (m_pFace->size->metrics.max_advance >> 6) / 2.0f;
|
||
oSizes.fAdvanceY = oSizes.fAdvanceX;
|
||
return oSizes;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
oSizes.eState = glyphstateNormal;
|
||
}
|
||
|
||
if (m_bIsNeedUpdateMatrix12)
|
||
UpdateMatrix2();
|
||
|
||
FT_Int32 _LOAD_MODE = m_bHintsSupport ? m_pFontManager->m_nLOAD_MODE : 40970;
|
||
if (0 != FT_Load_Glyph_Wrapper(m_pFace, unGID, _LOAD_MODE, m_bHintsSupport))
|
||
return oSizes;
|
||
|
||
FT_Glyph pGlyph = NULL;
|
||
if ( FT_Get_Glyph( m_pFace->glyph, &pGlyph ) )
|
||
return oSizes;
|
||
|
||
TT_OS2* pOS2 = (TT_OS2*)FT_Get_Sfnt_Table(m_pFace, ft_sfnt_os2);
|
||
|
||
bool bIsNeedBold = (m_bNeedDoBold == TRUE) ? true : false;
|
||
if (bIsNeedBold && pOS2 && pOS2->version != 0xFFFF && pOS2->usWeightClass >= 800)
|
||
bIsNeedBold = false;
|
||
|
||
if (pWorker)
|
||
{
|
||
if (bIsNeedBold)
|
||
FT_Outline_EmboldenXY(&((FT_OutlineGlyph)pGlyph)->outline, (m_unHorDpi * 64 / 25.4) / 3, 0);
|
||
FT_Outline_Decompose( &((FT_OutlineGlyph)pGlyph)->outline, pWorker->func_interface, pWorker->user );
|
||
return oSizes;
|
||
}
|
||
|
||
FT_BBox oBBox;
|
||
|
||
FT_Glyph_Get_CBox(pGlyph, 1, &oBBox);
|
||
FT_Done_Glyph(pGlyph);
|
||
|
||
FT_GlyphSlot pGlyphSlot = m_pFace->glyph;
|
||
|
||
oSizes.fAdvanceX = (float)(pGlyphSlot->linearHoriAdvance * m_dUnitsKoef / m_pFace->units_per_EM);
|
||
if (bIsNeedBold)
|
||
oSizes.fAdvanceX += 1;
|
||
|
||
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.fWidth = (float)(pGlyphSlot->metrics.width >> 6);
|
||
oSizes.oMetrics.fHeight = (float)(pGlyphSlot->metrics.height >> 6);
|
||
oSizes.oMetrics.fHoriAdvance = (float)(pGlyphSlot->metrics.horiAdvance >> 6);
|
||
oSizes.oMetrics.fHoriBearingX = (float)(pGlyphSlot->metrics.horiBearingX >> 6);
|
||
oSizes.oMetrics.fHoriBearingY = (float)(pGlyphSlot->metrics.horiBearingY >> 6);
|
||
oSizes.oMetrics.fVertAdvance = (float)(pGlyphSlot->metrics.vertAdvance >> 6);
|
||
oSizes.oMetrics.fVertBearingX = (float)(pGlyphSlot->metrics.vertBearingX >> 6);
|
||
oSizes.oMetrics.fVertBearingY = (float)(pGlyphSlot->metrics.vertBearingY >> 6);
|
||
|
||
if (isFromPicker && (0 == oSizes.oMetrics.fHoriAdvance && 0 == oSizes.oMetrics.fWidth))
|
||
{
|
||
oSizes.eState = glyphstateMiss;
|
||
return oSizes;
|
||
}
|
||
|
||
if (!isRaster)
|
||
return oSizes;
|
||
|
||
oSizes.bBitmap = true;
|
||
if (FT_Render_Glyph(pGlyphSlot, (FT_Render_Mode)m_pFontManager->m_nRENDER_MODE))
|
||
return oSizes;
|
||
|
||
TGlyphBitmap* pBitmap = &(oSizes.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 (bIsNeedBold)
|
||
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)
|
||
{
|
||
if ( !bIsNeedBold || !m_bAntiAliasing )
|
||
{
|
||
for ( nIndex2 = 0, pDstBuffer = pBitmap->pData, pSrcBuffer = pGlyphSlot->bitmap.buffer; nIndex2 < pBitmap->nHeight; ++nIndex2, 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 oSizes;
|
||
}
|
||
|
||
TFontCacheSizes CFontFile::GetChar(LONG lUnicode)
|
||
{
|
||
TFontCacheSizes* pCachedGlyph = m_oCache.Get(lUnicode);
|
||
if (NULL != pCachedGlyph)
|
||
return *pCachedGlyph;
|
||
|
||
TFontCacheSizes oSizes = CacheGlyph(lUnicode, false);
|
||
m_oCache.Add(oSizes);
|
||
return oSizes;
|
||
}
|
||
|
||
double CFontFile::GetCharWidth(int gid)
|
||
{
|
||
if (!m_pFace)
|
||
return 0;
|
||
|
||
if (0 != FT_Load_Glyph(m_pFace, gid, 40970))
|
||
return 0;
|
||
|
||
FT_Glyph pGlyph = NULL;
|
||
if (0 != FT_Get_Glyph(m_pFace->glyph, &pGlyph))
|
||
return 0;
|
||
|
||
double dRet = (double)(m_pFace->glyph->linearHoriAdvance * m_dUnitsKoef / m_pFace->units_per_EM);
|
||
|
||
FT_Done_Glyph(pGlyph);
|
||
|
||
return dRet;
|
||
}
|
||
|
||
int CFontFile::GetGIDByUnicode(int code)
|
||
{
|
||
int nCMapIndex = 0;
|
||
int unGID = m_bStringGID ? code : SetCMapForCharCode(code, &nCMapIndex);
|
||
|
||
if (unGID <= 0 && !m_bStringGID)
|
||
{
|
||
if (-1 != m_nSymbolic && code < 0xF000)
|
||
unGID = SetCMapForCharCode(code, &nCMapIndex);
|
||
}
|
||
|
||
return unGID;
|
||
}
|
||
|
||
int CFontFile::GetUnicodeByGID(int gid)
|
||
{
|
||
if (!m_pFace)
|
||
return 0;
|
||
|
||
FT_ULong charcode;
|
||
FT_UInt gindex;
|
||
|
||
charcode = FT_Get_First_Char(m_pFace, &gindex);
|
||
|
||
while (gindex != 0)
|
||
{
|
||
if (gindex == gid)
|
||
{
|
||
return charcode;
|
||
}
|
||
charcode = FT_Get_Next_Char(m_pFace, charcode, &gindex);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
INT CFontFile::GetString(CGlyphString& oString)
|
||
{
|
||
int nCountGlyph = oString.GetLength();
|
||
if (nCountGlyph <= 0)
|
||
return TRUE;
|
||
|
||
unsigned int unPrevGID = 0;
|
||
float fPenX = 0, fPenY = 0;
|
||
|
||
for (int nIndex = 0; nIndex < nCountGlyph; ++nIndex)
|
||
{
|
||
TGlyph* pCurGlyph = oString.GetAt(nIndex);
|
||
|
||
int unGID = 0;
|
||
int ushUnicode = pCurGlyph->lUnicode;
|
||
if (ushUnicode < 0) ushUnicode = 0;
|
||
|
||
TFontCacheSizes* pCacheGlyph = m_oCache.Get(ushUnicode);
|
||
if (!pCacheGlyph)
|
||
{
|
||
m_oCache.Add(CacheGlyph(ushUnicode, false));
|
||
pCacheGlyph = m_oCache.Get(ushUnicode);
|
||
}
|
||
|
||
if (true)
|
||
{
|
||
unGID = pCacheGlyph->GID;
|
||
|
||
if (glyphstateMiss == pCacheGlyph->eState)
|
||
{
|
||
oString.SetStartPoint (nIndex, fPenX, fPenY);
|
||
oString.SetBBox(nIndex, 0, 0, 0, 0);
|
||
oString.SetState (nIndex, glyphstateMiss);
|
||
|
||
double dAdvanceW = m_arrdFontMatrix[0] * pCacheGlyph->fAdvanceX;
|
||
fPenX += (float)(dAdvanceW + m_dCharSpacing);
|
||
|
||
unPrevGID = 0;
|
||
|
||
continue;
|
||
}
|
||
else if (glyphstateDefault == pCacheGlyph->eState)
|
||
{
|
||
oString.SetState(nIndex, glyphstateDefault);
|
||
}
|
||
else
|
||
{
|
||
oString.SetState(nIndex, glyphstateNormal);
|
||
}
|
||
|
||
if (m_bUseKerning && unPrevGID && (nIndex >= 0 && oString.GetAt(nIndex)->eState == oString.GetAt(nIndex - 1)->eState))
|
||
{
|
||
fPenX += GetKerning(unPrevGID, unGID);
|
||
}
|
||
|
||
float fX = oString.m_fX + fPenX;
|
||
float fY = oString.m_fY + fPenY;
|
||
|
||
float fXX = (float)(oString.m_arrCTM[4] + fX * oString.m_arrCTM[0] + fY * oString.m_arrCTM[2] - oString.m_fX);
|
||
float fYY = (float)(oString.m_arrCTM[5] + fX * oString.m_arrCTM[1] + fY * oString.m_arrCTM[3] - oString.m_fY);
|
||
|
||
oString.SetStartPoint(nIndex, fXX, fYY);
|
||
|
||
oString.GetAt(nIndex)->oMetrics = pCacheGlyph->oMetrics;
|
||
oString.SetBBox(nIndex, pCacheGlyph->oBBox.fMinX, pCacheGlyph->oBBox.fMaxY, pCacheGlyph->oBBox.fMaxX, pCacheGlyph->oBBox.fMinY);
|
||
|
||
double dAdvanceW = m_arrdFontMatrix[0] * pCacheGlyph->fAdvanceX;
|
||
fPenX += (float)(dAdvanceW + m_dCharSpacing);
|
||
|
||
pCurGlyph->bBitmap = pCacheGlyph->bBitmap;
|
||
pCurGlyph->oBitmap = pCacheGlyph->oBitmap;
|
||
}
|
||
unPrevGID = unGID;
|
||
}
|
||
|
||
oString.m_fEndX = fPenX + oString.m_fX;
|
||
oString.m_fEndY = fPenY + oString.m_fY;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
INT CFontFile::GetString2(CGlyphString& oString)
|
||
{
|
||
int nCountGlyph = oString.GetLength();
|
||
if (nCountGlyph <= 0)
|
||
return TRUE;
|
||
|
||
//if (1 == nCountGlyph)
|
||
// return GetString2C(oString);
|
||
|
||
unsigned int unPrevGID = 0;
|
||
float fPenX = 0, fPenY = 0;
|
||
|
||
for (int nIndex = 0; nIndex < nCountGlyph; ++nIndex)
|
||
{
|
||
TGlyph* pCurGlyph = oString.GetAt(nIndex);
|
||
|
||
int nUnicode = pCurGlyph->lUnicode;
|
||
|
||
TFontCacheSizes* pCacheGlyph = m_oCache.Get(nUnicode);
|
||
if (!pCacheGlyph || !pCacheGlyph->bBitmap)
|
||
{
|
||
m_oCache.Add(CacheGlyph(nUnicode, true));
|
||
pCacheGlyph = m_oCache.Get(nUnicode);
|
||
}
|
||
|
||
if (nUnicode < 0) nUnicode = 0;
|
||
int unGID = 0;
|
||
|
||
if (true)
|
||
{
|
||
unGID = pCacheGlyph->GID;
|
||
|
||
if (glyphstateMiss == pCacheGlyph->eState)
|
||
{
|
||
oString.SetStartPoint (nIndex, fPenX, fPenY);
|
||
oString.SetBBox(nIndex, 0, 0, 0, 0);
|
||
oString.SetState (nIndex, glyphstateMiss);
|
||
|
||
double dAdvanceW = m_arrdFontMatrix[0] * pCacheGlyph->fAdvanceX;
|
||
fPenX += (float)(dAdvanceW + m_dCharSpacing);
|
||
|
||
unPrevGID = 0;
|
||
|
||
continue;
|
||
}
|
||
else if (glyphstateDefault == pCacheGlyph->eState)
|
||
{
|
||
oString.SetState(nIndex, glyphstateDefault);
|
||
}
|
||
else
|
||
{
|
||
oString.SetState(nIndex, glyphstateNormal);
|
||
}
|
||
|
||
if (m_bUseKerning && unPrevGID && (nIndex >= 0 && oString.GetAt(nIndex)->eState == oString.GetAt(nIndex - 1)->eState))
|
||
{
|
||
fPenX += GetKerning(unPrevGID, unGID);
|
||
}
|
||
|
||
float fX = oString.m_fX + fPenX;
|
||
float fY = oString.m_fY + fPenY;
|
||
|
||
float fXX = (float)(oString.m_arrCTM[4] + fX * oString.m_arrCTM[0] + fY * oString.m_arrCTM[2] - oString.m_fX);
|
||
float fYY = (float)(oString.m_arrCTM[5] + fX * oString.m_arrCTM[1] + fY * oString.m_arrCTM[3] - oString.m_fY);
|
||
|
||
oString.SetStartPoint(nIndex, fXX, fYY);
|
||
|
||
oString.GetAt(nIndex)->oMetrics = pCacheGlyph->oMetrics;
|
||
oString.SetBBox(nIndex, pCacheGlyph->oBBox.fMinX, pCacheGlyph->oBBox.fMaxY, pCacheGlyph->oBBox.fMaxX, pCacheGlyph->oBBox.fMinY);
|
||
|
||
double dAdvanceW = m_arrdFontMatrix[0] * pCacheGlyph->fAdvanceX;
|
||
fPenX += (float)(dAdvanceW + m_dCharSpacing);
|
||
|
||
pCurGlyph->bBitmap = pCacheGlyph->bBitmap;
|
||
pCurGlyph->oBitmap = pCacheGlyph->oBitmap;
|
||
}
|
||
unPrevGID = unGID;
|
||
}
|
||
|
||
oString.m_fEndX = fPenX + oString.m_fX;
|
||
oString.m_fEndY = fPenY + oString.m_fY;
|
||
return TRUE;
|
||
}
|
||
|
||
INT CFontFile::GetString2C(CGlyphString& oString)
|
||
{
|
||
unsigned int unPrevGID = 0;
|
||
float fPenX = 0, fPenY = 0;
|
||
|
||
TGlyph* pCurGlyph = oString.GetAt(0);
|
||
int nUnicode = pCurGlyph->lUnicode;
|
||
if (nUnicode < 0) nUnicode = 0;
|
||
|
||
TFontCacheSizes* pCacheGlyph = m_oCache.Get(nUnicode);
|
||
if (!pCacheGlyph || !pCacheGlyph->bBitmap)
|
||
{
|
||
m_oCache.Add(CacheGlyph(nUnicode, true));
|
||
pCacheGlyph = m_oCache.Get(nUnicode);
|
||
}
|
||
|
||
if (true)
|
||
{
|
||
if (glyphstateMiss == pCacheGlyph->eState)
|
||
{
|
||
return TRUE;
|
||
}
|
||
else if (glyphstateDefault == pCacheGlyph->eState)
|
||
{
|
||
pCurGlyph->eState = glyphstateDefault;
|
||
}
|
||
else
|
||
{
|
||
pCurGlyph->eState = glyphstateNormal;
|
||
}
|
||
|
||
pCurGlyph->oMetrics = pCacheGlyph->oMetrics;
|
||
|
||
pCurGlyph->bBitmap = pCacheGlyph->bBitmap;
|
||
pCurGlyph->oBitmap = pCacheGlyph->oBitmap;
|
||
}
|
||
|
||
if (true)
|
||
{
|
||
float fX = oString.m_fX + fPenX;
|
||
float fY = oString.m_fY + fPenY;
|
||
|
||
pCurGlyph->fX = (float)(oString.m_arrCTM[4] + fX * oString.m_arrCTM[0] + fY * oString.m_arrCTM[2] - oString.m_fX);
|
||
pCurGlyph->fY = (float)(oString.m_arrCTM[5] + fX * oString.m_arrCTM[1] + fY * oString.m_arrCTM[3] - oString.m_fY);
|
||
}
|
||
|
||
double dAdvanceW = m_arrdFontMatrix[0] * pCacheGlyph->fAdvanceX;
|
||
fPenX += (float)(dAdvanceW + m_dCharSpacing);
|
||
|
||
oString.m_fEndX = fPenX + oString.m_fX;
|
||
oString.m_fEndY = fPenY + oString.m_fY;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
std::wstring CFontFile::GetFontFormat()
|
||
{
|
||
if (!m_pFace)
|
||
return L"";
|
||
|
||
const char* sFormat = FT_Get_X11_Font_Format(m_pFace);
|
||
return NSFile::CUtf8Converter::GetUnicodeFromCharPtr(sFormat, strlen(sFormat));
|
||
}
|
||
NSFonts::EFontFormat CFontFile::GetFontFormatType(FT_Face pFace)
|
||
{
|
||
if (!pFace)
|
||
return NSFonts::fontUnknown;
|
||
|
||
std::string wsFormat( FT_Get_X11_Font_Format( pFace ) );
|
||
|
||
if ( "Windows FNT" == wsFormat )
|
||
return NSFonts::fontWindowsFNT;
|
||
else if ( "TrueType" == wsFormat )
|
||
return NSFonts::fontTrueType;
|
||
else if ( "CFF" == wsFormat )
|
||
return NSFonts::fontOpenType;
|
||
|
||
return NSFonts::fontUnknown;
|
||
}
|
||
|
||
unsigned int CFontFile::GetNameIndex(const std::wstring& wsName) const
|
||
{
|
||
if (!m_pFace)
|
||
return 0;
|
||
|
||
int nLen = wsName.length();
|
||
char* sName = new char[nLen + 1];
|
||
if (!sName)
|
||
return 0;
|
||
|
||
sName[nLen] = 0x00;
|
||
for (int nIndex = 0; nIndex < nLen; nIndex++)
|
||
{
|
||
sName[nIndex] = (char)(wsName[nIndex]);
|
||
}
|
||
unsigned int unGID = FT_Get_Name_Index(m_pFace, sName);
|
||
delete[] sName;
|
||
|
||
return unGID;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////////////
|
||
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 / 64.0, (double)pPoint->y / 64.0 );
|
||
return 0;
|
||
}
|
||
|
||
static int GlyphPathLineTo(const FT_Vector *pPoint, void *pPath)
|
||
{
|
||
TFreeTypeFontPath *pGlyphPath = (TFreeTypeFontPath *)pPath;
|
||
|
||
pGlyphPath->pPath->LineTo( (double)pPoint->x / 64.0, (double)pPoint->y / 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 / 64.0;
|
||
double dYc = (double)pControlPoint->y / 64.0;
|
||
double dX3 = (double)pEndPoint->x / 64.0;
|
||
double dY3 = (double)pEndPoint->y / 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 / 64.0;
|
||
double dY1 = (double)pFirstControlPoint->y / 64.0;
|
||
double dX2 = (double)pSecondControlPoint->x / 64.0;
|
||
double dY2 = (double)pSecondControlPoint->y / 64.0;
|
||
double dX3 = (double)pEndPoint->x / 64.0;
|
||
double dY3 = (double)pEndPoint->y / 64.0;
|
||
|
||
pGlyphPath->pPath->CurveTo( dX1, dY1, dX2, dY2, dX3, dY3 );
|
||
pGlyphPath->bNeedClose = TRUE;
|
||
return 0;
|
||
}
|
||
|
||
NSFonts::IFontPath* CFontFile::GetGlyphPath(int nCode)
|
||
{
|
||
static FT_Outline_Funcs pOutlineFuncs =
|
||
{
|
||
&GlyphPathMoveTo,
|
||
&GlyphPathLineTo,
|
||
&GlyphPathConicTo,
|
||
&GlyphPathCubicTo,
|
||
0, 0
|
||
};
|
||
TFreeTypeFontPath oGlyphPath;
|
||
oGlyphPath.pPath = new CFontPath();
|
||
oGlyphPath.bNeedClose = FALSE;
|
||
|
||
CVectorWorker oWorker;
|
||
oWorker.func_interface = &pOutlineFuncs;
|
||
oWorker.user = &oGlyphPath;
|
||
|
||
CacheGlyph(nCode, false, &oWorker);
|
||
|
||
return oGlyphPath.pPath;
|
||
}
|
||
|
||
bool CFontFile::IsItalic()
|
||
{
|
||
if (!m_pFace)
|
||
return false;
|
||
|
||
return ((m_pFace->style_flags & FT_STYLE_FLAG_ITALIC) != 0) ? true : false;
|
||
}
|
||
|
||
bool CFontFile::IsBold()
|
||
{
|
||
if (!m_pFace)
|
||
return false;
|
||
|
||
TT_OS2* pOS2 = (TT_OS2*)FT_Get_Sfnt_Table(m_pFace, ft_sfnt_os2);
|
||
if (pOS2 && pOS2->version != 0xFFFF && pOS2->usWeightClass >= 800)
|
||
return true;
|
||
|
||
return ((m_pFace->style_flags & FT_STYLE_FLAG_BOLD) != 0) ? true : false;
|
||
}
|
||
|
||
bool CFontFile::IsSymbolic(bool bIsOS2Check)
|
||
{
|
||
if (!m_pFace)
|
||
return false;
|
||
|
||
bool bIsSymbol = (-1 != m_nSymbolic) ? true : false;
|
||
|
||
if (!bIsSymbol && bIsOS2Check)
|
||
{
|
||
TT_OS2* pOS2 = (TT_OS2*)FT_Get_Sfnt_Table(m_pFace, ft_sfnt_os2);
|
||
|
||
if (NULL != pOS2)
|
||
{
|
||
if (0 == (pOS2->ulCodePageRange1 & 0xF0000000))
|
||
bIsSymbol = true;
|
||
}
|
||
}
|
||
|
||
return bIsSymbol;
|
||
}
|
||
|
||
int CFontFile::GetEmbeddingLicenceType()
|
||
{
|
||
if (!m_pFace)
|
||
return 0;
|
||
|
||
int nType = 0;
|
||
TT_OS2* pOS2 = (TT_OS2*)FT_Get_Sfnt_Table(m_pFace, ft_sfnt_os2);
|
||
if (NULL != pOS2)
|
||
return (int)pOS2->fsType;
|
||
return 0;
|
||
}
|
||
void CFontFile::FillFontSelectFormat(NSFonts::CFontSelectFormat& oFormat)
|
||
{
|
||
if (!m_pFace)
|
||
return;
|
||
|
||
if (NULL == oFormat.wsName)
|
||
oFormat.wsName = new std::wstring(m_sName);
|
||
|
||
if (NULL == oFormat.wsFamilyClass)
|
||
oFormat.wsFamilyClass = new std::wstring(GetCorrectSfntName(m_pFace->style_name));
|
||
|
||
if (NULL == oFormat.bItalic)
|
||
oFormat.bItalic = new INT(IsItalic() ? 1 : 0);
|
||
|
||
if (NULL == oFormat.bBold)
|
||
oFormat.bBold = new INT(IsBold() ? 1 : 0);
|
||
|
||
if (NULL == oFormat.bFixedWidth)
|
||
oFormat.bFixedWidth = new INT(IsFixedWidth() ? 1 : 0);
|
||
|
||
TT_OS2* pOS2 = (TT_OS2*)FT_Get_Sfnt_Table(m_pFace, ft_sfnt_os2);
|
||
if (NULL != pOS2)
|
||
{
|
||
if (NULL == oFormat.pPanose)
|
||
{
|
||
oFormat.pPanose = new BYTE[10];
|
||
memcpy((void*)oFormat.pPanose, pOS2->panose, 10);
|
||
}
|
||
|
||
if (NULL == oFormat.ulRange1)
|
||
oFormat.ulRange1 = new UINT(pOS2->ulUnicodeRange1);
|
||
if (NULL == oFormat.ulRange2)
|
||
oFormat.ulRange2 = new UINT(pOS2->ulUnicodeRange2);
|
||
if (NULL == oFormat.ulRange3)
|
||
oFormat.ulRange3 = new UINT(pOS2->ulUnicodeRange3);
|
||
if (NULL == oFormat.ulRange4)
|
||
oFormat.ulRange4 = new UINT(pOS2->ulUnicodeRange4);
|
||
|
||
if (NULL == oFormat.ulCodeRange1)
|
||
oFormat.ulCodeRange1 = new UINT(pOS2->ulCodePageRange1);
|
||
if (NULL == oFormat.ulCodeRange2)
|
||
oFormat.ulCodeRange2 = new UINT(pOS2->ulCodePageRange2);
|
||
|
||
if (NULL == oFormat.usWeight)
|
||
oFormat.usWeight = new USHORT(pOS2->usWeightClass);
|
||
if (NULL == oFormat.usWidth)
|
||
oFormat.usWidth = new USHORT(pOS2->usWidthClass);
|
||
|
||
if (0 != m_pFace->units_per_EM)
|
||
{
|
||
double dKoef = ( 1000 / (double)m_pFace->units_per_EM );
|
||
|
||
if (NULL == oFormat.shAvgCharWidth)
|
||
oFormat.shAvgCharWidth = new SHORT((SHORT)pOS2->xAvgCharWidth * dKoef);
|
||
if (NULL == oFormat.shAscent)
|
||
oFormat.shAscent = new SHORT((SHORT)pOS2->sTypoAscender * dKoef);
|
||
if (NULL == oFormat.shDescent)
|
||
oFormat.shDescent = new SHORT((SHORT)pOS2->sTypoDescender * dKoef);
|
||
if (NULL == oFormat.shLineGap)
|
||
oFormat.shLineGap = new SHORT((SHORT)pOS2->sTypoLineGap * dKoef);
|
||
if (NULL == oFormat.shXHeight)
|
||
oFormat.shXHeight = new SHORT((SHORT)pOS2->sxHeight * dKoef);
|
||
if (NULL == oFormat.shCapHeight)
|
||
oFormat.shCapHeight = new SHORT((SHORT)pOS2->sCapHeight * dKoef);
|
||
}
|
||
else
|
||
{
|
||
if (NULL == oFormat.shAvgCharWidth)
|
||
oFormat.shAvgCharWidth = new SHORT(pOS2->xAvgCharWidth);
|
||
if (NULL == oFormat.shAscent)
|
||
oFormat.shAscent = new SHORT(pOS2->sTypoAscender);
|
||
if (NULL == oFormat.shDescent)
|
||
oFormat.shDescent = new SHORT(pOS2->sTypoDescender);
|
||
if (NULL == oFormat.shLineGap)
|
||
oFormat.shLineGap = new SHORT(pOS2->sTypoLineGap);
|
||
if (NULL == oFormat.shXHeight)
|
||
oFormat.shXHeight = new SHORT(pOS2->sxHeight);
|
||
if (NULL == oFormat.shCapHeight)
|
||
oFormat.shCapHeight = new SHORT(pOS2->sCapHeight);
|
||
}
|
||
|
||
if (NULL == oFormat.nFontFormat)
|
||
oFormat.nFontFormat = new int((int)GetFontFormatType(m_pFace));
|
||
|
||
if (NULL == oFormat.usType)
|
||
oFormat.usType = new USHORT(pOS2->fsType);
|
||
}
|
||
}
|