我有一个显示 PDF 的 React 组件。当我使用这个组件时,我只需要传递 PDF URL 即可。
<Previewer pdfUrl={pdfUrl}></Previewer>
该组件处理预览细节。我面临的问题是该组件不显示 PDF。我错过了什么吗?我应该怎么做才能解决这个问题?下面是一个演示:
const { StrictMode, useRef } = React;
const { createRoot } = ReactDOM;
const styles = {};
const Previewer = (props) => {
const canvasRef = useRef(null);
React.useEffect(() => {
if (props.pdfUrl) {
initPdf(props.pdfUrl);
}
}, []);
// Converted async/await function to use promise because Stack Snippet's Babel version does not support async/await
const initPdf = (pdfUrl) => {
// Reference CDN version of PDF.js instead of using ES6 import
const pdfJS = pdfjsLib;
pdfJS.GlobalWorkerOptions.workerSrc = "https://unpkg.com/[email protected]/build/pdf.worker.js";
return pdfJS.getDocument({
url: pdfUrl,
cMapUrl: 'https://cdn.jsdelivr.net/npm/[email protected]/cmaps/',
cMapPacked: true,
}).promise.then((pdf) => {
const totalPages = pdf.numPages;
for (let i = 0; i < totalPages; i++) {
renderPdfPage(pdf, pdfJS, i + 1);
}
});
}
// Converted async/await function to use promise because Stack Snippet's Babel version does not support async/await
const renderPdfPage = (pdf, pdfJS, pageNum) => {
return pdf.getPage(pageNum).then((page) => {
const viewport = page.getViewport({
scale: 1.0
});
let divPage = window.document.createElement("div");
if(!canvasRef || !canvasRef.current) return;
let canvas = divPage.appendChild(window.document.createElement("canvas"));
const canvasContext = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderContext = { canvasContext, viewport };
const renderTask = page.render(renderContext);
renderTask.promise.then(function () {
const textContent = page.getTextContent();
return textContent;
}).then(function (textContent) {
const textLayer = document.querySelector(`.${styles.textLayer}`);
if (!textLayer) return;
textLayer.style.left = canvas.offsetLeft + 'px';
textLayer.style.top = canvas.offsetTop + 'px';
textLayer.style.height = canvas.offsetHeight + 'px';
textLayer.style.width = canvas.offsetWidth + 'px';
textLayer.style.setProperty('--scale-factor', '1.0');
pdfJS.renderTextLayer({
textContentSource: textContent,
container: textLayer,
viewport: viewport,
textDivs: []
});
});
canvasRef.current.appendChild(divPage);
});
}
return (
<div className={styles.cavasLayer}>
<canvas id="the-cavas" ref={canvasRef} />
{/**<div className={styles.textLayer}></div>**/}
</div>
);
}
const pdfUrl = "https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"
const root = createRoot(document.getElementById("root"));
root.render(<StrictMode><Previewer pdfUrl={pdfUrl} /></StrictMode>);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script crossorigin src="https://unpkg.com/[email protected]/build/pdf.js"></script>
<div id="root"></div>
在
renderPdfPage
函数中,您创建一个 div
元素,其中包含具有渲染的 PDF 页面的 canvas
元素。然后将这些 div
附加到 canvas
元素。但是,如果浏览器支持 canvas
元素,则不会呈现 canvas
元素的后代。来自 canvas
元素的 规范:
元素时,他们还必须提供在呈现给用户时传达与canvas
位图基本相同的功能或目的的内容。该内容可以作为canvas
元素的内容放置。canvas
元素的内容(如果有)是该元素的canvas
。fallback content
后备内容的定义是:
某些嵌入的内容元素可以具有后备内容:当外部资源无法使用时要使用的内容(例如,因为它的格式不受支持)。元素定义说明后备是什么(如果有)。
您需要将父元素
canvas
更改为合适的元素。我在下面的演示中使用了 div
:
const { StrictMode, useRef } = React;
const { createRoot } = ReactDOM;
const styles = {};
const Previewer = (props) => {
const pdfContainerRef = useRef(null);
React.useEffect(() => {
if (props.pdfUrl) {
initPdf(props.pdfUrl);
}
}, []);
// Converted async/await function to use promise because Stack Snippet's Babel version does not support async/await
const initPdf = (pdfUrl) => {
// Reference CDN version of PDF.js instead of using ES6 import
const pdfJS = pdfjsLib;
pdfJS.GlobalWorkerOptions.workerSrc = "https://unpkg.com/[email protected]/build/pdf.worker.js";
return pdfJS.getDocument({
url: pdfUrl,
cMapUrl: 'https://cdn.jsdelivr.net/npm/[email protected]/cmaps/',
cMapPacked: true,
}).promise.then((pdf) => {
const totalPages = pdf.numPages;
for (let i = 0; i < totalPages; i++) {
renderPdfPage(pdf, pdfJS, i + 1);
}
});
}
// Converted async/await function to use promise because Stack Snippet's Babel version does not support async/await
const renderPdfPage = (pdf, pdfJS, pageNum) => {
return pdf.getPage(pageNum).then((page) => {
const viewport = page.getViewport({
scale: 1.0
});
let divPage = window.document.createElement("div");
if(!pdfContainerRef || !pdfContainerRef.current) return;
let canvas = divPage.appendChild(window.document.createElement("canvas"));
const canvasContext = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
const renderContext = { canvasContext, viewport };
const renderTask = page.render(renderContext);
renderTask.promise.then(function () {
const textContent = page.getTextContent();
return textContent;
}).then(function (textContent) {
const textLayer = document.querySelector(`.${styles.textLayer}`);
if (!textLayer) return;
textLayer.style.left = canvas.offsetLeft + 'px';
textLayer.style.top = canvas.offsetTop + 'px';
textLayer.style.height = canvas.offsetHeight + 'px';
textLayer.style.width = canvas.offsetWidth + 'px';
textLayer.style.setProperty('--scale-factor', '1.0');
pdfJS.renderTextLayer({
textContentSource: textContent,
container: textLayer,
viewport: viewport,
textDivs: []
});
});
pdfContainerRef.current.appendChild(divPage);
});
}
return (
<div className={styles.cavasLayer}>
<div id="pdf-container" ref={pdfContainerRef} />
{/**<div className={styles.textLayer}></div>**/}
</div>
);
}
const pdfUrl = "https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf"
const root = createRoot(document.getElementById("root"));
root.render(<StrictMode><Previewer pdfUrl={pdfUrl} /></StrictMode>);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script crossorigin src="https://unpkg.com/[email protected]/build/pdf.js"></script>
<div id="root"></div>