ReportLab导出PDF表格中文乱码需注册中文字体并用Paragraph包裹内容;数据须转字符串;列宽、合并单元格、跨页表头需显式设置;Web返回PDF要设Content-Disposition头;大表格用LongTable并复用样式,及时关闭BytesIO。

ReportLab导出PDF表格时中文乱码怎么办
默认字体不支持中文,Helvetica一渲染就是方块或空格。必须显式注册中文字体并设置为默认样式。
实操建议:
- 下载一个TTF中文字体(如
simhei.ttf或NotoSansCJKsc-Regular.otf),放在项目目录下(比如fonts/子目录) - 用
pdfmetrics.registerFont注册,再用ParagraphStyle指定fontName为该字体名 - 表格单元格内容必须用
Paragraph包裹,不能直接传字符串——否则样式不生效,中文仍乱码
示例关键行:
from reportlab.pdfbase import pdfmetrics<br>from reportlab.pdfbase.ttfonts import TTFont<br>pdfmetrics.registerFont(TTFont('SimHei', 'fonts/simhei.ttf'))<br>style = ParagraphStyle(name='Normal', fontName='SimHei', fontSize=10)
用Table生成动态表格的常见坑
表格数据是Python列表嵌套,但Table对“单元格类型”很敏感:字符串能直接用,但None、数字、日期会报AttributeError: 'int' object has no attribute 'split'。
立即学习“Python免费学习笔记(深入)”;
实操建议:
- 所有字段先统一转成字符串:
str(item) if item is not None else '' - 列宽别靠猜,用
colWidths参数显式设(单位是point),比如[80, 120, 100, *([60] * 4)] - 合并单元格用
span元组,格式是(start_col, start_row, end_col, end_row),注意行列索引从0开始 - 如果表格跨页,加
repeatRows=1让表头在每页顶部重复
Web接口返回PDF时Content-Type和响应处理
Flask或FastAPI里直接return Response(pdf_bytes, media_type='application/pdf')看似正确,但浏览器可能不自动下载,或中文文件名变乱码。
实操建议:
- 必须设
headers里的Content-Disposition,例如:attachment; filename="report_2024.pdf" - 文件名含中文?用
filename*=UTF-8''%E6%8A%A5%E5%91%8A.pdf编码方式,避免UnicodeEncodeError - PDF字节流别用
StringIO,必须用BytesIO,否则canvas.save()会失败 - 调试时先写到本地文件验证内容是否正常:
with open('debug.pdf', 'wb') as f: f.write(pdf_bytes)
性能瓶颈在哪:大表格导出慢、内存暴涨
ReportLab本身不慢,但错误用法会让内存翻几倍:反复创建Canvas、在循环里调用drawString画上百个单元格、没复用ParagraphStyle。
实操建议:
- 表格超过200行,改用
LongTable(它支持分页和更优内存管理) - 禁用
canvas.showPage()手动翻页——SimpleDocTemplate.build()会自动处理 - 字体只注册一次,样式对象复用,别在循环里反复新建
ParagraphStyle - 导出前用
sys.getsizeof(pdf_bytes)粗略看PDF体积,超5MB要考虑分段导出或压缩图片
最常被忽略的是:PDF生成后没及时关闭BytesIO,尤其在Web服务里,会导致连接挂起或内存泄漏。记得buffer.seek(0)之后就return,别留着buffer继续读写。










