PDF.js cannot display rendered canvas image
P粉766520991
2023-08-17 20:02:20
<p>I have a React component that displays a PDF. When I use this component, I just need to pass the URL of the PDF. </p>
<pre class="brush:php;toolbar:false;"><Previewer pdfUrl={pdfUrl}></Previewer></pre>
<p>The component handles the details of the preview. The problem I'm having is that this component doesn't display the PDF.Am I missing something? What should I do to solve this problem?下面是一个演示:</p>
<p><br /></p>
<pre class="snippet-code-js lang-js prettyprint-override"><code>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/pdfjs-dist@3.9.179/build/pdf.worker.js";
return pdfJS.getDocument({
url: pdfUrl,
cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.9.179/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>);</code></pre>
<pre class="snippet-code-html lang-html prettyprint-override"><code><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/pdfjs-dist@3.9.179/build/pdf.js"></script>
<div id="root"></div></code></pre>
<p><br /></p>
question
In the
renderPdfPagefunction, you create adivelement that contains thecanvaselement that renders the PDF page. Then append thesedivelements to acanvaselement. However, if the browser supports thecanvaselement, descendant elements of thecanvaselement will not be rendered. According to the specification of the canvas element:The rollback content is defined as follows:
solution
You need to change the parent
canvasto the appropriate element. In the demo below, I'm using adiv: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/pdfjs-dist@3.9.179/build/pdf.worker.js"; return pdfJS.getDocument({ url: pdfUrl, cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.9.179/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>);