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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 22:36:24