reportlab原生图表类不实用,必须用matplotlib生成图片再插入pdf;关键在于内存绘图、正确设置中文字体、保持宽高比缩放及双端字体注册。

ReportLab 本身不支持直接绘制柱状图、折线图或热力图这类复杂图表——它只提供 LinePlot、BarChart 等极简原生图表类,样式死板、坐标轴难调、中文支持差,实际项目中基本不可用。
必须用 matplotlib(或 seaborn)先生成图片,再把图片塞进 PDF。关键不是“能不能”,而是“怎么塞得稳、不失真、不糊、不乱码”。
为什么不能直接用 reportlab.graphics.charts?
reportlab 自带的 VerticalBarChart 或 LinePlot 类有硬伤:
• 坐标轴标签、图例文字无法可靠设置中文字体,即使注册了字体也常显示方块
• categoryAxis.categoryNames 不支持含空格/中文的字段名,一设就报 AttributeError: 'NoneType' object has no attribute 'split'
• 所有尺寸单位是“点”(points),但绘图区域宽高需手动算,稍错一点就裁边或留白过大
• 没有自动适配数据范围的 scale 逻辑,y 轴常从 0 开始,小数值图表全压扁在底部
• 无法添加趋势线、置信区间、双 y 轴等常见分析元素
用 matplotlib 生成图并插入 ReportLab 的正确流程
核心是:图在内存里画完 → 立即关闭 → 转字节流 → seek(0) → 包装成 Image Flowable → 缩放时保持宽高比
SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、
- 用
plt.figure(dpi=150, figsize=(6, 4))控制输出清晰度,避免 PDF 放大后模糊 - 中文必须在绘图前设置:
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans'],且加plt.rcParams['axes.unicode_minus'] = False防负号变方块 - 保存不用
bbox_inches='tight'(会动态裁边,导致后续缩放失准),改用plt.tight_layout(pad=0.8)+ 显式plt.subplots_adjust(...) - 写入
io.BytesIO()后,必须调buf.seek(0),否则Image(buf)读到空内容,PDF 里显示空白或报IOError: cannot identify image file - 插入时别直接设
width/height,先取原始尺寸:img.drawWidth和img.drawHeight,再按页面宽度缩放:img.drawWidth = width * 0.9,img.drawHeight = img.drawWidth * img.drawHeight / img.drawWidth
中文字体在 PDF 里显示为方块?两个地方都要修
这不是单一问题,是 matplotlib 渲染图时没中文字体 + ReportLab 插入时又没注册对应字体 的双重失败。修复要分两步走:
-
matplotlib端:确保系统有可用中文字体(如 Windows 的simhei.ttf或 macOS 的Heiti.ttc),并在脚本开头执行:import matplotlib as mplmpl.font_manager.fontManager.addfont("simhei.ttf")plt.rcParams.update({"font.sans-serif": ["SimHei"]}) -
ReportLab端:注册同一份字体文件(路径必须一致):from reportlab.pdfbase import pdfmetricsfrom reportlab.pdfbase.ttfonts import TTFontpdfmetrics.registerFont(TTFont("SimHei", "simhei.ttf"))
注意:这个注册只影响 ReportLab 自己画的文字(比如标题、表格单元格),不影响已生成的图片里的文字——图片里的中文是否正常,完全取决于 matplotlib 是否成功渲染
Flowable 流排版时图片撑出页边或压缩变形
根本原因是 Image 构造时不传尺寸,或传的 width/height 与图片原始像素比例不一致。ReportLab 不会自动等比缩放。
- 错误写法:
Image(buf, width=400, height=200)—— 若原图是 1200×600 像素,强行压成 400×200 就拉伸变形 - 正确做法:先实例化,再设尺寸:
img = Image(buf)orig_w, orig_h = img.drawWidth, img.drawHeightpage_width = letter[0] - 2 * inch # 减左右页边距img.drawWidth = page_width * 0.9img.drawHeight = orig_h * img.drawWidth / orig_w - 如果报告要导出 A4/Letter 多种尺寸,建议封装一个
fit_image_to_page(img, max_width, max_height)函数,统一处理等比缩放和上下居中
真正卡住人的从来不是“怎么画图”,而是图生成后那一连串隐性依赖:dpi 设置、内存缓冲区位置、字体路径一致性、宽高比锁定、以及 ReportLab 对 Flowable 尺寸的“零容忍”。漏掉任意一环,PDF 就要么空白、要么糊、要么全是方块——而这些错误往往没有明确报错,只静默失败。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










