【发布时间】:2021-02-17 03:05:18
【问题描述】:
我生成了一个 PDF 文件,其中包含带有 ReportLab 的西里尔字符(非 ASCII)。为此,我使用了支持此类字符的“Montserrat”字体。当我在Django的media文件夹中查看生成的PDF文件时,字符显示正确:
我在生成 PDF 的函数中使用以下代码嵌入了字体:
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
pdfmetrics.registerFont(TTFont('Montserrat', 'apps/Generic/static/Generic/tff/Montserrat-Regular.ttf'))
canvas_test = canvas.Canvas("media/"+filename, pagesize=A4)
canvas_test.setFont('Montserrat', 18)
canvas_test.drawString(10, 150, "Some text encoded in UTF-8")
canvas_test.drawString(10, 100, "как поживаешь")
canvas_test.save()
但是,当我尝试通过 HttpResponse 提供此 PDF 时,尽管以蒙特塞拉特字体显示,但西里尔字符未正确显示:
提供 PDF 的代码如下:
# Return the pdf as a response
fs = FileSystemStorage()
if fs.exists(filename):
with fs.open(filename) as pdf:
response = HttpResponse(
pdf, content_type='application/pdf; encoding=utf-8; charset=utf-8')
response['Content-Disposition'] = 'inline; filename="'+filename+'"'
return response
我几乎尝试了所有方法(使用FileResponse,使用with open(fs.location + "/" + filename, 'rb') as pdf...打开PDF)都没有成功。实际上,我不明白为什么,如果ReportLab 正确嵌入了字体(media 文件夹内的本地文件),提供给浏览器的文件没有嵌入字体。
有趣的是,我通过 Chrome 或 Edge 使用 Foxit Reader 来阅读 PDF。当我使用 Firefox 的默认 PDF 查看器时,会显示不同的错误字符。实际上在这种情况下字体似乎也是错误的:
编辑
感谢@Melvyn,我意识到错误并不在于直接从 Python 视图发送的响应中,而是在 AJAX 调用中的success 代码中,我将在此后留下:
$.ajax({
method: "POST",
url: window.location.href,
data: { trigger: 'print_pdf', orientation: orientation, size: size},
success: function (data) {
if (data.error === undefined) {
var blob = new Blob([data]);
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = filename + '.pdf';
link.click();
}
}
});
这是以某种方式改变编码的代码部分。
用 cmets 的想法解决
感谢我收到的所有 cmets,特别是来自 @Melvyn 的所有 cmets,我终于想出了一个解决方案。我没有创建Blob 对象,而是将AJAX 的responseType 设置为Blob 类型。从 JQuery 3 开始这是可能的:
$.ajax({
method: "POST",
url: window.location.href,
xhrFields:{
responseType: 'blob'
},
data: { trigger: 'print_pdf', orientation: orientation, size: size},
success: function (data) {
if (data.error === undefined) {
var link = document.createElement('a');
link.href = window.URL.createObjectURL(data);
link.download = filename + '.pdf';
link.click();
}
}
});
返回响应时处理错误
您可以从 Python 中返回错误(即捕获异常),如下所示:
except Exception as err:
response = JsonResponse({'msg': "Error"})
error = err.args[0]
if error is not None:
response.status_code = 403 # To announce that the user isn't allowed to publish
if error==13:
error = "Access denied to the PDF file."
response.reason_phrase = error
return response
然后,您只需使用 AJAX 的本机错误处理(在 success 部分之后):
error: function(data){
$("#message_rows2").text(data.statusText);
$('#errorPrinting').modal();
}
在this link 中查看更多详细信息。
我希望这篇文章可以帮助人们在生成非 ASCII(西里尔文)字符的 PDF 时遇到同样的问题。我花了好几天...
【问题讨论】:
-
确保字体嵌入在 PDF 中,而不是仅仅假设客户端将拥有该字体。请显示生成 PDF 的代码。
-
嗨@AntoinePinsard。我添加了与 reportlab 一起使用的行来嵌入字体。我想这就是你的意思,对吧?问题出在 httpresponse 中,在媒体内生成的文件中,一切都很好......
-
我已经在没有字体的计算机中检查了媒体中的 PDF 文件,它也正确显示。
-
@Sunil,我通过以下链接stackoverflow.com/questions/377644/… 解决了这个问题。基本上,您必须在 Python 中引发错误,然后使用 AJAX 原生错误处理。
-
它仍然失败,因为返回类型应为 blob。我通过在下面添加而不是仅仅返回类型来解决它。
xhr: function() { var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState == 2) { if (xhr.status == 200) { xhr.responseType = "blob"; } } }; return xhr; }
标签: javascript python django utf-8 reportlab