PDF 与文件工作台技术方案
1. 文档目的
本文把 requirements.md 和 competitive-analysis.md 中的产品目标转换为可实现的技术方案。方案假设从零开始开发 iPhone 和 iPad 原生应用,一次性完成已经规划的 P0、P1、后续版本能力,并为所有高风险功能定义实现方式、数据边界和验收条件。
本文中的“完整版本”包含:扫描、OCR、PDF 阅读与搜索、页面管理、合并/拆分、压缩、签名、标注、表单填写、永久打码、密码保护、批处理、模板、水印、页码、Shortcuts、Share Extension、iPad 拖放和 Apple Pencil,以及购买、恢复购买和家庭共享。
文档中明确暂缓的自有云盘、账号系统、在线协作、任意已有文本/图片编辑、高保真 Office 双向转换、AI 摘要/问答/翻译、语音功能和依赖服务器的 OCR 不在本版本范围内。这些能力会改变本地优先和无账号的核心架构,不能作为隐含需求加入。
2. 目标与约束
2.1 产品目标
- 用户从 Files、照片、相机或 Share Sheet 进入后,可以围绕一个明确任务完成处理并直接得到真实文件。
- 默认在设备本地完成导入、OCR、处理和导出,不上传用户文件,不要求登录。
- iPhone 和 iPad 使用同一套领域模型和处理引擎,界面根据屏幕尺寸、分屏状态和输入方式适配。
- 原始文件默认只读,所有处理生成新的 revision 或新文件;覆盖和删除必须显式确认。
- 处理前显示页数、源文件大小、预计输出大小、需要的权限和预计耗时。
- 处理过程中不弹出购买页面;付费提示只出现在任务结果、批量操作或高级选项处。
2.2 非功能目标
| 维度 | 目标 |
|---|---|
| 可用性 | 新用户首次打开后 60 秒内可以导入或扫描并开始处理 |
| 可靠性 | 10 页以内的常规 PDF 在设备端完成 OCR;处理失败不破坏源文件 |
| 兼容性 | 输出文件能在系统预览、Adobe Acrobat、PDF Expert 和常见浏览器中打开 |
| 性能 | 页面缩略图首屏尽量在 500 ms 内出现;长任务可取消、可恢复进度 |
| 隐私 | 文件内容、OCR 文本和签名不出设备;诊断日志默认不包含文件内容 |
| 可访问性 | VoiceOver、Dynamic Type、键盘导航、Reduce Motion、深色模式可用 |
| 可维护性 | 核心处理引擎与 UI 解耦,可用纯 Swift 测试和固定 PDF 样本回归 |
2.3 平台最低版本和版本策略
建议把版本策略定为“可安装下限 + 推荐支持下限 + 当前主测版本”三层,而不是只跟随最新系统。这里的版本号以正式开发时 App Store Connect 和 Xcode 的实际支持情况再复核一次。
| 层级 | iPhone/iPad | 用途 |
|---|---|---|
| 可安装下限 | iOS/iPadOS 18.0 | 本项目只支持 iPhone 与 iPad,使用统一的 SwiftData、SwiftUI、PDFKit、Vision 和 App Intents 实现 |
| 推荐支持下限 | iOS/iPadOS 18.0 | 正式支持线,减少系统 PDF、SwiftUI 和 Vision 行为差异 |
| 主测版本 | 推荐下限所在大版本的最新正式小版本 | 产品验收、性能门槛和 TestFlight 主要基线 |
| 追踪版本 | 当前最新正式大版本 | 提前发现系统升级后的兼容问题,不立即把它设为最低版本 |
因此,本项目的实际决策是:最低部署目标和正式支持线统一设为 iOS/iPadOS 18.0。这样可以让原生 PDF、SwiftUI、Vision、App Intents、拖放和多窗口行为使用同一套实现,避免为了旧系统保留两套持久化和界面路径。
工程工具链使用正式版 Xcode 和对应的 Swift 6.x,采用当时最新稳定 SDK,不使用 beta SDK 作为发布构建。打开严格并发检查,所有新系统能力通过 @available 和平台适配器隔离。最低版本上的核心流程不能依赖推荐版本专属 API:
- iOS/iPadOS:最低线支持文档扫描、照片选择器、Share Extension、Apple Pencil 基础输入、拖放、PDF 阅读和页面操作;OCR 选择设备可用的最高 Vision revision。
- 推荐版本可以启用更稳定的 SwiftUI 导航、App Intents、PDFKit 和系统 Vision revision,但必须保留最低线的可用降级路径。
版本之外还需要控制硬件基线:A12 设备作为“能运行”的兼容性样本;A14 或更新的 iPhone/iPad 作为 OCR、扫描、批处理和性能验收基线。这样可以避免应用虽然安装在旧设备上,却无法达到 10 页 OCR 和大文件处理的体验目标。
3. 完整功能范围
3.1 应用入口和文件接入
- 首页任务入口:扫描、签名、压缩、合并/拆分、导入文件。
- Files 导入:支持单选、多选、iCloud Drive 和第三方 File Provider;处理 security-scoped URL。
- 照片库导入:使用
PHPickerViewController/PhotosPicker,支持多张图片并保留用户选择范围。 - 相机扫描:iPhone/iPad 使用 VisionKit 自动识别边缘、透视校正、多页扫描和重拍。
- Share Extension:接收 PDF、图片和文档类型,写入 App Group 暂存目录后交给主应用;主应用未运行时也能完成接收。
- 拖放:iPad 支持从 Files、照片、邮件和其他应用拖入;iPhone 支持系统允许的文件拖放和 Share Sheet 导入。
- 文件类型:首版稳定支持 PDF、JPEG、PNG、HEIC、TIFF;其他类型显示不支持原因,不在本地偷偷转换。
- 最近任务:显示最近处理过的文档引用、状态和再次执行入口,不复制原文件内容。
- 文件重命名:单文件和批量重命名,支持模板变量(日期、序号、原文件名)。
3.2 阅读、搜索和页面管理
- PDF 阅读:单页、连续、缩略图、双页、适应宽度、适应页面、旋转、夜间/高对比度模式。
- 导航:页码跳转、上一页/下一页、文档目录、链接跳转和搜索结果定位。
- 搜索:搜索 OCR 文字和原生 PDF 文字,支持中文、英文、大小写和结果高亮。
- 页面操作:删除、复制、提取、插入图片/PDF、旋转 90/180/270 度、拖动重排、合并多个文件、拆分为单页或按范围拆分。
- 页面选择:连续范围、多选、全选和反选;所有危险操作显示影响页数。
- 页面预览:缩略图异步生成,内存压力过高时释放缓存并按需重建。
- 文档信息:页数、尺寸、方向、文件大小、创建/修改时间、加密状态和 OCR 状态。
3.3 扫描和 OCR
- 自动/手动快门、连续扫描、重拍、撤销上一页、调整顺序和裁切。
- 图像增强:原色、灰度、黑白、自动增强、去阴影、锐化;每页可以单独调整。
- OCR:设备端支持简体中文、繁体中文和英文;保存文字、置信度、段落和 bounding box。
- 可搜索 PDF:在页面图像上叠加可搜索文本层,保留页面外观。
- OCR 结果编辑:显示识别文本,允许用户修正文字;重新生成文本层而不改变原始页面图像。
- OCR 搜索和导出:搜索命中后定位页面;可将识别文本导出为 TXT/Markdown(只导出文字,不承诺版式还原)。
- OCR 质量指标:记录每页置信度、低置信度字符提示和用户手动修正数量,仅存本地。
3.4 签名、标注和表单
- 手写签名:PencilKit/触控板绘制、撤销重做、裁切空白、颜色和线宽。
- 图片签名:从照片或 Files 导入,自动去除纯色背景,可手动调整透明度。
- 签名库:保存、重命名、删除、默认签名;签名数据存 SwiftData/应用容器,不能写入源 PDF。
- 签名放置:拖动、缩放、旋转、复制到其他页面;导出时将签名固定到新 PDF。
- 基础标注:高亮、下划线、删除线、自由画笔、文本框、箭头、矩形、圆形、线条、便签。
- 标注属性:颜色、透明度、线宽、字体大小、字体颜色、前后顺序和复制粘贴。
- 标注编辑:选择、移动、缩放、删除、撤销/重做;兼容 PDFKit 原生 annotation 和应用自定义 annotation。
- 表单填写:识别 AcroForm 字段并支持文本框、复选框、单选框、下拉框;对于没有字段的静态表格提供“文本/勾选/签名”叠加模式。
- 表单导出:默认保留填写后的字段;提供“扁平化”选项,将字段和标注固定到新 PDF。
3.5 压缩、导出和保护
- 压缩预览:处理前显示源文件大小、页数、预计输出大小和预计质量损失。
- 压缩档位:轻度、平衡、强力和自定义;自定义包含目标 DPI、JPEG 质量、灰度/黑白、是否移除元数据。
- 压缩策略:对图片重采样和编码;保留矢量页面和文字时不栅格化;扫描页按页面独立处理。
- 目标大小:用户可以输入最大文件大小,算法迭代质量和 DPI;无法达到时给出可读性风险提示。
- 导出目标:保存到 Files、覆盖前另存为、AirDrop、邮件附件、消息和其他 Share Sheet 目标。
- 输出命名:原文件名 + 操作后缀,支持用户修改;不默认覆盖原文件。
- 密码保护:设置用户密码/所有者密码、打印/复制/修改权限;使用 PDF 加密输出并要求二次确认密码。
- 移除密码:打开受保护文档后验证密码,用户明确选择“移除保护”才导出未加密副本。
- 元数据:查看并编辑标题、作者、主题、关键词;导出时可选择移除元数据、附件和隐藏注释。
3.6 永久打码
- 标记模式:矩形、自由路径和文本匹配;显示待打码数量和影响页码。
- 应用打码:二次确认并明确“此操作不可撤销,源文件不会修改”。
- 安全实现:含打码区域的页面栅格化为新页面图像,移除原文本内容、注释、链接、隐藏图层、嵌入附件和相关元数据;必要时整份文档扁平化。
- 视觉样式:纯黑、白色或自定义颜色;默认黑色并可加入“已打码”覆盖文字。
- 结果验证:复制、搜索、文本提取和 PDF 对象扫描都不能取回遮挡内容;验证失败时禁止导出并保留未打码的源文件。
3.7 批处理、模板和自动化
- 批量压缩、合并、拆分、重命名、添加页码、添加水印和导出。
- 批处理队列:显示每个文件状态、进度、错误原因、重试和跳过;单项失败不影响其他项目。
- 模板:保存页面尺寸、页码位置、字体、颜色、水印、文件命名和压缩预设;模板不保存用户文件内容。
- 页码:起始页码、前缀/后缀、位置、边距、字体、奇偶页规则和跳过封面。
- 水印:文本或图片、透明度、角度、平铺/居中、前景/背景和应用页范围。
- Shortcuts/App Intents:导入文件、扫描结果处理、压缩、合并、重命名、导出;无 UI 任务必须有明确的输入/输出文件。
- iPad 多窗口:支持拖放文件到应用窗口、分屏处理和将结果拖到其他应用。
3.8 购买和权益
- 免费层:查看、搜索、基础标注、有限扫描/OCR、有限压缩/合并;用户至少可完整完成一次核心任务。
- Pro:无限 OCR、扫描、压缩、合并和批处理;高级签名、永久打码、密码保护、模板、水印、页码、Shortcuts 和高级导出。
- 购买模型:实现一次性买断和可选年订阅两种产品类型,首发界面默认展示买断,年订阅只在实际提供持续服务时启用;不使用默认周订阅。
- StoreKit 2:购买、恢复购买、家庭共享、价格本地化、交易验证、退款/撤销状态和离线缓存权益。
- 付费墙:不在启动、扫描和处理进度中打断;在结果页、批量入口或高级选项被点击时展示透明的权益、价格、周期和恢复购买入口。
4. 总体架构
4.1 工程目标和模块
采用 Swift 6、SwiftUI、Structured Concurrency 和 Swift Package Manager。iPhone 和 iPad 共用一个 iOS App target(TARGETED_DEVICE_FAMILY = 1,2)、领域层、处理引擎和大部分界面,平台层只负责设备 API 适配;Share Extension 单独作为 extension target 构建。
建议目录/模块如下:
PDFWorkbench/
App/ # 启动、路由、依赖注入、生命周期
Features/
Home/ # 任务入口和最近任务
Import/ # Files、Photos、Share Extension、拖放
Scanner/ # VisionKit/AVFoundation 扫描
Viewer/ # PDF 阅读、搜索、缩略图
Pages/ # 页面选择、重排、合并、拆分
OCR/ # Vision 识别、文本层、编辑和搜索
Annotation/ # 标注、签名、表单
Compression/ # 压缩预览和输出
Redaction/ # 永久打码和验证
Batch/ # 批处理、模板和队列
Export/ # Files/Share Sheet/AirDrop/邮件
Paywall/ # StoreKit 2 和权益
Settings/ # 隐私、默认设置、诊断导出
Domain/
Models/ # 与平台无关的值类型和业务实体
UseCases/ # 导入、处理、导出、权限和权益用例
Repositories/ # 文档、任务、签名、模板、权益接口
Processing/
PDFCore/ # PDFKit、CGPDF、Core Graphics 封装
OCRCore/ # Vision 和文本层写入
ImageCore/ # 解码、采样、增强、压缩
RedactionCore/ # 打码、扁平化、内容清理和验证
JobCore/ # actor 队列、取消、断点和临时文件
Persistence/
SwiftData/ # 最近任务、签名、模板、设置、作业状态
FileStore/ # 文件引用、security-scoped bookmark、暂存目录
Platform/
iOS/ # VisionKit、PhotosPicker、PencilKit、拖放
ShareExtension/ # NSExtension、App Group 文件交接
AppIntents/
Tests/
4.2 分层规则
- SwiftUI View 只负责状态展示和用户事件转发,不能直接操作
PDFDocument或文件 URL。 - Use Case 负责业务流程,例如
ImportDocumentUseCase、CompressDocumentUseCase、ApplyRedactionUseCase。 - Processing 模块负责纯文件转换和计算,输入/输出使用临时目录和不可变参数。
- 所有长任务运行在专用 actor 中,不能在主线程读写大文件或渲染整本 PDF。
- Repository 通过协议注入,便于替换真实文件系统、内存测试仓库和故障注入实现。
- UI 状态以
Observable/@MainActor管理;处理状态通过AsyncStream<ProgressEvent>回传。
4.3 任务执行模型
任何超过一页或可能超过 200 ms 的操作都创建 ProcessingJob:
- 创建任务记录、输入文件引用、参数快照和预估资源。
- 将源文件复制或安全链接到应用临时工作目录,源文件只读。
ProcessingEngine在ProcessingActor中分阶段执行并写入临时输出。- 每个阶段更新进度、日志和可恢复 checkpoint;用户可以取消。
- 完成后原子移动到用户选择的输出位置,写入新 revision 和摘要信息。
- 失败时删除临时文件,保留错误码、可读提示和重试入口。
不在 SwiftData 中保存大文件二进制。数据库只保存文件 URL、书签、校验哈希、页数、大小、缩略图路径和处理状态。
5. 核心数据模型
5.1 SwiftData 实体
DocumentRecord
id: UUID
displayName: String
sourceURLBookmark: Data?
localStagingURL: String?
contentHash: String
fileSize: Int64
pageCount: Int
mimeType: String
isEncrypted: Bool
ocrState: pending/running/ready/failed
createdAt, updatedAt, lastOpenedAt
DocumentRevision
id: UUID
documentID: UUID
parentRevisionID: UUID?
operation: import/scan/ocr/merge/split/compress/annotate/redact/export
outputURL: String
contentHash: String
parametersJSON: Data
createdAt
SignatureAsset
id: UUID
name: String
vectorDataOrPNGURL: String
color, scale, createdAt, lastUsedAt
Template
id: UUID
name: String
pageNumberStyleJSON: Data
watermarkStyleJSON: Data
compressionPresetJSON: Data
filenamePattern: String
ProcessingJob
id: UUID
type: String
inputRevisionIDs: [UUID]
state: queued/running/cancelled/failed/completed
progress: Double
checkpointJSON: Data?
errorCode: String?
createdAt, completedAt
AppSettings
defaultCompressionPreset
defaultExportFolderBookmark
preferredOCRLanguages
privacyAndDiagnosticsFlags
5.2 文件引用和生命周期
- 外部文件以 security-scoped bookmark 记录;每次访问前
startAccessingSecurityScopedResource(),完成后配对调用停止访问。 - 应用沙盒暂存目录只保存正在处理或用户明确选择“保留副本”的文件;后台清理过期暂存文件。
- Share Extension 通过 App Group 写入唯一目录,主应用接管后删除 extension 临时副本。
- iCloud Drive 文件不假设本地一直可用;打开前检查下载状态,处理前显示下载进度和剩余空间。
- 每个导出文件写入临时路径、执行 fsync/关闭句柄后再原子 rename,避免生成半个 PDF。
6. 关键处理方案
6.1 扫描
iPhone/iPad
使用 VNDocumentCameraViewController 获取页面图像,监听扫描完成、取消和错误。保存原始图像和增强后的渲染图,页面模型中记录拍摄方向、裁切四边形、滤镜和重新处理参数。用户确认后才生成 PDF 和启动 OCR。
质量要求
- 默认保存 200–300 DPI 等效输出;用户可在设置中选择质量。
- 低光、倾斜、阴影、重复页面和空白页面是固定回归样本。
- 页面顺序调整和重拍不能触发已确认页面的重复 OCR。
6.2 OCR 和可搜索 PDF
使用 VNRecognizeTextRequest,按设备能力选择 .accurate 或 .fast,识别语言为简体中文、繁体中文和英文。每页先用 ImageIO/Core Graphics 下采样到合理像素范围,再提交 Vision,避免整份高分辨率 PDF 占满内存。
OCR 结果包含原文、候选置信度、行/词 bounding box、语言和版本。文本层写入采用独立的 SearchablePDFWriter:
- 将页面图像作为背景写入新 PDF。
- 按 Vision 的坐标系转换到 PDF 坐标系。
- 写入不可见但可搜索的文字内容,保持行位置和字符顺序。
- 写入 OCR 版本、语言和页面状态到应用侧元数据,不把隐私文本写入诊断日志。
- 使用系统预览、PDFKit、命令行文本提取和 Acrobat 对同一页执行搜索回归。
由于 PDFKit 没有完整公开的文本层编辑接口,文本层写入必须由 SearchablePDFWriter 封装 Core Graphics 和必要的低层 PDF 内容流生成;不能用黑色/透明文本注释冒充 OCR。无法保持搜索能力时,必须明确降级为图像 PDF 并提示用户。
OCR 编辑只修改文本层,不修改扫描图像;用户导出时可以选择保留或重新生成文本层。
6.3 PDF 页面操作
统一使用 PDFDocument 做页面级读取和大多数标注操作,用 CGPDFDocument/Core Graphics 做合并、重建和压缩。跨文档操作先复制页面资源到临时 PDFDocument,再通过 PDFDocument.insert(_:at:) 或自有 writer 输出,避免修改输入对象。
- 合并:按用户选择顺序拼接页面,检测加密文档并逐一要求密码。
- 拆分:按页范围生成多个输出文件,名称使用序号和原名。
- 旋转:优先修改 page rotation;需要固定方向时再重绘页面。
- 重排/删除/复制:在内存中只保留页面索引和缩略图,最终导出时执行真实页面复制。
- 插入图片:按页面尺寸和边距生成图像页,保留方向和 DPI 信息。
6.4 压缩
压缩器先扫描页面资源,再按策略处理:
- 矢量文字、路径和线条默认保留。
- 大于目标 DPI 的图片使用 Lanczos/系统高质量缩放后重新编码。
- 透明图像根据页面实际需要保留 alpha,否则转为不透明 JPEG。
- 扫描页支持彩色、灰度和黑白;文本层单独保留。
- 清理未使用资源、缩略图、嵌入附件和可选元数据。
目标大小采用多轮估算,优先降低图片质量和 DPI,达到下限仍无法满足时返回“无法在可读范围内达到目标”的结果,不输出不可用文件。处理前的预计大小来自抽样页面,不把估算当作保证。
6.5 签名、标注和表单
交互层使用 PDFKit 的 PDFView、PDFAnnotation 和自定义 overlay。自定义 overlay 只保存交互状态,导出时转换为 PDF annotation 或扁平化内容。签名采用 PencilKit PKDrawing 保存矢量数据,同时生成 PDF 可嵌入的透明 PNG;导出时按页面坐标和旋转矩阵合成。
AcroForm 字段通过 CGPDF 字典解析并映射为 SwiftUI 控件。填写结果优先写入字段值和外观流;用户选择扁平化时重新绘制页面并删除可编辑字段。
6.6 永久打码
打码必须保证原文无法通过复制、搜索、文本提取、注释反编辑或资源恢复。默认实现如下:
- 标记阶段只保存
RedactionMark,不改变源文件。 - 应用阶段对含标记的页面以至少 200–300 DPI 渲染。
- 在渲染图上绘制打码区域,再生成新页面;删除该页面原有文字层、注释、链接和隐藏内容。
- 清理文档级 metadata、embedded files、JavaScript、附件和未使用资源。
- 对所有输出页面执行文本提取、搜索和对象扫描;验证任何打码区域不存在原字符串。
- 验证通过后才允许导出,并把结果写成新 revision。
若用户要求保留未打码页面的矢量文字,可以只栅格化受影响页面;若 PDF 结构复杂、无法证明安全,则整份 PDF 扁平化,而不是冒险保留可恢复内容。
6.7 密码和加密
打开加密 PDF 时由 UI 请求密码,密码只在当前任务内存中使用,不写入 SwiftData、日志或 URL。验证成功后才允许页面读取和处理。输出通过 PDF 加密选项设置用户/所有者密码和权限;密码移除必须生成新的未加密副本,并在结果页明确显示状态。
7. 平台集成
| 能力 | iPhone/iPad |
|---|---|
| 文件导入 | fileImporter、UIDocumentPicker、PhotosPicker、Share Extension |
| 扫描 | VisionKit 文档相机 |
| 阅读/标注 | PDFKit/PDFView |
| 手写 | PencilKit、Apple Pencil |
| 分享 | Share Sheet、Share Extension、AirDrop、邮件附件 |
| 自动化 | App Intents、Shortcuts |
| 文件安全 | security-scoped URL、应用沙盒和 App Group |
| 购买 | StoreKit 2,按 Apple ID 同步权益 |
Share Extension 和主应用共用 AppGroupFileHandoff,不得直接共享 UI 状态。Extension 只负责接收和落盘,所有 PDF 处理都在主应用或后台任务中进行。
8. 隐私、安全和权限
- 相机、照片、文件权限在实际触发前说明用途;只申请完成当前操作所需的权限。
- 默认不启用网络上传;项目不包含自建文件服务器、账号登录和远程 OCR。
- 网络权限只用于 App Store/StoreKit、系统分享目标和用户主动打开的第三方服务;产品代码不读取无关文件。
- 应用容器和 App Group 暂存目录使用系统沙盒;任务完成后按保留策略删除临时文件。
- 签名、OCR 文本、密码和打码原文不能进入崩溃日志、分析事件或埋点。
- Privacy Manifest、Info.plist 用途说明、App Store 隐私标签和隐私政策必须与实际 API 使用一致。
- 删除、覆盖、移除密码、永久打码和清理历史记录都要二次确认。
- 导出前显示目标应用/位置和文件大小;分享失败时不自动重试到未知目标。
9. StoreKit 2 设计
定义统一的权益层,不让 UI 直接读取产品 ID:
Entitlement
canUseBasicViewer
scanQuota
ocrQuota
compressionQuota
canUseAdvancedSignature
canRedact
canProtectWithPassword
canRunBatch
canUseShortcuts
canUseTemplates
StoreKitService 负责产品加载、购买、交易验证、Transaction.updates 监听、撤销/退款同步和恢复购买。EntitlementStore 缓存最近一次有效结果用于离线场景,并在下次联网或应用启动时重新验证。测试必须覆盖新购、重复购买、家庭成员、退款、撤销、卸载重装和跨设备恢复。
10. 错误处理和用户体验
所有错误归一化为可解释的 AppError:
permissionDenied:说明需要哪一项系统权限以及去设置的入口。fileUnavailable:文件尚未下载、被移动或 security-scoped bookmark 失效。passwordRequired/passwordIncorrect:允许重试,不能将密码写入错误详情。unsupportedPDF:指出加密、损坏、格式特性或资源限制。insufficientStorage:显示需要空间和可清理的临时文件。processingCancelled:保留源文件,清理临时输出。validationFailed:禁止导出危险结果,提供重新处理或导出原始副本。
长任务统一显示阶段、当前页、总进度、取消和后台状态。操作必须支持撤销/重做;跨文件批处理使用“重试失败项”,不因为一个文件失败而让用户重新做完全部任务。
11. 测试方案
11.1 单元和集成测试
- 页面合并、拆分、旋转、复制、重排和插入的页数/顺序/尺寸。
- 压缩前后大小、DPI、透明度、文字可选性和视觉质量。
- OCR 坐标转换、中文/英文混排、旋转页面和多栏布局。
- 签名缩放/旋转、标注坐标、AcroForm 字段和扁平化。
- 打码后的复制、搜索、文本提取、附件和 metadata 清理。
- 密码保护/移除、权限标志和错误密码。
- 任务取消、断点、临时文件清理、磁盘不足和应用被杀后恢复。
- StoreKit 2 交易、恢复购买、家庭共享和撤销。
11.2 固定样本集
建立不包含真实个人信息的版本化 PDF 样本:中文合同、发票、证件、表格、多栏文档、扫描阴影、低分辨率图片、旋转页面、超大图片、加密 PDF、带附件 PDF、带 JavaScript/注释 PDF 和损坏文件。每次处理引擎变更都执行 golden file、文本提取和渲染像素差异测试。
11.3 真机和系统测试
- iPhone 小屏、标准屏和大屏;iPad 分屏、多窗口、拖放和 Apple Pencil。
- iOS/iPadOS 支持版本、深色模式、动态字体、VoiceOver、键盘导航和 Reduce Motion。
- 本地文件、iCloud Drive 未下载状态、第三方 File Provider、AirDrop、邮件附件和 Share Extension。
- 低存储、无网络、后台切换、锁屏、应用被系统终止和恢复购买。
11.4 性能门槛
- 10 页中文扫描 PDF OCR 在目标设备上完成时间和峰值内存记录在测试报告中。
- 100 页 PDF 缩略图按需加载,滚动不因全量解码产生明显卡顿。
- 处理期间主线程不执行整页渲染、OCR 或大文件复制。
- 连续批处理不会随文件数量线性积累内存;每个任务结束释放页面对象和图片缓存。
12. 构建、发布和质量门禁
- 使用 Xcode Cloud 或同等 CI 构建 iOS、iPadOS、Share Extension 和测试目标。
- CI 执行 SwiftFormat/SwiftLint、编译警告视为失败、单元测试、处理引擎集成测试和 PDF 样本回归。
- 使用独立的 StoreKit Configuration 文件测试购买,不在开发环境调用真实购买。
- 发布前检查沙盒 entitlement、App Group、文件类型 UTI、Info.plist 权限说明、Privacy Manifest 和 App Store 隐私问卷。
- TestFlight 阶段使用真实设备验证扫描、OCR、签名、压缩、导出、打码、密码和恢复购买;记录不含文件内容的错误码和性能指标。
- 发生处理失败时优先保留原文件和可复现参数;提供用户主动导出的诊断包,诊断包不包含文档内容,除非用户明确选择附加样本。
13. 实施顺序
这里的顺序是工程依赖顺序,不代表缩减为 MVP:
阶段一:基础骨架和文件安全
- 建立 iOS App target、Share Extension target、Swift Package、依赖注入和 App Group。
- 完成文件引用、security-scoped bookmark、暂存目录、任务状态机和错误模型。
- 完成首页、最近任务、导入/导出和基础 PDF 阅读。
阶段二:PDF 引擎和页面工作流
- 完成缩略图、搜索、页面选择、合并、拆分、旋转、重排、复制、提取和插入。
- 完成 Core Graphics 输出、文件原子写入和基础回归样本。
阶段三:扫描、图像和 OCR
- 完成 iOS/iPadOS 扫描、图像增强和质量设置。
- 完成 Vision OCR、文本层写入、OCR 编辑和搜索。
阶段四:签名、标注、表单和导出
- 完成 PDFKit 标注、PencilKit 签名、签名库、AcroForm 和扁平化。
- 完成压缩预览、目标大小、Files/Share Sheet/AirDrop/邮件导出。
阶段五:安全文档能力
- 完成永久打码、验证器、密码保护/移除、metadata/附件清理。
- 建立安全回归样本和“验证失败禁止导出”门禁。
阶段六:批处理、模板和 Apple 自动化
- 完成批量压缩/合并/拆分/重命名/页码/水印、模板、App Intents、Shortcuts、iPad 多窗口、拖放和 Pencil 优化。
阶段七:商业化和发布
- 完成 StoreKit 2、权益层、买断/年订阅、家庭共享、恢复购买和离线缓存。
- 完成可访问性、本地化、性能压测、CI、TestFlight、审核资料和隐私一致性检查。
14. 完整版验收清单
- 能从 Files、照片、相机、Share Extension 和拖放进入任务。
- 能扫描多页纸张,调整顺序、裁切、增强并生成可搜索中文/英文 PDF。
- 能阅读、搜索、标注、签名、填写表单并将结果导出为真实文件。
- 能合并、拆分、旋转、重排、提取、插入、重命名和批量处理 PDF。
- 能预览压缩结果和文件大小,按目标大小输出可读 PDF。
- 能永久打码并证明复制、搜索、文本提取和对象恢复都无法取得原文。
- 能设置和移除密码、控制打印/复制权限,并正确处理加密输入。
- 能添加页码、水印、模板,并运行 Shortcuts 自动化。
- 能在 iPhone 和 iPad 使用,支持 iPad 多窗口、拖放、Apple Pencil、深色模式和 VoiceOver。
- 能购买、恢复购买、家庭共享、卸载重装后恢复权益;付费提示不打断进行中的任务。
- 源文件在所有失败、取消和覆盖风险场景下保持不变;临时文件和敏感数据按策略清理。
15. 主要风险和需要提前验证的决策
- OCR 文本层兼容性:PDFKit 对文本层写入能力有限,必须尽早验证自有 writer 在系统预览、Acrobat、PDF Expert 和文本提取工具中的搜索效果。
- 永久打码安全性:复杂 PDF、透明图层和附件可能包含隐藏副本;遇到无法证明安全的文档必须整页或整份栅格化。
- 压缩与可读性:目标大小和扫描文字清晰度存在冲突,需要用真实设备和中文小字号样本确定默认预设。
- 大文件内存:PDFKit 页面对象和高分辨率图像容易造成峰值内存,必须坚持按页处理和临时文件流转。
- 加密 PDF 兼容性:不同版本 PDF 加密、权限和字体嵌入行为差异较大,不能只用 PDFKit 的单一示例验收。
- StoreKit 地区价格:文档中的竞品价格和我们的建议价格只能作为方向,最终以 App Store Connect 的实际地区价格和 TestFlight 购买数据为准。
- 免费权益定义:需要在产品规格中明确每月扫描/OCR、压缩/合并次数、单文件页数和批处理上限,避免 UI、StoreKit 和审核文案不一致。