Django中xhtml2pdf转换HTML到PDF时UTF-8特殊字符显示为方框的问题求助
解决xhtml2pdf转换PDF时特殊字符显示为方框的问题
这个方框问题本质很简单:你用的Josefin Slab字体不支持这些特殊字符——不管是匈牙利语的带重音字母(Ő、Ű、ő、ű),还是Emoji(✅),都需要字体包含对应的字形才能正常显示。xhtml2pdf默认依赖系统字体,如果你的环境里的Josefin Slab没覆盖这些字符,就会用方框替代。
下面是几个靠谱的解决方法:
方法1:更换支持宽字符集的系统字体
直接换成原生支持多语言和Emoji的字体,比如Google的Noto Sans(几乎覆盖所有语言和常用符号),修改你的模板CSS:
body { font-family: 'Noto Sans', sans-serif; font-size: large; background-color: beige; }
如果是服务器环境,要先安装对应字体:
- Ubuntu/Debian:
sudo apt install fonts-noto - CentOS/RHEL:
sudo yum install google-noto-sans-fonts
方法2:嵌入自定义字体到PDF(最可靠,避免环境依赖)
如果不想依赖系统字体,直接把字体文件打包到项目里,让PDF嵌入字体,这样不管部署到什么环境都能正常显示:
- 准备字体文件:下载支持宽字符集的字体(比如Noto Sans的.ttf文件),放到Django项目的
static/fonts/目录下 - 修改模板CSS:用
@font-face引入字体
<!DOCTYPE html> <html lang="en"> <meta charset="UTF-8"> <head> {% load static %} <!-- 记得加载static标签 --> <style> @font-face { font-family: 'Noto Sans'; src: url('{% static "fonts/NotoSans-Regular.ttf" %}') format('truetype'); font-weight: normal; font-style: normal; } body { font-family: 'Noto Sans', sans-serif; font-size: large; background-color: beige; } </style> <!-- 其他head内容 --> </head> <body> <h2 class="utf">This is myŐ, Ű, ő or ű✅✅ pdf file with special char</h2> </body> </html>
- 修改pdf.py代码:添加
link_callback参数,让xhtml2pdf能正确找到静态文件路径
import os from django.conf import settings from django.contrib.staticfiles import finders from io import BytesIO from django.http import HttpResponse from django.template.loader import get_template from xhtml2pdf import pisa def link_callback(uri, rel): # 处理静态资源路径的回调函数 result = finders.find(uri) if result: if not isinstance(result, (list, tuple)): result = [result] path = list(os.path.realpath(p) for p in result)[0] else: # 区分静态文件和媒体文件路径 if uri.startswith(settings.MEDIA_URL): path = os.path.join(settings.MEDIA_ROOT, uri.replace(settings.MEDIA_URL, "")) elif uri.startswith(settings.STATIC_URL): path = os.path.join(settings.STATIC_ROOT, uri.replace(settings.STATIC_URL, "")) else: return uri # 处理绝对URL if not os.path.isfile(path): raise Exception(f"无法找到资源文件: {uri}") return path def html2pdf(template_source, context_dict={}): template = get_template(template_source) html = template.render(context_dict) result = BytesIO() # 传入link_callback处理静态字体文件 pdf = pisa.CreatePDF(BytesIO(html.encode('utf-8')), result, link_callback=link_callback) if not pdf.err: return HttpResponse(result.getvalue(), content_type="application/pdf") return None
额外注意:Emoji的特殊处理
有些基础字体(比如Noto Sans Regular)可能不包含Emoji,这时候可以在字体栈里加上专门的Emoji字体:
body { font-family: 'Noto Sans', 'Noto Sans Emoji', sans-serif; }
这样渲染器会自动用Noto Sans Emoji显示✅这类符号。
核心思路就是:确保你用的字体包含所有需要显示的字符的字形,要么依赖系统已安装的字体,要么直接嵌入字体到PDF里,就能解决方框问题了。
内容的提问来源于stack exchange,提问作者divyanshu mishra
相关产品推荐
相关产品推荐

