前置知识
- 这个 demo 使用到了一个知识点
- canvas 可以使用
toDataURL
来将当前画布视图生成一个快照,返回 Base64 地址
- 查看 demo
const getUrl = (url, callback) => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const img = new Image();
img.setAttribute("crossorigin", "anonymous");
img.src = url;
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const baseUrl = canvas.toDataURL("image/jpeg", 1);
appendImg(baseUrl);
};
};
const appendImg = (url) => {
const img = new Image();
img.src = url;
img.onload = () => {
document.body.appendChild(img);
};
};
getUrl("https://mozilla.github.io/pdf.js/images/logo.svg", appendImg);

- 可以看到,图片正常展示,但是有一些周围是黑色的
- 这是因为,canvas 背景色默认为透明色,当生成jpeg快照的时候,就变成黑色了
- 解决办法,看下方 demo
思路
:判断是否为透明色,将其改成白色
不足
:看到还是有些黑色锯齿,暂时没有解决
- 将其改为png格式即可

let imageData = ctx.getImageData(0, 0, img.width, img.height);
for (let i = 0; i < imageData.data.length; i += 4) {
if (imageData.data[i + 3] === 0) {
imageData.data[i] = 255;
imageData.data[i + 1] = 255;
imageData.data[i + 2] = 255;
imageData.data[i + 3] = 255;
}
}
ctx.putImageData(imageData, 0, 0);
回归正题
不使用第三方插件,初步尝试
- 直接使用 img 标签来展示
- safari 浏览器可以展示,但只展示 pdf 的第一页
- 谷歌浏览器展示失败
- 使用原生标签展示
embed
object
- 问题:可以展示,但是不太好看
<embed src="url" type="application/pdf" width="800" height="800" />
<object data="url" type="application/pdf" width="800" height="800"></object>
使用插件
- 官网:
http://mozilla.github.io/pdf.js/
- 先尝试一下,不用架子,在本地写个 demo 跑一下
- 到官网下载依赖文件
我下载的2.12.313版本
- 因为pdf通过插件本身处理后再插入canvas,他不是透明背景了,所以上面的透明转白色知识暂时没用到
- 思路:
- 解析每张pdf
实际上在这个demo中,这个插件帮我们做的只是读取pdf并且插入到canvas
- 然后依次将其插入到
新建的
canvas中,然后生成快照,将快照地址存储到数组中
- 循环数组,按照顺序位置,依次向canvas中放置图片,
- 然后将最终的canvas生成base64地址,传递给callback
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="./pdfjs-2.12.313-dist/build/pdf.js"></script>
<script src="./pdfjs-2.12.313-dist/build/pdf.worker.js"></script>
<title>测试</title>
</head>
<body>
<img src="" alt="" width="100%" height="auto" id="i1" />
<canvas id="the-canvas" style="border:1px solid black"></canvas>
</body>
</html>
<script>
function addPdf(url) {
let loadingTask = pdfjsLib.getDocument(url);
loadingTask.promise.then((pdf) => {
let numPages = pdf.numPages;
let scale = 2;
let Mycanvas = document.querySelector("#the-canvas");
let imgList = [];
let top = 0;
let height = 0;
let width = 0;
let okRender = new Promise(async (res, rej) => {
for (let i = 1; i <= numPages; i++) {
let page = await pdf.getPage(i);
let viewport = page.getViewport({
scale: scale,
});
let canvas = document.createElement("canvas");
canvas.height = viewport.height;
canvas.width = viewport.width;
console.log(viewport.height, 9);
let context = canvas.getContext("2d");
let renderContext = {
canvasContext: context,
viewport: viewport,
};
let success = await page.render(renderContext).promise;
if (i === 1) {
height = viewport.height;
width = viewport.width;
Mycanvas.height = viewport.height * numPages;
Mycanvas.width = viewport.width;
}
let dataURL = canvas.toDataURL("image/jpeg", 1);
imgList.push(dataURL);
console.log(i, numPages, imgList.length, imgList);
if (i === numPages) {
res();
}
}
});
okRender.then((res) => {
let context = Mycanvas.getContext("2d");
imgList.forEach((item) => {
var image = new Image();
image.src = item;
image.onload = () => {
context.drawImage(image, 0, top, width, height);
top += height;
};
});
});
});
}
addPdf("url");
</script>
- 感觉 ok 了,找个架子试一下
- 在项目里使用
- 虽然他名字为
pdfjs
,但是对应的 npm 包为 pdfjs-dist
- 在 ts 里面用的时候,安装声明文件要注意,
pdfjs-dist@2.2.228
对应@types/pdfjs-dist@2.1.7
,否则有些奇奇怪怪的问题
import pdfjs from "pdfjs-dist";
const addPdf = (url: string, callback?: any) => {
let loadingTask: any = pdfjs.getDocument(url);
loadingTask.promise.then((pdf: any) => {
let numPages = pdf.numPages;
let scale = 2;
let Mycanvas = document.createElement("canvas");
let imgList: any = [];
let top = 0;
let height = 0;
let width = 0;
let okRender = new Promise(async (res: any, rej) => {
for (let i = 1; i <= numPages; i++) {
let page = await pdf.getPage(i);
let viewport = page.getViewport({
scale: scale,
});
let canvas = document.createElement("canvas");
canvas.height = viewport.height;
canvas.width = viewport.width;
let context = canvas.getContext("2d");
let renderContext = {
canvasContext: context,
viewport: viewport,
};
let success = await page.render(renderContext).promise;
if (i === 1) {
height = viewport.height;
width = viewport.width;
Mycanvas.height = viewport.height * numPages;
Mycanvas.width = viewport.width;
}
let dataURL = canvas.toDataURL("image/jpeg", 1);
imgList.push(dataURL);
if (i === numPages) {
res();
}
}
});
okRender.then(() => {
let context = Mycanvas.getContext("2d");
imgList.forEach((item: any, index: number) => {
var image = document.createElement("img");
image.src = item;
image.onload = async () => {
context.drawImage(image, 0, top, width, height);
top += height;
if (index === imgList.length - 1) {
callback(Mycanvas.toDataURL("image/jpeg", 1));
}
};
});
});
});
};
问题
字体缺失
let loadingTask = pdfjsLib.getDocument({
url:pdf地址,
cMapUrl:'https://unpkg.com/pdfjs-dist@2.0.489/cmaps/',
cMapPacked: true
});
总结一下
- 如果不是在
page.render.promise.then
里面生成快照,那就是一团黑,因为他是异步,在外边优先执行会导致生成了一个透明画板的快照
- 放在
page.render.promise.then
也有点小问题,偶尔会出现第二张先渲染的情况,或偶尔黑屏
- 所以干脆直接 async 和 await 了