Django使用xhtml2pdf渲染阿拉伯语PDF报TTFError无法打开ttf文件
问题根因
报错由两个核心问题触发:
- 模板CSS中直接写Windows本地磁盘绝对路径作为字体地址,xhtml2pdf的CSS解析器无法正确识别反斜杠格式的Windows路径,加载字体时在临时目录转存环节出现权限异常、路径截断问题
- 调用pisa生成PDF时没有配置资源查找回调,xhtml2pdf默认会把所有资源路径当网络URL解析,Windows盘符(比如C:、D:)会被识别成非法URL协议,直接抛出
unknown url type错误,后续连锁触发临时字体文件无法打开的异常。
修复步骤
1. 改造utils.py,添加资源路径回调
给pisa传入专门的路径映射回调,让程序能正确找到本地静态资源(字体、图片等),替换原有代码如下:
import os from io import BytesIO from django.http import HttpResponse from django.template.loader import get_template from django.conf import settings from xhtml2pdf import pisa def link_callback(uri, rel): # 映射静态资源路径 if uri.startswith(settings.STATIC_URL): path = os.path.join(settings.STATIC_ROOT, uri.replace(settings.STATIC_URL, "")) # 映射用户上传媒体资源路径 elif uri.startswith(settings.MEDIA_URL): path = os.path.join(settings.MEDIA_ROOT, uri.replace(settings.MEDIA_URL, "")) else: return uri return os.path.abspath(path) def render_to_pdf(template_src, context_dict={}): template = get_template(template_src) html = template.render(context_dict) result = BytesIO() # 传入回调参数,指定资源查找规则 pdf = pisa.pisaDocument( BytesIO(html.encode("utf-8")), result, link_callback=link_callback ) if not pdf.err: return HttpResponse(result.getvalue(), content_type='application/pdf') return None
2. 修正Django静态文件配置
在settings.py中确认静态文件配置,执行python manage.py collectstatic把字体等静态资源收集到对应目录,确保arial-unicode-ms.ttf放在static/fonts目录下:
# settings.py 配置项 STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'static')
3. 修改HTML模板的字体路径
不要写磁盘绝对路径,改用Django标准静态资源路径,同时补充阿拉伯语从右到左的排版属性:
<!DOCTYPE html> <html lang="ar"> <head> <meta charset="UTF-8"> <title>Gift PDF</title> <style type="text/css"> @font-face { font-family: ArialUnicode; src: url("/static/fonts/arial-unicode-ms.ttf"); } body { margin: 0; font-family: "ArialUnicode", sans-serif; /* 阿拉伯语为从右到左书写语言,加这个属性避免字序颠倒 */ direction: rtl; } </style> </head> <body> <div class="headings">إلي</div> </body> </html>
4. 清理views.py冗余代码
原有代码中receipt_file = BytesIO(pdf.content)创建对象后未使用,属于冗余代码,可直接清理:
def generate_pdf(request): pdf = render_to_pdf('index.html') if pdf: response = HttpResponse(pdf, content_type='application/pdf') filename = "Invoice_.pdf" download = request.POST.get("download") if download: content = f"attachment; filename='{filename}'" else: content = f"inline; filename='{filename}'" response['Content-Disposition'] = content return response
5. 临时目录权限兜底
如果完成以上配置后仍报临时文件权限错误,给C:\Users\当前用户名\AppData\Local\Temp目录分配当前用户的完全控制权限,或用管理员身份启动Django开发服务即可。
内容的提问来源于stack exchange,提问作者user9479133
相关产品推荐
相关产品推荐

