乐于分享
好东西不私藏

iOS 27 Beta 2 苹方 VF,附技术分析

iOS 27 Beta 2 苹方 VF,附技术分析

最近发现某个家伙又开始说梦话。先说明一下,iOS 27 beta 2 的苹方 VF 与 iOS 26 的内容完全一致,只是刷了个版本号。(这么一来,我在之前就已经完成了 iOS 27 苹方 VF 提取件的「全球首发」。)

既然内容完全没有变化,我们可以直接使用先前发布的 iOS 26 苹方 VF 字体文件,获取方式见文末,也可以去上一篇 iOS 26 苹方 VF 发布页;之前已经下载过的朋友直接就能享受 iOS 27 的体验。:-)

iOS 26.4.1 / 27.0 beta 2 苹方 VF(含苹方-日)

抢着搞噱头是不好的,不能连最基本的事实核查都不做。

鉴于 2026 年 AI 的编程能力发展情况,大家都可以试试用 Alpha Argon 在 2024 年发布的 iOS 18 苹方提取思路分析进行 vibe coding、从而自行提取截至目前任意版本的苹方 VF。技术分析在iOS 18 苹方 VF 发布推文里早已放送,也可以来他的原始发布页:

https://zhuanlan.zhihu.com/p/703335162

可惜我看了看某个东西的所谓提取教程,开头就是事实性错误,还弄出「中国日本」这样的操作,你的脑袋是豆包生的吗?提取的门道是有的,门槛是客观存在的,只有一句话某个东西说对了,就是必须有一台 Mac;但说实在的,如果没有一颗正常的智人大脑,给了 Mac 也白搭。

最重要的事实错误:macOS、iOS 等自带的 PingFangUI.ttc 拿到原文件也不能正常读取,只有待在原本私有库下的路径时才能被 CoreText 读取。

import Foundationimport CoreTextlet copiedFileURL = URL("...")let data = Data(contentsOf: copiedFileURL)assert(data != nil)assert(CTFontManagerCreateFontDescriptorsFromData(data as CFData== nil)assert(CTFontManagerCreateFontDescriptorsFromURL(copiedFileURL as CFURL== nil)

系统给 PingFang UI 开了后门;将它复制到其他任何位置,都不能按普通字体文件正常读取。所以从 iOS 固件提取原文件来读取是不可能的。如果真的要通过 CoreText(更准确地说是 CoreGraphics)提取苹方,只能在最新的 macOS/iOS 实体机或 Xcode Simulator 上跑代码。

如果有幸确实拿到了 CTFont 实例,也不需要考虑 LSB 的值,CTFontCreatePathForGlyph 只返回规范后的轮廓。

let font = CTFontCreateWithFontDescriptor(......)var gid = 4635 as CGGlyphvar width = 0 as CGFloatCTFontGetAdvancesForGlyphs(font, .horizontal, &gid, &width, 1)let path = CTFontCreatePathForGlyph(font, gid, nil)!

这里所谓规范化指将字形的 LSB point 定为 x = 0,然后按照字号/UPM 缩放。如果一定要说,LSB 在这里就等于实际字面的最小横坐标;但既然记录了整个路径,为什么还要再单独强调 LSB 呢?

特意在这里列出需要提取 UPM 值也显得非常可疑。PingFang UI 的原始 UPM 没有多大意义,因为系统为了跟西文匹配,放大了 UPM 来缩小字身。要确定正常的 UPM,一般要把汉字字身宽定为 1em,因此对于苹方来说,正好是 1000。然而 hvgl 支持浮点坐标,UPM 选用 1000 并不能完全准确地反映 PingFang UI 带小数的坐标值。说「unitsPerEm=1000 :这是中文字形的标准设计单位,确保字形精度」就是不对了。

fontTools 没有从 JSON 生成 TTF 的功能,fontmake 也没有。

真正会大量使用 JSON 作为中间格式的是 OTFCC 和 FontLab (VFJ)。这里说「导出为 JSON 格式」,再「使用 fontTools 库生成标准 TTF 文件」,中间缺了真正关键的构建逻辑。

如果只是把 Swift 读出来的路径转存成 JSON,再在 Python 里读 JSON,那这几个 4 GB 大的文件很像是 AI 作出的决定。以及人类编写 Swift 或 Objective-C 时,CGPathElementType 是枚举类型,会直接使用具名成员,不会在意它的 raw value 是多少。要特意写出「type=0 → moveTo……」这种废话,就是 AI 最喜欢的掉书袋了。

另外,PingFang 和 SF 确实用的是二次曲线。但根本原因是 TT VF 处理起来更高效一些;CFF2 VF 太凌乱了。「二次贝塞尔曲线(qCurveTo) :苹果字体内部使用二次曲线而非三次曲线」显得没头没脑的;二次曲线没有三次曲线有表现力,结合前文关于 UPM 的表述,这个时候又不说「确保字形精度」了。AI 掉书袋 +1。

为五地字形分别扒了一遍数据,完全不懂 ttc 表共用机制。

五地字形共用了 hvgl 表,只通过不同的 cmap 来启用不同的字形映射。理论上五地扒出来的 JSON 文件大小不应该有明显差异,因为共用的轮廓数据占大头。

我自己扒出来的五地 7 采样 VFJ 只有 470 MB。当然,我在扒的过程中做了轮廓合并和简化无用锚点,所以体积小一些。如果自己参与了代码编写的过程的话,显然不乐意几乎相同的文件,每个 4 GB 的要重复五次。

看下来最后还有两处疑点:

- 不知道为什么要省去 wght = 200 的 thin 和 wght = 900 的 black。即使 black 可以说是新的粗体可以忽略,但旧的静态苹方有的 thin 要删去就很不可思议了。

为什么要相信说出「中国日本」的人能干出正常的事呢?

PingFangUI 21.4d1e1 获取链接:
GitHub,优先:
https://github.com/ACT-02/PingFangUI-VF/releases/tag/21.4d1e1
蓝奏云,备用:

https://wwbsh.lanzout.com/i0ug13pf0vvg

如果你喜欢,可以以完全自愿的方式 buy me a coffee(使用文末赞赏功能),作为对本文技术分析的欣赏和鼓励。

相关学习资料