如何在不安装字体的情况下使用字体

如何在不首先在用户系统上安装字体的情况下使用它

目录

 

介绍

很多时候,由于内部图形设计人员会选择字体,因此需要在应用程序中使用特定字体。为了使应用程序使用字体,需要使用安装程序来安装字体。用户计算机上的字体过多可能会大大降低系统速度。

实际上,您无需安装字体就可以摆脱困境:作为程序员,GDI和GDI +分别为您提供了两种添加字体的方式,供应用程序使用而无需安装字体。我将在本文中向您展示!

GDI的AddFontResourceEx

首先让我谈谈GDI的两个向应用程序添加字体的功能。然后,我将讨论GDI +自身的功能。您可以使用AddFontResourceEx添加物理字体文件供应用程序使用。

int AddFontResourceEx(
  LPCTSTR lpszFilename, 	// font file name
  DWORD fl,             	// font characteristics
  PVOID pdv             	// reserved
);

这是一个使用方法的例子AddFontResourceEx

CString szFontFile = "D:\\SkiCargo.ttf";

int nResults = AddFontResourceEx(
    m_szFontFile, 		// font file name
    FR_PRIVATE,    	// font characteristics
    NULL);

要使用添加的字体,只需在CreateFontCreateFontIndirect函数中指定其名称即可,就像其他已安装的字体一样。要知道字体的名称,只需在Windows资源管理器中右键单击TTF扩展名文件,然后选择“打开”即可看到其实际名称。或者,您可以使用我编写的TTFTTC类来了解字体名称。

注意:本文中的字体文件名称(“ SkiCargo.ttf ”)实际上是其字体名称“ SkiCargo”;通常不是这种情况!为了安全起见,请使用Windows资源管理器右键单击方法或刚才提到的TTFand TTC类来查找名称!

CClientDC dc(this);

dc.SetBkMode(TRANSPARENT);

LOGFONT lf;
memset(&lf, 0, sizeof(lf));
lf.lfHeight = -MulDiv(24, pDC->GetDeviceCaps(LOGPIXELSY), 72);
lf.lfWeight = FW_NORMAL;
lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
wcscpy_s(lf.lfFaceName, L"SkiCargo");

// create and select it
CFont newFont;
if (!newFont.CreateFontIndirect(&lf))
    return;
CFont* pOldFont = dc.SelectObject(&newFont);

// use a path to record how the text was drawn
wchar_t buf[] = _T("The quick brown fox jumps over the lazy dog!");
dc.TextOut( 10, 10, buf, wcslen(buf));

// Put back the old font
dc.SelectObject(pOldFont);

您必须记得RemoveFontResourceEx在应用程序退出之前先打电话。您应该注意,这些参数必须与您输入的参数相同AddFontResourceEx

扫描二维码关注公众号,回复: 12675674 查看本文章
BOOL RemoveFontResourceEx(
  LPCTSTR lpFileName,  	// name of font file
  DWORD fl,            	// font characteristics
  PVOID pdv            	// Reserved.
);

CString szFontFile = "D:\\SkiCargo.ttf";

BOOL b = RemoveFontResourceEx(
    m_szFontFile, 		// name of font file
    FR_PRIVATE,   		// font characteristics
    NULL         		// Reserved.
    );

GDI的AddFontMemResourceEx

如果我们的字体位于资源DLL,cabinet文件或档案压缩文件中,则可以将其提取到内存中,然后用于AddFontMemResourceEx从内存中读取它。

HANDLE AddFontMemResourceEx(
  PVOID pbFont,       	// font resource
  DWORD cbFont,       	// number of bytes in font resource 
  PVOID pdv,          	// Reserved. Must be 0.
  DWORD *pcFonts      	// number of fonts installed
);

这是一个如何AddFontMemResourceEx在资源中嵌入的字体文件上使用示例。注意:要了解如何将字体文件添加到资源中,可以在本文后面参考本

HINSTANCE hResInstance = AfxGetResourceHandle( );

HRSRC res = FindResource(hResInstance,
    MAKEINTRESOURCE(IDR_MYFONT),L"BINARY");
if (res) 
{
    HGLOBAL mem = LoadResource(hResInstance, res);
    void *data = LockResource(mem);
    size_t len = SizeofResource(hResInstance, res);

    DWORD nFonts;
    m_fonthandle = AddFontMemResourceEx(
        data,       	// font resource
        len,       	// number of bytes in font resource 
        NULL,          	// Reserved. Must be 0.
        &nFonts      	// number of fonts installed
        );

    if(m_fonthandle==0)
    {
        MessageBox(L"Font add fails", L"Error");
    }
}

要使用添加的字体,请参考前面的AddFontResourceEx示例。他们是一样的。就像其他已安装的字体一样使用它。您应RemoveFontMemResourceEx在应用程序退出之前致电。处理结束后,即使您不致电,系统也会卸载字体RemoveFontMemResourceEx注意:参数必须与您输入的参数相同AddFontResourceEx

BOOL RemoveFontMemResourceEx(
  HANDLE fh   // handle to the font resource
);

if(m_fonthandle)
{
    BOOL b = RemoveFontMemResourceEx(m_fonthandle);
    if(b==0)
    {
        MessageBox(L"Font remove fails", L"Error");
    }
}

GDI +的PrivateFontCollection的AddFontFile

对于GDI +,您可以使用其PrivateFontCollection类成员AddFontFile添加物理字体文件。

Status AddFontFile(const WCHAR* filename);

这是AddFontFile添加字体文件的方法:

Gdiplus::PrivateFontCollection m_fontcollection;
//...
CString szFontFile = szExePath + L"SkiCargo.ttf";

Gdiplus::Status nResults = m_fontcollection.AddFontFile(szFontFile);

这是使用刚刚添加到PrivateFontCollection对象中的字体的方法m_fontcollection

// When painting the text
FontFamily fontFamily;
int nNumFound=0;
m_fontcollection.GetFamilies(1,&fontFamily,&nNumFound);

if(nNumFound>0)
{
    Font font(&fontFamily,28,FontStyleRegular,UnitPixel);

    StringFormat strformat;
    wchar_t buf[] = L"The quick brown fox jumps over the lazy dog!";
    graphics.DrawString(buf,wcslen(buf),&font, 
             PointF(10.0f,10.0f),&strformat,&brush);
}

注意:与GDI的AddFontResourceEx和不同AddFontMemResourceEx,没有RemoveFontFilefor AddFontFile。所有添加的字体都会被PrivateFontCollection的析构函数删除。

GDI +的PrivateFontCollection的AddMemoryFont

对于GDI +,您可以使用其PrivateFontCollection类成员AddMemoryFont在内存中添加字体。

Status AddMemoryFont(const VOID *memory, INT length);

这是AddMemoryFont在资源中嵌入的字体文件上使用方法。与相似AddFontFile,没有RemoveMemoryFont呼叫。PrivateFontCollection的析构函数将处理所有事情。注意:要了解如何将字体文件添加到资源中,可以在本文后面参考本

HINSTANCE hResInstance = AfxGetResourceHandle( );

HRSRC res = FindResource(hResInstance,
    MAKEINTRESOURCE(IDR_MYFONT),L"BINARY");
if (res) 
{
    HGLOBAL mem = LoadResource(hResInstance, res);
    void *data = LockResource(mem);
    size_t len = SizeofResource(hResInstance, res);

    Gdiplus::Status nResults = m_fontcollection.AddMemoryFont(data,len);

    if(nResults!=Gdiplus::Ok)
    {
        MessageBox(L"Font add fails", L"Error");
    }
}

至于如何使用刚刚添加到PrivateFontCollection对象中的字体m_fontcollection,请参考前面的AddFontFile示例,它们是相同的。

获取TTF和TTC字体名称

我编写了两个类,分别是TTF和,分别TTC从TTF / OTF和TTC字体文件中读取字体名称。为了支持Matroska(mkv)文件字体读取或嵌入式字体资源读取,my TTFTTCclass支持解析内存中的字体文件。仅供参考,这些Matroska文件通常包含视频通道,多种语言的音频通道,字幕以及视频中字幕的字体。我的课程非常易于使用。下面是一个以物理方式或在内存中读取TTF文件并显示其信息的示例:

void TestReadTtfFromFile(const std::wstring& szFile)
{
    TTF ttf;
    ttf.Parse(szFile);
    Display(ttf);
}

void TestReadTtfFromMemory(const std::wstring& szFile)
{
    struct _stat bufferStat;
    int nRet = _wstat(szFile.c_str(), &bufferStat);
    FILE* pFile = _wfopen(szFile.c_str(), L"rb");
    if(pFile == NULL)
    {
        std::wcout<<L"Failed to create file"<<std::endl;
        return;
    }
    BYTE* buf = new BYTE[bufferStat.st_size];
    fread(buf,bufferStat.st_size,1,pFile);
    fclose(pFile);
    TTF ttf;
    ttf.Parse(buf, bufferStat.st_size);

    delete [] buf;

    Display(ttf);
}

void Display(TTF& ttf)
{
    std::wcout<<L"FontName : "<<ttf.GetFontName()<<std::endl;
    std::wcout<<L"Copyright : "<<ttf.GetCopyright()<<std::endl;
    std::wcout<<L"FontFamilyName : "<<ttf.GetFontFamilyName()<<std::endl;
    std::wcout<<L"FontSubFamilyName : "<<ttf.GetFontSubFamilyName()<<std::endl;
    std::wcout<<L"FontID : "<<ttf.GetFontID()<<std::endl;
    std::wcout<<L"Version : "<<ttf.GetVersion()<<std::endl;
    std::wcout<<L"PostScriptName : "<<ttf.GetPostScriptName()<<std::endl;
    std::wcout<<L"Trademark : "<<ttf.GetTrademark()<<std::endl;

    std::wstring szBold = ttf.IsBold() ? L"true" : L"false"; 
    std::wstring szItalic = ttf.IsItalic() ? L"true" : L"false"; 
    std::wstring szRegular = ttf.IsRegular() ? L"true" : L"false"; 

    std::wcout<<L"Bold : "<<szBold<<std::endl;
    std::wcout<<L"Italic : "<<szItalic<<std::endl;
    std::wcout<<L"Regular : "<<szRegular<<std::endl;

    std::wcout<<std::endl;
}

TTC是包含TTF字体集合的字体文件。下面是一个以物理方式或在内存中读取TTC文件并显示其信息的示例。

void TestReadTtcFromFile(const std::wstring& szFile)
{
    TTC ttc;
    ttc.Parse(szFile);
    Display(ttc);
}

void TestReadTtcFromMemory(const std::wstring& szFile)
{
    struct _stat bufferStat;
    int nRet = _wstat(szFile.c_str(), &bufferStat);
    FILE* pFile = _wfopen(szFile.c_str(), L"rb");
    if(pFile == NULL)
    {
        std::wcout<<L"Failed to create file"<<std::endl;
        return;
    }
    BYTE* buf = new BYTE[bufferStat.st_size];
    fread(buf,bufferStat.st_size,1,pFile);
    fclose(pFile);
    TTC ttc;
    ttc.Parse(buf, bufferStat.st_size);

    delete [] buf;

    Display(ttc);
}

void Display(TTC& ttc)
{
    for(size_t i=0; i<ttc.Size(); ++i )
    {
        std::wcout<<L"FontName : "<<ttc.GetFontName(i)<<std::endl;
        std::wcout<<L"Copyright : "<<ttc.GetCopyright(i)<<std::endl;
        std::wcout<<L"FontFamilyName : "<<ttc.GetFontFamilyName(i)<<std::endl;
        std::wcout<<L"FontSubFamilyName : "<<ttc.GetFontSubFamilyName(i)<<std::endl;
        std::wcout<<L"FontID : "<<ttc.GetFontID(i)<<std::endl;
        std::wcout<<L"Version : "<<ttc.GetVersion(i)<<std::endl;
        std::wcout<<L"PostScriptName : "<<ttc.GetPostScriptName(i)<<std::endl;
        std::wcout<<L"Trademark : "<<ttc.GetTrademark(i)<<std::endl;

        std::wstring szBold = ttc.IsBold(i) ? L"true" : L"false"; 
        std::wstring szItalic = ttc.IsItalic(i) ? L"true" : L"false"; 
        std::wstring szRegular = ttc.IsRegular(i) ? L"true" : L"false"; 

        std::wcout<<L"Bold : "<<szBold<<std::endl;
        std::wcout<<L"Italic : "<<szItalic<<std::endl;
        std::wcout<<L"Regular : "<<szRegular<<std::endl;

        std::wcout<<std::endl;
    }
}

注意:您应该始终调用GetFontFamilyName方法来获取字体名称,而不是GetFontName方法。大多数字体属于字体家族。例如,在Arial字体家族下,有几种Arial字体,其字体名称为“ Arial Bold”,“ Arial Bold Italic”,等等。以下是有关如何将TTFGetFontFamilyName方法与AddFontResourceEx函数一起使用的示例:

TTF m_Ttf;

//... During Initialization
CString szFontFile = "D:\\SkiCargo.ttf";

int nResults = AddFontResourceEx(
    m_szFontFile, 		// font file name
    FR_PRIVATE,           	// font characteristics
    NULL);

m_Ttf.Parse((LPCWSTR)(m_szFontFile));    
    
//... In the OnPaint method    
    
CClientDC dc(this);

dc.SetBkMode(TRANSPARENT);

LOGFONT lf;
memset(&lf, 0, sizeof(lf));
lf.lfHeight = -MulDiv(24, pDC->GetDeviceCaps(LOGPIXELSY), 72);
lf.lfWeight = FW_NORMAL;
lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
//wcscpy_s(lf.lfFaceName, L"SkiCargo");
wcscpy_s(lf.lfFaceName, m_Ttf.GetFontFamilyName().c_str());

// create and select it
CFont newFont;
if (!newFont.CreateFontIndirect(&lf))
    return;
CFont* pOldFont = dc.SelectObject(&newFont);

// use a path to record how the text was drawn
wchar_t buf[] = _T("The quick brown fox jumps over the lazy dog!");
dc.TextOut( 10, 10, buf, wcslen(buf));

// Put back the old font
dc.SelectObject(pOldFont);

注意:我在网络上找不到足够的信息来解析fon文件,该文件是带有“ fon”扩展名的字体文件。我尝试了反向工程来获取文件名,但是失败了。但是,我会继续尝试。

将字体文件添加到资源

要将字体文件添加到资源部分,请遵循我的演练示例。请注意,我的方法是直接编辑资源文件,而不是通过IDE的资源编辑器添加它,因为根据我的经验,资源编辑器倾向于弄乱资源的rc文件,从而导致所见即所得对话框编辑器不可用。注意:现在,最新的资源编辑器可能更加强大和稳定。要添加字体文件,我们必须分配一个资源ID来引用该字体。为此,请关闭您关注的解决方案或项目(如果已打开)。要分配资源ID,请打开Resource.h

//{
     
     {NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by TestGDI_AddFontMem.RC
//
#define IDR_MAINFRAME			128
#define IDD_TESTGDI_ADDFONTMEM_DIALOG	102

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NEXT_RESOURCE_VALUE		129
#define _APS_NEXT_CONTROL_VALUE		1000
#define _APS_NEXT_SYMED_VALUE		101
#define _APS_NEXT_COMMAND_VALUE		32771
#endif
#endif

我相信您比我的这个简单项目拥有更多的资源ID。让我们将定义的ID命名为“ IDR_MYFONT”。当然,您可以使用您认为合适的任何方式来命名。我们分配IDR_MYFONT,目前的价值_APS_NEXT_RESOURCE_VALUE 129。然后我们将增加_APS_NEXT_RESOURCE_VALUE; 这很重要,我们必须这样做,否则下一个资源将与您的字体共享相同的数字ID。下面是手动编辑的Resource.h的外观:

//{
     
     {NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by TestGDI_AddFontMem.RC
//
#define IDR_MAINFRAME			128
#define IDD_TESTGDI_ADDFONTMEM_DIALOG	102
#define IDR_MYFONT				129

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS

#define _APS_NEXT_RESOURCE_VALUE		130
#define _APS_NEXT_CONTROL_VALUE		1000
#define _APS_NEXT_SYMED_VALUE		101
#define _APS_NEXT_COMMAND_VALUE		32771
#endif
#endif

接下来,我们将编辑rc扩展文件。在您喜欢的文本编辑器中打开文件。注意:Visual Studio不应打开此rc文件所在的项目。搜索下面列出的部分:

/
//
// BINARY
//

如果找不到此部分,则可以自己添加。接下来添加字体ID及其字体文件。您的二进制部分可能看起来像这样。

/
//
// BINARY
//
IDR_MYFONT              BINARY                  "res\\SkiCargo.ttf"

如RC代码所示,IDR_MYFONT 是一个二进制资源,它引用项目文件夹的“ res ”子文件夹中的SkiCargo.ttf文件。

如果发现在资源中添加字体很麻烦,则可以重命名字体文件名及其扩展名,这样没人会知道该文件是一种字体并弄乱了它。您甚至可以加密或压缩它。在读取内存中的文件之前,只需对其解密或将其解压缩即可。

结论

您已经从GDI和GDI +中看到了两种方法,它们可以物理加载字体文件或从内存加载字体文件并使用它们。我希望这可以消除程序员在用户计算机上安装字体以使用它们的需要。我介绍了两个类来读取TTF和TTC字体文件的字体名称。您喜欢或不喜欢这篇文章的任何地方,请告诉我,以便我可以对本文进行改进。我希望您喜欢阅读我的文章!

猜你喜欢

转载自blog.csdn.net/greless/article/details/107981903