iMind Developers Blog

iMind開発者ブログ

PythonのPyFPDFでHTMLからPDF生成

概要

PyFPDFを使ってHTMLからPDFを生成する。

バージョン情報

  • Python 3.7.6
  • fpdf==1.7.2

インストール

$ pip install fpdf 

簡易なPDFの作成

まずは公式サイトの説明にもあるミニマムなコードでPDFファイルを作成してみる。

from fpdf import FPDF

pdf = FPDF()
pdf.add_page()
pdf.set_font('Arial', 'B', 16)
pdf.cell(40, 10, 'Hello World!')
pdf.output('tuto1.pdf', 'F')

f:id:mwsoft:20200214205046p:plain

set_fontとset_font_size

続いてフォントサイズを変えながら出力してみる。

set_fontのパラメータはこんな感じ。

set_font(family, style='', size=0)

先ほど実行した例文のBはbold。その他に I(Italic)と U(Underline)が指定できる。

サイズだけを変えたい場合は set_font_size を使う。

試しにUnderlineを指定して、フォントサイズを16, 24, 32と変えながらhello worldを表示してみる。

pdf = FPDF()
pdf.add_page()
pdf.set_font('Arial', 'U', 16)

for font_size in [16, 24, 32]:
    pdf.set_font_size(font_size)
    pdf.ln()  # line break
    pdf.cell(100, font_size / 2, 'Hello World!')

pdf.output('tuto1.pdf', 'F')

途中で入っている pdf.ln() はline breakで、これを入れないとセルが右に並ぶ形になる。

f:id:mwsoft:20200214210120p:plain

日本語フォントの指定

上述のset_fontで指定しているfamilyのArialは日本語には対応していないので、表示するテキストに日本を入れると下記のようなエラーになる。

UnicodeEncodeError: 'latin-1' codec can't encode character '\u3042' in position 95: ordinal not in range(256)

フォントをaddして日本語を表示してみる。

Linuxの例だとまずはfc-listでフォントファイルのパスを確認。

$ fc-list

出力された中から適当なフォントを選んでadd_fontして、set_fontする。

add_fontの内容は下記。

add_font(family, style='', fname='', uni=False)

familyに適当な名前を指定して、fnameにフォントのパスを入れ、日本語の場合はuni=Trueを指定しておく。

pdf = FPDF()
pdf.add_page()

# fontをaddする
font_path = '/usr/share/fonts/truetype/fonts-japanese-gothic.ttf'
pdf.add_font('gothic', fname=font_path, uni=True)

# 上で登録したfontをsetする
pdf.set_font('gothic')

pdf.cell(10, 10, 'あいうえお')
pdf.output('tuto1.pdf', 'F')

f:id:mwsoft:20200214212621p:plain

set_xy

set_xyでポジションを指定して任意の場所にテキストを表示してみる。

pdf = FPDF()
pdf.add_page()
pdf.set_font('Arial', 'B', 16)

pdf.set_xy(30, 50)
pdf.cell(50, 10, '30x50')

pdf.set_xy(100, 25)
pdf.cell(50, 10, '100x25')

pdf.output('tuto1.pdf', 'F')

f:id:mwsoft:20200214212719p:plain

line

lineで線を引いてみる。

pdf = FPDF()
pdf.add_page()
pdf.set_font('Arial', 'B', 16)

# テキストの表示
pdf.set_xy(30, 50)
pdf.cell(50, 10, '30x50')

# これに被るような位置に線を表示
pdf.line(20, 40, 60, 70)

pdf.output('tuto1.pdf', 'F')

lineの引数はそれぞれx1, y1, x2, y2。

画像を表示してみる

pdf.imageで画像を表示できる。

image(name, x=None, y=None, w=0, h=0, type='', link='')
pdf = FPDF()
pdf.add_page()
pdf.set_font('Arial', 'B', 16)

# 画像の表示
pdf.image('dog.jpg', x=10, y=20, w=60, h=60)

# 上からテキストを表示
pdf.set_xy(30, 20)
pdf.cell(10, 10, 'pretty dog')

pdf.output('tuto1.pdf', 'F')

f:id:mwsoft:20200214213156p:plain

pdf.imageへの引数はファイル名かURLのみ指定できるようで、bytesやio.BytesIOなどで引数を渡すことはできない。

一旦、tempfileに保存して処理をするしか無いのだろうか。

下記はNamedTemporaryFileでwithスコープを外れたらファイルが消える形で一時ファイルを書くケース。

import tempfile

pdf = FPDF()
pdf.add_page()
pdf.set_font('Arial', 'B', 16)

# imageをbyesで手元に持っていたとする
with open('dog.jpg', 'rb') as fp:
    image_data = fp.read()

with tempfile.NamedTemporaryFile() as temp:
    # tempfileに一度書き込む
    temp.write(image_data)
    temp.flush()

    # 画像を読み込む
    pdf.image('dog.jpg', x=10, y=20, w=60, h=60)

# 上からテキストを表示
pdf.set_xy(30, 20)
pdf.cell(10, 10, 'pretty dog')

pdf.output('tuto1.pdf', 'F')

HTMLからPDFに変換

HTMLをPDFに変換する場合は、HTMLMixinを継承したクラスを作り、write_htmlを呼び出す。

from fpdf import FPDF, HTMLMixin

class HtmlFPDF(FPDF, HTMLMixin):
    pass

html = '<html><h1>Hello World!</h1></html>'

pdf = HtmlFPDF()
pdf.add_page()
pdf.write_html(html)
pdf.output('tuto1.pdf', 'F')

残念な点として、PyFPDFではCSSには対応していないらしい。HTML/CSSからPDFを作りたい場合はxhtml2pdf等の他のライブラリを検討する必要がある。

改定履歴

Author: Masato Watanabe, Date: 2020-03-07, 記事投稿