QT/Embedded从ttf字库中提取指定汉字生成qpf字库的办法
解决思路: 获取汉字的unicode编码,使用unicode编码来获取ttf字库中的汉字来生成qpf字库.
首先对我所用的linux环境作以下说明:
Linux系统:
Redhat Linux 9
QT:
qt-embedded-free-3.3.3.tar.bz2
下面正式开始了:
1. 在根目录下新建一个目录
mkdir qte
2. 把qt-embedded-free-3.3.3.tar.bz2移到qte目录
mv qt-embedded-free-3.3.3.tar.bz2 /qte
3. 解压qt-embedded-free-3.3.3.tar.bz2
tar -jxvf qt-embedded-free-3.3.3.tar.bz2
4. 重命名qt-embedded-free-3.3.3.tar.bz2文件夹
mv qt-embedded-free-3.3.3 qte
5. 在根目录的qte目录下写如下shell脚本并保存成buildqte
#!/bin/sh
cd qte
export QTDIR=$PWD
export PATH=$QTDIR/bin:$PATH
export LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
./configure -qt-gfx-transformed -qvfb
make -C src #这样只是编译src目录下的源码 如果你没有 moc uic 工具就全部编译 你可以直接make而不要后面的 -C src
cd ..
6. 执行buildqte脚本编译x86版本的qte库.
./buildqte
7. 检查/qte/qte/bin目录是否有以下工具 qmake moc
如果有就行了,没有就请仔细看第5步,或许你的机器已经安装了qt3 x11版本,可以把uic她拷贝到/qte/qte/bin下使用.
8. 修改/qte/qte/src/kernel/qmemorymamager_qws.cpp文件。(修改的代码很少,这也是最关键的部分。)
说明:为什么要改这个文件
请进入如下目录 /qte/qte/tools/makeqpf/ 打开main.cpp文件(这是makeqpf工具的源码)
请关注下面的代码
129 class MakeQPF : public QMainWindow
130 {
131 Q_OBJECT
132 QListView* view;
133 public:
134 MakeQPF()
135 {
136 view = new QListView(this);
137 view->addColumn("Family");
138 view->addColumn("Size");
139 view->addColumn("Weight");
140 view->addColumn("Style");
141 setCentralWidget(view);
142 QString fontdir = qws_topdir() + "/lib/fonts";
143 readFontDir(fontdir);
144
145 connect(view,SIGNAL(selectionChanged(QListViewItem*)),this,SLOT(renderAndSave(QListViewItem*)));
146 }
重点关注145行,在这里调用了renderAndSave(QListViewItem*)槽
234
235 private slots:
236 void renderAndSave(QListViewItem* i)
237 {
238 ((FontViewItem*)i)->renderAndSave();
239 }
重点关注238行
54 void renderAndSave()
55 {
56 font = QFont(family,pointSize,weight,italic);
57 #ifdef Q_WS_QWS
58 memorymanager->savePrerenderedFont((QMemoryManager::FontID)font.handle());
59 #endif
60 setHeight(QFontMetrics(font).lineSpacing());
61 repaint();
62 }
重点关注58行
33 #ifdef Q_WS_QWS
34 #include <qmemorymanager_qws.h>
35 #endif
重点关注34行
58行中的memorymanager是在头文件qmemorymanager_qws.h中定义的。
打开/qte/qte/include/qmemorymanager_qws.h文件
77 #ifndef QT_NO_QWS_SAVEFONTS
78 void savePrerenderedFont(const QFontDef&, bool all=TRUE);
79 void savePrerenderedFont(FontID id, bool all=TRUE);
80 #endif
81 bool fontSmooth(FontID id) const;
82 int fontAscent(FontID id) const;
83 int fontDescent(FontID id) const;
84 int fontMinLeftBearing(FontID id) const;
85 int fontMinRightBearing(FontID id) const;
86 int fontLeading(FontID id) const;
87 int fontMaxWidth(FontID id) const;
88 int fontUnderlinePos(FontID id) const;
89 int fontLineWidth(FontID id) const;
90 int fontLineSpacing(FontID id) const;
91
92 private:
93 QMap<PixmapID,QMemoryManagerPixmap> pixmap_map;
94 int next_pixmap_id;
95 QMap<QString,FontID> font_map;
96 int next_font_id;
97 };
98
99 extern QMemoryManager* memorymanager;
重点关注79行和99行
79行的成员函数实现在/qte/qte/src/kernel/qmemorymanager_qws.cpp
打开上面的文件看到下面的代码( 生成qpf字库的工作就在下面做了, 下面是没有改动过的代码 )
991 #ifndef QT_NO_QWS_SAVEFONTS
992 void QMemoryManager::savePrerenderedFont(const QFontDef& f, bool all)
993 {
994 QMemoryManagerFont* mmf = (QMemoryManagerFont*)refFont(f);
995 savePrerenderedFont((FontID)mmf,all);
996 }
997
998 void QMemoryManager::savePrerenderedFont(FontID id, bool all)
999 {
1000 QMemoryManagerFont* mmf = (QMemoryManagerFont*)id;
1001
1002 if ( !mmf->renderer ) {
1003 qWarning("Already a ROM font");
1004 } else {
1005 if ( !mmf->tree )
1006 mmf->tree = new QGlyphTree(32,32,mmf->renderer); // 32 = " " - likely to be in the font
1007 if ( all ) {
1008 int j=0;
1009 //qDebug("Rendering %s",fontFilename(mmf->def).ascii());
1010 for (int i=0; i<=mmf->renderer->maxchar; i++) {
1011 if ( mmf->renderer->inFont( i ) ) {
1012 mmf->tree->get( i, mmf->renderer );
1013 if ( !(j++ & 0x3f) ) {
1014 // XXX keep it from becoming degenerate - should be in QGlyphTree
1015 mmf->tree->compress();
1016 QGlyphTree::balance(mmf->tree);
1017 }
1018 }
1019 }
1020 }
1021 mmf->tree->compress();
1022 QGlyphTree::balance(mmf->tree);
1023 //qDebug("DUMP..."); mmf->tree->dump();
1024 QFile f(fontFilename(mmf->def));
1025 f.open(IO_WriteOnly);
1026 f.writeBlock((char*)&mmf->fm,sizeof(mmf->fm));
1027 mmf->tree->write(f);
1028 }
1029 }
1030 #endif
重点关注1010行到1019行
里面中的for循环的i变量就是unicode编码对应。
如果你只是要大小写字母,你可以将for循环改成下面这样
for (int i=0x21; i<=0x7e; i++) //大小写字母和英文标点符号uincode编码是从0x21 到 0x7e
{
if ( mmf->renderer->inFont( i ) )
{
mmf->tree->get( i, mmf->renderer );
if ( !(j++ & 0x3f) )
{
// XXX keep it from becoming degenerate - should be in QGlyphTree
mmf->tree->compress();
QGlyphTree::balance(mmf->tree);
}
}
}
上面的循环就是生成只包含大小写字母和英文标点符号的qpf字库了。
现在我们的目标是生成指定汉字的qpf字库.
现在就以我修改的代码为例进行说明。
把qmemorymanager_qws.cpp中998-1029行中 void QMemoryManager::savePrerenderedFont(FontID id, bool all)
实现代码修改成如下代码:
void QMemoryManager::savePrerenderedFont(FontID id, bool all)
{
QMemoryManagerFont* mmf = (QMemoryManagerFont*)id;
if ( !mmf->renderer )
{
qWarning("Already a ROM font");
}
else
{
if ( !mmf->tree )
mmf->tree = new QGlyphTree(32,32,mmf->renderer); // 32 = " " - likely to be in the font
if ( all )
{
int j=0;
qDebug("Rendering %s",fontFilename(mmf->def).ascii());
//for (int i=0; i<=mmf->renderer->maxchar; i++) {
printf("mmf->renderer->maxchar=%u/n", mmf->renderer->maxchar);
for (int i=0x21; i<=0x7e; i++)//大小写字母和英文标点符号uincode编码是从0x21 到 0x7e(这是一般字库都要有的)
{
if ( mmf->renderer->inFont( i ) )
{
mmf->tree->get( i, mmf->renderer );
if ( !(j++ & 0x3f) )
{
// XXX keep it from becoming degenerate - should be in QGlyphTree
mmf->tree->compress();
QGlyphTree::balance(mmf->tree);
}
}
}
//把你需要的汉字加入到 tmp 中去,这样一来就可以实现从ttf中提取指定汉字的qpf字库了.
//本人的方法笨就笨在这里,每次添加了汉字就要重新编译qte库和makeqpf
QString tmp("开始停止登录退出参数设置户代码用户密码提示信息你输设置图像摄像图片预览图片浏览摄像状态信息设备状态通道检测");
//注意现在只能处理utf-8的汉字编码, 我是由汉字的utf-8编码来获取汉字unicode编码关于utf-8, 参考utf-8的相关信息
unsigned int len = tmp.length();
const char *ch = tmp.latin1();
unsigned int n;
unsigned int count = 0;
printf( "len=%u/n",len );
for( n=0; n<len ; )
{
if( ( (unsigned char)ch[n] ) >> 4 == 0xE)//0xE开始是汉字 汉字3字节
{
unsigned int sp1=0;
unsigned int sp2=0;
unsigned int sp3=0;
//以下是utf-8 转换成 unicode编码的过程
//分别获取三个字节,分别转换,然后三个相加就是所需的汉字unicode编码
sp1 =(unsigned int)( (unsigned char)ch[n] - 0xe0 );
sp2 =(unsigned int)( (unsigned char)ch[n+1] - 0x80 );
sp3 =(unsigned int)( (unsigned char)ch[n+2] - 0x80 );
//printf( "0x%x 0x%x 0x%x /n", sp1, sp2, sp3 );
sp1 = sp1 << 12;
sp2 = sp2 << 6;
sp3 = sp1 + sp2 + sp3;
//printf( "0x%x/n/n", sp3 );
if ( mmf->renderer->inFont( sp3 ) )
{
mmf->tree->get( sp3, mmf->renderer );
if ( !(j++ & 0x3f) )
{
// XXX keep it from becoming degenerate - should be in QGlyphTree
mmf->tree->compress();
QGlyphTree::balance(mmf->tree);
}
}
n += 3;
count++;
}
else if ( ( (unsigned char)ch[n] ) >> 7 == 0x0 )//单字节
{
n ++;
}
else if( ( (unsigned char)ch[n]) >> 5 == 0x6 )//双字节
{
n += 2;
}
}
}
mmf->tree->compress();
QGlyphTree::balance(mmf->tree);
//qDebug("DUMP..."); mmf->tree->dump();
QFile f(fontFilename(mmf->def));
f.open(IO_WriteOnly);
f.writeBlock((char*)&mmf->fm,sizeof(mmf->fm));
mmf->tree->write(f);
}
}
以上就是修改代码的过程
9.修改buildqte shell脚本.
把buildqte脚本中的下面一行注释掉
./configure -qt-gfx-transformed -qvfb
./buildqte 重新编译qte库
10.编译makeqpf工具(qte用这个工具来生成qpf字库).
注意:makeqpf一定要编译成qte的版本,不能编译成x11版本
cd /qte/qte/tools/makeqpf
../../bin/qmake -project
../../bin/qmake
make
(请按上面的命令执行)
11. 修该fontdir文件
fontdir文件一般默认在/usr/lib/qt-3.1/lib/fonts/目录下
具体修改,应该大家都清楚怎么做了.
下面是我的fontdir文件的内容
unifont unifont_160_50.qpf QPF n 50 160 su
#simfang simfang.bdf BDF n 50 140 su
#simfang simfang.ttf FT n 50 140 su
#simfnag simfang.pcf PCF n 50 140 su
#simfang simfang_140_50.qpf QPF n 50 140 su
#simfang simfang.ttf FT n 50 120 su
#simfang simhei_120_50.qpf QPF n 50 120 su
#simhei simhei.ttf FT n 50 140 su
#simhei simhei_140_50.qpf QPF n 50 140 su
#simkai simkai.ttf FT n 50 130 su
#simkai simkai_130_50.qpf QPF n 50 130 su
#
zysong zysong.ttf FT n 50 120 su
zysong zysong_120_50.qpf QPF n 50 120 su
12. 最后就是运行makeqpf生成qpf字库.
makeqpf可以在framebuffer中运行,也可以在qvfb中运行.
运行后你可以去看看自己的成果了,字库是不是小了.
后记:
字库提取的问题困扰了很久,我只用几百个汉字,但是每次生成的字库很大,对嵌入式来说存储空间是很不划算的.现在我的办法有点笨,但是总算能解决这个问题了,所以第一时间给大家共享出来,希望也能帮上你的忙.也希望大家提出建议和指出错误,如果有有心人,还希望能够在完善这个过程.(因为本人的办法确实还是比较麻烦.)