今回は前回に続きGo言語のライブラリgopdfを使ってPDFを扱う方法を紹介します。前回は基本的なgopdfの使い方を確認しました。今回はひな形の領収書PDFに金額や宛名を書き込んで領収書を動的に生成してみましょう。

  • ひな形PDFを元にして領主書を生成するツールを作りましょう

    ひな形PDFを元にして領主書を生成するツールを作りましょう

最初に新規プロジェクトを作成しよう

前回は、gopdfをインストールして簡単なPDFを作成する方法を確認しました。復習をこめて、go1.16に対応したプロジェクトの生成からモジュールのインストールまで作業してみましょう。

最初に適当なフォルダで、コマンドライン(WindowsならPowerShell、macOSならターミナル)を開いて、以下のコマンドを実行して新規モジュールを作成しましょう.今回は、writepdfという名前にしてみます。

go mod init writepdf

すると、go.mod というファイルが作成されます。そして、このファイルをテキストファイルなどで開いて、gopdfを利用する旨(2行目の一行)を追記します。

module writepdf
require github.com/signintech/gopdf v0.9.15

その後、以下のコマンドを実行して、gopdfモジュールのインストールを行います。

go mod download github.com/signintech/gopdf
go mod tidy

フォントとPDFのひな形を用意しよう

それから、前回と同じくPDF作成に日本語フォントを使います。IPAexフォントのIPAexゴシックをダウンロードしてプログラムと同じフォルダにコピーしておきましょう。

また、今回は領収書のひな形となるPDFに書き込みを行います。ここでは、以下のようなひな形となるPDFファイル「template.pdf」を用意しました。こちらからダウンロードできます。ダウンロードしたら「template.pdf」を同じフォルダにコピーしましょう。

  • 領収書のひな形PDF

    領収書のひな形PDF

すると、ディレクトリのファイルは以下のようになります。

  • プロジェクトのディレクトリ内のファイル

    プロジェクトのディレクトリ内のファイル

ひな形PDFを読み込むプログラムを作ろう

そして、まずはひな形PDFを読み込むだけのプログラムを作りましょう。やりたい事としては、ひな形PDFにテキストを書き込むのですが、実際の処理としては、新規PDFを作成してそこに既存のPDFを読み込んで表示するのです。なお、ひな形を読み込んだだけだと、ひな形のPDFと同じで区別が付きにくいので、左上から右下に一本罫線を入れて、ひな形を異なることを示しました。

以下のような「writepdf.go」というプログラムを作成します。

package main

import (
    "github.com/signintech/gopdf"
)

func main() {
    // gopdf のオブジェクトを作成 --- (*1)
    pdf := gopdf.GoPdf{}
    // ひな形に合わせて用紙は横向きのA4とする --- (*2)
    A4 := *gopdf.PageSizeA4
    A4Yoko := gopdf.Rect{W: A4.H, H: A4.W}
    pdf.Start(gopdf.Config{PageSize: A4Yoko})
    pdf.AddPage()
    // テンプレートファイルをインポート --- (*3)
    template := pdf.ImportPage("./template.pdf", 1, "/MediaBox")
    // インポートしたPDFをPDFで適用する --- (*4)
    pageH := 1080 * (A4Yoko.W / 1920) // テンプレートサイズ (1920x1080)
    pdf.UseImportedTemplate(template, 0, 0, A4Yoko.W, pageH)
    // 適当に線を入れる
    pdf.Line(0, 0, A4Yoko.W, A4Yoko.H)
    // PDFをファイルに書き出す --- (*5)
    pdf.WritePdf("ryosyusyo.pdf")
}

続いて以下のコマンドを実行しましょう。すると、「ryosyusyo.pdf」というPDFが生成されます。

go run .

生成されたPDFを開くと次のようになっています。

  • 新規PDFに既存のPDFを取り込んでみたところ

    新規PDFに既存のPDFを取り込んでみたところ

プログラムを確認してみましょう。(*1)の部分ではgopdfのオブジェクトを作成します。そして、(*2)では用紙設定を行います。ここでは、横向きのA4にしたいのですが、用紙サイズは縦向きしか用意されていないので、敢えてA4Yokoという用紙サイズを定義しました。

そして、(*3)ではひな形ファイルをインポートします。ImportPageメソッドを使って取り込みます。なお、どんな形式のPDFでも取り込めるという訳ではないようです。基本的なPDFは取り込めるようですが読めないこともあるようです。その場合は、別のツールを使ってPDFの形式を単純なものに変換したり、あるいは、画像に変換したりすることで回避できます。

ところで、インポートしたPDFは、実際に配置してはじめて見えるようになります。(*4)のUseImportedTemplateメソッドを使ってページに配置します。このとき、配置する座標を指定する必要があります。ここでは新規PDFにぴったり配置します。その際、縦横比が崩れないようにサイズを計算して貼り付けましょう。ここでひな形として用意したPDFは1920×1080ポイントでした。そのため、これをA4横サイズに収まるようにサイズを計算しました。そして、最後(*5)でPDFファイルに書き出します。

任意のテキストを差し込もう

PDFのひな形を読み込んだら、後はテキストを差し込むようなプログラムを作るだけです。とは言え、テキストをぴったり差し込みたい座標を調べるのも簡単ではありません。そこで、10ptごとに罫線を引いてだいたいの位置を求められるようにしましょう。その上でテキストを差し込みます。

同じく「writepdf.go」を次のように書き換えましょう。

package main

import (
    "github.com/signintech/gopdf"
)

func main() {
    // gopdf のオブジェクトを作成 --- (*1)
    pdf := gopdf.GoPdf{}
    // ひな形に合わせて用紙は横向きのA4とする
    A4 := *gopdf.PageSizeA4
    A4Yoko := gopdf.Rect{W: A4.H, H: A4.W}
    pdf.Start(gopdf.Config{PageSize: A4Yoko})
    pdf.AddPage()
    // テンプレートファイルをインポート --- (*2)
    template := pdf.ImportPage("./template.pdf", 1, "/MediaBox")
    pageH := 1080 * (A4Yoko.W / 1920)
    pdf.UseImportedTemplate(template, 0, 0, A4Yoko.W, pageH)
    // グリッド線を入れる --- (*3)
    drawGrid(&pdf, &A4Yoko)
    // TTFフォントを取り込む --- (*4)
    err := pdf.AddTTFFont("ipaexg", "./ipaexg.ttf")
    if err != nil {
        panic(err)
    }
    // 文字を書き込む --- (*5)
    // 宛名
    pdf.SetFont("ipaexg", "", 28)
    drawText(&pdf, 300, 140, "山田 太郎")
    // 日付
    pdf.SetFont("ipaexg", "", 15)
    drawText(&pdf, 600, 100, "3")  // 年
    drawText(&pdf, 635, 100, "10") // 月
    drawText(&pdf, 676, 100, "15") // 日
    // 金額
    pdf.SetFont("ipaexg", "", 28)
    drawText(&pdf, 280, 200, "¥ 15,800 -")
    // PDFをファイルに書き出す --- (*6)
    pdf.WritePdf("ryosyusyo.pdf")
}

func drawText(pdf *gopdf.GoPdf, x float64, y float64, s string) {
    pdf.SetX(x)
    pdf.SetY(y)
    pdf.Cell(nil, s)
}

func drawGrid(pdf *gopdf.GoPdf, page *gopdf.Rect) {
    ww := 10.0
    for i := 1; i < int(page.W/ww); i++ {
        if i%10 == 0 {
            pdf.SetLineWidth(0.8)
            pdf.SetStrokeColor(50, 50, 100)
        } else {
            pdf.SetLineWidth(0.3)
            pdf.SetStrokeColor(100, 100, 130)
        }
        x, y := float64(i)*ww, float64(i)*ww
        pdf.Line(x, 0, x, page.H)
        pdf.Line(0, y, page.W, y)
    }
}

同じく「go run .」のコマンドを実行すると、「ryosyusyo.pdf」が生成されます。これを開くと次のようになります。10ptずつに罫線が引かれるので、テキストを差し込む座標が分かりやすくなっています。

  • 罫線を引いて座標を調べやすくしたところ

    罫線を引いて座標を調べやすくしたところ

プログラムを確認してみましょう。(*1)の部分ではA4横サイズでgopdfをセットアップします。そして、(*2)でひな形PDFを取り込みます。(*3)の部分、およびプログラム末尾のdrawGrid関数で10ptごとにグリッド線を描画します。

そして、(*4)の部分ではTTFフォントを取り込み「ipaexg」という名前で利用できるように指定します。(*5)以降の部分で宛名や日付、金額を書き込みます。最後(*6)でPDFを書き出します。

グリッド線を消して完成

このようにして、テキストの位置を調整したら、プログラムの(*3)に記述したdrawGrid関数の呼出を消します。

  • グリッド線を消したら領収書の完成

    グリッド線を消したら領収書の完成

あとは、CSVファイルなどデータファイルを読み込んでPDFを生成するような処理を書き加えれば、立派な領収書生成ツールの完成するでしょう。

まとめ

以上、前回と今回でPDFを扱うプログラムを作ってみました。ここで見たとおりgopdfは必要な機能を網羅していながらも、シンプルで使い勝手が良いと感じました。

Go言語で作っておけば配布も簡単なのが良い点です。なおGo言語1.16では配布するバイナリにプログラムで利用するリソースファイルも添付できるようになりました。この仕組みを使えばフォントファイルやひな形ファイルも実行ファイルに含められます。配布がより便利になるでしょう。

自由型プログラマー。くじらはんどにて、プログラミングの楽しさを伝える活動をしている。代表作に、日本語プログラミング言語「なでしこ」 、テキスト音楽「サクラ」など。2001年オンラインソフト大賞入賞、2004年度未踏ユース スーパークリエータ認定、2010年 OSS貢献者章受賞。技術書も多く執筆している。直近では、「シゴトがはかどる Python自動処理の教科書(マイナビ出版)」「すぐに使える!業務で実践できる! PythonによるAI・機械学習・深層学習アプリのつくり方 TensorFlow2対応(ソシム)」「マンガでざっくり学ぶPython(マイナビ出版)」など。