我试图围绕truetype规范。在this page上,在'cmap'格式4部分中,参数idDelta被列为无符号16位整数(UInt16)。然而,更进一步,给出了几个例子,这里给idDelta赋值-9,-18,-27和1.这怎么可能?
idDelta
在该页面上的定义和使用并不一致。在struct subheader
中,它被定义为int16
,而稍早一点的同一个子标题被列为UInt16*4
。
这可能是规范中的一个错误。
如果你看看实际的实现,like this one from perl Tk,你会发现idDelta通常是签名的:
typedef struct SUBHEADER {
USHORT firstCode; /* First valid low byte for subHeader. */
USHORT entryCount; /* Number valid low bytes for subHeader. */
SHORT idDelta; /* Constant adder to get base glyph index. */
USHORT idRangeOffset; /* Byte offset from here to appropriate
* glyphIndexArray. */
} SUBHEADER;
或者看看libpdfxx的实现:
struct SubHeader
{
USHORT firstCode;
USHORT entryCount;
SHORT idDelta;
USHORT idRangeOffset;
};
这不是规范中的错误。他们在idDelta
行中显示负数的原因是All idDelta[i] arithmetic is modulo 65536.
(引自上面的部分)。这是如何工作的。
获取字形索引的公式是
glyphIndex = idDelta[i] + c
其中c
是字符代码。由于此表达式必须以模数65536为单位,因此如果使用大于2个字节的整数,则等效于以下表达式:
glyphIndex = (idDelta[i] + c) % 65536
idDelta
是一个u16,所以我们说它的最大值为65535(0xFFFF
),然后glyphIndex
将等于c - 1
,因为:
0xFFFF + 2 = 0x10001
0x10001 % 0x10000 = 1
当发生溢出时,您可以将此视为一个16整数,大约为0。
现在请记住,模数是重复除法,保留余数。那么在这种情况下,因为idDelta
只有16位,所以模数需要做的最大除法量是1,因为你可以通过添加两个16位整数得到的最大值是0x1FFFE
,它小于0x100000
。这意味着快捷方式是减去65536(0x10000
)而不是执行模数。
glyphIndex = (idDelta[i] - 0x10000) + c
这就是示例显示的表格中的值。这是我解码的.ttf文件的一个实际例子:
我想要字符代码97的索引(小写'a')。
idDelta[2] == 65507
glyphIndex = (65507 + 97) % 65536 === 68
与(65507 - 65536) + 97 === 68
相同