蓝点软件(北京)研发中心技术主管
2001/02/28
Contents: |
引言 |
逻辑字体、设备字体以及字符集之间的关系 |
MiniGUI 中的字符集支持 |
MiniGUI 中的字体支持 |
小结 |
资源 |
关于作者 |
在多字体和多字符集的抽象接口之上,MiniGUI 通过逻辑字体为应用程序提供了一致的接口。
int GUIAPI GetFirstWord (PLOGFONT log_font, const char* mstr, int len, |
有了这样的逻辑字体、设备字体和字符集结构定义,当我们需要新添加一种字符集或者字体支持时,只需按照我们的字体操作集和字符集操作集定义对应的新操作集结构即可,而对上层程序没有任何影响。
在 MiniGUI 中,每个特定的字符集由对应的字符集操作集来表示。字符集操作集的定义如下(include/gdi.h。前面的数字表示在该文件中的行数,下同):
250 typedef struct _CHARSETOPS |
- bytes_maxlen_char 用来表示该字符集中字符的最长字节数。通常情况下,一个字符集中的每个字符的长度一般是定长的,但是也有许多例外,比如在 GB18303、UNICODE 等字符集中,字符的最长字节数可能超过 4 字节。
- def_char 用来表示该字符集中的默认字符。该字段主要和字体配合使用。当某个针对该字符集的字体中缺少一些字符的定义时,就需要用默认字体替代这些缺少的字符。
在上述字符集的操作集定义中,后几个字段定义为函数指针,它们均由逻辑字体接口用来进行文本分析:
- len_first_char 返回多字节字符串中第一个属于该字符集的字符的长度。若不属于该字符集,则返回 0。
- char_offset 返回某个字符在该字符集中的位置。该信息可以由设备字体使用,用来从一个字体文件中获取该字符对应的宽度或点阵。
- nr_chars_in_str 计算字符串中属于该字符集的字符个数并返回。注意,传入的字符串必须均为该字符集字符。
- is_this_charset 判断给定的用来表示字符集的名称是否指该字符集。因为对某种特定的字符集,其名称不一定和 name 字段所定义的名称匹配。比如,对 GB2312 字符集,就可能有 gb2312-1980.0、GB2312_80 等各种不同的名称。该函数可以帮助正确判断一个名称是否指该字符集。
- len_first_substr 返回某个多字节字符串中属于该字符集的子字符串长度。如果第一个字符不属于该字符集,则返回为 0。
- get_next_word 返回多字节字符串中属于该字符集的字符串中下一个单词的信息。对欧美语言来说,单词之间由空格、标点符号、制表符等相隔;对亚洲语言来说,单词通常定义为字符。
- pos_first_char 该函数返回多字节字符串中属于该字符集的第一个字符的位置。
- conv_to_uc16 该函数将某个属于该字符集的字符,转换为 UNICODE 的 16 位内码。该函数主要用来从 TrueType 字体中获得字符的轮廓信息。因为 TrueType 字体使用 UNICODE 定位字符,所以需要这个函数完成特定字符集内码到 UNICODE 内码的转换。由于 MiniGUI-Lite 版本尚不支持 TrueType 字体,所以该函数在 MiniGUI-Lite 版本中无需定义。
在 src/font/charset.c 中,定义了系统支持的所有字符集操作集,并由函数 GetCharsetOps 返回某个字符集名称对应的字符集操作集(src/font/charset.c):
716 static CHARSETOPS* Charsets [] = |
如果我们需要定义一种新的字符集支持时,只需在该文件中添加相应的操作集函数以及对应的操作集结构定义即可,比如,对 EUCKR 字符集的支持定义如下(src/font/charset.c):
468 #ifdef _EUCKR_SUPPORT |
在 MiniGUI 中,设备字体定义如下(include/gdi.h):
319 struct _DEVFONT |
- name:该设备字体的名称。MiniGUI 中设备字体的名称格式如下:
<type>-<name>-<style>-<width>-<height>-<charset1[,charset2]>
其中每个域的含义如下:
- type:字体类型,比如RBF(MiniGUI 定义的等宽字体格式)、VBF(MiniGUI 定义的变宽字体格式)、TTF(TrueType 字体)等等。
- name:名称,比如 Song、Hei、Times 等等。
- style:该字体的样式,比如黑体、斜体等等。
- width:该字体的宽度,对矢量字体来说,可取 0。
- height:该字体的高度,对矢量字体来说,可取 0。
- charset1, charset2:该字体适用的字符集名称。
- style:字体样式。
- font_ops:设备字体对应的字体操作集。
- charset_ops:设备字体对应的字符集操作集。
- sbc_next、mbc_next:内部使用的链表维护字段。
- data:该设备字体相关的内部数据。
-
4.2 逻辑字体
228 typedef struct _LOGFONT { 229 char type [LEN_FONT_NAME + 1]; 230 char family [LEN_FONT_NAME + 1]; 231 char charset [LEN_FONT_NAME + 1]; 232 DWORD style; 233 int size; 234 int rotation; 235 DEVFONT* sbc_devfont; 236 DEVFONT* mbc_devfont; 237 } LOGFONT; 238 typedef LOGFONT* PLOGFONT;
逻辑字体的匹配算法可参见 src/gdi/logfont.c 和src/font/devfont.c 文件。限于篇幅,不再赘述。
4.3 设备字体操作集和字符集操作集一样,MiniGUI 中的设备字体操作集针对每种设备字体类型而定义,包括对这种设备字体的各种操作函数(include/gdi.h):
276 typedef struct _FONTOPS 277 { 278 int (*get_char_width) (LOGFONT* logfont, DEVFONT* devfont, 279 const unsigned char* mchar, int len); 280 int (*get_str_width) (LOGFONT* logfont, DEVFONT* devfont, 281 const unsigned char* mstr, int n, int cExtra); 282 int (*get_ave_width) (LOGFONT* logfont, DEVFONT* devfont); 283 int (*get_max_width) (LOGFONT* logfont, DEVFONT* devfont); 284 int (*get_font_height) (LOGFONT* logfont, DEVFONT* devfont); 285 int (*get_font_size) (LOGFONT* logfont, DEVFONT* devfont, int expect); 286 int (*get_font_ascent) (LOGFONT* logfont, DEVFONT* devfont); 287 int (*get_font_descent) (LOGFONT* logfont, DEVFONT* devfont); 288 289 /* TODO */ 290 // int (*get_font_ABC) (LOGFONT* logfont); 291 292 size_t (*char_bitmap_size) (LOGFONT* logfont, DEVFONT* devfont, 293 const unsigned char* mchar, int len); 294 size_t (*max_bitmap_size) (LOGFONT* logfont, DEVFONT* devfont); 295 const void* (*get_char_bitmap) (LOGFONT* logfont, DEVFONT* devfont, 296 const unsigned char* mchar, int len); 297 298 const void* (*get_char_pixmap) (LOGFONT* logfont, DEVFONT* devfont, 299 const unsigned char* mchar, int len, int* pitch); 300 /* Can be NULL */ 301 302 void (*start_str_output) (LOGFONT* logfont, DEVFONT* devfont); 303 /* Can be NULL */ 304 int (*get_char_bbox) (LOGFONT* logfont, DEVFONT* devfont, 305 const unsigned char* mchar, int len, 306 int* px, int* py, int* pwidth, int* pheight); 307 /* Can be NULL */ 308 void (*get_char_advance) (LOGFONT* logfont, DEVFONT* devfont, 309 int* px, int* py); 310 /* Can be NULL */ 311 312 DEVFONT* (*new_instance) (LOGFONT* logfont, DEVFONT* devfont, 313 BOOL need_sbc_font); 314 /* Can be NULL */ 315 void (*delete_instance) (DEVFONT* devfont); 316 /* Can be NULL */ 317 } FONTOPS;
比如,get_char_width 用来获得某个字符的宽度,而 get_char_bitmap 用来获得某个字符的位图信息等等。
155 static const void* get_char_bitmap (LOGFONT* logfont, DEVFONT* devfont, 156 const unsigned char* mchar, int len) 157 { 158 int offset; 159 unsigned char eff_char = *mchar; 160 VBFINFO* vbf_info = VARFONT_INFO_P (devfont); 161 162 if (*mchar < vbf_info->first_char || *mchar > vbf_info->last_char) 163 eff_char = vbf_info->def_char; 164 165 if (vbf_info->offset == NULL) 166 offset = (((size_t)vbf_info->max_width + 7) >> 3) * vbf_info->height 167 * (eff_char - vbf_info->first_char); 168 else 169 offset = vbf_info->offset [eff_char - vbf_info->first_char]; 170 171 return vbf_info->bits + offset; 172 }
其中,VARFONT_INFO_P 是一个宏,用来从设备字体的 data 字段中获得 VBFINFO 结构的指针。有了这个指针之后,该函数计算字符位图的偏移量最后返回字符的位图。
4.4 新设备字体的实现举例这里以 Adobe Type1 字体的实现为例,说明如何在 MiniGUI 中实现一种新的设备字体。MiniGUI 借用了 T1Lib 函数库实现了对 Type1 字体的支持。
- 字体通过运行时读取字库而被T1lib得知。即它是灵活可配置的。当然,它只支持Type 1字体。
- 字符或字符串只在需要时才被光栅化。
- 对字符串光栅化时支持字符间紧排,并且可以利用一个AFM文件提供紧排信息,如果没有这个文件,T1Lib可以直接生成这些信息,也可以将其输出到一个文件以备后用。
- 支持连字,连字是一个好的字体模型会提供的功能,目前,只有TEX和与其相关的软件包对连字支持得比较好。连字信息也包含在AFM文件里。
- 支持旋转和各种仿射变换。支持字体扩展,倾斜。
- 可以动态载入新的解码矢量。用新的解码矢量解析字体。
- 支持5灰度的低分辨率和17灰度的高分辨率的反走样。
- 字符串可以被添加下划线,上划线或者横线。
1) TYPE1INFO和TYPE1INSTANCEINFO 结构
22 typedef struct tagTYPE1GLYPHINFO { 23 int font_id; 24 //BBox font_bbox; 25 //int ave_width; 26 BOOL valid; 27 } TYPE1INFO, *PTYPE1INFO; 28 29 typedef struct tagTYPE1INSTANCEINFO { 30 PTYPE1INFO type1_info; 31 int rotation;/*in tenthdegrees*/ 32 T1_TMATRIX * pmatrix; 33 int size; 34 int font_height; 35 int font_ascent; 36 int font_descent; 37 38 int max_width; 39 int ave_width; 40 41 double csUnit2Pixel; 42 /* 43 * last char or string's info 44 * T1_SetChar, T1_SetString, T1_AASetSting, T1_AASetString all return a static 45 * glyph pointer, we save the related infomation here for later use. 46 * */ 47 char last_bitmap_char; 48 char last_pixmap_char; 49 char * last_bitmap_str; 50 char * last_pixmap_str; 51 int last_ascent; 52 int last_descent; 53 int last_leftSideBearing; 54 int last_rightSideBearing; 55 int last_advanceX; 56 int last_advanceY; 57 unsigned long last_bpp; 58 char * last_bits; 59 60 } TYPE1INSTANCEINFO, *PTYPE1INSTANCEINFO; 61
前面各个字段的意义可以根据名字推测出来,从csUnix2Pixel 开始则是为了实现的方便和高效而自己定义的一些变量,后面解释函数实现时将会说明。last*系列函数主要起缓冲的作用。
2) InitType1Fonts 和 TermType1Fonts 函数
其实主要的处理功能在 T1lib 中,每次程序向 T1lib 注册一个字体,T1lib会返回一个 Font ID,以后利用该ID 向T1lib请求关于对应字体的某些服务。
TermType1Fonts 则是注销 Type1 字体,关闭T1lib。
InitType1Fonts 注册向系统注册了用来处理 Abode Type1 字体的字体操作集,定义如下(src/font/type1.c):
780 static FONTOPS type1_font_ops = { 781 get_char_width, 782 get_str_width, 783 get_ave_width, 784 get_max_width, 785 get_font_height, 786 get_font_size, 787 get_font_ascent, 788 get_font_descent, 789 char_bitmap_size, 790 max_bitmap_size, 791 get_char_bitmap, 792 get_char_pixmap, 793 start_str_output, 794 get_char_bbox, 795 get_char_advance, 796 new_instance, 797 delete_instance 798};
- ascent:描述某个字符在基准线上有多少扫描线。这里以像素为单位(下同)。
- descent:描述某个字符在基准线下有多少扫描线。当字符的底线在基准线之下时,用负值来表示,所以整个字符的高度就是 ascent - descent。
- leftSideBearing:某个字符从其原点到最左边像素点的水平距离,也可以称为该字符的left margin。
- rightSideBearing:某个字符从其原点到最右边像素点的水平距离,也可以称为该字符的right margin。
- advanceX:在某字符的图象被放置后,当前原点需要前进的水平距离。它通常比字符图像的宽度要大,因为两个字符之间存在一定的空白。由于该值对齐至像素,所以一些要求精确的内部计算不能用它,会累积误差。
- advanceY:在某字符的图象被放置后,当前原点需要前进的竖直距离。
3) get_char_bitmap 和 get_char_pixmap
这两个函数是主要的光栅化函数。它们首先判断一下需要光栅化的字符是否刚刚被光栅化过,如果是,直接返回缓冲里的值。
T1_AASetLevel (T1_AA_LOW)
T1_AASetLevel (T1_AA_HIGH)
这里使用的反走样其实很简单,就是先将字体放大,然后再取样缩小。低精度是放大四倍(2*2),高精度则是放大16倍(4*4),灰度值则有n+1种。
当然,为了提高性能,每次光栅化的结果都要被放到缓冲里,下次如果要光栅化相同的字符,并且方式相同,则可以大大地提高效率。
给出当前原点值(*px,*py),调用该函数要求得到在字符被画出后的原点值(新的*px,*py),以及当前字符的宽度和高度。
6) new_instance 和 delete_instance
当用户创建一个新的逻辑字体时调用new_instance ,当用户删除一个逻辑字体时会调用delete_instance。
- 字符集及字体
- MiniGUI 资源
- MiniGUI 邮件列表