- 桌面应用
- 图形学
【免费下载链接】fontforge
Free (libre) font editor for Windows, Mac OS X and GNU+Linux
FontForge 是一款面向 Windows、macOS 与 GNU+Linux 的开源字体编辑器。在制作西文、希腊文、西里尔文、希伯来文、阿拉伯文等多文种字体时,组合字符(如 À、é、fi 连字、分数、罗马数字)的搭建是高频且繁琐的工程。本文以 FontForge 官方技术手册中的「Building Accented and other Composite Glyphs」(来源:doc/sphinx/techref/accented.rst)为核心骨架,结合仓库内实现代码 fontforge/fvcomposite.c 与偏好设置源码 fontforgeexe/prefs.c,系统讲解「Build Accented Glyph / Build Composite」命令的适用边界、判定逻辑、重音符选取与摆放算法、相关偏好项,以及如何在最坏情况下用复制引用手工搭建。读完本文,你将能在 FontForge 字体视图中一键批量生成组合字符,并理解底层为何这样摆放重音符、何时需要人工介入微调。
两个 Build 命令:Accented 与 Composite 的差异
在字体视图(Font View)中选中若干字形槽位,或在轮廓视图(Outline View)中对当前字形执行菜单命令:
- Element → Build → Build Accented Glyph:只负责搭建「带重音符的字符」(accented glyphs),即「基字 + 重音符」这类组合。
- Element → Build → Build Composite:是更通用的「组合字形」构建器,能处理连字(ligatures,如 fi、fl、ae、oe)、分数(fractions)、带括号的数字、罗马数字等。
两者的核心差别(见 doc/sphinx/ui/menus/elementmenu.rst 的Build Accented/Composite Glyph小节):
- 连字不会被 Build Accented 创建(例如 "fi" 连字必须用 Build Composite);
- Build Accented不会把 Greek Alpha 替换成 Latin A,而 Build Composite 两者都会做;
- Build Composite 会把连字、分数等更一般的组合一并构建。
作者特意提醒:像 "fi" 这种连字通常需要手工精修才能好看——"i" 的点要融进 "f" 的钩子中。如果你在 Build Composite 之后继续改动这两个字形,可能会不小心毁掉已完成的连字成果。
该命令不在位图视图(bitmap view)中提供。如果当前字形是带重音符的字形,且所需基字与重音符都已存在,命令会先删除前景层(foreground)里的所有内容,再往前景层放入两个引用:一个指向基字字形,一个指向重音符字形。例如构建 "À" 时,会加入对 "A" 的引用,并把对 "`" 的引用居中放到 "A" 上方。若 Edit → Copy From 设置为 All Fonts,则位图也会执行类似流程——即使在轮廓字形视图中,也会创建位图引用。
什么可以被构建:可从源码验证的判定清单
原文档列出了 Build 命令支持构建的众多字形类别,这些都能在 fontforge/fvcomposite.c 中找到对应判定逻辑:
- Unicode 规范分解(normative decompositions):如 "Aacute" 被定义为 "A" 顶部居中加 acute 重音符。Latin 重音字符、Latin 连字(ae、oe、fi、fl 等)、Greek/Cyrillic 重音字符、Hebrew/Arabic 连字、分数、括号内数字、罗马数字、预定义 Hangul 字符(U+AC00–U+DFAF)都在此列。核心入口是
SFIsCompositBuildable()(fvcomposite.c),它通过SFGetAlternate()取 Unicode 分解序列,逐字符检查是否已有对应字形、是否有递归引用。 - 韩文 Jamo 连字:许多 Jamo 其实是其他 Jamo 的连字(如 ssangkiyoeok U+1101 由两个 kiyoeok U+1100 组成),这并非 Unicode 规范分解,但 FontForge 同样知晓并能构建。
hascomposing()(fvcomposite.c)中对 U+1100–U+11C7 区间有专门处理注释:"Only build Jongsung out of chosung when doing a build composit"。 - i / j 与 dotlessi / dotlessj:"i"、"j" 可通过对 dotlessi/dotlessj 加 dot 重音符构建;反向操作即移除 "i"/"j" 的 dot 得到无点字形。对应
SCMakeDotless()判定(fvcomposite.c 与SFIsSomethingBuildable()中的调用)。 - 带扩展名的字形:如 "agrave.sc",会按 "agrave" 的规则用带 ".sc" 扩展的组件构建("a.sc" + "grave.sc");若重音符扩展变体不存在,则回退到裸重音符 "grave"。源码中 fvcomposite.c 会截取点号前的名称并按
UniFromName解析。 - 跨文种字形替换:FontForge 知道 Greek "Alpha"、Cyrillic A 长得像 Latin "A",可应要求互相替换(Latin、Greek、Cyrillic、Cherokee 等类似字形都支持)。
SFIsCompositBuildable()中特意注释避免递归引用:"Cyrillic gamma could refer to Greek gamma"(fvcomposite.c)。 - 零宽字符与固定尺寸空格/破折线:零宽空格系列(zero-width spaces)都可构建;更实用的是 U+2000–U+200F 固定尺寸空格和 U+2010–U+2015 固定尺寸规则线。
SFIsSomethingBuildable()(fvcomposite.c)中iszerowidth()与 U+2000–U+2015 区间均有判定。 - 用户在 Char Info 中自定义的连字:通过 Element → Char Info 对话框定义自己的连字后,FontForge 会为你构建。
- Adobe CID 字体集中的预旋转字形:CJK 竖排时西文通常应顺时针旋转 90°。对
vertcid_<cid>(CID 十进制编号)或非 CID 字体中的vertuni<uni>(四位十六进制)命名槽位执行 Build Accented,FontForge 会找到对应 Latin 字母并正确旋转。判定逻辑在SFIsRotatable()(fvcomposite.c),支持vertcid_*、vertuni*、.vert、.vrt2等多种命名约定。
关键判定总入口:SFIsSomethingBuildable()(fvcomposite.c)综合了以上全部条件:用户分解(user_decomp)优先;onlyaccents模式下拒绝把 Greek 重音用 Latin 重音构建(U+1FBD、U+1FBE、U+1FBF、U+1FEF、U+1FFD、U+1FFE 等);零宽与固定尺寸空间只在 Build Composite 时构建;组合可构建时还要满足hascomposing()或旋转条件。注意:命令执行前会校验"所有需要的基字与重音符是否已创建",未创建则不会构建,这是最常用的失败原因。
重音符选取策略:Unicode 规范之外的工程妥协
原文档明确说明,选择重音符是件微妙的事:
- Unicode 规定重音字符逻辑上由 U+0300–U+0340 区间的组合字符构建,但这对 PostScript 不友好。Type1 字体最好让重音字形落在 Adobe Standard Encoding 中,即使用 U+02D0 附近的重音或 ASCII 字形。
- 因此 FontForge 的查找顺序是:先找 U+02D0 附近的重音,其次找 ASCII 重音,最后才回退到 U+0300–U+0340 区间。
- Unicode 还统一了希腊与拉丁重音,导致照搬规范可能得到错误的字形。所以对希腊字母,FontForge 会寻找 U+1FBD 附近的重音,并要求同时存在 U+1FDD 这类双重重音。
- 部分 Adobe 字体的重音是为大写字母设计的(普通重音为小写设计),也有为西里尔字母设计的重音。此类字体中构建 "Agrave" 时,FontForge 会用 "A" + "Grave"(或 "grave.cap",而非标准 "grave")。
对应实现:GetGoodAccentGlyph()(fvcomposite.c 附近)负责重音字形选择;hascomposing()中显式列出 U+0384(tonos)、U+0385(dieresis/tonos)及 U+1FBF–U+1FFF 区间的大量特殊希腊重音码点(fvcomposite.c),印证了"希腊重音独立处理"的设计。
重音摆放:光学居中的艺术
文档特别强调:重音并非简单放在字形几何中心。例如:
- 把 acute 放到 "a" 顶部时,应放在 "a" 的最高处附近(接近中心但并非正中);
- 把 acute 放到 "k" 顶部(少见但存在)时,应以其竖干(stem)为居中基准;
- 希腊重音更复杂,FontForge 知道大量摆放细节,但不可能穷尽所有字形(同一字母在不同字体中形态各异,作者甚至调侃"取决于湿度与月相")。
文档给出了两组示意图(均存在于 doc/sphinx/images 目录):
- acenter.png 展示字形的中心点,ahighest.png 展示字形的最高点——两者并不重合;
- aacutemid.png 展示重音的中点,aacutebase.png 展示重音底部居中的情形——grave/acute 通常(并非总是)只按重音底部部分居中。
重音的居中方式取决于重音本身:通常 FontForge 试图让重音的中心对准基字的"中心",但 grave/acute 一般只对准重音底部部分。对于 cedilla 这类重音,通常应居中放在基字下方;其他重音可能需要偏左、偏右甚至叠压(overstruck)。FontForge 对绝大多数重音的正确摆放位置"或至少粗略近似"是知晓的。
从源码看,光学摆放是通过扫描轮廓极值实现的:SCFindTopXRange()/SCFindBottomXRange()(fvcomposite.c)遍历字形最高/最低点附近的样条(spline)与引用,借助_SplineSetFindXRangeAtYExtremum()计算极值点横向范围作为居中基准;SCFindTopBounds()(fvcomposite.c)则针对 cedilla、ogonek 这类"不按重音本身居中、而是按它与基字连接的小部分居中"的特殊情形。算法还考虑了 italic angle(斜体角),用tia = tan(ia)做坐标调整(fvcomposite.c)。
两个偏好项:微调重音位置
FontForge 在File → Preferences(见 doc/sphinx/ui/dialogs/prefs.rst)中提供两个偏好项,控制重音摆放:
- AccentOffsetPercent:控制字母顶部与重音底部之间的间距(对下沉重音则是字母底部与重音顶部的间距),以 em 尺寸的百分比表示,默认 6%。实现上对应 prefs.c 的
accent_offset整数变量。注意:有些情况下重音上方会多留一点空间——所有小写字母上的重音应处于同一高度,而 "o" 通常比 "u" 略高,所以小写重音统一按 "o" 的高度摆放(即使它位于 "u" 上方);大写字母同理。 - AcuteCenterBottom:控制 acute/grave(及双 acute/grave)重音是按整个字形居中,还是只按底部居中。实现上对应全局变量
GraveAcuteCenterBottom,默认值为 1(即按底部居中),定义于 fvcomposite.c,并在重音居中逻辑中用于 U+0300/0301(grave/acute)、U+030B(双 acute)与 U+030F(双 grave)的判断(fvcomposite.c)。偏好表中还有等价的AccentCenterLowest布尔项(prefs.c),与AcuteCenterBottom绑定同一个变量。
重要提醒(原文档的 note):FontForge 一定会做出某些"错误"的选择——文档作者坦言有太多他无知、审美不合或程序存在 bug 的地方。强烈建议构建完所有重音字符后逐一检查,并准备好手动调整。
斜体与极端情形:接受不完美
算法在一定程度上考虑了 italicangle,但处理斜体(italic/oblique)字体时 FontForge更可能做出你不满意的结果。这是内置局限,不是使用错误。
此外还有两类容易踩坑的边角情形:
- cedilla 的跨语言差异:如 g-cedilla 在不同语言中处理方式不同,请格外小心;Å 常把 ring 融入 A 顶部,但此处构建时 ring 会悬浮在 A 上方——构建后务必人工检查。
- 多重音字符:部分 Unicode 字符含不止一个重音,额外重音会按类似方式叠加处理;该命令也可用于生成更一般的复合字形。
兜底方案:手工构建复合字形
如果自动构建结果始终不理想,文档给出的最终方案是回到基础编辑能力:
- 用Edit → Copy Reference(Edit 菜单 Copy Reference)复制一份指向基字字形的引用;
- 用Edit → Paste粘贴到目标槽位;
- 重复同样的操作把重音符字形以引用形式叠加进去;
- 在轮廓视图中用移动工具精确调整引用位置,完成光学对齐。
这与自动命令的产物在结构上完全一致——前景层中的多个RefChar引用——但位置完全由你掌控,不再依赖算法的近似摆放。适合对摆放精度有苛刻要求的连字或特殊组合。
实战检查清单
- 构建前确认所需基字与重音字形都已存在(否则命令静默跳过);
- 连字、分数、罗马数字等必须用 Build Composite,Build Accented 只管"基字+重音";
- 希腊字母的重音来自 U+1FBD 附近专用码点,别指望通用 Latin 重音能直接套用;
- 构建完成后逐一目检每个重音字符,尤其留意 cedilla、Å、斜体字与多重音组合;
- 需要精细控制间距时,调整 Preferences 中的
AccentOffsetPercent(默认 6%)与AcuteCenterBottom; - 仍不满意就切到 Copy Reference + Paste 手工搭建,结构等价但位置可控。
延伸阅读
- 官方技术参考原文:doc/sphinx/techref/accented.rst
- 菜单命令说明(Build Accented/Composite Glyph、Build Duplicate):doc/sphinx/ui/menus/elementmenu.rst
- 核心实现:字形可构建判定、重音选择与光学摆放算法均在 fontforge/fvcomposite.c
- 偏好项注册与默认值:fontforgeexe/prefs.c、无界面版本 fontforge/noprefs.c
- 偏好对话框文档:doc/sphinx/ui/dialogs/prefs.rst
- 桌面应用
- 图形学
【免费下载链接】fontforge
Free (libre) font editor for Windows, Mac OS X and GNU+Linux
相关推荐
hello-uniapp音频录制与播放:实现语音功能的完整指南
hello uniapp音频录制与播放:实现语音功能的完整指南 hello uniapp是一个基于uni app框架的演示示例,提供了丰富的功能展示,其中音频录
示例工程前端FontForge 教程:构建带音字形、连字与 OpenType Lookups/Features 完整实战指南
FontForge 教程:构建带音字形、连字与 OpenType Lookups/Features 完整实战指南 本指南基于 FontForge 官方教程( d
桌面应用图形学TinyPinyin核心API详解:从单个字符到复杂字符串的拼音转换
TinyPinyin核心API详解:从单个字符到复杂字符串的拼音转换 TinyPinyin是适用于Java和Android的快速、低内存占用的汉字转拼音库,提供
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考