Django PDF 出力

1. 日本語対応フォントの印トール

日本語対応フォントを、staticディレクトリにインストールする

2. xhtml2pdf のインストール

pip install xmhtl2pdf

3.Viewの作成

Renderクラスを作成。このクラスをHTMLで作成されたデータを
xhml2pdfのpisaDocuemtクラスを使用しPDFへ変換する
処理を行うクラス。renderメソッドが処理で、リンク先のファイルを
読み込みをlink_callback 関数で定義している。

class Render:

    @staticmethod
    def render(path: str, params: dict):
        template = get_template(path)
        html = template.render(params)
        response = BytesIO()
        pdf = pisa.pisaDocument(BytesIO(html.encode("UTF-8")),
                                response,
                                encoding='utf-8',
                                link_callback=Render.link_callback)
        if not pdf.err:
            return HttpResponse(response.getvalue(), content_type='application/pdf')
        else:
            return HttpResponse("Error Rendering PDF", status=400)


    @staticmethod
    def link_callback(uri, rel):
        sUrl = settings.STATIC_URL
        sRoot = settings.STATIC_ROOT
        path = os.path.join(sRoot, uri.replace(sUrl, ""))

        if not os.path.isfile(path):
            raise Exception(
                '%s must start with %s' % \
                (uri, sUrl)
            )


        return path

注意
settings.pyはSTATIC_ROOTを追加しておく

STATIC_ROOT = os.path.join(BASE_DIR, 'static')

python manage.py collectstatic 実行しておく

4 View関数の作成

戻り値にRenderクラスのrenderを呼び出すように作成する

class Pdf(View):

    def get(self, request):
        orders = Order.objects.all()
        today = timezone.now()
        params = {
            'today': today,
            'orders': orders,
            'request': request
        }
        return Render.render('pdf.html', params)
_

URLの指定

urlpatterns = [
    ・・・
    path('render/pdf/', Pdf.as_view()),

5.テンプレートの作成

テンプレートファイルに日本語のフォントを指定し、作成する。

<!doctype html>
{% load static %}
<html>
<head>
    <meta charset="utf-8">
    <title>Sales Report</title>
    <style type="text/css">
         @font-face {
            font-family: "japanese";
            src: url("{% static 'ipam.ttf' %}");
        }
        @page {
            size: A4;
            margin: 1cm;
            font-family: "japanese";
        }
        html, body {
            font-family: "japanese";
        }
        .table {
            width: 100%;
            max-width: 100%;
            margin-bottom: 5px;
            background-color: #fff;
        }
        .table th,
        .table td {
            padding: 5px;
            vertical-align: top;
            border-top: 1px solid #000;
            text-align: center;
        }
        .table thead th {
            vertical-align: bottom;
            border-bottom: 2px solid #000;
        }
        .table tbody + tbody {
            border-top: 2px solid #000;
        }
        .table .table {
            background-color: #fff;
        }
        .list-group {
            display: block;
            width: 100%;
            list-style: none;
            margin-top: 15px;
            margin-bottom: 15px;
        }
        .list-group p {
            width: 100%;
            height: 20px;
            line-height: 20px;
            list-style: none;
            font-size: 1.1em;
        }
    </style>
</head>
<body>

<div class="container">
    <div class="card">
        <div class="card-header">
            <h3>Sales Report - {{ today | date:"d/m/Y" }}</h3>
        </div>

        <div class="list-group">
            <p>Name: {{ request.user.first_name }} {{ request.user.last_name }}</p>
        </div>

        <table class="table">
            <thead>
            <tr>
                <th>Date</th>
                <th>Customer</th>
                <th>Subject</th>
                <th>Price</th>
            </tr>
            </thead>
            <tbody>
            {% for order in orders %}
                <tr>
                    <td>{{ order.created_at | date:"Y/m/d" }}</td>
                    <td>{{ order.customer.name }}</td>
                    <td>{{ order.subhect }}</td>
                    <td>¥{{ order.price }}</td>
                </tr>
            {% endfor %}
            </tbody>
        </table>
    </div>
</div>

</body>
</html>