PDF 预览与签名批注写回 支持安卓 iOS 鸿蒙 UTS插件
支持 `Android`、`iOS`、`HarmonyOS` 的 PDF 预览能力封装,可用于打开本地 PDF 文件、翻页、缩放、签名记录、批注记录以及将记录写回到新 PDF,支持手势缩放预览,支持预览加密PDF。
PDF 预览与签名批注写回 支持安卓 iOS 鸿蒙 UTS插件
介绍
- 支持
Android、iOS、HarmonyOS的 PDF 预览能力封装,可用于打开本地 PDF 文件、翻页、缩放、签名记录、批注记录以及将记录写回到新 PDF,支持手势缩放预览,支持预览加密PDF。 - 适用于
uni-app与uni-app xApp 平台,不支持 Web 和各类小程序平台。 - 核心流程为:通过
<cz-pdf-viewer>组件挂载原生 PDF 视图,调用open加载文件,之后调用prevPage、nextPage、goToPage、zoomTo等方法进行交互;签名/批注通过addSignatureRecord、addAnnotationRecord等记录 API 管理,最后通过applyRecordsToPdfAsync将记录合入新 PDF。 - 即使当前平台不支持原生 PDF 预览(如模拟器),签名与批注记录模型仍可独立使用。
平台支持
| 平台 | 支持情况 |
|---|---|
| Android | 支持 |
| iOS | 支持 |
| HarmonyOS | 支持 |
| Web | 不支持 |
| 小程序 | 不支持 |
组件属性
| 属性名称 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| src | string |
"" |
PDF 文件路径 |
| sourceType | "filePath" | "contentUri" | "asset" |
"filePath" |
文件来源类型;iOS 当前以 filePath/asset 为主;Harmony 以沙箱 filePath 为主 |
| password | string |
无 | PDF 加密密码 |
| defaultPage | number |
1 |
打开后默认展示的页码 |
| enableSwipe | boolean |
true |
是否允许滑动手势翻页(仅 Android) |
| swipeHorizontal | boolean |
false |
是否横向翻页(仅 Android) |
| enableDoubleTap | boolean |
true |
是否启用双击缩放(仅 Android) |
| pageSnap | boolean |
false |
是否吸附翻页(仅 Android) |
| pageFling | boolean |
false |
是否 fling 翻页(仅 Android) |
| nightMode | boolean |
false |
夜间模式(仅 Android) |
| autoSpacing | boolean |
false |
自动页间距(Android 支持;iOS 近似映射为 page break 显示;Harmony 当前未单独接入) |
| pageSpacing | number |
0 |
页间距,具体效果以平台原生实现为准 |
| fitPolicy | "width" | "height" | "both" |
"width" |
页面适配策略;iOS 以 sizeToFit 基准缩放近似映射;Harmony 为原生 pageFit 映射,语义非完全一致 |
| minZoom | number |
1 |
最小缩放比例 |
| midZoom | number |
1.75 |
中间缩放比例 |
| maxZoom | number |
3 |
最大缩放比例 |
| enableAnnotationRendering | boolean |
true |
是否渲染 PDF 内嵌批注(仅 Android 支持开关) |
| autoOpen | boolean |
false |
组件挂载后是否自动根据 src 调用 open |
组件事件
| 事件名称 | 说明 |
|---|---|
load |
PDF 加载完成,可通过 event.detail.state 获取当前阅读状态 |
pageChange |
页码变化 |
zoomChange |
缩放比例变化 |
error |
发生错误,可通过 event.detail.state.lastErrorCode 与 lastErrorMessage 获取错误信息 |
viewerTap |
点击 PDF 阅读区域(仅 Android) |
recordsChange |
签名或批注记录发生变化(Android、HarmonyOS 支持;iOS 当前通过组件内部同步) |
组件方法(defineExpose)
| 方法名称 | 参数 | 返回 | 说明 |
|---|---|---|---|
| open | CzPdfOpenOptions | null |
boolean |
打开 PDF 文件。为 null 时使用组件属性构建打开参数 |
| reload | 无 | boolean |
重新加载当前 PDF |
| close | 无 | void |
关闭当前 PDF,释放原生资源 |
| prevPage | boolean | null(animated) |
boolean |
上一页 |
| nextPage | boolean | null(animated) |
boolean |
下一页 |
| goToPage | number(page), boolean | null(animated) |
boolean |
跳转到指定页码 |
| zoomTo | number(scale) |
boolean |
设置缩放比例 |
| resetZoom | 无 | boolean |
重置缩放 |
| fitToWidth | number | null(page) |
boolean |
适配页面宽度,不传则适配当前页 |
| getViewState | 无 | CzPdfViewState |
获取当前阅读状态快照 |
| getRecords | 无 | CzPdfRecordBundle |
获取当前签名与批注记录 |
| setRecords | CzPdfRecordBundle |
void |
替换全部记录 |
| clearRecords | 无 | void |
清空全部记录 |
| addSignatureRecord | CzPdfSignatureRecord |
void |
添加一条签名记录 |
| removeSignatureRecord | string(id) |
boolean |
按 ID 移除一条签名记录 |
| addAnnotationRecord | CzPdfAnnotationRecord |
void |
添加一条批注记录 |
| removeAnnotationRecord | string(id) |
boolean |
按 ID 移除一条批注记录 |
独立函数
| 函数名称 | 参数 | 返回 | 说明 |
|---|---|---|---|
| applyRecordsToPdf | CzPdfApplyRecordsOptions |
CzPdfApplyRecordsResult |
以同步方式将签名/批注记录写入新 PDF |
| applyRecordsToPdfAsync | CzPdfApplyRecordsOptions, CzPdfApplyRecordsCallback |
void |
以异步方式将签名/批注记录写入新 PDF(推荐用于生产环境) |
| createCzPdfRecordStore | CzPdfRecordBundle | null |
ICzPdfRecordStore |
创建一个独立于预览组件的记录管理实例,适用于无原生 PDF 预览的场景 |
平台差异说明
不同平台底层 PDF 能力不同,部分功能在三端的支持范围并不完全一致,接入前建议先阅读本节。
| API | Android | iOS | HarmonyOS | 说明 |
|---|---|---|---|---|
| open | 支持 | 支持 | 支持 | 三端都支持打开本地 PDF 文件;iOS/Harmony 的 asset 方案建议业务层先将资源转为绝对路径 |
| reload | 支持 | 支持 | 支持 | 关闭后再调用 reload 将返回 false,不会自动恢复 |
| close | 支持 | 支持 | 支持 | 关闭后会释放原生资源 |
| prevPage / nextPage | 支持 | 支持 | 支持 | 三端都支持翻页 |
| goToPage | 支持 | 支持 | 支持 | 三端都支持指定页码跳转 |
| zoomTo | 支持 | 支持 | 支持 | iOS 按 sizeToFit 基准归一化映射;Harmony 使用原生 PDFKit zoom 映射 |
| resetZoom | 支持 | 支持 | 支持 | 三端都支持重置缩放至基准倍率 |
| fitToWidth | 支持 | 支持 | 支持 | iOS 缩放基于 PDFView bounds 计算;Harmony 依赖原生布局就绪状态 |
| 签名记录模型 | 支持 | 支持 | 支持 | 三端纯数据模型一致,签名图片路径建议使用绝对路径 |
| 批注记录模型 | 支持 | 支持 | 支持 | 三端纯数据模型一致,支持 highlight、underline、square、text 四种批注类型 |
| applyRecordsToPdf | 支持 | 支持 | 支持 | 写回 PDF 为独立能力,不依赖组件实例;写回时会先清除输入 PDF 已有批注再写入新记录 |
| 横向翻页 | 支持 | 支持 | 不支持 | Harmony 当前未接入横向分页布局 |
| 夜间模式 | 支持 | 不支持 | 不支持 | 仅 Android 支持 |
| pageSnap / pageFling | 支持 | 不支持 | 不支持 | 仅 Android 支持 |
| 双击缩放开关 | 支持 | 不支持 | 不支持 | 仅 Android 支持控制开关;iOS/Harmony 由原生组件决定行为 |
| enableAnnotationRendering | 支持 | 不支持 | 不支持 | 仅 Android 支持控制是否渲染 PDF 内嵌批注 |
| recordsChange 事件 | 支持 | 不支持 | 支持 | iOS 当前通过组件内部 getRecords 同步记录状态 |
iOS 特别说明
- iOS 使用
PDFKit作为底层渲染引擎,缩放范围基于sizeToFit基准归一化映射。 fitToWidth依赖 PDFView 布局就绪,如果调用时布局未完成可能返回失败。createCzPdfRecordStore创建的记录管理实例在所有平台上完全独立于原生 PDF 视图,可用于模拟器或无原生预览的纯数据处理场景。- 写回 PDF 时会在后台 IO 队列加载 PDF 文档、清除已有批注、写入新记录并保存,最终在主线程回调结果。同步版本
applyRecordsToPdf会阻塞当前线程,生产环境建议使用applyRecordsToPdfAsync。
HarmonyOS 特别说明
- HarmonyOS 原生 PDFView 在
close后会隐藏视图(display:none),避免遮挡后续内容。 open后会自动恢复视图可见性。- HarmonyOS 写回 PDF 时默认使用
originalInputPath(原始文件路径),而非当前查看的 PDF 路径,以规避重复写回叠加批注的问题。 swipeHorizontal横向翻页能力鸿蒙侧当前未支持。
Android 特别说明
- Android 使用
PDFBox引擎进行 PDF 写回,打开 PDF 时会在 IO 线程预处理预览文件。 - Android 支持
contentUri方式打开 PDF(如从系统文件选择器获取的 URI)。 viewerTap事件仅在 Android 平台触发。enableSwipe、enableDoubleTap、pageSnap、pageFling、nightMode等阅读器专属参数仅 Android 平台生效。
数据结构说明
CzPdfViewState
getViewState() 或 load/pageChange/zoomChange 事件的 detail.state 中包含:
| 字段 | 类型 | 说明 |
|---|---|---|
| isSupported | boolean |
当前平台是否支持原生 PDF 预览 |
| isReady | boolean |
原生视图是否就绪 |
| isLoaded | boolean |
PDF 是否已加载 |
| page | number |
当前页码 |
| pageCount | number |
总页数 |
| zoom | number |
当前缩放比例 |
| minZoom | number |
最小缩放比例 |
| midZoom | number |
中间缩放比例 |
| maxZoom | number |
最大缩放比例 |
| source | string | null |
当前打开的文件路径 |
| sourceType | string | null |
当前文件来源类型 |
| lastErrorCode | string | null |
最近一次错误码 |
| lastErrorMessage | string | null |
最近一次错误描述 |
CzPdfSignatureRecord
签名记录结构:
type CzPdfSignatureRecord = {
id: string // 唯一标识
page: number // 所在页码
rect: CzPdfRect // 签名区域
imagePath: string // 签名图片路径(建议使用绝对路径)
createdAt?: number // 创建时间戳(毫秒)
extra?: UTSJSONObject // 扩展数据
}
CzPdfAnnotationRecord
批注记录结构:
type CzPdfAnnotationRecord = {
id: string // 唯一标识
type: "highlight" | "underline" | "square" | "text" // 批注类型
page: number // 所在页码
rects: CzPdfRect[] // 批注区域数组
color?: string // 颜色,如 "#FFEB3B"
opacity?: number // 透明度 0-1
contents?: string // 批注文本内容
author?: string // 作者
iconName?: string // 图标名称
createdAt?: number // 创建时间戳(毫秒)
extra?: UTSJSONObject // 扩展数据
}
CzPdfRect
矩形区域结构:
type CzPdfRect = {
x: number // 左上角 x
y: number // 左上角 y
width: number // 宽度
height: number // 高度
unit?: "pageRatio" | "pdfPoint" // 坐标单位,默认使用页面比例(0-1)
}
CzPdfRecordBundle
记录集合:
type CzPdfRecordBundle = {
signatures: CzPdfSignatureRecord[]
annotations: CzPdfAnnotationRecord[]
}
CzPdfApplyRecordsResult
写回结果:
| 字段 | 类型 | 说明 |
|---|---|---|
| success | boolean |
是否写回成功 |
| outputPath | string | null |
输出文件路径 |
| writtenSignatureCount | number |
成功写入的签名数量 |
| writtenAnnotationCount | number |
成功写入的批注数量 |
| errorCode | string | null |
失败时的错误码 |
| errorMessage | string | null |
失败时的错误描述 |
快速开始
1. 引入组件
在插件市场中导入cz-pdf-viewer到uni_modules,直接在 uvue 模板中使用 <cz-pdf-viewer>。
2. 基础 PDF 预览
// 模板中
<cz-pdf-viewer
ref="viewerRef"
src="/path/to/document.pdf"
sourceType="filePath"
autoOpen
@load="onLoad"
@pageChange="onPageChange"
@error="onError"
/>
// 脚本中
const viewerRef = ref()
function onLoad(event : UniNativeViewEvent) : void {
const state = event.detail.state
console.log(`PDF 加载完成,共 ${state.pageCount} 页`)
}
function onPageChange(event : UniNativeViewEvent) : void {
const state = event.detail.state
console.log(`当前页:${state.page}/${state.pageCount}`)
}
3. 通过方法控制
const viewer = viewerRef.value
if (viewer != null) {
// 打开 PDF
viewer.open({ src: "/path/to/document.pdf" })
// 翻页
viewer.nextPage()
viewer.prevPage()
// 跳转到第 3 页
viewer.goToPage(3)
// 缩放
viewer.zoomTo(1.5)
viewer.resetZoom()
viewer.fitToWidth()
}
4. 添加签名与批注记录
const viewer = viewerRef.value
if (viewer == null) return
// 添加签名记录
viewer.addSignatureRecord({
id: "sig-001",
page: 1,
rect: { x: 0.1, y: 0.1, width: 0.3, height: 0.15 },
imagePath: "/path/to/signature.png"
} as CzPdfSignatureRecord)
// 添加高亮批注
viewer.addAnnotationRecord({
id: "ann-001",
type: "highlight",
page: 1,
rects: [{ x: 0.15, y: 0.3, width: 0.7, height: 0.05 }],
color: "#FFEB3B",
opacity: 0.5,
contents: "重点内容"
} as CzPdfAnnotationRecord)
// 添加矩形框批注
viewer.addAnnotationRecord({
id: "ann-002",
type: "square",
page: 1,
rects: [{ x: 0.2, y: 0.5, width: 0.6, height: 0.2 }],
color: "#FF5722",
opacity: 0.8
} as CzPdfAnnotationRecord)
5. 写回到新 PDF
import { applyRecordsToPdfAsync, CzPdfApplyRecordsResult } from "@/uni_modules/cz-pdf-viewer"
const viewer = viewerRef.value
const records = viewer.getRecords()
applyRecordsToPdfAsync({
inputPath: "/path/to/original.pdf",
outputPath: "/path/to/output.pdf",
records: records,
overwrite: true
}, (result : CzPdfApplyRecordsResult) => {
if (result.success) {
console.log(`写回成功:签名 ${result.writtenSignatureCount} 条,批注 ${result.writtenAnnotationCount} 条`)
console.log(`输出文件:${result.outputPath}`)
} else {
console.log(`写回失败:${result.errorMessage}`)
}
})
6. 脱离预览使用记录模型
import { createCzPdfRecordStore, applyRecordsToPdfAsync } from "@/uni_modules/cz-pdf-viewer"
// 创建独立的记录管理实例(无需原生 PDF 视图)
const store = createCzPdfRecordStore(null)
store.addAnnotationRecord({
id: "ann-x",
type: "highlight",
page: 1,
rects: [{ x: 0.1, y: 0.2, width: 0.8, height: 0.06 }],
color: "#FFEB3B"
} as CzPdfAnnotationRecord)
applyRecordsToPdfAsync({
inputPath: "/path/to/input.pdf",
outputPath: "/path/to/output.pdf",
records: store.getRecords(),
overwrite: true
}, (result) => {
console.log(`写入 ${result.writtenAnnotationCount} 条批注`)
})
常用方法说明
open
打开一个 PDF 文件。参数为 CzPdfOpenOptions,其中 src 为必填项。
重要参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| src | string |
无(必填) | PDF 文件路径 |
| sourceType | CzPdfSourceType |
"filePath" |
文件来源类型 |
| password | string |
无 | 加密 PDF 的密码 |
| defaultPage | number |
1 |
打开后默认展示的页码 |
| swipeHorizontal | boolean |
false |
横向翻页(Android、iOS 支持) |
| fitPolicy | "width" | "height" | "both" |
"width" |
页面适配策略 |
| minZoom / midZoom / maxZoom | number |
1 / 1.75 / 3 |
缩放范围 |
prevPage / nextPage / goToPage
翻页相关方法。prevPage 到文件开头停止,nextPage 到末尾停止,均返回 boolean 表示是否成功。goToPage 直接跳转到指定页。
zoomTo / resetZoom / fitToWidth
缩放相关方法。zoomTo 接受缩放比例(相对基准倍率),resetZoom 恢复至 1 倍,fitToWidth 将指定页缩放至与视图等宽。
getRecords / setRecords / clearRecords
记录管理方法。getRecords 返回当前全部记录的深拷贝,setRecords 用于整体替换,clearRecords 清空全部签名与批注。
调用 clearRecords 后立即进行写回不会将旧记录带入输出 PDF。
addSignatureRecord / removeSignatureRecord
签名记录的增删。addSignatureRecord 需要提供 id、page、rect、imagePath,签名图片建议使用绝对路径以避免路径解析问题。
addAnnotationRecord / removeAnnotationRecord
批注记录的增删。支持四种批注类型:
highlight:高亮,可指定多个矩形区域underline:下划线square:矩形框,取第一个矩形区域text:文字批注
applyRecordsToPdf / applyRecordsToPdfAsync
将签名与批注记录写入新的 PDF 文件。执行时会:
- 加载输入 PDF
- 清除输入 PDF 上已有的批注(避免叠加)
- 逐页写入签名图片与批注标记
- 保存到输出路径
推荐使用异步版本 applyRecordsToPdfAsync,避免阻塞 UI。
完整示例
import {
applyRecordsToPdfAsync,
CzPdfAnnotationRecord,
CzPdfApplyRecordsResult,
CzPdfSignatureRecord
} from "@/uni_modules/cz-pdf-viewer"
export default {
methods: {
async demoFlow() {
const viewer = this.$refs.viewerRef
// 打开 PDF
viewer.open({
src: "/var/mobile/Containers/Data/Application/.../doc.pdf",
sourceType: "filePath"
})
// 添加签名
viewer.addSignatureRecord({
id: `sig-${Date.now()}`,
page: 1,
rect: { x: 0.7, y: 0.8, width: 0.25, height: 0.12 },
imagePath: "/path/to/signature.png",
createdAt: Date.now()
} as CzPdfSignatureRecord)
// 添加高亮
viewer.addAnnotationRecord({
id: `ann-${Date.now()}`,
type: "highlight",
page: 1,
rects: [{ x: 0.1, y: 0.15, width: 0.8, height: 0.04 }],
color: "#FFEB3B",
opacity: 0.4,
createdAt: Date.now()
} as CzPdfAnnotationRecord)
// 写回到新 PDF
const records = viewer.getRecords()
applyRecordsToPdfAsync({
inputPath: "/var/mobile/Containers/Data/Application/.../doc.pdf",
outputPath: "/var/mobile/Containers/Data/Application/.../output.pdf",
records: records,
overwrite: true
}, (result : CzPdfApplyRecordsResult) => {
if (result.success) {
uni.showToast({ title: "PDF 已保存" })
} else {
uni.showToast({ title: `保存失败:${result.errorMessage}`, icon: "none" })
}
})
}
}
}
注意事项
- 仅支持手机真机调试,不支持模拟器上的原生 PDF 预览(记录模型仍可独立使用)。
- 调用
open前确保src指向的文件存在且可读。 - 调用
close后再执行reload将返回false,需要重新调用open打开文件。 - 签名图片路径建议使用绝对路径,避免写回时因相对路径解析失败导致签名丢失。
- 写回 PDF 时会先清除输入 PDF 上已有的批注标记,避免多次写回叠加。
applyRecordsToPdf是同步方法,大文件或大量批注场景建议使用applyRecordsToPdfAsync。- 不同平台、不同文件格式(加密、损坏等)的支持程度不同,业务接入前建议通过
load与error事件进行流程控制。
更多推荐




所有评论(0)