TypeScriptでpdfjsを使ってPDFの先頭ページを表示しようと試行錯誤した話

# TL;DR
* npmで`pdfjs-dist`をインストールして使ったら失敗した。
* 公式サイトからstable版をダウンロードして来たら成功した。

# pdfjs

pdfjsの説明は、[本家gitのページ](https://github.com/mozilla/pdf.js#PDF.js)をgoogle先生の翻訳を参照します。

> PDF.jsはHTML5で構築されたPortable Document Format（PDF）ビューアです。
> 
> PDF.jsはコミュニティ主導型であり、Mozilla Labsによってサポートされています。私たちの目標は、PDFを解析してレンダリングするための汎用のWeb標準ベースのプラットフォームを作成することです。

PDFを表示しようとした場合、PDFを読み込んでghostscriptで画像に変換して画像を表示してもいいのですが、勉強も兼ねてPDF.jsを使用して表示できないか試しました。

## npmでpdfjs-distをインストール

「npm pdfjs」で検索すると、2018/5/29時点では先頭に**pdfjs-dist**がきて、次に**pdfjs**が来ています。
それぞれのページを開いてrepositoryを確認すると、**pdfjs-dist**がmozilla公式のようです。
実際、pdf.jsのgitのページの中にも、NPMとBowerには**pdfjs-dist**という名前で登録されている旨の記載があります。

というわけでpdfjs-distと、TypeScriptで使いたいので@types/pdfjs-distを合わせてインストール。

```shell
$ npm install --save pdfjs-dist @types/pdfjs-dist
```

今回インストールされたのは、

* pdfjs-dist@2.0.489
* @types/pdfjs-dist@0.1.2

バージョンに開きがあるのが非常に気になりますが、@types/pdfjs-distのnpmのサイトを確認したところ、何年も更新が止まっているわけではないみたいなので、とりあえず使ってみます。

## pdfjs-distを使う

### 試行錯誤1
なにも考えずに使ってみます。

```ts
import { PDFJS } from "pdfjs-dist";

PDFJS.getDocument( "hoge.pdf" )
	.then( ( pdfDoc: PDFDocumentProxy ) => {
		...
	} );
```

結果は以下の通り。

```console
Uncaught TypeError: Cannot read property 'getDocument' of undefined
```

なんでやねーん、というわけでこの書き方では失敗しました。
それにしても関連記事、少ないです...皆さんあまりpdfjs-distとTypeScriptの組み合わせは使わないのかな。

### 試行錯誤2
修正版はこちら。

```ts
import { PDFDocumentProxy, PDFJSStatic } from "pdfjs-dist";

const PDFJS: PDFJSStatic = require( "pdfjs-dist" );

PDFJS.getDocument( "hoge.pdf" )
	.then( ( pdfDoc: PDFDocumentProxy ) => {
		...
	} );
```

これは動きました。
よしよし。

しかし、実際にhtmlから呼び出してみると、コンソールにはWarningが表示されてしまいました。

```console
Warning: Error during font loading: The CMap "baseUrl" parameter must be specified, ensure that the "cMapUrl" and "cMapPacked" API parameters are provided.
```

表示されたPDFと、Acrobatで表示したPDFを見比べると、文字が表示されていません。
とりあえず、cMapUrlとcMapPackedを確認するように言われているので確認します。

cMapUrlはcmapsのディレクトリまでのパスを指定します。
cmapsは、`node_modules/pdfjs-dist/cmaps`を指定しました。

cMapPackedは、cmapの拡張子がbcmapだったらtrueのようです。

```ts
import { PDFDocumentProxy, PDFJSStatic } from "pdfjs-dist";

const PDFJS: PDFJSStatic = require( "pdfjs-dist" );
PDFJS.cMapUrl = "パスを入力";
PDFJS.cMapPacked = true;

PDFJS.getDocument( "hoge.pdf" )
	.then( ( pdfDoc: PDFDocumentProxy ) => {
		...
	} );
```

結果やいかに。

```console
Warning: Error during font loading: The CMap "baseUrl" parameter must be specified, ensure that the "cMapUrl" and "cMapPacked" API parameters are provided.
```

えぇぇ...。

Chromeの開発者ツールで中身をのぞいてみますが、原因特定できず。
途中まではcMapUrlに値が入っているのに、いざcMapUrlを使っている部分ではundefinedになっているという。
誰かわかる人、教えてください...。

### 試行錯誤3
npmを使ったインストールにこだわるのはやめました。
[公式サイト](https://mozilla.github.io/pdf.js/)からstable版をダウンロードして来て、pdf.jsをhtmlで指定することにします。

DLしたzipファイルを解凍して、必要ファイル（cmaps/、pdf.js、pdf.worker.js）をコピーして以下のような構成にします。

```
cmaps/
index.html
index.js
pdf.js
pdf.worker.js
```

htmlにscriptタグを追加。

```html
...
<script src="./pdf.js"></script>
...
```

ts側で読み込み。

```tsx
const PDFJS: PDFJSStatic = (window as any).PDFJS;

PDFJS.cMapUrl = "cmaps/";
PDFJS.cMapPacked = true;
```

はい、動きました。

うーん。
pdfjs-distがなぜ動かなかったのかわからない。

# PDFを表示するTypeScriptのコード

今回の用途としてはサムネイル表示したかっただけなので、コードとしては先頭ページの表示のみです。
ページ遷移とかは考慮していません。

```ts
const PDFJS: PDFJSStatic = (window as any).PDFJS;

PDFJS.cMapUrl = "cmaps/";
PDFJS.cMapPacked = true;

async renderThumbnail(
	canvas: HTMLCanvasElement,
	size: SizeType,
	buff: ArrayBuffer
): Promise<any> {
	const pdfDoc = await PDFJS.getDocument( new Uint8Array( buff ) );
	const page = await pdfDoc.getPage( 1 );
	let viewport = page.getViewport( 1, 0 );
	const larger = viewport.height > viewport.width ? viewport.height : viewport.width;
	const scale = size / larger;
	const rotate = 0;

	viewport = page.getViewport( scale, rotate );
	const ctx = canvas.getContext( "2d" )!;

	const renderContext: PDFRenderParams = {
		canvasContext: ctx,
		viewport: viewport
	};
	canvas.height = viewport.height;
	canvas.width = viewport.width;
	this.setStyle( {
		width: viewport.width + "px",
		height: viewport.height + "px"
	} );

	page.render( renderContext );
}
```