看 PDF 这件事,我换过太多工具,没一个能长期留在机器上。浏览器查看器打开就走,文档一多就散在标签堆里,缩略图、目录、上次读到哪全不管。桌面阅读器功能堆得满,启动要等,大半按钮从没点过,有的还要登录。Electron 那类更离谱,看一页纸先起一个 Chromium。
于是有了 AirPDF,当前 0.3.2,仓库在 https://github.com/RyzenWang/AirPDF。目标很小:打开文档,把页面放你面前,然后闭嘴。四条规矩——不联网、不要账号、不用内嵌浏览器、启动即用。阅读路径上不许有网络请求,界面和渲染在同一个原生进程里,不该出现需要盯着看的加载中。
名字里的 Air 指”轻”,不是安装包小,是打开和翻阅时感觉不到它。

渲染底层与阅读交互
渲染走 PDFium 原生库,按实际显示比例解码,高 DPI 下保持清晰。滚动连续,页面做虚拟化,只渲染视口附近的几页。左侧是缩略图与目录侧栏,顶部页码可直接输入跳转,底部显示文件信息。
全文搜索的结果按页分组,命中位置在页面和高亮框里同时标出。鼠标拖选、双击选词、全选、复制都支持,带旋转的页面也能正常选字、显示高亮、点链接。超链接分两类:文档内跳转落到目标页,http、https、mailto 交给系统处理。加密 PDF 自动弹密码框。

位置记忆、主题与本地化
阅读位置按文档记忆,精确到页内偏移;缩放模式也按文档记,适合宽度、适合页面或固定比例三选一。从目录、缩略图、搜索、页码跳转之后,Alt+Left 回到原位置。状态栏显示当前位置和进度百分比。
界面有浅色、深色、高对比度三套配色,跟随系统时随明暗实时切换。英文和简体中文两套界面,Ctrl+Shift+L 即时切换。标签页与多窗口共享同一份设置、主题和语言。工具栏按钮、输入框、列表都带无障碍名称。

深色主题

中文界面与最近打开列表
三平台产物与构建约束
同一份代码跑三个桌面平台,不依赖系统 WebView,也不需要另装运行时。三个平台都编译成 NativeAOT 原生二进制,启动到窗口都在 1 秒以内。
Windows 10/11 提供 Velopack 安装器(支持自动更新)和便携 zip,约 28 MB。macOS 13+ 有 arm64 / x64 的 pkg 与便携包。Linux 是便携 tar.gz,要求 glibc 2.34+(Ubuntu 22.04 起)和 fontconfig。渲染后端的原生库按平台分别携带,开箱即用。每次发版三平台一起构建,测试在 Linux CI 上跑,界面语言、快捷键、主题行为保持一致,Cmd 与 Ctrl 按平台自动映射。
读中文文档时,Linux 上建议装 CJK 字体,比如 fonts-noto-cjk。三个平台的包都还没签名,Windows 和 macOS 会弹安全警告。
AOT 构建下 Avalonia 的 XAML 编译要显式开启,AvaloniaUseCompiledBindingsByDefault 和 AvaloniaXamlIlDebuggerLaunch 这类开关配错,运行时才炸。项目把 IL 警告当错误处理,任何一条裁剪警告直接让构建失败——这是刻意的,反射路径被裁掉后崩在用户机器上比崩在 CI 上贵得多。排查时用 dotnet publish -r win-x64 -c Release /p:PublishAot=true /p:TrimmerSingleWarn=false 让每条警告单独列出,再对照 rd.xml 或 DynamicDependency 补根。
0.2 之后的三处硬仗
第一是全面切 NativeAOT。Windows 上从 5.0 秒到第一帧压到 0.7 秒,开销几乎全在启动时的 JIT,分散在 Avalonia 初始化、XAML 加载和首次布局里,没有哪一处是”慢调用”,所以只能换编译方式。Windows 包同时从 53 MB 裁到 28 MB。
第二是旋转页。原来几何和顺序两半都错:内容坐标没跟着 /Rotate 变换,文本层又把旋转页的字拆成单字或倒序。现在内容矩形统一走一个页面几何变换,文本层改从 PDFium 的文本接口读,字符按阅读顺序返回。验证这类修复不能只看渲染截图——用 pdfium_test 对同一页分别导出渲染位图和文本抽取结果,比对字符序列与页面坐标,混合方向页面(一页内多个 /Rotate 值)单独造样本,才能确认没有回归。
第三是 Linux 的 glibc 钉版。AOT 会链接构建机的 glibc,在 24.04 上编出来的二进制到 22.04 就报符号缺失。现在构建机固定 22.04,发版时读取产物的符号版本,超过 2.34 就让流水线失败。
签名工具链已就位,能校验证书、也能用临时自签证书跑通全流程,仓库里还没配正式证书。
实测数字与它的边界
本机多次运行的常见值,同一台机器上两份文件来回测会有波动。
启动到窗口出现(Windows)0.7 s;打开 4,215 页 / 15.8 MB 约 1.3 s;打开 9,277 页 / 35.3 MB 在 2–4 s;单页渲染 20–45 ms;4,215 页文档内存占用约 280 MB;全文搜索(9,277 页)1–3 s;Windows 下载大小 28 MB。
样本是美国法典两卷公开数据(govinfo),页数多、内容简单,正好把”按页数增长”的开销放大。打开文档后句柄常驻,翻页只渲染当前页,页面列表只实例化视口附近的页面,所以这些耗时基本不随页数线性增长。
搜索是例外。它逐页提取文本,没做索引缓存,页数越多越慢,这也是它还在待办里的原因。这套方案适合以阅读为主、偶尔搜索的场景;如果日常要在上千页文档里高频全文检索,现阶段的 AirPDF 不是最优解,得等索引缓存实现。
还没做完的
原生打印、另存副本、用户书签(在做,存在本地设置里,不改动原文档)、字符级文本选择(现在是词粒度)、批注(先只做高亮)、更多界面语言(需要先把本地化结构改成能装任意种语言)、配好 Windows 签名证书和 Apple 公证去掉安全警告、给缩放搜索语言切换和更新补端到端测试。
0.3.2 仍是预览版。阅读核心稳定,上述限制都在,发布包也还没签名。MIT 协议,issue 和 PR 都欢迎。
ID:7789