Package: django-weasypdf
1django-weasypdfA Django class-based view helper to generate PDF with WeasyPrint.Requires:WeasyPrintOptional requirements:matplotlib (to render plots)Contents1django-weasypdf1.1Installation1.2A sample report1.3Usage1.4Building a PDF document from a background process1.5Providing “extra_context” parameters1.6Customizing the templates1.7How to insert a page break1.8Adding Weasyprint to your project2Customizations examples2.1Add a custom logo2.2Embed images from media (ImageFields)3Adding a plot to the PDF documents3.1Try it4History4.1v0.1.24.2v0.1.14.3v0.1.04.4v0.0.54.5v0.0.44.6v0.0.34.7v0.0.24.8v0.0.11.1InstallationInstall the package by running:pipinstalldjango-weasypdfor:pipinstallgit+https://github.com/morlandi/django-weasypdfYou will probably build your own app, for examplereports, to provide custom derived Views
and templates:pythonmanage.pystartappreportsIn your settings, add:INSTALLED_APPS=[...'reports','weasypdf',]Note thatreportsis listed beforeweasypdfto make sure you can possibly
override any template.In your urls, add:fromdjango.urlsimportpath,includeurlpatterns=[...path('reports/',include('reports.urls',namespace='reports')),...Optionally, you might want to copy the default templates from ‘weasypdf/templates/weasypdf’ to ‘reports/templates/reports’
for any required customization; seeCustomizing the templatesbelow1.2A sample reportA test view has already been included in theweasypdfapp
to render a sample document for demonstration purposes.To use it, add the following to your urls:path('weasypdf/',include('weasypdf.urls',namespace='weasypdf')),then, visit:http://127.0.0.1:8000/weasypdf/test/print/1.3UsageYou can copy the following to yourreportsapp to have a sample view
to start working with:filereports/urls.py:fromdjango.urlsimportpathfrom.importviewsapp_name='weasypdf'urlpatterns=[path('test/print/',views.ReportTestView.as_view(),{'for_download':False,'lines':200,},name="test-print"),path('test/download/',views.ReportTestView.as_view(),{'for_download':True,'lines':200,},name="test-download"),]filereports/views.py:fromweasypdf.viewsimportWeasypdfViewclassReportView(WeasypdfView):#my_custom_data = Noneheader_template_name='weasypdf/header.html'footer_template_name='weasypdf/footer.html'styles_template_name='weasypdf/styles.css'defget_context_data(self,**kwargs):context=super(ReportView,self).get_context_data(**kwargs)#self.my_custom_data = context.pop('my_custom_data', None)# context.update({#     'footer_line_1': config.REPORT_FOOTER_LINE_1,#     'footer_line_2': config.REPORT_FOOTER_LINE_2,# })returncontextclassReportTestView(ReportView):body_template_name='weasypdf/pages/test.html'styles_template_name='weasypdf/pages/test.css'# header_template_name = None# footer_template_name = Nonetitle="Report Test"defget_context_data(self,**kwargs):context=super().get_context_data(**kwargs)# Add a plottry:from.plotimportbuild_plot_from_dataplot_image=build_plot_from_data(data=None,as_base64=True)context.update({'plot_image':plot_image,})except:pass# Add your stuff here ...context.update({...})returncontextorreplace `weasypdf/header.html` with `reports/header.html`, etc … when using
custom templates.filereports/pages/test.html:{% extends "weasypdf/base.html" %}

{% block content %}<h1>Test PDF</h1>{% if plot_image %}<imgclass="plot"src="data:image/png;base64,{{plot_image}}">{% endif %}

    {% with lines=lines|default:100 %}
        {% for i in "x"|rjust:lines %}<div>line {{forloop.counter}} ...</div>{% endfor %}
    {% endwith %}

{% endblock content %}You can now download the PDF document at:http://127.0.0.1:8000/reports/test/download/or open it with the browser at:http://127.0.0.1:8000/reports/test/print/You can inspect the HTML used for PDF rendering by appending?format=htmlto the url:http://127.0.0.1:8000/reports/test/print/?format=html1.4Building a PDF document from a background processAWeasypdfView.render_as_pdf_to_stream(self, base_url, extra_context, output)method is supplied for this purpose:defrender_as_pdf_to_stream(self,base_url,extra_context,output):"""
    Build the PDF document and save in into "ouput" stream.

    Automatically called when the view is invoked via HTTP (unless self.format == 'html'),
    but you can also call it explicitly from a background task:

        view = PdfTestView()
        context = view.get_context_data()
        with open(filepath, 'wb') as f:
            view.render_as_pdf_to_stream('', context, f)
    """A sample management command to build a PDF document outside the HTML request/response
cycle is available here:weasypdf/management/commands/build_test_pdf.py1.5Providing “extra_context” parametersSupply context parameters either in the urlpattern, or invoking get_context_data():fromurls.py:urlpatterns=[path('daily/print/',views.ReportDailyView.as_view(),{'exclude_inactives':False},name="daily-print"),]from a background task:fromdjango.core.files.baseimportContentFile# Create a View to work withfromreports.viewsimportReportDailyViewview=ReportDailyView()context=view.get_context_data(exclude_inactives=task.exclude_inactives,)# Create empty file as resultfilename=view.build_filename(extension="weasypdf")task.result.save(filename,ContentFile(''))# Open and write resultfilepath=task.result.pathwithopen(filepath,'wb')asf:view.render_as_pdf_to_stream('',context,f)1.6Customizing the templatesThese sample files:weasypdf
├── static
│   └── weasypdf
│       └── images
│           └── header_left.png
└── templates
    └── weasypdf
        ├── base.html
        ├── base_nomargins.html
        ├── styles.css
        ├── footer.html
        ├── header.html
        └── pages
            ├── test.css
            └── test.htmlcan be copied into your app’s local folderreports/templates/reports,
and used for any required customization:classReportView(WeasypdfView):header_template_name='reports/header.html'footer_template_name='reports/footer.html'styles_template_name='reports/styles.css'1.7How to insert a page break<pstyle="page-break-before: always"></p>1.8Adding Weasyprint to your projectAddweasyprintto your requirements:WeasyPrint==51and optionally to your LOGGING setting:LOGGING = {
    ...
    'loggers': {
        ...
        'weasyprint': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}Deployment:Install Courier fonts for PDF rendering# You can verify the available fonts as follows:
#    # fc-list
- name: Install Courier font for PDF rendering
    become: true
    become_user: root
    copy:
        src: deployment/project/courier.ttf
        dest: /usr/share/fonts/truetype/courier/The font file can be downloaded here:courier.ttfYou might also need to install the following packages:#weasyprint_packages:
- libffi-dev          # http://weasyprint.readthedocs.io/en/latest/install.html#linux
- python-cffi         # http://weasyprint.readthedocs.io/en/latest/install.html#linux
- python-dev          # http://weasyprint.readthedocs.io/en/latest/install.html#linux
- python-pip          # http://weasyprint.readthedocs.io/en/latest/install.html#linux
- python-lxml         # http://weasyprint.readthedocs.io/en/latest/install.html#linux
- libcairo2           # http://weasyprint.readthedocs.io/en/latest/install.html#linux
- libpango1.0-0       # http://weasyprint.readthedocs.io/en/latest/install.html#linux
- libgdk-pixbuf2.0-0  # http://weasyprint.readthedocs.io/en/latest/install.html#linux
- shared-mime-info    # http://weasyprint.readthedocs.io/en/latest/install.html#linux
- libxml2-dev         # http://stackoverflow.com/questions/6504810/how-to-install-lxml-on-ubuntu#6504860
- libxslt1-dev        # http://stackoverflow.com/questions/6504810/how-to-install-lxml-on-ubuntu#6504860For an updated list, check here:https://weasyprint.readthedocs.io/en/latest/install.html#linux2Customizations examples2.1Add a custom logoFor example you can save a custom bitmap with django-constance:CONSTANCE_ADDITIONAL_FIELDS={'image_field':['django.forms.ImageField',{}]}CONSTANCE_CONFIG={...'PDF_RECORD_LOGO':('','Image for PDF logo','image_field'),}then in yourheader.htmltemplate:<body><divclass="pageHeader"><imgclass="pageLogo"title="{{ PDF_RECORD_LOGO }}"src="media://{{ PDF_RECORD_LOGO }}"><divclass="pageTitle">{{print_date|date:'d/m/Y H:i:s'}} - {{title}}</div></div></body>2.2Embed images from media (ImageFields)If Image is a Model to keep the images you want to embed, use a templatetag like this:@register.filterdeflocal_image_url(image_slug):"""
    Example:
        "/backend/images/signature_mo.png"
    """url=''try:image=Image.objects.get(slug=image_slug)ifbool(image.image):url=image.image.url.lstrip(settings.MEDIA_URL)exceptImage.DoesNotExistase:passiflen(url):url='media://'+urlelse:url='static://reports/images/placeholder.png'returnurlthen, in your templates:<imgclass="pageLogoMiddle"src="{{'report-header-middle'|local_image_url}}">where‘report-header-middle’is the slug used to select the image.3Adding a plot to the PDF documentsIn the frontend, you have many javascript libraries available to plot data and
draw fancy charts.This doesn’t help you in embedding a plot in a PDF documents built offline, however;
in this case, you need to build an image server side.An helper function has been included in this app for that purpose; to use it,matplotlibmust be installed.At the moment, it is more a POC then a complete solution; you can either use it
from the package, or copy the source fileweasypdf/plot.pyin your project and usebuild_plot_from_data()as a starting point:defbuild_plot_from_data(data,chart_type='line',as_base64=False,dpi=300,ylabel=''):"""
    Build a plot from given "data";
    Returns: a bitmap of the plot

    Requires:
        matplotlib

    Keyword arguments:
    data -- see sample_line_plot_data() for an example; if None, uses sample_line_plot_data()
    chart_type -- 'line', 'bar', 'horizontalBar', 'pie', 'line', 'doughnut',
    as_base64 -- if True, returns the base64 encoding of the bitmap
    dpi -- bitmap resolution
    ylabel -- optional label for Y axis

    Data layout
    ===========

    Similar to django-jchart:

    - either (shared values for x)

        {
            "labels": ["A", "B", ...],
            "x" [x1, x2, ...],
            "columns": [
                [ay1, ay2, ...],
                [by1, by2, ...],
            ],
            "colors": [
                "rgba(64, 113, 191, 0.2)",
                "rgba(191, 64, 64, 0.0)",
                "rgba(26, 179, 148, 0.0)"
            ]
        }

    - or

        {
            "labels": ["A", "B", ..., ],
            "columns": [
                [
                    {"x": ax1, "y": ay1 },
                    {"x": ax2, "y": ay2 },
                    {"x": ax3, "y": ay3 },
                ], [
                    {"x": bx1, "y": by1 },
                    {"x": bx2, "y": by2 },
                ], ...
            ],
            "colors": ["transparent", "rgba(121, 0, 0, 0.2)", "rgba(101, 0, 200, 0.2)", ]
        }

    """then, in the view, add the resulting bitmap to context:defget_context_data(self,**kwargs):context=super().get_context_data(**kwargs)try:from.plotimportbuild_plot_from_dataplot_image=build_plot_from_data(data=None,chart_type='line',as_base64=True)context.update({'plot_image':plot_image,})except:passreturncontextIn the template, render it as an embedded image:<style>.plot{border:1pxsolid#ccc;width:18cm;height:6cm;margin:1.0cm0;}</style>{% if plot_image %}<imgclass="plot"src="data:image/png;base64,{{plot_image}}">{% endif %}3.1Try itThe management commandbuild_test_pdfcan be used with the “–plot_data” switch to test the resulting image:pythonmanage.pybuild_test_pdftest.png-o-p'{"labels": ["sin", "cos"], "x": [0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5], "columns": [[0.0, 9.09, -7.57, -2.79, 9.89, -5.44, -5.37, 9.91, -2.88, -7.51], [20.0, -13.07, -2.91, 16.88, -19.15, 8.16, 8.48, -19.25, 16.68, -2.56]]}'--plot_fontTahoma4History4.1v0.1.2PdfView renamed as WeasypdfView4.2v0.1.1Rename “pdf” module as “weasypdf”Sample project added4.3v0.1.0Publish on Pypi as “django-weasypdf”Support for ‘line’, ‘bar’, ‘horizontalBar’, ‘pie’, ‘line’, ‘doughnut’ plots4.4v0.0.5Support for plot added (only “line” chart type, at the moment; requires matplotlib)Customization examples added to readme4.5v0.0.4render_to_stream() renamed as render_as_pdf_to_stream()render_as_html_to_string() method added4.6v0.0.3render_to_stream() method added to produce a PDF from a background task4.7v0.0.2Some cleanup4.8v0.0.1Project start