517 lines
16 KiB
C++
517 lines
16 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
|
||
*
|
||
*/
|
||
// FontConverter.h : Declaration of the CFontConverter
|
||
|
||
#pragma once
|
||
#include "resource.h" // main symbols
|
||
|
||
#include FT_XFREE86_H
|
||
#include FT_TRUETYPE_TABLES_H
|
||
#include "FontFileTrueType.h"
|
||
#include "FontFileType1C.h"
|
||
#include "FontFileType1.h"
|
||
#include "Consts.h"
|
||
|
||
#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
|
||
#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
|
||
#endif
|
||
|
||
// IFontConverter
|
||
[
|
||
object,
|
||
uuid("F92BB671-8B39-4D4F-AC43-D697A20CE0CA"),
|
||
dual, helpstring("IFontConverter Interface"),
|
||
pointer_default(unique)
|
||
]
|
||
__interface IFontConverter : IDispatch
|
||
{
|
||
|
||
[id(101)] HRESULT ToOTF([in] BSTR bsInFontFile, [in] BSTR pbsFontFileOut, [in, satype("unsigned short")] SAFEARRAY *pUnicode, [in] BSTR bsName, [in] long nFlag );
|
||
[id(102)] HRESULT ToOTF2([in] BSTR bsInFontFile, [in, satype("unsigned short")] SAFEARRAY *pUnicode, [in] BSTR bsName, [in] long nFlag, [in] long lSrcFaceIndex, [out, satype("BYTE")] SAFEARRAY** ppFontData);
|
||
|
||
//----- Для дополнительных функций ----------------------------------------------------------------
|
||
|
||
[id(10001)] HRESULT SetAdditionalParam([in] BSTR ParamName, [in] VARIANT ParamValue);
|
||
[id(10002)] HRESULT GetAdditionalParam([in] BSTR ParamName, [out] VARIANT * ParamValue);
|
||
|
||
};
|
||
|
||
|
||
|
||
// CFontConverter
|
||
|
||
[
|
||
coclass,
|
||
default(IFontConverter),
|
||
threading(apartment),
|
||
vi_progid("AVSFontConverter.FontConverter"),
|
||
progid("AVSFontConverter.FontConverter.1"),
|
||
version(1.0),
|
||
uuid("3B756386-2427-461C-B979-3AB33F2A057E"),
|
||
helpstring("FontConverter Class")
|
||
]
|
||
class ATL_NO_VTABLE CFontConverter : public IFontConverter
|
||
{
|
||
public:
|
||
CFontConverter()
|
||
{
|
||
}
|
||
|
||
|
||
|
||
DECLARE_PROTECT_FINAL_CONSTRUCT()
|
||
|
||
HRESULT FinalConstruct()
|
||
{
|
||
return S_OK;
|
||
}
|
||
|
||
void FinalRelease()
|
||
{
|
||
}
|
||
|
||
public:
|
||
|
||
STDMETHOD(ToOTF)(BSTR bsFontIn, BSTR bsFontOut, SAFEARRAY *pUnicodeArray, BSTR bsName, long nFlag)
|
||
{
|
||
FT_Library pLibrary = NULL;
|
||
if ( FT_Init_FreeType( &pLibrary ) )
|
||
return S_FALSE;
|
||
|
||
FT_Face pFace = NULL;
|
||
|
||
// открываем файл
|
||
HANDLE hFile = CreateFile( (LPCWSTR)bsFontIn, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
if (INVALID_HANDLE_VALUE == hFile)
|
||
return NULL; // Невозможно открыть файл
|
||
|
||
// мапим этот файл в память - так быстрее читаются данные из файла
|
||
DWORD nFileSize = GetFileSize(hFile, NULL);
|
||
HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, nFileSize, NULL);
|
||
if (NULL == hMapFile)
|
||
{
|
||
CloseHandle( hFile );
|
||
return NULL; // Невозможно создать отображение файла
|
||
}
|
||
|
||
void *pBaseAddress = MapViewOfFile( hMapFile, FILE_MAP_READ, 0, 0, 0 );
|
||
if ( !pBaseAddress )
|
||
{
|
||
CloseHandle( hMapFile );
|
||
CloseHandle( hFile );
|
||
return NULL;
|
||
}
|
||
|
||
FT_Open_Args oOpenArgs;
|
||
oOpenArgs.flags = FT_OPEN_MEMORY;
|
||
oOpenArgs.memory_base = (BYTE*)pBaseAddress;
|
||
oOpenArgs.memory_size = (FT_Long)nFileSize;
|
||
|
||
CFontFileTrueType* pTTF = CFontFileTrueType::LoadFromFile( bsFontIn );
|
||
FT_Error oerrr;
|
||
if ( oerrr = FT_Open_Face( pLibrary, &oOpenArgs, 0, &pFace ) )
|
||
{
|
||
FT_Done_FreeType( pLibrary );
|
||
CloseHandle( hMapFile );
|
||
CloseHandle( hFile );
|
||
UnmapViewOfFile( pBaseAddress );
|
||
|
||
return S_FALSE;
|
||
}
|
||
|
||
CString sFontFormat( FT_Get_X11_Font_Format( pFace ) );
|
||
|
||
// Проверим флаг конвертации и исходный формат шрифта
|
||
bool bNeedConvert = false;
|
||
|
||
if ( nFlag == c_lFromAll || ( _T("TrueType") == sFontFormat && nFlag & c_lFromTT ) || ( _T("CFF") == sFontFormat && nFlag & c_lFromCFF ) || ( _T("Type 1") == sFontFormat && nFlag & c_lFromT1 ) )
|
||
bNeedConvert = true;
|
||
|
||
bool bIsGids = (c_lFlagsGids & nFlag);
|
||
|
||
if ( bNeedConvert )
|
||
{
|
||
if ( _T("CFF") == sFontFormat || _T("Type 1") == sFontFormat )
|
||
{
|
||
TCharBuffer oCFF;
|
||
CFontFileType1C *pT1C = NULL;
|
||
if ( _T("Type 1") == sFontFormat )
|
||
{
|
||
// Сначала сконвертируем Type1 в CFF
|
||
CFontFileType1* pT1 = CFontFileType1::LoadFromFile( bsFontIn );
|
||
pT1->ToCFF( &CharBufferWrite, &oCFF );
|
||
delete pT1;
|
||
|
||
// Конвертируем CFF в OpenTypeCFF
|
||
pT1C = CFontFileType1C::LoadFromBuffer( oCFF.sBuffer, oCFF.nLen );
|
||
}
|
||
else
|
||
{
|
||
// FreeType отдает тип шрифта CFF, в случаях когда файл имеет тип OpenType(CFF).
|
||
// Если так оно и есть, тогда нам с файлом ничего делать на надо.
|
||
pT1C = CFontFileType1C::LoadFromFile( bsFontIn );
|
||
}
|
||
|
||
if ( pT1C )
|
||
{
|
||
FILE* pFile = _wfopen( bsFontOut, _T("wb+") );
|
||
pT1C->ToOpenTypeCFF( &FileWrite, pFile, pFace );
|
||
::fclose( pFile );
|
||
}
|
||
|
||
delete pT1C;
|
||
}
|
||
else if ( _T("TrueType") == sFontFormat && ( pUnicodeArray != NULL || bsName != NULL ) )
|
||
{
|
||
CFontFileTrueType* pTTF = CFontFileTrueType::LoadFromFile( bsFontIn );
|
||
if ( pTTF )
|
||
{
|
||
USES_CONVERSION;
|
||
char *sName = ( NULL == bsName ? NULL : W2A( bsName ) );
|
||
unsigned char *pUseGlyfs = NULL;
|
||
long lGlyfsCount = pFace->num_glyphs;
|
||
|
||
if ( pUnicodeArray )
|
||
{
|
||
// Сначала составим список нужных нами GID
|
||
LONG lCount = pUnicodeArray->rgsabound[0].cElements;
|
||
unsigned short* pUnicode = (unsigned short*)pUnicodeArray->pvData;
|
||
unsigned short* pGIDs = new unsigned short[lCount];
|
||
int nCMapIndex = 0;
|
||
|
||
int nSymbolicIndex = GetSymbolicCmapIndex(pFace);
|
||
|
||
if (!bIsGids)
|
||
{
|
||
for ( int nIndex = 0; nIndex < lCount; nIndex++ )
|
||
{
|
||
pGIDs[nIndex] = SetCMapForCharCode( pFace, pUnicode[nIndex], &nCMapIndex );
|
||
|
||
if ((pGIDs[nIndex] == 0) && (-1 != nSymbolicIndex) && (pUnicode[nIndex] < 0xF000))
|
||
{
|
||
pGIDs[nIndex] = SetCMapForCharCode( pFace, pUnicode[nIndex] + 0xF000, &nCMapIndex );
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
memcpy(pGIDs, pUnicode, lCount * sizeof(unsigned short));
|
||
}
|
||
|
||
pUseGlyfs = new unsigned char[lGlyfsCount];
|
||
::memset( pUseGlyfs, 0x00, lGlyfsCount * sizeof(unsigned char) );
|
||
pUseGlyfs[0] = 1; // нулевой гид всегда записываем
|
||
for ( int nGID = 1; nGID < lGlyfsCount; nGID++ )
|
||
{
|
||
if ( 1 != pUseGlyfs[nGID] )
|
||
{
|
||
bool bFound = false;
|
||
for ( int nIndex = 0; nIndex < lCount; nIndex++ )
|
||
{
|
||
if ( nGID == pGIDs[nIndex] )
|
||
{
|
||
bFound = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Если данный символ составной (CompositeGlyf), тогда мы должны учесть все его дочерные символы (subglyfs)
|
||
if ( bFound && 0 == FT_Load_Glyph( pFace, nGID, FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE ) )
|
||
{
|
||
for ( int nSubIndex = 0; nSubIndex < pFace->glyph->num_subglyphs; nSubIndex++ )
|
||
{
|
||
FT_Int nSubGID;
|
||
FT_UInt unFlags;
|
||
FT_Int nArg1;
|
||
FT_Int nArg2;
|
||
FT_Matrix oMatrix;
|
||
FT_Get_SubGlyph_Info( pFace->glyph, nSubIndex, &nSubGID, &unFlags, &nArg1, &nArg2, &oMatrix );
|
||
|
||
if ( nSubGID < lGlyfsCount )
|
||
pUseGlyfs[nSubGID] = 1;
|
||
}
|
||
}
|
||
|
||
if ( bFound )
|
||
pUseGlyfs[nGID] = 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
FILE* pFile = _wfopen( bsFontOut, _T("wb+") );
|
||
pTTF->WriteTTF( &FileWrite, pFile, sName, NULL, pUseGlyfs, lGlyfsCount );
|
||
::fclose( pFile );
|
||
}
|
||
else
|
||
{
|
||
// error parse font
|
||
// Просто копируем файл
|
||
CopyFile( bsFontIn, bsFontOut, FALSE );
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Просто копируем файл
|
||
CopyFile( bsFontIn, bsFontOut, FALSE );
|
||
}
|
||
|
||
FT_Done_Face( pFace );
|
||
FT_Done_FreeType( pLibrary );
|
||
|
||
CloseHandle( hMapFile );
|
||
CloseHandle( hFile );
|
||
UnmapViewOfFile( pBaseAddress );
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
STDMETHOD(ToOTF2)(BSTR bsFontIn, SAFEARRAY *pUnicodeArray, BSTR bsName, long nFlag, long lFaceIndex, SAFEARRAY** ppData)
|
||
{
|
||
// функция просто скопирована и немного доработана. это все из-за нехватки времени.
|
||
|
||
FT_Library pLibrary = NULL;
|
||
if ( FT_Init_FreeType( &pLibrary ) )
|
||
return S_FALSE;
|
||
|
||
FT_Face pFace = NULL;
|
||
|
||
// открываем файл
|
||
HANDLE hFile = CreateFile( (LPCWSTR)bsFontIn, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||
if (INVALID_HANDLE_VALUE == hFile || NULL == ppData)
|
||
return NULL; // Невозможно открыть файл
|
||
|
||
// мапим этот файл в память - так быстрее читаются данные из файла
|
||
DWORD nFileSize = GetFileSize(hFile, NULL);
|
||
HANDLE hMapFile = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, nFileSize, NULL);
|
||
if (NULL == hMapFile)
|
||
{
|
||
CloseHandle( hFile );
|
||
return NULL; // Невозможно создать отображение файла
|
||
}
|
||
|
||
void *pBaseAddress = MapViewOfFile( hMapFile, FILE_MAP_READ, 0, 0, 0 );
|
||
if ( !pBaseAddress )
|
||
{
|
||
CloseHandle( hMapFile );
|
||
CloseHandle( hFile );
|
||
return NULL;
|
||
}
|
||
|
||
FT_Open_Args oOpenArgs;
|
||
oOpenArgs.flags = FT_OPEN_MEMORY;
|
||
oOpenArgs.memory_base = (BYTE*)pBaseAddress;
|
||
oOpenArgs.memory_size = (FT_Long)nFileSize;
|
||
|
||
CFontFileTrueType* pTTF = CFontFileTrueType::LoadFromFile( bsFontIn );
|
||
FT_Error oerrr;
|
||
if ( oerrr = FT_Open_Face( pLibrary, &oOpenArgs, lFaceIndex, &pFace ) )
|
||
{
|
||
FT_Done_FreeType( pLibrary );
|
||
CloseHandle( hMapFile );
|
||
CloseHandle( hFile );
|
||
UnmapViewOfFile( pBaseAddress );
|
||
|
||
return S_FALSE;
|
||
}
|
||
|
||
CString sFontFormat( FT_Get_X11_Font_Format( pFace ) );
|
||
|
||
// Проверим флаг конвертации и исходный формат шрифта
|
||
bool bNeedConvert = false;
|
||
|
||
if ( nFlag == c_lFromAll || ( _T("TrueType") == sFontFormat && nFlag & c_lFromTT ) || ( _T("CFF") == sFontFormat && nFlag & c_lFromCFF ) || ( _T("Type 1") == sFontFormat && nFlag & c_lFromT1 ) )
|
||
bNeedConvert = true;
|
||
|
||
bool bIsGids = (c_lFlagsGids & nFlag);
|
||
|
||
TCharBuffer* pCharBuffer = NULL;
|
||
|
||
if ( bNeedConvert )
|
||
{
|
||
pCharBuffer = new TCharBuffer(100000); // ~100Kb
|
||
if ( _T("CFF") == sFontFormat || _T("Type 1") == sFontFormat )
|
||
{
|
||
TCharBuffer oCFF;
|
||
CFontFileType1C *pT1C = NULL;
|
||
if ( _T("Type 1") == sFontFormat )
|
||
{
|
||
// Сначала сконвертируем Type1 в CFF
|
||
CFontFileType1* pT1 = CFontFileType1::LoadFromFile( bsFontIn );
|
||
pT1->ToCFF( &CharBufferWrite, &oCFF );
|
||
delete pT1;
|
||
|
||
// Конвертируем CFF в OpenTypeCFF
|
||
pT1C = CFontFileType1C::LoadFromBuffer( oCFF.sBuffer, oCFF.nLen );
|
||
}
|
||
else
|
||
{
|
||
// FreeType отдает тип шрифта CFF, в случаях когда файл имеет тип OpenType(CFF).
|
||
// Если так оно и есть, тогда нам с файлом ничего делать на надо.
|
||
pT1C = CFontFileType1C::LoadFromFile( bsFontIn );
|
||
}
|
||
|
||
if ( pT1C )
|
||
{
|
||
pT1C->ToOpenTypeCFF( &CharBufferWrite, pCharBuffer, pFace );
|
||
}
|
||
|
||
delete pT1C;
|
||
}
|
||
else if ( _T("TrueType") == sFontFormat && ( pUnicodeArray != NULL || bsName != NULL ) )
|
||
{
|
||
CFontFileTrueType* pTTF = CFontFileTrueType::LoadFromFile( bsFontIn );
|
||
if ( pTTF )
|
||
{
|
||
USES_CONVERSION;
|
||
char *sName = ( NULL == bsName ? NULL : W2A( bsName ) );
|
||
unsigned char *pUseGlyfs = NULL;
|
||
long lGlyfsCount = pFace->num_glyphs;
|
||
|
||
if ( pUnicodeArray )
|
||
{
|
||
// Сначала составим список нужных нами GID
|
||
LONG lCount = pUnicodeArray->rgsabound[0].cElements;
|
||
unsigned short* pUnicode = (unsigned short*)pUnicodeArray->pvData;
|
||
unsigned short* pGIDs = new unsigned short[lCount];
|
||
int nCMapIndex = 0;
|
||
|
||
int nSymbolicIndex = GetSymbolicCmapIndex(pFace);
|
||
|
||
if (!bIsGids)
|
||
{
|
||
for ( int nIndex = 0; nIndex < lCount; nIndex++ )
|
||
{
|
||
pGIDs[nIndex] = SetCMapForCharCode( pFace, pUnicode[nIndex], &nCMapIndex );
|
||
|
||
if ((pGIDs[nIndex] == 0) && (-1 != nSymbolicIndex) && (pUnicode[nIndex] < 0xF000))
|
||
{
|
||
pGIDs[nIndex] = SetCMapForCharCode( pFace, pUnicode[nIndex] + 0xF000, &nCMapIndex );
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
memcpy(pGIDs, pUnicode, lCount * sizeof(unsigned short));
|
||
}
|
||
|
||
pUseGlyfs = new unsigned char[lGlyfsCount];
|
||
::memset( pUseGlyfs, 0x00, lGlyfsCount * sizeof(unsigned char) );
|
||
pUseGlyfs[0] = 1; // нулевой гид всегда записываем
|
||
for ( int nGID = 1; nGID < lGlyfsCount; nGID++ )
|
||
{
|
||
if ( 1 != pUseGlyfs[nGID] )
|
||
{
|
||
bool bFound = false;
|
||
for ( int nIndex = 0; nIndex < lCount; nIndex++ )
|
||
{
|
||
if ( nGID == pGIDs[nIndex] )
|
||
{
|
||
bFound = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Если данный символ составной (CompositeGlyf), тогда мы должны учесть все его дочерные символы (subglyfs)
|
||
if ( bFound && 0 == FT_Load_Glyph( pFace, nGID, FT_LOAD_NO_SCALE | FT_LOAD_NO_RECURSE ) )
|
||
{
|
||
for ( int nSubIndex = 0; nSubIndex < pFace->glyph->num_subglyphs; nSubIndex++ )
|
||
{
|
||
FT_Int nSubGID;
|
||
FT_UInt unFlags;
|
||
FT_Int nArg1;
|
||
FT_Int nArg2;
|
||
FT_Matrix oMatrix;
|
||
FT_Get_SubGlyph_Info( pFace->glyph, nSubIndex, &nSubGID, &unFlags, &nArg1, &nArg2, &oMatrix );
|
||
|
||
if ( nSubGID < lGlyfsCount )
|
||
pUseGlyfs[nSubGID] = 1;
|
||
}
|
||
}
|
||
|
||
if ( bFound )
|
||
pUseGlyfs[nGID] = 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
pTTF->WriteTTF( &CharBufferWrite, pCharBuffer, sName, NULL, pUseGlyfs, lGlyfsCount );
|
||
}
|
||
}
|
||
|
||
LONG nSizeFont = pCharBuffer->nLen;
|
||
|
||
SAFEARRAYBOUND rgsab;
|
||
rgsab.lLbound = 0;
|
||
rgsab.cElements = nSizeFont;
|
||
|
||
SAFEARRAY* pArray = SafeArrayCreate(VT_UI1, 1, &rgsab);
|
||
memcpy(pArray->pvData, pCharBuffer->sBuffer, nSizeFont);
|
||
|
||
RELEASEOBJECT(pCharBuffer);
|
||
|
||
*ppData = pArray;
|
||
}
|
||
else
|
||
{
|
||
SAFEARRAYBOUND rgsab;
|
||
rgsab.lLbound = 0;
|
||
rgsab.cElements = nFileSize;
|
||
|
||
SAFEARRAY* pArray = SafeArrayCreate(VT_UI1, 1, &rgsab);
|
||
memcpy(pArray->pvData, pBaseAddress, nFileSize);
|
||
|
||
*ppData = pArray;
|
||
}
|
||
|
||
FT_Done_Face( pFace );
|
||
FT_Done_FreeType( pLibrary );
|
||
|
||
CloseHandle( hMapFile );
|
||
CloseHandle( hFile );
|
||
UnmapViewOfFile( pBaseAddress );
|
||
|
||
return S_OK;
|
||
}
|
||
|
||
STDMETHOD(SetAdditionalParam)(BSTR bsParamName, VARIANT vParamValue)
|
||
{
|
||
return S_OK;
|
||
}
|
||
STDMETHOD(GetAdditionalParam)(BSTR bsParamName, VARIANT *pvParamValue)
|
||
{
|
||
return S_OK;
|
||
}
|
||
|
||
};
|
||
|