今回はフロントエンドでPDFを作成できるライブラリpdfmake(https://pdfmake.github.io/docs/0.1/)を紹介します。
このライブラリではテキストや画像、テーブルの挿入はもちろん、レイアウトもcssライクに調整したものをPDFとして出力して、ダウンロードや別ウィンドウで開いたりといったことが可能です。(作成例:pdfmake-sample )
デフォルトでは日本語は対応していないので、その設定方法も紹介します。
動作環境
筆者の以下の環境で検証を行いました。
Node.js | 14.17.3 |
React.js | 18.2.0 |
Next.js | 12.2.3 |
pdfmake | 0.2.5 |
pdfmakeのインストール
pdfmakeはビルドしたファイルへのパスをscriptタグに埋め込んだり、npmでインストールしてそのモジュールを呼び出して使用する方法がありますが
今回は、npmでインストールする方法を紹介します。
1 2 |
$ yarn add pdfmake $ yarn add @types/pdfmake // typescriptを使用している場合 |
日本語フォントの設定
pdfmakeのデフォルトのフォントはRobotoなので日本語は表示できません。
なので、日本語フォントを取得して、そのフォントを使用できるように設定しなければいけません。
日本語フォント(源真ゴシック)をダウンロード
こちらのサイト(http://jikasei.me/font/genshin/)から日本語フォントをダウンロードします。
サイトの一番下に「zip形式でダウンロード」とあるので、そちらからzipファイルを取得します。
フォントを静的なファイルディレクトリにコピー
zipを展開するとGenshin...とついたファイルが大量に入っているのがわかります。
この中で「GenShinGothic-Normal.ttf」と「GenShinGothic-Bold.ttf」を静的なファイルディレクトリにコピーします。
今回はNext.jsを使用しているのでpublic/fontsディレクトリにコピーしました。
1 2 3 4 |
public/ ├── fonts ├── GenShinGothic-Bold.ttf └── GenShinGothic-Normal.ttf |
pdfmakeの使用
では実際にpdfmakeの使用例をサンプルコードを載せながら紹介します。
まずはpdfmakeをインストールする必要があります。
1 2 3 4 |
import pdfMake from "pdfmake/build/pdfmake" import pdfFonts from "pdfmake/build/vfs_fonts" (<any>pdfMake).vfs = pdfFonts.pdfMake.vfs |
そして、先ほど取得した日本語フォントを読み込む設定をします。
1 2 3 4 5 6 7 8 |
pdfMake.fonts = { gensin: { normal: `${window.location.origin}/fonts/GenShinGothic-Normal.ttf`, bold: `${window.location.origin}/fonts/GenShinGothic-Bold.ttf`, italics: `${window.location.origin}/fonts/GenShinGothic-Normal.ttf`, bolditalics: `${window.location.origin}/fonts/GenShinGothic-Bold.ttf` } } |
設定が終わったら、pdfmakeを使用するラッパークラスを作成し、それを呼び出す部分を合わせて実装します。
ラッパークラス(utils/pdf.ts)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
import pdfMake from "pdfmake/build/pdfmake" import pdfFonts from "pdfmake/build/vfs_fonts" import { cloneDeep as _cloneDeep } from 'lodash' import { Content, TableCell, TDocumentDefinitions } from "pdfmake/interfaces" (<any>pdfMake).vfs = pdfFonts.pdfMake.vfs pdfMake.fonts = { gensin: { normal: `${window.location.origin}/fonts/GenShinGothic-Normal.ttf`, bold: `${window.location.origin}/fonts/GenShinGothic-Bold.ttf`, italics: `${window.location.origin}/fonts/GenShinGothic-Normal.ttf`, bolditalics: `${window.location.origin}/fonts/GenShinGothic-Bold.ttf` } } export class PDF { documentDefinitions: TDocumentDefinitions = { pageSize: 'A4', pageOrientation: 'portrait', pageMargins: [40, 60, 40, 60], defaultStyle: { font: 'gensin' }, content: [], images: {}, styles: { text: { margin: [0, 0, 3, 0] }, paragraph: { fontSize: 22, margin: [0, 10] }, table: { margin: [0, 10] } } } setTable(widths: (string | number)[], body: TableCell[][]) { const contentItem: Content = { table: { widths, body }, style: ['table'] } this.setContent(contentItem) } setText(text: string) { const contentItem: Content = text this.setContent(contentItem) } setParagraph(text: string) { const contentItem: Content = { text, style: ['paragraph'] } this.setContent(contentItem) } setImage(imageName: string, imageUrl: string, width: number, height: number) { const contentItem: Content = { image: imageName, width, height } this.setContent(contentItem) this.documentDefinitions.images![imageName] = imageUrl } generate(): pdfMake.TCreatedPdf { return pdfMake.createPdf(this.documentDefinitions) } private setContent(contentItem: Content) { const oldContent: Content = _cloneDeep(this.documentDefinitions.content) this.documentDefinitions.content = [oldContent, contentItem] } } |
このクラスをページのボタンクリックで呼び出してPDFをダウンロードできるようにします。
pages/index.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import type { NextPage } from 'next' import styles from '../styles/Home.module.scss' import { PDF } from '../utils/pdf' const Home: NextPage = () => { const handleGeneragePdf = () => { const pdf = new PDF() pdf.setParagraph('ユーザ情報') pdf.setTable([100, 120], [ [{ text: 'name', bold: true }, 'shiro'], [{ text: 'email', bold: true },'shiro@test.com'], [{ text: 'age', bold: true }, '35'], [{ text: 'phone', bold: true }, 'XXX-XXXX-XXXX'] ]) pdf.setParagraph('pdfmakeとは') pdf.setText('pdfmakeとはJavaScriptでPDFを生成することができるライブラリです') pdf.setText('フロントエンド、バックエンドの両方で使用可能です') pdf.setText('デフォルトでは日本語フォントは使用できないため、源真ゴシックフォントをダウンロードしてフォントの設定をする必要があります') pdf.setParagraph('サンプル画像') pdf.setText('画像の挿入は画像へのURL、ファイルシステムからの相対パス、dataURIなどが使用できます') pdf.setImage('sunset', 'https://images.unsplash.com/photo-1494548162494-384bba4ab999?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=880&q=80', 450, 300) pdf.generate().download() } return ( <div className={styles.page}> <button className={styles.page__button} onClick={handleGeneragePdf}> PDF作成 </button> </div> ) } export default Home |
コードの解説
pdfmakeでPDFを生成するざっくりとした手順は
- documentDefinitionsを定義
- documentDefinitionsのcontentに出力したい内容を格納
- pdfMake.createPdf(documentDefinitions)でPDFのオブジェクトを作成
- pdfMake.createPdf(documentDefinitions).open()で別タブで開く、pdfMake.createPdf(documentDefinitions).download()でダウンロード
となっています。
サンプルコード(utils/pdf.ts)ではクラスの初期化時にdocumentDefinitionsを作成して、setParagaphやsetTableなどのメソッドを通じてcontentのアイテムを追加しています。
そして、出力するタイミングでgenerateを呼び出してダウンロードしています。
documentDefinitionsの定義は公式(https://pdfmake.github.io/docs/0.1/document-definition-object/)を参考にすると良いと思います。
まとめ
- pdfmakeはフロントエンドでPDFを生成できるライブラリ
- 日本語フォントはデフォルトではないので、源真ゴシックをダウンロードして使用できるように設定が必要
- documentDefinitionsのオブジェクトをもとにPDFを生成する