前回の続きです。
www.te-tou.tokyo
DjangoとweasyprintでPDF出力は無事に出来たものの、激しく文字化けしていました。
ついでに用紙が縦になっていたり、セルの中の文字が予定外に折り返していたり、印刷したいURLをviews.pyに引き渡したりする方法がよく分かりませんでしたが一通り解決しました。
日本語文字化け解消
views.pyの中でCSSを指定して、フォントを設定してあげたら解消しました。
from .models import Person from django.http import HttpResponse from django.template.loader import render_to_string # CSSもインポートしておく from weasyprint import HTML, CSS 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({ },request) pdf_file = HTML(request.GET.get('path')).write_pdf( stylesheets=[ # CSSを指定してフォントを設定することで、文字化け解消。 CSS(string='body { font-family: serif !important }'), ], ) response = HttpResponse(pdf_file, content_type='application/pdf') response['Content-Disposition'] = 'filename="scoringsheet.pdf"' return response
出力結果文字化け解消しています。
ついでに後述する以下も解消されている状態です。
- 印刷用紙を横向に設定
- セル内の文字折り返ししない
- 印刷したいページのURLをtemplateからviews.pyに引き渡し
細かいことは抜きにして、結構きれいにPDF出力出来たと思います。
用紙の設定
weasyprintでlandscapeです。
テンプレートファイル(HTML)にスタイル属性を追加します。
この中に用紙設定を書きます。
<style> @page { size: landscape; margin: 0.2in 0.1in 0.2in 0.1in; </style>
landscapeが用紙横印刷ですね。
この辺りにA3とか書いておけば、印刷用紙サイズも変更できます。初期値はA4みたいです。
ついでに用紙の余白も設定しときます。
セル内の文字折り返し禁止
これもテンプレートファイル(HTML)にスタイル属性を設定します。
<style> @page { size: landscape; margin: 0.2in 0.1in 0.2in 0.1in; # 文字折り返し禁止 table tr span { white-space: nowrap; } </style>
white-space: nowrap;です。
「table tr span」のclass名は、table rawで横列(ヘッダ)部分を折り返し禁止にしています。
印刷したいページのURLをviews.pyに引き渡す。
PDF変換する流れは、
- 対象のページへアクセス
- 「PDF出力」ボタンを押下し、対象のページをPDF変換
という想定です。
そのため、まず始めに対象ページに「ボタン」をつけます。
template(HTML)こんな感じです。
次にそのボタンに自分のページのURLを引数として持たせます。
Django(python?)で今アクセスしているページのURLを取得する方法は、
{{ request.build_absolute_uri }}
と書くと、フルパスを取得してくれます。
ソースは下記の通り。
template(HTML)
<form action="/exportpdf/shinsa/" method="get" target="_blank"> <input type="hidden" name="path" value={{ request.build_absolute_uri }} > <button type="submit"> PDF出力 </button> </form>
これでボタンを押下した際に、自分のページのURLを引数に持たせることができました。
次に、views.pyで受け取ります。
viewsで引数を受け取るために、
request.GET.get('name属性')
これでリクエストを受け取ります。
views.py
from .models import Person from django.http import HttpResponse from django.template.loader import render_to_string from weasyprint import HTML, CSS 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({ },request) # request.GET.get('ボタンのnameを指定') pdf_file = HTML(request.GET.get('path')).write_pdf( stylesheets=[ CSS(string='body { font-family: serif !important }'), ], ) response = HttpResponse(pdf_file, content_type='application/pdf') response['Content-Disposition'] = 'filename="scoringsheet.pdf"' return response
これでviews.pyにURLを直書きしなくても、印刷したいページのURLを受け渡せます。
templateファイルを指定しているので、なんでも良いわけでは無いと思います。が、同一のページでクエリで集計していたり抽出している結果を出力したい場合に有効だと思います。
はっきり言って仕組みを完全に理解していないので、そこそこ怪しいです。
まとめ
普段バッチプログラムとか作っていると、htmlを忘れるので当然のことがなかなかできなくなります。
特にCSS。
課題
CSSファイルをstaticで作ったのにファイルが読み込めません。的なエラーが出てしまいました。
なので今はtemplate(HTML)ファイルにスタイルシートを直書きしてます。
はっきり言ってダサいので、この辺りをきれいにしたいです。