Django CBV: Programmatically save HTML page to PDF file on the server

╄→尐↘猪︶ㄣ 提交于 2019-12-10 11:47:18

问题


Visiting the relevant url, an html page is rendered. I need to programmatically save this html page to a pdf file on the server.

views.py

class PdfView(DetailView):
    model = TheModel
    template_name = 'pdf.html'

urls.py

urlpatterns = [
    url(
        r'^(?P<pk>[0-9]+)/$',
        PdfDataView.as_view(),
        name='pdf-data',
    ),
]

回答1:


Try an approach like below. Although it presents serving file to the end user via response, but modifying it to write file on server should be easy for you:

The functional view:

def pdf_sticker(request, pk):
    spot = get_object_or_404(Spot, pk=pk)
    if spot.is_certificated:
        pdf, result = render_to_pdf(
            'www/pdf_sticker.html',
            {
                'pass_smth': 'needed_in_render',
                'MEDIA_ROOT': settings.MEDIA_ROOT,
                'STATIC_ROOT': settings.STATICFILES_DIRS[0],
                'pagesize': 'A6',
            }
        )
        if not pdf.err:
            return HttpResponse(result.getvalue(), content_type='application/pdf')
        return HttpResponse('We had some errors')
    else:
        raise Http404

The helper method:

from io import StringIO, BytesIO
from xhtml2pdf import pisa

from django.template.loader import get_template


def render_to_pdf(template_src, context_dict):
    template = get_template(template_src)
    html = template.render(context_dict)
    result = BytesIO()
    pdf = pisa.pisaDocument(
        StringIO(html),
        dest=result,
        encoding='UTF-8'
    )

    return pdf, result

the template:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>


        <title>Sticker for certificated spot</title>
        <style type="text/css">
                 @font-face {
                    font-family: "Lobster";
                    src: url("{{ STATIC_ROOT }}/font/Lobster-32.ttf");
                    font-weight: normal;
                    font-style: normal;
                }
                @font-face {
                    font-family: "Lato";
                    src: url("{{ STATIC_ROOT }}/font/Lato-Hairline.ttf");
                    font-weight: 100;
                    font-style: thin;
                }
                @font-face {
                    font-family: "Nobile";
                    src: url("{{ STATIC_ROOT }}/font/Nobile-34.ttf");
                    font-weight: normal;
                    font-style: normal;
                }

                @page {
                    size: {{ pagesize }};
                    margin: 0.5cm;
                }

        </style>
    </head>
    <body>
        <center>
            <img src="{{ STATIC_ROOT }}/{{ SPOT_PROJECT_NAME }}/certificate.png" height="260px">
            <p style="font-family:Lobster;">
                <span style="font-size:60px;">
                    {% settings_value "SPOT_PROJECT_SUBJECT" %}<br>
                </span>
                <span style=" font-size:32px;">
                    friendly spot!
                </span>
                <br>
                <table>
                    <tr>
                        <td colspan=2>
                             <img src="http://{{BASE_HOST}}{% url 'www:qrencode_link' pk=spot.pk size=4 %}">
                        </td>
                    </tr>
                    <tr>
                        <td colspan=2 height=5></td>
                    </tr>
                    <tr>
                        <td colspan=2>
                            <span style="font-family:Nobile; font-size:15px;">
                            Powered by: <img src="{{ STATIC_ROOT }}/{{ SPOT_PROJECT_NAME }}/logo.png" height="50px"> </span>
                            <span style="font-family:Lobster; font-size:25px;">
                                    {{ SPOT_PROJECT_NAME }}
                            </span>
                        </td>
                    </tr>
                </table>
            </p>
        </center>
    </body>
</html>

Working for Python 3.6, Django 2.0.

Libraries versions:

Django==2.0.2
xhtml2pdf==0.2b1

Regarding the part of writing it to local file on server this might help:

http://xhtml2pdf.readthedocs.io/en/latest/usage.html#using-with-python-standalone




回答2:


The following solution saves a pdf version of the web page during the HTTP request, before the HTTP response is served to the browser.

I used weasyprint because it managed to take care of the unicode characters out of the box.

views.py

from weasyprint import HTML

class PdfView(DetailView):
    model = TheModel
    template_name = 'pdf.html'

    def get(self, request, *args, **kwargs):
        template_response = super().get(self, request, *args, **kwargs)
        HTML(string=template_response.rendered_content).write_pdf(
            '/path/to/test.pdf',
            stylesheets=['/path/to/pdf.css']
        )
        return template_response

A better solution, outside the view, would be to use requests to fetch the HTML page and then create the pdf file. It is better because the server will not have to wait for the pdf to be created, potentially blocking other requests waiting to be serverd:

>>>import requests
>>>resp = requests.get('http://my-url/object_id')
>>>HTML(string=resp.content).write_pdf('/path/to/test.pdf', stylesheets=['/path/to/pdf.css'])


来源:https://stackoverflow.com/questions/49225195/django-cbv-programmatically-save-html-page-to-pdf-file-on-the-server

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!