一,安装pdf-lib库:
1,pdf-lib的官网:
https://pdf-lib.js.org/
2,安装pdf-lib
liuhongdi@lhdpc:/data/vue/pdf/image2pdf$ npm install --save pdf-lib added 4 packages in 5s
3,查看已安装的pdf-lib库的版本:
liuhongdi@lhdpc:/data/vue/pdf/image2pdf$ npm list pdf-lib image2pdf@0.1.0 /data/vue/pdf/image2pdf └── pdf-lib@1.17.1
4,安装fontkit,添加中文字体时要用到
liuhongdi@lhdpc:/data/vue/pdf/image2pdf$ npm install --save @pdf-lib/fontkit added 1 package in 3s
5,查看安装的fontkit库的版本:
liuhongdi@lhdpc:/data/vue/pdf/image2pdf$ npm list @pdf-lib/fontkit image2pdf@0.1.0 /data/vue/pdf/image2pdf └── @pdf-lib/fontkit@1.1.1
说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest
对应的源码可以访问这里获取: https://github.com/liuhongdi/
或: https://gitee.com/liuhongdi
说明:作者:刘宏缔 邮箱: 371125307@qq.com
二,使用pdf-lib
1,配置:
创建字体文件夹:
public/fonts
把字体文件复制到目录下
说明:
请使用中文的ttf字体或otf字体,
使用ttc字体时会报错
2,js代码:
<template> <div> <div style="800px;margin: auto;"> <div > 选择pdf: <input type="file" ref="hiddenfile" accept="application/pdf" @change="handlePdf" class="hiddenInput" /> </div> <div style="800px;font-size:12px;margin-top:10px;"> <el-button type="info" @click="downPdf" plain style="400px;">保存并下载pdf</el-button> </div> <div style=" 800px;margin-top: 20px;"> <div v-for="(item,i) in imgFiles" :key="i" :id="`div_${i}`" style="265px;position: relative;float:left;overflow: hidden;"> <canvas :id="`pdf_canvas_${item.id}`" style="border: 1px solid #eeeeee;float:left;" ></canvas> </div> </div> </div> </div> </template> <script> import * as pdfjs from 'pdfjs-dist' import * as pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry' pdfjs.GlobalWorkerOptions.workerSrc = pdfjsWorker import { degrees, PDFDocument, rgb } from 'pdf-lib' import fontkit from "@pdf-lib/fontkit"; import {ref} from "vue" import {ElMessage} from "element-plus"; import { showLoading, hideLoading } from '@/utils/loading' export default { name: "PdfText", setup() { //pdf转成的图片文件,保存在数组中 const imgFiles = ref([]); //pdf的名字,记录下来 const pdfName = ref(""); //加载文件的结果集 const result = ref(""); //选择文件的处理 const handlePdf = (e) => { showLoading(); //hideLoading(); let filePaths = e.target.files; //清空原有缩略图 if (filePaths.length === 0) { //未选择,则返回 hideLoading(); return false; } imgFiles.value.length = 0; let file = e.target.files[0]; pdfName.value = file.name.substring(0, file.name.lastIndexOf(".")); const reader = new FileReader(); reader.readAsArrayBuffer(file); reader.onload = () => { //把读取到的结果集保存到result result.value = reader.result; const loadingTask = pdfjs.getDocument(reader.result) loadingTask.promise.then((pdf) => { var pageNum = pdf.numPages; console.log("页数:" + pageNum); //准备图片 for (let i = 1; i <= pageNum; i++) { let one = {id: i, imgSrc: ''}; imgFiles.value.push(one); } let maxHeight = 0; //处理 for (let i = 1; i <= pageNum; i++) { pdf.getPage(i).then((page) => { const canvas = document.getElementById('pdf_canvas_' + i); const ctx = canvas.getContext('2d') const viewport = page.getViewport({scale: 1}) // 画布大小,默认值是300px,height:150px canvas.height = viewport.height; canvas.width = viewport.width; //显示为图片的目标宽度 const destWidth = 263; canvas.style.width = destWidth + 'px'; // 根据pdf每页的宽高比例设置canvas的高度 let destHeight = destWidth * (viewport.height / viewport.width); canvas.style.height = destHeight + 'px'; page.render({ canvasContext: ctx, viewport, }); //如果目标高度更高,则调整每个canvas的高度,使显示高度统一 if (destHeight > maxHeight) { maxHeight = destHeight; for (let i = 0; i < pageNum; i++) { let canvas = document.getElementById('div_' + i); console.log("canvas name:" + 'div_' + i); console.log(canvas); canvas.style.height = maxHeight + 'px'; } } }) //加载完成 } // end for hideLoading(); }) }; } //下载的pdf,下载添加文字水印 const downPdf = async () => { //判断是否选择了pdf文件 if (imgFiles.value.length <= 0) { ElMessage.error("请先选择pdf文件"); return false; } const pdfDoc = await PDFDocument.load(result.value); const pages = pdfDoc.getPages(); // 自定义字体挂载 pdfDoc.registerFontkit(fontkit); var defaultFontBuffer = await fetch( '/fonts/SourceHanSerifSC-Light.otf', ).then(res => res.arrayBuffer()); const helveticaFont = await pdfDoc.embedFont(defaultFontBuffer); //加文本,演示时只加在了第一页 const firstPage = pages[0] const { height } = firstPage.getSize() firstPage.drawText('用js添加的水印文字上线了,测试看会不会显示完整,字号什么的!', { x: 5, y: height / 2 + 300, size: 50, font: helveticaFont, color: rgb(0.95, 0.1, 0.1), rotate: degrees(-45), opacity: 0.8, }) const pdfBytes = await pdfDoc.save(); saveByteArray(pdfName.value + "_" + timeFormat() + '.pdf', pdfBytes); } //保存pdf并下载 const saveByteArray = (reportName, byte) => { var blob = new Blob([byte], {type: "application/pdf"}); var link = document.createElement('a'); link.href = window.URL.createObjectURL(blob); var fileName = reportName; link.download = fileName; link.click(); }; //补0 const add0 = (m) => { return m < 10 ? '0' + m : m } //格式化时间 const timeFormat = () => { var time = new Date(); var y = time.getFullYear(); var m = time.getMonth() + 1; var d = time.getDate(); var h = time.getHours(); var mm = time.getMinutes(); var s = time.getSeconds(); let res = y + add0(m) + add0(d) + add0(h) + add0(mm) + add0(s); return res; } return { handlePdf, downPdf, imgFiles, } } } </script> <style scoped> </style>
三,测试效果
pdf的效果:
说明:pdf文件体积会变大,应该是同时打包了用到的字体文件的原因
四,查看vue框架的版本:
liuhongdi@lhdpc:/data/vue/pdf/image2pdf$ npm list vue child@0.1.0 /data/vue/pdf/image2pdf ├─┬ @vue/cli-plugin-babel@5.0.6 │ └─┬ @vue/babel-preset-app@5.0.6 │ └── vue@3.2.37 deduped └─┬ vue@3.2.37 └─┬ @vue/server-renderer@3.2.37 └── vue@3.2.37 deduped