警告
萬一本文有更新的版本, 也許可以在 http://fractal.csie.org/~eric/fontconfig 找到,任何使用本文中提及的方法所造成之社會成本損失將不會被負責。
版權聲明
在保留此版權聲明及原作者的情況下, 本文可以被任意轉錄。有關於更詳細的條件請見: http://creativecommons.org/licenses/by-nc-sa/1.0/ -- EricCheng
See also:
Wprint 中文列印 Patch 與 freetype
Fontconfig
晚近的 XFree86 除了有了 freetype 的內建,加強了對於 TrueType 等向量字型的支援外,最近 Keith Packard的 Xft 與 Fontconfig 也是一個對於字型整合所做的嘗試,在最新的 XFree86 4.3.x 與freetype/Xft2/Fontconfig 的支援下,X 下的程式對於一個統一的字型選擇與繪製介面又進了一步。
雖然 XFree86 本身包含 Fontconfig, Fontconfig 事實上是一個可以獨立出來的介面,它是一個 library不是一套 user app, 它所做的就是提供一套 font matching 的機制,讓使用 Fontconfig的程式可以不必自己實作一套字型的選取方法。如此只要使用 Fontconfig的程式愈多,單一的一套設定檔就可以被用在愈多的應用程式,應用程式本身可以利用 Fontconfig所得到的字型名稱去畫字,也可以架構在自己原先的字型選擇架構之上 (如 Qt), 以達成對舊的設定的一定的向後相容性。
Fontconfig 有許多好處,例如:
字型的安裝。與其把要用的字型拿來放在一個目錄, 然後用 ttfmkdir / defoma / ttfm 等東西生出 fonts.dir再指給 xtt/freetype, 再重新啟動 X font server 或 xset fp rehash, 現在只要把字型丟或symlink 到 ~/.fonts 或任何其他經過指定的目錄, 就可以 _立即_ 開始使用了。當然在使用沒有支援 Fontconfig的程式時, 仍然要用傳統的方法。Fontconfig 除了可以吃 TrueType, 也可以吃 Type1 或 pcf 等等傳統的點陣字。
字型 matching 的設定。雖然 Fontconfig 已經附上了一套不錯的設定檔讓在未被設定的情況下也都能夠有一個可以使用的系統,但其實對於個別字型的設定更有彈性。這個是透過 Fontconfig 所使用的 xml 設定檔達成的。稍後再說。
Fontconfig 會儘可能找出一套字,可以滿足顯示不同語言的需求。
Xft
Xft 也是一套 library, 它使用 Fontconfig match 到了所要的字型之後, 來決定該如何畫這些字。Xft會看情況而決定要不要使用 core protocol 或 XRender 來畫字。XRender 是 XFree86 4.x 新增的extension, 我認為這是為了保留 X 的向後相容性所新增的一個 hack, 不過因為它可以用來畫 anti-aliased的字,目前的使用愈來愈廣泛。不過 anti-aliased text 只有在使用向量字型的時候有用, 繪製點陣字的時候就要使用 corelib. Core library(以 x-truetype 或 freetype 作為backend)自然也是可以畫向量字,只不過畫出來的就不能有 anti-aliased 的效果了。
有時當 XRender 不能使用時(如你是透過網路用一個舊的 X server 來執行 X apps), Xft 也可以使用 core lib 來畫字。應用程式不必為這些問題操心,達到資訊隱藏、各制湔?哪康摹?
Freetype
Freetype 是一個很棒的畫字函式庫,XFree86 4.3 內含了 2.1.2. Freetype 提供 Xft如何畫字的資訊,包括處理 anti-aliasing 或 hinting. 因此 freetype 的改變會影響到 Xft 畫出來的字,而Fontconfig 的改變會影響到 Xft 如何去選字來畫。
fonts.conf
這裡所講的就是最新的 Fontconfig 與 Xft2 的設定。對於舊的 Xft1.0 的 ~/.xftconfig 就不提了。
如果你裝了 fontconfig, 那麼它應該已經附上了一套預設的設定檔。可以到 /etc/fonts/ (一些 Linuxdistributions) 或 /usr/X11R6/etc/fonts (一些 BSD flavors) 底下找找看 fonts.conf這個檔案。
fonts.conf 是簡單的 xml 格式,在 etc/ 裡面的 fonts.conf 是 system-wide的設定,一般不建議直接更改它,可以更改 local.conf 或是自己家目錄下面的 ~/.fonts.conf . 關於 fonts.conf的各種語法,由於 manpage 裡頭已經寫得很詳盡,所以這裡只是提及比較重要的一些部份,有興趣者可以 man fonts-conf.所有的設定都放在 <fontconfig> 與 </fontconfig> 之間,而其中可以包含許多 tags,詳細的 tags 可以參照 fonts.dtd 或者是 manpage.
<dir> 裡面是一個路徑,fontconfig 會自己遞迴地去找這個路徑裡頭的所有字型,如: <dir>/foo/bar/myfonts</dir>
<include> 可以把其他的設定檔引進來,它們的格式是一樣的。
其中最重要的 element 應該是 <match> 了。match 主要有兩種用法, 一種是 pattern match,另一種是 font match. 前者會把所有的字型 match出來,所以針對它的更改會套用到所有的字型的選擇方式上。為什麼要更改字型的屬性? 因為這樣可以針對個別的字型告訴 Fontconfig該如何去處理這些字型,或是告訴 renderer 該如何去畫這些字型。這裡是一些常用到屬性的列表,關於所有的屬性請洽 manpage:
family - String - 就是一般所看到的字型的名稱了, 如 Arial
style - String - 字型的 style, 像是 Regular, Bold, Italic...
spacing - Int - 字型的寬度, Proportional 是有不同的寬度, monospace 是單一的寬度 (如 terminal 的字型)
antialias - Bool - 決定該字型是否要被 anti-alias 繪製; 只能用在向量字型上
hinting - Bool - 決定該字型是否要打開 hinting
autohint - Bool - 決定是否要用 Freetype 自己的 hint 方法來 hint 字型, 還是用預設的方法來 hint
rgba - Bool - 決定是否要用 subpixel 的方式來畫字, 可以是 none (只用灰階), rgb, bgr, vrgb, vbgr
Hinting 用來最佳化字型顯示的方法。由於螢幕的像素有限,向量字型的縮放需要有更多的考量, 例如當一條線位在兩個像素格子中間時,該取左邊的格子還是右邊的格子? 如果這方面的控制沒有做好,就常常會出現字型的襯線沒有對齊,或是小字歪七扭八的情況。 Hinting是額外的資訊, 它告訴 renderer 該如何處理這些細節的部份,使得向量字在小字的時候能夠好看。也因此 Hinting是非常費時費人力的工作,TrueType 字型很多,但是有良好 Hinting 的字型不多。拙劣的 Hinting 就會讓字變得很難看。
為了稍微改善這個問題,freetype 有 autohint 的功能,可以自動為沒有 hint 的字型做 hinting 的工作。另外由於TrueType 的 hinting 是有專利的,不能完全自由地使用, autohint 就不受這個限制。autohint自然無法做得像人力的 hint 一樣好,不過至少比沒有 hint 要好些。話雖如此,對於許多筆劃複雜的文字 (如中文) 目前 freetype的 autohint 還做得不甚完美,而因為建立完整的 hinting 的難度,即使是英文字,原本就很高,內建有 hinting的中文字型就少之又少了。所以常常有人抱怨中文字在螢幕上很難看,就是沒有理想 hinting, 或者是使用了 autohinter所造成的一些反效果。
Anti-alias 是將字型在幕後先以數倍的大小來繪製,然後再縮成想要的大小,未滿一格的格子用灰階補點。由於原本 X 所支援的 logic咚悴环笫褂茫??圆庞?XRender 的 extension 來達成目的。除了一般的 Anti-alias 之外,Xft 還支援了為 LCD所設計的 subpixel rendering.
什麼是 subpixel rendering? 如果你用放大鏡去看 LCD,會發現一個正方形的像素是由三個長方形小像素構成的。這排列通常是紅綠藍,也就代表如果液晶螢幕的水平解析度是 1024 個像素,它其實有1024x3 = 3072 個點,只不過這些點是 rgbrgbrgb... 依序排列的。以白底黑字為例,如果需要滿格的像素,rgb三格就需是全關 (0,0,0), 如果只是右邊三分之二部份, 就關掉 g 和 b, 留下最左邊的 r開著。這樣子理論上就會有原來三倍的水平像素可以使用,大幅增加了液晶螢幕的解析度。但由於只開著紅色或黃色或其他顏色,會有很明顯的光暈,所以一般會採用 filtering的方式,把一個次像素的值往左右兩格分散(因為無論對哪一格次像素來說,它的左右兩格的顏色和本身都是不同的,所以往左右兩格分散可以均勻影響亮度),成為 1/3, 1/3, 1/3 分佈;但這樣的壞處是會顯得太模糊了一點,於是再多一層,把原先三格分成 5 格,但權重改為 1/9 2/93/9 2/9 1/9。3/9那一格就是原本的次像素,而鄰近的格子就用這樣的方法分散後和原來該次像素格子的光度值相加,達到像素往中央集中,卻又不太模糊的效果。WindowsXP 有個 ClearType 選項可以打開對液晶螢幕顯示最佳化,其基本原理就是 subpixel rendering. Xft也有這樣的功能,不過 Xft 做得更多,除了 subpixel 外,還加上了 anti-aliasing。Fontconfig 的 rgba選項就是設定液晶螢幕次像素的排列方式,一般都是 rgb, v開頭的表示三種顏色是縱向排列。如果好奇的話可以拿放大鏡仔細瞧瞧,或用數位相機近拍下來放大觀察。
很多問題是出在 hinting, 因為許多時候, distribution 會把 freetype 的 bytecode hinting打開,代表使用字型內部的 bytecode 來做 hinting 修正,如果像 freetype 預設沒有打開或是使用 freetype裡頭的 autohinter, 有時效果不錯,有時卻不盡人意。另外 hinting 費時費力,大部分的字型設計師在做 hinting的時候都只有針對點陣字的顯示做 hinting 的工作,這表示如果我們在顯示小字又用 anti-aliasing的話,通常是不在字型設計師最佳化的範圍內的; 當 hinting 不當的時候,小字 anti-aliasing就會顯得非常難看(如歪七扭八或擠成一團)。關於這方面 freetype 做了很多的努力, autohinter 也就是讓程式自己做hinting 的演算法。由於 hinting 實在是個很棘手的問題,Mac OS X 對於 anti-aliasing 字型就都不使用hinting. 好在 fontconfig 可以讓我們調整這些細部的設定,讓我們針對個別的字型做不同的處理。
話題回到 pattern match: 要使用 pattern match, 只需要加入如下的 pattern, 它就會對所有的字型作用:
<match target="pattern">
...
</match>
中間放的可以是一連串的 test, 然後是一連串的 edit. test 的用法是:
<test qual="any|all|first|not_first"
name="屬性"
compare="eq|not_eq|less|less_eq|more|more_eq|contains|not_contains">
值
</test>
any 指的是說, 只要字型的該屬性 list 之中有一項有符合要 test 的值, test 就會成立。all 的話要 list之中所有的都符合,first 要第一個符合, not_first 要除了第一個以外有符合的。通常只會用到 any, 預設也是 any.name 裡面填的就是前面所提的屬性, 如 name="family". compare 是比較的條件, eq 是相等, less 是小於,以此類推。 <test> 所包住的那個值就是要用來比較的值,包括: int, double, string, matrix,bool 等等。一旦 test 的條件都成立, 就會進行到 edit 的階段,代表編輯符合條件上述 test 條件的屬性:
<edit name="屬性"
mode="assign|assign_replace|prepend|append|prepend_first|append_last">
值
</edit>
注意在 fontconfig 中, 屬性 (property) 可以是一個 list, 亦即一個屬性可有許多的值。 assign 是說把match 到的值取代掉, assign_replace 是說把該 list 的所有值取代成指定的值, prepend 則是插在 list中被 match 到的那個值的前頭, 以此類推。
fonts.conf 裡面有一個範例:
<match target="pattern">
<test name="prefer_outline">
<bool>true</bool>
</test>
<test name="family">
<string>Times</string>
</test>
<edit name="family" mode="prepend" binding="same">
<string>Times New Roman</string>
</edit>
</match>
這個 pattern match 是說, 當 prefer_outline 的值是 true 的時候, 而且字型的 family 又叫做Times, 那麼就把它的 family list 前面加入 Times New Roman。這樣做的原因是, Times 本身是點陣字,如果希望在許多應用程式指定用 Times 顯示時, 不要用點陣字顯示, 而要用 Times New Roman 這個 TrueType字型顯示, 這樣可以把 Times New Roman 的優先權提在 Times 的前面。 Family matching 是另一種match 方法,它的用法和 pattern matching 差不多,只是它是針對個別字型的屬性作修改,用法是:
<match target="font">
...
</match>
舉個例子,如果我想讓所有字型預設能夠打開 anti-aliasing, hinting 並且使用 subpixel rendering, 我就寫:
<match target="font">
<edit name="antialias"><bool>true</bool></edit>
<edit name="rgba" mode="assign"><const>rgb</const></edit>
<edit name="hinting"><bool>true</bool></edit>
</match>
但是我可能覺得 Luxi Mono 這個字型在某些時候, subpixel 不太好看, 我就寫:
<match target="font">
<test name="family"><string>Luxi Mono</string></test>
<edit name="rgba"><const>none</const></edit>
</match>
FAQ
Q. 我手上有很多 ttf, 我要怎麼裝它們?
前面說過啦, 把它們全部丟到 ~/.fonts/ 裡頭去吧。做 symbolic link 也可以。丟完之後就跑一下 fc-list 列出所有已安裝的字型看看有沒有在裡面。
Q. 我裝好了字型, 可是我的程式 (rxvt, aterm, gtk1.x) 卻不能使用它們?
因為這些程式是使用 X 的 core fonts, 不是使用 fontconfig 也沒有支援 Xft,就沒有辦法享受這樣的便利,不過還是可以透過傳統的方式來裝這些字型。新的 gtk2, Gnome2, mlterm, Mozilla(Firebird), Qt3.x 都支援了 fontconfig。
Q. 我想要使用新細明體,可以嗎?
可以, 把 mingliu.ttc 丟到 ~/.fonts 就行了。
Q. 我想要像 Windows 上小字那樣的新細明體,那是怎樣辦到的呢?為什麼在一些大小,新細明體的筆劃會破碎呢?
(新)細明體在 11, 12, 13, 15, 16, 20 點的大小有特別做內嵌的點陣字,換句話說,由於中文字的 hinting不易,有時點陣字會比較有效。又因為新細明體使用了 bytecode 來組合筆劃, 沒有編進 bytecode interpreter 的freetype 版本在 render 的時候,就會碎掉。請確定您系統上 freetype2 的 source 之中,include/freetype/config/ftoption.h 裡面的#defineTT_CONFIG_OPTION_BYTECODE_INTERPRETER 是不是有打開。也不可以使用內建的autohinter. 由於是上述幾個特定的大小是內建點陣字型,所以沒有被 bytecode interpreter 影響。
確定了 freetype 有編進 bytecode interpreter 之後, 設定讓新細明體在這些大小時顯示內建的點陣字而不要用 anti-aliased, 可以在 ~/.fonts.conf 加入:
<match target="font">
<test name="family"><string>PMingLiU</string></test>
<edit name="antialias"><bool>true</bool></edit>
<edit name="hinting"><bool>true</bool></edit>
<edit name="autohint"><bool>false</bool></edit>
</match>
<match target="font">
<test name="family"><string>PMingLiU</string></test>
<test name="size" compare="less_eq"><int>12</int></test>
<edit name="antialias" mode="assign"><bool>false</bool></edit>
<edit name="hinting" mode="assign"><bool>true</bool></edit>
</match>
Q. 我的細明體 (MingLiU) 的英文字和中文字會等寬?
因為 MingLiU 宣稱自己是 monospaced 字型,但實際上它有兩種寬度:中文的全形以及英文的半形。於是 freetype 就被騙了; 同樣的事情也發生在其他華康的一些字型上。Freetype 有個 globaladvance 的 flag:
<match target="font">
<test name="family"><string>MingLiU</string></test>
<edit name="globaladvance"><bool>false</bool></edit>
</match>
萬一因為不明的原因, 這樣做沒有用, 那麼還可以改 spacing:
<match target="font">
<test name="family"><string>MingLiU</string></test>
<edit name="spacing"><int>0</int></edit>
</match>
0 是 proportional 的 spacing, 100 是 mono, 110 是 charcell.
Q. 我想要把 Gnome2 選單的中英文字型分開設。
Gtk2 可以使用兩組特殊的 alias: Sans 和 Serif. Sans 是無襯線的意思,也就是如 Arial, Verdana等等邊緣是方的字。Serif 則是有襯線的字,如 Times. 由於 fontconfig 有字型取代的機制, 可以修改/etc/fonts/fonts.conf 裡面的這一段:
相關:
Screenshot
<alias>
<family>Bitstream Vera Sans</family>
<family>Helvetica</family>
<family>Arial</family>
<family>Verdana</family>
<family>Nimbus Sans L</family>
<family>Luxi Sans</family>
<family>Kochi Gothic</family>
<family>PMingLiU</family>
<family>AR PL KaitiM GB</family>
<family>AR PL KaitiM Big5</family>
<family>Baekmuk Dotum</family>
<family>SimSun</family>
<default><family>sans-serif</family></default>
</alias>
與這一段:
<alias>
<family>sans-serif</family>
<prefer>
<family>Bitstream Vera Sans</family>
<family>Verdana</family>
<family>Nimbus Sans L</family>
<family>Luxi Sans</family>
<family>Arial</family>
<family>Helvetica</family>
<family>Kochi Gothic</family>
<family>PMingLiU</family>
<family>AR PL KaitiM GB</family>
<family>AR PL KaitiM Big5</family>
<family>Baekmuk Dotum</family>
<family>SimSun</family>
</prefer>
</alias>
把想要加入替換 list 的字型加進去。排愈前面的字型, 在當他有符合要顯示的語言的文字的時候就會被用上,如我把 PMingLiU設在文鼎字型前面,PMingLiU 就會在需要顯示中文的時候優先被選到。當然嚴格來說,PMingLiU 並不能算是 Sans-serif而要算是 serif, 但因為我要跟 Bitstream Vera Sans 搭配,故放在一起。
Q. 我遇到了奇怪的問題,可是不知從何找起,怎麼辦?
XFT_DEBUG 這個環境變數可以顯示不同的偵錯資訊,打開一個 terminal, 把 XFT_DEBUG 設在要執行的程式之前,也許它可以幫助你找到問題。其中可以設的數值有:
XFT_DBG_OPEN 1
XFT_DBG_OPENV 2
XFT_DBG_RENDER 4
XFT_DBG_DRAW 8
XFT_DBG_REF 16
XFT_DBG_GLYPH 32
XFT_DBG_GLYPHV 64
XFT_DBG_CACHE 128
XFT_DBG_CACHEV 256
XFT_DBG_MEMORY 512
要同時開啟某幾個偵錯選項,就把它們的值相加就可以了。如 XFT_DEBUG=3 mozilla 就是以開啟第一和第二選項的模式來開啟mozilla. 有趣的是,當 GLYPH 和 GLYPHV 同時開啟時, Xft 會在 console 用 ascii art印出它所畫的字 javascript:window.open(this.src);" style="CURSOR: pointer" onload="return imgzoom(this,550)">
Q. 我手上的字型都很難看。有什麼比較不錯的字型?
英文字型來說, Bitstream Vera Sans, Bitstream Vera Serif, Bitstream Vera Mono都是高品質又是 free 的字型。Bitstream Cyberbit 可以免費取得(現在已經不是免費的了),又有頗完整的 Unicodecoverage, 包含中日韓等等的字集。另外 Microsoft 和 Monotype 買的 Verdana, Times NewRoman 等等也都具有漂亮的 hinting; Kochi Gothic 和 Kochi Mincho 是高品質的 free日文字型。Arial Unicode MS 的 Unicode coverage 也很大,只是這套字型為了這麼大的 coverage,相對地在許多地方,如筆劃與外觀,就必須做出一些犧牲。如果要拿來看中文的小字的話,目前最好把 hinting 關掉(中文字型大部分把hinting 關掉會有比較令人高興的外觀,除了新細明體是一定要打開以外)
Q. 這份 FAQ 實在太沒有幫助了。我要找的問題都找不到。很多地方都寫錯了。
如果有寫錯的地方,為免再造成誤導,也請不吝指正。這裡是 wiki, 也可以直接點上面的 Edit this page 來加入自己的修改。
Q. 我照著這些方法設卻不能動。一切都太麻煩了!
要讓一切合自己的意要付出一定的代價。或許你可以找到一個會設的人,請他吃一頓飯或什麼的,然後找他來幫你照你的意思設。