I am trying to make invoices by creating html file and convert it to pdf and than send as http response. The problem is that those invoices contains polish characters which UTF-8 does not display. I have tried to use ISO-8859-2 to display them, but than I am getting error: ('charmap' codec can't encode characters in position 1159-1163: character maps to ).
utils.py:
from io import BytesIO
from django.http import HttpResponse
from django.template.loader import get_template
from xhtml2pdf import pisa
def render_to_pdf(template_src, context_dict={}):
template = get_template(template_src)
html = template.render(context_dict)
result = BytesIO()
pdf = pisa.pisaDocument(
src=BytesIO(html.encode('ISO-8859-2')),
dest=result,
encoding='UTF-8'
)
if pdf.err:
return HttpResponse('We had some errors <pre>' + html + '</pre>')
return HttpResponse(result.getvalue(), content_type='application/pdf')
views.py:
class GeneratePDF(View):
def get(self, request, pk=None):
"getting data here"
pdf = render_to_pdf("invoice.html", data)
if pdf:
response = HttpResponse(pdf, content_type='application/pdf')
filename = "Sales_Invoice_%s.pdf" % ("name")
content = "inline; filename=%s" % (filename)
download = request.GET.get("download")
if download:
content = "attachment; filename='%s'" % (filename)
response['Content-Disposition'] = content
return response
return Response(status=rest_status.HTTP_404_NOT_FOUND)
invoice.html:
<html>
<head>
<title>Sales Invoice - {{ sales_invoice.id }}</title>
<style>
@page {
size: A4 portrait;
@frame header_frame {
-pdf-frame-content: header_content;
left: 50pt; width: 512pt; top: 50pt; height: 40pt;
}
@frame content_frame {
left: 50pt; width: 512pt; top: 90pt; height: 632pt;
{% comment %} -pdf-frame-border: 1; {% endcomment %}
}
@frame footer_frame {
-pdf-frame-content: footer_content;
left: 50pt; width: 512pt; top: 772pt; height: 20pt;
}
}
</style>
<head>
<body>
<div id="header_content"><h1>Sales Invoice {{ sales_invoice.id }}</h1></div>
<p>
<strong>Date of issue:</strong> Krakow, {{ today | date:"d/m/Y" }}<br />
<strong>Date of sale:</strong> {{ today | date:"d/m/Y" }}<br />
<strong>Date of payment:</strong> {{ today | date:"d/m/Y" }}<br />
<strong>Payment:</strong> cash<br />
</p>
{% if sales_invoice.parts %}
<table id="cssTable" class="cssTdTh">
<thead>
<tr>
<th>Part</th>
<th>Code</th>
<th>Quantity</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{% for part in sales_invoice.parts %}
<tr>
<td>{{ part.name }}</td>
<td>{{ part.code }}</td>
<td>{{ part.amount }}</td>
<td>{{ part.price_out }}</td>
</tr>
{% endfor %}
<tr>
<td colspan="3">Total</td>
<td>{{sales_invoice.total_part_price}}</td>
</tr>
</tbody>
</table>
{% endif %}
<br />
{% if sales_invoice.works %}
<table id="cssTable" class="cssTdTh">
<thead>
<tr>
<th>Work</th>
<th>Price</th>
</tr>
</thead>
<tbody>
{% for work in sales_invoice.works %}
<tr>
<td>{{ work.name }}</td>
<td>{{ work.price }}</td>
</tr>
{% endfor %}
<td>Total</td>
<td>{{sales_invoice.total_work_price}}</td>
</tbody>
</table>
{% endif %}
<div id="footer_content">Footer</div>
</body>
</html>
print("==============================================================")
for work in sales_invoice.works.all():
print(work.name)
for part in sales_invoice.parts.all():
print(part.name)
print("==============================================================")
output:
==============================================================
Wymiana łącznika elastycznego
Wymiana sprężarki klimatyzacji
Montaż radia
==============================================================
Bęben
Bagnet
Alternator
==============================================================
Result I am getting:
Using ISO-8859-2: result
Using UTF-8: result
Pip freeze:
Django==3.2.7
xhtml2pdf==0.2.5
The problem was not in encoding, it is just fonts. The solution is to use Asian fonts that mhtml2pdf supports. xhtml2pdf documentation
For example client can have polish letters in name, so I use this font here
<td style="font-family: STSong-Light">{{client.full_name}}</td>
utils.py:
from io import BytesIO
from django.http import HttpResponse
from django.template.loader import get_template
from xhtml2pdf import pisa
def render_to_pdf(template_src, context_dict={}):
template = get_template(template_src)
html = template.render(context_dict)
result = BytesIO()
pdf = pisa.pisaDocument(
src=BytesIO(html.encode('UTF-8')),
dest=result,
encoding='UTF-8'
)
if pdf.err:
return HttpResponse('We had some errors <pre>' + html + '</pre>')
return HttpResponse(result.getvalue(), content_type='application/pdf')