【问题标题】:Reading TrueType 'cmap' Format 4 Subtable in Swift 3在 Swift 3 中读取 TrueType 'cmap' 格式 4 子表
【发布时间】:2016-11-30 08:13:21
【问题描述】:

如何在 Swift 中编写以下 C 代码?

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

我正在尝试使用一个简单的小型二进制数据读取器来读取 Format 4 TrueType 字符映射表。在涉及指针操作之前,一切都很好,因为在 C 中工作时我几乎无法做出指针的正面或反面,更不用说那些伪装成带有Unsafe 前缀的指针了。

我尝试了多种方法,但似乎都没有什么效果。我想我只是不确定如何在 Swift 中使用指针“地址”。

例如,这里有一个关于我在哪里的更完整的想法:

// The following variables are all [UInt16]:
// - startCodes
// - endCodes
// - idDeltas
// - idRangeOffsets

var gids = [Int]()

// Iterate segments, skipping the last character code (0xFFFF)
for i in 0 ..< segCount - 1 {

    let start = startCodes[i]
    let end = endCodes[i]
    let delta = idDeltas[i]
    let rangeOffset = idRangeOffsets[i]
    let charRange = start ..< (end + 1)

    if rangeOffset == 0 {
        gids.append(contentsOf: charRange.map { charCode in
            return (charCode + delta) & 0xFFFF
        })
    }
    else {
        for charCode in charRange {
            // ???
        }
    }
}

在上面的代码中,您会注意到???。这是我使用上面提到的奇怪的 C-pointer-address-pointer-huh 技巧检索字形索引的地方。问题是,我就是想不通。替换我真正理解的变量,这就是我得到的:

for charCode in charRange {
        Not too sure about this   Actual value of idRangeOffset[i]
                 |                          |
                 v                          v
    glyphIndex = *(&idRangeOffset[i] + rangeOffset / 2 + (charCode - start))
                   ^
                   |
                Or this
}

是否有任何 Swift 3 指针大师可以带领我走上启蒙之路?任何帮助将不胜感激!

【问题讨论】:

  • 对于您创建glyphIndex 的代码,外括号内的部分会创建一个指针。这些括号前面的* 然后获取该指针指向的数据
  • 外括号内的第一位,&amp;idRangeOffset[i] 获取idRangeOffset[i] 中值的地址。在 C 中,这是一个指针,即一个保存另一个对象地址的变量。第二部分,+ rangeOffset / 2 + (charCode - start),然后向 address 添加更多值,以便它现在指向其他地方。
  • 这应该如何在swift中正确表示我不知道我害怕
  • @Toby 那么,例如,假设int idRangeOffset[] = {10, 20, 30, 40, 50}idRangeOffset[2] 中的“地址”实际上是指针的地址(而不是数组中的索引2)吗?如果是这样,“地址”究竟是什么?我们是在谈论二进制数据中的偏移量,还是什么?我为所有的问题道歉,指针只是让我头疼。
  • Ben, idRangeOffset[2] 不会产生 n 地址,但 &amp;idRangeOffset[2] 会。它将给出该数组中第二项的地址(内存中的位置)。

标签: c swift pointers character-encoding truetype


【解决方案1】:

如果我将你的伪 C 代码逐字翻译成 Swift,它会是这样的:

//"May not work" example, do no use this
glyphIndex = withUnsafePointer(to: &idRangeOffset[i]) {idRangeOffsetPointer in
//In some cases `idRangeOffsetPointer` can work as `&idRangeOffset[i]` in C...
    (idRangeOffsetPointer + Int(rangeOffset) / 2 + Int(charCode - start)).pointee
    //To make pointer operation in Swift, all integers need to be `Int`,
    //And to match the C-rule of integer operation, you need to cast each portion to `Int` before adding operation
    //The equivalent to C's dereferencing operator `*` in Swift is `pointee` property
}

但这可能无法按预期工作,因为 Swift 的 inout 参数的 copy-in/copy-out 语义。 Swift 可能会创建一个包含单个元素idRangeOffset[i] 的时域,并将它的地址传递给idRangeOffsetPointer,因此指针操作的结果可能指向时域附近的某个地方,这是完全没用的。

如果您想从指针操作中获得有意义的结果,您可能需要在保证数组的所有元素都放置在连续区域中的上下文中工作。

您还应该知道 C 语句:

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

基于这样一个事实,即整个idRangeOffsetglyphIdArray 被放置在一个连续的区域中,没有任何间隙或填充。 (我假设你很了解格式 4。)

因此,如果您的 idRangeOffset 仅包含 segCount 元素,则以下代码将不起作用。

//"Should work" in a certain condition
glyphIndex = idRangeOffset.withUnsafeBufferPointer{idRangeOffsetBufferPointer in
    let idRangeOffsetPointer = idRangeOffsetBufferPointer.baseAddress! + i
    //`idRangeOffsetPointer` is equivalent to `&idRangeOffset[i]` in C inside this closure
    return (idRangeOffsetPointer + Int(rangeOffset) / 2 + Int(charCode - start)).pointee
}

但考虑到 C 中的指针和数组语义,上面的代码等价于:

glyphIndex = idRangeOffset[i + Int(rangeOffset) / 2 + Int(charCode - start)]

//`*(&arr[i] + n)` is equivalent to `arr[i + n]` in C

重复一遍,数组idRangeOffset 需要包含idRangeOffset 和glyphIdArray 的全部内容

【讨论】:

  • 好吧,我觉得我们越来越近了。在我发布我的问题之前,我尝试了withUnsafeBufferPointer 技术,但无济于事。但是,您告诉我我需要在idRangeOffset 内容的末尾包含glyphIndexArray 的内容,所以它是一个长的连续数组?这是否允许将底层存储存储为堆上的内聚内存单元或其他东西?原谅我的无知;我的 Swift C 游戏没有我想要的那么清晰。
  • 它是一个长的连续数组? 是的。它可能包含数十万种元素。 这是否允许将底层存储作为一个内聚的内存单元存储在堆上或其他东西上? 严格意义上来说不是这样,但是当通过下标访问时你可以认为它是正确的> 或 withUnsafeBufferPointer 的闭包内。正如 OmniProg 的回答所暗示的,在二进制数据格式中,内容的含义可能取决于它们的绝对位置或偏移量。希望我的回答能帮助你达到目标。
【解决方案2】:

为了补充@OOPer 所说的内容,我建议将感兴趣的整个 cmap 或子表读入内存,并使用文档作为指南在 Swift 中使用它们;例如,参见https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cmap.html。您也可以使用 C 实现作为参考,但除非您是一位经验丰富的 C 程序员,否则这不是一条好路:C 中有太多的微妙之处可以并且会咬到您。在 C 中,在指针和偏移量方面使用 cmap 非常方便。因为 Swift 对指针不友好,所以最好只在表中使用偏移量。您可能会遇到将映射的不同部分解释为不同类型的值的问题,但至少您不必处理指针魔术。

【讨论】:

  • 感谢您的建议。实际上,我正在尝试将其全部加载到内存中,以便以“Swift”的方式处理内容,而对于大多数其他表(包括其他 Format 子表),这就是我一直在做的事情。这个愚蠢的 C 指针是现在唯一让我退缩的东西。除了 Format 4 解析之外,我真的对这些东西感觉很好。我拥有的所有表格都是不错的值类型,我在lazy-在我可以加载的地方加载数据,这真的很甜蜜。我完全同意你的看法;正如你所说的那样,我尽量避免过多的 C:有很多微妙之处!
猜你喜欢
  • 2021-06-12
  • 2019-12-19
  • 2021-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多