仕事上でPDFを使う機会は多いのではないでしょうか。

昔は専用のソフトウェアを購入して作成するのが一般的でしたが、今ではOS標準の機能でPDFを作成できたりします。

今回はMonacaアプリ上でPDFを作成してみます。クライアントサイドでの生成できるので、サーバーを用意することなく手軽に利用できます。

利用するライブラリ

今回、次のライブラリを利用します。

PDFMakeは特に肝になります。

html-to-pdfmakeは、HTMLをPDFMakeに合わせたフォーマットに変換するライブラリです。

markedは、Markdown記法をHTMLに変換します。

なお、UIフレームワークとしてFramework7を利用しています。

PDFMakeを日本語対応させる

まず行うべきことなのですが、PDFMakeはデフォルトでは日本語フォントに対応していません。

そのため、次の手順でのカスタマイズが必要となります。なお、実行にはNode.jsが必要になりますのでインストールしてください。

  1. PDFMakeの取得
  2. 日本語フォントの用意
  3. カスタマイズ版PDFMakeの生成

PDFMakeの取得

PDFMakeを公式リポジトリより取得します。

$ git clone https://github.com/bpampuch/pdfmake.git
$ cd pdfmake

日本語フォントの用意

日本語フォントを用意します。無償フォントのIPAフォントがお勧めです。

公式サイトよりダウンロードしたら、TTFファイルをPDFMakeの examples/fonts ディレクトリに保存します。

リリース IPA Fonts/IPAex Fonts 4書体パック_IPAフォント(Ver.003.03) - IPAフォント - OSDN

カスタマイズ版PDFMakeの生成

コマンドプロンプトやターミナルにて、先程git cloneしたフォルダ内で、ビルドを実行します。
次のコマンドでビルドが実行されます。

$ npm run build

これで build フォルダの中に pdfmake.min.jsvfs_fonts.js が作成されました。

これをMonacaアプリの www/js フォルダの中に入れます。

www/index.htmlの編集

index.htmlでは、プロジェクトに必要な次のライブラリを読み込みます。

<script src="js/pdfmake.min.js"></script>
<script src="js/vfs_fonts.js"></script>
<script src="https://cdn.jsdelivr.net/npm/html-to-pdfmake/browser.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>

PDFMakeの初期化

js/app.js にてPDFMakeの初期化をします。 ipa というキーで、IPAフォントを指定し、初期化を行います。

pdfMake.fonts = {
  ipa: {
    normal: "ipagp.ttf",
    bold: "ipagp.ttf",
  }
};

ベースとなるHTML

今回は /pdf/ というルーティングに対して pages/pdf.html を開くように設定しています。

以下は js/routes.js の内容です。

const routes = [
  {
    path: "/",
    url: "./index.html",
  },
  {
    path: "/pdf/",
    componentUrl: "./pages/pdf.html",
  },
  // Default route (404 page). MUST BE THE LAST
  {
    path: "(.*)",
    url: "./pages/404.html",
  },
];

そして pages/pdf.html の概要は次のようになっています。

設定しているアクションは1つです。

  • PDFファイルを生成するための create アクション

#id のテキストエリアに対して、Markdown記法に沿ったテキストを記述します。

<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner sliding">
        <div class="title">PDF</div>
      </div>
    </div>
    <div class="page-content">
      <div class="list no-hairlines-md">
        <ul>
          <li class="item-content item-input">
            <div class="item-inner">
              <div class="item-input-wrap">
                <textarea id="markdown" class="resizable"></textarea>
              </div>
            </div>
          </li>
        </ul>
        <div class="block block-strong">
          <div class="row">
            <button class="button col" @click="${create}">作成</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
  export default (props, { $store, $onMounted }) => {
    // ここに後述するコードを書きます
    return $render;
  }
</script>

PDFを生成する

PDFファイルを生成する create 関数の内容です。

const create = async () => {
  // この中に記述します
}

MarkdownをHTMLへ変換する

#markdown に書かれた内容をHTMLに変換します。

// MarkdownからHTMLに変換
const html = marked.parse($("#markdown").val());

このHTMLを htmlToPdfmake 関数を使ってPDFMakeの内容に変換します。

// HTMLをPDFMakeの形式に変換
const content = htmlToPdfmake(html);

HTMLからPDFを生成する

PDFを生成します。 js/app.js にて定義したスタイルを defaultStyle として指定します。 content は先ほど作成したPDFMakeに沿った内容です。

Blob型で取得する際には createPdf 関数を実行した後、getBlob を実行します。

// PDFのスタイル設定
const dd = {
  defaultStyle: {
    font: "ipa"
  },
  content
};

// PDFを生成
const pg = pdfMake.createPdf(dd);
// 生成したPDFをBlobで取得
const blob = await pg.getBlob();

PDFファイルを保存する

生成したPDFデータは、アプリのローカルファイルとして今回は保存しましょう。

これにはファイル操作 プラグインが必要となります。コードは次の流れで処理を実行していきます。

// ファイルシステムのリクエスト
const fs = await requestFileSave();

// 書き込み用に新規ファイルのリクエスト
const file = await getFile(fs);

// PDFデータをファイルへ書き込み
const res = await writeFile(file, blob);

// 保存完了ダイアグラム
alert(ファイルを保存しました ${file.nativeURL})

次に各処理について、詳細を説明していきます。

出力するPDFのファイル名は export.pdf に固定しています。

// ファイルシステムのリクエスト
const requestFileSave = () => {
    return new Promise((res, rej) => {
        window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, res, rej);
    });
};

// 書き込み用ファイルのリクエスト
const getFile = (fs) => {
    return new Promise((res, rej) => {
        fs.root.getFile("export.pdf", {
            create: true, exclusive: false
        }, res, rej);
    });
};

// ファイル書き込み
const writeFile = (file, data) => {
    return new Promise((res, rej) => {
        file.createWriter(fileWriter => {
            fileWriter.onwriteend = res;
            fileWriter.onerror = rej;
            fileWriter.write(data);
        });
    });
};

まとめ

PDFMakeの記法を覚えれば、かなり詳細にこだわったPDFが生成できます。

簡易的に出力するのであれば、今回のようにMarkdownを経由するととても簡単にPDFを生成できます。

ぜひPDFMakeの使い方を覚えて、MonacaアプリからPDFファイルを作成してください。

次の記事では、生成したPDFをアプリ内で表示する機能を作成していきます。