最近由于仰慕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的折腾仍是须要谨慎。