2023年11月06日
从零开始使用PDF.js构建PDF阅读器
便携文档格式(PDF)被广泛用于在各种平台上共享文档。PDF之所以受欢迎,是因为它以一种方式保持文档的格式和布局,使得任何操作系统或PDF查看器都不会显示任何修改的迹象。PDF是通过称为PDF查看器的软件工具来显示的,这些工具具有许多与文档交互的功能,如导航、放大和缩小以及跳转到特定页面。
每个PDF查看器都有特定的功能,这限制了新功能的可能性。因此,开发人员可能希望创建自己的PDF查看器软件,以满足其特定的需求和偏好,比如文档分析或数据提取。创建自定义PDF查看器有许多优势,其中一些如下:
- 定制化 :开发自己的PDF查看器可以让您定制其界面、功能和功能,以满足您特定的需求。
- 集成 :构建自定义PDF查看器可以让您无缝集成到您的平台或应用程序中,而不会牺牲任何功能。
- 安全和隐私 :自定义PDF查看器可以让您控制加密和访问控制等安全措施,以确保PDF文件中敏感内容的保密性。
在本文中,您将使用PDF.js库构建一个简单的自定义PDF查看器。PDF.js是Mozilla开发的基于HTML5的开源PDF查看器库,用于呈现和解析PDF文档。
使用PDF.js库开发PDF查看器
在开始之前,您需要选择一个代码编辑器和PDF.js库,可以通过CDN(本教程中使用)连接,也可以从GitHub下载。
设置环境
首先,您将设置一个包含HTML、CSS和JavaScript功能的简单网页的Vanilla JS项目。
在文件夹中设置一个名为PDF Viewer 的项目文件夹,并在文件夹中创建以下三个文件:
- index.html
- styles.css
- viewer.js
项目文件夹结构应如下所示:
项目结构
为PDF查看器创建用户界面
下一步是创建一个界面来使用PDF查看器。
打开index.html
文件,然后输入以下代码并保存:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Simple PDF Viewer</title> <link rel="stylesheet" href="styles.css"> </head> <body> <h1>Simple PDF Viewer</h1> <div id="pdf-container"></div> <div id="page-controls"> <button id="prev-page">Previous</button> <span id="page-num">Page 1</span> <button id="next-page">Next</button> <input type="number" id="go-to-page-input" placeholder="Go to Page"> <button id="go-to-page-btn">Go</button> </div> <div> <button id="zoom-out">Zoom Out</button> <button id="zoom-in">Zoom In</button> </div> <input type="file" id="file-input" accept=".pdf"> </body> </html>
上述代码创建了一个简单的界面,包括以下元素:
- 用于显示PDF文件的容器
- “上一页”和“下一页”按钮,用于导航PDF文件的页面
- 用于跳转到PDF文件指定页面编号的输入字段
- “放大”和“缩小”按钮
- 用于从用户本地文件资源管理器中选择PDF文件的按钮
除了创建用户界面外,还有两个用于外部JavaScript文件的脚本标签,用于向网页添加功能。
第一个脚本标签https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js
是用于呈现PDF文档的PDF.js库的链接。这将把PDF.js库集成到您的项目中。
第二个脚本标签viewer.js
是用于实现PDF查看器功能的自定义JavaScript文件的链接。
这样,HTML文件中的代码就结束了。生成的网页应如下所示:
PDF查看器
使用CSS为界面添加样式
虽然本教程不侧重于为用户界面添加样式,但您可以使其看起来更加美观。将以下代码保存在styles.css
中:
/* styles.css */ body { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; font-family: Arial, sans-serif; } #pdf-container { border: 1px solid #ccc; overflow: auto; width: 80%; max-width: 800px; height: 70vh; } .button-group, #zoom-controls, #file-controls { display: flex; align-items: center; margin-top: 10px; } .button-group button, #zoom-controls button, #file-controls button, #file-controls input[type="file"] { margin: 0 5px; padding: 5px 10px; border: none; background-color: #007bff; color: #fff; cursor: pointer; border-radius: 4px; font-size: 14px; transition: background-color 0.3s ease; } .button-group button:hover, #zoom-controls button:hover, #file-controls button:hover, #file-controls input[type="file"]:hover { background-color: #0056b3; } #go-to-page-input, #go-to-page-btn { margin-left: 5px; }
上述代码将元素居中,为显示PDF的空间进行了组织,并对所有按钮和输入字段进行了对齐。
生成的网页现在应如下所示:
带有CSS样式的PDF查看器
使用JavaScript和PDF.js添加功能
现在,您已经使用CSS组织了PDF查看器的界面。最后一步是使用JavaScript和PDF.js库添加功能。
首先,为了向PDF查看器添加功能,您需要将所有相关代码放入名为DOMContentLoaded
的事件侦听器中。这样可以确保在HTML文档完全加载后执行JavaScript代码:
document.addEventListener('DOMContentLoaded', function() { // 所有代码在此处 });
您还需要通过JavaScript启用与HTML(DOM)元素的交互,例如按钮和输入字段。document.getElementById()
函数允许访问文档对象模型(DOM)元素,从而可以为稍后使用的每个元素创建变量:
const pdfContainer = document.getElementById('pdf-container'); const prevPageBtn = document.getElementById('prev-page'); const nextPageBtn = document.getElementById('next-page'); const pageNumSpan = document.getElementById('page-num'); const goToPageInput = document.getElementById('go-to-page-input'); const goToPageBtn = document.getElementById('go-to-page-btn'); const zoomOutBtn = document.getElementById('zoom-out'); const zoomInBtn = document.getElementById('zoom-in'); const fileInput = document.getElementById('file-input');
然后,您将初始化以下三个变量:pdfDoc
、pageNum
和scale
。pdfDoc
变量存储加载的PDF文档实例,pageNum
变量跟踪当前显示的页面编号,scale
变量用于跟踪缩放比例,以实现放大和缩小功能:
let pdfDoc = null; let pageNum = 1; let scale = 1.0;
接下来,renderPage()
函数呈现加载的PDF文件的特定页面。它获取PDF页面,调整画布尺寸以匹配页面,清除容器,并将页面内容呈现到画布上,从而在容器内有效地显示页面:
async function renderPage(num) { const page = await pdfDoc.getPage(num); const viewport = page.getViewport({ scale: scale }); const canvas = document.createElement('canvas'); const canvasContext = canvas.getContext('2d'); canvas.height = viewport.height; canvas.width = viewport.width; pdfContainer.innerHTML = ''; pdfContainer.appendChild(canvas); const renderContext = { canvasContext, viewport, }; await page.render(renderContext); }
loadPDF()
函数从给定的URL加载PDF文档,使用pdfjsLib.getDocument()
方法。然后调用renderPage()
来显示初始页面并更新页面计数:
async function loadPDF(url) { const loadingTask = pdfjsLib.getDocument(url); pdfDoc = await loadingTask.promise; renderPage(pageNum); pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`; }
接下来,您将为多个按钮添加点击功能的事件侦听器,例如:
prevPageBtn
和nextPageBtn
按钮用于页面导航,同时更新要显示的页面编号goToPageBtn
按钮,用于跳转到goToPageInput
字段中输入的值(页面编号),以跳转到PDF文件的特定页面zoomOutBtn
和zoomInBtn
按钮,用于放大和缩小显示的页面
prevPageBtn.addEventListener('click', () => { if (pageNum > 1) { pageNum--; renderPage(pageNum); pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`; } }); nextPageBtn.addEventListener('click', () => { if (pageNum < pdfDoc.numPages) { pageNum++; renderPage(pageNum); pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`; } }); goToPageBtn.addEventListener('click', () => { // Event listener for the "Go to Page" button const targetPage = parseInt(goToPageInput.value); if (targetPage >= 1 && targetPage <= pdfDoc.numPages) { pageNum = targetPage; renderPage(pageNum); pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`; } }); zoomOutBtn.addEventListener('click', () => { if (scale > 0.25) { scale -= 0.25; renderPage(pageNum); } }); zoomInBtn.addEventListener('click', () => { if (scale < 3) { scale += 0.25; renderPage(pageNum); } });
最后一部分是fileInput
侦听器,用于检查change
事件以加载和呈现用户从本地文件资源管理器中选择的任何文件:
fileInput.addEventListener('change', async (event) => { const selectedFile = event.target.files[0]; if (selectedFile) { const fileURL = URL.createObjectURL(selectedFile); loadPDF(fileURL); } });
以下是添加功能的完整代码。打开已创建的viewer.js
文件,然后输入以下代码并保存:
document.addEventListener('DOMContentLoaded', function() { const pdfContainer = document.getElementById('pdf-container'); const prevPageBtn = document.getElementById('prev-page'); const nextPageBtn = document.getElementById('next-page'); const pageNumSpan = document.getElementById('page-num'); const goToPageInput = document.getElementById('go-to-page-input'); // Added input element const goToPageBtn = document.getElementById('go-to-page-btn'); // Added button element const zoomOutBtn = document.getElementById('zoom-out'); const zoomInBtn = document.getElementById('zoom-in'); const fileInput = document.getElementById('file-input'); let pdfDoc = null; let pageNum = 1; let scale = 1.0; async function renderPage(num) { const page = await pdfDoc.getPage(num); const viewport = page.getViewport({ scale: scale }); const canvas = document.createElement('canvas'); const canvasContext = canvas.getContext('2d'); canvas.height = viewport.height; canvas.width = viewport.width; pdfContainer.innerHTML = ''; pdfContainer.appendChild(canvas); const renderContext = { canvasContext, viewport, }; await page.render(renderContext); } async function loadPDF(url) { const loadingTask = pdfjsLib.getDocument(url); pdfDoc = await loadingTask.promise; renderPage(pageNum); pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`; } prevPageBtn.addEventListener('click', () => { if (pageNum > 1) { pageNum--; renderPage(pageNum); pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`; } }); nextPageBtn.addEventListener('click', () => { if (pageNum < pdfDoc.numPages) { pageNum++; renderPage(pageNum); pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`; } }); goToPageBtn.addEventListener('click', () => { // Event listener for the "Go to Page" button const targetPage = parseInt(goToPageInput.value); if (targetPage >= 1 && targetPage <= pdfDoc.numPages) { pageNum = targetPage; renderPage(pageNum); pageNumSpan.textContent = `Page ${pageNum} of ${pdfDoc.numPages}`; } }); zoomOutBtn.addEventListener('click', () => { if (scale > 0.25) { scale -= 0.25; renderPage(pageNum); } }); zoomInBtn.addEventListener('click', () => { if (scale < 3) { scale += 0.25; renderPage(pageNum); } }); fileInput.addEventListener('change', async (event) => { const selectedFile = event.target.files[0]; if (selectedFile) { const fileURL = URL.createObjectURL(selectedFile); loadPDF(fileURL); } }); });
这标志着使用PDF.js开发自定义PDF查看器的开发工作已经完成。
测试PDF查看器
使用PDF.js完成PDF查看器项目后,现在让我们尝试渲染一些PDF并看看它的效果。
如果你渲染一个PDF文件,它会看起来像这样:
PDF查看器测试
放大:
PDF查看器:放大
缩小:
PDF查看器:缩小
下一页:
PDF查看器:下一页
上一页:
PDF查看器:上一页
结论
在本文中,您了解了如何使用PDF.js库从头开始开发自定义PDF查看器。您可以在这些步骤的基础上构建许多不同版本的PDF查看器,并添加您需要的任何功能。
您可以在这个GitHub存储库中找到本文中使用的所有代码。