Django使用xhtml2pdf模板时出现无法打开字体文件TTFError
问题诱因
- 核心原因是CSS中
@font-face配置的字体路径写法错误:直接写Windows本地磁盘的绝对路径F:\MyProjects\my_app\app\static\app\fonts\font_name.ttf时,xhtml2pdf的CSS解析器不识别这种盘符开头的本地路径,解析过程中会把路径截断,最终尝试访问无效路径,抛出第一个[Errno 13] Permission denied: ttf'权限错误。 - 第一个异常触发后,xhtml2pdf会进入降级逻辑:尝试将字体文件拷贝到系统临时目录生成临时ttf文件加载,这时候要么当前运行Django进程的用户没有临时目录写入权限、要么安全软件拦截了临时文件写入/读取操作,就会抛出第二个
Can't open file "C:\Users\User~1\AppData\Local\Temp\*.ttf"的TTFError。 - 额外语法问题:原有
@font-face样式块末尾缺少分号,且CSS中Windows路径的反斜杠属于转义字符,会进一步干扰路径解析。
修复方案
修正CSS中字体的路径写法,禁止直接使用本地磁盘绝对路径
确认字体文件存放在Django静态文件目录下(当前存放的app/static/app/fonts/路径符合Django静态文件规范),在Django模板中使用static模板标签引用字体路径,修正后的@font-face写法如下:/* 模板顶部要先加载静态标签:{% load static %} */ @font-face { font-family: RTLFont; src: url('{% static 'app/fonts/font_name.ttf' %}'); }注意样式块每个属性末尾要加分号,路径统一使用正斜杠。
给xhtml2pdf配置资源加载回调函数,避免路径解析错误
在/api/visits/print对应的视图函数中传入link_callback参数,手动映射静态资源、字体文件的本地绝对路径,让xhtml2pdf可以直接读到源字体文件,不会触发临时文件拷贝的降级逻辑。参考代码如下:import os from django.conf import settings from django.http import HttpResponse from django.template.loader import get_template from xhtml2pdf import pisa def visits_print_view(request): template = get_template("your_pdf_template.html") # 传入模板需要的业务上下文 html = template.render({"your_context_key": "your_context_value"}) response = HttpResponse(content_type="application/pdf") response["Content-Disposition"] = 'inline; filename="visit_print.pdf"' def resource_link_callback(uri, rel): # 映射静态文件路径 if uri.startswith(settings.STATIC_URL): base_dir = settings.STATICFILES_DIRS[0] if settings.DEBUG else settings.STATIC_ROOT static_path = os.path.join(base_dir, uri.replace(settings.STATIC_URL, "", 1)) return static_path if os.path.isfile(static_path) else uri # 映射媒体文件路径 if uri.startswith(settings.MEDIA_URL): media_path = os.path.join(settings.MEDIA_ROOT, uri.replace(settings.MEDIA_URL, "", 1)) return media_path if os.path.isfile(media_path) else uri return uri pisa_result = pisa.CreatePDF( html, dest=response, link_callback=resource_link_callback ) if pisa_result.err: return HttpResponse("PDF生成失败", status=500) return response可选排查项
如果配置完路径后仍有临时文件相关报错,检查运行Django的用户对Windows临时目录(C:\Users\你的用户名\AppData\Local\Temp)是否有读写权限,临时关闭安全软件的目录防护规则测试即可。
内容的提问来源于stack exchange,提问作者Mohamed El-shiekh
相关产品推荐
相关产品推荐

