PythonでPDFに図形やテキストを描画する

PythonでPDFに図形やテキストを描画する方法の解説ページアイキャッチ

このページでは Python で PDF に図形やテキストを描画する方法について解説していきます。

例えばですが、下の図のような PDF のページがあった場合、

PDFへの図形描画のイメージ1

下の図のようにそのページの上に長方形や円、テキストなどを描画し、それを新たな PDF ファイルとして保存する方法の解説になります。

PDFへの図形描画のイメージ2

PDF に図形やテキストを描画する時に使用するライブラリ

PDF の構造や仕組みは難しいので、この PDF に直接図形やテキストを描画するスクリプトを作成するのは困難です。

ですので、PDF に対して図形やテキストの描画を手軽に行いたいのであればライブラリを利用することをオススメします。

今回は下記の2つのライブラリを利用します。

  • pdfrw
  • ReportLab

各ライブラリの関係性を表したのが下の図になります。

ライブラリの関係性

pdfrw

pdfrw は PDF の読み込みや保存を行うライブラリです。

今回紹介する方法では PDF の読み込みを行うためにこの pdfrw を利用します。

インストールは pip コマンド1つで行うことができます。

pip install pdfrw

スポンサーリンク

ReportLab

ReportLab は PDF に図形の描画やテキストの描画、さらには PDF の保存を行うライブラリです。

PDF に図形やテキストを描画し、さらに描画後の PDF を保存するためにこの ReportLab を利用します。

pdfrw 同様、ReportLab のインストールは pip コマンドで行うことができます。

pip install reportlab

PDF に図形やテキストを描画する方法

続いては PDF に図形やテキストを描画する方法について解説していきます。

ライブラリのインポート

事前に必要なライブラリ・パッケージのインポートを行います。

インポート
from pdfrw import PdfReader
from pdfrw.buildxobj import pagexobj
from pdfrw.toreportlab import makerl
from reportlab.pdfgen import canvas
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.pdfbase import pdfmetrics
from reportlab.lib.units import mm

スポンサーリンク

PDF データの作成

まずは PDF データの作成を行います。ここでは空の PDF データを作成します。

PDFデータの作成

この空の PDF データに、読み込んだ PDF のページを展開し、さらにその上に図形・テキストを描画することで、PDF への図形・テキストの描画を実現していきます。

PDFデータの作成は reportlab.pdfgen モジュールで定義されている Canvas クラスのコンストラクタ実行により行うことができます。

保存先PDFデータの作成
# 保存先PDFデータを作成
cc = canvas.Canvas(out_path)

out_path には図形やテキストを描画した PDF の保存先ファイルパスを指定します。

後に解説する save メソッドを実行することで、out_path に指定したパスに PDF が保存されることになります。

今後の解説では cc というオブジェクトが多く登場しますが、全てこの canvas.Canvas のインスタンスであると考えてください。

PDF の読み込み

次は図形やテキストを描画したい PDF ファイルを読み込みます。

PDFの読み込み

PDF の読み込みは pdfrw の PdfReader クラスのコンストラクタ実行により行うことができます。

PDFの読み込み
# PDFを読み込む
pdf = PdfReader(in_path, decompress=False)

in_path には図形やテキストを描画する基となる PDF のファイルパスを指定します。

以降で説明する処理では、この in_path で指定した PDF に対して図形の描画を行い、out_path に指定したファイルパスに保存することになります。

PDF のページデータ取得

さらに読み込んだ PDF ファイルからページデータを取得します。

PdfReader クラスのインスタンスは、pages 属性を持っており、コレは PDF のデータがページ単位で格納されたリストになります。

ですので、このリストの要素を取得することで、特定のページのデータのみを取得することができます。

PDFのページデータ取得
# PDFのページデータを取得
page = pdf.pages[1]

上記では page がページ1のデータを参照することになります。

ページ番号は 0 からカウントされるところが注意点です。

スポンサーリンク

PDF データへのページデータの展開

今度は、最初に作成した PDF データに先ほど取得したページデータを展開します。

ページデータの展開

ここでポイントになるのが、保存先 PDF データの作成は ReportLib の機能を用いて行ったのに対し、PDF のページデータ取得は pdfrw の機能を用いて行った点です。

要は保存先の PDF データと取得したページデータは異なるライブラリのオブジェクトになっています。

ですので、そのままページデータを PDF データに展開することはできませんので、事前に pdfrw のオブジェクトを ReportLib のオブジェクトに変換してやる必要があります。

この変換を行うのが pdfrw.toreportlab モジュールの makerl 関数になります。

さらに makerl 関数にはページデータそのものではなく、ページデータを XObj というオブジェクトに変換したものを引数に指定する必要があります。

この変換は pdfrw.buildxobj モジュールの pagexobj 関数になります。

これらの変換を行うスクリプトは下記のようになります。

ReportLabオブジェクトへの変換
pp = pagexobj(page) #ページデータをXobjへの変換
rl_obj = makerl(cc, pp) # ReportLabオブジェクトへの変換 

rl_obj は ReportLab のオブジェクトですので、コレを PDF データに展開します。この展開は Canvas クラスの doForm メソッドにより行うことができます。

ページデータの展開
cc.doForm(rl_obj) # 展開

展開したページデータへの図形の描画

いよいよここから展開したページデータの上に図形を描画していきます。

まずはテキストを除く図形の描画について説明していきます。

図形の描画

ReportLab の座標系

ReportLab では、図形を描画するにあたって、どの位置に描画するかを座標で指定してやる必要があります。

ReportLab で扱う座標はちょっとクセがあるので、ここで説明しておきます。

ReportLab で扱う座標では、原点はページの左下になります。

さらに、縦方向の正方向が上、横方向の正方向が右です。

ReportLabにおける座標系

プログラミングで扱う画像は、原点はページの左上、縦方向の正方向が下、横方向の正方向が右のものが多いです。

これらとは原点の位置と縦方向の正方向が逆である点に注意してください。

長方形の描画

続いて実際に描画を行う方法について解説していきます。

まずは長方形です。

長方形の描画は Canvas クラスの rect メソッドにより行うことができます。

rect
def rect(x, y, width, height, stroke=1, fill=0)

rect メソッドを実行することで、座標 (x, y) を長方形の “左下” とした位置に、幅 width、高さ height の長方形が描画されます。

長方形の描画

stroke は枠線の有無を設定する引数で、1 を指定すれば枠線あり、0 を指定すれば枠線なしになります。

fill は塗り潰しの有無を設定する引数で、1 を指定すれば塗り潰しあり、0 を指定すれば塗り潰しなしになります。

xywidthheight で指定する値の単位が何なのかは正直私も分かっていないです…。

が、指定する値に reportlab.lib.units モジュールの mm を掛けてやれば、mm 単位での指定を間接的に行うことができます。

例えば、原点(ページの左下)から横方向に 10mm、縦方向に 20mm 離れた位置に幅 30mm、高さ 40mm、枠線あり、塗り潰しありの長方形を描画する際には、下記のように rect メソッドを実行します。

rectの使用例
cc.rect(10 * mm, 20 * mm, 30 * mm, 40 * mm, 1, 1)

円の描画

次は円の描画です。

円の描画は Canvas クラスの circle メソッドにより行うことができます。

circle
def circle(x_cen, y_cen, r, stroke=1, fill=0)

circle メソッドを実行することで、座標 (x_cen, y_cen) を中心とした位置に、半径 r の円を描画することができます。

円の描画

stroke は枠線の有無を設定する引数で、1 を指定すれば枠線あり、0 を指定すれば枠線なしになります。

fill は塗り潰しの有無を設定する引数で、1 を指定すれば塗り潰しあり、0 を指定すれば塗り潰しなしになります。

例えば、原点(ページの左下)から横方向に 100mm、縦方向に 120mm 離れた位置を中心とした半径 40mm、枠線あり、塗り潰しありの円を描画する際には、下記のように circle メソッドを実行します。

circleの使用例
cc.circle(100 * mm, 120 * mm, 40 * mm, 1, 1)

線の描画

次は線の描画です。

線の描画は Canvas クラスの line メソッドにより行うことができます。

line
def line(x1, y1, x2, y2)

line メソッドを実行することで、座標 (x1, y1) から座標 (x2, y2) に線を描画することができます。

線の描画

例えば、原点(ページの左下)から横方向に 10mm、縦方向に 20mm 離れた位置から、原点から横方向に 200mm、縦方向に 274mm 離れた位置への線を描画する際には、下記のように line メソッドを実行します。

lineの使用例
cc.line(10 * mm, 20 * mm, 200 * mm, 274 * mm)

線の色の設定

描画する線や、長方形や円描画時の枠線の色を Canvas クラスの setStrokeColor メソッドや setStrokeColorRGB メソッド等で変更することが可能です。

まず setStrokeColor メソッドの定義は下記のようになります。

setStrokeColor
def setStrokeColor(aColor, alpha=None)

aColor には色名を指定することができます(カラーオブジェクトを渡すこともできるらしい)。

また alpha0 から 1 の小数の値を設定することで、色の透明度を設定することが可能です。値が小さいほど透明度が上がります。例えば 0.5 を指定すれば半透明の色になります。

線だと分かりにくいですが、次に説明する塗り潰しの色設定で設定すると透明度による見た目の違いが分かりやすいと思います。

続いて、setStrokeColorRGB メソッドの定義は下記のようになります。

setStrokeColorRGB
def setStrokeColorRGB(r, g, b, alpha=None)

r は赤色の強さ、g は緑色の強さ、b は青色の強さをそれぞれ 0 から 1 の小数の値で設定します。

色を赤色に設定したい場合は r のみ 1、他を 0 にする、青紫色に設定したい場合は r0.5g0b1にする、といった感じです。

塗り潰しの色の設定

長方形や円描画時に fill1 に設定した場合は図形の塗り潰しが行われます。

この時の塗り潰しの色を Canvas クラスの setFillColor メソッドや setFillColorRGB メソッド等で変更することが可能です。

これらは setStrokeColorsetStrokeColorRGB と線の色を設定するか塗り潰しの色を設定するかの違いしかなく、引数の意味合いも同じですので、メソッドの定義や引数についての説明は省略させていただきます。

線の太さの設定

線や長方形や円の枠線の太さを Canvas クラスの setLineWidth メソッドにより変更することができます。

setLineWidth
def setLineWidth(width)

setLineWidth メソッドで線の太さを設定したのちに、linerectcircle メソッドを実行すれば、線や枠線が width で指定した太さで描画されます。

例えば線の太さを 10mm にする際には下記のように setLineWidth メソッドを実行します。

setLineWidthの使用例
cc.setLineWidth(10 * mm)
cc.circle(100 * mm, 150 * mm, 50)

展開したページデータへのテキストの描画

展開したページデータにテキストを描画することもできます。

次はその方法を解説していきます。

テキストの描画

座標や色の扱いについては、展開したページデータへの図形の描画で解説したものと同様になりますのでここではこれらの解説は省きます。

テキストの描画

テキストの描画は Canvas クラスの drawString メソッドにより行うことができます。

drawString
def drawString(x, y, text, mode=None, charSpace=0, direction=None, wordSpace=None)

drawString メソッドを実行することで、座標 (x, y) を左下とした位置に、text で指定した文字列を描画することができます。

テキストの描画

charSpace を指定することで、文字と文字との間隔を設定することもできます。

その他の引数については詳細はわかっていないです…。mode を変更すると太字になったりしたのでフォントの詳細を設定する引数なのかもしれません…。

例えば、原点(ページの左下)から横方向に 10mm、縦方向に 20mm 離れた位置にテキスト “Hello, World” を描画する際には、下記のように drawString メソッドを実行します。

drawStringの使用例
cc.drawString(10 * mm, 20 * mm, "Hello, World")

フォントの設定

描画するテキストのフォントを設定することも可能です。

これには Canvas クラスの setFont メソッドを使用します。

setFont
def setFont(psfontname, size, leading=None)

psfontname に指定したフォント名および size に指定したサイズにフォントが設定されますので、この後に drawString メソッドを実行することで設定後のフォントを用いてテキスト描画を行うことができます。

どんなフォントが利用できるかは、reportlab.pdfbase.pdfmetrics モジュールに用意されている getRegisteredFontNames 関数により取得することができます。

使用できるフォント名の取得
print(pdfmetrics.getRegisteredFontNames())

日本語の描画

ReportLab では日本語のテキストの描画を行うためには、setFont メソッドで「日本語を描画できるフォント名」を設定した後に drawString メソッドを実行する必要があります。

これを行わないと文字化けしてしまいます。

ただし ReportLab の初期状態では日本語を描画できるフォント名が登録されていないので、事前に登録を行い、その後 setFont メソッドを実行する必要があります。

この辺りの解説は下記ページが詳しいと思います。

Pythonで日本語をPDFに出力する(ReportLabを利用)

このページによれば HeiseiKakuGo-W5 は ReportLab で使用できる&日本語を表示できるフォントとのことですので、下記のように事前に HeiseiKakuGo-W5 を登録した後に setFont メソッドを実行することで日本語の描画を行うことができます。

日本語テキストの描画
font_name = "HeiseiKakuGo-W5"
pdfmetrics.registerFont(UnicodeCIDFont(font_name))
cc.setFont(font_name, 30)
cc.drawString(10 * mm, 10 * mm, "こんにちは、世界")

色の設定

文字の色は Canvas クラスの setFillColor メソッドや setFillColorRGB メソッド等で変更することが可能です。

これらのメソッドの詳細は塗り潰しの色の設定線の色の設定で解説していますので、こちらを読んでいただければと思います。

これらも setFont メソッド同様に、drawString メソッドよりも前の段階で実行します。

例えば文字の色を青紫色&半透明に設定するのであれば下記のように setFillColorRGB を実行します。

setFillColorRGBの使用例
cc.setFillColorRGB(0.5, 0, 1, 0.5)
font_name = "HeiseiKakuGo-W5"
pdfmetrics.registerFont(UnicodeCIDFont(font_name))
cc.setFont(font_name, 30)
cc.drawString(10 * mm, 10 * mm, "こんにちは、世界")

スポンサーリンク

ページデータの確定

ページに対して必要な図形やテキストの描画が完了した際には、終了処理としてページデータを確定させます。

このページデータの確定は Canvas クラスの showPage メソッドにより行うことができます。

ページデータの確定
# ページデータの確定
cc.showPage()

ページデータの確定とは、要は ReportLab に、このページの編集は完了したよーってことを伝える処理です。

これにより、次に cc.doForm が実行された際には、引数で指定したページデータが新しいページとして PDF データに展開されることになります。

逆にページデータを確定させずに cc.doForm を実行すると、引数で指定したページデータが今まで編集していたページデータに上書きされる形で PDF データに展開されてしまいます。

showPage メソッドを実行すれば、続けて次のページに対して図形の描画を行うことができます。

この場合はPDF のページデータ取得に戻って再度ページデータ取得から図形描画&ページデータの確定を繰り返すことになります(具体的なスクリプトは最後に紹介します)。

PDF の保存

最後に図形やページを描画した PDF を保存して完了です。

PDFの保存

この PDF の保存は Canvas クラスの save メソッドにより行うことができます。

PDFの保存
# PDFの保存
cc.save()

これにより、PdfReader により読み込んだ PDF の上に図形やテキストを描画したデータが canvas.Canvas に指定したパスに PDF として保存されることになります。

ただし保存されるのは showPage メソッドで確定したページだけなので注意してください。

複数ページ保存する場合は、save メソッド実行前に、そのページ分 showPage メソッドを実行する必要があります。

PDF に図形やテキストを描画するスクリプトの例

最後にここまで解説してきた内容を踏まえてスクリプトを紹介したいと思います。

input.pdf という PDF ファイルを用意しておけば、スクリプトを実行して実際に図形描画後の PDF が出来上がるところを確認することもできます。

保存ファイル名は output.pdf としています。

注意

実行すると output.pdf というファイルに PDF が保存されますので、もし既に output.pdf がある場合は事前に退避しておいてください

スクリプトを変更することで動きがどのように変わるかを観察してみると、より pdfrw や ReportLab の動作の理解が深まると思います。

スポンサーリンク

PDF の最初のページに図形やテキストを描画するスクリプト

まず紹介するのが最初のページのみに図形やテキストを描画するスクリプトになります。

最初のページのみに描画
from pdfrw import PdfReader
from pdfrw.buildxobj import pagexobj
from pdfrw.toreportlab import makerl
from reportlab.pdfgen import canvas
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.pdfbase import pdfmetrics
from reportlab.lib.units import mm

in_path = "input.pdf"
out_path = "output.pdf"

# 保存先PDFデータを作成
cc = canvas.Canvas(out_path)

# PDFを読み込む
pdf = PdfReader(in_path, decompress=False)

# PDFのページデータを取得
page = pdf.pages[0]

# PDFデータへのページデータの展開
pp = pagexobj(page) #ページデータをXobjへの変換
rl_obj = makerl(cc, pp) # ReportLabオブジェクトへの変換  
cc.doForm(rl_obj) # 展開

# 円の描画
cc.setFillColorRGB(0.5, 0, 1, 0.5)
cc.setLineWidth(5 * mm)
cc.circle(100 * mm, 150 * mm, 50, fill=1)

# 線の描画
cc.setLineWidth(10 * mm)
cc.setStrokeColor("green")
cc.line(10 * mm, 20 * mm, 200 * mm, 274 * mm)

# 長方形の描画
cc.setFillColor("white", 0.5)
cc.setStrokeColorRGB(1.0, 0, 0)
cc.rect(0 * mm, 0 * mm, 100 * mm, 100 * mm, fill=1)

# 英語の描画
cc.setFillColor("purple")
cc.drawString(10 * mm, 10 * mm, "Hello, World")

# 日本語の描画
cc.setFillColorRGB(0, 1, 1, 0.5)
font_name = "HeiseiKakuGo-W5"
pdfmetrics.registerFont(UnicodeCIDFont(font_name))
cc.setFont(font_name, 30)
cc.drawString(10 * mm, 10 * mm, "こんにちは、世界")

# ページデータの確定
cc.showPage()

# PDFの保存
cc.save()

PDF の全ページに図形やテキストを描画するスクリプト

次に紹介するのが全ページに図形やテキストを描画するスクリプトになります。

といってもループ処理が増えただけです。

全ページに描画
from pdfrw import PdfReader
from pdfrw.buildxobj import pagexobj
from pdfrw.toreportlab import makerl
from reportlab.pdfgen import canvas
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.pdfbase import pdfmetrics
from reportlab.lib.units import mm

in_path = "input.pdf"
out_path = "output.pdf"

# 保存先PDFデータを作成
cc = canvas.Canvas(out_path)

# PDFを読み込む
pdf = PdfReader(in_path, decompress=False)

# PDFのページデータを取得
for page in pdf.pages:

	# PDFデータへのページデータの展開
	pp = pagexobj(page) #ページデータをXobjへの変換
	rl_obj = makerl(cc, pp) # ReportLabオブジェクトへの変換  
	cc.doForm(rl_obj) # 展開

	# 円の描画
	cc.setFillColorRGB(0.5, 0, 1, 0.5)
	cc.setLineWidth(5 * mm)
	cc.circle(100 * mm, 150 * mm, 50, fill=1)

	# 線の描画
	cc.setLineWidth(10 * mm)
	cc.setStrokeColor("green")
	cc.line(10 * mm, 20 * mm, 200 * mm, 274 * mm)

	# 長方形の描画
	cc.setFillColor("white", 0.5)
	cc.setStrokeColorRGB(1.0, 0, 0)
	cc.rect(0 * mm, 0 * mm, 100 * mm, 100 * mm, fill=1)

	# 英語の描画
	cc.setFillColor("purple")
	cc.drawString(10 * mm, 10 * mm, "Hello, World")

	# 日本語の描画
	cc.setFillColorRGB(0, 1, 1, 0.5)
	font_name = "HeiseiKakuGo-W5"
	pdfmetrics.registerFont(UnicodeCIDFont(font_name))
	cc.setFont(font_name, 30)
	cc.drawString(10 * mm, 10 * mm, "こんにちは、世界")

	# ページデータの確定
	cc.showPage()

# PDFの保存
cc.save()

まとめ

このページでは Python で PDF に図形やテキストを描画する方法について解説しました。

PDF に図形やテキストを描画する方法は下記の通りです。

  • ライブラリのインポート
  • PDF データの作成
  • PDF の読み込み
  • PDF のページデータ取得
  • PDF データへのページデータの展開
  • 展開したページデータへの図形の描画
  • 展開したページデータへのテキストの描画
  • ページデータの確定
  • PDF の保存

pdfrw と ReportLab を使えば比較的簡単に PDF への描画できるようになると思います。

今回紹介したもの以外にも様々な機能があるようですので、是非みなさんもいろいろ試してみてください!

下記ページではここで紹介した PDF への図形やテキストの描画を応用した、PDF 編集アプリの作り方についても解説しています!

アプリへの仕立て方なども解説していますので、是非こちらのページも読んでみてください!

PDF編集アプリの作り方解説ページのアイキャッチ【Python】PDF編集アプリを開発

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です