Windows矢量字体字模的提取
2009-11-11
作者:王 赤 周 维
摘 要:利用Windows提供的丰富的字体库,调用Win32 API函数,将矢量汉字文本转化为位图,以提取汉字字模,用于电子系统的信息显示。
关键词:Windows矢量字体Win32 API函数 汉字字模 位图
很多场合都需要用到汉字显示,如公共汽车上用来报站的电子显示牌、商场用来显示各种商品信息的电子显示牌等。Windows提供了丰富的字体库。如何利用这些字体库进行汉字显示,是需要解决的一个问题。Windows支持GDI字体和设备字体二大类字体。GDI字体存储在硬盘文件中,而设备字体是输出设备所固有的。GDI字体分为三种类型:点阵字体、笔划字体和TrueType字体。点阵字体的字模可以从字库文件中直接得到,而后二种是矢量字体,无法直接得到它们的字模,所以必须将笔划字体和TrueType字体点阵化,以获得所需要的字模。
通常情况下,电子系统的信息显示使用16×16(32字节)的点阵字库,例如在Win98下的Chs16.fon即为16×16(32字节)的字库文件。从中提取字模的方法是:汉字的内码为二个字节,设为a和b。a的大小应该介于0xa1和0xfe之间,其区码为qu=a-0xa0,位码为wei=b-0xa0,汉字字模在字库文件中的位置为offset=((qu-1)×94+(wei-1))×32。
本文主要介绍从Windows的矢量字体中提取字模的方法。此方法已成功地运用于单片机系统设计中,解决了汉字显示的问题。在实际应用中,可以直接调用Win32 API函数,将需要提取字模的汉字文本转化为位图,以此实现汉字的点阵化,用来提取字模。
1 字体设置
首先需要设置字体。Win32 SDK提供了用于字体选择的通用对话框,只需调用ChooseFont函数,其返回值为一个布尔值。具体定义为BOOL ChooseFont(LPCHOOSEFONT lpcf)。调用此函数后,弹出字体选择对话框,在此可以选择所需要的字体、字形、大小等参数。选择完毕后,如果点击了字体选择对话框上的确定键,此函数返回一个非零值;若点击的是取消键,则函数返回一个零值。调用此函数前,还要定义二个变量:
CHOOSEFONT cf;
LOGFONT logfont;
CHOOSEFONT是有十多个字段的结构体,包含了ChooseFont函数用来初始化字体选择对话框的各种信息。LOGFONT也是一个结构体,包含14个字段,定义了字体的各种属性。当点击确定键后,系统通过LOGFONT结构返回选定的字体信息。返回的字体信息保存在CHOOSEFONT结构的lpLogFont字段指定的LOGFONT结构中。
下面是调用ChooseFont函数的代码:
//初始化CHOOSEFONT
cf.lStructSize =sizeof (CHOOSEFONT);
cf.hwndOwner =hwnd; //当前窗口的句柄
cf.hDC =NULL;
cf.lpLogFont =&logfont;//系统返回的字体信息保存在此处
cf.iPointSize =0;
cf.Flags =CF_INITTOLOGFONTSTRUCT|CF_
SCREENFONTS|CF_EFFECTS;
cf.rgbColors =0;
cf.lCustData =0;
cf.lpfnHook =NULL;
cf.lpTemplateName =NULL;
cf.hInstance =NULL;
cf.lpszStyle =NULL;
cf.nFontType =0;
cf.nSizeMin =0;
cf.nSizeMax =0;
ChooseFont(&cf); //此函数调用后弹出字体选择通用对话框
如果ChooseFont(&cf)函数返回非零值,则字体已经选定。选定的字体就保存在logfont变量中。接下来要做的就是创建选定的逻辑字体。可以调用CreateFontIndirect函数创建逻辑字体。CreateFontIndirect函数接受一个指向LOGFONT结构的指针,具体定义为HFONT CreateFontIndirect(CONST LOGFONT?鄢lplf)。代码如下:
HFONT hNewFont=CreateFontIndirect(&logfont);
至此字体创建就完成了。直接调用SeletObject函数就可以将创建的逻辑字体选入设备描述表。在位图转换里将使用SelectObject函数将hNewFont选入内存设备描述表。但还要注意一点,在程序结束前,必须调用DeleteObject(hNewFont)函数来释放字体句柄,避免内存泄漏。下面介绍文本转换为位图的具体实现过程。
2 位图转换
此处以提取一个“婷”字的字模为例进行说明。首先需要定义如下变量:
static WCHAR Hanzi[]=“婷”;
static HBITMAP hBitmap;
static int cxBitmap,cyBitmap;
static HDC hdc,hdcMem;
PAINTSTRUCT ps;
SIZE size;
cxBitmap、cyBitmap是所要创建的位图的大小,二者与GetTextExtentPoint32函数得到的文本大小一致,在此处即是“婷”字的大小。
以下是将汉字文本转化为位图的具体方法,一般在WM_PAINT消息中处理。
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);//得到当前窗口的设备句柄
hdcMem=CreateCompatileDC(hdc);//创建一个内存设备描述表
SelectObject(hdcMem,hNewFont);//将创建的字体选入内存设备描述表
GetTextExtendPoint32W(hdcMem,Hanzi,1,&size);
//获取要显示的文本的大小
cxBitmap =size.cx.;
cyBitmap =size.cy;
hBitmap =CreateCompatileBitmap(hdc,cxBitmap,
cyBitmap); //创建一个位图句柄
SelectObject(hdcMem, hbitmap);//将位图选进内存设备描述表
TextOutW(hdcMem,0,0,Hanzi,1);//将汉字画在内存设备描述表的位图上
BitBlt(hdc,0,0,cxBitmap,cyBitmap,hdcMem,0,0,SRC-
COPY); //将位图显示在窗口的客户区,用来观察显示的效果
至此,汉字的点阵化过程就完成了,接下来就应该提取字模。
3 提取字模
提取字模要用到的是GetPixel函数,定义为COLORREF GetPixel(HDC hdc,int nXPos,int nYPos)。此函数返回一个COLORREF类型的值,即nXPos、nYPos所指定的点的RGB值。位图的大小在前文已经确定,在此范围类将每个象素点扫描一次,根据返回的RGB值生成点阵码。因为Windows矢量字体有灰度等级,所以必须选择合适的RGB值,用来判断此点是否有效。白色的RGB值是FFFFFFH,深灰的是808080H,黑色的是000000H。可以选择深灰做为判断依据,当函数返回值小于808080H时,认为此点有效。下面是提取字模的函数,以字节为存储单位,从第一行第一个点开始扫描:
static Zimo[2048];//点阵码存在此数组中
void GetZimo(HDC hdc,int nXPos,int nYPos)
{
int Hang,Lie; //Hang为扫描的行数
int temp,i,j,g;
Hang=nYPos;
Lie=nXPos;
if(Lie % 8==0 ){
Lie=Lie/8; //位图的宽度是8的整数倍,所以
//只需要Lie/8个字节来存储字模
Temp=0;
}
else{
temp=Lie % 8;
Lie=Lie/8+1; //位图的宽度不是8的整数倍,
//所以只需要Lie/8+1个字节来存储字模
}
memset(Zimo,0,2048);//将字模数组全置0
for(i=0;i
for(k=0;k
if(g<0x00808080)
Zimo[i*Lie+j]+=(unsigned
char)pow(2,7-k);
}
}
else{
for(k=0;k<8;k++){
g=(int)GetPixel(hdc,j*8+k,i);
if(g<0x00808080)
Zimo[i*Lie+j]+=(unsigned
char)pow(2,7-k);
}
}
}
}
}
在WM_PAINT消息中,调用GetZimo(hdcMem,cxBit-
map,cyBitmap)即可得到汉字的字模。在程序的最后,还必须做些扫尾的工作:
DeleteObject(hBitmap);//使用完后必须释放设备描述
//表和位图句柄,避免内存泄漏
DeleteObject(hNewFont);
DeleteDC(hdcMem);
EndPaint(hwnd,&ps);
Return 0; //WM_PAINT消息处理完后返回
4 输出结果
以“婷”字为例,弹出字体选择对话框后,字体选新宋体、字形选常规、字号选小二。得出Hang=24,Lie=3,存储72个字节。字模为00H,00H,00H,00H,00H,00H,06H,03H,00H,04H,01H,80H,04H,01H,0CH,04H,3EH,F0H,0CH,00H,00H,7FH,CFH,F8H,08H,88H,10H,08H,88H,10H,18H,8FH,F0H,10H,90H,04H,11H,BFH,FEH,11H,20H,04H,31H,60H,08H,21H,40H,18H,1FH,0FH,E0H,03H,01H,80H,05H,C1H,80H,0CH,C1H,80H,08H,01H,80H,10H,0DH,80H,20H,03H,00H,00H,00H,00H。在纸上画出点阵码,正好是“婷”字,如图1所示。从cxBitmap和cyBitmap可以知道,“婷”字点阵大小是24×24。
5 结束语
利用文本转位图的方法,可以从Windows丰富的字体库中提取各种字体的字模,不再局限于单一的字体,从而丰富了电子显示系统的设计。如果从点阵字库中提取字模,存在着一些不足,最主要的是可供选择的字体太少。另外,使用本文介绍的方法,还可以提取简单的图片点阵,更加丰富了电子显示系统的设计。
参考文献
1 Petzold C.Windows程序设计.北京:北京大学出版社,1999
2 本书编写组.新编Windows API参考大全.北京:电子工业 出版社,2001
3 李宏.Windows API常用技巧汇编.北京:清华大学出版社,2000
4 博嘉科技主编.Windows API For 2000/XP实例精解.北京:电子工业出版社,2002