Xft字体库:体系结构及用户指南
Keith Packard
XFree86 Core Team, SuSE Inc.
keithp@keithp.com
本文由本站会员麦氏赛扬翻译,manux代为发表,原文排版非常漂亮,但是由于html代码问题,发到这里后只能勉强看懂原意,后面评论里面将会添加一个下载连接,感兴趣的朋友可以下载回去浏览。
摘要
X渲染扩展(X Render Extension)提供了一个新的基于客户方字形(glyph)和字体管理的字形渲染体系结构。这个扩展设计在解决了许多相关技术难题的同时,也把光栅化字体、配置字体以及定制字体使用的责任交给了每一个X客户程序。
编写Xft库是为了给X应用程序提供一个能访问FreeType字体光栅化引擎和X渲染扩展的、便于使用的接口,鉴于FreeType没有提供配置和定制字体的功能,Xft也担负了这一任务。Xft提供了新的字体命名约定、复杂而精密的字体匹配和选择机制,并对相关功能进行充分的抽象,从而使得一般应用程序既能够从使用X渲染扩展的文本输出获得益处,又能在不支持这一扩展的X服务器上正常工作。
1 引言
X渲染扩展[Pac01]把访问字体文件和生成字形图像的功能从X服务器移到了X客户一方。采用客户方字形管理的X应用程序在以下几个方面有优势:可以访问字体文件的所有细节,应用程序可以指定特有字体,渐增的光栅化处理(incremental rasterization),并且有可能与其他部件共享字体,例如打印机。此外,鉴于底层的渲染机制基于图像而非字形,字形的光栅化技术、乃至字体文件格式本身都不再依赖于X服务器的能力,所以现在新字体技术的集成速度可以跟得上独立应用程序的开发,而不必遥遥无期地等待新的X服务器增强技术。
当X服务器不再负责管理字体文件的访问和字形生成,就需要一个新的函数库在客户方完成相应的任务。由于X渲染扩展在设计上支持消锯齿(anti-aliased)图形,这个新的函数库需要支持高质量的消锯齿字形光栅化。
FreeType项目[TT00]开发了一个完整的字体光栅化引擎,不仅支持大多数轮廓字体格式,还支持标准的X PCF位图字体,X渲染扩展接收字形图像并使之在屏幕上显现。为了让应用程序能在屏幕上显现高质量的文本,所需要做的就是在FreeType和X渲染扩展之间放置一层薄薄的“粘合”代码。
对于不支持渲染扩展的X服务器,这个函数库还需要提供访问“核心”字体(使用原始X核心协议访问的字体)的能力,这就使得应用程序能在转向新函数库时仍然支持老式X服务器。
FreeType库没有指定如何定位字体文件,而是需要应用程序提供字体文件名,这就把配置和定制可用字体集合的负担放在了FreeType库以外,因此,这个新的“粘合”层也需要提供一些配置功能以便在桌面环境中应用。
2 X渲染扩展字形管理
X渲染扩展提出了几个简单抽象供应用程序管理字形。每个Glyph结构包括一个覆盖字形外形的alpha掩码(一个描述不透明值的矩形映象)、从 alpha掩码原点到名义字符原点的偏移量、到下一字形的位移(包括垂直和水平的偏移量),GlyphSet结构则包含了一个字形结构的集合,应用程序使用一个32位的索引对字形集进行编号。
应用程序绘制文本时,把一个GlyphSet标识符以及一系列针对该GlyphSet的索引发送到X服务器,X服务器通过对指定位置使用字形结构中的偏移量调整确定绘制位置,并渲染alpha掩码来完成对每个字形的处理,后续字形的绘制位置则是通过在当前原点加上位移向量实现。正如X核心协议中的 PolyText请求,在同一个请求中可以对字形序列作出调整位置、改变GlyphSet等变动,从而使得一个复杂的字符串在一次操作中完成渲染。
为了覆盖世界上更多的民族,操作系统支持的语言和区域集合不断扩展,伴随这种扩展,大多数字体中包含的字形数也大大增加,当今流行的轮廓字体中会包含几千个字形。十多年前,渐增式渲染字形被看作一种合理的优化,现在已成为各种字体机制中的基本组成部分,以尽可能减少每种字体占用的内存,并缩短访问一种新字体时所需的时间。X渲染扩展通过允许在需要时把一个Glyph加入已存在的GlyphSet,提供了这种渐增式渲染支持。由于在添加Glyph的过程中没有任何从X服务器到X客户的信息流,这一过程可以完全异步进行。这种异步性保证了即使面对一个高网络延迟的环境,仍有可接受的性能表现。
当应用程序传送它们需要显示的字形图像时,X服务器通过在任何可能情况下共享相同字形来节省内存。
3 FreeType库
FreeType项目的初衷是要构建一个自由的TrueType字体光栅化器。FreeType的第一版提供了与现有系统相当的高质量TrueType光栅化器,FreeType的第二版对内部结构进行了一般化以支持更多字体格式,除了支持Type-1、OpenType和CID等众多轮廓字体格式, FreeType现在还支持X的标准PCF格式(可移植编译格式)的位图字体。
FreeType不仅提供光栅化以及度量字形的接口,还提供存取字体文件内各种形式的字距调整和字形替换等表格的机制。这就在基础字体含有相应表格的前提下,使应用程序能够获得在各种区域中定位字形所必需的数据。
既然FreeType项目明确地要构建一个通用的字体函数库,在XFree86开发一个新函数库的负担就可以大大减轻,因为可以直接采用现有系统,并提供 “粘合代码”改变FreeType数据结构使之使用X渲染扩展的要求。这固然使得应用程序需要面对FreeType函数库可能的变化,但考虑到 FreeType是一个成熟的项目,相对于完全由XFree86开发一个新函数库的情形,这种变化的严重性大概会轻很多。
字体命名和配置不属于FreeType函数库,这些“杂务”交给了应用程序。考虑到FreeType应用于各种环境,有些甚至没有文件系统,为保证 FreeType得到最大程度应用并独立于系统策略,这种设计思想是适当的。提供这些支持成为Xft实现中最困难的部分,并且其中一部分可能很快就被替换。
4 XLFD命名
X核心协议规定了用非结构化字符串命名字体的方法,X逻辑字体描述(XLFD)[SG92]用于在字符串名格式中加入结构信息。在开发X时,用于桌面计算的轮廓字体还是一个相对新奇的事务,所以X核心协议和XLFD都是基于位图字体设计的,当围绕缩放字体命名的语法和语义加入XLFD时,基于XLFD的开发已经进行了相当长的时期。
XLFD中字体命名语法的意图在于仅通过名字就可以向应用程序提供足够的字体信息,这样就可以在不访问字体数据情况下,进行字体选择和字体列表表示。
XLFD还提供了使用包含“?”和“*”的名字打开字体的标准策略,使用这类名字时,选中的字体将是第一个匹配的字体,即使用相同模式请求列出字体时返回的第一个。不幸的是,X服务器保存字体名时为了高效搜索,会在各字体目录中进行内部排序,所以不能保证“*”的默认值是合理的。例如,当在字体名的 weight字段使用“*”时,X服务器会把bold字体列在normal字体之前。
这个策略真正失败之处在从point(点值)尺寸到pixel(像素)尺寸的映射。XLFD在字体名中分别提供了两个轴向上的pixel尺寸、point 尺寸和resolution(解析度),标准的X字体按照解析度分别存放,“75dpi”和“100dpi”下各自存放着与该解析度匹配的各种点值尺寸的字体,其他字体目录下一般是为了在75dpi屏幕光栅化。
协议指导X服务器按照在字体路径(译注:font path,应指配置文件中相应节)中出现的顺序去搜索字体目录,这就使字体路径决定了对解析度的倾向性。如果100dpi的目录列在前面,当应用程序在字体名的resolution字段用“*”时,只要在100dpi目录下存在匹配字体就会使用该字体,否则才去尝试75dpi的字体。
应用程序如果在字体名中仅指定point尺寸,而在resolution字段使用“*”,那么最终将会得到一组随即尺寸的字体:那些在100dpi目录下发现的字体按照100dpi屏幕光栅化,其他字体则按照75dpi屏幕光栅化从而会显得小一些。
最终的结果是XLFD的字体匹配充满了危险,应用程序经常列出所有可用字体(作出选择)然后提交完整XLFD字体名(译注:不含“?”和“*”)给X服务器。
XLFD的另一个问题是在字体名中包含了字形的平均宽度字段。对于需要在不同总体宽度的字体中进行选择的应用程序而言,这是个非常有用的信息,而且对位图字体也很容易计算。但是对轮廓字体,除非在指定尺寸下对每个字形进行光栅化计算,该字段值不能算出。仅仅列出一个特定尺寸下所有的可用字体就会导致光栅化每一个字体的每一个字形。
XLFD提供了关于可用字体的有用信息,出列平均宽度,这些信息都是容易计算并交付应用程序的。使用XLFD的应用程序应该在本地管理XLFD字体名,而不要依赖服务器方字体匹配,也就是通过列出可用字体收集信息,再利用这些信息构造完整字体名。
鉴于XLFD没有提供一种按照语义匹配的合理方案,需要有新方案允许在应用程序给定一组约束情况下,基础的字体系统能够定位一个适当的字体。这样的系统需要有足够的灵活性以便能够包含现在不能预料的新字体特性,也不需要应用程序完全指定字体的方方面面。
5 设计一个新函数库
Xft在三个方面与环境交互:通过编程接口与应用程序交互,通过配置文件与系统交互,通过让用户指定字体名与用户交互。虽然这三方面在函数库中紧密相关,但从设计角度来说,它们是分离的。
5.1 应用程序接口设计
Xft的首要目标是把FreeType的输出和X渲染扩展结合起来,但是,为了Xft能作为现有的Xlib文本输出例程的替代物而被人接受,其次要目标包括支持核心X字体,尽管这样做可能以损失应用程序功能为代价。
由于FreeType不提供字体选择功能,所以Xft的一部分要进行字体匹配。采用现有的XLFD机制会极大地限制字体匹配地能力,所以Xft提出了一种新格式。这种选择机制被设计为总能匹配某种字体,允许应用程序假设适当地字体存在,避免在每个级别上都要考虑失败回落。
另一个要求是函数库要提供合理地匹配,例如需要italic(斜体)字体时用oblique(倾斜)字体代替,在不指定weight要求时使用中等 weight的字体。这允许应用程序在指定字体时不必指定所有的特征,而且可以期望得到合理的结果。当应用程序的请求存在相互矛盾时,决定哪些特征更重要的政策能提供解决方案。
为了在函数库级别上简化可选的文本编码,所有的文本输出例程只接受Unicode编码的数据。其他的编码需要由应用程序负责转换,无论是在操作系统边界还是应用程序与Xft之间。由于大部分现存的字体一般都有Unicode编码(译注:指其内部检索采用的编码方式,可能有多种,置于不同表格结构,这取决于具体的字体文件格式),或是某种易于转换到Unicode的编码,这个策略显著地简化了函数库地内部结构,同时以一致的视图呈现于应用程序之前。
另一个重要目标是尽可能避免把内部数据结构暴露给应用程序。X渲染扩展允许渐增式下载新字形,设计Xft时允许按需光栅化新字形,这就要求(字形的)度量信息通过一个函数获取,而不是象Xlib中那样直接访问相应数据结构。
最后,Xft被设计为一个完整的字体访问函数库,所以底层的FreeType数据类型未被隐藏,以便应用程序可以直接使用FreeType自身。这降低了Xft的复杂度,同时允许应用程序完全存取可用的字体信息。
5.2 字体命名设计
Xft字体命名设计方案最初源于这样的观念,即字体名应该反映字体属性,不管是应用程序想要的属性还是字体自身的属性。由于应用程序的需求并不固定,而字体的特性也不断得到增强,下述想法逐渐形成:字体名应该用一个可变的指定属性列表表示,每个属性有一组相关值。允许属性有多个值,提供了为字体的家族(family)名或风格(style)等属性指定可接受的替代值的能力。
这一设计统一了应用程序的字体请求和可用字体的表示。一个XftPattern是一组指定属性,每个属性有一个或多个值。每个可用字体用一个给出了该字体特性的XftPattern来描述。应用程序构造一个描述了其请求的XftPattern,该XftPattern其后将与所有描述可用字体的 XftPattern进行匹配,与应用程序请求最匹配的可用字体将被选中。这种“最近匹配”机制保证每个应用程序字体请求都能匹配一个字体,虽然该字体的精确特性可能与应用程序的请求有一定差别。
一种XftPattern的文本表示方法最终被设计出来,这就允许应用程序在很大程度上象当前一样使用字符串去选择字体。
XftPattern提供了一种简单但可扩展的机制,供应用程序与所使用的字体之间传达请求和能力,具体的用法在第6节描述。
5.3 字体列表设计
X核心协议提供了支持查询可用字体集合的原语,简单的、shell风格的模式(Pattern)传送到X服务器,匹配该模式的字体名集合被返回。只有获得几乎包括系统中所有字体的列表,才能发现可用的字体家族集合。应用程序要负责从这些大量数据中提取有用信息。
真正需要的是能向系统请求特定的相关信息,并由系统丢弃多余数据。应用程序可以专注于检验和操作信息,而不是花费很大气力去解析XLFD字符串。
Xft把用于返回可用字体数据的信息分为两部分,第一部分是从可用字体中做选择所需的模式,第二部分是决定应用程序有兴趣接收信息的一组属性名。匹配字体的列表将被修剪,以消除在所选属性中重复的项。
最后需要说明的是,“最好匹配”的观念在列出字体时没有用,应用程序用字体列表为用户产生对话框,或者用来发现字体家族的能力。这时,需要对模式中的每个元素做精确匹配,而不是测量可用字体与请求模式的差距(以寻找最好匹配——译注)。
5.4 配置设计
为避免可能在字体配置和定制机制出现互不兼容的分支,需要一个标准。鉴于没有可用的现有标准,在Xft中加入字体配置至少能保证X应用程序使用相同方式共享和命名字体。
一个基本的定制需求是允许一种字体作为另一种字体的替代,这种“别名”机制能在某种字体不可用时,选择拥有近似特性的字体来维护文本的外观统一。另一个有用的任务是支持一般的“mono”、“sans”和“serif”字体,应用程序可以在大多数情况下按照用户偏好选用。
另一个目标是要使添加字体尽可能容易。核心X字体配置机制使用X服务器字体路径列出所有查找字体的目录集合,在每个目录下,需要生成两个单独的配置文件来进行字体名到文件名的映射。如果这些文件丢失或损坏,就不能正确地进行字体选择。不再使用这些配置文件,会使系统总体上更加健壮。
最后,用户和管理员需要定制特定字体的光栅化。一些用户希望避免对某些字体、或是某些尺寸范围内的字体使用消锯齿功能,另一些需要对某些字体调整尺寸和间距。
实际上,Xft并非放置所有这些配置功能的正确场所,因为X应用程序并不是唯一对访问字体和字体信息感兴趣的程序。把这些配置机制移出Xft,放在一个能被非X应用程序使用的函数库,将是近期开发的焦点(译注:该功能已经移到FontConfig函数库。)
6 Xft字体名
Xft的字体名用一个指定属性的列表表示,其中每个属性带有一个有类型值的列表,这个属性集合存放在XftPattern数据结构中。有一些例程用于创建、编辑XftPattern以及进行与可用字体列表进行匹配的操作。Xft有一些内部支持的属性,如表1中所示;但是并不限制应用程序仅使用这些属性, Xft会忽略所有不理解的属性。
为了让XftPattern能够传输和存储,一个模式的结构能用一个字符串表示。这些字符串的格式如图1所示。每一个支持的(属性)名字有一个隐含类型,以便解析相关值,这样就避免使用引号或其它词法机制来区分不同类型。一些指定常量可以取代一些值或者完整的“name=value”对,因为常量唯一确定了相关属性名字,没有必要再显式给出名字。表2给出了可用的常量列表。这些常量中的每一个在内部都用一个值表示,这就允许在应用程序请求的值和可用字体的值之间进行相似匹配。例如,一个应用程序请求“demibold”时,会选中“bold”而不是“medium”。这有助于达成应用程序的意图,即使在开发和测试过程中没有与之匹配的可用字体。
名字 类型 C语言名
family String XFT_FAMILY
style String XFT_STYLE
slant Int XFT_SLANT
weight Int XFT_WEIGHT
size Double XFT_SIZE
pixelsize Double XFT_PIXEL SIZE
encoding String XFT_ENCODING
spacing Int XFT_SPACING
foundry String XFT_FOUNDRY
core Bool XFT_CORE
antialias Bool XFT_ANTIALIAS
xlfd String XFT_XLFD
file String XFT_FILE
index Int XFT_INDEX
rasterizer String XFT_RASTERIZER
outline Bool XFT_OUTLINE
scalable Bool XFT_SCALABLE
rgba Int XFT_RGBA
scale Double XFT_SCALE
render Bool XFT_RENDER
minspace Bool XFT_MINSPACE
dpi Double XFT_DPI
charwidth Int XFT_CHAR WIDTH
charheight Int XFT_CHAR HEIGHT
matrix Matrix XFT_MATRIX
表1 Xft字体名属性
name : families sizes properties
families : family-names
|
family-names : family-names ,string
| string
sizes : -size-list
|
size-list : size-list ,number
| number
properties : properties :property
|
property : string =value
| named-constant
value : string
| number
| boolean
| named-constant
| matrix
matrix : number number number number
Examples:
times,serif-12:italic
courier,mono-14:matrix=1 .1 0 1
图1: Xft字体名语法.
另外,虽然XLFD字体名不理想,但是它在现有的X应用程序中很常见,而且也表达了一组期望的字体特性。Xft能够把XLFD字体名转换成XftPattern,以便能使用Xft匹配规则选择字体,而不用第4节中所述的XLFD匹配规则。
名字 常量 值
weight light 0
medium 100
demibold 180
bold 200
black 210
slant roman 0
italic 100
oblique 110
spacing proportional 0
mono 100
charcell 110
rgba rgb 1
bgr 2
vrgb 3
vbgr 4
表2 Xft字体名常量
Xft字体名被设计为可扩展,以便即使Xft/FreeType2界面继续成长并为新系统提供模式元素情况下,现有应用程序依然能够正确运转。
7 Xft配置文件
使用核心协议,应用程序保证可以存取所有可用字体,因为由X服务器负责定位字体;现在字体管理移到了客户方,定位字体成为应用程序的责任。没有了可以共享的集中配置机制,每个应用程序可用的字体集合都可能有很大不同,安装和选择字体就可能有问题或者出错。
Xft配置文件的主要作用是指定可用字体文件的位置,其次是调整字体选择以及定制光栅化参数。
默认情况下,Xft的配置文件“XftConfig”在/usr/X11R6/lib/X11目录下,但可以通过在XFT_CONFIG环境变量中指定一个不同的文件名来改变。
文件中任何地方都可以放置注释,“#”字符将使其后直到行尾的部分成为注释。其他的语法将在下面几节说明。
7.1 字体目录
在配置字体所在位置时,Xft使用了一种极为简单的方法。指定一个目录的列表,Xft在这些目录中查找字体文件,所有找到的字体文件将被加入用于匹配的可以字体列表。(字体)在目录中的位置是无关紧要的,因为Xft总是在所有字体中进行最佳匹配。在配置文件中,用如下格式的行来指定目录:
dir "/usr/X11R6/lib/X11/fonts/Type1"
在目录下不再需要其他配置,Xft自动扫描目录来发现字体。
7.2 嵌套配置文件
为了把配置文件划分为便于管理的部分,也为了允许基于用户的函数库定制,Xft配置文件中允许出现如下格式的行:
include "/usr/local/lib/XftAliases"
includeif "~/.xftconfig"
两种格式的唯一区别在于:使用第一种格式,当被引用的文件找不到时,会发出警告消息。“~”字符指的是用户的主目录。因为Xft配置文件由用户的应用程序自己进行解析,这就能在不管X的显示(display)情况下,实现基于用户的定制。
7.3 字体模式编辑
为了能在字体匹配过程中实现字体替换以及其他的调整,也为了能配置光栅化过程,Xft配置文件中可以包含一些操作,这些操作能在匹配过程结束前修改Xft模式。这些操作被称为“编辑命令”(editing commands),与模式匹配时执行以修改模式,每个命令按照它们在配置文件中出现的顺序执行。这些命令的语法示于图2。
command : match tests edit edits
tests : test tests
|
test : any | all name compare value
compare : == | != | < | <= | > | >=
edits : edit edits
|
edit : name eqop expr ;
eqop : = | += | =+
expr : value
| name
| expr binop expr
| !expr
| expr ?expr :expr
binop : || | && | == | != | < | <= | > | >= | + | - | * | /
Examples:
# Use LuciduxSerif as default serif’ed font
match any family == "serif"
edit family += "LuciduxSerif";
# Avoid using anti-aliasing at some sizes for LuciduxSerif face
match any family == "LuciduxSerif" any size < 14 any size > 8
edit antialias = false;
图2 Xft模式编辑语法
match子句用来选择待编辑的模式,其值为“真”时才会进行编辑。如果表达式使用“any”前缀,那么当与指定字段相关的任何值符合条件时,子句取“真”值;如果表达式使用“all”前缀,那么只有当所有值符合条件时,子句才取“真”值。
edit子句在模式的字段上操作,可以替换或者修正相关值。如果在match子句中引用模式中同一元素,则匹配值将被标记。
在edit子句中,“+=”操作符把值插入标记值之前,“=+”操作符把值插入标记值之后,“=”操作符则替换标记值。如果没有值被标记,“+=”和“=+”操作符分别把值加入整个值列表的头和尾,“=”操作符将会替换所有的值。
表达式中的操作符的含义都很明显,一种可能有些意外的情况,是“+”操作符可以用于连接字符串。表达式中可能引用字体中的某些字段,这种情况下,使用该字段的第一个值。
使用此机制几个月后,发现这种机制很复杂,并且完成不了一些预期的任务。缺陷之一是编辑只影响进入的模式,而不对匹配的字体进行任何操作。这使得禁止 LuciduxSerif字体消锯齿效果的例子(译注:图2中的第二个例子)产生非预期的结果,例如,当以如下名字指定字体时,
Times,LuciduxSerif,serif-10
match子句将会成功匹配该模式,从而导致Times字体在显示时失去了消锯齿效果。
另一个问题是字体别名含义不清,目前还没有提供精确语义。关于字体别名,有两种可能的语义:一种是在任何情况下都用一种字体替换其它,另一种是只有当某些字体不存在时,才用特定字体替换。关于这一点的明确声明既能澄清文件格式,也有助于改进匹配的语义。(译注:本段中的“字体”对应原文的face)
8 字体匹配
Xft中字体匹配的目标是从应用程序接收一组字体特性的描述,然后从所有可用字体中返回最好的。应用程序调用XftFontMatch函数,并提供 XftPattern形式的字体规格说明。模式按照7.3节中所描述的方式进行编辑,表3所示的X资源可以用来修改模式。如果应用程序没有指定像素尺寸,将根据指定的点值尺寸(如果也没有指定,使用12.0)乘以缩放因子,再利用指定的dpi值将点值转换为像素。其他的X资源用作相关模式元素的默认值。
X资源 类型 效果 默认值
Xft.render Bool 指导Xft使用客户方字体 HasRender
Xft.core Bool 指导Xft使用服务器方字体 !HasRender
Xft.antialias Bool 选择字形是否需要消锯齿 True
Xft.rgba Number 指定LCD屏幕上的子像素顺序 0
Xft.minspace Bool 消除行间的额外间距 False
Xft.scale Number 所有字体点值尺寸的缩放因子 1.0
Xft.dpi Number 用于从点值尺寸转换为像素尺寸 屏幕的垂直dpi
表3 用X资源调整Xft值
完整的模式随后将用于与所有可用字体进行比较,只有表4中所示的字段在比较中有用。表中字段的顺序是有意义的,表中靠前的元素不匹配将使后面元素的匹配变得无效。如果模式或字体缺少某一元素,将默认为匹配。数字值按照其差值的大小进行度量,这就是为什么指定“oblique”将选择“italic”,而不是“roman”。(译注:参见表2的常量定义。)
Order Name Type
1 foundry String
2 encoding String
3 antialias Bool
4 family String
5 spacing Number
6 pixelsize Number
7 style String
8 slant Number
9 weight Number
10 rasterizer String
11 outline Bool
表4 Xft字段比较的匹配顺序
有多个值的字段倾向于匹配列表中靠前项的字体,按照这种方式,下面名字将相对于“LuciduxSerif”优先匹配“Times”。
Times,LuciduxSerif,serif-10
一但选中了某一字体,将会构造一个能精确描述该字体的XftPattern,以便应用程序知道究竟选用了什么字体。这个模式包括底层字体文件的信息,从而允许应用程序直接通过FreeType访问未获得的信息。在模式中有、而字体中没有的字段也被加上,这就允许应用程序之间交换字体模式的信息,这种通信既可以在应用程序与光栅化器之间,也可以在应用程序自身发生。
9 X核心字体处理
虽然Xft的主要目标是在FreeType和X渲染扩展基础上,提供客户方字体支持,但是为了在X渲染扩展不可用时提供应用程序兼容性,同时X核心字体支持也是合理的。这必然限制函数库的能力,但即使这样,很多应用程序无论使用X核心字体或基于渲染扩展字体机制时,不加修改地使用Xft函数,另一些则只需作一些小小的改动以便注意到何时不能使用FreeType函数。
核心字体处理有两个部分:字体选择和渲染。字体选择是通过列出可用的X字体,将其转换为XftPattern结构,然后从应用程序接收模式进行匹配。由于按照接近程度进行匹配,而不是采用简单的shell模式匹配,一般情况下,这种匹配得到的结果要比用XLFD指定更有用。这有助于简化应用程序的设计,因为在此之前它们需要包含可观的、用于X核心字体匹配的机制。
一但选定某种核心字体,渲染就成为简单的事情:可以让Xft例程调用标准Xlib中文本绘制例程。唯一的困难是把应用程序提供的Unicode字形映射到字体所支持的编码。
这种合并的效果之一是Xft渲染例程只提供那些渲染扩展和核心字体都支持的能力。应用程序可以为绘制指定半透明的颜色值,但是如果使用的是核心字体,该指定将被忽略。类似地,Xft不提供选择光栅化操作或者组合操作(译注:X渲染扩展在服务器方的基本设计思想是基于图像组合操作),因为它们都只被底层的一种渲染机制支持。最终的结果是一种新的使用核心字体的方式,相对于Xlib中的对应物,在许多方面更容易使用,并且功能更强大。
10 Xft接口总览
Xft编程接口很容易划分到三个单独区域:处理XftPattern以及匹配字体、在屏幕上绘制字形、用于提供底层FreeType函数库接口的一小部分。
10.1 Xft数据结构
XftValue
typedef enum _XftType {
XftTypeVoid,
XftTypeInteger,
XftTypeDouble,
XftTypeString,
XftTypeBool,
XftTypeMatrix
} XftType;
typedef struct _XftValue {
XftType type;
union {
char *s;
int i;
Bool b;
double d;
XftMatrix *m;
} u;
} XftValue;
一个XftValue存放一个XftPattern元素的一个值,应该作为一个加了标签的联合来对待:使用联合前应该先设置或检查“type”字段。字符串或者矩阵元素单独存放,Xft从应用程序接收XftValue结构后,总是根据这些指针复制数据,这就使得应用程序自由地决定使用静态或是堆栈中的存储。
XftPattern
typedef struct _XftPattern
XftPattern;
XftPattern是一个不透明地数据结构,用于存放一个指定元素的列表,而每个元素又有一个XftValue的列表。Xft为构建和查询这些模式提供了接口。
XftFont
typedef struct _XftFont {
int ascent;
int descent;
int height;
int max_advance_width;
Bool core;
XftPattern *pattern;
union {
struct {
XFontStruct *font;
} core;
struct {
XftFontStruct *font;
} ft;
} u;
} XftFont;
当打开字体时,返回XftFont数据结构,并用于绘制字形。其可见成员提供了关于字体的少量信息。如果core的值为真,底层使用X核心字体,这时u.core.font 指向一个 XfontStruct结构;否则底层使用FreeType字体而u.ft.font字段引用XftFontStruct结构。
XftFontStruct
typedef struct _XftFontStruct
XftFontStruct;
struct _XftFontStruct {
FT_Face face;
GlyphSet glyphset;
int min_char;
int max_char;
FT_F26Dot6 size;
int ascent;
int descent;
int height;
int max_advance_width;
int spacing;
int rgba;
Bool antialias;
int charmap;
XRenderPictFormat *format;
XGlyphInfo **realized;
int nrealized;
Bool transform;
FT_Matrix matrix;
};
也许这个结构将来会隐藏起来,用一些适当的函数访问某些内部字段。最有用的字段当属“face”,用于引用底层的FreeType对象,但是在内存中保存这类对象开销太大,所以这些应该被缓冲并且按需调入。应用程序在使用该字段时,明智的方法是通过宏包装,以备将来该字段不再可见。关于该结构其他字段的可见性还需进一步核查。
XftDraw
typedef struct _XftDraw
XftDraw;
XftDraw封装了在X可画物上渲染字形所需的状态。对于使用渲染扩展的情况,需要Picture,对于使用X核心协议情况,需要GC和相关像素值。(译注:可画物,drawable,是Xlib编程基本概念,包括window和pixmap)
XftColor
typedef struct _XftColor {
unsigned long pixel;
XRenderColor color;
} XftColor;
渲染扩展需要RGBA,而X核心需要像素值;XftColor保存了二者,并有相应例程来初始化以及释放相关资源。对于真彩色可视类型,分配例程为了避免往返传递的开销,在本地计算像素值,这样来消除性能下降。如果已知将使用渲染扩展,“color”成员可以手动初始化而不去设置“pixel”的值。(译注:可视类型,visual type,是Xlib编程基本概念,可以是StaticGray, StaticColor,TrueColor, GrayScale, PseudoColor, 或DirectColor之一)
XftObjectSet
typedef struct _XftObjectSet
XftObjectSet;
Xft的字体列示机制使用XftObjectSet来限制返回给应用程序的数据总量,XftObjectSet用于存放一个字段名的列表。
XftFontSet
typedef struct
_XftFontSet {
int nfont;
int sfont;
XftPattern **fonts;
} XftFontSet;
XftFontSets指向一组模式集合,用作字体列示函数的返回值。
XftResult
typedef enum _XftResult {
XftResultMatch,
XftResultNoMatch,
XftResultTypeMismatch,
XftResultNoId
} XftResult;
需要返回搜索结果的函数使用这个数据类型来表示它们的发现,XftResultMatch表示发现了对象XftResultNoMatch说明没有发现匹配对象,XftResultTypeMismatch说明发现了某个对象,但是类型不对,XftResultNoId说明发现了对象,但是比请求的值少。
10.2 字体模式操作
许多Xft操作涉及XftPattern对象,鉴于应用程序不应该直接访问这些对象,有一组例程用于操作它们。
XftPatternCreate
XftPattern *
XftPatternCreate (
void)
创建一个空的模式。
XftPatternDuplicate
XftPattern *
XftPatternDuplicate (
XftPattern *p)
创建一个新模式,其所有值源自“p”所指对象。如果值引用其他存储(字符串和矩阵),相关存储将被复制到新分配存储区中。
XftPatternDestroy
void
XftPatternDestroy (
XftPattern *p)
释放所有相关的存储,包括模式中引用的字符串和矩阵。
XftPatternAdd
Bool
XftPatternAdd (
XftPattern *p,
const char *object,
XftValue value,
Bool append)
把“value”加入“object”字段的值列表中。如果“append”为True,值将被加到列表尾部,否则插入到头部。如果 “value”引用字符串或者矩阵,XftPatternAdd将分配新存储区并复制,只有在分配空间失败时,XftPatternAdd才返回 False。此函数是其他XftPatternAdd函数的基础。
Bool
XftPatternAddInteger (
XftPattern *p,
const char *object,
int i)
Bool
XftPatternAddDouble (
XftPattern *p,
const char *object,
double d)
Bool
XftPatternAddString (
XftPattern *p,
const char *object,
const char *s)
Bool
XftPatternAddMatrix (
XftPattern *p,
const char *object,
const XftMatrix *s)
Bool
XftPatternAddBool (
XftPattern *p,
const char *object,
Bool b)
这些函数中的每一个都会创建适当类型的临时XftValue,并作为参数传递给XftPatternAdd,调用时“append”设为True。
XftPatternGet
XftResult
XftPatternGet (
XftPattern *p,
const char *object,
int id,
XftValue *v)
XftPatternGet搜索指定的模式,找到名字与“object”匹配的元素,然后在值列表中找到第“id”个(从0开始计数)个元素,把结果值存入“v”。此函数不为字符串和矩阵分配存储区,所以应用程序必须确保不在XftPattern自身的生存期之外引用该值。此函数是其他 XftPatternGet函数的基础。
XftResult
XftPatternGetInteger (
XftPattern *p,
const char *object,
int n,
int *i)
XftResult
XftPatternGetDouble (
XftPattern *p,
const char *object,
int n,
double *d)
XftResult
XftPatternGetString (
XftPattern *p,
const char *object,
int n,
char **s)
XftResult
XftPatternGetMatrix (
XftPattern *p,
const char *object,
int n,
XftMatrix **s)
XftResult
XftPatternGetBool (
XftPattern *p,
const char *object,
int n,
Bool *b)
这些函数中的每一个都调用XftPatternGet。如果结果的数据类型与函数不匹配,函数将返回XftResultTypeMismatch,否则与XftPatternGet的返回值相同。
XftPatternBuild
XftPattern *
XftPatternBuild (
XftPattern *orig,
...)
“orig”之后的参数构成了一个用于加入到指定模式的名字、类型和值的列表,如果“orig”为空,将分配一个新模式。模式参数的格式如下:
char *object,
XftType type,
union {
char *s;
int i;
Bool b;
double d;
XftMatrix *m;
} u
列表用一个空对象结束,例如:
p = XftPatternBuild (
0,
XFT_FAMILY,
XftTypeString,
"mono",
XFT_SIZE,
XftTypeDouble,
12.0,
0);
这个函数是一个便捷函数,它把若干XftPatternAdd调用封装到一个语句,其语义与作一组相当的XftPatternAdd调用是一样的。
XftPattern *
XftPatternVaBuild (
XftPattern *orig,
va_list va)
这个函数使用变参列表,其参数格式与XftPatternBuild 一致,产生结果也一样。
10.3 字体选择
Xft的字体选择函数分若干级别,所用到的模式既可以由简单的字体名字符串隐含生成,也可以由应用程序显式管理,在交付下层匹配接口前进行操作。作为最基本的级别,Xft提供:
XftFontMatch
XftPattern *
XftFontMatch (
Display *dpy,
int screen,
XftPattern *pattern,
XftResult *result)
此函数用应用程序生成的XftPattern进行配置文件编辑和X资源替换(译注:见第7、8节),然后把得到的模式在可用字体集合中匹配,最接近的字体名以另一个XftPattern的形式返回,该模式包含足够的信息以使用XftFontOpenPattern函数打开字体。如果失败,返回值为0,并且在“result”中放置错误指示。
XftFontOpenPattern
XftFont *
XftFontOpenPattern (
Display *dpy,
XftPattern *pattern)
XftFontOpenPattern接受匹配的字体模式,为字体创建XftFont结构。返回的XftFont包含一个对传入模式的引用,该模式将在以该XftFont为参数调用XftFontClose时被销毁。
XftFontOpen
XftFont *
XftFontOpen (
Display *dpy,
int screen,
...)
“screen”之后的参数构成了一个暗含的XftPattern,如同10.2节对XftPatternBuild参数的描述。下面是一个例子:
f = XftFontOpen (
dpy,
DefaultScreen(dpy),
XFT_FAMILY,
XftTypeString,
"mono",
XFT_SIZE,
XftTypeDouble,
12.0,
0);
根据这些参数可以创建一个模式,将其作为参数调用XftFontMatch,返回结果有传递给XftFontOpenPattern,最终产生本函数的返回值。
XftFontOpenName
XftFont *
XftFontOpenName (
Display *dpy,
int screen,
const char *name)
与XftFontOpen类似,“name”参数构成暗含的XftPattern,XftFontMatcn和XftFontOpenPattern以同样方式被调用以最终得到匹配的XftFont。
XftFontOpenXlfd
XftFont *
XftFontOpenXlfd (
Display *dpy,
int screen,
const char *xlfd)
除了通过解析“xlfd”指向的XLFD字体名得到XftPattern以外,此函数与XftFontOpenName完全相同。
XftFontClose
void
XftFontClose (
Display *dpy,
XftFont *font)
下层的核心或者FreeType字体对象被关闭,字体引用的模式被销毁。
10.4 XftDraw操作
Xft提供了可以屏蔽X核心和渲染扩展这两种不同渲染机制之间差异的一种抽象,XftDraw对象提供了两种模式的操作对象,并包装了两种渲染模型所需的适当信息。
XftDrawCreate
XftDraw *
XftDrawCreate (
Display *dpy,
Drawable drawable,
Visual *visual,
Colormap colormap)
此例程创建一个XftDraw对象,引用所涉及的可画物以及相关的可视类型及颜色图。即便使用指定了最终像素的像素图(pixmap)进行渲染,也需要指定visual参数。
XftDrawCreateBitmap
XftDraw *
XftDrawCreateBitmap (
Display *dpy,
Pixmap bitmap)
如果渲染目标是1位的位图,应使用此函数,而非XftDrawCreate。
XftDrawChange
void
XftDrawChange (
XftDraw *draw,
Drawable drawable)
此函数切换下层的渲染目标,但不会影响XftDraw对象的其他属性。应用程序应负责保证新的可画物与原来的可画物有相同的可视类型。
XftDrawDisplay,XftDrawDrawable,
XftDrawColormap,XftDrawVisual
Display *
XftDrawDisplay (
XftDraw *draw)
Drawable
XftDrawDrawable (
XftDraw *draw)
Colormap
XftDrawColormap (
XftDraw *draw)
Visual *
XftDrawVisual (
XftDraw *draw)
这些函数简单地从不透明地XftDraw结构中返回相关值。
XftDrawDestroy
void
XftDrawDestroy (
XftDraw *draw)
此函数将销毁XftDraw对象及其分配的任何私有数据,但引用的X可画物并不销毁。
10.5 字形渲染
有了XftFont和XftDraw,下一步就是使用它们在屏幕上显示文本。这相对简单,因为只用Unicode编码,唯一需要改变的是如何保存字形。在这一节中还给出了一些便利函数,让应用程序能使用与其他操作类似的数据结构。
XftTextExtents
void
XftTextExtents<8,16,32,Utf8> (
Display *dpy,
XftFont *font,
XftChar<8,16,32,8> *string,
int len,
XGlyphInfo *extents)
这些函数测量指定字符串的显示宽度,并在“extents”结构中返回。
XftDrawString
void
XftDrawString<8,16,32,Utf8> (
XftDraw *d,
XftColor *color,
XftFont *font,
int x,
int y,
XftChar<8,16,32,8> *string,
int len)
这些函数中的每一个显示一个字符串。若用渲染扩展,使用Over操作绘制字符串;若用核心机制,使用GXcopy绘制并且作用于所有平面。(译注:原文最后一句为it is painted with GXcopy and a full planemask,plane指特定可视类型下的颜色平面,planemask是GC的成员,按位标记平面,说明绘制操作影响哪些平面;with a full planemask意味着影响所有平面)
XftDrawRect
void
XftDrawRect (
XftDraw *d,
XftColor *color,
int x,
int y,
unsigned int width,
unsigned int height)
这个简单的函数用指定的颜色画一个矩形。
XftDrawSetClip
Bool
XftDrawSetClip (
XftDraw *d,
Region r)
通过指定区域来设置XftDraw的裁减区(clip)列表,以后可能会增加使用矩形列表来指定区域的等同函数。
10.6 字体列示
Xft提供一套相对复杂的机制来列示可用字体,此机制不仅可以列出可用的字体face,还可以在不返回无关数据的前提下列出某种特定face的可用风格。为达到这个目标,应用程序在提供用于字体匹配的模式外,还要说明字体模式中哪些字段是重要的;对每种匹配的字体,都会根据应用程序提供的 XftObjectList选中的字段生成返回列表中的一项,由这些项构成的无重复集合将返回给应用程序。
列出字体是一个独立于打开字体的基本过程,此过程中模式如何匹配字体的语义与10.3节中有所不同。在这种上下文中,匹配要求模式中每个元素必有值与相关字体元素精确匹配。
XftObjectSetCreate
XftObjectSet *
XftObjectSetCreate (
void)
创建一个空的XftObjectSet。
XftObjectSetAdd
Bool
XftObjectSetAdd (
XftObjectSet *os,
const char *object)
在XftObjectSet中添加一个字段名。会分配新的存储区,并从参数中复制字段名,这样就允许应用程序能释放或重用其存储空间。
XftObjectSetDestroy
void
XftObjectSetDestroy (
XftObjectSet *os)
The XftObjectSet is destroyed along with any other
referenced storage.
XftObjectSetBuild, XftObjectSetVaBuild
XftObjectSet *
XftObjectSetBuild (
const char *first,
...)
XftObjectSet *
XftObjectSetVaBuild (
const char *first,
va_list va)
XftObjectSetBuild通过NULL结束的对象名列表创建XftObjectSet,使用此函数能快速构建一个名字的常量集合而避免一长串函数调用,其语义效果等同于一系列调用。XftObjectSetVaBuild使用一个可变参数列表完成同样的工作。
XftListFontsPatternObjects
XftFontSet *
XftListFontsPatternObjects (
Display *dpy,
int screen,
XftPattern *pattern,
XftObjectSet *os)
对匹配“pattern”的每种字体生成一个新的模式,其中仅包括“os”所指定的元素;此函数把得到的新模式合并后返回,以保证其中没有重复的模式。
XftListFonts
XftFontSet *
XftListFonts (
Display *dpy,
int screen,
...)
可变参数包括一个NULL结束的模式元素列表,与10.2节中关于XftPatternBuild的描述一致,其后还有一个NULL结束的字段名列表,与本节中关于XftObjectSetBuild的描述一致。根据参数生成模式和对象集合后,此函数调用 XftListFontPatternObjects并直接返回调用结果。
10.7 访问FreeType
在需要与底层的FreeType函数库或渲染扩展需要更复杂交互的地方,Xft提供一些函数以便直接访问。
XftFreeTypeGet
XftFontStruct *
XftFreeTypeGet (
XftFont *font)
此函数返回给定字体的底层XftFontStruct对象,当底层字体不是FreeType时返回NULL。
XftRenderString
void
XftRenderString<8,16,32,Utf8> (
Display *dpy,
Picture src,
XftFontStruct *font,
Picture dst,
int srcx, int srcy,
int x, int y,
XftChar<8,16,32,8> *string,
int len)
这四个相关函数都提供相对于使用渲染扩展画文本更强的控制,它们提供一个任意源图片(picture),用于填充文本。应用程序使用这些例程,还能更有效地缓冲源图片。(译注:Picture可能是FreeType引入的数据结构)
11 字体信息缓冲
如同7.1节所述,函数库通过扫描配置文件中列出的目录生成可用字体集合。要发现字体的特性需要使用FreeType打开字体文件,一大批文件需要处理,这可能会花费相当长的时间,尤其是字体很多的情况。
为提高此操作的性能,Xft在两个地方缓存搜索结果。每个目录下可能有一个名为XftCache的文件,其中每一行列出了字体文件名、字体在文件的索引以及用XftPattern字符串格式表示的字体信息。如果一种字体没有在任何XftCache文件中发现其信息,Xft会在用户主目录下生成一个. xftcache文件,其中包含字体文件名、字体索引、文件修改时间和XftPattern。
XftCache文件由xftcache程序创建,该程序是Xfree86发布的一个标准部件。鼓励用户在要加入Xft配置文件的其他目录下运行此程序,并且每当目录内容改变时重新运行。Xft会自动管理每用户的.xftcache文件内容,仅存放那些不在任何XftCache文件中的未知字体。
Xft仍然会在开始时扫描每个目录,但是会在使用FreeType打开文件前先检查每目录和每用户缓冲中是否有相应文件名,这能在保持查询字体目录方法得到的精确性前提下,极大地减少应用程序的启动时间。
12 未来的研究方向
Xft始于把X渲染扩展和FreeType光栅化器结合在一起的简单努力,为了支持更多的应用程序,加入了一些新的能力。这些新功能都是必须的,但其中的一些已经超出了以X为中心的函数库。特别是字体配置机制,应该从Xft中提出来放入一个单独的函数库,以便打印驱动程序和其他非X的字体使用者能共享该机制。这样能确保所有应用程序,而不仅是那些在屏幕上显示信息的程序,都能受惠于字体更易安装、管理。
Xft还需要支持国际化的额外能力。目前,XftFont对象引用单一FreeType字体,如果该字体没有包括渲染特定文档所需的全部字形,缺少的字形不会正确显示。需要增加某种发现替换字形的机制,XftFont结构也应扩展以便使用多个底层FreeType字体face进行自动字形替换。
对于那些不希望自动替换的应用程序,可能应该基于所需的Unicode字形子集匹配字体。字体文件中有字形覆盖范围的信息,应用程序应该能指定所需的字形范围,而Xft应该基于该指定进行字体匹配。
13 结论
Xft的开发模型与大多数X开发不同,函数库早期在其他项目中发布和集成,其他X项目等到基本稳定了才提交给社区中进行详细评判。
随着对此函数库的使用逐步增长,在没有太多影响其他项目的情况下,一些主要的提高被加入。未来的增强可能需要改变现有应用程序,这应被视作发展过程的正常部分。此函数库在很大程度上为Xfree86社区开辟了新天地,只有通过大范围的使用和评判,才能开发出适当的体系结构。
Xft的设计与实现进展很快,虽然仅在一年前启动,Xft已经成为许多现在的X项目的基本组成部分。(译注:原文发表于2001年11月在Oakland, California, USA举行的Proceedings of the XFree86 Technical Conference)
参考文献
[Pac01] Keith Packard. Design and Implementation of the X Rendering Extension. In FREENIX Track, 2001 Usenix Annual Technical Conference, Boston, MA, June 2001. USENIX.
[SG92] Robert W. Scheifler and James Gettys. X Window System. Digital Press, third edition, 1992.
[TT00] David Turner and The FreeType Development Team. The design of FreeType 2, 2000. http://www.freetype.org/freetype2/docs/design/.