Swift at Apple: Migrating the TrueType Hinting Interpreter

Swift at Apple: Migrating the TrueType Hinting Interpreter

Apple 的 Swift 之旅:迁移 TrueType 微调解释器

Scott Perry Scott Perry 是 Apple 安全团队的一员,专注于 Swift 的应用。 June 12, 2026 2026 年 6 月 12 日

TrueType is a widely used vector font standard for rendering text in web pages, PDFs, operating systems, and applications. Familiar fonts like Helvetica, Garamond, and Monaco are all built on TrueType outlines. The format specifies a hinting interpreter intended to help outlines rasterize faithfully on low-resolution displays. Modern high-resolution displays enable beautiful typography from outlines alone, but TrueType fonts that need hinting to render legibly remain in use and we continue to support them. TrueType 是一种广泛使用的矢量字体标准,用于在网页、PDF、操作系统和应用程序中渲染文本。Helvetica、Garamond 和 Monaco 等熟悉的字体都是基于 TrueType 轮廓构建的。该格式定义了一种微调(Hinting)解释器,旨在帮助轮廓在低分辨率显示器上忠实地进行光栅化。现代高分辨率显示器仅凭轮廓就能呈现出精美的排版,但那些需要微调才能清晰渲染的 TrueType 字体仍然在使用中,我们也继续提供支持。

Font parsers process data from untrusted sources, making the TrueType hinting interpreter a security-critical attack surface. To make the format more resilient on Apple platforms, we rewrote its hinting interpreter from C to memory-safe Swift for the Fall 2025 releases. In addition to memory safety, we also improved performance: on average, our Swift interpreter runs 13% faster than the C interpreter it replaced. To accompany this post, we’ve also published the source code of the Swift TrueType hinting interpreter. We hope sharing our experience helps others doing similar work in Swift. 字体解析器会处理来自不可信来源的数据,这使得 TrueType 微调解释器成为了一个关键的安全攻击面。为了提高该格式在 Apple 平台上的稳健性,我们在 2025 年秋季发布版本中,将其微调解释器从 C 语言重写为内存安全的 Swift。除了内存安全性之外,我们还提升了性能:我们的 Swift 解释器平均比它所取代的 C 语言解释器快 13%。随本文一同发布的,还有 Swift TrueType 微调解释器的源代码。我们希望分享这些经验能帮助其他在 Swift 中进行类似工作的人。

TrueType and the hinting engine

TrueType 与微调引擎

Apple developed TrueType in the late 1980s and released it with the launch of System 7 in 1991. TrueType was a major breakthrough for the time: it gave font developers enormous control over how glyphs are displayed, with an advanced grid-fitting algorithm and a sophisticated hinting engine built around a special-purpose bytecode interpreter. TrueType did all this on computers that were vastly less powerful than today’s, so it had to be extremely well-tuned for performance. Apple 在 20 世纪 80 年代末开发了 TrueType,并于 1991 年随 System 7 的发布而推出。TrueType 在当时是一项重大突破:它通过先进的网格拟合算法和围绕专用字节码解释器构建的复杂微调引擎,赋予了字体开发者对字形显示方式的巨大控制权。TrueType 在当时性能远不如今天的计算机上实现了这一切,因此它必须在性能上进行极佳的调优。

Then the internet revolutionized how fonts were used. TrueType became embeddable in PDF files in 1994 and in web pages in 2008, and it remains as relevant as ever. However, these new use cases brought additional risk: TrueType could now be exposed to untrusted fonts from anywhere on the internet. TrueType fonts may contain programs the hinting engine runs through a bytecode interpreter. This interpreter involves input-driven control flow, complex data structures, and careful memory management—exactly the kind of code that’s hard to make perfect and where memory errors are easier to exploit. This high inherent complexity also makes correctness especially important. 随后,互联网彻底改变了字体的使用方式。TrueType 于 1994 年可嵌入 PDF 文件,2008 年可嵌入网页,至今依然具有重要意义。然而,这些新的使用场景带来了额外的风险:TrueType 现在可能会接触到来自互联网任何地方的不可信字体。TrueType 字体可能包含微调引擎通过字节码解释器运行的程序。该解释器涉及输入驱动的控制流、复杂的数据结构和精细的内存管理——这正是那种难以做到完美且容易被利用内存错误的代码。这种固有的高复杂性也使得正确性显得尤为重要。

Rewriting in Swift

使用 Swift 重写

A rewrite required a memory-safe language that could integrate into the existing codebase and provide an equivalent level of performance to the implementation it was replacing. Swift was the obvious choice for the task. Binary compatibility was crucial for this project to succeed: existing programs had to continue to function the same as they did before, effectively unaware that a new implementation was in place. This means not just interface compatibility but pixel-identical glyph rendering as well, relative to the C implementation. 重写需要一种内存安全的语言,它既能集成到现有代码库中,又能提供与被取代的实现相当的性能。Swift 是这项任务的不二之选。二进制兼容性对于该项目的成功至关重要:现有的程序必须像以前一样继续运行,实际上无法察觉到新实现的存在。这意味着不仅是接口兼容,相对于 C 语言实现,字形渲染也必须实现像素级的一致。

Hinting can radically change the on-screen appearance of glyphs, so a small change in the interpreter’s behavior could result in substantial user-visible changes. For this project, we defined correctness to mean exact compatibility with the C implementation’s outputs. 微调可以彻底改变字形在屏幕上的外观,因此解释器行为的微小变化都可能导致用户可见的重大差异。对于本项目,我们将正确性定义为与 C 语言实现输出的完全兼容。

Validating correctness

验证正确性

To ensure correctness, we developed two test suites. The first was a unit test suite that can target both implementations, providing exhaustive (99.7%) code coverage for both. This suite is included with the open source release of the Swift interpreter. Then, to represent real-world workloads, we used a fuzzer to minimize a corpus of 10 million PDF files down to 4,200 without any loss of code coverage. The documents in the minimized corpus embedded 25,572 fonts with a total of 27 million glyphs that we rendered using four different transformations each, comparing the resulting bitmaps against the reference interpreter. This gave us confidence in the new interpreter’s compatibility. By the end of the project, we wrote nearly four times as many lines of test code as we wrote for the Swift interpreter itself. 为了确保正确性,我们开发了两套测试套件。第一套是单元测试套件,可以针对两种实现进行测试,为两者提供了详尽(99.7%)的代码覆盖率。该套件已包含在 Swift 解释器的开源发布中。随后,为了模拟真实世界的工作负载,我们使用模糊测试工具(fuzzer)将 1000 万个 PDF 文件的语料库精简至 4200 个,且没有损失任何代码覆盖率。精简后的语料库中嵌入了 25,572 种字体,总计 2700 万个字形,我们使用四种不同的变换方式对每个字形进行渲染,并将生成的位图与参考解释器进行对比。这使我们对新解释器的兼容性充满信心。在项目结束时,我们编写的测试代码行数几乎是 Swift 解释器本身代码行数的四倍。

Achieving high performance

实现高性能

Once our new implementation passed all its tests, we turned our attention to performance. We assessed performance at a high level using PDF render time, and then iterated on improvements guided by benchmarks that rendered all of the glyphs from three different fonts. These improvements fell into four main categories. 一旦我们的新实现通过了所有测试,我们就将注意力转向了性能。我们使用 PDF 渲染时间在高层级上评估了性能,然后通过渲染三种不同字体的所有字形的基准测试来指导改进。这些改进主要分为四大类。

Minimizing runtime overhead 最小化运行时开销

Swift uses automatic reference counting for managing the lifetime of shared reference types, and runtime exclusivity checking for preventing overlapping access to data structures. These sources of overhead are often exacerbated by aliasing, an irreducible amount of which existed in the interpreter’s specification by design. These sources of overhead can be eliminated by giving up the convenience of copyability, adopting ~Copyable value types (see also: struct instead of class) throughout the architecture, reserving reference types for high-level abstractions. Span, introduced in Swift 6.2 with back-deployment support all the way back to macOS 10.14.4 and iOS 12.2, allowed us to efficiently operate on sequences of these types. Swift 使用自动引用计数来管理共享引用类型的生命周期,并使用运行时排他性检查来防止对数据结构的重叠访问。这些开销来源往往会因别名(aliasing)而加剧,而解释器的规范设计中本身就存在不可消除的别名。通过放弃可复制性的便利,在整个架构中采用 ~Copyable 值类型(另请参阅:使用 struct 代替 class),并将引用类型保留用于高层抽象,可以消除这些开销。Swift 6.2 中引入的 Span(支持回溯部署至 macOS 10.14.4 和 iOS 12.2)使我们能够高效地操作这些类型的序列。

Moving data around 数据迁移

Sometimes we want to change the “shape” of structured data when crossing language boundaries to better match the idioms on the other side. In Swift, glyph outlines are represented by a sequence of points, each of which carries a flag for whether it’s ‘on a curve’, flags for whether it’s been ‘touched’ on each axis, and three coordinate pairs: original (in the font’s base units), scaled (to the desired point size), and hinted (the interpreter program’s output). The original C code stored these points in one struct with eight arrays. This is good from a performance perspective because it’s cache-friendly: you can operate on a dimension of many points in long runs, which is fast. But exposing the data in Swift as a collection of point elements… 有时,当跨越语言边界时,我们希望改变结构化数据的“形状”,以更好地匹配另一侧的惯用写法。在 Swift 中,字形轮廓由一系列点表示,每个点都带有是否“在曲线上”的标志、在每个轴上是否被“触碰”的标志,以及三对坐标:原始坐标(以字体的基本单位)、缩放坐标(至目标字号)和微调坐标(解释器程序的输出)。原始的 C 代码将这些点存储在一个包含八个数组的结构体中。从性能角度来看,这很好,因为它对缓存友好:你可以对长序列中多个点的维度进行操作,速度很快。但将这些数据在 Swift 中作为点元素的集合暴露出来……