1、embed 标签实现在线预览 pdf
最近要实现一个功能 —— 在线预览 pdf 文件,一开始在网上查找了下资料,都是说使用 pdf.js
来实现在线预览 pdf,但是对于要实现的功能,需要去加载整个文件,似乎有点不太理想,后面回想起 html5 中提供了embed标签,该标签可以将外部内容嵌入文档中的指定位置,我们可以看下用法:
<embed src="资源地址" width="400" height="400"></embed>
效果展示(展示的效果会根据浏览器自身的支持展示不同的界面效果):
2、隐藏工具栏
实现了在线预览 pdf 之后,有时候不想让用户通过页面上的下载按钮和打印按钮进行下载和打印,需要隐藏这个功能,一开始我的思路是通过获取页面上工具栏的 DOM,然后去添加样式,让按钮进行隐藏,可惜的是获取不到这个 DOM(或许可以获取,只是我还不知道如何去取,知道的大佬可以告知我一声,感谢~),于是去查了下资料,我们可以在外部链接后面拼接上 #toolbar=0
来把工具栏进行隐藏,虽然可以实现隐藏下载按钮和打印按钮,但是同时也是限制了其他的操作,不过好在只需要预览,不需要其他操作,该方法还是可取的。
3、pdf 添加水印
解决了下载和打印的问题之后,又增加了一个功能,需要在 pdf 上添加水印,也就是需要操作到 pdf 对象了,在这里推荐一个插件库 pdf-lib
,该库是专门对 pdf 文件进行操作的一个库,可以让我们获取到 pdf 对象的属性和操作 pdf 对象,废话不多说,上代码:
(1) 安装依赖
// 1、安装依赖
npm install pdf-lib --save
// 2、自定义字体库依赖
npm install "@pdf-lib/fontkit" --save
(2)使用 pdf-lib
import { PDFDocument } from "pdf-lib"
export const addWaterMark = async (url: string, config?: object) => {
// 获取文件的 arrayBuffer 流,获取路径可以前端本地项目文件,如果是脚手架,文件需要放在 public 目录下
// 也可以从后台获取返回的 base64 文件流
const existingPdfBytes = await fetch(url).then(res => res.arrayBuffer())
// 生成一个 pdf 文档对象
const pdfDoc = await PDFDocument.load(existingPdfBytes)
// 获取所有的 pdf 页面
const pages = pdfDoc.getPages()
for (let i = 0; i < pages.length; i++) {
const page = pages[i]
const { height } = page.getSize()
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 3; j++) {
page.drawText('watermark', {
x: 200 * j,
y: (height / 4) * i,
size: 16,
opacity: 0.3,
})
}
}
}
// 生成 unit64Arrary文件流
const pdfBytes = await pdfDoc.save();
let blobData = new Blob([pdfBytes], { type: "application/pdf;Base64" });
// 返回一个新的 url 链接
return window.URL.createObjectURL(blobData);
}
以上代码可以简单的实现 pdf 添加水印的功能,但存在一个问题,pdf-lib
不支持中文,如果用了中文,控制台会报错;
如果要实现中文水印,可以有两种解决方法,第一种是添加自定义字体包,第二种是通过 canvas 生成文字图片,然后绘制在 pdf 文件上,个人比较偏向第二种,因为第一种需要加载整个字体包,一般都比较大,不是很推荐,我们来看看各自的实现方式:
方式一:添加自定义字体包
import { PDFDocument } from "pdf-lib"
import fontkit from "@pdf-lib/fontkit"
export const addWaterMark = async (url: string, config?: object) => {
const existingPdfBytes = await fetch(url).then(res => res.arrayBuffer())
const pdfDoc = await PDFDocument.load(existingPdfBytes)
// 自定义字体包路径,放在 public 目录
const fonturl = './font/font.ttf';
const fontBytes = await fetch(fonturl).then((res) => res.arrayBuffer());
pdfDoc.registerFontkit(fontkit)
const customFont = await pdfDoc.embedFont(fontBytes)
const pages = pdfDoc.getPages()
for (let i = 0; i < pages.length; i++) {
const page = pages[i]
const { width, height } = page.getSize()
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 3; j++) {
page.drawText('中文', {
x: 200 * j,
y: (height / 4) * i,
size: 16,
// 自定义字体
font: customFont,
opacity: 0.3,
})
}
}
}
const pdfBytes = await pdfDoc.save();
let blobData = new Blob([pdfBytes], { type: "application/pdf;Base64" });
return window.URL.createObjectURL(blobData);
}
方式二:通过 canvas 生成文字图片
export const addWaterMark = async (url: string, config: PDFConfig) => {
// 水印文字,文字大小
const { text, size } = config;
const existingPdfBytes = await fetch(url).then(res => res.arrayBuffer())
const pdfDoc = await PDFDocument.load(existingPdfBytes)
// 生成 canvas 文字图片
let canvas = document.createElement('canvas')
const textBlockWidth = size * (text.length + 1)
const textBlockHeight = size * 2
canvas.width = textBlockWidth
canvas.height = textBlockHeight
const ctx = canvas.getContext('2d')
ctx.fillStyle = '#000'
ctx.font = `${size}px Microsoft`
ctx.fillText(text, size / 2, size)
const png = canvas.toDataURL('img/png')
const imagePDF = await pdfDoc.embedPng(png)
const pages = pdfDoc.getPages()
for (let i = 0; i < pages.length; i++) {
const page = pages[i]
const { width, height } = page.getSize()
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 3; j++) {
// 将文字图片绘制到 pdf
page.drawImage(imagePDF, {
x: 200 * j,
y: (height / 4) * i,
opacity: 0.3,
})
}
}
}
const pdfBytes = await pdfDoc.save();
let blobData = new Blob([pdfBytes], { type: "application/pdf;Base64" });
return window.URL.createObjectURL(blobData);
}
以上便是两种实现水印的方式,其中用到了 drawText
和 drawImage
两个方法,在 page 对象上,还有其他可以操作的方法,可以实现更多的功能,这里就不进行详细介绍;
4、 总结
在线预览 pdf 可以使用 embed
标签,不过可能存在一定的浏览器兼容问题,谨慎使用,或者使用 pdf.js
去实现预览,如果我们还需要对 pdf 对象进行操作,我们可以使用 pdf-lib
插件库,具体步骤分为:
- 获取文件的 arrayBuffer 流;
- 生成一个 pdf 文档对象;
- 对 pdf 对象进行操作;
- 生成 unit64Arrary文件流;
- 生成新的 pdf 链接;
除了可以添加水印外,可以设置水印的旋转角度、颜色等,也可以将添加了水印的 pdf 进行下载(pdf-lib 的 download 方法),可以根据实际需求来完成,要注意的是,设置颜色需要用插件库提供的颜色转换 API,即 rgb
,具体使用可以看源码的实现方式;