はじめに
先回の記事でReportlabというPDF生成ライブラリの使い方を簡単に説明しました。
ただどうにもその使い方だと使いづらいところがあり、実用には向きそうもありませんでした。
この前以下の記事を読んだ際、Reportlabにまだまだ有用な機能があることを知ったのでまとめてみようと思います。
非常に簡単にPDFが作れそうです。
前回の記事まとめ
前回はReportlabのcanvas.drawStringというメソッドを使用してPDFファイルの指定した位置に文字を入力したりしていました。ただ、毎回文字を入力する位置を指定しないといけないということと、長い文章の時は自動で折り返されずはみ出してしまうという問題があり非常に面倒なやり方でした。
PLATYPUS
Reportlabにはplatypusというライブラリがあり、これを使えば上記の問題は解決するようです。
platypusでは、テンプレートとflowableというものを使うことで自由自在に文章や画像や表をレイアウトすることができるようです。
platypusを用いたPDFファイル生成の流れ
platypusを用いたページの具体的な構成は以下のようになっています。
参照: Chapter 5: Platypus - ReportLab Docs
ここで、軽く出てくる要素について説明します。
DocTemplates:これは作りたい資料全体のコンテナ的なクラス
PageTemplates:FrameやFlowableをどう配置するかページ単位のテンプレート
Frames:Flowablesを配置できる領域を規定したもの。これらをどこに配置するのかをPageTemplatesで指定する
Flowables:文字列や画像等の要素。わざわざ位置を指定する必要がなく、追加された順番にテンプレートに従って自動で配置される要素。要素同士が重ならないように位置も自動で調整され、文字列の場合自動で折り返しもしてくれるので便利
Platypusの使い方
簡単な例として全ページ同じレイアウトを使用するようなPDFファイルを生成してみます。
DocTemplateの準備
DocTemplatesにはいくつか種類があるみたいですが、今回はBaseDocTemplateというクラスを使用します。
from reportlab.platypus import BaseDocTemplate, PageTemplate from reportlab.lib.pagesizes import A4, mm, portrait from reportlab.pdfbase import pdfmetrics from reportlab.platypus.frames import Frame # DocTemplateの生成 file_path = "./hello_world.pdf" doc = BaseDocTemplate(file_path, title="テスト", pagesize=portrait(A4), ) width, height = A4 # 595px, 842px
BaseDocTemplateの引数には出力ファイルパス、タイトル、サイズを入力します。
ここにFrameの配置が定義されたPageTemplateを登録します。
PageTemplateの準備
先の図のtwo columnレイアウトみたいにしてみましょう。ページの左右半分ずつのFrameを作成し、PageTemplateにリストとして渡せばOKです。
from reportlab.platypus import BaseDocTemplate, PageTemplate from reportlab.lib.pagesizes import A4, mm, portrait from reportlab.pdfbase import pdfmetrics from reportlab.platypus.frames import Frame # DocTemplateの生成 file_path = "./hello_world.pdf" doc = BaseDocTemplate(file_path, title="テスト", pagesize=portrait(A4), ) width, height = A4 # 595px, 842px # Frameの定義 show = 1 #Frameの枠を表示 frames = [ Frame(15*mm, 15*mm, width / 2 - 15*mm, height - 30*mm, showBoundary=show), Frame(width / 2, 15*mm, width / 2 - 15*mm, height - 30*mm, showBoundary=show), ] # PageTemplateの定義とDocTemplateへの登録 page_template = PageTemplate("test", frames=frames) doc.addPageTemplates(page_template)
PageTemplateの引数にはそのページレイアウトの名前と、テンプレートとして配置するフレームのリストを渡します。
DocTemplateへの登録は.addPageTemplatesメソッドを使用して行います。
内容の入力
さっそく文字を入力してPDFファイルを作成してみましょう。
前回の記事とは異なり、今回の方法では文字や画像等のFlowableオブジェクトをリストでまとめ、DocTemplateに登録することでいい感じに配置してくれます。
文字列のFlowableオブジェクトにはParagraphというクラスを使います。
以下がサンプルコードです。
from reportlab.platypus import BaseDocTemplate, PageTemplate from reportlab.platypus import Paragraph from reportlab.lib.styles import ParagraphStyle from reportlab.lib.pagesizes import A4, mm, portrait from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase import cidfonts from reportlab.platypus.frames import Frame # 日本語フォントの登録 pdfmetrics.registerFont(cidfonts.UnicodeCIDFont("HeiseiKakuGo-W5")) # フォントスタイルの設定 PS = ParagraphStyle style_body = PS(name = 'body', fontName = "HeiseiKakuGo-W5", fontSize = 12, leading = 13) # DocTemplateの生成 file_path = "./hello_world.pdf" doc = BaseDocTemplate(file_path, title="テスト", pagesize=portrait(A4), ) width, height = A4 # 595px, 842px # Frameの定義 show = 1 #Frameの枠を表示 frames = [ Frame(15*mm, 15*mm, width / 2 - 15*mm, height - 30*mm, showBoundary=show), Frame(width / 2, 15*mm, width / 2 - 15*mm, height - 30*mm, showBoundary=show), ] # PageTemplateの定義とDocTemplateへの登録 page_template = PageTemplate("test", frames=frames) doc.addPageTemplates(page_template) # 日本語フォントの登録 pdfmetrics.registerFont(cidfonts.UnicodeCIDFont("HeiseiKakuGo-W5")) # フォントスタイルの設定 PS = ParagraphStyle style_body = PS(name = 'body', fontName = "HeiseiKakuGo-W5", fontSize = 12, leading = 13) ## 内容記入 flowables = [] # 資料全体のflowableの格納用リスト para = Paragraph("Hello, world!", style_body) flowables.append(para) para = Paragraph("Hello, world2!", style_body) flowables.append(para) doc.multiBuild(flowables) # flowableのリストをDocTemplateに登録し、PDFを作成
出力結果は以下の通り。
大体の流れはわかりましたでしょうか?前回みたいに遂次位置を指定するのではなく、リストに追加していくだけで勝手に要素を配置してくれるので非常に便利です。
フレームやページ移動
また、フレームやページの移動に関してですが、追加したflowableオブジェクトが現在のフレームに収まらないと判断した場合、勝手に次のフレームや次のページに移動して書き込んでくれますが、FrameBreakやPageBreakを使って明示的にフレームやページ移動も可能です。
from reportlab.platypus import BaseDocTemplate, PageTemplate, FrameBreak, PageBreak from reportlab.platypus import Paragraph from reportlab.lib.styles import ParagraphStyle from reportlab.lib.pagesizes import A4, mm, portrait from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase import cidfonts from reportlab.platypus.frames import Frame # 日本語フォントの登録 pdfmetrics.registerFont(cidfonts.UnicodeCIDFont("HeiseiKakuGo-W5")) # フォントスタイルの設定 PS = ParagraphStyle style_body = PS(name = 'body', fontName = "HeiseiKakuGo-W5", fontSize = 12, leading = 13) # DocTemplateの生成 file_path = "./hello_world.pdf" doc = BaseDocTemplate(file_path, title="テスト", pagesize=portrait(A4), ) width, height = A4 # 595px, 842px # Frameの定義 show = 1 #Frameの枠を表示 frames = [ Frame(15*mm, 15*mm, width / 2 - 15*mm, height - 30*mm, showBoundary=show), Frame(width / 2, 15*mm, width / 2 - 15*mm, height - 30*mm, showBoundary=show), ] # PageTemplateの定義とDocTemplateへの登録 page_template = PageTemplate("test", frames=frames) doc.addPageTemplates(page_template) # 日本語フォントの登録 pdfmetrics.registerFont(cidfonts.UnicodeCIDFont("HeiseiKakuGo-W5")) # フォントスタイルの設定 PS = ParagraphStyle style_body = PS(name = 'body', fontName = "HeiseiKakuGo-W5", fontSize = 12, leading = 13) ## 内容記入 flowables = [] # 資料全体のflowableの格納用リスト para = Paragraph("Hello, world!", style_body) flowables.append(para) para = Paragraph("Hello, world2!", style_body) flowables.append(para) #次のフレームへ flowables.append(FrameBreak()) para = Paragraph("Hello, world3!", style_body) flowables.append(para) #ページへ flowables.append(PageBreak()) para = Paragraph("Hello, next page!", style_body) flowables.append(para) para = Paragraph("すごく長い文章も勝手に折り返しされて表示されます。この機能が非常に便利なのでもうdrawStringでの書き方には戻れなさそうですね。", style_body) flowables.append(para) doc.multiBuild(flowables) # flowableのリストをDocTemplateに登録し、PDFを作成
結果は以下。長い文章も勝手に折り返して配置してくれているのがわかるでしょうか?
画像の挿入
画像の挿入にはImageというFlowableオブジェクトを使用します。multiBuildメソッド前に以下を追加しました。
公式ドキュメントにはjpegしか許さないみたいに書いてますが、なぜかpngでもいけました。
from reportlab.platypus import Image im = Image('./rennai_kaeruka.png', width=50*mm, height=50*mm, hAlign="LEFT") flowables.append(im) para = Paragraph("図:カエル化現象", style_body) flowables.append(para)
ImageもParagraphと同じ方法で追加できていますね。
表の挿入
表の挿入にはTableというFlowableオブジェクトを使用し同様に追加すればOKです。
全コード載せておきます。
from reportlab.platypus import BaseDocTemplate, PageTemplate, FrameBreak, PageBreak from reportlab.platypus import Paragraph, Image, Table, TableStyle from reportlab.lib.styles import ParagraphStyle from reportlab.lib import colors from reportlab.lib.pagesizes import A4, mm, portrait from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase import cidfonts from reportlab.platypus.frames import Frame # 日本語フォントの登録 pdfmetrics.registerFont(cidfonts.UnicodeCIDFont("HeiseiKakuGo-W5")) # フォントスタイルの設定 PS = ParagraphStyle style_body = PS(name = 'body', fontName = "HeiseiKakuGo-W5", fontSize = 12, leading = 13) # DocTemplateの生成 file_path = "./hello_world.pdf" doc = BaseDocTemplate(file_path, title="テスト", pagesize=portrait(A4), ) width, height = A4 # 595px, 842px # Frameの定義 show = 1 #Frameの枠を表示 frames = [ Frame(15*mm, 15*mm, width / 2 - 15*mm, height - 30*mm, showBoundary=show), Frame(width / 2, 15*mm, width / 2 - 15*mm, height - 30*mm, showBoundary=show), ] # PageTemplateの定義とDocTemplateへの登録 page_template = PageTemplate("test", frames=frames) doc.addPageTemplates(page_template) # 日本語フォントの登録 pdfmetrics.registerFont(cidfonts.UnicodeCIDFont("HeiseiKakuGo-W5")) # フォントスタイルの設定 PS = ParagraphStyle style_body = PS(name = 'body', fontName = "HeiseiKakuGo-W5", fontSize = 12, leading = 13) ## 内容記入 flowables = [] # 資料全体のflowableの格納用リスト para = Paragraph("Hello, world!", style_body) flowables.append(para) para = Paragraph("Hello, world2!", style_body) flowables.append(para) #次のフレームへ flowables.append(FrameBreak()) para = Paragraph("Hello, world3!", style_body) flowables.append(para) #ページへ flowables.append(PageBreak()) para = Paragraph("Hello, next page!", style_body) flowables.append(para) para = Paragraph("すごく長い文章も勝手に折り返しされて表示されます。この機能が非常に便利なのでもうdrawStringでの書き方には戻れなさそうですね。", style_body) flowables.append(para) im = Image('./rennai_kaeruka.png', width=50*mm, height=50*mm, hAlign="LEFT") flowables.append(im) para = Paragraph("図:カエル化現象", style_body) flowables.append(para) # 表の作成 flowables.append(FrameBreak()) #次のフレームへ para = Paragraph("Table. 果物価格", style_body) flowables.append(para) data = [['ID', '名前', '価格'], ['0', 'リンゴ', 100], ['1', 'ミカン', 400], ['2', 'イチゴ', 300], ['3', 'ブドウ', 1000], ] table = Table(data, colWidths=20*mm, rowHeights=10*mm) table.setStyle(TableStyle([ ('FONT', (0, 0), (2, 4), 'HeiseiKakuGo-W5', 12), ('BOX', (0, 0), (2, 4), 1, colors.black), ('INNERGRID', (0, 0), (2, 4), 1, colors.black), ('VALIGN', (0, 0), (2, 4), 'MIDDLE'), ])) table.setStyle(TableStyle([ ('BACKGROUND', (0, 0), (2, 0), colors.lightskyblue), ])) flowables.append(table) doc.multiBuild(flowables) # flowableのリストをDocTemplateに登録し、PDFを作成
適当な表を作成しましたが、簡単に表が書けることがわかってもらえたかと思います。
終わりに
今回は一通りReportLabのPlatypusを使用したPDFファイルの作成方法について紹介しました。前回紹介した方法に比べ、格段に楽にPDFファイルを作れることがわかっていただけたかと思います。
今回は全ページ同じテンプレートを使用しましたが、ページごとに異なるテンプレートを使用することも可能なので、次回はそちらについて例を交えて紹介します。