为何单个TrueType字体文件中的字体映射不完全一致?
TrueType字体多编码映射的差异观察与问题解析
现象概述
解析TrueType字体文件时发现,部分文件包含多个字体映射表,但同一文件内的各映射表并非完全一致,以下是两个不同厂商字体文件的具体情况:
文件1详情
包含3个字体映射表:
PlatformID=0, EncodingID=3, ByteOffset=28 PlatformID=1, EncodingID=0, ByteOffset=60 PlatformID=3, EncodingID=1, ByteOffset=28
- 偏移量28处的映射表为Segment mapping to delta values:映射从字符33(对应字形3)开始,将字符
0xFFFF映射至字形0。 - 偏移量60处的映射表为Byte Encoding Table:包含与上述表相同的映射(不含
0xFFFF的映射),额外添加了以下控制字符到空白字形的映射:- 字符0 → 字形[1]
- 字符8 → 字形[1]
- 字符9 → 字形[2]
- 字符13 → 字形[2]
- 字符29 → 字形[1]
其中字形[1]、[2]均为空,可避免Tab、CR等字符显示为“缺失字符”字形,但存在两个疑问:为何未包含LF(换行符,字符10)的映射?为何要将字符29(分组分隔符)映射至空白字形?
文件2详情(来自不同厂商)
包含2个字体映射表:
PlatformID=1, EncodingID=0, ByteOffset=20 PlatformID=3, EncodingID=1, ByteOffset=282
- 偏移量282处的映射表为Segment mapping to delta values:映射从字符32(对应字形3)开始,将字符
0xFFFF映射至字形0。 - 偏移量20处的映射表为Byte Encoding Table:包含与上述表大量相同的信息,同样添加了以下控制字符到空白字形的映射:
- 字符0 → 字形[1]
- 字符8 → 字形[1]
- 字符9 → 字形[3]
- 字符13 → 字形[2]
- 字符29 → 字形[1]
其中字形[1]、[2]、[3]均为空。
技术问题解答
1. 为何两个文件中的Byte Encoding Table均包含这些额外映射?
Byte Encoding Table(BET)针对单字节编码场景设计,添加这些映射主要出于以下原因:
- 避免显示“缺失字符”占位符:若控制字符未被映射,渲染引擎通常会显示默认的缺失字形(如方框),破坏排版美观,映射到空白字形可让这类字符“隐形”,符合控制字符本身无可见内容的特性。
- 保证排版一致性:部分控制字符(如Tab、CR)在排版中有特定行为,若字体未明确映射,不同渲染引擎可能有不同处理逻辑,统一映射到空白字形可确保跨环境表现一致。
- 兼容旧系统规则:早期排版系统对控制字符处理要求严格,厂商添加这些映射是为了适配旧系统的渲染逻辑,避免异常显示。
2. 为何仅这些特定控制字符(0、8、9、13、29)被映射至空字形?
这些字符的选择和它们的实际使用场景直接相关:
- 字符0(空字符NULL):几乎不会出现在正常文本中,若显示会干扰排版,映射到空白字形是合理处理。
- 字符8(退格BS)、字符9(制表符Tab):属于常用文本控制字符,退格在现代排版中很少直接显示,映射到空白可避免占位符;制表符有排版间距功能,空白映射能让它仅发挥间距作用,不显示可见标记。
- 字符13(回车CR):在Windows等系统中是换行的一部分,单独显示无意义,映射到空白可避免多余占位符。
- 字符29(分组分隔符GS):属于ASCII控制字符,用于数据分组,极少在普通文本中出现,若误引入文本,空白映射可避免破坏排版。
至于未包含LF(字符10,换行符),是因为LF在排版中明确用于换行,其行为由渲染引擎/排版系统直接处理,不需要字体额外定义映射——字体仅需处理那些会被当作普通字符渲染的控制字符,LF不属于此类,因此无需添加映射。
内容的提问来源于stack exchange,提问作者Betty Crokker
相关产品推荐
相关产品推荐

