vikkyshostak
@vikkyshostak
< This head full of dreams.

Генерация из HTML шаблона в PDF с кириллицей в Django 2.x (xhtml2pdf)?

Всем привет.

Столкнулся с проблемой, которая встречается очень часто в гугле, но ни одно решение так и не заработало, либо относится к Python 2.7.x. Собственно, поэтому и спрашиваю тут...

Есть сайт на Django 2.0.2 (Python 3.6.1). Для генерации PDF используется xhtml2pdf.

Нужно сохранять поля модели (тупо заказ) в PDF и давать для скачивания пользователям. Всё бы ничего, но кириллица отображается, как квадратики (■■ ■ ■■ ■ ■■ ■ ■ ■), но с латиницей всё окей.

Вот мой код:

# 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)

А вот сам шаблон templates/pdf/simple.html (сохранён в 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>

Что я делаю не так? Буду рад толковым комментариям!
Помогите пожалуйста, бьюсь уже целый день с этим :(
  • Вопрос задан
  • 331 просмотр
Пригласить эксперта
Ответы на вопрос 1
TheDeadOne
@TheDeadOne
Седой и строгий
Рендер не может сам загружать ссылки, поэтому не может получить шрифт /static/fonts/OpenSans/OpenSans-Regular.ttf. У функции pisaDocument есть параметр link_callback, в который можно передать функцию преобразующую http-адреса в локальные пути. Например такую:
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)
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через TM ID
Похожие вопросы
18 авг. 2018, в 18:35
1000 руб./за проект
18 авг. 2018, в 17:54
6000 руб./за проект
18 авг. 2018, в 18:44
350 руб./за проект