BlenderCN论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 1388|回复: 6

从 TTF 文件检索字体名称(中文版)

[复制链接]
发表于 2014-6-17 19:41:32 | 显示全部楼层 |阅读模式
本帖最后由 云风如我 于 2014-6-17 19:58 编辑

一篇通过二进制方式,读取 TTF 获得字体名称的方法,VC 代码,谁熟悉 VC 的可以参考一下,将其改成大蟒的代码,也许能解决 Blender 读取字体,无法识别中文字体名的问题。

未命名.JPG

中文版:
http://www.orcode.com/article/GDI_20113180.html

中文版的可读性很差,我找到了英文原版,重新整理了一下中文版,方便阅读。

Environment: VC6, MFC, Win95/98/NT/2000/XP

简介

每个人都可以得到一个安装的字体的字体名称。但是如果字体仍然没有安装在系统中,你想知道那是什么,编程?
当然,你可以暂时把它添加到系统字体,并得到其属性,然后(恩. ..但你怎么会找到现在是安装的字体)。
好吧,也许你可以想想其他方法,但我决定去寻找规范的TrueType和OpenType字体文件。
幸运的是,微软已经对这些文件的很好的文章。如果你想知道更多关于他们,看这篇文章的末尾的链接。

编写代码

由于所有我感兴趣的(和你在大多数情况下)的只是字体名称和TTF文件没有其他属性,我们的代码是要去很简单(实际上只有一个函数)。从给定的文件检索功能将字体名称和返回到调用程序。

数据类型的定义

由于是在Windows头文件(或者我没有找到)中定义的无结构,我们该怎么做我们自己。我们需要 4 个结构和 2 个宏(我稍后会解释,他们)。

一个 TTF 文件的几个表组成,每个表代表的一些数据,关于它的类型。有些表是必需的,有些则不是。实际上,我们只需要其中之一,被称为"名",如名称表。这是其中的字体信息存储的地方,如字体名称,版权,商标和其他



  1. //这是 TTF 文件头
  2. typedef struct _tagTT_OFFSET_TABLE{
  3.   USHORT  uMajorVersion;
  4.   USHORT  uMinorVersion;
  5.   USHORT  uNumOfTables;
  6.   USHORT  uSearchRange;
  7.   USHORT  uEntrySelector;
  8.   USHORT  uRangeShift;
  9. }TT_OFFSET_TABLE;

  10. //在 TTF 文件及其位置和名称(标签)表
  11. typedef struct _tagTT_TABLE_DIRECTORY{
  12.   char  szTag[4];             //表的名称
  13.   ULONG uCheckSum;     //检查总和
  14.   ULONG uOffset;           //从文件开始偏移
  15.   ULONG uLength;          //表的长度,以字节为单位
  16. }TT_TABLE_DIRECTORY;

  17. //头名称表
  18. typedef struct _tagTT_NAME_TABLE_HEADER{
  19.   USHORT uFSelector;      //格式选择。始终为0
  20.   USHORT uNRCount;        //名称记录计数
  21.   USHORT uStorageOffset;  //字符串存储偏移,从
  22.                                       //从表的开始
  23. }TT_NAME_TABLE_HEADER;

  24. //名称表中的记录
  25. typedef struct _tagTT_NAME_RECORD{
  26.   USHORT uPlatformID;
  27.   USHORT uEncodingID;
  28.   USHORT uLanguageID;
  29.   USHORT uNameID;
  30.   USHORT uStringLength;
  31.   USHORT uStringOffset;    //开始从存储区域
  32. }TT_NAME_RECORD;</P>
复制代码


现在,唯一剩下的就是我之前谈到的宏。宏定义如下所示:
  1. #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
  2. #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)),SWAPWORD(LOWORD(x)))
复制代码

现在,那是什么?我们需要这些宏的原因是,TTF文件存储在 big-endian 格式,不像在 Windows 系统中,所有的文件都在 Little-Endian。
是的,我知道这听起来很愚蠢与所有这些“endians。”big-endian 是由摩托罗拉的处理器,例如,在高字节首先被存储;而在 Little-Endian(适用于Intel处理器),高字节是最后一个。

例如,你有一个整型变量1(也就是4个字节长)。尝试将其保存到一个文件中,并在任意一个十六进制编辑器打开它。您将看到:
  1. 01 00 00 00      //Little Endian - Intel
复制代码

这是 Little Endian 系统 (Intel). 但在 Big-Endian (Motorola), 数将被存储反之亦然,因为在这个例子中:

  1. 00 00 00 01      //Big Endian - Motorola
复制代码

因此,这些格式是不兼容的。而且 TTF 文件 正如我所说,存储在 Motorolla 风格(Big Endian)。这就是为什么我们需要这些宏来重新排列从 TrueType 字体文件检索的变量字节。

读文件

现在,我们准备读TTF文件。所以让我们开始。首先我们需要读取文件头 (TT_OFFSET_TABLE structure):

  1. CFile f;
  2. CString csRetVal;
  3. //lpszFilePath 我们的字体文件的路径
  4. if(f.Open(lpszFilePath, CFile::modeRead|CFile::shareDenyWrite)){
  5.   //定义和读取文件头
  6.   TT_OFFSET_TABLE ttOffsetTable;
  7.   f.Read(&ttOffsetTable, sizeof(TT_OFFSET_TABLE));
  8.   //记得要重新排列你要使用的字段中的字节
  9.   ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
  10.   ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
  11.   ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
  12.   //检查,这是一个TrueType 字体和版本是 1.0
  13.   if( ttOffsetTable.uMajorVersion != 1 ||
  14.       ttOffsetTable.uMinorVersion != 0)
  15.     return csRetVal;
复制代码

紧接着文件头的偏移表。在这里你可以发现一个有趣的偏移表,在我们的例子中“名”。

  1.   TT_TABLE_DIRECTORY tblDir;
  2.   BOOL bFound = FALSE;
  3.   CString csTemp;
  4.   for(int i=0; i< ttOffsetTable.uNumOfTables; i++){
  5.     f.Read(&tblDir, sizeof(TT_TABLE_DIRECTORY));
  6.     csTemp.Empty();
  7.     //表的标签不能超过 4 个字符
  8.     strncpy(csTemp.GetBuffer(4), tblDir.szTag, 4);
  9.     csTemp.ReleaseBuffer();
  10.     if(csTemp.CompareNoCase(_T("name")) == 0){
  11.       //我们发现我们的表。重新排列顺序,并退出循环
  12.       bFound = TRUE;
  13.       tblDir.uLength = SWAPLONG(tblDir.uLength);
  14.       tblDir.uOffset = SWAPLONG(tblDir.uOffset);
  15.       break;
  16.     }
  17.   }
复制代码

我们终于找到了名称表,让我们读它的头:

  1.   if(bFound){
  2.     //移动,以抵消我们从偏移表
  3.     f.Seek(tblDir.uOffset, CFile::begin);
  4.     TT_NAME_TABLE_HEADER ttNTHeader;
  5.     f.Read(&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER));
  6.     //再次,不要忘记交换字节!
  7.     ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
  8.     ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
  9.     TT_NAME_RECORD ttRecord;
  10.     bFound = FALSE;
复制代码

名称表头后,在它的记录。因此,我们需要通过运行的所有记录,找到有趣的信息 - 字体名称。

  1. for(int i=0; i<ttNTHeader.uNRCount; i++){
  2.   f.Read(&ttRecord, sizeof(TT_NAME_RECORD));
  3.   ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
  4.   // 1 表示,这是字体的名称。例如 0
  5.   // 决定版权信息
  6.   if(ttRecord.uNameID == 1){
  7.     ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
  8.     ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
  9.     //保存文件的位置,这样我们就可以返回继续搜索
  10.     int nPos = f.GetPosition();
  11.     f.Seek(tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset, CFile::begin);

  12.     //错误修正:看到SimonSays后读更多 TCHAR                 
  13.     TCHAR lpszNameBuf = csTemp.GetBuffer(ttRecord.uStringLength + 1);                     
  14.     ZeroMemory(lpszNameBuf, ttRecord.uStringLength + 1);

  15.     f.Read(csTemp.GetBuffer(ttRecord.uStringLength + 1),
  16.                             ttRecord.uStringLength);
  17.     csTemp.ReleaseBuffer();
  18.     //是的,仍然需要检查如果不是空的字体名称
  19.     //如果是,继续搜索
  20.     if(csTemp.GetLength() > 0){
  21.       csRetVal = csTemp;
  22.       break;
  23.     }
  24.     f.Seek(nPos, CFile::begin);
  25.   }
  26. }
复制代码

这就是全部!现在我们可以返回 csRetVal 包含我们的字体名称。

您可以下载完整的工作功能,并在代码中使用。我还包括一个具有相同功能的演示项目,但定制位,所以它返回的版权和商标信息。

如果你想继续与 TTF 文件,你可以看看他们在微软的规范。但记得更深你要特遣队,TrueType 和 OpenType之间的差异,您可能会发现更多的。不管怎样,下面是关于TTF的文章的链接。


参考

 楼主| 发表于 2014-6-17 19:42:05 | 显示全部楼层
本帖最后由 云风如我 于 2015-3-31 16:13 编辑

英文版本:
http://www.codeguru.com/cpp/g-m/gdi/fonthandlinganddetection/article.php/c3659/Retrieving-the-Font-Name-from-a-TTF-File.htm

代码与程序下载:
http://www.codeproject.com/Articles/2293/Retrieving-font-name-from-TTF-file

完整代码:



  1. typedef struct _tagTT_OFFSET_TABLE{
  2.         USHORT        uMajorVersion;
  3.         USHORT        uMinorVersion;
  4.         USHORT        uNumOfTables;
  5.         USHORT        uSearchRange;
  6.         USHORT        uEntrySelector;
  7.         USHORT        uRangeShift;
  8. }TT_OFFSET_TABLE;

  9. typedef struct _tagTT_TABLE_DIRECTORY{
  10.         char        szTag[4];                        //table name
  11.         ULONG        uCheckSum;                        //Check sum
  12.         ULONG        uOffset;                        //Offset from beginning of file
  13.         ULONG        uLength;                        //length of the table in bytes
  14. }TT_TABLE_DIRECTORY;

  15. typedef struct _tagTT_NAME_TABLE_HEADER{
  16.         USHORT        uFSelector;                        //format selector. Always 0
  17.         USHORT        uNRCount;                        //Name Records count
  18.         USHORT        uStorageOffset;                //Offset for strings storage, from start of the table
  19. }TT_NAME_TABLE_HEADER;

  20. typedef struct _tagTT_NAME_RECORD{
  21.         USHORT        uPlatformID;
  22.         USHORT        uEncodingID;
  23.         USHORT        uLanguageID;
  24.         USHORT        uNameID;
  25.         USHORT        uStringLength;
  26.         USHORT        uStringOffset;        //from start of storage area
  27. }TT_NAME_RECORD;

  28. #define SWAPWORD(x)                MAKEWORD(HIBYTE(x), LOBYTE(x))
  29. #define SWAPLONG(x)                MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))


  30. CString GetFontNameFromFile(LPCTSTR lpszFilePath)
  31. {
  32.         CFile f;
  33.         CString csRetVal;

  34.         if(f.Open(lpszFilePath, CFile::modeRead|CFile::shareDenyWrite)){
  35.                 TT_OFFSET_TABLE ttOffsetTable;
  36.                 f.Read(&ttOffsetTable, sizeof(TT_OFFSET_TABLE));
  37.                 ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
  38.                 ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
  39.                 ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);

  40.                 //check is this is a true type font and the version is 1.0
  41.                 if(ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0)
  42.                         return csRetVal;
  43.                
  44.                 TT_TABLE_DIRECTORY tblDir;
  45.                 BOOL bFound = FALSE;
  46.                 CString csTemp;
  47.                
  48.                 for(int i=0; i< ttOffsetTable.uNumOfTables; i++){
  49.                         f.Read(&tblDir, sizeof(TT_TABLE_DIRECTORY));
  50.                         strncpy(csTemp.GetBuffer(5), tblDir.szTag, 4);
  51.                         csTemp.ReleaseBuffer(4);
  52.                         if(csTemp.CompareNoCase(_T("name")) == 0){
  53.                                 bFound = TRUE;
  54.                                 tblDir.uLength = SWAPLONG(tblDir.uLength);
  55.                                 tblDir.uOffset = SWAPLONG(tblDir.uOffset);
  56.                                 break;
  57.                         }
  58.                 }
  59.                
  60.                 if(bFound){
  61.                         f.Seek(tblDir.uOffset, CFile::begin);
  62.                         TT_NAME_TABLE_HEADER ttNTHeader;
  63.                         f.Read(&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER));
  64.                         ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
  65.                         ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
  66.                         TT_NAME_RECORD ttRecord;
  67.                         bFound = FALSE;
  68.                         
  69.                         for(int i=0; i<ttNTHeader.uNRCount; i++){
  70.                                 f.Read(&ttRecord, sizeof(TT_NAME_RECORD));
  71.                                 ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
  72.                                 if(ttRecord.uNameID == 1){
  73.                                         ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
  74.                                         ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
  75.                                         int nPos = f.GetPosition();
  76.                                         f.Seek(tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset, CFile::begin);

  77.                                         //bug fix: see the post by SimonSays to read more about it
  78.                                         TCHAR lpszNameBuf = csTemp.GetBuffer(ttRecord.uStringLength + 1);
  79.                                         ZeroMemory(lpszNameBuf, ttRecord.uStringLength + 1);
  80.                                         f.Read(lpszNameBuf, ttRecord.uStringLength);
  81.                                         csTemp.ReleaseBuffer();
  82.                                         if(csTemp.GetLength() > 0){
  83.                                                 csRetVal = csTemp;
  84.                                                 break;
  85.                                         }
  86.                                         f.Seek(nPos, CFile::begin);
  87.                                 }
  88.                         }                        
  89.                 }
  90.                 f.Close();
  91.         }
  92.         return csRetVal;
  93. }
复制代码



回复 支持 反对

使用道具 举报

发表于 2014-6-17 21:21:16 | 显示全部楼层
python可能无法改变这个列表里显示的,我一直是假设能否是一个ps字体选择的下拉序列。

点评

你是指图中右下角的字形显示么? 这个倒还是小问题,关键是文件列表中的字体,无法正常的显示文件名。 我觉得应该分两步走,首先,应该能识别出中文字体的名称。从技术上来说,应该不成问题。 然后,由于 Blender  详情 回复 发表于 2014-6-17 21:45
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-6-17 21:45:06 | 显示全部楼层
本帖最后由 云风如我 于 2014-6-17 22:02 编辑
nirenyang 发表于 2014-6-17 21:21
python可能无法改变这个列表里显示的,我一直是假设能否是一个ps字体选择的下拉序列。 ...


你是指图中右下角的字形显示么?

这个倒还是小问题,关键是文件列表打开框中的字体文件,无法正常的显示文件名。

我觉得应该分两步走,首先,能识别出中文字体的名称。从技术上来说,应该没问题。

然后,由于 Blender 访问字体,是通过绝对路径访问字体文件名的(其实所有程序都一样),看看能否将 Blender 的打开字体框(图中的大列表),改造成一个“详细资料”列表,就像在资源管理器中,访问 C:\WINDOWS\Fonts 一样。

这样字体能够正常显示名称,而双击选中文件时,又能正常访问其文件名。

23.JPG

至于 PS 的那种列表框,其实也是一样的道理。也就是说程序必须能将“字体名”与“字体文件名”对应起来。

Adobe 的解决办法就是用一个 AdobeFnt.let 文件,记录对应关系。

目前 Blender 能识别字体名,但无法识别中文字体名,像我电脑里的微软雅黑,就识别成了 MicrosoftYaHeiBold 。

点评

最好是不管字体在哪,直接能选。最好。[attachimg]4781[/attachimg]  详情 回复 发表于 2014-6-17 22:02
回复 支持 反对

使用道具 举报

发表于 2014-6-17 22:02:13 | 显示全部楼层
云风如我 发表于 2014-6-17 21:45
你是指图中右下角的字形显示么?

这个倒还是小问题,关键是文件列表打开框中的字体文件,无法正常的显示 ...

最好是不管字体在哪,直接能选。最好。

Snap1.jpg

点评

记事本能通过访问【字体名】,完成对【字体文件名】的访问,好像是通过注册表完成对应关系的转换的…… 理论上 Blender 应该也是能设计成这样的,这像这项。左边是字体名,右边是字体文件名,这样访问起来就没问题  详情 回复 发表于 2014-6-17 22:10
回复 支持 反对

使用道具 举报

 楼主| 发表于 2014-6-17 22:10:20 | 显示全部楼层
本帖最后由 云风如我 于 2014-6-17 22:19 编辑
nirenyang 发表于 2014-6-17 22:02
最好是不管字体在哪,直接能选。最好。


记事本能通过访问【字体名】,完成对【字体文件名】的访问,好像是通过注册表完成对应关系的转换的……

理论上 Blender 应该也是能设计成这样的,类似这项。左边显示字体名,右边显示字体文件名,这样访问起来就没问题了。

2323.JPG

但也必须要和 PS 一样,建立索引数据(AdobeFnt.let),完成对应关系才行,不然每次都得去扫描字体。

而且很可能也会有和 PS 一样的缺点,系统目录中字体很多的话,第一次建立启动索引(扫描所有字体)时,会减慢软件的启动速度。

不过要实现这个功能,也还是得先想办法解决,对中文字体名的识别问题……


点评

也许这个项目会对这个需求有帮助 http://wiki.blender.org/index.php/Dev:Ref/Proposals/UI/Asset_Manager  详情 回复 发表于 2014-6-17 23:14
回复 支持 反对

使用道具 举报

发表于 2014-6-17 23:14:23 | 显示全部楼层
本帖最后由 nirenyang 于 2014-6-17 23:18 编辑

云风如我 发表于 2014-6-17 22:10
记事本能通过访问【字体名】,完成对【字体文件名】的访问,好像是通过注册表完成对应关系的转换的……

...

也许等这个项目出来再看比较好,我前些时间看周会上有说到
http://wiki.blender.org/index.ph ... ls/UI/Asset_Manager

其他:
http://wiki.blender.org/index.php/User:Elubie/AssetBrowser


回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

Blender最新中文教学视频|Blender头条|小黑屋|手机版|Archiver|Blender中国 ( 蜀ICP备17002929号 )360网站安全检测平台

GMT+8, 2019-7-16 17:15 , Processed in 0.026461 second(s), 21 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表