FreeType 2 教程
第二步 管理字型
@ 2009 David Turner (david@freetype.org)
@ 2009 The FreeType Development Team (http://www.freetype.org/)
翻译:liaoying
简介
这是FreeType2教程的第二部分,它包括以下内容:
获取字型数据
简单管理字型图像
获取全局数据(包括字距kerning)
渲染一个简单文本(包括字距kerning功能)
渲染一个居中文本(包括字距kerning功能)
渲染一个经过转换过的文本(包括centering功能)
通过字型数据(metrics)设计字体单位,以及怎样放置到设备上.
1.字型数据(Glyph metrics)
字型数据,就如它的名字所暗示的一样,是某些和每个字型相关的,用于字型布局的数据.
对于一个字型,常常出现两组数据:用于水平布局的数据(比如Latin, Cyrillic, Arabic, Hebrew等语言)和用于垂直布局的数据(比如Chinese, Japanese, Korean等语言).
注意只有一少部分字体格式提供了垂直布局的数据.你可以通过宏FT_HAS_VERTICAL来测试一个给定的face是不是包含垂直布局的数据.
某些字型数据可以在第一次载入字型到字型槽之后进行访问,访问必须功过face->glyph->metrics结构体,它是FT_Glyph_Metrics类型.我们将在后面仔细讨论.现在,我们只需注意它所包含的属性:
width 这是字型图像边框(bbox)的宽度,它和布局方向无关.
height 这是字型图像框(bbox)的高度,也和布局方向无关.需要注意的是,不要将这个height和FT_Size_Metrics中的height搞混了.
horiBearingX 对于水平布局,这是当前笔位置到字型图像框最左边距的水平距离.
horiBearingY 对于水平布局,这是基线到字型图像框最上面的垂直距离.
horiAdvance 对于水平布局,这是绘制字符串的时候笔位置的水平增量.
vertBearingX 垂直布局用,偷懒不译,水平布局搞清楚了,垂直布局也就差不多了.
vertBearingY 同上.
vertAdvance 同上.
注意:由于不是所有的字体都一定包含垂直布局的字型数据,vertBearingX, vertBearingY 和 vertAdvance这三个属性应该在确保FT_HAS_VERTICAL()返回TRUE的时候有效.(原文有误,为False).
这个图将这些数据描述得很清楚.
水平布局时的数据:图片链接
垂直布局时的数据:图片链接
字型数据face->glyph->metrics通常是用26.6的像素格式来表示(26.6格式:其实32位的数据,高26位表示整数,低6位表示小数,目的是用整数模拟浮点数,以兼容不支持浮点运算的处理器.使用时,右移6位或者乘以1/64即可转换为普通整数).如果在调用FT_Load_Glyph或者FT_Load_Char的时候,给于FT_LOAD_NO_SCALE标记,则这些数据将以原始单位表示.
字型槽对象也有一些我们感兴趣的属性,他们可以减轻我们开发者的工作.你可以用face->glyph->xxx来进行访问,xxx可以是以下一些属性:
advance 这个属性是FT_Vector类型(二维矢量),它为一个字型保留了经过变换的步进(advance)属性.当你使用FT_Set_Transform函数进行变换的时候,这个属性将非常有用,这已经在教程的第一部分的旋转字型部分进行了说明.除了这种情况,它的值就默认是[metrics.horiAdvance,0].当然,如果你载入字体的时候指定了FT_LOAD_VERTICAL标记,它的值就是[0,metrics.vertAdvance].
linearHoriAdvance 这个属性包含字型在水平方向上线性变换后的步进值.的确,字型槽返回的metrics.horiAdvance的值通常是被字体驱动取整,然后用来载入字型图像之后的像素坐标(它将是和64的乘积). linearHoriAdvance 是一个16.16的定点浮点数据,如果要得到原始数据,不妨除以65536或者是右移16位.它可以用来呈现虚拟的设备无关的文本布局.
linearVertAdvance 垂直布局用,和水平的差不多.
2.管理字型图像
字型槽中载入的字型图像可以转换为一个位图,或者在载入的时候使用FT_LOAD_RENDER,或者直接调用FT_Render_Glyph(),都可以.每当你载入了一个新的字型图像,原来字型槽中的字型图像将被冲掉.
有时候,在转换为位图之前,你可能需要从字型槽中获取字型图像,并将它在你的应用程序中缓存起来,甚至需要做一些附加的变换和度量.
FreeType2的API有一些特定的扩展功能,它可以轻松,灵活并具有一般性的处理字型图像.要使用这些API,需要包含FT_GLYPH_H头文件:#include FT_GLYPH_H
现在我们解释一下怎样使用这些函数:
a.提取字型图像
你可以很容易的提取一个单独的字型图像.如下代码所示:
FT_Glyph glyph; /* a handle to the glyph image */ ...
error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NORMAL );
if ( error )
{ ... }
error = FT_Get_Glyph(face->glyph, &glyph );
if ( error )
{ ... }
如你所见,我们做了这些事:
1.创建一个FT_Glyph的变量,名字叫做glyph.这是一个字型图像的句柄(指针).
2.载入一个字型图像到face的字型槽.我们不需要使用FT_LOAD_RENDER,因为我们仅仅指向抓取一个可变换的字型图像,用于下面的变换.
3.从字型槽中拷贝一个字型图像到一个新的FT_Glyph对象中,调用FT_Get_Glyph即可.这个函数返回一个错误码并填充glyph.
重要的是,我们必须注意到提取出来的字型图像和原始的字型图像(仍然在字型槽中)是同样的格式.举个例子,如果我们从TrueType的字体文件中载入了一个字型,那么这个字型图像将可变换的矢量轮廓.
如果你想精确的知道字型是怎样模拟和存储的,你可以访问glyph->format属性.一个字型对象可以调用FT_Done_Glyph来进行销毁.
字型对象精确的包含了一个字型图像和一个表示字型步进的2维的矢量(以16.16方式表示,根据译者验证, glyph->advance.x , face->glyph->advance.x , face->glyph->metrics->horiAdvance这三个值是一样的).
注意,不像其他FreeType对象,library不会保留所有分配了内存的glyph对象的列表.这意味着,你必须手动销毁这些glyph对象,而不是使用FT_Done_FreeType来做所有的清理工作.
b.变换,拷贝字型图像
如果字型图像是可变换的(必须保证glyph->format不是FT_GLYPH_FORMAT_BITMAP),那么我们可以随时调用FT_Glyph_Transform()来进行变换.
你也可以调用FT_Glyph_Copy()拷贝一份单独的字型图像.下面是一些示例代码:
FT_Glyph glyph, glyph2;
FT_Matrix matrix;
FT_Vector delta;
//... load glyph image in `glyph' ...
/* copy glyph to glyph2 */
error = FT_Glyph_Copy( glyph, &glyph2 );
if ( error )
{ ... could not copy (out of memory) ... }
/*translate `glyph' */
delta.x = -100 * 64;
/* coordinates are in 26.6 pixel format */
delta.y = 50 * 64;
FT_Glyph_Transform( glyph, 0, &delta );
/* transform glyph2 (horizontal shear) 貌似是变斜体字矩阵*/
matrix.xx = 0x10000L;
matrix.xy = 0.12 * 0x10000L;
matrix.yx = 0;
matrix.yy = 0x10000L;
FT_Glyph_Transform( glyph2, &matrix, 0 );
注意,变换时,2*2的变换矩阵经常需要16.16的步进向量,所以你不需要重新计算它.
c.度量字型图像
你可以调用FT_Glyph_Get_CBox ()来获取任何字型图像的控制框(CBox),可以获得可变换或者不可不换的CBox:
FT_BBox bbox;
FT_Glyph_Get_CBox( glyph, bbox_mode, &bbox );
坐标系统是相对于字型的原点(笔位置,0,0)的,通常使用Y向上的常用坐标系统.这个函数需要一个特别的参数,即bbox的模式,来指定控制框的坐标系统怎样表示.
如果字型载入的时候使用了FT_LOAD_NO_SCALE标记,那么bbox_mode必须使用FT_GLYPH_BBOX_UNSCALED来获取没有变换成为26.6格式的字体单位.FT_GLYPH_BBOX_SUBPIXELS的值是这个常量的另外一个名字,实质是一样的.
注意控制框的最大坐标值是专用的,即每个字型都不同,这意味着每载入一个字型,你都需要要使用整数或者26.6格式的数来重新计算字型图像的宽度和高度:
width = bbox.xMax - bbox.xMin;
height = bbox.yMax - bbox.yMin;
还要注意,对于26.6的坐标系统,如果FT_GLYPH_BBOX_GRIDFIT作为bbox mode,那么,坐标系将是网格对齐的.相当于:
bbox.xMin = FLOOR( bbox.xMin )
bbox.yMin = FLOOR( bbox.yMin )
bbox.xMax = CEILING( bbox.xMax )
bbox.yMax = CEILING( bbox.yMax )
如果要将bbox置于整数坐标系统中,把bbox_mode置为FT_GLYPH_BBOX_TRUNCATE即可.
最后,如果要将控制框置于网格对齐的坐标系统中,将bbox_mode置为FT_GLYPH_BBOX_PIXELS即可.
d.将字型图像转换为位图
一旦你很方便的完成了字型的缓存或者转换之后,你也许需要将字型对象转换为位图.调用FT_Glyph_To_Bitmap()函数可以很方便的完成转换.它负责将任何字型转换为位图:
FT_Vector origin;
origin.x = 32; /* 1/2 pixel in 26.6 format */
origin.y = 0;
error = FT_Glyph_To_Bitmap(&glyph, render_mode, &origin, 1 );
/*destroy original image == true */
注意:
1.第一个参数是字型句柄的地址(指针的指针),当这个函数被调用的时候,函数将通过这个指针来访问字型对象.调用完成之后,这个句柄将指向一个新的字型对象,这个新的字型对象包含渲染后的位图.
2.第二个参数是一个标准的渲染模式,用来指定你想得到那种位图.FT_RENDER_MODE_DEFAULT将得到一个8位抗锯齿灰度位图,使用FT_RENDER_MODE_MONO将得到一个1位单色位图.
3.第三个参数是一个二维向量的指针,它用来在转换为位图之前,转化源字型的位置.注意,函数调用之后,源图像将被转化回它自己的原点位置.如果你不需要在渲染位图之前做转换,则置NULL;
4.最后一个参数是一个布尔类型,它指定源字型对象是否在调用后被销毁.如果置为false,应用程序又没有保留其指针,那么源字型对象将永远不能销毁.
字型对象总是包含一个位图(如果没有返回错误的话),你必须将它的句柄强转成FT_BitmapGlyph类型之后才能访问.这种类型是FT_Glyph的一种"子类",它包含了额外的属性,详见FT_BitmapGlyphRec:
left 和字型槽的bitmap_left一样,这是原点到位图左边界的水平距离.它以整数的方式存放.
top 和字型槽的bitmap_top一样,它是原点到位图上边界的垂直距离.它以整数方式存放,符合和Y的正方向一样.
bitmap 和字型槽的bitmap一样,这是位图的数据.
3.全局字型数据
和字型数据不一样的是,全局数据是用来描述整个字体Face的距离和特性.他们或者以26.6像素格式的方式,或者以font units为单位,方便变换.
a.设计全局数据
对于可变换的数据格式,所有的全局数据都是以font unit为单位来呈现,这样方便接下来的变换,符合上一部分教程所描述的规则.你可以直接通过FT_Face的句柄来访问他们.
不过,使用前你需要见车字体的face是否可变换.使用宏FT_IS_SCALABLE可以很方便的检测,如果可变换,返回True;
在可变换的条件下,你可以访问到的全局数据有:
units_per_EM 这事字体Face的EM举行的大小,它用于将字型映射到设备上,正如上一章节所说.它的值往往是2048(对于TrueType字体),或者是1000(对于Type 1字体),但是也有可能是其他值.比如说对于固定大小的点阵字体,它的值是1.
bbox 全局的字型框,它是一个Face里面所有字型框的最大值.
ascender 全局的上行高度,它是一个Face里面,对于所有字型,基线到字型最高点的最大值.不幸的是,在不同的字体里面,它的定义是不同的.在有些字体中,它代表所有拉丁字符的上行高度(不包括accents?),有些又包括accents.还有一些它表示bbox.yMax.
descender 全局的下行高度.和上面的差不多,取反即可.
height 全局的字体高度,它简单的用来表示一行的高度,即基线到基线之间的高度.注意他经常大于上行高度和下行高度的绝对值之和.但是不能保证字体不会越过这个高度.
max_advance_width 所有字型的水平步进的最大值.它可以用来快速的计算一个文本字符串的最大宽度.它不能理解为字型图像宽度的最大值.
max_advance_height 用于垂直布局,意义同上.
underline_position 当你想要渲染一段带下划线的文本的时候,这个值用来表示相对于基线的下划线的位置.如果它在基线下方,那么它将是一个负值.
underline_thickness 表示下划线的厚度(高度).
要记住,不幸的是,上行高度和下行高度的值有可能是不可靠的,由于不同字体之间的差异.