Go+LuaでHTMLからPDFを生成するCLIツールを作った
以前、Goでプロビジョニングツールを作った でも書いたように、GoとGopherLuaでCLIツールをいろいろ実装していて、今回もその成果物の一つです。
概要
Goで書かれたワンバイナリで動くPDF生成ツールです。現時点で動作環境は64bitのMacとLinuxのみをサポートしてます(windowsはまだ)。全体的にコードの実装はcofuからコピペして雑に作ったので、使いかたもほぼ同じです。
html2pdf
というコマンドをreleaseページからダウンロードしてPATHの通ったディレクトリに配置し、
local html2pdf = require "html2pdf" local example = html2pdf.pdf "example.pdf" example.options = { page_size = "A4", } example.pages = { input_content = "hello world!" }
のようなLuaのコードをexample.lua
に保存。html2pdf example.lua
を実行する。
$ html2pdf example.lua ==> Starting html2pdf... ==> Loaded 1 pdf config. ==> Evaluating example.pdf output_file: example.pdf ==> Complete!
これでexample.pdf
が出力されます。上記の例だとコンテンツを設定するinput_content
に単純な文字列(hello world!)を指定しているだけですが、実際にはHTMLを書いて使います。
使い方
URLを指定
input_content
の代わりにinput
を使えばURLを指定できます。
local html2pdf = require "html2pdf" local example = html2pdf.pdf "example.pdf" example.pages = { input = "https://github.com/kohkimakimoto/html2pdf" }
複数ページ
pages
には複数のページを設定できます。
example.pages = { { input = "https://github.com/kohkimakimoto/html2pdf" }, { input = "https://github.com/kohkimakimoto/cof" }, }
表紙
cover
で表紙を設定できます。ページの指定と同じくinput_content
かinput
でコンテンツを指定できます。
example.cover = { input_content = "<b>title</b>" -- or -- input = "https://..." }
その他オプション
html2pdfは内部でwkhtmltopdfを使っています。というより、実装的にはwkhtmltopdfにLuaスクリプティング機能を載せたラッパーコマンドになっています。なのでwkhtmltopdfのオプションが使えます。options
で以下のようにして指定します。
example.options = { page_size = "A4", margin_left = 5, margin_top = 5, margin_bottom = 5, margin_right = 5, orientation = "Landscape", -- etc... }
詳しくはwkhtmltopdfのドキュメントを参考にしてください。wkhtmltopdf docs
変数
-var
か-var-file
オプションで実行時にコマンドラインから変数を渡せます。変数はJSONで以下のように指定します。
$ html2pdf example.lua -var='{"output_file": "foo.pdf"}'
スクリプト内ではvar
変数として格納されています。
local html2pdf = require "html2pdf" local example = html2pdf.pdf "example.pdf" example.output_file = var.output_file example.pages = { input = "https://github.com/kohkimakimoto/html2pdf" }
Shebang
#!/usr/bin/env html2pdf
をスクリプトファイルの先頭に記述して実行権限をつければ、直接実行してPDFを吐くスクリプトになります。
Markdown
MarkdownからHTMLを生成するLuaモジュールも組み込んだので、すこしだけLuaでロジックを書けばmarkdownからPDFドキュメントを作ることもできます。以下にサンプルを作ったので参考にしてください。
https://github.com/kohkimakimoto/html2pdf/tree/master/_example
表紙付きドキュメントを生成するサンプルには結果のPDFもアップロードしてあります。
https://github.com/kohkimakimoto/html2pdf/blob/master/_example/doc/doc.pdf
DSLな記法
設定はLuaのDSLスタイルでも記述することもできます。次の2つの例は同じ設定です。もりもりコードを書くのでなければ、DSLのほうが見やすいかもしれません。
pdf "hello.pdf" { pages = { input_content = "hello world!" }, }
PlainなLua
local html2pdf = require "html2pdf" local hello = html2pdf.pdf "hello.pdf" hello.pages = { input_content = "hello world!" }
※DSLで使っているpdf
関数はhtml2pdf.pdf
メソッドのエイリアスになっています。
実装の話
当初、PDF変換部分のwkhtmltopdfを調べていたら、これはCで書かれたプロダクトだったので、Goからcgoをつかって連携しようかと考えて調査したのですが、どうも共有ライブラリをロードするGoのプログラムはスタンドアロンなバイナリにならないようだと判明。あとクロスコンパイル周りで罠があったりとか、Macではcgoをつかってスタティックなビルドができないとか。。。(結構いろいろ調べたのですが、もう詳細忘れた。。。とにかくできそうになかった。自分のスキルが足りない可能性ももちろんありますが)。cgoツライ。
html2pdfを使うマシンにwkhtmltopdfをインストールしておけばいいだけなのですが、これでは当然、スタンドアロンなシングルバイナリとはいえないわけです。これはイヤだなと。
で、wkhtmltopdfコマンド自体がスタンドアロンなバイナリなので、もうこのバイナリ自体をまるごとgo-bindataでGoのコードに埋め込んでおき、実行時に/tmp
ディレクトリ以下にwkhtmltopdfのコマンドファイルを生成して、それをhtml2pdfから叩くという荒業を使うことにしました。これで見た目上はhtml2pdfのみで動いているように見えるし、別途wkhtmltopdfをインストールする必要もない。
自分は何に使っているのか
個人事業主やってるので、クライアントに提出する請求書をPDFで生成するのに使っています。