【備忘録】PythonでPDF出力してみよう reportlab編

# 経緯
DjangoでPDF出力を行いたい。PC環境にインストールなどは行いたくなく、pipでインストールできるreportlabを採用。
恐らくまた使用する機会があると思うので、備忘録として残しておく。
始点終点を指定する方法よりめちゃ楽なのでオススメ。

# 参考にさせて頂いたサイト
https://qiita.com/kokardy/items/92e8f3b65c965e20de34
タイトル笑いました。
ただ、確かに少しぐぐったところこのやり方で行うサイトは少ない。
（ほかのサイトは始点、終点を全て指定して記載する方法）

https://www.reportlab.com/docs/reportlab-userguide.pdf
reportlab ユーザーガイド（英語）

# reportlabインストール
pythonはインストールしている前提です。
ここでは3.9.7を使ってます。

~~~cmd
python -m pip install reportlab

Collecting reportlab
  Downloading reportlab-3.6.9-cp39-cp39-win_amd64.whl (2.3 MB)
     |████████████████████████████████| 2.3 MB 1.1 MB/s
Requirement already satisfied: pillow>=4.0.0 in c:\users\kutaa\anaconda3\lib\site-packages (from reportlab) (8.4.0)
Installing collected packages: reportlab
Successfully installed reportlab-3.6.9
~~~
インストール完了です。
python2と3が共存している場合、errorが出る場合があるよう。
その場合、pip3を使用してpython3環境にインストールしよう。

# pythonコード
上記サイトのコードをほぼ~~パクリ~~参考にさせて頂きました。
※この例ではデスクトップにtest.pdfファイルを出力します。

~~~python
from reportlab.platypus import BaseDocTemplate, PageTemplate
from reportlab.platypus import Paragraph, PageBreak, FrameBreak
from reportlab.platypus.flowables import Spacer
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.pagesizes import A4, mm
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase import cidfonts
from reportlab.platypus.frames import Frame
import os

def main():
    pdfmetrics.registerFont(cidfonts.UnicodeCIDFont("HeiseiMin-W3"))

    buf = os.path.expanduser("~") + "/Desktop/test.pdf"

    doc = BaseDocTemplate(buf, 
        title="reportlabテスト",
        pagesize=(200*mm, 200*mm),
        )

    show = 1 #Frameの枠を表示
    frames = [
            Frame(25 * mm, 120*mm, 150*mm, 50*mm, showBoundary=0),
            Frame(10 * mm, 30*mm, 70*mm, 80*mm, showBoundary=show),
            Frame(110 * mm, 50*mm, 75*mm, 60*mm, showBoundary=show),
        ]

    page_template = PageTemplate("frames", frames=frames)
    doc.addPageTemplates(page_template)


    style_dict ={
        "name":"nomarl",
        "fontName":"HeiseiMin-W3",
        "fontSize":20,
        "leading":20,
        "firstLineIndent":20,
        }
    style = ParagraphStyle(**style_dict)

    flowables = []

    space = Spacer(10*mm, 10*mm)

    para = Paragraph("1ページ目です", style)
    flowables.append(para)
    flowables.append(space)
    para = Paragraph("ここで<br/>改行されます。スペース&nbsp;が入ります。", style)
    flowables.append(para)
    flowables.append(space)
    para = Paragraph("次のページに進む", style)
    flowables.append(para)

    #改ページ
    flowables.append(PageBreak()) 

    para = Paragraph("<span backcolor=yellow>フーレム1</span>", style)
    flowables.append(para)
    para = Paragraph("Paragraphを使うことで長い文章を自動で折り返します。さらにはみ出せば次のページに進みます。", style)
    flowables.append(para)
    para = Paragraph("ちゃんと続きの高さから次のParagraphを描画してくれます。", style)
    flowables.append(para)
    para = Paragraph("Frameを使えば、はみ出した分は次のフレームまたはページへ自動で進みます。", style)
    flowables.append(para)
    para = Paragraph("Frameを使うことで、段組みのような複雑なレイアウトを表現できます。", style)
    flowables.append(para)

    #次のフレームへ
    flowables.append(FrameBreak())

    para = Paragraph("<span color=red>フレーム3</span>", style)
    flowables.append(para)
    para = Paragraph("FrameBreakを使えば次のFrameに進みます。", style)
    flowables.append(para)
    para = Paragraph("PageBreakを使えば次のPageに進みます。", style)
    flowables.append(para)

    doc.multiBuild(flowables)

if __name__ == '__main__':
    main()
~~~

# 解説
ページサイズ指定
~~~python
pagesize=(200*mm, 200*mm),
~~~

使用するフレーム指定
第1引数：座標x
第2引数：座標y
第3引数：幅
第4引数：高さ
showBoundary：0（枠無し） or 1（枠有り）
~~~python
show = 1 #Frameの枠を表示
    frames = [
            Frame(25 * mm, 120*mm, 150*mm, 50*mm, showBoundary=0),
            Frame(10 * mm, 30*mm, 70*mm, 80*mm, showBoundary=show),
            Frame(110 * mm, 50*mm, 75*mm, 60*mm, showBoundary=show),
        ]
~~~

スペースサイズ指定、スペース挿入
~~~python
space = Spacer(10*mm, 10*mm)
flowables.append(space)
~~~

# 最後に
始点、終点を指定するやり方よりめちゃくちゃ簡単です。
ありがとうございました。