如何通过ReportLab将带hatch pattern的Matplotlib图表渲染为PDF?
解决Matplotlib Hatch图案经svg2rlg转ReportLab PDF不渲染的问题
问题概述
使用Matplotlib绘制带hatch图案的图表,导出为SVG后通过svg2rlg转换为ReportLab的Drawing对象,最终生成的PDF中hatch图案丢失。原因是svg2rlg无法处理Matplotlib SVG输出中的url(#xxx)格式的pattern引用。
解决方案
通过解析并修改Matplotlib生成的SVG内容,将引用的hatch pattern替换为直接绘制的路径,让svg2rlg能够识别并渲染。以下是修改后的完整代码:
from io import BytesIO import matplotlib.pyplot as plt import numpy as np from reportlab.graphics import renderPDF from reportlab.pdfgen import canvas from svglib.svglib import svg2rlg from lxml import etree # 生成图表 x = np.arange(100) y = np.random.randint(50, 90, 100) fig = plt.figure() plt.plot(x, y, "o", color="b") plt.axhspan(ymin=40, ymax=55, hatch="/", facecolor="none") plt.close(fig) # 关闭弹窗,避免干扰 # 保存为SVG到内存 imgdata = BytesIO() fig.savefig(imgdata, format="svg") imgdata.seek(0) # 解析SVG并处理hatch pattern引用 tree = etree.parse(imgdata) root = tree.getroot() ns = {'svg': 'http://www.w3.org/2000/svg'} # 提取所有pattern定义,记录路径和样式 pattern_map = {} for pat in root.xpath('//svg:defs/svg:pattern', namespaces=ns): pat_id = pat.get('id') path_elem = pat.xpath('.//svg:path', namespaces=ns)[0] pattern_map[pat_id] = { 'd': path_elem.get('d'), 'stroke': path_elem.get('stroke'), 'stroke-width': path_elem.get('stroke-width') } # 遍历所有使用pattern引用的元素,替换为手动绘制的斜线 for elem in root.xpath('//*[@fill]', namespaces=ns): fill_val = elem.get('fill') if fill_val.startswith('url(#'): pat_id = fill_val[5:-1] if pat_id not in pattern_map: continue # 获取原矩形的位置和尺寸 x1 = float(elem.get('x')) y1 = float(elem.get('y')) width = float(elem.get('width')) height = float(elem.get('height')) pat_style = pattern_map[pat_id] stroke_width = float(pat_style['stroke-width']) # 生成斜线路径,匹配Matplotlib hatch="/"的密度 step = stroke_width * 5 current_y = y1 while current_y < y1 + height: # 创建斜线元素 path = etree.SubElement(elem, '{http://www.w3.org/2000/svg}path') path.set('d', f'M{x1},{current_y} L{x1+width},{current_y+width}') path.set('stroke', pat_style['stroke']) path.set('stroke-width', pat_style['stroke-width']) current_y += step # 移除原有的pattern引用填充 elem.set('fill', 'none') # 将修改后的SVG写回内存 modified_svg = BytesIO() tree.write(modified_svg, encoding='utf-8', xml_declaration=True) modified_svg.seek(0) # 转换为ReportLab Drawing并生成PDF drawing = svg2rlg(modified_svg) drawing.scale(0.5, 0.5) c = canvas.Canvas("test.pdf") renderPDF.draw(drawing, c, 10, 200) c.drawString(100, 100, "Hatch pattern rendered successfully") c.showPage() c.save()
代码说明
- 解析SVG:使用
lxml读取Matplotlib生成的SVG,提取<defs>中的pattern定义,记录hatch的路径样式。 - 替换pattern引用:找到所有使用
url(#xxx)填充的矩形元素,根据原pattern的样式,在矩形内手动绘制多条斜线,模拟hatch效果。 - 重新生成PDF:将修改后的SVG传入
svg2rlg,再通过ReportLab生成包含hatch图案的PDF。
内容的提问来源于stack exchange,提问作者Julien
相关产品推荐
相关产品推荐

