用 Vue 3 与 pdf-lib 在浏览器中整理 PDF 页面
译注:本文编译自 dev.to 上的一篇技术教程,原文标题为《How to Organize PDF Pages in the Browser with Vue 3 and pdf-lib》,作者演示了如何完全在浏览器端实现 PDF 页面整理功能。原文链接见文末。
重新组织 PDF 页面,通常意味着要调整页面顺序、插入空白页,以及管理页面子集。本文介绍如何用 Vue 3 和 pdf-lib 构建一个基于浏览器的 PDF 整理工具。
要解决的问题:灵活的页面管理
PDF 整理功能大致包含以下几个环节:
- 加载源 PDF 文件
- 将页面以可拖拽的缩略图形式展示
- 支持重排、删除与插入
- 按修改后的页面顺序生成新的 PDF
技术栈
- Vue 3,使用 Composition API
- pdf-lib,负责 PDF 操作
- SortableJS,负责拖拽排序
- Vite,负责打包
核心实现
vue
<script setup lang="ts">
import { ref } from 'vue'
import { PDFDocument } from 'pdf-lib'
import Sortable from 'sortablejs'
const file = ref<File | null>(null)
const pages = ref<number[]>([])
const organizing = ref(false)
const result = ref<Uint8Array | null>(null)
async function handleFile(e: Event) {
const input = e.target as HTMLInputElement
if (!input.files?.[0]) return
file.value = input.files[0]
const arrayBuffer = await file.value.arrayBuffer()
const pdfDoc = await PDFDocument.load(arrayBuffer)
pages.value = Array.from({ length: pdfDoc.getPageCount() }, (_, i) => i)
}
function initDragDrop() {
const el = document.getElementById('page-list')
if (!el) return
Sortable.create(el, {
animation: 150,
onEnd: (evt) => {
const oldIndex = evt.oldIndex!
const newIndex = evt.newIndex!
const [moved] = pages.value.splice(oldIndex, 1)
pages.value.splice(newIndex, 0, moved)
}
})
}
async function organizePdf() {
if (!file.value || pages.value.length === 0) return
organizing.value = true
const arrayBuffer = await file.value.arrayBuffer()
const pdfDoc = await PDFDocument.load(arrayBuffer)
const newPdf = await PDFDocument.create()
for (const pageIndex of pages.value) {
const [copiedPage] = await newPdf.copyPages(pdfDoc, [pageIndex])
newPdf.addPage(copiedPage)
}
result.value = await newPdf.save()
organizing.value = false
}
</script>关键实现细节
1. 页面索引追踪
维护一个页面索引数组来表示当前顺序:
typescript
const pages = ref<number[]>([])
// 初始状态:[0, 1, 2, 3, ...]2. 拖拽重排
用 SortableJS 处理视觉上的重排,并同步更新索引数组:
typescript
Sortable.create(el, {
onEnd: (evt) => {
const [moved] = pages.value.splice(evt.oldIndex, 1)
pages.value.splice(evt.newIndex, 0, moved)
}
})3. 生成整理后的 PDF
按新的顺序复制页面:
typescript
for (const pageIndex of pages.value) {
const [copiedPage] = await newPdf.copyPages(pdfDoc, [pageIndex])
newPdf.addPage(copiedPage)
}4. 插入空白页
在指定位置插入空白页:
typescript
pages.value.splice(insertIndex, 0, -1) // -1 表示空白页导出时再作相应处理:
typescript
if (pageIndex === -1) {
newPdf.addPage() // 添加空白页
} else {
const [copiedPage] = await newPdf.copyPages(pdfDoc, [pageIndex])
newPdf.addPage(copiedPage)
}局限性
没有可视化页面预览
为所有页面生成缩略图预览可能非常占用内存。
解决方案:懒加载生成缩略图,或只渲染前 N 页。
一次只能处理一个文件
每次会话只能整理一个 PDF。
解决方案:增加多文件上传支持。
导出后无法撤销
文件一旦下载,改动就无法回退。
解决方案:为近期操作增加撤销栈。
小结
构建一个浏览器端 PDF 整理工具,主要涉及:
- 用 pdf-lib 加载并解析 PDF
- 实现拖拽重排
- 按目标顺序复制页面
- 支持插入空白页
作者提供了一个在线示例,可在 en.sotool.top/organize-pdf 试用。
原文链接
https://dev.to/sunshey/how-to-organize-pdf-pages-in-the-browser-with-vue-3-and-pdf-lib-lmo