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

分享于 

15分钟阅读

多媒体

  繁體
  • 下载演示项目-206.31 KB
  • 下载源代码-243.93 KB
  • Sample Font

    介绍

    为了使应用程序使用字体,需要使用安装程序来安装字体。用户计算机上的字体过多可能会大大降低系统速度。

    实际上你可以不用安装字体:GDI和GDI+都提供了两种方式,供程序员在不安装的情况下为应用程序添加字体。

    GDI的AddFontResourceEx

    让我首先讨论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函数中指定它名称,就像其他已安装的字体一样,

    注:本文中的字体文件名("SkiCargo.ttf")实际上是它的字体名"SkiCargo";通常情况下不是这样的!

    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 itCFont newFont;if (!newFont.CreateFontIndirect(&lf))
     return;
    CFont* pOldFont = dc.SelectObject(&newFont);// use a path to record how the text was drawnwchar_t buf[] = _T("The quick brown fox jumps over the lazy dog!");
    dc.TextOut( 10, 10, buf, wcslen(buf));// Put back the old fontdc.SelectObject(pOldFont);

    必须记住在应用程序退出之前调用RemoveFontResourceEx,你应该注意,参数必须与提供给AddFontResourceEx的参数相同!

    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,cab文件或归档压缩文件中,则可以将它提取到内存中,然后使用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");
     }
    }

    PrivateFontCollection =s共GDI+ 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 textFontFamily 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的AddFontResourceExAddFontMemResourceEx不同,RemoveFontFile没有用于AddFontFile,所有添加的字体都将由PrivateFontCollection =s析构函数删除。

    PrivateFontCollection =s共GDI+ AddMemoryFont

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

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

    以下是如何在资源中嵌入的字体文件上使用AddMemoryFont,类似于AddFontFile,没有要调用的RemoveMemoryFont,一切都将由PrivateFontCollection =s析构函数处理,注意:要了解如何向资源添加字体文件,可以在本文的后面参考这个部分

    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字体名称

    我编写了两个类,即TTFTTC,分别从ttf otf和TTC字体文件中读取字体名称,为了支持Matroska (mkv )文件字体读取或嵌入字体资源读取,TTFTTC类支持在内存中解析字体文件,以下是一个物理或在内存中读取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方法,大多数字体都属于font-family,例如,在Arial字体家族下,有几种Arial字体,其字体名称为"Arial Bold","Arial Bold Italic",依此类推。以下是有关如何将TTF =s GetFontFamilyName方法与AddFontResourceEx函数一起使用的示例:

    TTF m_Ttf;//... During InitializationCString 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 methodCClientDC 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 itCFont newFont;if (!newFont.CreateFontIndirect(&lf))
     return;
    CFont* pOldFont = dc.SelectObject(&newFont);// use a path to record how the text was drawnwchar_t buf[] = _T("The quick brown fox jumps over the lazy dog!");
    dc.TextOut( 10, 10, buf, wcslen(buf));// Put back the old fontdc.SelectObject(pOldFont);

    相关文章