跳到正文

从活字到字体源码(7):连字让源码换一种读法

从活字到字体源码

  1. 字形怎样学会复用
  2. 中文字体走进现代出版
  3. 西文字体穿过机器时代
  4. 那些熟悉的字体从哪里来
  5. 字体从像素格走向轮廓
  6. 可变字体把变化装进一个文件
  7. 连字让源码换一种读法(当前)
  8. 字体源码藏在哪些文件里
  9. Ligconsolata Next 的连字改造
  10. AI 改字体时到底在做什么
  11. 不会字体设计,也能看懂字体改动

在编辑器里打开连字,!== 可能显示成一个整体符号;复制到没有连字的输入框,又恢复为三个字符。文件内容没有来回改写,变化发生在排版时选择字形的过程里。

理解这条路径,需要把字符和 glyph 分开。字符是文本保存的内容,glyph 是字体提供给排版程序的字形。二者之间可以有上下文选择与多字符合并。

同一段 ASCII 文本经过字符映射和 GSUB 后显示为连字

字符映射之后还会发生替换

简化到本例,!== 先通过字体的 cmap 映射为 exclam、equal、equal。排版引擎应用相应 feature 时,再用 GSUB 规则选择连字 glyph。Microsoft 的 GSUB 规范定义了这些字形替换结构。

这条图示省略了脚本分析、语言选择、定位等环节,只说明连字的层次。编译器、搜索与版本 diff 继续处理原始 !、=、=;字体并没有把源文件中的字符改成另一个 Unicode 符号。

传统拉丁排版中的 fi、fl 也有连字,复杂文字还有依赖位置和上下文的字形变化。编程字体使用了同一类排版能力,为多字符操作符安排整体形态。

Feature 分工影响编辑器默认体验

OpenType feature 是供应用选择的一组排版行为。下面按本项目的实现分工列出用途,名称的通用定义可在 Microsoft 的注册特性 a—e与 k—o中核对。

Feature 通用作用 Ligconsolata Next 的用法
liga 标准连字 !=、==、=>、<= 等固定操作符
dlig 可选连字 保留历史兼容,镜像当前固定规则
calt 上下文替换 长箭头、标点对齐、注释前缀及数字上下文
ssXX / cvXX 风格集与字符变体 作为可选风格入口,不承载新增的核心编程行为

启用方式由应用决定。以 VS Code 为例,1.40 的官方说明明确写明 editor.fontLigatures: true 启用 liga 和 calt。其他编辑器、终端和网页 CSS 要分别核对,不能从一个总开关推断所有 OpenType feature 都会启用。

连成一个 glyph,总宽度仍要保留

advance width 是排完一个字形后,排版位置向前推进的距离。它与墨迹边界不同,字形可以在自己的占用空间里保留左右空白。

默认位置里,Ligconsolata Next 的一个 Latin cell 为 500 font units,=> 两字符共 1000,!== 三字符共 1500。连字输出也要保留这些总值,才能沿用等宽网格。

真实字体轮廓中原始字符和连字占用相同总宽度

图中的竖线标出原有字符格。连字可以跨格连接,但右端推进位置保持不变。总宽度正确是排版前提;光标怎样进入连字、选区怎样绘制,还取决于应用的 cluster 和 caret 处理,不能仅凭 hmtx 宽度就宣布所有编辑器交互正常。

上下文让同一组符号有不同处理

固定的 => 适合直接替换。----> 的长度却可以不断增加,如果每个长度都造一个 glyph,清单会一直增长。项目用 calt 选择起点、中段与端点,让各个片段共同组成长箭头。

// comment 和 https://example.com 则呈现另一种约束。两者都有相邻 slash,显示目的不同;项目选择只有后接普通空格时,才启用注释前缀连字。它是字符上下文判断,并不了解语言语法,更无法识别一个字符串到底是不是注释。

相同符号因空格与相邻字符不同而触发或避开连字

图中 ␣ 仅用于标出普通空格,实际源码保存的仍然是空格。块注释边界和逻辑 slash 组合也使用了局部条件,尽量避开 glob、正则与路径。这样的 guard 可以缩小误触范围,不能把字体升级为语法分析器。

字形相近会直接影响代码阅读

== 和 ===、!= 和 !== 有不同语义,需要保留清楚的两横线与三横线。项目早期曾从三横线 glyph 压缩出两字符版本,GSUB 与宽度检查都通过,图片里却仍像三条线。这个问题要回到轮廓派生方式修正。

因此 specimen 必须从 ASCII 输入和真实字体生成。把 != 换成 Unicode ≠,只能展示另一个字符的字形,无法证明该字体如何处理原始操作符。

总结

连字在 glyph 层改变显示,字符层保留原文。固定替换、上下文判断与可选风格分别影响默认体验,而等宽关系要求输出保留原始总推进宽度。判断连字实现时,要同时检查真实字形输出、宽度与相近操作符的可辨认程度,任何一项都不能由概念示意图代替。