Django:ReportLabによるPDFデータ作成で苦戦したところ

Django:ReportLabによるPDFデータ作成で苦戦したところ

『ReportLab』はDjangoでPDF出力をしようとしたときによく選択されるライブラリなのだそうです。

最近、抱えていたプログラムの方の案件がひと段落したので「他を探そう」と思ったのですが「新しいポートフォリオをくれ」と言われます。
毎回Excelで書きだすのも大変なので「仕事履歴をDB化して常に最新を出力できるように仕込んでしまおう」と思い実装してみました。

で、所感ですが「めんどくせ~~~」と。プログラミングの初体験がこれだったら死ねるかも。
少しでも楽に作る方法を含め、まとめていきたいと思います。

ReportLabが使える2つの方法

私は勉強を兼ねていたので大変な方を選択しましたが『ReportLab』では2つの方法でPDFを作成することができます。

  1. ゼロ(白紙)から罫線などの書式も含めファイルを作成する
  2. あらかじめ用意した書式PDFを利用してファイルを作成する

[手段1]の方が面倒です。何が面倒でどうすると楽になるかは後述するとして、まずは[手段2]の構成から。

簡潔ですね。ポイントは【書式PDFにスキャナで読み込んだデータを使わない事】です。
これを行ってしまうと書き込み時にForループが使えなくなってしまいます。
理由は「スキャナを使うと罫線の位置などが微妙にずれているから」なので、Forループで一覧から必要数出力しようとしても「2列目まではOKだけど3列目が罫線の上に文字が入った」なんて事が発生します。

使う際はExcelなどで作成した書式をそのままPDF出力したものを利用しましょう。

書式からすべてをReportLabで作成するとズレのないPDFを作成できる

罫線なんかを含め白紙からすべての出力をコントロールすればズレは生じません。
でもね、行数と手数が増えて、何よりも罫線や枠の位置決めが超面倒だったりします。

このあたりはLaravelで使っていた【phpspreadsheet】とか優れてますよね。
HTMLで書いたViewをそのままPDFに出力してくれるから慣れたコードですべてを構築できる。
うん、素晴らしい。

PeportLabの面倒な所をまとめてみるとこんな感じ

  1. ポイントとミリ(またはcm)との変換/換算が面倒
  2. 罫線の重なりで一部分だけ線が太くなったりすることがある
  3. 画像の配置とサイズ変更
  4. 行数が増えて見難い&問題個所を探し難い

書式PDFの読み込みで構築すると[1,2,4]が大幅改善されるので苦労は少なくなります。

ゼロベースで構築しなくてはいけない時に楽するポイント

それでもゼロベースで作成しなくちゃならない場合は次のポイントを押さえましょう。

  1. 書式(フォーマット)はグリッド設計にする
  2. 設計したグリッドは出力して手元に置いておく
  3. 1グリッドのサイズを記録しておく(forループで多用します)
  4. PeportLabへのサイズ指示は mm に統一する ※ptでもよいのですが慣れるまで勘は働きにくいです。

書式フォーマット(グリッド)をExcelで書こうとすると悩むところ

Excelは[ mm ]という単位で作られていません。ptとピクセルでコントロールすることになります。

  • 1インチ = 25.4mm
  • 1ポイント = 1インチの72分割 = 25.4 ÷ 72 mm = 0.352777777777778 mm
  • A4サイズ = 210×297mm = 595.2759 × 841.8898pt

Excelの嫌なところは縦横を同じピクセルにしてセルを正方形に見立ててみても、縦横のポイント値が大きく異なっていて何を信じればよいかわからない点です。
ポイントって決まった値になる筈なのですが、Excelではあまり信用してはいけません。
そして恐ろしいことに、縦横のピクセルサイズを合わせても縦横の出力サイズは同じになりません。
こういう時は自分で決めたグリッドをベースに枠の配置を行っていくのが最善手段です。

グリッドの例

これは私がよく使うグリッドです。
Excelで縦横25ピクセルにすると、標準的な有効印刷範囲に横25マス、縦40マスを差し込むことができます。
1マスサイズは 横6.76mm×縦6.35mm
上下左右に入っている数字は、左下を基準点としたときの高さと幅です。『PeportLab』では左下角が基準点(0,0)になるのでそれに合わせてグリッド数値を加算していきます。
このグリッドでは、上下に24.675mm 左右に20.5mm の余白ができるので、標準印刷に近い余白を確保できます。

PeportLabへのサイズ指示を【mm】に統一する

PeportLabでは左下の角を基点としてテーブルや文字をどこに配置するか指示を出します。
上のグリッド例の16pt文字のAであれば【 p.drawString(27.26 * mm, 234.23 * mm, ‘A’) 】といった形です。
この[ 数値 * mm ] を利用するためには必要な関数をUSEしなくてはなりません。そんなのを諸々含めた具体例が下です。

文字サイズはptで行っていますが、枠線の配置などは全てmmで行っているのがわかると思います。
そして、指示している場所は作成したグリッドの交点となっています。

書式フォーマットと記載内容については関数を分けてあげる

ついついフォーマットとデータの投入を1つの関数で行ってしまいがちですが、これは絶対に分けた方が良いです。
理由は簡単で、修正が楽になるから。
長いスクリプトから要件個所を探すのは中々に大変ですが、小分けにしてしまえば場所の特定は容易です。
また、分けているからこその特権『使いまわし』も可能となります。

罫線までスクリプトで組んでしまえば、あとはリストを作ってループで書き込むだけ

フォーマットがあって記載内容のリストを作成できれば、後はforループで書いていけばOKです。
グリッド設計してあれば上下左右の増える幅(レコード毎にどれ位ずらすか)は算出できます。

まとめ

このフォーマットを作るのにまる一日。
もう面倒ったらないです。
忙しい時には絶対やらない作業でした。
でもまぁ、一回作っちゃえばあとはレコードが増える度に内容更新してくれるんだから便利ですよね。