PDF Viewer KMP
支持 Context7 ,AI直接能读懂最准确的用法。
| 库 | English | 简体中文 |
|---|---|---|
pdf-core | Using PDF Core | PDF Core 使用指南 |
pdf-viewer | Using PDF Viewer | PDF Viewer 使用指南 |
| API 参考 | Dokka | — |
前言:跨平台 PDF 的“老大难”与内核选型
在做跨平台(Kotlin Multiplatform / Compose Multiplatform)开发时,PDF 的渲染与交互一直是一块极其让人头疼的“硬骨头”。
虽然各平台看似都有现成的解决方案,但实际落地时会发现处处是坑:
- 平台 API 割裂且能力参差:Android 系统的原生
PdfRenderer功能极其简陋,缺少高阶文本交互,复杂文档还容易出现 OOM 或渲染异常;iOS 虽有成熟的PDFKit,但行为与 Android 完全不同;桌面端(macOS / Windows / Linux)缺乏统一且开箱即用的轻量级方案;Web 端更是往往不得不引入庞大臃肿的 JS 库。 - 渲染与坐标的一致性极差:不同平台底层引擎不同,字体渲染排版、页面旋转、视口缩放计算、文本字符的坐标矩形(Quad/Bounds)都会存在细微偏差,导致跨端的搜索高亮、跳转交互几乎不可能有一致的体验。
为了彻底抹平不同平台的差异,在为 KMP 生态打造 PDF Viewer KMP 时,我最终选定了 Google Chrome 同款的开源 PDF 引擎 —— PDFium 作为全平台的统一底层内核。
为什么选择 Chrome 的 PDFium?它的核心优势是什么?
很多开发者提到 Chrome 内核,第一反应是庞大的 Chromium 浏览器或者 Blink 排版引擎,而实际上,PDFium 是 Google 从 Chromium 中抽离并持续维护的高性能、纯 C++ 开源 PDF 渲染库(其前身是 Google 与福昕 Foxit 合作开源的成熟引擎)。
选型 PDFium 的主要优势有以下几点:
-
工业级的稳定性与规范兼容度(Chrome 背书) 作为 Chrome 浏览器多年以来的默认内置 PDF 引擎,PDFium 每天承受全球数十亿用户以及海量结构怪异、复杂排版、老旧编码的极端 PDF 文档考验。相比市面上许多第三方小众解析库,它对 PDF 规范的兼容性、防崩溃能力以及容错度均属于工业级水准。
-
跨平台“像素级”渲染一致性 PDF 渲染最忌讳“千端千面”。选用 PDFium 后,Android、iOS、JVM 桌面端(macOS/Linux/Windows)以及浏览器端(JS/Wasm)全部共享同一套 C++ 核心逻辑。不仅视觉渲染效果、字体抗锯齿表现完全一致,文本提取、关键词搜索、字符坐标高亮及超链接解析的底层逻辑也是 100% 统一的,彻底终结了平台间的兼容性地狱。
-
极致的运行时性能与更低的内存开销 PDFium 采用原生 C++ 编写,在内存复用、光栅化(Rasterization)速度和指令集优化上极为激进。相较于用 WebView 套壳或纯 JavaScript 解析(如重量级 PDF.js),原生 PDFium 启动速度快得多、CPU 开销小、内存占用可控,非常适合高帧率的缩放、平移和懒加载列表渲染。
-
真正的全平台编译支持(覆盖 WebAssembly) PDFium 的可移植性非常强:
- 在 Android 上通过 NDK/JNI 编译;
- 在 Desktop(JVM)上无缝打包动态库原生运行;
- 在 iOS 上可静态编译打包(为 KMP cinterop 提供自包含静态归档,免去分发 dylib 和签名的麻烦);
- 在 Web 端更可通过 Emscripten 编译为高效的 WebAssembly (Wasm),让浏览器端同样拥有接近原生的渲染性能与完全一致的能力集。
正因如此,基于 PDFium 构建底层核心(pdf-core)并在此之上封装声明式的 Compose Multiplatform 交互组件(pdf-viewer),成为了一套高内聚、高一致性、全平台可用的理想架构。接下来,本文将分享如何在 Kotlin Multiplatform 项目中集成和使用这套方案。
Demo
下载已签名的 Android 示例应用:pdf-viewer-kmp-demo.apk。
以下截图来自在 OPPO Android 真机上运行的 Demo:
| Demo 首页 | PDF 阅读器 | 搜索与高亮 |
|---|---|---|
项目包含两个公开库:
| 库 | 用途 |
|---|---|
pdf-core | 打开 PDF 文档,提供元数据、页面渲染、文本、搜索和链接等不依赖 Compose UI 的 API。 |
pdf-viewer | 提供 Compose Multiplatform PdfView 组件,并传递依赖 pdf-core。 |
支持平台
| 平台 | 支持目标 |
|---|---|
| Android | arm32、arm64、x86、x64 |
| iOS | 真机 arm64、模拟器 arm64 |
| JVM 桌面端 | macOS arm64/x64、Linux x64、Windows x64 |
| 浏览器 | JavaScript、Wasm |
目前不支持 iOS x64 和 Catalyst。内置 PDFium 固定为 chromium/7961
版本,不包含 V8 和 XFA。
添加依赖
添加 Maven Central,并在 commonMain 中选择需要的库:
repositories {
mavenCentral()
}
kotlin {
sourceSets {
commonMain.dependencies {
// 只使用 PDF 底层 API:
implementation("io.github.limuyang2:pdf-core:0.3.0")
// 或使用 Compose Viewer,pdf-core 会被传递引入:
implementation("io.github.limuyang2:pdf-viewer:0.3.0")
}
}
}
快速开始:PDF Core
应用只需要读取或渲染 PDF、不需要内置 Compose UI 时,可直接使用
pdf-core:
import io.github.limuyang2.pdf.core.PdfPixelSize
import io.github.limuyang2.pdf.core.PdfRenderRequest
import io.github.limuyang2.pdf.core.PdfSource
import io.github.limuyang2.pdf.core.PdfViewer
suspend fun renderFirstPage(pdfBytes: ByteArray): ByteArray {
val document = PdfViewer.open(PdfSource.Bytes(pdfBytes))
try {
require(document.pageCount > 0)
val bitmap =
document[0].render(
PdfRenderRequest(
outputSize = PdfPixelSize(1200, 1600),
),
)
try {
return bitmap.copyPixels()
} finally {
bitmap.close()
}
} finally {
document.closeAndAwait()
}
}
密码、元数据、文本、搜索、链接、位图格式、资源生命周期、异常和各平台配置, 请参阅 PDF Core 使用指南。
快速开始:PDF Viewer
pdf-viewer 使用纵向滚动的 Compose 页面列表显示整个文档:
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import io.github.limuyang2.pdf.core.PdfDocument
import io.github.limuyang2.pdf.viewer.PdfView
import io.github.limuyang2.pdf.viewer.rememberPdfViewState
@Composable
fun DocumentPreview(document: PdfDocument) {
val state = rememberPdfViewState()
PdfView(
document = document,
state = state,
modifier = Modifier.fillMaxSize(),
maxZoom = 4f,
gestureZoomEnabled = true,
)
}
调用方拥有 PdfDocument,必须在页面销毁时关闭它。PdfView 只负责关闭内部
临时渲染的位图。
在 Compose 中打开文档、状态控制、缩放配置、渲染尺寸限制、自定义加载和错误 UI、链接处理等内容,请参阅 PDF Viewer 使用指南。
各平台配置概览
- Android: 支持 API 24 及更高版本,PDFium JNI runtime 会自动加载。
- JVM: 原生 PDFium 库已内置,会自动解压到系统临时目录。部分 JVM
运行时可能需要
--enable-native-access=ALL-UNNAMED。 - 浏览器: 需要在配置的 PDFium 静态资源目录中部署
manifest.properties、pdfium-adapter.js、pdfium.js和pdfium.wasm。 - iOS: 最低部署版本为 iOS 14.0。PDFium 已通过发布的 KLIB 静态链接, 应用无需额外嵌入动态库。
详细配置参见 PDF Core 使用指南。
当前能力
所有当前后端均支持:
- 字节数组输入源和密码保护文档;
- 文档信息、权限、元数据和页面标签;
- 页面尺寸、固有旋转方向和边界框;
- 完整页面 BGRA8888 渲染;
- 基础文本提取;
- 支持大小写、整词和连续匹配选项的文本搜索;
- 内部跳转目标、URI 动作和链接标注区域。
调用可选 API 前,可以通过 PdfViewer.capabilities 检查当前平台能力。
当前尚未实现:书签、内嵌缩略图、公开后端的文本布局几何信息、随机访问输入源、
局部裁剪渲染、表单、编辑、渐进式加载或渲染、JavaScript 和 XFA。不支持的操作
会抛出 PdfUnsupportedFeatureException。