来源:sc115.com | 155 次浏览 | 2015-09-27
如果在一些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字体的核心信息,因此通常它是最大的表。因为的位置索引是一张单独的表,图元数据表就完全只是图元的序列而已,每个图元以图元头结构开始:
typedef struct
{
WORD numberOfContours; //contor number,negative if composite
FWord xMin; //Minimum x for coordinate data.
FWord yMin; //Minimum y for coordinate data.
FWord xMax; //Maximum x for coordinate data.
FWord yMax; //Maximum y for coordinate data.
}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类的一个方法。
int KTrueType::DecodeGlyph(int index, KCurve & curve, XFORM * xm) const
{
const GlyphHeader * pHeader = GetGlyph(index);
if ( pHeader==NULL )
{
// assert(false);
return 0;
}
int nContour = (short) reverse(pHeader->numberOfContours);
if ( nContour<0 )
{
return DecodeCompositeGlyph(pHeader+1, curve); // after the header
}
if ( nContour==0 )
return 0;
curve.SetBound(reverse((WORD)pHeader->xMin), reverse((WORD)pHeader->yMin),
reverse((WORD)pHeader->xMax), reverse((WORD)pHeader->yMax));
const USHORT * pEndPoint = (const USHORT *) (pHeader+1);
int nPoints = reverse(pEndPoint[nContour-1]) + 1; // endpoint of last contour + 1
int nInst = reverse(pEndPoint[nContour]); // instructon length
curve.m_glyphindex = index;
curve.m_glyphsize = (int) GetGlyph(index+1) - (int) GetGlyph(index);
curve.m_Ascender = m_Ascender;
curve.m_Descender = m_Descender;
curve.m_LineGap = m_LineGap;
GetMetrics(index, curve.m_advancewidth, curve.m_lsb);
if ( curve.m_glyphsize==0 )
return 0;
curve.m_instrsize = nInst;
const BYTE * pFlag = (const BYTE *) & pEndPoint[nContour] + 2 + nInst; // first byte in flag
const BYTE * pX = pFlag;
int xlen = 0;
for (int i=0; i
int unit = 0;
switch ( pX[0] & G_XMASK )
{
case G_XADDBYTE:
case G_XSUBBYTE:
unit = 1;
break;
case G_XADDINT:
unit = 2;
}
if ( pX[0] & G_REPEAT )
{
xlen += unit * (pX[1]+1);
i += pX[1];
pX ++;
}
else
xlen += unit;
}
const BYTE * pY = pX + xlen;
int x = 0;