# PDF 阅读方案评估 ## 概述 PDF 是知习支持的核心文件格式之一。本文档明确 PDF 在各平台的处理策略、技术选型边界和后续路线图。 ## 核心结论 1. **iOS 第一版继续使用 PDFKit / QuickLook** — 系统内置,无需额外依赖 2. **Rust 暂不接入 PDFium** — 增加包体但不创造足够价值 3. **文本选择由平台能力承担** — PDFKit(iOS)、PdfRenderer(Android) 4. **PDF 搜索后置** — 待文本提取方案确定后再做 5. **扫描 PDF / OCR 暂缓** — 明确不在第一版范围 --- ## 方案对比 ### 候选方案 | 方案 | 二进制大小 | 平台支持 | 文本提取 | 搜索 | 标注 | 成熟度 | |------|----------|---------|---------|------|------|--------| | **QuickLook (iOS/macOS)** | 0(系统内置) | Apple only | 否 | 否 | 否 | 高 | | **PDFKit (iOS/macOS)** | 0(系统内置) | Apple only | 是 | 是 | 是 | 高 | | **Android PdfRenderer** | 0(系统内置) | Android only | 部分 | 否 | 否 | 中 | | **pdfium-render (Rust)** | ~15MB/平台 | 全平台 | 是 | 可自建 | 否 | 中 | | **MuPDF (C)** | ~8MB/平台 | 全平台 | 是 | 可自建 | 部分 | 高 | ### 评估维度 | 维度 | QuickLook (iOS) | PDFKit (iOS) | pdfium-render (Rust) | |------|----------------|-------------|---------------------| | 集成成本 | 极低(QLPreviewController) | 低(原生 API) | 高(交叉编译、binding) | | 包体影响 | 0 | 0 | ~15MB × 平台数 | | 页面渲染 | ✅ | ✅ | ✅(需要 bitmap pipeline) | | 文本提取 | ❌ | ✅ | ✅ | | 文本选择 | ❌(只能看不选) | ✅ | 需自建 UI | | 搜索 | ❌ | ✅ | 可自建 | | 阅读位置 | App 侧维护 | App 侧 + delegate | Rust 侧统一 | | 统一数据模型 | ❌ | ❌ | ✅ | | 跨平台复用 | ❌ | ❌ | ✅ | --- ## 各平台策略 ### iOS / macOS **当前(M3-M4):PDFKit** ``` MaterialReaderView → PreviewMode.platformPreview → QuickLook sheet (QLPreviewController) → App 侧监听页码变化 → 生成 ReadingPosition::Pdf { pageNumber, pageProgress, overallProgress } → pushReadingEvent ``` 优势: - 零依赖,系统自带 - 渲染质量高 - 支持系统级文本选择、搜索 - 用户熟悉的交互 局限: - QuickLook 不暴露页码变化回调(需要 PDFKit 的 PDFView 才能精确跟踪) - 无法提取文本传给 Rust 做统一搜索 - App 侧需用 PDFView delegate 替代 QLPreviewController 以获得页码回调 **后续增强**:如果需要文本提取/搜索,把 QuickLook sheet 替换为 PDFView + PDFDocument,通过 `PDFDocument.string` 提取全文传给 Rust `search_text`。 ### Android **当前策略:系统预览 / 外部 App** Android 有 `PdfRenderer`(API 21+),可渲染页面为 Bitmap。但第一版不做内置 PDF 阅读器。 ``` MaterialReaderView (Android) → PreviewMode.platformPreview → Intent.ACTION_VIEW + content:// URI → 系统 PDF 阅读器 / Chrome → 回到 App 后手动记录阅读时长 ``` 后续可选:`PdfRenderer` + `RecyclerView` 自建阅读器,用 Rust `ReadingPosition::Pdf` 统一位置模型。 ### 鸿蒙 / Windows / Web 均优先走系统预览或浏览器内置 PDF 阅读器。Rust 只统一 `ReadingPosition::Pdf` 模型。 --- ## 阅读位置模型 Rust 已定义统一的 `ReadingPosition::Pdf`: ```rust ReadingPosition::Pdf { page_number: u32, // 1-based 页码 page_progress: f32, // 0.0 ~ 1.0,该页内滚动比例 overall_progress: f32, // 0.0 ~ 1.0,全书进度 } ``` App 侧职责: - iOS:PDFView.pageChange delegate → 更新位置 - Android:监听页面变化 → 更新位置 - 所有平台:用同一套 `ReadingPosition::Pdf` 模型,不重复造轮子 --- ## Rust 侧职责边界 ### 当前(M3-M4) - `MaterialType::Pdf` — 文件类型识别 ✅ - `ReadingPosition::Pdf` — 统一位置模型 ✅ - `PreviewMode::PlatformPreview` — 预览模式映射 ✅ - `pdf.rs` — 模块占位(注释说明走平台预览)✅ ### 不做的 - 不集成 PDFium - 不做 PDF 渲染(bitmap 生成) - 不做 PDF 文本提取 - 不做 PDF 标注 - 不做 OCR ### 后续评估(M5+) 如果以下条件满足 **3 项以上**,重新评估 PDFium 集成: 1. Android 需要内置 PDF 阅读器 2. 搜索需要在 PDF 中定位 3. 需要跨平台统一的文本提取 4. 用户量大到平台差异成为维护负担 5. PDFium 交叉编译经验积累充分 --- ## 搜索策略 | 阶段 | 方案 | 能力 | |------|------|------| | M3-M4 | 不搜索 PDF | 用户在平台预览器中手动使用系统搜索 | | M5+ | 平台提取 + Rust 搜索 | PDFKit.string / PdfRenderer → 传给 Rust `search_text` | | 远期 | PDFium 提取 + Tantivy | 全文索引,支持 PDF 内定位 | --- ## 扫描 PDF / OCR **明确不进入知习范围**。理由: - OCR 是独立技术领域,与"阅读内核"定位不符 - 高精度 OCR 需要专门模型(Tesseract / Apple Vision / ML Kit) - 绝大多数学习资料是原生电子文档,非扫描件 - 扫描件场景可由用户自行 OCR 后导入 --- ## 后续路线图 ``` M3 ✅ — QuickLook sheet + ReadingPosition::Pdf M4 ● — pdf-strategy.md(本文档) M5 ○ — iOS 迁移至 PDFView(获得文本提取和页码回调) — Rust 接收 PDF 全文做 search_text M6+ ○ — 评估 PDFium(Android 自建阅读器需求驱动) — 如集成 PDFium:文本提取 + bitmap 渲染 + 搜索定位 ``` --- ## 验收确认 - [x] iOS 第一版继续使用 PDFKit / QuickLook - [x] Rust 暂不接 PDFium - [x] PDF 文本选择由平台能力承担 - [x] PDF 搜索后置 - [x] 扫描 PDF / OCR 暂缓 - [x] 文档存在,方案决策明确,有后续路线图