更新记录
1.0.0(2026-10-03) 下载此版本
- 生成固定版式 PDF,支持文字、图片、矩形和线段。
- 多张图片按顺序生成统一尺寸 PDF,并处理图片方向。
- 查询 PDF 页数、页面尺寸、元数据和加密状态。
- 渲染页面图片、提取文字和关键词检索。
- 合并、拆分、抽取、复制、重排、旋转和裁剪页面。
- 批量文字水印,支持平铺/单枚、字体、颜色、透明度、角度、间距和前后图层。
- 阅读密码、所有者密码、打印/复制权限和授权解密。
- 元数据、书签、表单、批注和栅格化处理。
- HTTPS 下载、进度回调、下载取消和插件缓存文件清理。
平台兼容性
uni-app x(5.26)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| × | × | 5.0 | 13 | × | × |
其他
| 多语言 | 暗黑模式 | 宽屏模式 | 蒸汽模式 |
|---|---|---|---|
| × | × | × | √ |
lf-pdfkit
lf-pdfkit 是面向 uni-app x 的 UTS API 插件,用于在 Android 和 iOS 设备本地生成、读取、编辑和保护 PDF。插件不上传文档内容,也不依赖 WebView 或 Office/HTML 渲染引擎。
版本与支持范围
- 插件版本:
1.0.0 - HBuilderX:
5.26及以上 - uni-app x:
5.26及以上 - Android:最低 API 21
- iOS:最低 13.0
- 支持平台:Android、iOS
- 不支持平台:Web、鸿蒙、小程序和普通 uni-app Vue 工程
这是 API 插件,所有能力均通过 script 中的 UTS 方法调用。插件不提供可直接嵌入页面的 PDF 阅读组件;如需预览,可使用 renderPdf 得到图片后由宿主页面展示,或自行实现页面组件。
插件包含 Kotlin、Swift 原生实现以及 Android Maven 依赖。使用前必须制作包含插件依赖的自定义调试基座,或直接使用云打包。仅复制源码而继续使用不包含 PDFBox 依赖的旧基座,无法运行 Android 功能。
安装与导入
将 uni_modules/lf-pdfkit 放入 uni-app x 工程,在 UTS 页面中从插件根目录导入:
import { createPdf, getPdfInfo, renderPdf } from '@/uni_modules/lf-pdfkit'
不要直接导入 utssdk 内部文件。插件对外类型和函数均从根入口导出,直接导入根目录可以保证平台实现和类型提示一致。
能力总览
| 能力 | 说明 | 主要接口 |
|---|---|---|
| 业务文档生成 | 将文字、图片、矩形和线段写入多页 PDF | createPdf |
| 图片成册 | 将照片或扫描图按顺序排成统一尺寸的 PDF,并处理方向 | imagesToPdf |
| 文档查询 | 查询页数、尺寸、元数据、加密状态和文件大小 | getPdfInfo |
| 页面渲染 | 将 PDF 页面渲染为 PNG/JPEG 图片 | renderPdf |
| 文字提取与检索 | 提取页面文字,按关键词查找位置和上下文 | extractText、searchPdf |
| 页面编排 | 合并、拆分、抽取、复制、重排和旋转页面 | mergePdf、splitPdf、selectPages、rotatePdf |
| 页面裁剪 | 修改页面可视区域 | cropPdf |
| 批量水印 | 写入平铺或单枚文字水印,支持字体、角度、图层和透明度 | watermarkPdf |
| 文档保护 | 设置密码和打印/复制权限,使用所有者密码解密 | encryptPdf、decryptPdf |
| 文档结构 | 读写元数据和书签 | setMetadata、getOutlines、setOutlines |
| 表单处理 | 查询字段、填写字段并按需固化 | getFormFields、fillForms |
| 批注与栅格化 | 添加便签/高亮/矩形/图片盖章,或生成视觉副本 | annotatePdf、rasterizePdf |
| 文件管理 | 下载 HTTPS 文件、清理插件缓存文件 | downloadPdf、releaseFiles |
基本数据类型
PdfSource
所有读取和编辑接口都使用 PdfSource:
type PdfSource = { path: string, password?: string }
path 必须是本地文件路径,也可以是 Android content:// 输入地址或 file:// 地址。网络地址不能直接交给 PDF 接口,应先使用 downloadPdf 下载。password 只在本次调用期间使用,不写入文件元数据或日志。
PdfFile
写文件接口返回:
type PdfFile = { path: string, pageCount: number, fileSize: number }
fileSize 的单位是字节。未指定 outputPath 时,文件会写入插件私有缓存目录;需要长期保存时,请由宿主应用复制到自己的持久目录。
页码、坐标和尺寸
- 所有页码从
1开始。 - 页面尺寸和坐标使用 PDF point,
1 point = 1/72 英寸。 - A4 页面约为
595 × 842point。 createPdf的文字、图片、矩形和线段坐标以未旋转页面左上角为原点,x 向右、y 向下。cropPdf的裁剪矩形遵循同样的左上角坐标约定。rotatePdf的角度必须是 90 的整数倍。- 颜色统一使用
#RRGGBB,不依赖 CSS 颜色名解析。
所有写操作都会生成新文件,不覆盖输入文件,也不覆盖已存在的输出文件。显式传入的 outputPath 必须指向不存在的文件,且父目录必须已经存在。
生成 PDF
createPdf 适合生成业务单据、报价单、报告、清单和固定版式文档。插件负责 PDF 页面、文字和矢量元素的写入;分页规则、金额计算和业务排版由调用方准备。
支持的元素类型:
kind |
必填字段 | 说明 |
|---|---|---|
text |
x、y、text |
文字;可选 width、height、fontSize、fontPath、fontFamily、color |
image |
x、y、path |
图片;必须提供 width、height |
rect |
x、y、width、height |
lineWidth 为 0 时填充,大于 0 时描边 |
line |
x、y、width、height |
绘制线段,长度不能为 0 |
import { createPdf, getPdfInfo, PdfPage, PdfText } from '@/uni_modules/lf-pdfkit'
const elements: PdfText[] = [
{ kind: 'text', x: 36, y: 36, width: 523, height: 36,
text: '项目交付报告', fontSize: 24, color: '#173454',
fontPath: '/static/fonts/your-font.ttf' } as PdfText,
{ kind: 'rect', x: 36, y: 100, width: 523, height: 64,
color: '#E8F1F5', lineWidth: 0 } as PdfText,
{ kind: 'line', x: 36, y: 188, width: 523, height: 0,
color: '#173454', lineWidth: 1 } as PdfText
]
const file = await createPdf({
pages: [{ width: 595, height: 842, elements: elements } as PdfPage],
metadata: { title: '项目交付报告', author: '业务系统' }
})
const info = await getPdfInfo({ path: file.path })
console.log(info.pageCount, file.path)
文字不会自动创建下一页。文字超出 width 或 height 时,接口会返回参数或内容错误;调用方应提前分行、分页或扩大文本框。Android 使用自定义 TTF 时,字体必须包含文字所需字形,并且调用方必须拥有字体授权。
图片成册
import { imagesToPdf } from '@/uni_modules/lf-pdfkit'
const file = await imagesToPdf({
paths: photoPaths,
width: 595,
height: 842,
margin: 28,
fit: 'contain'
})
参数说明:
paths:图片路径数组,顺序就是 PDF 页序。width、height:输出页面尺寸,默认接近 A4。margin:图片区域边距,单位为 point。fit:contain保持完整图片并留白;cover填满区域并裁剪;fill拉伸填满。- Android 会读取图片 EXIF 方向并完成方向校正。
查询、渲染和文字处理
import { getPdfInfo, renderPdf, extractText, searchPdf } from '@/uni_modules/lf-pdfkit'
const source = { path: file.path }
const info = await getPdfInfo(source)
const images = await renderPdf({ source: source, pages: [1], scale: 1.5, format: 'png' })
const texts = await extractText({ source: source, pages: [1, 2] })
const hits = await searchPdf({ source: source, query: '验收', maxResults: 20 })
renderPdf 返回插件缓存中的图片路径。使用完毕后,可把这些图片路径传给 releaseFiles 清理。渲染图片不等于修改原 PDF。扫描 PDF 只有图像,没有文字层,因此 extractText 和 searchPdf 无法识别扫描图中的文字;插件不提供 OCR。
页面编排
import { mergePdf, splitPdf, selectPages, rotatePdf, cropPdf } from '@/uni_modules/lf-pdfkit'
const merged = await mergePdf({
sources: [{ path: firstPath }, { path: secondPath, password: ownerPassword }]
})
const split = await splitPdf({ source: { path: merged.path }, groups: [[1], [2, 3]] })
const reordered = await selectPages({ source: { path: merged.path }, pages: [3, 1, 1] })
const rotated = await rotatePdf({ source: { path: merged.path }, pages: [1], degrees: 90 })
const cropped = await cropPdf({ source: { path: merged.path }, pages: [1], rect: { x: 20, y: 20, width: 555, height: 802 } })
页面编排主要保留页面内容,不保证完整合并不同文件的文档级表单、名称树、附件、标签、JavaScript、全部书签和数字签名。包含复杂交互结构的生产合同,应在目标阅读器中验收输出结果。重写已签名 PDF 通常会使原数字签名失效。
批量水印
水印写入 PDF 页面内容层,不会把整页栅格化:
import { watermarkPdf } from '@/uni_modules/lf-pdfkit'
const marked = await watermarkPdf({
source: { path: file.path },
text: '内部资料', mode: 'tile', angle: 45,
fontPath: '/static/fonts/your-font.ttf', fontSize: 24,
color: '#64748B', opacity: 0.18,
spacingX: 72, spacingY: 56, layer: 'foreground'
})
| 参数 | 默认值 | 范围或说明 |
|---|---|---|
mode |
tile |
tile 多行平铺;single 单枚居中 |
angle |
45 |
-180 至 180 度,正值在视觉上逆时针旋转 |
fontFamily |
sans-serif |
sans-serif、serif 或 monospace |
fontPath |
不指定 | 自定义 TTF,优先级高于 fontFamily |
fontSize |
24 |
1 至 256 point |
color |
#808080 |
必须是 #RRGGBB |
opacity |
0.18 |
0 至 1,0 为透明,1 为不透明 |
spacingX / spacingY |
72 / 56 |
0 至 1440 point |
layer |
foreground |
foreground 在原内容上方;background 在下方 |
水印不是安全涂黑,不会删除底层文字,也不能作为 DRM。背景水印可能被不透明底色或扫描图片遮挡,扫描 PDF 通常应使用前景层。单枚水平水印可以显式设置 mode: 'single', angle: 0。
加密、权限与解密
import { encryptPdf, decryptPdf } from '@/uni_modules/lf-pdfkit'
const locked = await encryptPdf({
source: { path: file.path },
userPassword: 'reader-secret', ownerPassword: 'owner-secret',
allowPrinting: true, allowCopying: false
})
const plain = await decryptPdf({
source: { path: locked.path, password: 'owner-secret' }
})
ownerPassword 必须非空,并且不能与 userPassword 相同。普通编辑接口不会悄悄解除原文档加密;处理受保护 PDF 前,应先使用所有者密码显式解密到新文件。权限位不是 DRM,其他软件可能不遵守,但插件会在需要复制、修改或解密时执行权限检查。
元数据、书签、表单和批注
import { setMetadata, getOutlines, setOutlines, getFormFields, fillForms, annotatePdf } from '@/uni_modules/lf-pdfkit'
const updated = await setMetadata({
source: { path: file.path },
metadata: { title: '归档报告', author: '档案系统', keywords: '归档,报告' }
})
const outlines = await getOutlines({ path: updated.path })
const withOutlines = await setOutlines({
source: { path: updated.path },
items: [{ title: '首页', page: 1, depth: 0 }]
})
const fields = await getFormFields({ path: withOutlines.path })
const filled = await fillForms({
source: { path: withOutlines.path },
values: [{ name: 'customerName', value: '示例客户' }], flatten: false
})
const annotated = await annotatePdf({
source: { path: filled.path },
annotations: [{ page: 1, kind: 'note', rect: { x: 40, y: 40, width: 80, height: 40 }, text: '需要复核' }]
})
书签 depth 从 0 开始,层级不能跳级。表单字段能否填写取决于原文档字段类型、只读状态和平台能力。图片盖章只是视觉批注,不属于数字签名。
临时文件与下载
import { downloadPdf, releaseFiles } from '@/uni_modules/lf-pdfkit'
const task = downloadPdf({
url: 'https://example.com/report.pdf',
headers: { Authorization: 'Bearer token' }, timeout: 60000
}, (event) => { console.log(event.progress) })
const downloaded = await task.promise
await releaseFiles([downloaded.path])
downloadPdf 只接受 HTTPS 地址,返回 promise 和 cancel()。下载成功只代表 HTTP 状态为 2xx,服务器仍可能返回登录页或其他非 PDF 内容;交给 PDF 接口打开时才会验证格式。普通 PDF 操作没有硬取消保证。
插件输出默认位于应用缓存目录,系统可能在空间不足时清理缓存。宿主应用需要长期保存文件时,应在 Promise 成功后复制到自己的持久目录,并在确认不再使用预览图片后调用 releaseFiles。不要释放仍被页面使用的文件。
平台差异与限制
Android
- 使用 PDFBox-Android
2.0.27.0和 AndroidX ExifInterface1.3.7。 - 单个输入或输出 PDF 不超过 128 MiB;一次请求的输入输出总量不超过 512 MiB。
- 单个输入和一次批量输出最多 100 页。
- 单次位图处理上限约为 1200 万像素,并会检查页面尺寸、PDF 对象数量和绘制复杂度。
- 中文及其他非拉丁文字必须使用包含对应字形的 TTF
fontPath。 - 页面抽取、拆分和合并会拒绝无法安全重建的 AcroForm、Widget、Link、Popup/Reply 等交互结构。
fillForms支持文本、复选框、单选按钮和部分选择字段;不支持 XFA,多选字段不能按数组契约填写。- 输出文件使用排他创建,不覆盖已有文件;复制期间不要提前读取尚未成功返回的路径。
iOS
- 使用系统 PDFKit、UIKit、CoreGraphics、ImageIO 和 CoreText。
- 输入和输出 PDF 最大 256 MiB,单个输入和一次输出最多 100 页,后台任务队列最多 8 个。
- 加密密码最多 32 个可打印 ASCII 字符,具体加密算法由系统 PDFKit 决定。
- 表单填写主要支持文本和复选框;
flatten: true需要 iOS 16 或更高版本。 - 水印和图片盖印遇到包含大纲、Widget、Link、标签、图层或签名的复杂 PDF 时可能拒绝处理,以避免悄悄损坏文档结构。
- 系统字体度量与 Android 不完全一致。跨端要求版式一致时,请两端使用同一授权 TTF 并分别验收。
不提供的能力
- OCR 和扫描图片文字识别。
- HTML、Word、Excel 或网页转 PDF。
- PKI/PAdES 证书数字签名、签名验证和 PDF/A、PDF/UA 合规校验。
- 安全涂黑、不可恢复的敏感信息删除和 DRM。
- 原生连续滚动 PDF 阅读器、原生文字选择、双指缩放和批注编辑工具栏。
- 富文本排版、Emoji 复杂塑形、竖排文字和 Office 级分页引擎。
错误处理
所有 Promise 失败都会返回 LfPdfError,包含 errSubject: 'lf-pdfkit'、errCode 和 errMsg。业务代码应优先按错误码分流,不要只匹配错误文本。
| 错误码 | 含义 | 常见原因 |
|---|---|---|
| 9020001 | 参数错误 | 页码、坐标、颜色、密码或输出路径无效 |
| 9020002 | 文件错误 | 文件不存在、无权读取、输出目录不存在 |
| 9020003 | 密码错误 | 未提供密码或密码不正确 |
| 9020004 | PDF 处理失败 | PDF 损坏、解析失败、渲染失败或写入失败 |
| 9020005 | 不支持 | 平台、文档结构或系统版本不支持 |
| 9020006 | 资源超限 | 页数、文件大小、像素、对象数量或队列超限 |
| 9020007 | 网络失败 | HTTPS 请求失败或 HTTP 状态不是 2xx |
| 9020008 | 主动取消 | downloadPdf().cancel() 被调用 |
| 9020009 | 权限不足 | PDF 不允许复制、修改、填写或装配 |
发布包说明
DCloud UTS 插件的发布包必须保留插件根目录和 package.json。本 API 插件的核心文件是 utssdk/interface.uts、utssdk/index.uts、utssdk/app-android 和 utssdk/app-ios。发布包不应包含 unpackage、node_modules、.git、示例工程、构建缓存或与插件无关的文档。
本插件通过 utssdk/app-android/config.json 声明 PDFBox Maven 依赖,不在压缩包内重复携带 jar/aar。发布前应使用 HBuilderX 自定义基座和云打包分别验证 Android、iOS 的生成、渲染、水印、密码、页面编排和临时文件清理流程。
许可证
插件自有代码采用 MIT 许可证,详见 LICENSE。Android 依赖 PDFBox-Android 和 AndroidX ExifInterface,iOS 使用系统框架。分发应用或二次打包时,请同时遵守这些依赖的许可证和 NOTICE 要求。

收藏人数:
下载插件并导入HBuilder
下载示例项目ZIP
赞赏(0)
下载 719
赞赏 7
下载 12651324
赞赏 1953
赞赏
京公网安备:11010802035340号