[转]TrueType字体结构

TrueType字体一般包含在单个TrueType字体文件中,其文件后缀为.TTF。算法

OpenType字体是以相似于TrueType字体的格式编码的POSTSCRIPT字体。OPENTYPE字体使用.OTF文件后缀。OPENTYPE还容许把多个OPENTYPE字体组合在一个文件中以利于数据共享。这些字体被称为TrueType字体集(TrueType collection),其文件后缀为.TTC。
        TrueType字体用machintosh的轮廓字体资源的格式编码,有一个惟一的标记名"sfnt"。windows没有macintosh的位图字体资源格式,字体目录包含了字体格式的版本号和几个表,每一个表都有一个tableentry结构项,tableentry结构包含了资源标记、校验和、偏移量和每一个表的大小。下面是TrueType字体目录的c语言定义:windows

typedef sturct
{
    char tag[4];
    ULONG checkSum;
    ULONG offset;
    ULONG length;
}TableEntry;
 
typedef struct
{
    Fixed sfntversion; //0x10000 for version 1.0
    USHORT numTables;
    USHORT searchRange;
    USHORT entrySelector;
    USHORT rangeShift;
    TableEntry entries[1];//variable number of TableEntry

}TableDirectory;

TableDirectory结构的最后一个字段是可变长度的tableentry结构的数组,安体中的每一个表对应其中一项。TrueType字体中的每一个表都保存了不一样的逻辑信息-----如图元中数据、字符到图元的映射、字距调整信息等等。有表是必须的,有些是可选的。数组

        下表列出了TrueType字体中常见的表。服务器

head 字体头 字体的全局信息
cmap 字符代码到图元的映射 把字符代码映射为图元索引
glyf 图元数据 图元轮廓定义以及网格调整指令
maxp 最大需求表 字体中所需内存分配状况的汇总数据
mmtx 水平规格 图元水平规格
loca 位置表索引 把元索引转换为图元的位置
name 命名表 版权说明、字体名、字体族名、风格名等等
hmtx 水平布局 字体水平布局星系:上高、下高、行间距、最大前进宽度、最小左支撑、最小右支撑
kerm 字距调整表 字距调整对的数组
post

PostScript信息 全部图元的PostScript FontInfo目录项和PostScript名网络

PCLT PCL 5数据 HP PCL 5Printer Language 的字体信息:字体数、宽度、x高度、风格、记号集等等
OS/2 OS/2和Windows特有的规格 TrueType字体所需的规格集

        在TableDirectory结构中,全部的TableEntry结构都必须根据它们的标记名排序。好比,cmap必须出如今head前,而head必须在glyf前。可是实际的表能够出如今TrueType字体文件中的任意位置。
        Win32API 提供了一个应用程序可用于查询原始TrueType字体信息的函数:
        DWORD GetFontData(HDC hDC,DWORD dwTable ,DWORD dwOffset, LPVOID lpbBuffer ,DWORD cbData);
       GetFontData 函数能够用于查询设备上下文中当前逻辑字体所对应的TrueType字体,所以传递的不是逻辑字体句柄,而是设备上下文句柄。你能够查询整个TrueType文件基是文件中的一个表。要查询整个文件的话dwTable参数应该为0;不然,应该传递要查询的表的四字符标记的DWORD格式。参数dwOffset是要查询的表中的起始偏移,要查询整个表的话应该为0;参数 lpbBuffer是缓冲区的地址,cbData是缓冲区的大小。若是最后个参数为NULL和 0,GetFontData函数返回字体文件或表的大小;就会把到的数据拷贝到应用程序所提供的缓冲区中。
        下面的例和查询整个TrueType字体的原始数据:数据结构

  

 1 TableDirctory * GetTrueTypeFont (HDC hDC ,DWORD &nFontSize)
 2 {
 3     //query font size
 4     nFontSize=GetFontData(hDC,0,0,NULL,0);
 5  
 6     TableDirectory * pFont =(TableDirectory *)new BYTE(nFontSize);
 7     if (pFont==NULL)
 8         return NULL;
 9     GetFontData(hDC,0,0,pFont,nFontSize);
10  
11     return pFont;
12 }

GetFontData使得应用程序可以在本身的文档中内嵌TrueType字体,以确保这些文档能在没有相应字体的其余机器上显示。它的作法是容许应用程序查询字体数据,而后写入到文档中做为文档的一部分,在文档被打于时再安装该字体以确保文档能以建立时一样的方式显示。好比,Windows NT/2000的假脱机程序在打印到远端服务器时会在假脱机文件中内嵌入TrueType字体以保证文档能在另外一台机器上正确地打印。
        一旦接受到TrueType字体的原始数据,它的头中的TableDirectory结构很容易分析。须要检查的只有版本号和表的数目,而后就能够检查单个的表。咱们来看一些重要的和有趣的表。less


1.字体头
        字体头表(head表)中包含了TrueType字体的全局信息。下面是字体头表的结构。编辑器

typedef sturct
{
    Fixed Table;//x00010000 ro version 1.0
    Fixed fontRevision;//Set by font manufacturer.
    ULONG checkSumAdjustment;
    ULONG magicNumer; //Set to 0x5f0f3cf5
    USHORT flags;
    USHORT unitsPerEm; //Valid range is from 16 to 16384
    longDT created; //International date (8-byte field).
    longDT modified; //International date (8-byte field).
    FWord xMin; //For all glyph bounding boxes.
    FWord yMin; //For all glyph bounding boxes.
    FWord xMax; //For all glyph bounding boxes.
    FWord xMax; //For all glyph bounding boxes.
    USHORT macStyle;
    USHORT lowestRecPPEM; //Smallest readable size in pixels.
    SHORT fontDirctionHint;
    SHORT indexToLocFormat; //0 for short offsets ,1 for long.
    SHORT glyphDataFormat; //0 for current format.
}Table_head;

  字体的历史记录在三个字段中:字全版本号、字体最初建立时间和字体最后修改时间。有8 个字节用于记录时间戳,记录的是从1904年1月1日午夜12:00开始的秒数,所以咱们不用担忧y2k问题,或是什么y2m问题。
        字体设计时是针对一个参考网格设计的,该网格被称为em-square,字体中的图元用网格中的坐标表示。所以em-squrare的大小决定胃该字体的图元被缩放的方式,同时也反映胃该字体的质量。字体头中保存了每一个em-square的格数和能包含全部图元的边界框。Em-square的有效值是从16到16384,常见的值是204八、4096和8192。好比,Windings字体的em-square的格数是2048,图元的边界框是[0,-432,2783,1841]。
        字体头表中的其余信息包括最小可读像素大小、字体方向、在位置表中图元索引的格式和图元数据格式等等。
最大需求表
        TrueType字体是一种很是灵活的数据结构,它能够包含可变数目的图元,每一个图元能够有不一样数目的控制点,甚至还能够有数量可变的图元指令。最大需求表的目的是告知字体栅格器(rasterizer)对内存的需求,以便在出来字体前分配合适大小的内存。由于性能对字体栅格器很是重要,像MFC的CAarray那样须要频繁进行数据拷贝操做的动态增加的数据结构不合要求。下面是maxp表的结构。函数

typedef struct
{
    Fixed Version;//0x00010000 for version 1.0.
    USHORT numGlypha; //Number of glyphs in the font .
    USHORT maxPoints; //Max points in noncomposite glyph .
    RSHORT maxContours; //Max contours in noncomposite glyph.
    USHORT maxCompositePoints;//Max points in a composite glyph.
    USHORT maxCompositeContours; //Max contours in a composite glyph.
    USHORT maxZones;// 1 if not use the twilight zone [Z0],//or 2 if so use Z0;2 in most cases.
    USHORT max TwilightPoints ;/ Maximum points used in Z0.
    USHORT maxStorage; //Number of storage area locations.
    USHORT maxFunctionDefs; //Number of FDEFs.
    USHORT maxStackElements; //Number of depth.
    USHORT maxSizeOfInstructions; //Max byte count for glyph inst.
    USHORT maxComponentElements; //Max number top components refernced.
    USHORT maxComponentDepth; //Max levels of recursion.
}Table_maxp;

numGlyphs字段保存了字体中图元的总数,这决定了到位置表的图元索引的数量,能够用于验证图元索引的有效性。TrueType字体中的每一个图元均可以是合成图元或简单图元。简单图元能够有一条或多条轮廓,套用一些控制点定义。合成图元用几个其余图元的组合来定义。maxPoints\maxCountors\maxCompositePoints maxCompositeContours这几个字段说明了图元定义的复杂度。
        除了图元的定义,TrueType字体还使用了图元指令用于提示字体扫描器如何对控制点进行调整以获得更均衡更漂亮的光栅化后的图元。图元指令也能够出如今字体程序表(fpgm表)以及控制值程序表(“prep”)的全局字体层中。TrueType图元指令是一个伪计算机字节指令,该机相似于Java的虚拟机,这些指令能够用堆栈计算机执行。MaxStackElements maxSizeOfInstructions两个字段同志堆栈计算机这些指令的复杂度。
        以Windings字体为例,该字体有226个图元,图元最多有47条轮廓线,简单图元最多有268个点,合成图元最多有141个点,合成图元最多有14条轮廓线,最坏状况下须要492层堆栈,最长的指令有1119个字节。
        字符到图元索引的映射表(cmap表)定义了从不一样代码页中的字符代码到图元索引的映射关系,这是在TrueType字体中存取图元信息的关键。cmap表包含几个了表以支持不一样的平台和不一样的字符编码方案。布局

cmap表

  

typedef struct
{
    USHORT Platform;   //platform ID
    USHORT EncodingID; //encoding ID
    ULONG TableOffset; //offset to encoding table ?
 
typedef struct {
    WCHAR wcLow;
    USHORT cGlyphs;
}
 
typedef struct
{
    DWORD cbThis; //sizeof (GLYPHSET)+sizeof(WCRANGE)+(cRanges-1)
    DWORD flAccel;
    DWORD cGlyphsSupported;
    DWORD cRanges;
    WCRANGE ranges[1]; //ranges[cRanges]
}GLYPHSET;
 
DWORD GetFontUnicodeRanges(HDC hDC,LPGLYPHSET lpgs);
DWORD GetGlyphIndices(HDC hDC,LPCTSTR lpstr,int c ,LPWORD pgi,DWORD fl);

 一般一种字体只提供UNICODE字符集中的字符的一个子集。这些字符能够被分组为多个区域,cmap映射表中就是这么作的。GetFontUnicodeRanges函数在一个GLYPHSET结构中返回支持的图元的数量、支持的UNICODE区域的数量以及设备上下文中字体的这些区域的详细信息。GLYPHSET是一个可变长的结构,其大小取决于所支持的UNICODE区域的数量。所以,和Win32 API中支持可变长结构同样, GetFontUnicodeRanges函数一般须要调用两次。第一次调用时获得以NULL指针做为最后一莜参数,GDI会返回所需窨的大小。调用者而后分配所需的内存,再次调用以获得真正的数据。这两种状况下,GetFontUnicodeRanges函数都会返回保存整个结构所需的数据大小。MSDN文档可能仍是错误地描述成了若是第二个参数是NULL,GetFontUnicodeRanges函数返回指向GLYPHSET结构的指针。
下面是用于查询上下文中当前字体GLYPHSET结构的一个简单函数。

 1 GLYPHSET *QueryUnicodeRanges(HDC hDC)
 2 {
 3     //query for size
 4     DWORD size=GetFontUnicodeRanges(hDC,NULL);
 5  
 6     if (size==0) return NULL;
 7     GLYPHSET *pGlyphSet=(GLYPHSET *)new BYTE(size);
 8  
 9     //get real data
10     pGlyphSet->cbThis=size;
11     size=GetFontUnicodeRanges(hDC,pGlyphSet);
12  
13     return pGlyphSet;
14 }

若是在一些Windows TrueType字体上试着调用GetFontUnicodeRanges函数,你会发现这些字体一般支持1000个以上的图元,这些图元被分红几百个UNICODE区域。好比,“Times New Roman”有我143个图元,分布在145个区域中,和一个区域是0x20到0x7f,便可打印的7位ASCII代码区域。
        GetFontUnicodeRanges函数只使用了TrueType字体“cmap”表的一部分部分信息,即从UNICODE到图元索引的映射域。GetGlyphIndices函数则能真正使用这些映射关系把一个字符串转换为一个图元索引的数组。它接收一个设备上下文句柄、一个字符串指针、字符串长度、一个WORD数组的指针和一个标志。生成的图元索引将保存在WORD数组中。若是标志为GGI_MASK_NONEXISTING_GLYPHS,找不到的字符的图元索引会被标注成0xFFFF。此函数获得的图元索引能够传给其余GDI函数,如ExtTextOut函数。

2.位置索引

        TrueType字体中最有用的信息是glyf表中的图元数据。有了图元索引,要找到相应的图元,须要表(loca表)索引以把图元索引转换为图元数据表内的偏移量。
位置索引表中保存了n+1个图元数据表的索引,其中n是保存在最大需求表中的图元数量。最后一个额外的偏移量并不指向一个新图元,而是指向最后一个图元的偏移量和当前图元的偏移量和当前图元的偏移量间的差值获得图元的长度。
        位置索引表中的每个索引以无符号短整数对齐的,若是使用了短整数格式,索引表实际存储的是WORD偏移量,而不是BYTE偏移量。这合得短整数格式的位置索引表能支持128KB大小的图元数据表。

3.图元数据
        图元数据(glyf表)是TrueType字体的核心信息,所以一般它是最大的表。由于的位置索引是一张单独的表,图元数据表就彻底只是图元的序列而已,每一个图元以图元头结构开始:

1 typedef struct
2 {
3     WORD numberOfContours; //contor number,negative if composite
4     FWord xMin; //Minimum x for coordinate data.
5     FWord yMin; //Minimum y for coordinate data.
6     FWord xMax; //Maximum x for coordinate data.
7     FWord yMax; //Maximum y for coordinate data.
8 }GlyphHeader;

对于简单图元,numberOfContours字段中保存的是当前图元的轮廓线的数量;对于合成图元,numberOfContours字段是一个负值。后者的轮廓线的总数必须基于组成该合成图元的全部图元的数据计算获得。GlyphHeader结构中后四个字段记录了图元的边界框。
        对于简单图元,图元的描述紧跟在 GlyphHeader 结构以后。图元的描述由几部分信息组成:全部轮廓线结束点的索引、图元指令和一系列的控制点。每一个控制点包括一个标志以 x 和 y 坐标。概念上而言,控制所需的信息和 GDI 函数 PolyDraw 函数所需的信息相同:一组标志和一组点的坐标。但 TrueType 字体中的控制点的编码要复杂得多。下面是图元描述信息的概述:

USHORT endPtsOfContours[n]; //n=number of contours
USHORT instructionlength;
BYTE instruction[i]; //i = instruction length
BYTE flags[]; //variable size
BYTE xCoordinates[]; //variable size
BYTE yCoordinates[]; //variable size

 图元能够包含一条或多条轮廓线。好比,字母"O"有两条轮廓线,一条是内部的轮廓,另外一条是外部的轮廓。对于每一条轮廓线,endPtsOfContours数组保存了其终点的索引,从该索引中能够计算出轮廓线中点的数量。好比,endPtsOfContours[0]是第一休轮廓线上点的数量,endPtsOfContours[1] - endPtsOfContours[0] 是第二条轮廓线上点的数量。
        终点数组后是图元指令通知度和图元指令数组。咱们先跳过它们,先来讨论控制点。图元的控制点保存在三个数组中:标志符数组、x 坐标数组和 y 坐标数组。找到标志数组的起始点很简单,可是标志数组没有相应的长度字,也没有直接其余两个数组的方法,你必须先解码标志符数组才能解释 x 和 y 坐标数组。
        咱们提到em-square被限制为最大为16384个网格,所以一般状况下须要各两个字节来表示 x 坐标和 y 坐标。为了节省空间,图元中保存的是相对坐标。第一个点的坐标是相对(0,0)记录的,全部随后的点记录者是和上一个点的坐标差值。有些差值能够用一个字节表示,有些差值为0,另一些差值则没法用单个字节表示。标志符数组保存了每一个坐标的编码信息以及其余一些信息。下面是标志中各个位的含义的总结:

typedef enum
{
    G_ONCURVE = 0x01,  // on curve ,off curve
    G_REPEAT = 0x08,   //next byte is flag repeat count
    G_XMASK = 0x12,
    G_XADDBYTE = 0x12, //X is positive byte
    G_XSUBBYTE = 0x12, //X is negative byte
    G_XSAME = 0x10,    //X is same
    G_XADDINT = 0x00,  //X is signed word
    G_YMASK = 0x24,
    G_YADDBYTE = 0x24, //Y is positive byte
    G_YSUBBYTE = 0x04, //Y is negative byte
    G_YSAME = 0x20,    //Y is same
    G_YADDINT = 0x00,  //Y is signed word
};

在第8章中咱们讨论了直线和曲线,咱们提到了一段三阶Bezier曲线有四个控制点定义:位于曲线上(on-curve)的起始点、两个不在曲线上(off-curve)的控制点和一个曲线上的结束点。TureType字体中的图元轮廓是用二阶Bezier曲线定义的,有三个点:一个曲线上的点,一个曲线外的点和另外一个曲线上的点。多个连续的不在曲线上的点是容许的,但不是用来定义三阶或更高阶的Bezier曲线,而是为了减小控制点的数目。好比,对于on-off-off-on模式的四个点,会加入一个隐含的点使之成为on-off-on-off-on,所以定义的是两段二阶Bezier曲线。
        若是设置了G_ONCURVE位,那么控制点在曲线上,不然不在曲线上。

        若是设置了G_REPEAT,标志数组中的下一字节表示重复次数,当前标志应该重复指定的次数。所以,标志数组中实际使用了某种类型的行程编码。

        标志中的其余位用于描述相应的x坐标和y坐标的编码方式,它们能够表示当前相寻坐标是否和上一个相同、正的单字节值、负的单字节值或有符号两字节值。
        解码图元的描述是一个两次扫描的起始点。而后再遍历图元定义中的每个点把它转换为更容易管理的格式。程序清单14-2列出了解码TrueType图元的函数,它是KTrueType类的一个方法。

 1 int KTrueType::DecodeGlyph(int index, KCurve& curve, XFORM* xm) const
 2 {
 3     const GlyphHeader* pHeader = GetGlyph(index);
 4     if (pHeader == NULL)
 5     {
 6         // assert(false);
 7         return 0;
 8     }
 9  
10     int nContour = (short)reverse(pHeader->numberOfContours);
11     if (nContour < 0)
12     {
13         return DecodeCompositeGlyph(pHeader+1, curve); // after the header
14     }
15  
16     if (nContour == 0)
17         return 0;
18  
19     curve.SetBound(reverse((WORD)pHeader->xMin), reverse((WORD)pHeader->yMin),
20         reverse((WORD)pHeader->xMax), reverse((WORD)pHeader->yMax));
21  
22     const USHORT* pEndPoint = (const USHORT *) (pHeader+1);
23  
24     int nPoints = reverse(pEndPoint[nContour-1]) + 1; // endpoint of last contour + 1
25     int nInst = reverse(pEndPoint[nContour]); // instructon length
26  
27     curve.m_glyphindex = index;
28     curve.m_glyphsize = (int) GetGlyph(index+1) - (int) GetGlyph(index);
29     curve.m_Ascender = m_Ascender;
30     curve.m_Descender = m_Descender;
31     curve.m_LineGap = m_LineGap;
32  
33     GetMetrics(index, curve.m_advancewidth, curve.m_lsb);
34  
35     if ( curve.m_glyphsize==0 )
36         return 0;
37  
38     curve.m_instrsize = nInst;
39  
40     const BYTE * pFlag = (const BYTE *) & pEndPoint[nContour] + 2 + nInst; // first byte in flag
41     const BYTE * pX = pFlag;
42  
43     int xlen = 0;
44  
45     for (int i=0; i<nPoints; i++, pX++)
46     {
47         int unit = 0;
48  
49         switch ( pX[0] & G_XMASK )
50         {
51         case G_XADDBYTE:
52         case G_XSUBBYTE:
53             unit = 1;
54             break;
55  
56         case G_XADDINT:
57             unit = 2;
58         }
59  
60         if ( pX[0] & G_REPEAT )
61         {
62             xlen += unit * (pX[1]+1);
63  
64             i += pX[1];
65             pX ++;
66         }
67         else
68             xlen += unit;
69     }
70  
71     const BYTE * pY = pX + xlen;
72  
73     int x = 0;
74 }

 KTrueType类处理TrueType字体的加载和解码,随书光盘中有它的完整源代码。DecodeGlyph给出图元索引和可选的变换矩阵,处理的是单个图元的解码。参数curve是KCurve类,用于把TrueType图元定义保存为32位的点的数组以及一个标志数组,用GDI进行显示。这些代码能够做为简单TrueType字体编辑器的基础。
        代码中调用了GetGlyph方法,该方法用位置表索引找到该图元的GlyphHeader结构。从中获得图元的轮廓线数目。注意必须反转该值的字节序,由于TrueType字体用的是Big-Endian字节序。若是该值为负值,说明这是一个合成图元,应该转而调用DecodeCompositeGlyph方法。接下支的代码定位了endPtsOfContours数组,找出点的总数,而后跳过指令找到标志数组的起始位置。
        接下去须要讲到的是x坐标数组的始位置和长度,这须要遍历标志数组一次。对于每个控制点,它在x坐标数组中所占空间可能为0到2个字节,这取决于它的相对坐标是0、单个字节仍是两个字节。
        根据x坐标数组的地址和长度能够获得y坐标的地址。接下去的代码遍历全部的轮廓线,解码其中的控制点,把相对坐标转换为绝对坐标,而后把它加入到曲线对象中。若是须要的话,会对每一个控制点作变换。
        回想一下,TrueType使用的是二阶Bezier曲线,容许在两个曲线上的点之间有多个不在曲线上的点。为了简化曲线绘制算法,KCurve::Add方法在每两个不在曲线上的点之间加入一个额外的在曲线上的点。

        处理了简单图元以后,咱们来看看合成图元。合成图元用一个经变换的图元序列定义。每一个经变换的图元的定义包括三个部分:一个标志、一个图元索引和一个变换矩阵。标志字段决定了变换矩阵的编码方式。编码的目的也是为了节省一些空间,加外还说明了是否已到达序列的终点。一个完整的2D affine变换须要6个值。但若是只是平移的话,只须要两个值(dx, dy),这两个值能够保存为两个字节或两个字。若是 x 和 y 以相同的值缩放,此外还须要一个缩放值。取通常的状况下仍然须要6个值,可是不少时候能够节省几个字节。用于变换的值以2.14的有符号定点格式保存,dx 和 dy 值除外,这两个值以整数形式保存。获得合成图元的过程其实是变换和组合几个图元的过程。好比,若是字体中的一个图元是另外一个图元的精确镜像,它只需定义为一个合成图元,能够经过对另外一个图像作镜像变换便可。程序清单14-3列出了解码合成图元的代码。

 1 int KTrueType::DecodeCompositeGlyph(const void* pGlyph, KCurve& curve) const
 2 {
 3     KDataStream str(pGlyph);
 4     unsigned flags;
 5     int len = 0;
 6  
 7     do
 8     {
 9         flags = str.GetWord();
10  
11         unsigned glyphIndex = str.GetWord();
12  
13         // Argument1 and argument2 can be either x and y offsets to be added to the glyph or two point numbers.
14         // In the latter case, the first point number indicates the point that is to be matched to the new glyph.
15         // The second number indicates the new glyph's "matched" point. Once a glyph is added, its point numbers
16         // begin directly after the last glyphs (endpoint of first glyph + 1).
17  
18         // When arguments 1 and 2 are an x and a y offset instead of points and the bit ROUND_XY_TO_GRID is set to 1,
19         // the values are rounded to those of the closest grid lines before they are added to the glyph.
20         // X and Y offsets are described in FUnits.
21  
22         signed short argument1;
23         signed short argument2;
24  
25         if (flags & ARG_1_AND_2_ARE_WORDS)
26         {
27             argument1 = str.GetWord(); // (SHORT or FWord) argument1;
28             argument2 = str.GetWord(); // (SHORT or FWord) argument2;
29         }
30         else
31         {
32             argument1 = (signed char) str.GetByte();
33             argument2 = (signed char) str.GetByte();
34         }
35  
36         signed short xscale, yscale, scale01, scale10;
37  
38         xscale = 1;
39         yscale = 1;
40         scale01 = 0;
41         scale10 = 0;
42  
43         if ( flags & WE_HAVE_A_SCALE )
44         {
45             xscale = str.GetWord();
46             yscale = xscale; // Format 2.14
47         }
48         else if (flags & WE_HAVE_AN_X_AND_Y_SCALE)
49         {
50             xscale = str.GetWord();
51             yscale = str.GetWord();
52         }
53         else if (flags & WE_HAVE_A_TWO_BY_TWO)
54         {
55             xscale = str.GetWord();
56             scale01 = str.GetWord();
57             scale10 = str.GetWord();
58             yscale = str.GetWord();
59         }
60  
61         if (flags & ARGS_ARE_XY_VALUES)
62         {
63             XFORM xm;
64  
65             xm.eDx = (float) argument1;
66             xm.eDy = (float) argument2;
67             xm.eM11 = xscale / (float) 16384.0;
68             xm.eM12 = scale01 / (float) 16384.0;
69             xm.eM21 = scale10 / (float) 16384.0;
70             xm.eM22 = yscale / (float) 16384.0;
71  
72             len += DecodeGlyph(glyphIndex, curve, & xm);
73         }
74         else
75             assert(false);
76     }
77     while (flags & MORE_COMPONENTS);
78  
79     if (flags & WE_HAVE_INSTRUCTIONS)
80     {
81         unsigned numInstr = str.GetWord();
82  
83         for (unsigned i=0; i<numInstr; i++)
84             str.GetByte();
85     }
86  
87     // The purpose of USE_MY_METRICS is to force the lsb and rsb to take on a desired value.
88     // For example, an i-circumflex (Unicode 00ef) is often composed of the circumflex and a dotless-i.
89     // In order to force the composite to have the same metrics as the dotless-i,
90     // set USE_MY_METRICS for the dotless-i component of the composite. Without this bit,
91     // the rsb and lsb would be calculated from the HMTX entry for the composite (or would need to be
92     // explicitly set with TrueType instructions).
93  
94     // Note that the behavior of the USE_MY_METRICS operation is undefined for rotated composite components.
95  
96     return len;
97 }

对于用三个控制点(p0,p1,p2)定义的二阶Bezier曲线,相应的三阶Bezier曲线的控制点为(p0,(p0+2*p1)/3,(2*p1+p2)/3,p2)。


4.图元指令
        程序清单14-2和14-3给人的印象是TrueType字体的栅格器能够经过扫描和转换图元的轮廓来轻松地实现,好比,用GDI和StrokeAndFillPath函数来填充图元轮廓绘制出来的路径。这种简单的字体栅格器的实现并非颇有用,除非它只用于高分辨诣的设备如打印机等。
        简单栅格器获得的图像笔画粗细不一,有信息的遗漏,有字符特征的损失以及不对称等缺陷。当点阵变小是,状况不会更糟。总之,简单字体栅格器在小尺寸时会产生字迹模糊的结果。在大尺寸时会产生很差看的结果,只有在点阵增大时结果才会改善。
        当在大的em-square(典型的是2048)中定义的图元轮廓缩小为小得多的网格时(如32*32),不可避免会损失精度并引入偏差。

        TrueType解决这个问题的方法是控制图元轮廓从em-square到栅格网格的缩放过程,使获得的结果看起来效果更好,和原始图元的设计尽可能接近。这种技术被称为网格调整(grid fitting),它想达到的目标有:
        消除网格位置的可能影响,保证笔画的粗细和网格的相对位置无关。
        控制图元中关键位置的尺寸
        保持对称性和衬线等 重要的图元设计细节。

        TrueType字体中网格调整的需求在两个地方中编码:控制值表(control value table)和每一个图元的网格调整指令。
        控制值表("cvt"表)用于保存一个数组,这些值被称为网格调整指令。好比,对于有衬线的字体,基线、衬线高度、大写字母笔划的宽度等值都或以是被控制的值。它们能够以字体设计者已知的次序保存在控制值表中,而后用它们的索引来引用。在字体光栅化过程当中,控制值表中的值根据点阵的大小缩放。在网络调整指令中引用这些值能够保证使用的值与网枸的位置无关。好比,若是水平线[14,0,25,200]能够用CVT表中的两个值定义为[14,0,14+CVT[stem_width],0+CVT[cap_height]],那么该线的宽度和高度会和所在网格的相对位置无关,保持不变。
        每个图元的定义中附加有一个指令序列,该指令序列被称为图元指令,该背景令序列用于控制此图元的网格高速。图元指令线用控制值表中的值,以保证在索引图元中这些值相同。
        图元指令是一种基于堆栈的伪计算机的指令。堆栈计算机经常使用于计算机语言的解释性实现。好比,Forth(用于嵌入式系统的一种强大而简洁的语言)、RPL(HP计算器使用的语言)和Java虚拟机都是堆栈计算机。
        堆栈计算机一般没有寄存器,全部的计算都在堆栈上进行(有些堆栈计算机使用分开的控制堆栈和数据堆栈)。好比,压入指令把一个值压入堆栈,弹出指令从堆栈中弹出上面的值,二元加法指令弹出上面的两个值 ,而后把它们的和压入堆栈。

转自: https://blog.csdn.net/g0ose/article/details/81208488

相关文章
相关标签/搜索