缘起

最近因为仰慕org-mode,从vim迁移到了Emacs。偶然发现org-mode中调出的calendar第一行居然没有对齐,排查一下发现是字体的问题。刚好也想改改Emacs的字体,于是我就开始了一段查找资料的过程。

背景

纯原始Emacs,init.el中仅有Customize的theme信息,以前一直使用的是默认字体。笔者刚开始使用Emacs三天,因假期在即得以偷闲有时间查资料。

浅探

现象:Org mode使用C-c .调出的Calendar第一行日期没有对齐,看起来非常别扭。

过程:再横排出现问题但是竖排没有,Google之,是字体不等宽的问题。于是乎,最简单的方式就是设置一个等宽字体,Google 一下“Emacs设置等宽字体”得到函数set-face-attribute,抄之,写作

(set-face-attribute 'default nil :font "Fira Code Retina")

其中,Fira Code是我很喜欢的一个等宽字体。

结果:Calendar显示正常。

追究

技术就是解决问题,当问题被解决后继续投入精力就是浪费精力。

然而字体问题远不止这么简单,现在我设置了一个等宽字体,如果我想再换一个中文字体呢?再使用一遍set-face-attribute显然是行不通的。使用微软雅黑等宽这样的特制字体自然是很方便,但是这样就限制了选择字体的自由。

路线

明确一下,我的目标是能够为不同语言定制字体,最好保持等宽,并且能够适应放大缩小。

很显然,问题在于emac在背后是如何选择一个字体(font)的。从头捋一遍,思路如下:

  1. Emacs内部使用的编码集是什么?是否支持Unicode?
  2. Emacs对编码方式的支持如何?
  3. 对特定的character,Emacs如何选择字体?
  4. set-face-attribute是如何起作用的?应该用什么方式实现定制?

前两个问题使用Google可以得到,Emacs在最近(实际上也是很久以前了)的版本中使用Unicode重新实现了一遍,我的Emacs是24,支持Unicode;Emacs支持多种编码方式,至少utf-8、gb2312、gbk这样的编码方式是支持的。

face

对于第三个问题,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

fontset

Emacs Wiki上对fontset有一个基本的描述,总结起来要点如下:

  • fontset是能够确定某个font能表示哪些character。它使用<CHARSET or CHAR RANGE> - <FONT NAME>的二元组的表来实现这一点。
  • fontset可以被修改,因而如果想使用某种特殊的font来绘制某些字符,使用标准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,能够解决中英混排等宽的问题,作者自述原理是”让中文字体和英文字体使用不同的字号,从而实现中英文对齐“,效果非常不错,安装也很方便,推荐大家试试。

结论

鱼和熊掌不可得兼,选择了选取字体的自由后,就势必牺牲了适配的便捷性。

  • 可以set-face-attribute设置一个默认的中英文等宽字体
  • 通过修改fontset来修改特定的字体
  • 可以使用第三方包来解决问题。

另外,找完之后才发现,我上一秒还在看Org-mode学习timestamp的用法,回过神来就已经开了十几个网页学习Emacs的font了。这种time-killer的折腾还是需要谨慎。

font and face, 浅探Emacs字体选择机制及部分记录的更多相关文章

  1. [Python][小知识][NO.4] wxPython 字体选择对话框(O.O 不知道放到那里就放到这个分类的)

    1.前言 O.O 前两天回家浪了两天,断更了 哎~~~ o.o 有时候,有木有想改标签或编辑框中内容的字体呀?(o.o 反正我是没有). wxpython也可以说是所在的操作系统,有字体选择器,给我们 ...

  2. OCR技术浅探(转)

    网址:https://spaces.ac.cn/archives/3785 OCR技术浅探 作为OCR系统的第一步,特征提取是希望找出图像中候选的文字区域特征,以便我们在第二步进行文字定位和第三步进行 ...

  3. PyQt4 颜色选择,字体选择代码

    # -*- coding: utf-8 -*- """ ------------------------------------------------- File Na ...

  4. 跟我一起学QT_QT标准对话框_字体选择框

    标准对话框 QT的标准对话框分为以下几种 颜色对话框 文件对话框 字体对话框 输入对话框 消息对话框 进度对话框 错误信息对话框 向导对话框 字体选择框 字体选择框位于 QFontDialog 类中 ...

  5. Html中版权符号的字体选择问题(如何让版权符号更美观)

    一.发现问题 ©是html的中版权的符号,但是字体选择的不对会带来一些问题.如果是宋体,这个符号显示的就是很奇怪的一个符号. 二.解决问题 复制代码 代码如下: <span style=&quo ...

  6. OCR技术浅探:基于深度学习和语言模型的印刷文字OCR系统

    作者: 苏剑林 系列博文: 科学空间 OCR技术浅探:1. 全文简述 OCR技术浅探:2. 背景与假设 OCR技术浅探:3. 特征提取(1) OCR技术浅探:3. 特征提取(2) OCR技术浅探:4. ...

  7. 字体选择框QFontComboBox

    self.combobox_2 = QFontComboBox(self)  # 实例化字体列表框 combobox.currentFont()  返回字体选择框中当前的字体 self.combobo ...

  8. 浅谈:Redis持久化机制(二)AOF篇

    浅谈:Redis持久化机制(二)AOF篇 ​ 上一篇我们提及到了redis的默认持久化方式RDB,是一种通过存储快照数据方式持久化的机制,它在宕机后会丢失掉最后一次更新RDB文件后的数据,这也是由于它 ...

  9. 浅谈Java的反射机制和作用

    浅谈Java的反射机制和作用 作者:Java大师 欢迎转载,转载请注明出处 很多刚学Java反射的同学可能对反射技术一头雾水,为什么要学习反射,学习反射有什么作用,不用反射,通过new也能创建用户对象 ...

随机推荐

  1. Mysql跨数据库(在同一IP地址中)复制表

    数据库表间数据复制分类 在利用数据库开发时,常常会将一些表之间的数据互相导入.当然可以编写程序实现,但是,程序常常需要开发环境,不方便.最方便是利用sql语言直接导入.既方便而修改也简单.以下就是导入 ...

  2. while、dowhile、switchcase 循环嵌套、穷举、迭代

    for(var i=0;i<5;i++)//假如规定到5 { document.write("123456<br/>") } 如果变成这样 var i=0: fo ...

  3. java8新特性学习1

    java8增加了不少新特性,下面就一些常见的新特性进行学习... 1.接口中的方法 2.函数式接口 3.Lambda表达式 4.java8内置的四大核心函数式接口 5.方法引用和构造器引用 6.Str ...

  4. ios appstore 上架应用被拒绝原因

    ios appstore 上架应用被拒绝原因 应用程序崩溃 界面布局有明显错误挂羊头卖狗头的应用包括未公开的或隐藏功能的使用私有API应用程序读取或写入数据超出其指定的容器区域以任何方式下载代码的应用 ...

  5. jquery mobile 移动web(6)

    jquery mobile 针对移动端设备的事件类型. 1.touch 事件. tap 快速触摸屏幕并且离开,类似一种完整的点击操作. taphold 触摸屏幕并保持一段时间. swipe 在1秒内水 ...

  6. React中需要多个倒计时的问题

    最近有一个需求是做一个闪购列表,列表中每一个商品都有倒计时,如果每一个倒计时都去生成一个setTimeout的话,一个页面就会有很多定时器,感觉这种做法不是非常好,于是换了一个思路. 思路是这样的,一 ...

  7. jquery获取父级元素、子级元素、兄弟元素

    1:$(this).parent(expr) 找父亲节点,可以传入expr进行过滤,比如$("span").parent()或者$("span").parent ...

  8. [Usaco2006 Mar]Mooo 奶牛的歌声(单调栈裸题)

    1657: [Usaco2006 Mar]Mooo 奶牛的歌声 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 961  Solved: 679[Submi ...

  9. 洛谷P1196 [NOI2002]银河英雄传说(带权并查集)

    题目描述 公元五八○一年,地球居民迁至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展. 宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争.泰山压顶 ...

  10. 网站title标题被改并被百度网址安全中心提醒的解决办法

    国庆假日期间我们Sine安全接到众多网站站长求助网站标题被改导致在百度搜索中百度安全中心提醒被拦截,导致网站正常用户无法浏览网站被跳转到一些菠菜du博网站,而且很明显的一个特征就是在百度中搜索关键词的 ...