このページでは Python で PDF に図形やテキストを描画する方法について解説していきます。
例えばですが、下の図のような PDF のページがあった場合、
下の図のようにそのページの上に長方形や円、テキストなどを描画し、それを新たな PDF ファイルとして保存する方法の解説になります。
Contents
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データの作成は reportlab.pdfgen モジュールで定義されている Canvas
クラスのコンストラクタ実行により行うことができます。
# 保存先PDFデータを作成
cc = canvas.Canvas(out_path)
out_path
には図形やテキストを描画した PDF の保存先ファイルパスを指定します。
後に解説する save
メソッドを実行することで、out_path
に指定したパスに PDF が保存されることになります。
今後の解説では cc
というオブジェクトが多く登場しますが、全てこの canvas.Canvas
のインスタンスであると考えてください。
PDF の読み込み
次は図形やテキストを描画したい PDF ファイルを読み込みます。
PDF の読み込みは pdfrw の PdfReader
クラスのコンストラクタ実行により行うことができます。
# PDFを読み込む
pdf = PdfReader(in_path, decompress=False)
in_path
には図形やテキストを描画する基となる PDF のファイルパスを指定します。
以降で説明する処理では、この in_path
で指定した PDF に対して図形の描画を行い、out_path
に指定したファイルパスに保存することになります。
PDF のページデータ取得
さらに読み込んだ PDF ファイルからページデータを取得します。
PdfReader
クラスのインスタンスは、pages
属性を持っており、コレは 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
関数になります。
これらの変換を行うスクリプトは下記のようになります。
pp = pagexobj(page) #ページデータをXobjへの変換
rl_obj = makerl(cc, pp) # ReportLabオブジェクトへの変換
rl_obj
は ReportLab のオブジェクトですので、コレを PDF データに展開します。この展開は Canvas
クラスの doForm
メソッドにより行うことができます。
cc.doForm(rl_obj) # 展開
展開したページデータへの図形の描画
いよいよここから展開したページデータの上に図形を描画していきます。
まずはテキストを除く図形の描画について説明していきます。
ReportLab の座標系
ReportLab では、図形を描画するにあたって、どの位置に描画するかを座標で指定してやる必要があります。
ReportLab で扱う座標はちょっとクセがあるので、ここで説明しておきます。
ReportLab で扱う座標では、原点はページの左下になります。
さらに、縦方向の正方向が上、横方向の正方向が右です。
プログラミングで扱う画像は、原点はページの左上、縦方向の正方向が下、横方向の正方向が右のものが多いです。
これらとは原点の位置と縦方向の正方向が逆である点に注意してください。
長方形の描画
続いて実際に描画を行う方法について解説していきます。
まずは長方形です。
長方形の描画は Canvas
クラスの rect
メソッドにより行うことができます。
def rect(x, y, width, height, stroke=1, fill=0)
rect
メソッドを実行することで、座標 (x, y
) を長方形の “左下” とした位置に、幅 width
、高さ height
の長方形が描画されます。
stroke
は枠線の有無を設定する引数で、1
を指定すれば枠線あり、0
を指定すれば枠線なしになります。
fill
は塗り潰しの有無を設定する引数で、1
を指定すれば塗り潰しあり、0
を指定すれば塗り潰しなしになります。
x
、y
、width
、height
で指定する値の単位が何なのかは正直私も分かっていないです…。
が、指定する値に reportlab.lib.units モジュールの mm
を掛けてやれば、mm
単位での指定を間接的に行うことができます。
例えば、原点(ページの左下)から横方向に 10mm、縦方向に 20mm 離れた位置に幅 30mm、高さ 40mm、枠線あり、塗り潰しありの長方形を描画する際には、下記のように rect
メソッドを実行します。
cc.rect(10 * mm, 20 * mm, 30 * mm, 40 * mm, 1, 1)
円の描画
次は円の描画です。
円の描画は Canvas
クラスの 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
メソッドを実行します。
cc.circle(100 * mm, 120 * mm, 40 * mm, 1, 1)
線の描画
次は線の描画です。
線の描画は Canvas
クラスの line
メソッドにより行うことができます。
def line(x1, y1, x2, y2)
line
メソッドを実行することで、座標 (x1, y1
) から座標 (x2, y2
) に線を描画することができます。
例えば、原点(ページの左下)から横方向に 10mm、縦方向に 20mm 離れた位置から、原点から横方向に 200mm、縦方向に 274mm 離れた位置への線を描画する際には、下記のように line
メソッドを実行します。
cc.line(10 * mm, 20 * mm, 200 * mm, 274 * mm)
線の色の設定
描画する線や、長方形や円描画時の枠線の色を Canvas
クラスの setStrokeColor
メソッドや setStrokeColorRGB
メソッド等で変更することが可能です。
まず setStrokeColor
メソッドの定義は下記のようになります。
def setStrokeColor(aColor, alpha=None)
aColor
には色名を指定することができます(カラーオブジェクトを渡すこともできるらしい)。
また alpha
に 0
から 1
の小数の値を設定することで、色の透明度を設定することが可能です。値が小さいほど透明度が上がります。例えば 0.5
を指定すれば半透明の色になります。
線だと分かりにくいですが、次に説明する塗り潰しの色設定で設定すると透明度による見た目の違いが分かりやすいと思います。
続いて、setStrokeColorRGB
メソッドの定義は下記のようになります。
def setStrokeColorRGB(r, g, b, alpha=None)
r
は赤色の強さ、g
は緑色の強さ、b
は青色の強さをそれぞれ 0
から 1
の小数の値で設定します。
色を赤色に設定したい場合は r
のみ 1
、他を 0
にする、青紫色に設定したい場合は r
を 0.5
、g
を 0
、b
を 1
にする、といった感じです。
塗り潰しの色の設定
長方形や円描画時に fill
を 1
に設定した場合は図形の塗り潰しが行われます。
この時の塗り潰しの色を Canvas
クラスの setFillColor
メソッドや setFillColorRGB
メソッド等で変更することが可能です。
これらは setStrokeColor
、 setStrokeColorRGB
と線の色を設定するか塗り潰しの色を設定するかの違いしかなく、引数の意味合いも同じですので、メソッドの定義や引数についての説明は省略させていただきます。
線の太さの設定
線や長方形や円の枠線の太さを Canvas
クラスの setLineWidth
メソッドにより変更することができます。
def setLineWidth(width)
setLineWidth
メソッドで線の太さを設定したのちに、line
や rect
、circle
メソッドを実行すれば、線や枠線が width
で指定した太さで描画されます。
例えば線の太さを 10mm にする際には下記のように setLineWidth
メソッドを実行します。
cc.setLineWidth(10 * mm)
cc.circle(100 * mm, 150 * mm, 50)
展開したページデータへのテキストの描画
展開したページデータにテキストを描画することもできます。
次はその方法を解説していきます。
座標や色の扱いについては、展開したページデータへの図形の描画で解説したものと同様になりますのでここではこれらの解説は省きます。
テキストの描画
テキストの描画は Canvas
クラスの 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
メソッドを実行します。
cc.drawString(10 * mm, 20 * mm, "Hello, World")
フォントの設定
描画するテキストのフォントを設定することも可能です。
これには Canvas
クラスの setFont
メソッドを使用します。
def setFont(psfontname, size, leading=None)
psfontname
に指定したフォント名および size
に指定したサイズにフォントが設定されますので、この後に drawString
メソッドを実行することで設定後のフォントを用いてテキスト描画を行うことができます。
どんなフォントが利用できるかは、reportlab.pdfbase.pdfmetrics モジュールに用意されている getRegisteredFontNames
関数により取得することができます。
print(pdfmetrics.getRegisteredFontNames())
日本語の描画
ReportLab では日本語のテキストの描画を行うためには、setFont
メソッドで「日本語を描画できるフォント名」を設定した後に drawString
メソッドを実行する必要があります。
これを行わないと文字化けしてしまいます。
ただし ReportLab の初期状態では日本語を描画できるフォント名が登録されていないので、事前に登録を行い、その後 setFont
メソッドを実行する必要があります。
この辺りの解説は下記ページが詳しいと思います。
このページによれば 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
を実行します。
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 の保存は Canvas
クラスの save
メソッドにより行うことができます。
# 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 編集アプリの作り方についても解説しています!
アプリへの仕立て方なども解説していますので、是非こちらのページも読んでみてください!
【Python】PDF編集アプリを開発