前言
在实际场景需要给 PDF 文件进行压缩,由于文件内容比较敏感,不信任网上那些广告漫天的站点,所以自己来实现一个。
第一步,在 GitHub 上搜索是否有类似项目,找到一个使用 HTML 实现的 OpenToolKit/CompressPDF: Fast in-browser PDF compressor (github.com),但是其站点无法启动,没再进行维护更新了;
第二步,克隆源码看看别人是如何进行实现的。
第三方库
1、pdfkit-standalone 用于新建 PDF 文件,其中的 PDFDocument 没找到对应类,在项目中直接引入了;
2、blob-stream 配合 FileSaver 对文件进行处理的脚本,没找到对应的包,也在项目中直接引入了;
3、pdfjs 用于读取 PDF 文件内容,对应的包为 pdfjs-dist - npm (npmjs.com);
4、FileSaver 将压缩的文件进行保存,对应的 npm 包地址为 file-saver - npm (npmjs.com);
5、sortable 用于拖拽排序,这个不管它(属于 noise ,完全不用管,只管 PDF 相关内容)。
核心原理
1、input 将文件上传之后,通过 FileReader 读取并传达给 PDFjs;
2、通过 canvas 对加载的 PDF 当前页进行压缩;
3、新建一个空白 PDF,添加上一步处理的数据,最后进行保存。
function loadPdf(url: string, range: number, name: string) {
const loadingTask = pdf.getDocument({
url,
disableRange: true
})
imgData.length = 0
loadingTask.promise.then((pdfDoc) => {
const totalPages = pdfDoc.numPages
for (let i = 1; i <= totalPages; i++) {
pdfDoc.getPage(i).then((page) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
const viewport = page.getViewport({ scale: 1 });
canvas.width = viewport.width
canvas.height = viewport.height
const renderContext = {
canvasContext: ctx,
viewport,
}
const renderTask = page.render(renderContext);
renderTask.promise.then(function () {
imgData.push({src: canvas.toDataURL("image/jpeg", +range), width: canvas.width, height: canvas.height})
if(imgData.length === totalPages){
img2Pdf(name, compressing)
}
})
})
}
})
}
function img2Pdf(name: string) {
const options = {
autoFirstPage: false,
compress: false
}
const doc = new PDFDocument(options)
const stream = doc.pipe(blobStream())
for (let i = 0; i < imgData.length; i++) {
const {src, width, height} = imgData[i]
doc.addPage({
size: [width, height],
})
doc.image(src, 0, 0)
}
doc.end()
stream.on("finish", function () {
var output_blob = stream.toBlob("application/pdf")
saveAs(output_blob, name)
});
}
具体代码详情请看 Efrice/compress-pdf: compress pdf without sever (github.com)。
结合图片加水印
之前做了一个图片加水印的功能,也是通过 canvas 来实现的,那么 PDF 肯定也可以加水印。同理,图片也可以压缩。最后,升级了 watermark 的功能,在线使用,感兴趣源码请移步代码仓 Efrice/watermark: Add watermark to image/pdf, or compression. (github.com)。