[pdf.js] 手軽にPDFファイルをHTMLに埋め込めるWebComponentを作った

## PDFファイルを手軽にHTMLに埋め込みたい

PDFファイルをHTMLに埋め込む場合、従来だと`<iframe>`タグを使用するか、[pdf.js](http://mozilla.github.io/pdf.js/)を使用する方法、[Google Driveのプレビュー用URLを使う方法](https://stackoverflow.com/a/69671843)がありました。
ただし、それぞれ

- `<iframe>`タグを使用する方法 → PCのみ埋め込み可、スマホ非対応❌
- pdf.jsを使用する方法 → スマホに対応しているが、公式サイトからzipファイルを解凍し自分でサーバーに設置する必要がある（面倒）⚠️
- Google Driveのプレビュー用URLを使う方法 → 複数のPDFを埋め込むと挙動が不安定になってしまった＆公式でサポートされているものなのか不明❌

という制限がありました。
詳しくは[こちらの記事](https://developer.kaizenplatform.com/entry/2021/07/19/090000)によくまとめられています。

https://developer.kaizenplatform.com/entry/2021/07/19/090000

pdf.jsを使うとしても、できることなら`<script>`タグを一行差し込むだけで使えたら最高です。

## pdf.js を WebComponent 化し、ライブラリとして配布

以上を踏まえて、`<script>`タグを一行差し込むだけでpdf.jsが使えるライブラリを作りました！

使い方は次のようになります。

```html:使用例
<!-- scriptタグでライブラリを読み込み -->
<script src="https://deno.land/x/embed_pdf@v1.2.0/mod.js" type="module"></script>

<!-- src属性にpdfのファイルパスを指定する -->
<embed-pdf src="./path/to/file.pdf"></embed-pdf>
```

scriptタグを読み込んだ後、`<embed-pdf>`という名前のHTMLタグを設置することで、PDFを表示させることができます。

このHTMLファイルをブラウザで表示させると、以下のような感じでPDFファイルが埋め込まれているのが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/678625/37d73450-5046-c4f0-1d3d-3d783cab9123.png)

ライブラリの公開URLはこちら↓です。

https://deno.land/x/embed_pdf

ソースコードはGitHubで公開しています↓。

https://github.com/ayame113/embed-pdf-element

## ライブラリ作成にあたっての技術選定

作成にあたって、以下の条件で技術選定しました。

#### 条件1. npm install等なしで使えること

今回の自分のユースケースでは、導入対象のWebサイトは単純な静的ファイルサーバーであったため、npm等のツールチェーンや追加のビルド手順を（PDFを埋め込むためだけに）新たに導入したくないという事情がありました。
そのため、ライブラリはCDNからJSファイルを配信する形で提供することを検討しました。

#### 条件2. 公開が簡単なこと

npmに公開する場合、アクセストークンの管理（数か月ごとに更新など）が必要になり、面倒なので、これ以外の選択肢を検討しました。
また、TypeScriptによる型チェックを行いながらビルド不要でリリースできる方法を検討しました。

#### 条件3. バンドルサイズを小さくすること

今回はUI部品をライブラリ化していくので、Reactコンポーネントとして構築することも検討しました。
しかし、ライブラリのユーザーがReactを使用していない場合、このコンポーネントのためだけに追加で130KBをダウンロードさせることになってしまいます（これはVue.jsコンポーネントやPreactコンポーネントとして構築した時も同じ）。
そのため、これ以外の方法を検討しました。

#### 条件4. 全モダンブラウザで利用可能なこと

ただしIE対応はしないことにしました。

以上の条件から、以下のような構成でライブラリを作成・配信しました。

- Denoを使用して開発
  - ブラウザで動かすコードをそのまま型チェックにかけられる（＝ビルドステップ不要）
- https://deno.land/x で公開
  - ここに公開しておけば、ブラウザから直接scriptタグで読み込める（CDN代わりになる）
  - GitHub連携ができ、新しいリリースをGitHub上で作成すると自動で新しいバージョンがpublishされる
    - npm publishで必要だったアクセストークンの管理等は不要
  - ドキュメントも自動生成される
- WebComponentでコンポーネント化
  - 結果、このライブラリ単独のサイズは2.8KBになったので充分許容範囲

ライブラリのサイズについては、依存関係のpdf.jsのサイズが大きいという問題があります。そこで、

- モバイル端末の場合 → pdf.jsを使って表示
- それ以外の場合 → ブラウザがPDF埋め込み表示に対応しているため、それを使用

という方法でロードサイズを抑えました。

## まとめ

- PDFをHTMLに（全デバイス対応で）埋め込むのは意外と面倒
- scriptタグを1行差し込むだけでPDFが埋め込めたら最高→作った
- https://deno.land/x/embed_pdf で公開中

### おまけ：車輪の再発明

実は、同様の思想のライブラリはこれまでに結構作成されています。

- [A PDF Embed Web Component](https://www.raymondcamden.com/2022/10/02/a-pdf-embed-web-component)
- [telecomsante/pdf-viewer](https://www.webcomponents.org/element/telecomsante/pdf-viewer)
- [streetturtle/pdf-element](https://www.webcomponents.org/element/streetturtle/pdf-element)
- [Creating a Web Component with PDF.js](https://pspdfkit.com/blog/2019/create-web-component-pdfjs/)
- [PDF viewer widget](https://www.npmjs.com/package/pdf-viewer-web-component)
- [justinfagnani/pdf-viewer-element](https://github.com/justinfagnani/pdf-viewer-element)

しかし、6～8年前に開発が止まっている、古すぎてビルド手順が不明、最初の1ページしか表示できない、次ページボタンなどのUI要素は自分で作成する必要がある、pdf.jsは別途自分でダウンロードする必要がある、など今回のユースケースには合わなそうなものばかりでした。
そのため仕方なく自分が新たにライブラリを書いてリリースしておきました。