跳到正文

从活字到字体源码(9):Ligconsolata Next 的连字改造

从活字到字体源码

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

Ligconsolata Next 的目标来自一个很具体的偏好:保留 Inconsolata 的字母、括号与阅读节奏,同时补齐常用编程连字。Fira Code 是行为与覆盖面的参考,新增字形仍从 Inconsolata 自己的轮廓派生。

这里集中讨论改造中的几个决定。完整支持清单由生成脚本与项目功能图库维护,文章的样例只覆盖下面解释的机制。

先让已有连字进入普通编辑器路径

Inconsolata 已有的 dlig baseline 包括 !==、===、->、=>、>=、<-、<=。Next 保留这层兼容,同时把固定替换同步到 liga,让普通编辑器的连字开关能使用它们。两字符的 !=、== 属于 Next 补充并修正的形态,不能算作上游 baseline 已有能力。

编辑器默认体验围绕 liga 与 calt 安排。dlig 继续兼容历史用法;字母风格等选择留给 ss / cv,避免默认体验依赖用户逐个配置高级特性。

两横线与三横线分别生成

早期生成方式从三横线字形横向缩放得到 == 与 !=。它保留了宽度,却没有改变横线数量,视觉上仍容易被读成 identity 运算符。

后来 == 改为从原始 equal 的两条横线派生,!= 使用相同的两横线结构并结合斜杠。=== 与 !== 则继续保留三条清楚的横线,整体视觉中心对齐原始等号。

当前字体真实轮廓中的两横线、三横线与占用格数

图中关闭 calt,单独检查 liga 中的固定字形。早期方案的问题由项目的迁移复盘记录;图里展示的是当前轮廓。

独立输入也需要检查。早期规则在 calt=1 时会把孤立 === 当成长等号序列的开头,输出两条连续长线;同一份字体中的 a === b 却能命中三横线固定字形。修复后的规则要求三个等号后还有可延展序列的字符,才启动长线替换。这样既保留孤立 === 的三横线,也能继续处理 ===>、====> 等长箭头。字体 review 篇保留了修复前后的真实轮廓和采样记录。

更长的 ====、===== 用两条连续长线处理注释分割线,不能把这一规则套回 ===。类似地,-- 保留两个减号的间隔,---- 才承担连续分割线的样式。

比较符与箭头分开设计

<=、>= 表示比较,<==、==> 表示方向。项目默认比较符从 <、> 的下斜边派生一条平行、等长的下斜线,端点使用竖切。它参考 Fira Code 默认比较符的处理方向,轮廓来源仍是本字体。

这个调整不能只看包围盒居中。第二条斜线的视觉跨度、端点位置与切口,都需要和原始斜边对照。水平横线式比较符可以作为未来风格选项,但当前默认形态有明确选择。

长箭头用片段延展

固定枚举 ->、-->、---> 无法覆盖所有长度。项目用 start、middle、end 片段处理长箭头,各片段共同保留原始总推进宽度。

长箭头在真实 shaping 结果中的起点、中段和终点

片段需要和固定连字协调。<==== 的剩余等号如果先被 === 接管,长箭头就会不完整。因此 calt 使用 start lookup 与多轮 extend lookup,并对 ==、===、!==、i-- 及共享前缀的固定组合设置避让。

这类冲突仅看 feature 文本很难判断,要检查最终 glyph 序列。新增一个固定操作符,也可能要求修改已有长箭头的 guard。

注释规则接受保守的上下文边界

无条件合并 // 会改到 URL,无条件合并 /*、*/ 会改到 glob。项目选择普通空格作为局部触发条件:

输入 当前处理
// comment、/// reference 后接空格,启用注释前缀
https://example.com、//TODO 保留 slash
/* block */ 起始边界后、结束边界前有空格时启用
**/*.{ts,tsx}、src/*.ts slash 与 asterisk 边界保持原样,** 仍可连写
a /\ b、x \/ y 两侧普通空格下启用逻辑形态
/\d/、\/tmp 保留对应组合,反斜杠仍可能使用默认细形态

//TODO 即使在语法上是注释,也不会得到该前缀连字。字体只能看到 shaping run 内的字形上下文,这个代价换来了更容易解释的 URL 与路径保护。

Markdown hash badge 同样依赖局部上下文。## title 到 ###### title 仅在 shaping run 开头且后接普通空格时显示可数徽标;它不能跨编辑器的语法分段判断整行含义。七个及以上井号使用无编号的延展序列。

用真实字体维护样张与 QA

overview-samples.txt、catalog 样例和功能图库从 ASCII 文本出发,经 HarfBuzz 排字后提取字体轮廓。静态图能够证明采样时的字形,源码变化后必须重新生成。documentation/demo/index.html 另外加载实际字体,允许输入自己的片段并切换 feature。

当前项目已有 100、400、700、900 字重与 11、13、16、20px 的深浅背景矩阵,不能再写成「尚未做多字重 QA」。它仍然只是已选样本和位置的检查,当前 upright 源码没有因此获得 Italic 支持。

总结

这次改造把固定操作符放进 liga,把长度与局部上下文交给 calt,并始终维护原始字符的总推进宽度。两横线、比较符与注释 guard 表明,覆盖更多组合之前,要先决定哪些差异必须看清、哪些上下文宁可保守。真实字体样张与回归样例让这些选择可以继续核查,而不只停留在设计描述里。