最近由於仰慕org-mode,從vim遷移到了Emacs。偶然發現org-mode中調出的calendar第一行竟然沒有對齊,排查一下發現是字體的問題。恰好也想改改Emacs的字體,因而我就開始了一段查找資料的過程。html
純原始Emacs,init.el中僅有Customize的theme信息,之前一直使用的是默認字體。筆者剛開始使用Emacs三天,因假期在即得以偷閒有時間查資料。node
現象:Org mode使用C-c .調出的Calendar第一行日期沒有對齊,看起來很是彆扭。git
過程:再橫排出現問題可是豎排沒有,Google之,是字體不等寬的問題。因而乎,最簡單的方式就是設置一個等寬字體,Google 一下「Emacs設置等寬字體」獲得函數set-face-attribute,抄之,寫做github
(set-face-attribute 'default nil :font "Fira Code Retina")
其中,Fira Code是我很喜歡的一個等寬字體。vim
結果:Calendar顯示正常。ide
技術就是解決問題,當問題被解決後繼續投入精力就是浪費精力。函數
然而字體問題遠不止這麼簡單,如今我設置了一個等寬字體,若是我想再換一箇中文字體呢?再使用一遍set-face-attribute
顯然是行不通的。使用微軟雅黑等寬這樣的特製字體天然是很方便,可是這樣就限制了選擇字體的自由。學習
明確一下,個人目標是可以爲不一樣語言定製字體,最好保持等寬,而且可以適應放大縮小。字體
很顯然,問題在於emac在背後是如何選擇一個字體(font)的。從頭捋一遍,思路以下:this
set-face-attribute
是如何起做用的?應該用什麼方式實現定製?前兩個問題使用Google能夠獲得,Emacs在最近(實際上也是好久之前了)的版本中使用Unicode從新實現了一遍,個人Emacs是24,支持Unicode;Emacs支持多種編碼方式,至少utf-八、gb23十二、gbk這樣的編碼方式是支持的。
對於第三個問題,GNU Emacs Manual的font slection一節中指出:
在Emacs將一個字符繪製到圖形顯示設備以前,它必須爲這個字符選擇一個字體。正常來講,Emacs會自動根據該font被賦予的那個face的屬性選擇字體——具體而言,就是face屬性
:family
,:weight
,:slant
, and:width
。這個選擇過程也依賴於被顯示的那個字符——某些字體只能顯示有限的字符。若是沒有精確符合條件的字體,Emacs就會尋找匹配程度最高的字體。
那麼,什麼是face呢?繼續查閱Emacs手冊,在Display Faces一節中,有:
當Emacs顯示給定的文本片斷時,文本的視覺外觀能夠由從不一樣來源指定的face肯定。若是這些來源同時對某個特定的字符指定了超過一個的face,那麼Emacs將會把這些faces的屬性合併起來。
不管是Wiki仍是能找到的資料,對於Face的定義都是」關於要顯示出來的東西的外在屬性的定義「,包括font的屬性(family,width,slant等等),還有顏色、下劃線等等等等(Emacs wiki上甚至說「咱們須要一個明確的定義」)。話句話說,face指定了咱們會看到什麼東西。
同一節指出了合併face的屬性的優先級。其中最低的優先級是default face,也就是我一開始查到的命令所設置的東西,使用M-h f set-face-attribute
能夠獲得
(set-face-attribute FACE FRAME &rest ARGS)
Set attributes of FACE on FRAME from ARGS.
This function overrides the face attributes specified by FACE's
face spec. It is mostly intended for internal use only.If FRAME is nil, set the attributes for all existing frames, as
well as the default for new frames. If FRAME is t, change the
default for new frames only.
由於設置了默認的face,而且init.el和別的插件(org)也沒有更改face,因此對於可以用Fira Code顯示的character,Emacs自動選擇了Fira Code。
那麼,Emacs又是如何尋找」匹配程度最高「的字體的呢?這就不得不說到另一個概念了:fontset
Emacs Wiki上對fontset有一個基本的描述,總結起來要點以下:
使用M-x describe-fontset <RET> <RET>
能夠查詢到當前fontset的詳細信息(運行比較慢),個人顯示以下(通過了修改):
Fontset: -outline-Fira Code Retina-normal-normal-normal-mono-17----c--fontset-auto1
CHAR RANGE (CODE RANGE)
FONT NAME (REQUESTED and [OPENED])
C-@ .. (#x43 .. #x9F)
-------------iso8859-1
(#xA0)
--微軟雅黑------------
¡ .. « (#xA1 .. #xAB)
-------------iso8859-1
¬ (#xAC)
--微軟雅黑------------
.. ¯ (#xAD .. #xAF)
-------------iso8859-1
° .. ± (#xB0 .. #xB1)
--微軟雅黑------------*
...
CHAR RANGE打頭的第二行以及第三行是表頭,下面每兩行是一組,每組第一行格式是
<範圍開始處符號> .. <範圍結束處符號> (<範圍開始處符號碼值> .. <範圍結束處符號碼值>)
第二行便是XLFD格式的font描述,這是X window system的字體標準。
當Emacs發現指定的face中的font(個人是Fira Code)沒法顯示這個字符時,它就會按照字符集到fontset中找到可以顯示這個字符的字體,而且使用之。
fontset可使用set-fontset-font
來進行修改。我設置中文字符的代碼以下,其中script的順序來自這篇博客:
(dolist (charset '(kana han symbol cjk-misc bopomofo)) (set-fontset-font (frame-parameter nil 'font) charset (font-spec :family "微軟雅黑"))
set-fontset-font的使用很是易於理解,
(set-fontset-font NAME TARGET FONT-SPEC &optional FRAME ADD)
Modify fontset NAME to use FONT-SPEC for TARGET characters.
以上代碼其實就是從frame-parameter中取出當前frame的fontset,而後向這個fontset插入某些字符的字體。TARGET能夠是字符範圍的起始和結束的cons;能夠是script的名字(個人就是script的名字),也能夠是一個charset。FONT-SPEC能夠用font-spec來肯定字體,不用手寫XLFD了。
使用按鍵組合C-u C-x =
能夠查看point下的那個character的信息,好比筆者在」你「字上按下以後顯示如此:
position: 192 of 192 (99%), column: 0
character: 你 (displayed as 你) (codepoint 20320, #o47540, #x4f60)
preferred charset: chinese-gbk (GBK Chinese simplified.)
code point in charset: 0xC4E3
script: han
syntax: w which means: word
category: .:Base, C:2-byte han, L:Left-to-right (strong), c:Chinese, j:Japanese, |:line breakable
to input: type "C-x 8 RET HEX-CODEPOINT" or "C-x 8 RET NAME"
buffer code: #xE4 #xBD #xA0
file code: #xC4 #xE3 (encoded by coding system chinese-gbk-dos)
display: by this font (glyph code)
uniscribe:-outline-微軟雅黑-normal-normal-normal-sans-20----p--iso8859-1 (#x482)
Character code properties: customize what to show
name: CJK IDEOGRAPH-4F60
general-category: Lo (Letter, Other)
decomposition: (20320) ('你')
關於emacs的中英文混排下的等寬以及放縮兼容,這篇博客狠狠地折騰了一把Emacs中文字體進行了一系列探索,改進了把中文字體和英文字體各自設置一個固定的值的方法,轉爲某種字體設置放縮係數,最終獲得了一個不錯的結果。
可是字體之間的寬度並非一個固定的比例,對於每種不一樣的中文——英文字體組合,使用者都須要找不一樣的參數,仍是比較麻煩的。雖然字體並非一個常換的東西(也許。從這個角度講,或許直接換一箇中英文兼有的等寬字體纔是正道。
github上也有個項目cnfonts,可以解決中英混排等寬的問題,做者自述原理是」讓中文字體和英文字體使用不一樣的字號,從而實現中英文對齊「,效果很是不錯,安裝也很方便,推薦你們試試。
魚和熊掌不可得兼,選擇了選取字體的自由後,就勢必犧牲了適配的便捷性。
另外,找完以後才發現,我上一秒還在看Org-mode學習timestamp的用法,回過神來就已經開了十幾個網頁學習Emacs的font了。這種time-killer的折騰仍是須要謹慎。