注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Code@Pig Home

喜欢背着一袋Code傻笑的Pig .. 忧美.欢笑.记忆.忘却 .之. 角落

 
 
 

日志

 
 

[win32] 如何从中文字体名字,获取字体文字路径  

2015-08-07 15:12:59|  分类: win32 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
游戏中,我们要用 FreeType2,就必须知道 ttf 文件的全路径。
win32 下怎么搞呢。

比如:"黑体" => c:\windows\fonts\simhei.ttc


基本思路
 1. 获得一个字体的 English Name,比如黑体是 "SimHei"
 2. 根据 English Name,去注册表("HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts")中遍历找到对应的 .ttc 文件名


获得 English Name
这一步比较坑,要利用 GetFontData() 去拿 .ttc 文件中的信息。然后手工解析。
-----------------------------------
#include <Windows.h>
#include <cstdint>
#include <cassert>
#include <string>
#include <iostream>

uint16_t readBigEndianWord( uint8_t * p)
{
    return ( p[0] << 8) + p[1];
}

struct NameRecord
{
    uint16_t platformID;
    uint16_t platformSpecificID;
    uint16_t languageID;
    uint16_t nameID;
    uint16_t length;
    uint16_t offset;
};

void readNameRecord( NameRecord * nr, uint8_t * p)
{
    nr->platformID         = readBigEndianWord( p);
    nr->platformSpecificID = readBigEndianWord( p+2);
    nr->languageID         = readBigEndianWord( p+4);
    nr->nameID             = readBigEndianWord( p+6);
    nr->length             = readBigEndianWord( p+8);
    nr->offset             = readBigEndianWord( p+10);
}

int printEnglishName( const char* fontName)
{
    HFONT font = CreateFont(
        900, 0,
        0, 0,
        40,
        0,
        FALSE,
        FALSE,
        GB2312_CHARSET,
        OUT_DEFAULT_PRECIS,
        CLIP_DEFAULT_PRECIS,
        ANTIALIASED_QUALITY,
        DEFAULT_PITCH,
        fontName
    );

    HDC dc = ::CreateCompatibleDC(NULL);
    SelectObject(dc, font);


    const DWORD cMaxNameTableSize = 1024 * 1024;
    DWORD bufferSize = GetFontData(dc, 'eman', 0, NULL, 0); // "name" backwards
    if (bufferSize == 0 || bufferSize == GDI_ERROR || bufferSize > cMaxNameTableSize)
        return 1;
  
    BYTE* buffer = ( BYTE*) malloc(sizeof (BYTE )*bufferSize);
    if (GetFontData(dc, 'eman', 0, buffer, bufferSize) == GDI_ERROR)
        return 2;

    assert(bufferSize >= 6);

    uint16_t format = readBigEndianWord(buffer);
    uint16_t count = readBigEndianWord(buffer+2);
    uint16_t stringOffset = readBigEndianWord(buffer+4);
    assert(bufferSize >= stringOffset);

    BYTE* strings = buffer + stringOffset;
    NameRecord *nameRecords = (NameRecord*)malloc( sizeof( NameRecord)*count);

    UINT offset = 6;
    for ( int i = 0; i < count; i++) {
        assert(bufferSize >= offset + 12);
          
        NameRecord *nr = &nameRecords[i];
        readNameRecord(nr, buffer + offset);
        offset += 12;

        // platformID, 3 == Microsoft
        // platformSpecificID, encoding
        // languageID, 0x409(1033) == English
        // nameID, 1 == font family
        //         6 == PostScript name of the font
        if (nr->platformID == 1 && nr->platformSpecificID == 0 && nr->languageID == 0 && nr->nameID == 6)
        {
            char *name = ( char*) malloc(nr->length+1);
            memcpy(name, strings + nr->offset, nr->length);
            name[nr->length] = '\0';
            printf( "font: %s, mac, name: %s\n" , fontName, name);
            free(name);
        }
        else if (nr->platformID == 3 && nr->platformSpecificID == 1 && nr->languageID == 0x409 && nr->nameID == 1)
        {
            wchar_t *name = ( wchar_t*) malloc(nr->length+2);
            for ( int i = 0; i < nr->length; i+=2)
            {
                name[i/2] = (strings[nr->offset+i] << 8) + strings[nr->offset+i+1];
            }
            name[nr->length/2] = 0;

            char *nameAscii = (char*)malloc(nr->length/2+1);
            WideCharToMultiByte( CP_ACP, 0, name, -1, nameAscii, nr->length/2+1, NULL , NULL );
            nameAscii[nr->length/2] = '\0';

            std::cout << "font: " << fontName << ", windows, name: " << nameAscii << std::endl;

            free(nameAscii);
            free(name);
        }
    }

    free(nameRecords);
    free(buffer);


    DeleteDC(dc);
    DeleteObject(font);
    return 0;
}

int main()
{
    const char *fontNames[] = { "等线" , "等线 Light" , "仿宋" , "楷体" , "微软雅黑" , "微软雅黑 Light" , "宋体" , "黑体" , "新宋体" };
    for ( int i = 0; i < (sizeof(fontNames)/ sizeof(fontNames[0])); ++i)
    {
        printEnglishName(fontNames[i]);
    }
    return 0;
}
-----------------------------------
font: 等线, windows, name: DengXian
font: 等线 Light, windows, name: DengXian Light
font: 仿宋, mac, name: FangSong
font: 仿宋, windows, name: FangSong
font: 楷体, mac, name: KaiTi
font: 楷体, windows, name: KaiTi
font: 微软雅黑, windows, name: Microsoft YaHei
font: 微软雅黑 Light, windows, name: Microsoft YaHei Light
font: 宋体, mac, name: SimSun
font: 宋体, windows, name: SimSun
font: 黑体, windows, name: SimHei
font: 新宋体, mac, name: NSimSun
font: 新宋体, windows, name: NSimSun
-----------------------------------
这里有一个要注意的。win32 下要用 nameID == 1,这样获得的 English Name 才是与注册表中的是对应的。
nameID == 6,获得 "DengXian-Light",多了个 "-"
nameID == 1,获得 "DengXian Light"

参考seayoung的实现(实现有bug,因为win32 platform下,字体名是utf-16的,不能直接memcpy就完事了)
参考苹果的实现
关于 'name' table 的详细描述,看这里


获得 .ttc 路径
参考这里的代码即可,很详细。


  评论这张
 
阅读(1192)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017