【问题标题】:How to correctly understand TrueType cmap's subtable Format 4?如何正确理解 TrueType cmap 的子表 Format 4?
【发布时间】:2019-12-19 01:25:51
【问题描述】:

以下是TrueType字体格式documentation提供的关于“格式4:段映射到增量值”子表格式字段的信息,可用于cmap字体表(第用于将字符代码映射到字形索引):

Type  Name    Description
1. uint16     format  Format number is set to 4.
2. uint16     length  This is the length in bytes of the subtable.
3. uint16     language    For requirements on use of the language field, see “Use of the language field in 'cmap' subtables” in this document.
4. uint16     segCountX2  2 × segCount.
5. uint16     searchRange     2 × (2**floor(log2(segCount)))
6. uint16     entrySelector   log2(searchRange/2)
7. uint16     rangeShift  2 × segCount - searchRange
8. uint16     endCode[segCount]   End characterCode for each segment, last=0xFFFF.
9. uint16     reservedPad     Set to 0.
10. uint16    startCode[segCount]     Start character code for each segment.
11. int16     idDelta[segCount]   Delta for all character codes in segment.
12. uint16    idRangeOffset[segCount]     Offsets into glyphIdArray or 0
13. uint16    glyphIdArray[ ]     Glyph index array (arbitrary length)

(注意:我对字段进行了编号以允许引用它们)

大部分字段,如1. format2. length,3。语言,9。 reservedPad`是微不足道的基本信息并且可以理解。

其他字段4. segCountX25. searchRange6 .entrySelector7. rangeShift 我认为具有预先计算值的一些奇怪方式,但基本上只是存储段数segCount 的一种冗余方式(隐含)。还有那些我没有大头疼的了解的领域。

最后还有代表数组的字段。每个段都有一个字段8. endCode10. stadCode11. idDelta12. idRangeOffset可能/可能不会甚至是一个字段13. glyphIdArray。这些是我仍然难以正确解释的领域,也是这个问题的主题。

为了得到最有帮助的答案,请允许我快速勾勒出我对这些领域的看法:

  • 基本上是逐段工作,每个段将字符代码从startCode 映射到endCode 到字体字形的索引(反映它们在glyf 表中出现的顺序)。
  • 将字符代码作为输入
  • 将字形索引作为输出
  • 段是通过迭代它们来确定的,检查输入值是否在startCodeendCode的范围内。
  • 有了这样找到的段,字段各自的字段idRangeOffsetidDelta也被确定了。
  • idRangeOffset 传达了特殊的含义
  • case A) idRangeOffset 被设置为特殊值 0 意味着输出可以是 根据输入值(字符代码)和idDelta 计算得出。 (我认为是glyphId = inputCharCode + idDeltaglyphId = inputCharCode - idDelta
  • 案例 B) idRangeOffset 不是 0 会发生不同的事情,这是我在这里寻求答案的一部分。

关于案例 B),文档指出:

如果段的 idRangeOffset 值不为 0,则映射 字符代码依赖于 glyphIdArray。字符代码偏移量 startCode 被添加到 idRangeOffset 值。这笔款项用作 从 idRangeOffset 本身的当前位置到索引的偏移量 出正确的 glyphIdArray 值。这种晦涩的索引技巧有效 因为 glyphIdArray 紧跟在字体中的 idRangeOffset 文件。产生字形索引的 C 表达式是:

glyphId = *(idRangeOffset[i]/2
            + (c - startCode[i])
            + &idRangeOffset[i])

我认为这提供了一种将连续输入范围(因此是“段”)映射到存储在字段 glyphIdArray 中的值列表的方法,可能作为一种提供无法通过 idDelta 计算的输出值的方法,例如无序/不连续。这至少是我对文档中描述为“晦涩难懂”的内容的阅读。

【问题讨论】:

    标签: truetype


    【解决方案1】:

    因为 glyphIdArray[] 在 TrueType 文件中跟在 idRangeOffset[] 后面,所以有问题的代码段

    glyphId = *(&idRangeOffset[i]
             + idRangeOffset[i]/2
             + c - startCode[i])
    

    指向glyphIdArray[]中所需位置的内存地址。详细说明原因:

    • &idRangeOffset[i]指向idRangeOffset[i]的内存地址

    • 向前移动idRangeOffset[i]字节(或idRangeOffset[i]/2 uint16's)将您带到glyphIdArray[]的相关部分

    • c - startCode[i]glyphIdArray[] 中包含所需 ID 值的位置

    从这里开始,如果这个ID不为零,你将添加idDelta[i]以获得c对应的字形编号。

    重要的是要指出*(&idRangeOffset[i] + idRangeOffset[i]/2 + (c - startCode[i])) 确实是伪代码:您不希望将值存储在程序的内存中,而是文件中的内存地址

    在没有指针的更现代的语言中,上面的代码段转换为:

    glyphIndexArray[i - segCount + idRangeOffset[i]/2 + (c - startCode[i])]
    

    原始代码段中的&idRangeOffset[i] 已替换为i - segCount(其中segCount = segCountX2/2)。这是因为范围偏移量(idRangeOffset[i]/2)是相对于内存地址&idRangeOffset[i]而言的。

    【讨论】:

    • 那么,如何在没有指针的现代语言中计算 GlyphID?因为如果你有 python 列表或 JS 数组,而不是指向某个内存位置的指针,C 代码不是超级有用
    • (例如,已知文件位置为idRangeOffset“数组”的开头和段i,如何找到字形和glyphIdArray 索引?)
    • 我已经编辑了我的答案,以尽我所能解决您的问题。答案就在那里,但我的解释可以使用详细说明。 PS:您的贝塞尔曲线入门对我非常有用,所以您回复我的答案既是一种荣幸,也是一个非常有趣的巧合!
    • 太好了,谢谢!另外,有趣的事实:入门书本身诞生于 2011 年与字体相关的事物。
    猜你喜欢
    • 1970-01-01
    • 2021-08-28
    • 2021-06-12
    • 1970-01-01
    • 2017-08-20
    • 1970-01-01
    • 1970-01-01
    • 2019-12-19
    • 2011-09-27
    相关资源
    最近更新 更多