ReportLabでpdfに表を描いてみたところ、悩んだところがあったため、メモを残しておきます。
なお、詳細はReportLabの公式ドキュメント中の「ReportLab PDF LibraryUser Guide」のp77〜にも記載があります。
目次
環境
今回使うReportLabの実装は、基本的な形は以下となります。必要に応じてこのクラスを継承、_draw()
メソッドをオーバーライドして各表を描きます。
注意点として、デフォルトと異なり、pdfの原点を左上(bottomup=False
)にしてあります。
base.py
from django.http import HttpResponse
from django.views import View
from reportlab.lib.pagesizes import A4
from reportlab.lib.pagesizes import portrait
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.pdfgen import canvas
from reportlab.lib.units import mm
class BaseView(View):
filename = 'example.pdf'
title = 'title: example'
font_name = 'HeiseiKakuGo-W5'
is_bottomup = False
def get(self, request, *args, **kwargs):
response = HttpResponse(status=200, content_type='application/pdf')
response['Content-Disposition'] = 'filename="{}"'.format(self.filename)
pdfmetrics.registerFont(UnicodeCIDFont(self.font_name))
size = portrait(A4)
doc = canvas.Canvas(response, pagesize=size, bottomup=self.is_bottomup)
doc.setTitle(self.title)
doc.drawString(10*mm, 10*mm, self.__class__.__name__)
self._draw(doc)
return response
def _draw(self, doc):
pass
複数列・複数行の表を作成
複数列・複数行の表を描くには、
- 複数列:配列の要素が複数あるデータを用意
- 複数行:2次元配列としてデータを用意
とします。
以下の場合、3列3行の表のデータとなります。
data = [
['行1-列1', '行1-列2-*********', '行1-列3-*********-*********'],
['行2-列1', '行2-列2-*********', '行2-列3-*********-*********'],
['行3-列1', '行3-列2-*********', '行3-列3-*********-*********'],
]
上記のデータを使って表を描いてみます。
multi_rows.py
class BasicMultiRows(BaseView):
def _draw(self, doc):
data = [
['行1-列1', '行1-列2-*********', '行1-列3-*********-*********'],
['行2-列1', '行2-列2-*********', '行2-列3-*********-*********'],
['行3-列1', '行3-列2-*********', '行3-列3-*********-*********'],
]
table = Table(data)
table.setStyle(TableStyle([
('FONT', (0, 0), (-1, -1), self.font_name, 9),
('BOX', (0, 0), (-1, -1), 1, colors.black),
('INNERGRID', (0, 0), (-1, -1), 0.25, colors.red),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
]))
table.wrapOn(doc, 50*mm, 10*mm)
table.drawOn(doc, 50*mm, 10*mm)
doc.save()
結果は以下の通りです。bottomup=Falseなので、原点は左上になります。
indexの降順で、表の上から下に並びます。
ちなみに、bottomup=Trueにした場合、原点は左下となり、こんな感じになります。
indexの昇順で、表の上から下に並びます。
複数列・複数行の表で、セルの高さや幅を指定
上の例では、表のセルの高さや幅は自動計算で設定されました。
任意の位置にあるセルの高さや幅を指定したい場合は、Tableオブジェクトを生成する際にrowHeights
(行の高さ)やcolWidths
(列の幅)を使います。
列・行ごとに設定したい場合はタプルで長さを渡し、全て一律で設定したい場合は単一値を指定します。
例えば、列幅は左から20mm・40mm・60mm、行の高さは一律10mmとしたい場合、
table = Table(data, colWidths=(20*mm, 40*mm, 60*mm,), rowHeights=10*mm)
と書くと、結果は以下となります。
セルごとの設定
一番左の列のみ色を塗る
ここまでは表のセル全体に関する設定でしたので、次はセルごとの設定を行います。
ReportLabでは、セルごとの設定はTableStyle
を使います。
TableStyleで指定するセル位置の表記は、ExcelでいうところのR1C1
形式にて、(列, 行)
のタプルで表現します。
また、列と行のindexは0から始まります。
例えば、5x5の表に対して左側の列だけ色塗りをする場合、
table.setStyle(TableStyle([
('VALIGN', (0, 0), (-1, -1), 'TOP'),
('BACKGROUND', (0, 0), (0, 4), colors.lightpink),
]))
のようにTableStyleのコンストラクタにて、
- タプルの2番目の要素に、開始セル位置:
(0, 0)
- タプルの3番目の要素に、終了セル位置:
(0, 4)
をそれぞれ指定します。
結果は、
となります。
一番左の列のみ色を塗る (indexはマイナスバージョン)
(列, 行)
のindexにはマイナス値も設定することができます。
Pythonのindexのマイナス値と同じ考え方ですので、-1
は最後、-2
は最後から2番目となります。
例えば左側の列だけ色塗りするには、
table.setStyle(TableStyle([
('BACKGROUND', (0, 0), (0, -1), colors.lightpink),
]))
のように指定します。
応用として、中央の範囲を色塗りする場合は、
table.setStyle(TableStyle([
('BACKGROUND', (1, 0), (3, 4), colors.lightpink),
]))
とすると、
となります。
また、マイナスのindexを使う場合は、
table.setStyle(TableStyle([
('BACKGROUND', (1, 0), (-2, -1), colors.lightpink),
]))
とすると、
となります。
罫線を引く
ReportLabではTableStyleのLine Commandsを使って罫線を引きます。
Line Commandsにはいくつかの種類がありますので、それぞれ試してみます。
なお、Line Commandsの形式は以下の通りです。
table.setStyle(TableStyle([
(<Line Comamnd名のリテラル>, 開始セルを示したタプル, 終了セルを示したタプル, 線の太さを示す数値, 線の色),
]))
セルの左側に罫線を引く
LINEBEFORE
を使います。
table.setStyle(TableStyle([
('LINEBEFORE', (0, 0), (0, 4), 0.25, colors.black),
]))
セルの右側に罫線を引く
LINEAFTER
を使います。
table.setStyle(TableStyle([
('LINEAFTER', (0, 0), (0, 4), 0.25, colors.black),
]))
セルの上に罫線を引く (bottomupの影響あり)
LINEABOVE
を使います。
table.setStyle(TableStyle([
('LINEABOVE', (0, 0), (0, 4), 0.25, colors.black),
]))
なお、今回の場合、bottomup=False
と原点を左上にしていますので、セルの下側に罫線があります。
セルの下に罫線を引く (bottomupの影響あり)
LINEBELOW
を使います。
table.setStyle(TableStyle([
('LINEBELOW', (0, 0), (0, 4), 0.25, colors.black),
]))
なお、こちらもbottomup=False
の影響を受け、セルの上側に罫線があります。
セルの外枠に罫線を引く
BOX
を使います。
table.setStyle(TableStyle([
('BOX', (0, 0), (0, 4), 0.25, colors.black),
]))
もしくは、OUTLINE
でも同じ結果になります。
table.setStyle(TableStyle([
('OUTLINE', (0, 0), (0, 4), 0.25, colors.black),
]))
セルの内側に格子状の罫線を引く
INNERGRID
を使います。
なお、今までの例と異なり、分かりやすくするためにセルの範囲を中央にしてあります。
table.setStyle(TableStyle([
('INNERGRID', (1, 1), (3, 3), 0.25, colors.black),
]))
セルのすべてに罫線を引く
GRID
を使います。
なお、こちらも、分かりやすくするためにセルの範囲を中央にしてあります。
table.setStyle(TableStyle([
('GRID', (1, 1), (3, 3), 0.25, colors.black),
]))
セルを結合する
SPAN
を使います。
下記の例は、
- 2列2行目から4列4行目までをSAPNで結合
- 2列2行目から3列3行目までの背景色をlightpink
としています。
なお、SAPNで結合した部分にあるデータは、最初のセルを除いて削除されることに注意します。
table.setStyle(TableStyle([
('GRID', (0, 0), (-1, -1), 0.25, colors.black),
('SPAN', (1, 1), (3, 3)),
('BACKGROUND', (1, 1), (2, 2), colors.lightpink),
]))
GitHubに上げてあります。table_style
アプリが今回のアプリです。
thinkAmi-sandbox/Django_ReportLab_on_Heroku-sample