分类: LINUX
2008-03-05 18:06:55
GTK+(基于DirectFB)的字体绘制是通过pango+freetype+fontconfig三者协作来完成的,其中,fontconfig负责字体的管理和配置,freetype负责单个字符的绘制,pango则完成对文字的排版布局。这几天花了点时间研究GTK+的字体处理,这里记录一些笔记。
用pango排版文字,首先要创建一个PangoLayout对象。一般来说,不必直接调用pango_layout_new函数创建,而是调用gtk提供的包装函数gtk_widget_create_pango_layout。在该函数又调用gtk_widget_get_pango_context函数获得PangoContext对象。
什么是PangoContext呢?顾名思义,PangoContext就是用来保存Pango上下文信息的。其中最重要的是PangoFontMap对象,从字面上讲PangoFontMap是从字体描述到字体之间的映射,它的实际功能就是加载字体。它根据字体的描述,调用fontconfig的函数找到对应的字体文件,然后调用freetype的函数加载该字体。
l 字体的初始化。
字体的初始化并不是在gtk_init函数完成中,而是在实际使用字体时才初始化。调用路径为
u gtkwidget.c: gtk_widget_get_pango_context
u gtkwidget.c: gtk_widget_create_pango_context
u gdkpango-directfb.c: gdk_pango_context_get
u pangoft2-fontmap.c: pango_ft2_get_context
u pangoft2-fontmap.c: pango_ft2_font_map_for_display
u pangoft2-fontmap.c: pango_ft2_font_map_new
u ftinit.c: FT_Init_FreeType
初始化并不加载任何字体,字体的加载是根据需要,要任何时候都可以加载。在初始化时,主要是加载freetype的后端。内置的Freetype后端是在ftmodule.h里定义的,可以在此进行裁减,默认内置的后端有:
FT_USE_MODULE(autofit_module_class)
FT_USE_MODULE(tt_driver_class)
FT_USE_MODULE(t1_driver_class)
FT_USE_MODULE(cff_driver_class)
FT_USE_MODULE(t1cid_driver_class)
FT_USE_MODULE(pfr_driver_class)
FT_USE_MODULE(t42_driver_class)
FT_USE_MODULE(winfnt_driver_class)
FT_USE_MODULE(pcf_driver_class)
FT_USE_MODULE(psaux_module_class)
FT_USE_MODULE(psnames_module_class)
FT_USE_MODULE(pshinter_module_class)
FT_USE_MODULE(ft_raster1_renderer_class)
FT_USE_MODULE(sfnt_module_class)
FT_USE_MODULE(ft_smooth_renderer_class)
FT_USE_MODULE(ft_smooth_lcd_renderer_class)
FT_USE_MODULE(ft_smooth_lcdv_renderer_class)
FT_USE_MODULE(otv_module_class)
FT_USE_MODULE(bdf_driver_class)
l 字体的加载。
程序第一次显示文字或者后面改变文字的字体,都可能会导致字体加载。在pango排版时,若对应的字体没有加载,它会调用pango_fc_font_map_load_fontset函数去加载该字体。
函数pango_fc_font_map_get_patterns会根据字体的描述,调用FcPatternBuild创建一个pattern。pattern是fontconfig的一个术语,fontconfig根据pattern的描述,在配置文件中查找最接近要求的字体,这是一种模糊查询,不必定义精确的查找条件, 它通常始终会返回一种相近的字体。
如果pattern对应的字体没有加载,则调用pango_fc_font_map_new_font加载该字体。转了N道弯之后,来到了pango_ft2_font_get_face函数,该函数从pattern中取出一些参数,其中最重要的是字体文件名,然后调用FT_New_Face真正加载字体。
l 字体的匹配。
如果没有fontconfig的帮助,要精确的指定字体参数是件痛苦的事情。那些神秘的参数很容易把人搞晕了,即使指定了正确的字体参数,换一个系统如果没有该字体,程序可能就完全不能运行了。而在fontconfig的帮助下,你只要说明对字体的要求,fontconfig总是可以帮你找到最接近要求的字体。
Fontconfig的主配置文件在/etc/fonts/目录里,也可以在~/.fonts目录下存放用户自己的配置信息。/etc/fonts/fonts.conf文件是默认的配置,一般不要修改它,但可以修改/etc/fonts/local.conf,fonts.conf包含了local.conf。
配置文件里的dir段指明了字体文件存放的路径,在小机上,字体的存放路径一般与PC上不同,所以可能要修改dir段,加入实际字体文件所在的路径。其它配置一般不用修改,使用默认的即可。
要注意,字体有三类,Serif、Sans serif和Monospace。“sans”来源于古法语, 意为“without”, 即“非”。 而“serif”来源于荷兰语, 译为衬线, 指字母的拐角或端点位置的修饰线。Serif和Sans serif是非等宽字体,而Monospace是等宽字体。通过下面的实际效果可以看出它们的差异。
Serif : E
Sans serif : E
Monospace: Courier
l 字体的显示。
pango_ft2_font_render_glyph函数调用了FT_Load_Glyph去加载字模数据,我想当然的 认为字体是在pangoft2-render.c中绘制的,而且GTK/GDK都不应该知道freetype的细节信息,否则两者之间的耦合就太死了。
但是事实是,Pango排版完成后,并没有管字体显示的问题。字体真正显示是在gdk_directfb_draw_glyphs函数里完成的,它调用_glyph_surface_cache_get_surface函数把freetype的字模bitmap拷贝到一个surface里,然后把该surface中的内容blit到窗口的surface上。
l 修改widget的字体。
修改widget的字体很简单,调用gtk_widget_modify_font函数即可。如:
PangoFontDescription *desc = pango_font_description_from_string("Serif 12"); gtk_widget_modify_font(widget, desc); pango_font_description_free(desc); |
l GTK+2.6 + DirectFB-0.9.22关于字体问题的BUG。
字体无法显示。Fontconfig配置正确,对应的字体文件也存在,但是字体就是显示不出来。原因在于,字体显示实际上中调用的函数gdk_draw_glyphs_transformed而不是gdk_draw_glyphs,而函数gdk_draw_glyphs_transformed在gdk-directfb中并没有实现,这导致了字体无法显示。解决方法:若不存在旋转字体,可以直接用gdk_draw_glyphs来实现gdk_draw_glyphs_transformed。
大部文字都带有下划线,界面显得很怪异。原因在于,glyphsurfacecache.c中的函数surface_new创建surface后没有把surface清理一下,里面有些垃圾数据,而字模又并不一定完全占满整个surface,所以这些垃圾数据也被Blit到窗口上了。解决方法:这可以在调用CreateSurface成功后,再调用一下Clear函数。
参考资料:
~~end~~