zoukankan      html  css  js  c++  java
  • 【Qt开发】【ARM-Linux开发】 QT在ARM上显示字体的问题

    在PC机上利用QT开发的应用程序在设置字体时,在PC上运行,可根据自己的设置,字体随之变大或变小。而移植到ARM上运行时发现,显示体与所设置的字体不用,字体普遍偏小。经过上网搜索发现,是环境变量字库目录($FONTDIR)下没有相应的字库文件。
    我利用的是OK6410开发板,板子里已有移植好的QT4.7.1,然后其字库目录是/opt/qt4.7.1/lib/font.(编写在脚本/bin/qt4)。

    在嵌入式linux及QT/Embedded-2.3.7下制作QPF字库
    1、几种格式字库简介

     QT支持四种格式的字库(TTF,BDF,PFA/PFB,QPF)(见参考文献[3]),但在产品中,如果直接使用,TTF或PFA/PFB。即让应用程序在显示的时候再计算点阵,最终的效果并不理想,会发现有些字大,有些字小,而且需要占用非常多的FLASH和内存,速度也有点慢,所以我在此不想过多的介绍PFA/PFB。如果直接使用BDF,速度非常慢,而且需要占用比较多的FLASH和内存;使用QPF,速度和占用其它资源是最小的,因此我们最终的产品中采用QPF格式。下面我简单介绍,TTF,BDF和QPF字体的结构,这样就比较容易理解后面的转换过程。

    1.1  TTF字体
       TTF(TrueType Font)是Apple公司和Microsoft公司共同推出的字体文件格式,随着Windows的流行,已经变成最常用的一种字体文件表示方式。TTF字体已经成功用在Windows中文版生成汉字字库,此字体采用二次B样条曲线来描述字符轮廓,对字符轮廓的上的点,按顺时针方向从小到大编号,填充部分在其右边。TTF文件结构分成三个部分:文件名(12Bytes),描述表目录(每个16Bytes),描述表数据。
        对于每一个字,都有一个假想的矩形框,正常情况字是不会超出这个矩形框的,中文属于象形字,不象英文,大小不一致,比如:英文中的f就可能会超出矩形框。微软把矩形的高度称为EM,实际字符的高度称为BODY.矩形框最原始的坐标系是矩形的中心为原点,但为了实际字体在打印和显示的使用过程中的方便,通常将坐标原点放在左下角,或中下。
        通常,在实际打印过程中,TTF字体是用像素来度量的,如何将矩形框中的字体转成像素呢?有一个计算公式,实例如下:如果18个点的72点每英寸屏下有一个550的长度,矩形框内有2048个单位。那其像素为550*18*72/72/2048=4.83像素。显然,每英寸里的点取的越多,字就越逼真,同时这样的存储空间和计算的时间也就越多。在嵌入程序开发过程中,这往往是不可以接受的,因为嵌入式系统的硬件资源本来就很有限,如果真的这样的话,在显示过程会很慢。并且如果为了提高速度而减少每英寸中的点数,则字体失真的情况很严重。更加具体的关于,TTF字体的内容可见参考资料[3]。
        另外,在Windows下编程,Microsoft实现了让用户对字体操作处理具有透明性,有关字体结构定义见参考文献[4]。

    1.2 BDF字体
        BDF(Bitmap Distribution Format)是在X窗口系统中的一种表示位图字体的文件格式。是X协会定义的一种标准,是ASCII文件 它由两部分组成,一是表示字体整体属性的文件头信息;二是每一个字符独有的属性和位图数据。我以16*16的位图字体为例描述BDF字体文件格式。
    STARTFONT2.1 /*后面跟一个版本号,指出该字体文件版本*/
    COMMENT /*表示注释*/
    FONT -adobe -courier -bold -r -normal -16 -160 -75 -75 -m -160 -gb2312.1980 -0 
    /*表示字体名*/
    SIZE 16 75 75 /*字符大小与在X,Y方向上的分辨率*/
    FONTBOUNDINGBOX 16 16 0 0 /*X方向上宽度与Y方向高度及x和Y方向上的偏移*/
    STARTPROPERTIES 16 /*设置字体的属性项目数*/
    FOUNDRY "Adobe" /*字体的制造厂家*/
    FAMILY_NAME "Courier" /*字体的变种字型*/
    WEIGHT_NAME "Bold" /*字体的印刷权*/
    SLANT "R" /*字体字型的设计情况*/
    SEWINDTH_NAME "Normal" /*字体的缩放因素*/
    ADD_STYLE_NAME "" /*唯一的标识该字体,一般为空*/
    PIXEL_SIZE 16 /*依赖于设备的字体尺寸*/
    POINT_SIZE 160 /*设计字体的实际尺寸*/
    RESOLUTION_X 75 /*设计字体的水平分辨率*/
    RESOLUTION_Y 75 /*设计字体的垂直分辨率*/
    SPACING "m" /*指出字符宽度是定长还是可变*/
    AVERAGE_WIDTH 160 /*字体中所有字符的平均宽度*/
    CHARSET_REGISTRY "gb2312.1980" /*字符集名*/
    CHARSET_ENCODING "0" /*字符集编号*/
    FONT_DESCENT 0 /*基线下的高度*/
    FONT_ASCENT 16 /*基线上的高度*/
    ENDPROPERTIES /*属性项设置结束*/
    CHAR 6775 /*字体文件中的字符数*/
    STARTCHAR 啊 /*字符起始标志及名称*/
    ENCODE 3021 /*X服务器在存取该字符时使用的编码。如汉字国标码*/
    SWIDTH 1000 0 /*X和Y方向上的逻辑宽度和高度*/
    DWIDTH 16 0 /*字符在x和Y方向上的设备单位宽度*/
    BBX 16 16 0 0 /*字符边界框的宽度,高度以及偏移*/
    BITMAP /*字符的位图的信息起始标志*/
    0000 /*字符位图*/
    04a0
    ……
    0590
    ENDCHAR /*字符结束标志*/
    STARTCHAR 阿 /*第二个字符开始*/
    ……
    ENDFONT /*BDF字体文件结束标志*/

    1.3 QPF字体简介
        QPF格式的字库是仅用于QT/Embedded的不可缩放的字体,在程序运行过程中,对TTF格式的字体,在第一次装入使用时,都要以给定的字体大小进行处理;而对于BDF字体,当其使用时,所有字体都必须被处理;而对于QPF字体,均以相同格式的存储。所以在字体显示时,Qt只要读取字体,做相应分析,然后显示就完成了,这样进一步减少了对RAM资源的浪费。QPF字体是基于UNICODE编码的,这为QT/Embedded良好的可移植性奠定了基础。有关QPF更详细的资料可以查阅参考文献[5]。

    1.4FONTDIR目录下的文件格式:
    一个典型的FONTDIR文件的内容如下所示:
    fixed fixed_120_50.qpf QPF n 50 120
    helvetica helvetica_80_50.qpf QPF n 50 80
    helvetica helvetica_120_50.qpf QPF n 50 120 u
    helvetica helvetica_120_75.qpf QPF n 75 120 u
    helvetica helvetica_140_75.qpf QPF n 75 140
    helvetica helvetica_180_75.qpf QPF n 75 180
        文件中每行都标识一个特定的字库,每个段的含义是:第一列为name,第二列为file,第三列为renderer,相当于字型格式,所以有BDF,TTT,QPF等选择。第四列n表示iitalic,表示是否为斜体字。第五列表示weight,其中50表示Normal,75表示Bold。第六列表示size,例如:120表示12pt。第七列为flags,有下面三个选择:s=smooth(anti-aliased)u =unicode range when saving (default is Latin 1 a = ASCII range when saving(default is Latin 1))
                                                             —— 摘自参考文献[1]
        其中属性file,renderer(BDF,TTF,QPF)和size特别要设置对,其它属性问题不大。还要注意如果在该目录下有QPF的文件,系统只会使用QPF格式的文件,而不会读取其它格式的文件,不管FONTDIR里面的内容是什么。如果有多个QPF文件,应用程序按照大小,家族,黑体和斜体的顺序查找,即首先查找大小和自己一样的字库,大小无法区分唯一的字库的再看对应的家族,还是无法区分的再看是否黑体,是否斜体。可以参考PC上的字库索引文件FONTDIR:
    例如:
    -cclib -song -medium -r -normal -jiantizi -16 -160 -75 -75 -c -160 -gb2312 1980 -0
    其中,每个段的含义如下:
    cclib:制造商
    song:字体族,此处表示“宋体”字
    medium:字权重(中等),还有bold(粗体)选项
    r:倾斜,R(Roman),I(Italic),O(Oblique)
    normal:字符集宽度,此外还有condensed,narrow,double
    jiantizi:附加说明(此处意义为“简体字”)
    16:用像素衡量的宽度。
    160:点数 10
    75(1):水平分辨率(dpi)
    75(2):垂直分辨率(dpi)
    c:间距。c:square,m:fixed width,p:variable width
    160:平均宽度(10*pixels)
    gb2312.1980:注册字符集,标准名
    0:第0套,基本集
                                                          —— 摘自参考文献[2]

    2 如何从TTF字体文件转成QPF字体文件
    2.1 把TTF转换成BDF
        尽管不推荐使用TTF格式的字库,但由于TTF格式的字库可以转换成任意大小的BDF字库,而可以找到的BDF字库都是固定大小的,因此在实际制作QPF字体文件时,还是需要TTF格式的字库。把TTF转换成BDF的方法如下:
    ./ttf2bdf source.ttf -p yourSize -o destination.bdf
        即利用软件ttf2bdf可以把源文件source.ttf转换成大小为yourSize的BDF格式的文件destination.bdf。那在程序内部是如何实现将TTF转成BDF的呢?由2.2.1和2.2.2的介绍,并且查阅参考文献[3],可以知道TTF的内部存储结构。其中最核心的部分是TTF文件格式中的12个字节的文件表:表目录按tag以升序排列。


    Type Name Description
    ULONG  tag 4字节的标识
    ULONG checkSum 表中的CheckSum
    ULONG offset TrueType font文件的起始偏移量Offset
    ULONG length 表长


        还有一个有关Offset表的信息,包括版本号,表的数量,查找范围。入口选择,转换范围。
        通过操作文件表,将描述表中的数据取出来,按照BDF字体所定义的格式写入,就可以生成对应的字体。比如,可以给出一小段c语言程序,此程序用于计算当前CheckSum的位置。
    ULONG
    CalcTableChecksum(ULONG *Table,ULONG Length)
    {
    ULONG Sum = 0L;
    ULONG *Endptr=Table+((Length+3)& ~3)/sizeof(ULONG);
    while(Table<endptr)
     Sum += *Table++;
    return Sum;
    }
        利用此程序可以将每个字体的信息分开,并将每个字体信息从文件中取出来,对每个字体进行操作。在从TTF转到BDF过程中,仅通过使用WINDOW的函数是很不方便的,最方便的办法是使用c语言对字体进行操作。我在此列出转换过程中最重要的几个值:PIXEL_SIZE,POINT_SIZE,RESOLUTION_X,RESOLUTION_Y,FONT_DESCENT,FONT_ASCENT,SWIDTH,DWIDTH,BBX。这些值决定着最后生成的BDF字体与TTF字体的失真度,因为TTF是可缩放的,而BDF是固定大小的,所以在转换过程中一定会出现失真的情况。

    2.2 对得到的BDF文件进行调整
        由于从软件xmbdfed里得到的三个字库不符合系统的要求,因此需要手动对其进行一些调整,其它方法得到的BDF文件不需要进行调整。
    直接从xmbdfed里得到的字库是按照GB2312-80.0进行编码的,因此首先要将其转换成符合UNICODE编码,这需要用到我手动写的一个程序gb2unieode,把源文件拷贝到gb2unicode程序的目录下,将其名字改为hanzist24a.txt(程序的要求,也可以不改名字而修改程序里源文件的名字再重新编译),然后运行./change之后,就完成了,因为QT中因现成的转换函数可以调用,在此我不想多说关于用程序转换的代码。因为有一种更简单的方法,即在Word中打开GB字库。然后另存为UNICODE字库文件就行了。
        等待该命令执行完后(注意需要的时间比较长),对所生成的文件dest.txt进行排序,方法如下:把dest.txt的名字改为dest.bdf(只要后缀名为bdf就行);然后运行软件xmbdfed,用其打开该文件(xmbdfed只能直接打开后缀明为bdf的文件),另存为yourname.bdf即可。排完序后需要手
    动修改处理yourname.bdf。
        由上面介绍的BDF字体格式,现在对转换得到的BDF字体文件进行如下手工处理:先删除行说明属性默认的字符的行DEFAULT_CHAR 8481,因为从GB2312转换成UNICODE后,编码为8481的字符已经不存在了,如果不删除该行,运行时会出现段错误。然后删除那些存在于两个16号字库但在标准GB2312中没有的一些字模。标准GB2312中有7445个字模,而两个16字库有7612个字模,多出来的那部分在用gb2unicode转换的时候因为找不到相应的GB2312码而没有写进dest.txt的字模开始行"STARTCHAR ****"。可以用xmbdfed打开,如果出错则说明还有一些不完整的字模,还需要手动删除这些不完整的字模,如果可以正确打开则说明目标文件已经是正确的。(24号字库不存在这个问题,因此不需要进行本项操作。)最后从其它大小相同的.bdf文件中拷贝编码为20-7F(ASCII码)和编码为FE54(分号;)的字模到目标文件。

    2.3 把TTF.BDF转换成QPF
        从参考文献[5]中可以知道QT提供的把TTF,BDF转换成QPF的方法有两种,一种是工具makeqpf,这个命令无论是在Pc机上还是在开发板上都没有效果(但是QT的官方网站却说是可以的,不知道为什么,并且,也不是每个版本的QT都有makeqpf这个工具,还要说明的是,文献[6]中说这个工具是可以的,不过我没有试验成功)。
        另一种是运行应用程序时加上选项-savefonts,如在开发板上运行应用程序的命令:./sulfur -qws -savefonts
        如果此时系统中/usr/qt/lib/fonts目录下没有QPF格式的字库而只有TTF或BDF格式的字库,对应的文件fontdir中只保留要转换的文件的行,QT就会在运行时首先生成QPF格式的字库。尽管通过TTF字体也可以得到QPF字体,不过最好不要这样做,因为失真太大。

    3 总结
        经过本人对字库进行上述处理后,就可以在嵌入式开发板上显示各种字体,只要找到给定TTF或BDF的字库,如果找不到BDF字库,可以找到字库,然后通过TTF2BDF这个程序来得到BDF字库。这样在开发板上可以显示行书,楷书等字体,并且字体显示也很正常,不会出现大小不一致的问题。但是,本人认为,由于这是嵌入式开发,存储资源非常有限,如果能进一步将字库缩小,只留下程序中所要用的汉字组成的字库就好了,这是需要下一步研究的方向。

    参考文献:
    [1] http://jserv.Sayya.org/qtopia/doe/qte.txt
    [2] http://www.Linuxfans.org/nuke/modules.php?name=Forums&file= viewtopic&t=82950&highlight=fonts.dir
    [3] Microsoft Corporation. TTF Technical Specification Revision 1.66 November 1995.
    [4] Windows 2000编程核心技术精解[M].中国水利水电出版社,2001.
    [5] http://doc.Trolltech.com/3.0/emb-fonts.html
    [6] 黄敬群.Qt/Embedded中文处理实战[M]

    为什么在ARM板上字体会变小
    因为QT在ARM板上计算DPI值错误。

    解决的方法就是设置好qt的dpi,

    qt是根据显示器的物理长度或者宽度于分辨率的关系来计算dpi的, 所以我们设置export QWS_DISPLAY="LinuxFB:mmWidth95:0" export QWS_SIZE="480x272" 这样qt在所有的平台上显示的字体都一样大了 就好了,


    什么是DPI值呢?每英寸的像素点数,即单位点/英寸。网络上有好多写ppi与dpi区别的帖子,没大看懂,也无关紧要。我的显示屏的分辨率即(设备分辨率)是800*600;水平分辨率是800,垂直分辨率是600,显示器物理宽度16cm,物理高度12cm,
    则由式:


    将qt是根据显示器的物理长度或者宽度于分辨率的关系来计算dpi的。dpi的计算如下:

    水平 dpi = 水平 resolution /(显示器宽度cm/2.54 )

    垂直 dpi = 垂直 resolution /(显示器高度cm/2.54 )
    1英寸=2.54cm,可知,我的水平DPI和垂直DPI都是127.

    在QFont当中有两种方式设置字体大小,一种是PixelSize,另一种是PointSize
    Point实际是磅,也就是 1/72 inch
    我们可以从QPaintDevice中得到当前DPI(Dot per inch)。因此 Pixel = DPI * Point / 72
    例如设置字体为20Point。 那么字体的像素大小是 90 * 20 / 72 约等于 25.而DPI=120时,同样20point,像素增大,字体变大。可以将字体设置为25Pixel,看到字体的大小就是20Point和25Pixel大小是一样的。
    所以在同一显示屏下,
    屏幕大小就固定了,其显示能力也固定了,也就是说显示器横向、纵向能够显示的像素点是有上限的,如果显示器的最佳显示分辨率为1440×900,那么该显示器横向上最多也就1440个像素点,再高就不行了!是硬件限制。纵向上也是如此。也就是从硬件上讲(或者说是物理上),显示器的物理DPI出厂后就确定了!包含两个固定值:

    1)屏幕尺寸,如14.1英寸

    2)分辨率上限,如1440×900

    所以,每像素的尺寸(1Pixel=?Inch)是个常量(屏幕上一定范围的区域)。因而,设置的DPI值越大,字体越大。
    而你设置的屏幕分辨率(横、纵方向像素个数)越高,则能看到的东西越多,但是东西看起来越小(高分辨率相当于用圆珠笔画画,笔画细;低分辨率相当于用毛笔画画,笔画粗!



    QFont当中设置的大小,实际上字体的高度,由如下组成。
    pixel pitch(点距): 像素点间距离。可以用25.4mm / DPI得到。
    字体在屏幕上的实际大小 = 字体像素大小 * 点距=(显示屏DPI*Point/72)*(25.4/设置DPI)
    因此如果想要看到更大的字体可以使用更大的点距。 有些显示器的设置分辨率(dpi)比较大,那么单位长度中的像素点就比较多,这样一个字所占的长度就会比较少。

    参考资料:http://hi.baidu.com/xf19890224/item/46dfe33a31989f05ceb9fe33
                    http://blog.csdn.net/kelleniiii/article/details/8968327

    设置环境变量改变DPI


    如果直接使用qt4的桌面环境,所以直接进qt4桌面的启动脚本进行环境变量的修改。

    关于设置的格式,在前面给的链接里有提到:

    export QWS_DISPLAY="[:]... [:]"

    具体的操作如下:

    vi ./bin/qt4

    将原始的export QWS_DISPLAY=:1注释掉,在这一行的开头添加一个#即可。

    添加:

    export QWS_DISPLAY="LinuxFB:mmWidth=230:0" 

    export QWS_SIZE="800x600"

    其中mmWidth=230是自己根据实际的需要进行调整的。数字越大,显示出来的字体越小。

    下面的QWS_SIZE是我的开发板上的触摸屏的分辨率。

    Qt 嵌入式 Linux显示管理

    Qt的显示管理,Qt嵌入式客户端默认的行为是将其器件写入内存, 而服务器负责把内存的内容存储到屏幕上。服务器使用屏幕驱动器将内存的内容复制到屏幕显示。

    当服务端应用程序开始启动时,使用Qt的插件系统,屏幕驱动被加载

    内容:


    可用的驱动

    嵌入式LinuxQt为Linux framebuffer提供的驱动, 有virtual framebuffer, transformed screens, VNC servers和 multi screens. 运行configure脚本可以列出可用的驱动:

     ./configure -help

    在Qt的默认配置文件中,只有一个未加速的Linux framebuffer driver (/dev/fb0) 是可用的.通过使用configure脚本可以让各种驱动可用或禁用. 例如:

     ./configure -qt-gfx-transformed

    通常的屏幕驱动能通过QScreen的子类和创建的屏幕驱动插件(从QScreenDriverPlugin类继承)被执行. QScreenDriverFactory类默认执行将自动检测插件,在运行期加载驱动到服务端应用程序.


    指定一个驱动

    通过设置QWS_DISPLAY环境变量去指定使用哪个驱动,例如 (假设当前的shell是bash,ksh,zsh或者sh):

     export QWS_DISPLAY="[:]...
              [:]"

    的有效参数是LinuxFbQVFbVNCTransformedMulti 和 keys identifying custom drivers,  的参数通常是用来区分是否是相同的显示屏和是否支持多显示屏(更多详细信息请察看运行Qt嵌入式Linux应用程序文档). 驱动的详细选项描述请参看下面表格.


    驱动详细选项 可用的 描述
    tty= LinuxFb 程序运行时传送至控制台的设备文件.
    nographicsmodeswitch LinuxFb 确定应用程序没有在图形模式下.
    littleendian LinuxFb 在大端系统下告诉驱动必须处理小端的frame buffer.
    mmWidth= LinuxFb, QVFb 屏幕的物理宽度 (通常是按分辨率计算).
    mmHeight= LinuxFb, QVFb 屏幕的物理宽度 (通常是按分辨率计算).
    LinuxFb
    VNC, Transformed, Multi 指定一个子驱动.
    Transformed 指定屏幕的旋转. x的有效值为90, 180和270.
    offset= Multi 指定子窗口左上角的坐标系(默认0,0).

    当运行应用程式时,也可以通过 -display选项设置QWS_DISPLAY环境变量.例如:

     myApplication -display "[:]...
              [:]"

    子驱动和多驱动

    VNC, Transformed和Multi screen drivers都依赖子驱动. 指定一个驱动的一般方法如下::

     export QWS_DISPLAY="[:][:]...
             [ :]"

    就子驱动而言, 在每个子驱动和显示的数目去区分多个驱动和显示器是很重要的. 多屏幕驱动可以有多个子驱动,例如:

     export QWS_DISPLAY="Multi: QVFb:0 QVFb:offset=640,0:1 :2"

    如果子驱动没有被指定,VNC screen驱动被默认为一个虚拟屏驱动.在这种情况下VNC驱动允许指定一些额外的参数(可选的),默认虚拟屏的尺寸和深度:

    • size=
    • depth=
    • mmHeight=
    • mmWidth=

    运行一个尺寸为720x480像素32位的VNC虚拟屏的例子如下:

     export QWS_DISPLAY="VNC:size=720x480:depth=32"

    在Linux framebuffer上运行VNC屏驱动的例子:

     export QWS_DISPLAY="VNC:LinuxFb"

    在这最后一个例子, Qt同时使用两个显示驱动, 设备屏幕和通过网络的VNC客户端的显示的显示输出.

  • 相关阅读:
    修复PLSQL Developer 与 Office 2010的集成导出Excel 功能
    Using svn in CLI with Batch
    mysql 备份数据库 mysqldump
    Red Hat 5.8 CentOS 6.5 共用 输入法
    HP 4411s Install Red Hat Enterprise Linux 5.8) Wireless Driver
    变更RHEL(Red Hat Enterprise Linux 5.8)更新源使之自动更新
    RedHat 5.6 问题简记
    Weblogic 9.2和10.3 改密码 一站完成
    ExtJS Tab里放Grid高度自适应问题,官方Perfect方案。
    文件和目录之utime函数
  • 原文地址:https://www.cnblogs.com/huty/p/8517590.html
Copyright © 2011-2022 走看看