为emacs正确配置英文、中文、符号字体的正确方式?

记录自己的一些折腾经历,也希望有高人能帮忙解答一些困惑。

在自己攒配置的过程中,最早抄的是centaur emacs中custom-example.el的配置,并且自己稍作了一些修改:

(defun centaur-setup-fonts ()
  "Setup fonts."
  (when (display-graphic-p)
    ;; Set default font
    (cl-loop for font in '("Cascadia Code" "Fira Code" "Jetbrains Mono"
                           "SF Mono" "Hack" "Source Code Pro" "Menlo"
                           "Monaco" "DejaVu Sans Mono" "Consolas")
             when (font-installed-p font)
             return (set-face-attribute 'default nil
                                        :family font
                                        :height (cond (sys/macp 130)
                                                      (sys/win32p 110)
                                                      (t 100))))

    ;; Set mode-line font
    ;; (cl-loop for font in '("Menlo" "SF Pro Display" "Helvetica")
    ;;          when (font-installed-p font)
    ;;          return (progn
    ;;                   (set-face-attribute 'mode-line nil :family font :height 120)
    ;;                   (when (facep 'mode-line-active)
    ;;                     (set-face-attribute 'mode-line-active nil :family font :height 120))
    ;;                   (set-face-attribute 'mode-line-inactive nil :family font :height 120)))

    ;; Specify font for all unicode characters
    (cl-loop for font in '("Segoe UI Symbol" "Symbola" "Symbol")
             when (font-installed-p font)
             return (set-fontset-font t 'unicode font nil 'prepend))

    ;; Emoji
    (cl-loop for font in '("Noto Color Emoji" "Apple Color Emoji")
             when (font-installed-p font)
             return (if (>= emacs-major-version 28)
                        (set-fontset-font t 'emoji (font-spec :family font) nil 'prepend)
                      (set-fontset-font t 'symbol (font-spec :family font) nil 'prepend)))

    ;; Specify font for Chinese characters
    (cl-loop for font in '("WenQuanYi Micro Hei" "PingFang SC" "Microsoft Yahei" "STFangsong")
             when (font-installed-p font)
             return (progn
                      (setq face-font-rescale-alist `((,font . 1.3)))
                      (set-fontset-font t '(#x4e00 . #x9fff) (font-spec :family font))))))

(centaur-setup-fonts)
(add-hook 'window-setup-hook #'centaur-setup-fonts)
(add-hook 'server-after-make-frame-hook #'centaur-setup-fonts)

这当中我觉得最妙的地方在于window-setup-hookserver-after-make-frame-hook,由于我的习惯是后台始终开着emacs daemon,如果不把字体配置挂到server-after-make-frame-hook下,emacsclient所创建的frame字体就会出错。同时,又可以单独设置中英文字体,可以自己杂烩喜欢的字体,定制性强的同时,也格外方便迁移。

折腾点一:符号与emoji

centaur的版本先设置了"Segoe UI Symbol" "Symbola" "Symbol"给所有的unicode用,然后在此基础上单独设置中文与emoji字体。但是,无论是unicode还是emoji,似乎都无法一款字体走天下,包含所有的符号。参考mastering emacs的文章,我使用了unicode-fonts这个包。虽然显示效果只是差强人意,但是确实绝大部分的符号和emoji都能正确显示了。不过我仍然不清楚unicode-fonts与上面set-fontset-font的优先级关系。绝大部分情况下,我还是希望自己指定一个主要使用的字体,有不能显示的再从其他字体当中寻求补全,以得到更好的一致性。

折腾点二:中文vsCJK

centaur代码里有一段(set-fontset-font t '(#x4e00 . #x9fff) (font-spec :family font)),这里的'(#x4e00 . #x9fff)让我格外在意,这是否正好对应了中文在unicode当中的起止位置呢?

Emacs manual当中说到:

For example, Japanese characters belong to the ‘kana’ script, but Japanese text also mixes them with Chinese characters so the following uses the ‘han’ script to set up Emacs to use the ‘Kochi Gothic’ font for Japanese text:

(set-fontset-font "fontset-default" 'han "Kochi Gothic")

(For convenience, the ‘han’ script in Emacs is set up to support all of the Chinese, Japanese, and Korean, a.k.a. CJK, characters, not just Chinese characters.)

我不清楚,如果这里改为(set-fontset-font t 'han (font-spec :family font))避免一些硬编码,是否会更好呢?

此外,我使用的是‎霞鹜文楷作为主要的中文显示字体,但这款字体并不包含日文、韩文字体,所以在显示日文时显得格外别扭(此处未能成功复现问题,之前に、け、る都fallback到了一个sarasa mono的italic字体上,导致汉字直立夹着假名斜体):

图片

对此,我的解决方案是分别设置中文与日文字体

(set-fontset-font t 'han (font-spec :family "霞鹜文楷" :weight 'bold))
(set-fontset-font t 'kana (font-spec :family "Sarasa Gothic J" :weight 'normal :slant 'normal))

折腾点三(尚未尝试):fontaine.el

近两年的emacs新星protesilaos出品的包fontaine.el也让我十分感兴趣,能够把字体设置到非常细的粒度:

(regular
 ;; I keep all properties for didactic purposes, but most can be
 ;; omitted.
 :default-family "Monospace"
 :default-weight regular
 :default-height 100
 :fixed-pitch-family nil ; falls back to :default-family
 :fixed-pitch-weight nil ; falls back to :default-weight
 :fixed-pitch-height 1.0
 :fixed-pitch-serif-family nil ; falls back to :default-family
 :fixed-pitch-serif-weight nil ; falls back to :default-weight
 :fixed-pitch-serif-height 1.0
 :variable-pitch-family "Sans"
 :variable-pitch-weight nil
 :variable-pitch-height 1.0
 :bold-family nil ; use whatever the underlying face has
 :bold-weight bold
 :italic-family nil
 :italic-slant italic
 :line-spacing nil)

涉及到等宽、变宽字体,衬线、无衬线字体,但对于非西文字体则没有涉及。这方面的设置可以认为与前面的不同语言字体设置是正交的,非常期待这两者如果配合起来会摩擦出什么样的火花来。

9 个赞

日语书写体系包括汉字和假名。

'han 包括中文、日语、韩语中的汉字,但是不包含日语假名。

'kana 用于定义日语假名。例如: “に、け、る”等。

具体的讨论可以参见这里。符号之类的或许还要'CJK-misc

2 个赞

我emacs中显示的symbol, 一部分是默认字体(Menlo)的,一部分是Apple Color Emoji。 不知道有没有办法让所有symbol都是用Apple Color Emoji。

set-default-fontset set-frame-font set-face-attribute 'default

三者之间是什么关系?网上搜到了很多set-default-fontset的用法,李杀用的是set-frame-font,centaur里面用的又是为set-frace-attribute来设置default字体,这三种用法有什么异同?

set-frame-font set-face-attribute 'default 这两个作用差不多(见下方引用),都可用作设置默认字体。单一的某种字体可能无法显示所有字符,但是字体集(fontset, a list of fonts)可以。

set-face-attribute 'default函数的参数更多,可以设置下划线、粗体等, 详见link

set-default-fontset我没有找到解释。你指的是不是set-fontset-font,这个主要是对于上面设置的默认字体不支持的字符提供字体集,从而让字符更好地显示。

Globally Change the Default Font link

To change the default font for new (non special-display) frames, put either of these in your init file:

(add-to-list 'default-frame-alist '(font . FONT ))

(set-face-attribute 'default t :font FONT )

To change the default font for the current frame, as well as future frames, put either of these in your init file:

(set-face-attribute 'default nil :font FONT )

(set-frame-font FONT nil t)

这些函数的‘FONT’ 参数也可以使用fontset。Emacs里面可以自己构建fontset, 不过具体操作我还不是很懂。

3 个赞

In emacs-fontsets:

If no fontset is specified for use, then Emacs uses an ASCII font, with ‘fontset-default’ as a fallback for characters the font does not cover. The standard fontset is only used if explicitly requested, despite its name.

In emacs-modifying fontsets:

Fontset settings like those above only affect characters that the default font doesn’t support, so if the ‘Kochi Gothic’ font covers Latin characters, it will not be used for displaying Latin scripts, since the default font used by Emacs usually covers Basic Latin.

默认字体default font 优先于‘fontset-default’。 set-font-set一般设置的是‘fontset-default’。 如果unicode-fonts修改了default font, 那么unicode-fonts优先于set-fontset-font。

怎么才能将汉字宽度设置成英文字符的两倍呢?

那样更有利于对其,但是似乎比较难设置?

选择等宽等高的字体,比如英文字体 Iosevka 和霞鹜文楷、仓耳今楷就是等高,宽度1:2的。

1 个赞

这个折腾的怎么样了?有什么心得分享下吗?

一位大佬整合后的代码

1 个赞

全贴过去就行了吗

font-installed-p 怎么定义的

(defun font-installed-p (font-name)
  "Check if font with FONT-NAME is available."
  (find-font (font-spec :name font-name)))

好,谢谢,我试试看呢。