やりたかったこと
- PDF.js を導入して PDF Viewer を作成する
- IE 11 に対応する
- IDE で PDF.js のプロパティやメソッドに補完を効かせて開発する
結論
- PDF.js を node_module としてインストールする
- 開発時は node_module の PDF.js をインポートすることで IDE の補完を有効にする
- 本番時は Prebuilt (ES5-compatible) を HTML で読み込む
- Webpack の externals を用いて HTML で読み込む JavaScript ファイルと連携する
ディレクトリ構成
.
├── dist
│ └── js
│ └── main.js
├── src
│ └── js
│ └── main.js
├── vendor
│ └── pdfjs-2.4.456-es5-dist
│ └── 省略
├── README.md
├── babel.config.js
├── index.html
├── package-lock.json
├── package.json
└── webpack.config.js
PDF.js のダウンロードページ から Prebuilt (ES5-compatible) の Stable 版をダウンロードして vendor ディレクトリ配下に解凍します。バージョンは上記サンプルと異なる場合があります。
依存モジュール
npm i -D webpack webpack-cli terser-webpack-plugin babel-loader @babel/core @babel/preset-env glob pdfjs-dist
各ファイル
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Title</title>
</head>
<body>
<canvas id="the-canvas"></canvas>
<script src="vendor/pdfjs-2.4.456-es5-dist/build/pdf.js" defer></script>
<script src="dist/js/app.js" defer></script>
</body>
</html>
src/js/main.js
import "core-js/stable";
import "regenerator-runtime/runtime";
const pdfjsLib = require("pdfjs-dist");
pdfjsLib.GlobalWorkerOptions.workerSrc = "vendor/pdfjs-2.4.456-es5-dist/build/pdf.worker.js";
pdfjsLib.getDocument("/path/to/xxxx.pdf").promise.then(
(pdf) => {
console.log("pdf loaded");
pdf.getPage(1).then((page) => {
const viewport = page.getViewport({ scale: 1.0 });
const canvas = document.querySelector("#the-canvas");
canvas.width = viewport.width;
canvas.hieght = viewport.hieght;
const ctx = canvas.getContext("2d");
const renderCtx = {
canvasContext: ctx,
viewport: viewport,
};
page.render(renderCtx).promise.then(
() => {
console.log("page rendered");
},
(e) => {
console.error(e);
}
);
});
},
(e) => {
console.error(e);
}
);
console.log(pdfjsLib);
webpack.config.js
const path = require("path");
const glob = require("glob");
const TerserPlugin = require("terser-webpack-plugin");
module.exports = (env, argv) => {
const IS_PROD = argv.mode == "production";
console.log("NODE_ENV: ", env.NODE_ENV);
console.log("IS_PROD: ", IS_PROD);
console.log("--mode: ", argv.mode);
const srcDir = path.resolve(__dirname, "src", "js");
const entries = {};
glob.sync("*.js", { cwd: srcDir, ignore: [] }).forEach((filename) => {
const entry = filename.replace(".js", "");
entries[entry] = path.resolve(srcDir, filename);
});
console.log(entries);
return {
mode: argv.mode,
devtool: IS_PROD ? "none" : "source-map",
entry: entries,
output: {
path: path.resolve(__dirname, "dist"),
filename: "js/[name].js",
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
},
],
},
externals: [
{
"pdfjs-dist": "pdfjsLib",
},
],
optimization: IS_PROD
? {
minimizer: [
new TerserPlugin({
terserOptions: {
compress: { drop_console: true },
},
}),
],
}
: {},
};
};
babel.config.js
module.exports = (api) => {
api.cache(true);
return {
presets: [
[
"@babel/preset-env",
{
useBuiltIns: "usage",
corejs: {
version: "3",
},
debug: true,
},
],
],
};
};
package.json
{
"scripts": {
"dev": "webpack --env.NODE_ENV=development --mode development --progress",
"prod": "webpack --env.NODE_ENV=production --mode production --progress"
},
"devDependencies": {
"@babel/core": "^7.10.2",
"@babel/preset-env": "^7.10.2",
"babel-loader": "^8.1.0",
"core-js": "^3.6.5",
"pdfjs-dist": "^2.4.456",
"regenerator-runtime": "^0.13.5",
"terser-webpack-plugin": "^3.0.3",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
},
"dependencies": {}
}
試してみたこと
gulp × browserify × babelify
や webpack × babel
などで HTML で直接 PDF.js を読み込むのではなく、node_module の PDF.js をバンドルして es5 にトランスパイルする手法も試してみましたが、何かしらのエラーが出てしまいことごとく失敗に終わりました。
webpack の externals の設定を使うことで今回やりたかったことが実現できましたが、もっといい方法があれば知りたいです。