JavaScriptでPDFのサムネールを出す

らら
らら

はじめに

pdfのサムネールを自動でって話があって・・

perlとかならImageMagickとかあるけど・・

JavaScriptでできないかしらべてみた。

pdfThumbnails

下記が見つかった。。

https://github.com/scandel/pdfThumbnails

内部的には

PDF.jsが必要ぽい。

https://mozilla.github.io/pdf.js/

注意なのが現在 2024年でバージョン4系が公開されているが、pdfThumbnailsではバージョン3系で動作した。

バージョン4だとmjs系なので、pdfjsLibあたりが定義がいるとか。そんなとこ・・pdfThumbnailsではバージョン2以上と記載もあるが

バージョン3の最新で筆者は行った。。

下記を解凍して、ディレクトリ名をpdfjsにしてその中にbuildが入る感じで・・

pdfjs-3.11.174-dist.zip

デモソースは下記に変更・・


<script src="pdfThumbnails.js" data-pdfjs-src="pdfjs/build/pdf.js"></script>

PDF.jsバージョン4から拡張子がECMAScript Modules (mjs)になっているので、クライアントサイドでつかうと変更とかめんどうなので・・

アパッチに下記とか・・


AddType text/javascript mjs

面倒なので・・わかる人だけ・・バージョン4・・わからない場合は。。バージョン3を・・

nodeがわかるあにきたちは。。バージョン4で・・

関連

PDF.jsのviewer.htmlの応用
https://www.omakase.net/blog/2021/02/pdfjsviewerhtml.html

V3使いかた


<script src="/path/to/pdfThumbnails.js" data-pdfjs-src="pdfjs/build/pdf.js"></script>

手法的には、遅延読み込みLazy Loadとか競合しそうかな。


<img data-pdf-thumbnail-file="/my/file.pdf" src="pdf.png">

生成する画像サイズの指定は、下記で・・


<img data-pdf-thumbnail-file="/my/file.pdf" data-pdf-thumbnail-width="200">
<img data-pdf-thumbnail-file="/my/file.pdf" data-pdf-thumbnail-height="150">

data-pdf-thumbnail-fileにPDFファイル名とパス

srcは表示されない場合の画像

pdfThumbnails.js

ソースは、PDFを1ページ目だけキャンパスに表示させて、toDataURL(base64)でimg srcに入れる感じ・・


/**
 * Find all img elements with data-pdf-thumbnail-file attribute,
 * then load pdf file given in the attribute,
 * then use pdf.js to draw the first page on a canvas, 
 * then convert it to base64,
 * then set it as the img src.
 */
var createPDFThumbnails = function(){
	var worker = null;
	var loaded = false;
	var renderQueue = [];
	// select all img elements with data-pdf-thumbnail-file attribute
	var nodesArray = Array.prototype.slice.call(document.querySelectorAll('img[data-pdf-thumbnail-file]'));
	if (!nodesArray.length) {
		// No PDF found, don't load PDF.js
		return;
	}
	if (!loaded && typeof(pdfjsLib) === 'undefined') {
		var src = document.querySelector('script[data-pdfjs-src]').getAttribute('data-pdfjs-src');
		if (!src) {
			throw Error('PDF.js URL not set in "data-pdfjs-src" attribute: cannot load PDF.js');
		}
		var script = document.createElement('script');
		script.src = src;
		document.head.appendChild(script).onload = renderThumbnails;
		loaded = true;
	} else {
		renderThumbnails();
	}
	function renderThumbnails() {
		if (!pdfjsLib) {
			throw Error("pdf.js failed to load. Check data-pdfjs-src attribute.");
		}
		nodesArray.forEach(function(element) {
			if (null === worker) {
				worker = new pdfjsLib.PDFWorker();
			}
			var filePath = element.getAttribute('data-pdf-thumbnail-file');
			var imgWidth = element.getAttribute('data-pdf-thumbnail-width');
			var imgHeight = element.getAttribute('data-pdf-thumbnail-height');
			pdfjsLib.getDocument({url: filePath, worker: worker}).promise.then(function (pdf) {
				pdf.getPage(1).then(function (page) {
					var canvas = document.createElement("canvas");
					var viewport = page.getViewport({scale: 1.0});
					var context = canvas.getContext('2d');
					if (imgWidth) {
						viewport = page.getViewport({scale: imgWidth / viewport.width});
					} else if (imgHeight) {
						viewport = page.getViewport({scale: imgHeight / viewport.height});
					}
					canvas.height = viewport.height;
					canvas.width = viewport.width;
					page.render({
						canvasContext: context,
						viewport: viewport
					}).promise.then(function () {
						element.src = canvas.toDataURL();
					});
				}).catch(function() {
					console.log("pdfThumbnails error: could not open page 1 of document " + filePath + ". Not a pdf ?");
				});
			}).catch(function() {
				console.log("pdfThumbnails error: could not find or open document " + filePath + ". Not a pdf ?");
			});
		});
	}
};
if(document.readyState === "complete" || (document.readyState !== "loading" && !document.documentElement.doScroll)) {
	createPDFThumbnails();
} else {
	document.addEventListener("DOMContentLoaded", createPDFThumbnails);
}

さいごに

実際動かしてみると・・PDFのサイズがでかいものをたくさんリストするような使い方だと、サムネール表示が遅いです。

その場合、ブラウザー側のメモリ、スペックも必要かな・・って感じ

画面設計によっては使い道はありそう。

関連記事