大坑描述
在不改动源码的情况下,对支持Typescript的mozilla/pdf.js[email protected]
- 渲染模糊
- 文字遮罩层渲染
- 文字遮罩层缩放过小时文字遮罩层无法对齐
这三个问题的处理。
Typescript下pdf.js的引入
pdf渲染部分与文字遮罩层渲染部分的引入方法,不一定合理,仅供参考。
import { getDocument, GlobalWorkerOptions, renderTextLayer } from "pdfjs-dist";
import {
PDFDocumentLoadingTask,
PDFDocumentProxy,
PDFPageProxy,
TextContent
} from "pdfjs-dist/types/display/api";
由于pdf.js的ts版api文档不完善,目前ts版api信息只能在pdfjs-dist/types/display/api
中查看源码及注释说明。
Typescript下pdf.js的pdf渲染
三个问题(坑)
如何解决pdf渲染后模糊的问题
通过设置
getViewport()
的scale
参数,例如getViewport({scale: 3})
,以大比例渲染pdf,再通过css给canvas
设置相对较小的height
与width
缩小pdf,这样可以得到相对清晰的pdf。(注意:区分canvas
的height
、width
属性与其css的height
、width
的区别!)关键代码:
typescript// scale参数建议设置为2以上,以大比例渲染pdf const viewport = page.getViewport({ scale: props.renderingScale }); // PDF canvas初始化 const canvas = document.getElementById("pdf-demo-canvas") as HTMLCanvasElement; // 通过css设置canvas的高度与宽度,达成缩放的目的 canvas.style.height = canvasHeight + "px"; canvas.style.width = canvasWidth + "px"; const context = canvas.getContext("2d") as CanvasRenderingContext2D; // 使用viewport返回的高度与宽度作为canvas的高度与宽度属性 canvas.height = viewport.height; canvas.width = viewport.width;
Typescript下的文字遮罩层渲染函数
与Javascript调用pdf.js不同,使用Typescript时需要调用
PDFPageProxy
的getTextContent()
函数获取TextContent
类型的文字遮罩层参数,然后调用renderTextLayer()
渲染文字遮罩层。关键代码:
typescriptpage.render({...}).promise.then(() = >{ ... // 调用getTextContent函数 return page.getTextContent(); }).then((textContent: TextContent) = >{ ... // ts版 pdf.js遮罩层渲染设置 renderTextLayer({...}); }, (err: Error) = >{ console.log(err); });
文字遮罩层在缩放比例过小时无法对齐的问题
这里的思路跟解决渲染模糊问题有关。由于文字遮罩层需要使用与
canvas
同样的viewport
的渲染,所以我们给文字遮罩层的css设置与canvas
的height
和width
属性相同的高度与宽度,通过计算canvas
的css高度(或宽度)与canvas
的高度(或宽度) 属性得到一个缩放比,然后通过transform: scale()
应用该缩放比达到文字遮罩层等比缩放的目的。最终解决了文字遮罩层在缩放比例过小时无法对齐的问题。关键代码:
typescript// 创建文字遮罩层 const textLayerDiv = document.createElement("div"); textLayerDiv.setAttribute("class", "text-layer"); // 设置文字遮罩层的css高度与宽度 textLayerDiv.style.width = canvas.width + "px"; textLayerDiv.style.height = canvas.height + "px"; // 计算canvas的css宽度与canvas宽度属性的比例并设置scale缩放 textLayerDiv.style.transform = `scale(${ canvasWidth / canvas.width })`; textLayerDiv.style.transformOrigin = "left top"; const pdfDiv = document.querySelector(".pdf") as HTMLElement; pdfDiv.appendChild(textLayerDiv);
主要代码
- 由于是配合
Vue3
的composition api,所以getViewport()
调用的是props
中的renderingScale
属性,可以自行修改为普通的number
- 仅列举主体代码,部分代码使用
...
做省略处理
HTML部分
<!-- Vue SFC -->
<template>
<div class="pdfReader-wrap">
...
<div class="pdf">
<canvas id="pdf-demo-canvas"></canvas>
</div>
...
</div>
</template>
Typescript部分
/**
* 移除已有的遮罩层,避免遮罩层重复渲染
*/
const removeTextLayer = () = >{
const layer = document.querySelector(".text-layer");
if (layer) layer.remove();
};
/**
* 渲染pdf
* @param canvasHeight canvas高度
* @param canvasWidth canvas宽度
* @param pdfLoadingTask getDocument返回的LoadingTask类型参数
* @param pageNum 当前页数
*/
const renderPDF = (canvasHeight: number, canvasWidth: number, pdfLoadingTask: PDFDocumentLoadingTask, pageNum: number) = >{
pdfLoadingTask.promise.then((pdf: PDFDocumentProxy) = >{
pageCount.value = pdf.numPages;
// 获取PDF的指定页面
pdf.getPage(pageNum).then((page: PDFPageProxy) = >{
// scale参数建议设置为2以上,以大比例渲染pdf
const viewport = page.getViewport({
scale: props.renderingScale
});
// PDF canvas初始化
const canvas = document.getElementById("pdf-demo-canvas") as HTMLCanvasElement;
// 通过css设置canvas的高度与宽度,达成缩放的目的
canvas.style.height = canvasHeight + "px";
canvas.style.width = canvasWidth + "px";
const context = canvas.getContext("2d") as CanvasRenderingContext2D;
// 使用viewport返回的高度与宽度作为canvas的高度与宽度属性
canvas.height = viewport.height;
canvas.width = viewport.width;
// 渲染PDF canvas
page.render({
canvasContext: context,
viewport: viewport,
enableWebGL: true // 启用WebGL加速
}).promise.then(() = >{
return page.getTextContent();
}).then((textContent: TextContent) = >{
// 检测是否已有遮罩层,若存在则删除遮罩层,若getViewport的scale固定,则可以省略掉该函数
removeTextLayer();
// 创建文字遮罩层
const textLayerDiv = document.createElement("div");
textLayerDiv.setAttribute("class", "text-layer");
textLayerDiv.style.width = canvas.width + "px";
textLayerDiv.style.height = canvas.height + "px";
// 计算文字遮罩层的缩放比例
textLayerDiv.style.transform = `scale(${
canvasWidth / canvas.width
})`;
textLayerDiv.style.transformOrigin = "left top";
const pdfDiv = document.querySelector(".pdf") as HTMLElement;
pdfDiv.appendChild(textLayerDiv);
// ts版 pdf.js遮罩层渲染设置与文字遮罩层渲染
renderTextLayer({
textContent: textContent,
container: textLayerDiv,
viewport: viewport
});
},
(err: Error) = >{
console.log(err);
});
});
},
(err: Error) = >{
console.error(err);
});
};
CSS部分
canvas
与文字遮罩层.text-layer
均为canvas
与.text-layer
的重叠。
.pdfReader-wrap {
...
.pdf {
position relative
width 90vw
height 100%
overflow auto
color transparent
canvas {
position absolute
top 0
left 0
overflow visible
}
>>> .text-layer {
position absolute
top 0
left 0
background transparent
overflow hidden
line-height 1
span {
position absolute
white-space pre
transform-origin 0 0
}
::selection {
background-color rgba(0, 0, 255, 0.3)
}
}
}
}