Javascript 通过HttpResponse和AJAX提供服务时,非ASCII字符不能正确显示在PDF中
我已经生成了一个PDF文件,其中包含带有Javascript 通过HttpResponse和AJAX提供服务时,非ASCII字符不能正确显示在PDF中,javascript,python,django,utf-8,reportlab,Javascript,Python,Django,Utf 8,Reportlab,我已经生成了一个PDF文件,其中包含带有ReportLab的西里尔字符(非ASCII)。为此,我使用了支持此类字符的“蒙特塞拉特”字体。当我查看Django的媒体文件夹中生成的PDF文件时,字符正确显示: 我在生成PDF的函数中使用以下代码嵌入了字体: from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import A4 from reportlab.pdfbase import pdfmetrics from r
ReportLab
的西里尔字符(非ASCII)。为此,我使用了支持此类字符的“蒙特塞拉特”字体。当我查看Django的媒体
文件夹中生成的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
,使用open(fs.location+“/”+filename,'rb')以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();
}
}
});
这是以某种方式改变编码的代码部分
用评论中的想法解决问题
感谢所有我收到的评论,特别是@Melvyn的评论,我终于想出了一个解决方案。我没有创建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
部分之后):
有关详细信息,请参阅
我希望这篇文章能帮助那些在生成非ASCII(西里尔字母)字符的PDF时遇到同样问题的人。我花了好几天时间…您正在进行一些编码/重新编码,因为如果您查看文件之间的差异,就会发现其中充斥着: 你说你没有设置编码和字符集就试过了,但我认为测试不正确-很可能你看到了一个浏览器缓存版本 正确的方法是使用FileResponse,传入文件名并让Django找出正确的内容类型 以下是工作环境的可再现性试验: 首先,将西里尔文(Cyrillic_good.pdf)放入媒体根目录中 将以下内容添加到URL.py:
#urls.py
from django.urls import path
from .views import pdf_serve
urlpatterns = [
path("pdf/<str:filename>", pdf_serve),
]
现在启动runserver并请求http://localhost:8000/pdf/Cyrillic_good.pdf
如果这不能复制一个有效的pdf,这是一个本地问题,您应该查看中间件、操作系统或小绿人,而不是代码。我在本地处理您的文件,没有发生任何损坏
事实上,现在获得损坏pdf的唯一方法是在Django发送后修改浏览器缓存或响应,因为内容长度检查将阻止发送与磁盘上文件大小不同的文件
JS部分
我希望转换发生在blob构造函数中,因为可以将blob转换为类型。我不确定默认值是否是二进制安全的。
同样奇怪的是,你的数据有一个错误属性,你将整个数据传递给blob,但我们看不出你在对什么承诺做出反应。
成功:函数(数据){
if(data.error==未定义){
console.log(data)//这将提供信息
var blob=新blob([数据]);
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download=filename+'.pdf';
link.click();
}
}
对于在视图中执行表单验证的用户,您需要在js文件中添加以下代码,因为返回类型应为blob
xhr: function() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 2) {
if (xhr.status == 200) {
xhr.responseType = "blob";
}
}
};
return xhr;
},
success: function (response, textStatus, jqXHR) {
var blob = new Blob([response])
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="contract.pdf";
link.click();
},
error: function (response, textStatus, jqXHR) {
$('#my_form').click();
}
确保该字体嵌入到PDF中,而不仅仅是假设客户端将拥有该字体。请显示生成PDF.Hi@AntoinePinsard的代码。我添加了与reportlab一起使用的行,以嵌入字体。我想这就是你的意思,对吧?问题出在httpresponse中,在媒体内部生成的文件中,一切正常……我已在没有字体的计算机中检查了媒体中的PDF文件,并且该文件也正确显示。@Sunil,我通过以下链接解决了此问题。基本上,您必须在Python中引发一个错误,然后使用AJAX本机错误处理。我通过添加下面的内容而不仅仅是返回类型来解决这个问题<代码>xhr:function(){var xhr=new XMLHttpRequest();xhr.onreadystatechange=function(){if(xhr.readyState==2){if(xhr.status==200){xhr.responseType=“blob”;}}};return xhr;}使用
FileResponse
这种方式会导致以下错误ValueError:read of closed file
。显然,fileresponse
不能与上下文管理器一起使用(请参阅)。无论如何,我已经清除了所有缓存,并且直接使用了返回FileResponse(open(filename))
。这在lib/encodings/cp1252.py
的第23行中产生了以下错误:字符映射代码无法解码位置561中的字节0x8d:character map to
。因此,它似乎无法正确地执行t
#urls.py
from django.urls import path
from .views import pdf_serve
urlpatterns = [
path("pdf/<str:filename>", pdf_serve),
]
xhr: function() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 2) {
if (xhr.status == 200) {
xhr.responseType = "blob";
}
}
};
return xhr;
},
success: function (response, textStatus, jqXHR) {
var blob = new Blob([response])
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download="contract.pdf";
link.click();
},
error: function (response, textStatus, jqXHR) {
$('#my_form').click();
}