仕事で PDF をブラウザのページ中に埋め込んで表示したいことがあり、そのときに PDF.js を利用しました。
のちのち再び使いたくなったときのために、骨格を抜き出してまとめたものです。
サーバには Elixir の Phoenix framework を使っていますが、ほぼ静的なページで Phoenix らしいことは特に何もしていないので、Rails などでも同じように使える、はずです。
assets/js/MyPdf.js
PDF.js の利用をこの一つのファイルに分離しています。
内容としては PDF.js の example にあるものと同じですが、class を使った記述に書き換えています。
class MyPdf { constructor(canvas, afterRendered) { this.canvas = canvas this.context = this.canvas.getContext('2d') this.pageNumber = 1 this.rendering = false this.afterRendered = afterRendered } setAfterRendered(afterRendered) { this.afterRendered = afterRendered } loadDocument(path) { const renderPdf = (pdf) => { this.pdf = pdf this.getPage() } pdfjsLib .getDocument(path) .promise .then(renderPdf) } setPage(pageNumber) { if (1 <= pageNumber && pageNumber <= this.pdf.numPages && !this.rendering) { this.pageNumber = pageNumber this.getPage() } } nextPage() { this.setPage(this.pageNumber + 1) } prevPage() { this.setPage(this.pageNumber - 1) } getPage() { if (!this.rendering) { this.rendering = true this.pdf.getPage(this.pageNumber).then(this.renderPage.bind(this)) } } renderPage(page) { const viewport = page.getViewport({scale: 1}) this.canvas.height = viewport.height this.canvas.width = viewport.width page .render({ canvasContext: this.context, viewport }) .promise .then(() => { if (this.afterRendered) { this.afterRendered(this.pdf.numPages, this.pageNumber) } this.rendering = false }) } } export { MyPdf }
assets/js/app.js
MyPdf を利用します。 HTML の要素の操作はこちらで済ませて MyPdf ではそれらを意識せずに済むようにしています。
import "../css/app.scss" import "phoenix_html" import { MyPdf } from './MyPdf' const myPdf = new MyPdf(document.getElementById('the-canvas')) myPdf.setAfterRendered((numPages, pageNumber) => { document.getElementById('page').innerText = `${pageNumber} / ${numPages}` }) document.getElementById('next').addEventListener('click', myPdf.nextPage.bind(myPdf)) document.getElementById('prev').addEventListener('click', myPdf.prevPage.bind(myPdf)) myPdf.loadDocument('sample.pdf')
lib/pdfjs_web/templates/layout/app.html.eex
レイアウトテンプレートです。
CDN から PDF.js を読み込んでいます。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <title>Pdfjs · Phoenix Framework</title> <link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/> <script defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.9.359/pdf.min.js" integrity="sha512-U5C477Z8VvmbYAoV4HDq17tf4wG6HXPC6/KM9+0/wEXQQ13gmKY2Zb0Z2vu0VNUWch4GlJ+Tl/dfoLOH4i2msw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> </head> <body> <main role="main" class="container"> <%= @inner_content %> </main> </body> </html>
lib/pdfjs_web/templates/page/index.html.eex
PDF の表示領域とページ送りのボタンを配置したテンプレートです。
先に書いた通り、このテンプレートの要素と MyPdf を、 assets/js/app.js が結びつけています。
<button id="prev"><<</button> <span id="page"></span> <button id="next">>></button> <canvas id="the-canvas"></canvas>