python生成pdf的集中方式比较

https://qiita.com/godan09/items/13866970972bf3a1c243

PDFで见积书を出力するにあたって関连ライブラリのメリデメを洗い出してみました。
そのライブラリの中で実装方法とアウトプットを提示し、比较できる状态にしすることが本稿の目的です。

この记事は2つの手法のPDF出力ライブラリをリスト化して比较しました。その手法と、各种ライブラリは次のとおりです。

  • HTMLをPDFを化して出力する
    • django-wkhtmltopdf
    • django_xhtml2pdf
    • WeasyPrint
  • コードからPDFを生成するもの
    • reportlab

また比较に関しては次の点に注目しています。

  • ライブラリの使いやすさ
    • 使うのは容易か
    • コードは复雑にならないか
    • 动作は重くないか
    • 动作させるために别途ソフトウェアのインストールは必要か
  • 自由度

    • A4一枚の见积书を作成するのは容易か
    • レイアウトに対して细かい设定が可能か
  • 保守性

    • メンテナンスはしやすいか
    • ライブラリの更新频度
    • 出力されたPDFのクオリティは问题なさそうか
    • ネット上の情报は多いか

TL;DR.

个人的な主観的な比较を置いておきます。

ライブラリ名使いやすさ自由度保守性备考
django-wkhtmltopdf  
django_xhtml2pdf ×  
WeasyPrint  
reportlab ×  

HTML to PDF

django-wkhtmltopdf

ドキュメント django-wkhtmltopdf 3.2.0 documentation

グーグル検索でPython PDFで検索したときに検索上位に出るwkhtmltopdfをdjango向けにラップされたライブラリです。
Djangoのクラスベースビューに対応しており非常に安易に导入できます。
生成されるPDFもデフォルトできれいに出力されています。ただ、别途ソフトウェアのインストールが必要なのでAppEngineでの导入の手间は大変そうです(要検证)。

インストール方法

$ pipenv install django-wkhtmltopdf

别途wkhtmltopdfからソフトウェアインストールが必要。

SampleCode

from wkhtmltopdf.views import PDFTemplateView

class PdfSampleView(PDFTemplateView):
    filename = 'my_psdf.pdf'    
    template_name = "pdf_sample/sample.html"

生成されたPDF

django_xhtml2pdf

PythonでHTMLをPDFに変换するライブラリ django_xhtml2pdfをDjango向けにラップしたライブラリです。
クラスベースビュー向けのmixinを提供されていて简単に使用できます。またデコレーターが标准でサポートされています。

ただしドキュメントが少なく、オプションはほとんど无い。またCSSの解釈が独特なためか通常のHTMLとは违う构成で出力されます。

xhtml2pdf / django-xhtml2pdf

インストール方法

$ pipenv install django_xhtml2pdf

SampleCode

from django_xhtml2pdf.views import PdfMixin

class Xhtml2pdfSampleView_(PdfMixin, TemplateView):
    template_name = "pdf_sample/sample.html"

生成されたPDF

WeasyPrint

wkhtmltopdfに近いPDF生成ツールとライブラリ。ドキュメントが充実しています。

wkhtmltopdfほどではないがxhtmlよりかは高品质なPDFが出力されます。xhtmlとくらべ相対的にHTMLとの出力に差分がすくない。ただ、インストールドキュメントを见るとパッケージとは别にインストールが必要とのためAppEngineではむずかしそうです(要调查)。

インストール| WeasyPrintドキュメント

WeasyPrint — WeasyPrint 51 documentation

SampleCode

from weasyprint import HTML, CS
from django.http import HttpResponse
from django.template.loader import get_templat

class WeasyPrintView(TemplateView):
    template_name = 'pdf_sample/sample.html'

    def get(self, request, *args, **kwargs):

        html_template = get_template('pdf_sample/sample.html')
        context = super().get_context_data(**kwargs)

        html_str = html_template.render(context)  
        pdf_file = HTML(string=html_str, base_url=request.build_absolute_uri()).write_pdf(
        )

        response = HttpResponse(pdf_file, content_type='application/pdf')
        response['Content-Disposition'] = 'filename="fuga.pdf"'

        return response

生成されたPDF

HardCodePDF

reportlab

Pythonのコード上で実际にレイアウトを指定して生成するライブラリです。

スタイルを含めコード上で起こすため必然的に长くなる。PDFの生成は问题なく実行できます。

またすべてのデータをコードで挿入できるため実装の自由度は非常に高いです。

オプションとドキュメントも充実しているため一通りの帐簿などの作成は可能です。

ReportLab - Content to PDF Solutions

インストール方法

$ pipenv install reportlab

SampleCode

from django.views.generic import TemplateView
from django.http import HttpResponse

from reportlab.pdfgen import canvas
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.cidfonts import UnicodeCIDFont
from reportlab.lib.pagesizes import A4, portrait
from reportlab.platypus import Table, TableStyle
from reportlab.lib.units import mm
from reportlab.lib import colors

class ReportlabView(TemplateView):
    template_name = 'pdf_sample/sample.html'

    def get(self, request, *args, **kwargs):

        response = HttpResponse(status=200, content_type='application/pdf')
        response['Content-Disposition'] = 'filename="example.pdf"'
        # response['Content-Disposition'] = 'attachment; filename="example.pdf"'

        self._create_pdf(response)
        return response

    def _create_pdf(self, response):
        # 日本语が使えるゴシック体のフォントを设定する
        font_name = 'HeiseiKakuGo-W5'
        pdfmetrics.registerFont(UnicodeCIDFont(font_name))

        # A4縦向きのpdfを作る
        size = portrait(A4)

        # pdfを描く场所を作成:pdfの原点は左上にする(bottomup=False)
        pdf_canvas = canvas.Canvas(response)
        # ヘッダー
        font_size = 24  # フォントサイズ
        pdf_canvas.setFont("HeiseiKakuGo-W5", font_size)
        pdf_canvas.drawString(93 * mm, 770, "见积书")
        font_size = 10
        pdf_canvas.setFont("HeiseiKakuGo-W5", font_size)
        pdf_canvas.drawString(
            150 * mm, 813, f"见积発行日: "
        )
        pdf_canvas.drawString(
            150 * mm,
            800,
            "xxxxxxxxxxx-xxxxxxxxxx",
        )

        # (4) 社名
        data = [
            [f"ほげほげ会社御中", ""],
            ["案件名", "ほげほげ案件"],
            ["御见积有効限:発行日より30日", ""],
        ]

        table = Table(data, colWidths=(15 * mm, 80 * mm), rowHeights=(7 * mm))
        table.setStyle(
            TableStyle(
                [
                    ("FONT", (0, 0), (-1, -1), "HeiseiKakuGo-W5", 12),
                    ("LINEABOVE", (0, 1), (-1, -1), 1, colors.black),
                    ("VALIGN", (0, 0), (1, -1), "MIDDLE"),
                    ("VALIGN", (0, 1), (0, -1), "TOP"),
                ]
            )
        )
        table.wrapOn(pdf_canvas, 20 * mm, 248 * mm)
        table.drawOn(pdf_canvas, 20 * mm, 248 * mm)



        pdf_canvas.drawString(20 * mm, 238 * mm, "下记の通り御见积申し上げます")
        # (4) 社名
        data = [
            ["合计金额(消费税込)", f"1000 円"],
        ]

        table = Table(data, colWidths=(50 * mm, 60 * mm), rowHeights=(7 * mm))
        table.setStyle(
            TableStyle(
                [
                    ("FONT", (0, 0), (1, 2), "HeiseiKakuGo-W5", 10),
                    ("BOX", (0, 0), (2, 3), 1, colors.black),
                    ("INNERGRID", (0, 0), (1, -1), 1, colors.black),
                    ("VALIGN", (0, 0), (1, 2), "MIDDLE"),
                    ("ALIGN", (1, 0), (-1, -1), "RIGHT"),
                ]
            )
        )
        table.wrapOn(
            pdf_canvas,
            20 * mm,
            218 * mm,
        )
        table.drawOn(
            pdf_canvas,
            20 * mm,
            218 * mm,
        )

        # 品目
        data = [["内容", "开始月", "终了月", "単価", "数量", "金额"]]

        for idx in range(13):
            data.append([" ", " ", " ", " ", " ", ""])

        data.append([" ", " ", " ", "合计", "", f"{1000:,}"])
        data.append([" ", " ", " ", "消费税", "", f"{1000 * 0.10:,.0f}"])
        data.append([" ", " ", " ", "税込合计金额", "", f"{1000 * 1.10:,.0f}"])
        data.append(
            [" ", " ", " ", "", "", ""],
        )

        table = Table(
            data,
            colWidths=(70 * mm, 25 * mm, 25 * mm, 20 * mm, 20 * mm, 20 * mm),
            rowHeights=6 * mm,
        )
        table.setStyle(
            TableStyle(
                [
                    ("FONT", (0, 0), (-1, -1), "HeiseiKakuGo-W5", 8),
                    ("BOX", (0, 0), (-1, 13), 1, colors.black),
                    ("INNERGRID", (0, 0), (-1, 13), 1, colors.black),
                    ("LINEABOVE", (3, 11), (-1, 18), 1, colors.black),
                    ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
                    ("ALIGN", (1, 0), (-1, -1), "RIGHT"),
                ]
            )
        )
        table.wrapOn(pdf_canvas, 17 * mm, 100 * mm)
        table.drawOn(pdf_canvas, 17 * mm, 100 * mm)

        pdf_canvas.drawString(17 * mm, 100 * mm, "<备考>")

        table = Table(
            [[""]],
            colWidths=(180 * mm),
            rowHeights=90 * mm,
        )

        table.setStyle(
            TableStyle(
                [
                    ("FONT", (0, 0), (-1, -1), "HeiseiKakuGo-W5", 8),
                    ("BOX", (0, 0), (-1, -1), 1, colors.black),
                    ("INNERGRID", (0, 0), (-1, -1), 1, colors.black),
                    ("VALIGN", (0, 0), (-1, -1), "TOP"),
                ]
            )
        )
        table.wrapOn(pdf_canvas, 17 * mm, 5 * mm)
        table.drawOn(pdf_canvas, 17 * mm, 5 * mm)
        pdf_canvas.showPage()

        # pdfの书き出し
        pdf_canvas.save()



生成されたPDF生成

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章