PDF.js 如何正确处理 PDF 加载错误并等待页面渲染完成?
PDF.js 如何正确处理 PDF 加载错误并等待页面渲染完成【免费下载链接】pdf.jsPDF Reader in JavaScript项目地址: https://gitcode.com/gh_mirrors/pd/pdf.js在浏览器里用 PDF.js 作为库加载 PDF 时有两个时序问题必须处理PDF 的下载与解析是异步的getDocument不会直接返回文档对象加载可能失败页面渲染同样是异步的调用page.render()后并不等于画面已经画完而文档明确说明同一个 canvas 不能同时用来绘制两页。本文基于仓库中的 示例文档 和 Hello World 示例、Previous/Next 示例、pdf2png Node 脚本给出一条可执行的路径捕获加载错误并确定渲染何时完成。准备条件了解 Promise示例文档 开头明确指出 PDF.js 重度依赖 Promise如果 Promise 对你还不熟建议先熟悉再继续。必须指定 worker 路径示例中的注释要求 The workerSrc property shall be specifiedpdfjsLib.GlobalWorkerOptions.workerSrc ../../node_modules/pdfjs-dist/build/pdf.worker.mjs;需要一个 HTTP 服务器Getting Started 文档 说明 worker 不支持file://地址所以不能双击 HTML 直接打开要起一个服务器如果使用源码构建且有 Node可以运行npx gulp server。远程 PDF 需要 CORSHello World 示例 中的注释写明如果提供的是远程服务器上的绝对 URL需要在那台服务器上配置 CORS 头。库脚本路径仓库示例通过node_modules/pdfjs-dist/build/pdf.mjs引入库例如示例文件中的script src../../node_modules/pdfjs-dist/build/pdf.mjs typemodule/script这是相对于examples/learning/目录的路径。你自己的项目应替换为实际安装的 pdfjs-dist 位置。Getting Started 文档 也给出了预构建下载与 CDNjsDelivr、cdnjs、unpkg三种获取方式。加载文档用 Promise 捕获错误pdfjsLib.getDocument()返回的是一个PDFDocumentLoadingTask实例它的promise属性会在解析完成时 resolve 出文档对象加载失败时这个 promise 会 reject错误处理就挂在它上面。示例文档 对 Hello World with document load error handling 一节给出的说明是该示例演示了 how promises can be used to handle errors during loading并 wait until a page is loaded and rendered。最小加载写法取自 Hello World 示例 的注释风格加上错误处理// // If absolute URL from the remote server is provided, configure the CORS // header on that server. // const url ./helloworld.pdf; pdfjsLib.GlobalWorkerOptions.workerSrc ../../node_modules/pdfjs-dist/build/pdf.worker.mjs; // // Asynchronous download PDF // const loadingTask pdfjsLib.getDocument({ url }); try { const pdf await loadingTask.promise; // 加载成功pdf 是文档对象 } catch (reason) { // 加载失败reason 是拒绝原因 console.log(reason); }仓库中一个可直接参照的try/catch实例是 Node 脚本 pdf2png.mjs它把await loadingTask.promise之后的整个流程包在try里catch (reason)中执行console.log(reason)。渲染页面用renderTask.promise等待完成加载成功后取第一页、创建 viewport、准备 canvas这部分照 Hello World 示例 照搬即可const page await pdf.getPage(1); const scale 1.5; const viewport page.getViewport({ scale }); // Support HiDPI-screens. const outputScale window.devicePixelRatio || 1; const canvas document.getElementById(the-canvas); const context canvas.getContext(2d); canvas.width Math.floor(viewport.width * outputScale); canvas.height Math.floor(viewport.height * outputScale); canvas.style.width Math.floor(viewport.width) px; canvas.style.height Math.floor(viewport.height) px; const transform outputScale ! 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null; const renderContext { canvasContext: context, transform, viewport, }; page.render(renderContext);这里要回答渲染什么时候算完成page.render()返回一个 render task等待它的 promise 即可。Previous/Next 示例 中对应的代码和注释是var renderTask page.render(renderContext); // Wait for rendering to finish renderTask.promise.then(function () { // 渲染完成可以安全地在 canvas 上发起下一次绘制 });await renderTask.promise等价于上面的.then写法pdf2png.mjs 用的就是这种写法渲染完成后才调用canvas.toBuffer(image/png)导出图片。多页切换渲染未完就排队不要并发示例文档 对 Previous/Next 示例的说明是The same canvas cannot be used to perform to draw two pages at the same time -- the example demonstrates how to wait on previous operation to be complete. 也就是说翻页时如果上一次渲染还没结束新的请求必须排队。prevnext.html 的实现方式用pageRendering标志记录是否正在渲染用pageNumPending记录排队中的页码queueRenderPage(num)正在渲染就把页码存入pageNumPending否则立即渲染在renderTask.promise.then(...)回调里把pageRendering置回false并检查pageNumPending不为null就继续渲染那一页。function queueRenderPage(num) { if (pageRendering) { pageNumPending num; } else { renderPage(num); } }文档中queueRenderPage的注释说明了这个函数契约If another page rendering in progress, waits until the rendering is finished. Otherwise, executes rendering immediately. 前/后页按钮的点击处理里还会用pageNum 1和pageNum pdfDoc.numPages做边界保护避免越界取页。Node 环境下的完整链路可选分支如果你不是浏览器而是 Node 脚本场景pdf2png.mjs 展示了完整的加载—渲染—等待—释放链路import fs from fs; import { getDocument } from pdfjs-dist/legacy/build/pdf.mjs; // Some PDFs need external cmaps. const CMAP_URL ../../../node_modules/pdfjs-dist/cmaps/; const CMAP_PACKED true; // Where the standard fonts are located. const STANDARD_FONT_DATA_URL ../../../node_modules/pdfjs-dist/standard_fonts/; // Loading file from file system into typed array. const pdfPath process.argv[2] || ../../../web/compressed.tracemonkey-pldi-09.pdf; const data new Uint8Array(fs.readFileSync(pdfPath)); // Load the PDF file. const loadingTask getDocument({ data, cMapUrl: CMAP_URL, cMapPacked: CMAP_PACKED, standardFontDataUrl: STANDARD_FONT_DATA_URL, }); try { const pdfDocument await loadingTask.promise; console.log(# PDF document loaded.); // Get the first page. const page await pdfDocument.getPage(1); // Render the page on a Node canvas with 100% scale. const canvasFactory pdfDocument.canvasFactory; const viewport page.getViewport({ scale: 1.0 }); const canvasAndContext canvasFactory.create(viewport.width, viewport.height); const renderContext { canvasContext: canvasAndContext.context, viewport, }; const renderTask page.render(renderContext); await renderTask.promise; // Convert the canvas to an image buffer. const image canvasAndContext.canvas.toBuffer(image/png); fs.writeFile(output.png, image, function (error) { if (error) { console.error(Error: error); } else { console.log(Finished converting first page of PDF file to a PNG image.); } }); // Release page resources. page.cleanup(); } catch (reason) { console.log(reason); }注意几点适用条件Node 路径用pdfjs-dist/legacy/build/pdf.mjs的getDocument文件读成Uint8Array后通过data传入而不是url文档说明 Some PDFs need external cmaps所以cMapUrl和standardFontDataUrl参数需要指向 node_modules 中的实际目录示例里是相对于examples/node/pdf2png/的路径你复制时按自己的目录结构替换。脚本默认处理的 PDF 是仓库自带的web/compressed.tracemonkey-pldi-09.pdf也可以通过第一个命令行参数传入其他 PDF 路径。结果验证浏览器没有专门的日志判断依据就是时序本身——只有在renderTask.promise的回调里渲染才算完成之后的代码更新页码计数器、发起下一次渲染、导出 canvas都是安全的。prevnext.html 在加载成功后用pdfDoc.numPages更新页码显示在renderTask.promise.then里才放行下一次渲染这就是文档给出的完成判定方式。Node文档示例输出pdf2png.mjs 的示例运行会打印# PDF document loaded.写文件完成后打印Finished converting first page of PDF file to a PNG image.writeFile出错时打印Error:加错误对象。这些是文档中的示例输出具体字符串以脚本实际内容为准。限制与边界file://下 worker 不可用必须通过 HTTP 服务器访问页面源码构建可用npx gulp server见 Getting Started 文档 的 Trying the Viewer 一节。远程 PDF 需要服务端 CORS 配置否则加载会在网络层失败并 rejectloadingTask.promise示例注释见上。同一 canvas 不能并发绘制两页这是文档明确给出的限制因此多页场景必须按renderTask.promise串行排队而不是并行发起渲染。Core 层不在本文范围内Getting Started 文档 说明 core 层的 API 可能变化、直接算高级用法本文所有写法都基于 display 层 APIgetDocument/getPage/render这一层的 API 是版本号所依据的稳定接口。参考资料示例文档加载、取页、渲染的代码说明以及错误处理与等待渲染的示例索引Hello World 示例浏览器端最小加载与渲染Previous/Next 示例等待renderTask.promise并串行排队渲染pdf2png Node 脚本Node 端加载错误处理与渲染等待的完整链路Getting Started 文档分层说明、下载/CDN 方式、服务器要求【免费下载链接】pdf.jsPDF Reader in JavaScript项目地址: https://gitcode.com/gh_mirrors/pd/pdf.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考