Electron + Vue.jsで画面をPDF化する手順

公開日:2019/06/13 更新日:2019/06/13
Electron + Vue.jsで画面をPDF化する手順のサムネイル

はじめに

Electron + Vue.jsで構築したデスクトップアプリで画面をPDF化するための手順をまとめます。

できるようになること

以下のように、ボタンをクリックすることで、その画面をPDF化して開けるようになります。

generate-pdf-vue-electron-demo.gif

このデモのコードは以下のGitHubにあげています。

github.com

Electron + Vue.js generate PDF sample

前提と環境

以下の通りです。

  • OS : Ubuntu 18.04
  • Vue CLI : 3.8.2
  • Electron : 5.0.0

Electron + Vue.jsアプリについては、以下の手順に従って構築した前提となります。Vue CLI Plugin Electron Builderを使って作成したElectron + Vue.jsアプリが前提となりますが、その他のプラグインやツールを使用した場合でもメインのPDF化部分については共通になると思います。

www.virment.com

ElectronはWebアプリケーションの開発に使われるJavascriptやHTML、CSSなどの技術を用いてデスクトップアプリを開発できるフレームワークです。1つのソースでクロスプラットフォーム対応(Windows、Mac OS、Linux)のデスクトップアプリを開発することができます。この記事では、Ubuntu上でElectronとさらにVue.jsを用いたデスクトップアプリ開発をはじめるために必要なツールのインストール手順と、実際にデモアプリを起動させるまでをまとめます。

参考文献

PDF化部分を以下の記事を参考にさせて頂きました。ありがとうございます。

dev.to

Hi Everyone. I was caught up in a situation where I need...

PDF化の概要

Electronでは、IPC通信を使用することでとメイン処理部分(メインプロセス)と各画面内の処理(レンダラープロセス)が連携できます。PDF化の処理についても、画面上に用意したボタンをクリックすることで、メインプロセスにPDF化の処理をIPC通信でリクエストしてメインプロセスがPDFを作成します。PDF化の処理については、Electronデフォルトで使用可能なprintToPDFというAPIを使用します。以降では、メインプロセスとレンダラープロセスそれぞれの処理を説明します。

修正するファイルについて

Vue CLI Plugin Electron BuilderでElectronアプリを構築した場合、デフォルトではアプリのディレクトリ直下にsrcというディレクトリがあり、srcの中身は以下のようになっています。ただし、以下はVuex、Router、TypeScriptを有効化している場合です。

src
.
├── App.vue
├── assets
├── background.ts
├── components
├── main.ts
├── router.ts
├── shims-tsx.d.ts
├── shims-vue.d.ts
├── store.ts
└── views

上記はTypeScriptを使用するよう指定したために拡張子がtsになっています。また、shims-tsx.d.tsshims-vue.d.tsもTypeScriptを使用するよう指定した場合のみ作成されます。これらのファイルは、IDEに対して.vueファイルとjsxファイルを扱えるようにするためのファイルです。 TypeScriptを使用するよう指定していない場合は、srcディレクトリは以下のようになっていると思います。

src
.
├── App.vue
├── assets
├── background.js
├── components
├── main.js
├── router.js
├── store.js
└── views

background.tsbackground.js)がメインプロセスのファイルになり、レンダラープロセスのファイルはviews配下にあるVueファイルになります。 この記事では、TypeScriptを使っている前提としますが、JavaScriptの場合と比べて処理内容については変わらず型の指定などの違いぐらいになります。

レンダラープロセス側の処理

Vue CLI Plugin Electron Builderを使って作成したアプリのデフォルト状態のHome.vueに少しだけ追記します。以下が追記したコードです。主に、クリックによってcreatePDFという適当に付けた名前のメソッドを呼び出すボタンを追加しています。createPDFによってメインプロセスにPDF化をリクエストします。 なお、Home.vueがない場合は、他の適当な.vueファイルで試してみてください。

src/views/Home.vue
<template>
  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js + TypeScript App !!"/>
     <button id='print-pdf' @click="createPDF()">Convert to PDF</button>
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src
import { ipcRenderer } from 'electron'

@Component({
  components: {
    HelloWorld
  }
})
export default class Home extends Vue {
  pdfname: string = "mypdf"
  // ボタンクリックでPDF作成をメインプロセスにリクエスト
  createPDF () {
    ipcRenderer.send('print-to-pdf', this.pdfname)
  }
  mounted () {
    // mountでメインプロセスからのIPC通信待機
    ipcRenderer.on('wrote-pdf', (event: any, path: any) => {
      const msg = `PDFを ${path} に作成しました。`
      console.log(msg)
    })
  }
}
</script>

次にメインプロセス側の処理を説明します。

メインプロセス側の処理

メインプロセスのbackground.tsにPDF化部分の処理を追記します。具体的には、以下をbackground.tsの末尾に追記します。また、importでもいくつか追記しています。

src/background.ts
'use strict'

import { app, protocol, BrowserWindow, ipcMain, shell } from 'electron'
import {
  createProtocol,
  installVueDevtools
} from 'vue-cli-plugin-electron-builder/lib'
import fs from 'fs'
import os from 'os'
import path from 'path'

const isDevelopment = process.env.NODE_ENV !== 'production'

(...途中省略...)

// 以下追記
// PDF作成処理
ipcMain.on('print-to-pdf', (event: any, arg: any) => {
  // 作成するPDFの保存パスを指定
  const pdfPath = path.join(os.tmpdir(), arg + '.pdf')
  const win = BrowserWindow.fromWebContents(event.sender)

  // Electronデフォルトで使用できるprintToPDFを使用する
  win.webContents.printToPDF({}, (error, data) => {
    if (error) return console.log(error.message)

    fs.writeFile(pdfPath, data, err => {
      if (err) return console.log(err.message)
      shell.openExternal('file://' + pdfPath)
      event.sender.send('wrote-pdf', pdfPath)
    })
    
  })
});

PDF化部分については、Electronでデフォルトで使用できるprintToPDFを使用しています。printToPDFについては以下の公式ドキュメントに詳細が載っています。

electronjs.org

Render and control web pages.

なお、PDF化に関するオプションも指定できるようになっています。次節で説明します。

PDF化のオプションについて

以下のようにprintToPDFにオプションを渡すことができます。

src/background.ts
// PDF作成処理
ipcMain.on('print-to-pdf', (event: any, arg: any) => {
  // 作成するPDFの保存パスを指定
  const pdfPath = path.join(os.tmpdir(), arg + '.pdf')
  const win = BrowserWindow.fromWebContents(event.sender)

  // printToPDFにオプションを渡す
  win.webContents.printToPDF({
      marginsType: 0,
      pageSize: 'A4',
      printBackground: false,
      printSelectionOnly: false,
      landscape: false
    }, (error, data) => {
    if (error) return console.log(error.message)

    fs.writeFile(pdfPath, data, err => {
      if (err) return console.log(err.message)
      shell.openExternal('file://' + pdfPath)
      event.sender.send('wrote-pdf', pdfPath)
    })
    
  })
});

指定できるオプションは以下のようになっています。

オプション名 内容
marginsType マージンの指定。0:デフォルトマージン, 1:マージンなし、2:最小マージン。 なお、具体的なマージン値については未確認ですが、これはChrominum(Chrome)での印刷時に指定できるマージンと同じものです。
pageSize 用紙サイズとして次の値を指定可能。A3A4A5LegalLetterTabloid。また、具体的な幅と高さも指定可能。例えば、{ width: 100000, height: 100000 }を渡すと幅と高さが100000マイクロメートル(10cm)の用紙でPDF化します。マイクロメートルなので注意が必要です。
printBackground CSSで指定しているバックグラウンドを印刷するかの指定。falsetrueで指定。
printSelectionOnly 選択範囲のみを印刷するかの指定。falsetrueで指定。
landscape 印刷の向きを指定。falseで横向き、trueで縦向き。
page-break-before 常に新しいページで印刷するかの指定。指定したい場合はpage-break-before: alwaysをオプションに加える。

まとめ

ElectronはChrominumがベースとなっているためPDF化についてデフォルトで対応できとても簡単でした。

開発アプリ

nanolog.app

毎日の小さな出来事をなんでも記録して、ログとして残すためのライフログアプリです。