The fonts for many East Asian languages have two typeface names: an English name and a localized name.
比如:
宋体 vs SimSun
幼圆 vs SimYou
有时候我们需要得到English name,比如PDF中存储的字体名一般都是英文名,经常会用到English name和localized name之间来回转换,本文只说如何从localized name转换为English name,反之其实同理。
具体做法就是用EnumFonts函数枚举所有字体,在它的callback function处理每个字体的的信息,得到它的English name,并把English name和localized name map起来,每次用的时候都可以查询。
代码如下:
class CFontInfo{
...
CMapStringToString m_mapLocToEngFontName;
int createFontNameMaps();
...
}
struct NAMERECORD {
USHORT platformID;
USHORT encodingID;
USHORT languageID;
USHORT nameID;
USHORT length;
USHORT offset;
};
#define SWAPBYTES(w) ( ((USHORT)((w)[0]) << 8) | (w)[1])
//callback function
int CALLBACK MyEnumFontsProc(CONST LOGFONT *lplf,CONST TEXTMETRIC *lptm,DWORD dwType,LPARAM lpData)
{
for(int j=0;j<LF_FACESIZE;j++)
{
if(lplf->lfFaceName[j]==0)
return 1;
else if(lplf->lfFaceName[j]<0 || lplf->lfFaceName[j]>128)
break;
}
HDC hdc=GetDC(NULL);
HFONT hfont = CreateFontIndirect(lplf);
if(hfont) {
HFONT hfontOld = (HFONT)::SelectObject(hdc, hfont);
if(hfontOld) {
DWORD dw = ::GetFontData(hdc, 'eman', 0, NULL, 0);
if(dw != GDI_ERROR) {
LPBYTE lpv = (BYTE *)malloc(dw);
if(lpv) {
dw = ::GetFontData(hdc, 'eman', 0, lpv, dw);
if(dw != GDI_ERROR) {
UINT offset = 0;
USHORT format, count, stringOffset;
NAMERECORD * rgNR;
format = SWAPBYTES(&lpv[offset]);
offset += sizeof(USHORT);
count = SWAPBYTES(&lpv[offset]);
offset += sizeof(USHORT);
stringOffset = SWAPBYTES(&lpv[offset]);
offset += sizeof(USHORT);
rgNR = (struct NAMERECORD *)calloc(count, sizeof(NAMERECORD));
for(int i = 0; i < count; i++) {
rgNR[i].platformID = SWAPBYTES(&lpv[offset]);
offset += sizeof(USHORT);
rgNR[i].encodingID = SWAPBYTES(&lpv[offset]);
offset += sizeof(USHORT);
rgNR[i].languageID = SWAPBYTES(&lpv[offset]);
offset += sizeof(USHORT);
rgNR[i].nameID = SWAPBYTES(&lpv[offset]);
offset += sizeof(USHORT);
rgNR[i].length = SWAPBYTES(&lpv[offset]);
offset += sizeof(USHORT);
rgNR[i].offset = SWAPBYTES(&lpv[offset]);
offset += sizeof(USHORT);
// The name string is at
// lpv + stringOffset + rgNR[i].offset
// The name string length is rgNR[i].length
// The bytes in both cases must each be run
// through something like the SWAPBYTES macro
// because all font information is big endian
// regardless of endian-ness of the platform).
//http://www.microsoft.com/typography/otspec/name.htm
if(rgNR[i].languageID==0 && rgNR[i].nameID==4)//Full font name
{
CString strEngName((LPCSTR)(lpv+stringOffset+rgNR[i].offset),rgNR[i].length);
CString strLogName(lplf->lfFaceName);
CFontInfo *pFontInfo=(CFontInfo*)lpData;
CString strOut;
if(pFontInfo->m_mapLocToEngFontName.Lookup(strLogName, strOut)==0)
{
pFontInfo->m_mapLocToEngFontName[strLogName] = strEngName;
}
break;
}
}
free(rgNR);
}
free(lpv);
}
}
::SelectObject(hdc, hfontOld);
}
::DeleteObject(hfont);
}
ReleaseDC(NULL, hdc);
return 1;
}
int CFontInfo::createFontNameMaps()
{
HDC dc=::GetDC(NULL);
if(!dc)
return 0;
EnumFonts(dc,NULL,MyEnumFontsProc,(LPARAM)this);
::ReleaseDC(NULL, dc);
return m_mapLocToEngFontName.GetCount();
}
//conver logic name to english name
CString strEngName;
if(m_mapLocToEngFontName.Lookup(pCurFontInfo->m_strName, strEngName))
{
//do something
}
ps: 实际测试,“微软雅黑”并没有找到对应的英文名,我记得“微软雅黑”的英文名是Microsoft Yahei,它对应的languageID=0x0409,如果去掉languageID==0的条件,“微软雅黑”得到的英文名就是空,查看点击打开链接 ,也没有必须用languageID==0的理由,但是不用它就不对,不晓得为什么,这个后续再研究吧.
ps2:google了一下,发现找到了这段代码的出处,应该是作者受不了windows居然不提供这样的API,然后自己去看了OpenType Specification,研究出来的方法,这是那篇文章的地址:点击打开链接