V
V
Vic Shostak2018-02-22 01:39:40
Django
Vic Shostak, 2018-02-22 01:39:40

Generating from HTML template to Cyrillic PDF in Django 2.x (xhtml2pdf)?

Hello.

I ran into a problem that occurs very often in Google, but not a single solution has worked, or refers to Python 2.7.x. That's actually why I'm asking here...

There is a site on Django 2.0.2 (Python 3.6.1). For PDF generation xhtml2pdf is used.
It is necessary to save the model fields (stupidly order) in PDF and give it to users for download. Everything would be fine, but the Cyrillic alphabet is displayed as squares ( ■■ ■ ■■ ■ ■■ ■ ■ ■), but everything is ok with the Latin alphabet.
Here is my code:
# app/utils.py

from xhtml2pdf import pisa

def render_to_pdf(template_src, context_dict):
    result = BytesIO()
    template = render_to_string(template_src, context_dict)
    pdf = pisa.pisaDocument(BytesIO(template.encode('UTF-8')), result)

    if not pdf.err:
        return HttpResponse(result.getvalue(), content_type='application/pdf')

    return None

# app/views.py

from .utils import render_to_pdf

class GeneratePDF(LoginRequiredMixin, View):
    def get(self, request, order_type, order_id):
        """
        Generate PDF from HTML template
        """

        try:
            order = BasicOrder.objects.get(
                id=order_id, user=request.user.id, status__in=[1, 2]
            )
        except BasicOrder.DoesNotExist:
            return HttpResponse(status=404)

        context = {
            'order_id': order.id
        }
        template = 'pdf/simple.html'
        pdf = render_to_pdf(template, context)

        if pdf:
            filename = 'order_{}_{}.pdf'.format(order_type, order_id)
            content = 'inline; filename="{}"'.format(filename)

            if request.GET.get('save_to_file') == 'true':
                content = 'attachment; filename="{}"'.format(filename)

            response = HttpResponse(pdf, content_type='application/pdf')
            response['Content-Disposition'] = content
            return response

        return HttpResponse(status=404)

And here is the template itself templates/pdf/simple.html(saved in UTF-8):
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Заказ № {{ order_id }}</title>
    <style type="text/css">
      @page {
        size: letter landscape;
        border: 1px solid #999999;
        margin: .4cm;
      }
      
      @font-face {
        font-family: 'OpenSansRegular';
        src: url("/static/fonts/OpenSans/OpenSans-Regular.ttf");
      }
      
      body {
        font-family: 'OpenSansRegular', sans-serif;
        font-size: 16px;
      }
      
      .container {
        padding: .4cm;
      }
    </style>
  </head>
  <body>
    <div class="container">
      Заказ № {{ order_id }}
    </div>
  </body>
</html>

What am I doing wrong? I will be glad to sensible comments!
Please help, I've been struggling with this all day :(

Answer the question

In order to leave comments, you need to log in

1 answer(s)
S
Sergey Gornostaev, 2018-02-22
@sergey-gornostaev

The renderer can't load links itself, so it can't get the /static/fonts/OpenSans/OpenSans-Regular.ttf font. The pisaDocument function has a link_callback parameter, to which you can pass a function that converts http addresses to local paths. For example this one:

def fetch_pdf_resources(uri, rel):
    if uri.find(settings.MEDIA_URL) != -1:
        path = os.path.join(settings.MEDIA_ROOT, uri.replace(settings.MEDIA_URL, ''))
    elif uri.find(settings.STATIC_URL) != -1:
        path = os.path.join(settings.STATIC_ROOT, uri.replace(settings.STATIC_URL, ''))
    else:
        path = None
    return path


pdf = pisa.pisaDocument(BytesIO(template.encode('UTF-8')), result,
                                                           encoding='utf-8',
                                                           link_callback=fetch_pdf_resources)

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question