te-touのブログ

音楽制作・車・プログラミング・旅行・その他考えてる事など、備忘録的なことも含めて個人的なページです。

Django weasyprintでPDF出力。(インストールと各種エラー対応と最終的に文字化け)




掲題の通り、Djangoで作成した画面をweasyprintを使ってPDF出力して印刷できるようにします。


まずは、weasyprintをインストールします。
しかし、いろんな物に依存関係があるようなので色々パッケージのインストールが必要です。
エラーを確認しながら進めます。なんか行き当たりばったりです。

環境
  • python : 3系
  • Django : version 3.0.2
  • SQLite
  • macOS Catalina

とりあえず、何かしらのアプリが作成されていることが前提です。
ついでにHomebrewもインストールされていることも前提です。

weasyprintインストール

Djangoのバーチャル環境にインストールするので、アプリをアクティベートしてからターミナルで以下のコマンドを打ちます。

pip install WeasyPrint

pipを最新化しろとメッセージが出たので、アップグレードします。

python3 -m pip install --upgrade pip
ソース

ざっくりソースを書きます。
views.py

from .models import Person
from django.http import HttpResponse
from django.template.loader import render_to_string
from weasyprint import HTML
import tempfile

def generate_pdf(request):
    from django.template.loader import get_template
# テンプレートを指定
    html_template = get_template('testapp/scoringsheet_list.html') 

    html_str = html_template.render({
#引数とかある場合は、ここに設定する。
      'Member': Member,
      'Scoringsheet': Scoringsheet,
                },request)  

    pdf_file = HTML('http://127.0.0.1:8000/scoringsheet?event=4').write_pdf(
# weasyprintではCSSを指定してあげないと参照してくれないようなので、必要なら指定する。
# 今回は特に使わないのでコメント
#        stylesheets=[
#           CSS('/static/css/something.css')
#        ],
    )

    response = HttpResponse(pdf_file, content_type='application/pdf')
# ダウンロードした時のファイル名称を設定。
    response['Content-Disposition'] = 'filename="scoringsheet.pdf"'

    return response

urls.py

urlpatterns = [
# weasyprint
    path('generate/pdf/', views.generate_pdf, name='generate_pdf'),
]
試しにPDF化実行すると、各種エラー発生

urlsに書いたアドレスにアクセス。
ローカルサーバーだと、以下のようなアドレスです。
http://127.0.0.1:8000/generate/pdf/

エラーが出ました。

django OSError: no library called "cairo" was found

cairoってのが無いようなので、サーバーを落としてHomebrewでインストールします。

brew install python3 cairo pango gdk-pixbuf libffi

再度サーバー起動。またエラー。

no library called "libcairo-2" was found
cannot load library 'libcairo.so': dlopen(libcairo.so, 2): image not found
cannot load library 'libcairo.2.dylib': dlopen(libcairo.2.dylib, 2): image not found
cannot load library 'libcairo-2.dll': dlopen(libcairo-2.dll, 2): image not found

なんか、色々ネットで対処法を探しているとpython3系ならpip3でインストールしないといけない。らしいので再度weasyprintをインストール。

pip3 install WeasyPrint

必要かどうか不明ですが、ここでもう一度cairoをインストール。

brew install cairo

また別のエラーが出ました。
もしかしたら、前にも出ていたかもしれませんがここで気がつきました。

Error: The following formula
  [#<Dependency: "python@3.8" []>, #<Options: []>]
cannot be installed as binary package and must be built from source.
Install the Command Line Tools:
  xcode-select --install

xcodeがバーチャル環境にちゃんと入っていないのかな?
よくわかりませんが、サーバーを落としてxcodeをインストール。

xcode-select --install

再度サーバー起動。またエラー。

    raise OSError(msg)
OSError: cannot load library 'pango-1.0': dlopen(pango-1.0, 2): image not found.  Additionally, ctypes.util.find_library() did not manage to locate a library called 'pango-1.0'

pangoってのが無いようなので、サーバー落としてpangoをインストール。

brew install pango

サーバー再起動。成功です。

PDF出力テスト

PDF出力するURLにアドレスにアクセスして、PDF出力テストします。
派手に文字化けしてしまいました。
(Djangoのテンプレート画面)

f:id:te-tou:20200614144612p:plain
Django テンプレート
(weasyprintでPDF出力結果)
f:id:te-tou:20200614144752p:plain
weasyprint PDF出力結果

日本語と数字が全滅です。
HTMLにはちゃんと文字コード指定をしてるのですが、関係ないようです。
ちなみに改ページしても、ヘッダはページごとについてきたので安心しました。

今後の課題

まずは上記の文字化けをなんとかしないといけません。
それが出来たら、A4で横向き(landscape)出力設定をしたいです。
現状調べたところ、よくわからないです。

まとめ

weasyprintはインストールが一つでは済まないのが難点です。
ソース自体はサンプルコードが結構落ちているので、とりあえずはなんとかなります。
しかし、細かい不具合についての対処法はあまり無いように感じました。この辺りは結構努力が必要そうです。
最後に、Djangoのテンプレート通りに出力はしてくれない様なのでCSSはweasyprint用にCSSはちゃんと指定した方が良いみたいです。

ついでに

(上記のソースでは関係ありませんが)ソースの書き方によっては、日本語があるとユニコードエラーが出るようなのでoutput.nameを以下のように変更。

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb5 in position 10: invalid start byte
output = open(output.name, 'r')
↓fix
output = open(output.name, 'rb')




www.facebook.com
www.youtube.com