本意是想让博客看起来像是$\LaTeX$文档。

1. 使用$\LaTeX$字体

1.1 换个字体

如果仅仅是想更换字体,那么只需要先找到这部分内容对应的css样式文件修改即可。比如我的主题中正文部分的样式文件位于assets/css/common/main.css,接下来指定font-family参数:

.main {
    font-family: default_font_1, default_font_2, serif;
}

之后Hugo会首先尝试使用第一种字体,如果系统中不存在该字体,则会使用第二种字体,以此类推。最后的serif指使用系统默认的衬线字体。

1.2 使用自定义字体

其实使用自定义字体最快的方式就是选择使用自己喜欢字体的主题,比如我一开始就想用texify3这个$\LaTeX$风格的主题,但是后来发现魔改难度比较大于是换了现在的主题。所以我就想是不是只要把texify3主题中的字体和样式文件复制到我的主题的对应位置就行了?

texify3主题中正文部分的结构文件位于layouts/_default/baseof.html,样式文件位于assets/sass/layouts/common.scss,字体调用的样式文件位于assets/sass/fonts.scss,字体文件位于static/fonts/,具体来说

baseof.html:

<div id="wrapper">
    {{ block "main" . }}{{ end }}
</div>

common.scss:

@import "../fonts";

#wrapper {
    font-family: "Latin Modern Roman", "Times New Roman", serif;
}

fonts.scss:

@font-face {
    font-display: swap;
    font-family: 'Latin Modern Roman';
    font-style: normal;
    font-weight: normal;
    src: url('../fonts/lmroman-normal.woff');
}

有趣的事情发生了,fonts.scss中使用的路径十分奇怪,既不是相对结构文件的路径,也不是相对样式文件的路径,但却可行!

ChatGPT对于src参数的解释是相对样式文件的路径,但是如果使用“正确”的路径../../static/fonts/lmroman-normal.woff会发现不可行。

另外使用绝对路径/Users/.../fonts/lmroman-normal.woff或是{{ lmroman-normal.woff | absURL}}也都不可行。

更糟糕的是,复制到我的主题后,连一开始的../fonts/lmroman-normal.woff也不可行了。

起初我以为是scss格式的问题,因为在texify3中使用scss样式文件时有转译为css格式的逻辑

{{ $options := dict "transpiler" "dartsass" "targetPath" "css/common.css" }}
{{ with resources.Get "sass/layouts/common.scss" | toCSS $options | postCSS | minify | fingerprint }}
    <link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
{{ end }}

但是事实上@font-face部分语法是完全相同的,用ChatGPT的回答就是“没有用到scss的特性”。

1.3 问题解决

在我的主题中,样式文件位于assets/css/common/,字体文件位于static/fonts/

有一次偶然间我把路径改成../../fonts/lmroman-normal.woff(加了一个后退../),然后就好了!而且不管加多少个都行!比如../../../../../fonts/lmroman-normal.woff

谁知道是为什么呢?

1.4 “Latin Modern Roman” + 宋体

现在回到正题,我们解决了如何调用字体文件的问题,但是这些字体文件从何而来呢?

$\LaTeX$默认的英文字体就是texify3中使用的“Latin Modern Roman”,默认的中文字体是宋体。

我在网上找到了一些宋体的字体文件,比如这个,但是实装下来私以为不怎么好看,感觉和$\LaTeX$中使用的有所差别,于是弃用。还有这个,是思源宋体等一类字体的源文件,可以用来创建对应的字体文件,相对繁琐。

最后使用的是Mac自带的宋体,也是Mac默认的衬线字体,用起来感觉就是$\LaTeX$!(后来一想,可能是因为$\LaTeX$用的本来就是系统自带的宋体?一整个白忙活)

2. 仅自己可见?

以上对于孤芳自赏者算是足够了,但是作为用户不一定安装了这些字体,甚至换成手机或是平板就打回原形了。(于是对于孤芳自赏者都不足够了)怎么办呢,其实只要把字体文件也放在服务器上,若有需要(强制)下载即可。

主要是这个宋体。打开Mac的自带应用“字体册”(Font Book),搜索“宋体”,右键“在访达中显示”,即可得到ttc格式的八合一宋体大礼包。ttc是“TrueType Collection”的缩写,可以包含多个ttf字体,而ttf是“TrueType Font”的缩写,通常包含单个字体。

这里给出了转换方法。对于Mac用户最好用的应该是第四个回答中提到的傻瓜应用DfontSplitter: ttc$\mapsto$ttf,在应用商店免费下载,甚至没有广告。其他系统可以参考第一个回答(使用fontforge: ttc$\mapsto$sfd,风格古早,操作复杂)或是第二个回答(使用onlinefontconverter.com,没试过)。

转换完成后会得到8个ttf格式的字体文件,其中有4个属于将要用到的字体“宋体-简”,为从细到粗的Light, Regular, Bold, Black四种款式。可以通过肉眼分辨,也可以使用之前提到的fontforge,或者直接在我的仓库下载。

最后实际用下来感觉只需要RegularBlack一细一粗(对应font-weight: normal/bold)两种就可以了其实。

3. 字体精简

一个新的问题是,这个宋体的字体文件实在太大了,比其他所有文件加起来还要大(接近70mb),导致用户第一次加载时需要很长时间(比如热心网友rqdmap花了38.44s)来下载字体文件。

事实上这些字体文件是有大量冗余的,一方面宋体本身包含了中英文和大量符号而我们只需要中文部分,另一方面博客本来就没多少内容(sad)。于是一个朴素的想法就是统计博客用到的所有字符并依此精简字体文件,具体实现参考网页字体精简方案。代码部分完全照搬就好了,只有以下两处微末改动:

  1. 统计字符时,由于我是在IDE中运行的,需要手动调整工作目录。
  2. 调用fontmin时,可以直接输出为woff2格式
var fontmin = new Fontmin()
	.src(['./path/to/src.ttf'])
	.dest('./path/to/dest')
	.use(Fontmin.glyph({
		text: text,
		hinting: false,
	}))
	.use(Fontmin.ttf2woff2());

最后在博客更新脚本中使用subprocess库调用就好了

# run blog_word_count.py
os.chdir('path/to/blog_word_count.py')
subprocess.run("python blog_word_count.py", shell=True)

# run min.js
os.chdir('path/to/min.js')
subprocess.run("node min.js", shell=True)

Songti_SC_regular.ttf为例,原始文件的大小是20.9M,精简之后的大小是455K,转为woff2后的大小是238K,成效卓著。

另外对于这些字体文件格式的差别,从文件大小的角度简单来说就是ttf > woff > woff2,以下是ChatGPT的回答


TTF(TrueType Font)、WOFF(Web Open Font Format)和WOFF2(Web Open Font Format 2)是三种常见的字体文件格式,它们在设计、压缩和用途等方面略有不同:

TTF(TrueType Font):

  • TTF 是一种常见的字体文件格式,最初由Apple和Microsoft共同开发,用于在计算机上显示字体。它使用了一种称为 TrueType 的字体渲染技术。
  • TTF 文件通常较大,因为它们包含了字体的完整信息,包括轮廓、字形、字符集等。
  • TTF 字体在网页上使用时,可能会导致较长的加载时间和较高的网络带宽消耗。

WOFF(Web Open Font Format):

  • WOFF 是一种专门为网络优化的字体文件格式,旨在提供跨平台的字体支持,并通过压缩减少字体文件的大小。
  • WOFF 格式相比于 TTF,经过了压缩,因此文件大小更小,有助于提高网页的加载速度和性能。
  • WOFF 格式不支持加密,但支持元数据,可以提供更多的字体信息。

WOFF2(Web Open Font Format 2):

  • WOFF2 是 WOFF 格式的进化版,旨在进一步减少字体文件的大小和提高性能。
  • WOFF2 文件经过了更强的压缩,相比于 WOFF 文件,可以实现更大的压缩率,因此文件大小更小。
  • WOFF2 格式支持使用 Brotli 压缩算法,这使得它在相同质量下比 WOFF 格式更小。
  • WOFF2 格式还提供了更好的压缩和解压性能,因此在网络传输和网页加载方面更为高效。

总的来说,WOFF 和 WOFF2 是专门为网络优化设计的字体文件格式,它们相对于传统的 TTF 格式具有更好的压缩率和性能,因此更适合在网页上使用。而WOFF2 是WOFF的升级版本,具有更高的压缩效率和更好的性能。


4. 使用Nerd Fonts

Nerd Fonts是一个开源项目,目的是给一些主流的编程字体加上各种图标,这样显示图标时不需要使用额外的工具,对于博客来说则是不需要再用svg一笔一笔画出来了。以下列举了几种Nerd Fonts的使用方法。

4.1 调用样式表

这是我最终采取的懒人方式~~(适用于低技术力人群)~~,参考Nerd Fonts/Icons。只需要在head部分加入

<head>
    <link rel="stylesheet" href="https://www.nerdfonts.com/assets/css/webfont.css">
    ...
</head>

即可。然后调用图标<i class="nf nf-fa-heart"></i>$\Rightarrow$

这样做的好处是不需要考虑字体文件和调用逻辑,直接用图标就完事了。事实上这和本文的主题也比较契合,因为对于Nerd Fonts中的上万个图标,如果只是取一瓢饮,并不需要使用完整的字体文件。

一个问题

如果直接在markdown中调用图标但是不管用需要关闭Hugo的安全模式,其在渲染时会自动跳过markdown中的html语法。只需要在config.toml中加入

[markup.goldmark.renderer]
    unsafe = true  # Allow HTML in md files

参考stackocerflow

4.2 直接使用Nerd Fonts字体文件

前往Nerd Fonts/Fonts下载现成的Nerd Fonts字体也是不错的选择,但仅限于主流编程字体,我使用的"Latin Modern Roman" + 宋体均不在其列。

4.3 使用Nerd Fonts字体补丁

Nerd Fonts授人以渔,使用font-patcher可以给其他任何字体打上图标补丁。使用方法不再赘述,仅记录几个遇到的问题

  • 与非主流编程字体比如宋体冲突,在打补丁的过程中不断出现“名为xxx的字形被映射到xxx,但它的名称表明它应映射到xxx”的警告。
  • 与之前提到的字体精简方法冲突,先打补丁再使用字体精简脚本会由于上面的原因报错。
  • 如果先精简字体再打补丁,会导致字体文件肉眼可见地变大(精简之后字符大约有2k+,而图标有1w+)。

至于字体文件编码方式云云不想深究,退而使用第一种方法。

$\ddot{\smile}$