You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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路径的反斜杠属于转义字符,会进一步干扰路径解析。
修复方案
  1. 修正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' %}');
    }
    

    注意样式块每个属性末尾要加分号,路径统一使用正斜杠。

  2. 给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
    
  3. 可选排查项
    如果配置完路径后仍有临时文件相关报错,检查运行Django的用户对Windows临时目录(C:\Users\你的用户名\AppData\Local\Temp)是否有读写权限,临时关闭安全软件的目录防护规则测试即可。


内容的提问来源于stack exchange,提问作者Mohamed El-shiekh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 10:36:42