• 13323阅读
  • 4回复

qte-从ttf字库中提取指定汉字生成qpf字库的笨办法 [复制链接]

上一主题 下一主题
离线lzwwiner
 

只看楼主 倒序阅读 楼主  发表于: 2007-04-23
— 本帖被 shiroki 执行取消置顶操作(2007-12-12) —
qte-从ttf字库中提取指定汉字生成qpf字库的笨办法

作者:刘志文  2007/4/23  Email:lzwwiner@163.com

这篇文档大家可以随意转载.我本人只保留所有权.转载请注明作者和出处.


解决思路: 获取汉字的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中运行.
    运行后你可以去看看自己的成果了,字库是不是小了.

后记:
    字库提取的问题困扰了很久,我只用几百个汉字,但是每次生成的字库很大,对嵌入式来说存储空间是很不划算的.
    现在我的办法有点笨,但是总算能解决这个问题了,所以第一时间给大家共享出来,希望也能帮上你的忙.
    也希望大家提出建议和指出错误,如果有有心人,还希望能够在完善这个过程.(因为本人的办法确实还是比较麻烦.)

    这篇文档大家可以随意转载.我本人只保留所有权.转载请注明作者和出处.
    2007/04/22
    刘志文
    lzwwiner@163.com
[ 此贴被lzwwiner在2007-05-08 14:24重新编辑 ]
还没有更好的签名!
离线onebyte

只看该作者 1楼 发表于: 2007-06-11
好文章!!!
多谢志文兄!!
离线w0zg
只看该作者 2楼 发表于: 2007-06-11
呵呵,很好的文章啊,谢谢
linux: RedHat 9.0
离线youngst

只看该作者 3楼 发表于: 2007-12-02
学习啊
离线shiroki

只看该作者 4楼 发表于: 2007-12-12
真不错
--
shiro is White
ki is tree
http://www.cuteqt.com
论坛 http://www.cuteqt.com/bbs
博客 http://www.cuteqt.com/blog
博客镜像: http://sites.cuteqt.com/cuteqt
Linux/Qt/嵌入式讨论群 http://qun.qq.com/air/5699823
快速回复
限100 字节
 
上一个 下一个