8 月 19 日更新:一些关于 a11y 和排版方式的 内容 已经变化。20 日更新:加入了 新样式。2020 年 2 月 13 日更新:为过窄的页面设计了一个新的布局。
在博客中使用 BibTeX 的需求起源于一些需要引用文献的博文。例如之前的 NC¹ 属性加密 KW19 ,那时候我是手工格式化参考文献的。当然,能自动化就不要手工去做,于是几个月前开始了 BibTeX-TS——TypeScript 写的 BibTeX 解析器。现在设施已经足够完备,我可以把
alpha.bst
的实现近似翻译到 JavaScript 里,成为博客构建系统的一部分。位图版的题图(BibTeX 徽标)衍生自 Wikimedia Commons。
BibTeX-TS 和博客工具链
BibTeX-TS 的需求脱胎于我对 blogging 工具链的要求,不过它是一个一般向的项目。主要功能是把 .bib 文件解析为结构化的对象并加以操作。
整个过程花了几个月,从西雅图到北京,我在研究的空余时间(或者说研究累了需要换换脑筋)慢慢完成。中间有两次推倒重来的过程:第一次没有记录在 git 的历史里,第二次可以在 git 的历史中找到。
在西雅图的时候主要写了基本对象模型、解析器和 BST 语言的基本函数(例如 purify$
用于清理字符串、format.name$
用于格式化名字),几个比较有意思的:
- 由于面向对象的引入,一些 BST 函数被分成了几步,譬如
format.name$
本来是接受两个字符串和一个整数(一堆名字、一个名字格式和第几个名字)输出一个字符串(格式化好的那个名字),在 BibTeX-TS 里分成把字符串解析成名字、把字符串解析成名字格式、用名字格式格式化名字三部分。 - 我发现了一个新的写 parser 的模式——用下一个状态转移的委托(函数指针)表示状态,例如 .bib 解析器 就是这样写出来的。
- 我看了很多遍 Déc07Mar09 ,这两篇文档详尽地解释了 BibTeX 的细节(比 bibtex.org 清楚多了)。很多细节都是值得注意的,还有一部分细节是过时的(例如
purify$
也会删除所有非 ASCII 字符,这对汉语文献非常不友好,虽然可能并不需要引用很多汉语文献),我希望和原版尽量接近,但也有选择性。
在北京做的部分是实现简易 TeX 渲染框架并实例化框架、着手翻译 alpha.bst
的实现(我翻译的方法就是盯着它并把它的每个类型的渲染函数翻译一遍;实际上很容易写一个 BST 解析器和解释器,毕竟基础设施已经有了,而且 BST 是一个非常容易模拟的语言),见于 此(当然这个实现和 alpha.bst
是有出入的,例如我没有对文章标题做 title case 转换,因为很多人根本不会写 BibTeX 文件,按照 alpha.bst
的程序渲染出来的结果乱七八糟)。还有就是(我已经轻车熟路的)实现 BibTeX-TS 和博客工具链之间的胶水,以及稍微更新一下实现用 Markdown 行内代码 `cite:key1,key2`
语法做文内引用、用代码块 ```blog-bib
语法做 BibTeX 渲染,以及把锚记跳转 hook 起来。
整个过程的结果就是我的博客以后可以用 BibTeX 自动格式化参考文献,以及手有点抽筋儿。
自定义样式
可以用 @string { property = {value} }
自定义样式:
设置 | 属性 | 取值(第一个为默认) |
---|---|---|
BibTeX 样式 | bst |
alpha、plain、abbrv |
引用周围的括号 | brackets |
[]、() |
多个引用的分隔符 | comma |
,、; |
分隔符后空白大小 | space |
thin、normal、none |
昵称列格式 | nickname |
cite、[]、. |
昵称列对齐 | align |
right、left |
语言 | lang |
任意 |
其中,若 nickname
值为 cite,则根据 brackets
推断:如果 brackets
是 [],则 nickname
取 [];如果 brackets
是 (),则 nickname
取 .。align
仅对足够宽的页面生效。lang
属性用于设置参考文献 div
的 lang
属性,如果没有则不设置。修改上述属性的例子见 Blogging my blog。
例子
本文本身就是一个例子,文中的 BibTeX 都可以直接从官网复制而不加修改(当然我实际上修改了一些让渲染结果更适合 Web)。几个有意思的特性:
- 所有的功能都是静态的,客户端不需要 JavaScript。
- 支持 crossref 交叉引用(这是标准样式的一部分),作为引用文章描述文字的 TeX 代码中包含的
\cite
也会被处理,例如 KW19 的描述文字里有一个对 IR19 的引用。 - 多次引用同一篇文章,每次的引用都有自己的身份,因此点击引用的链接后可以通过“返回”链接重新回到正确的位置。例如 Dam92 和 Dam92 。
- 可以一次引用多篇文章,例如 GSW13QWW18CDG⁺17 。
- 为了支持汉语文章的渲染,我添加了两个新的 TeX 控制序列
\nickname
和\zhCN
。前者只有在渲染文章昵称(首字母)时才渲染当前分组内的后续内容,否则不渲染;后者把该分组内后续内容渲染为带有汉语语言标记的 HTML。如果作者名字写 {\nickname L}{\nickname i}{\nickname}{\nickname\bgroup}{ }{\egroup\egroup\bgroup\zhCN 李雷},则渲染结果是 Li19 。前面三个\nickname
控制序列是为了确保提取文章昵称的时候得到 Li,后面通过巧妙的组合使得purify$
整个字符串的时候得到的是 Li 李雷(最长的拼音音节可以有 6 个字符,对齐是为了排序),并且通过\nickname
控制序列确保 Li 不会被渲染。 - 如果你在 LaTeX 里
\cite{key1,key2}
且选择逗点后不加空格,则 LaTeX 似乎不允许在一个\cite
控制序列产生的中括号内换行(至少 LLNCS 是这样的),我觉得必要时逗点后换行比较合理。这个问题和逗点后没有空格是联动的,大概是 LaTeX 认为没有后面空格的逗点是小数(逗)点,所以禁止换行——然而这在一些情况下的排版结果简直惨不忍睹。- LaTeX 里的解决方法是在 , 后面加上 \hspace{0pt}。
- 在我的 blog 里,为了解决上面这个问题,同时为了更好的 accessibility,我把 [,] 包裹在 span 里,并且设置
aria-hidden="true"
,且在逗点的 span 里放置一个内部是空格的 span,它的字体大小由需要的空格的大小控制。这篇文章选择的空格样式是“无空格”,实际的实现里是有一个字号为 0 的空格。举个例子:“Gro11DLO⁺18 。”按照简体中文排版规则外加“允许引用内逗点后断行”,前面这句话可以断行的地方是:“举”“个”“例”周围、冒号后、逗点后、右引号后。 - 有趣的实际评测:Edge 44.17763.1.0、Safari for iOS 12.4、Safari for macOS 10.14.6、Chrome 73.0.3683.103 for Windows (x64) 似乎都认为冒号后、右引号后不可以断行;Chrome 76.0.3809.100 for Windows (x64) 允许冒号后、右引号后断行。
- 一个美中不足的地方是每个
`cite:key`
产生的 a 标签的 ID 是依据“这是第几次 cite 这个 key”产生的,这导致文章修改后链接的 ID 可能变化,对书签不友好。这是技术难度、书写难度和效果之间的权衡——要么我需要人工智能来指定一个比较稳定的 ID,要么我需要每次手工指定 ID(这太麻烦了),要么我选择放弃书签稳定性。我选择了放弃书签稳定性。 - 除了 Li19 ,这篇文章用到的例子文献都是我读过的😄。
请启用 JavaScript 来查看由 Disqus 驱动的评论。