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

如何通过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()

代码说明

  1. 解析SVG:使用lxml读取Matplotlib生成的SVG,提取<defs>中的pattern定义,记录hatch的路径样式。
  2. 替换pattern引用:找到所有使用url(#xxx)填充的矩形元素,根据原pattern的样式,在矩形内手动绘制多条斜线,模拟hatch效果。
  3. 重新生成PDF:将修改后的SVG传入svg2rlg,再通过ReportLab生成包含hatch图案的PDF。

内容的提问来源于stack exchange,提问作者Julien

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 17:19:02