我正在使用reportlab
生成报告。我可以通过四个步骤定义创建过程:1)通过API获取数据,2)过滤数据,3)使用matplotlib
生成图形,以及4)使用reportlab
在PDF中插入信息。 / p>
我在this(感谢Patrick Maupin!)和this(感谢Larry Meyn!)中找到了flowable
matplotlib
的答案{ {1}}。我进行了一些更改,并在下面的代码部分中进行了复制:
ReportLab
我的问题出在import os
from matplotlib import pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
from reportlab.platypus import Paragraph, SimpleDocTemplate, Flowable
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfgen import canvas
from pdfrw import PdfReader, PdfDict
from pdfrw.buildxobj import pagexobj
from pdfrw.toreportlab import makerl
try:
from cStringIO import StringIO as BytesIO
except ImportError:
from io import BytesIO
styles = getSampleStyleSheet()
style = styles['Normal']
#Info if you want to run the script
div = ['may/18', 'jun/18', 'jul/18', 'aug/18', 'sep/18', 'oct/18', 'nov/18', \
'dec/18', 'jan/19', 'feb/19', 'mar/19', 'apr/19']
mes = [0, 15, 149, 0, 59, 0, 0, 101, 45, 25, 751.07, 5358.3]
acc = [0, 15, 164, 164, 223, 223, 223, 324, 369, 394, 1145.07, 6503.37]
class PdfImage(Flowable):
"""
Generates a reportlab image flowable for matplotlib figures. It is initialized
with either a matplotlib figure or a pointer to a list of pagexobj objects and
an index for the pagexobj to be used.
"""
def __init__(self, fig=None, width=200, height=200, cache=None, cacheindex=0):
self.img_width = width
self.img_height = height
if fig is None and cache is None:
raise ValueError("Either 'fig' or 'cache' must be provided")
if fig is not None:
imgdata = BytesIO()
fig.savefig(imgdata, format='pdf')
imgdata.seek(0)
page, = PdfReader(imgdata).pages
image = pagexobj(page)
self.img_data = image
else:
self.img_data = None
self.cache = cache
self.cacheindex = cacheindex
def wrap(self, width, height):
return self.img_width, self.img_height
def drawOn(self, canv, x, y, _sW=0):
if _sW > 0 and hasattr(self, 'hAlign'):
a = self.hAlign
if a in ('CENTER', 'CENTRE', TA_CENTER):
x += 0.5*_sW
elif a in ('RIGHT', TA_RIGHT):
x += _sW
elif a not in ('LEFT', TA_LEFT):
raise ValueError("Bad hAlign value " + str(a))
canv.saveState()
if self.img_data is not None:
img = self.img_data
else:
img = self.cache[self.cacheindex]
if isinstance(img, PdfDict):
xscale = self.img_width / img.BBox[2]
yscale = self.img_height / img.BBox[3]
canv.translate(x, y)
canv.scale(xscale, yscale)
canv.doForm(makerl(canv, img))
else:
canv.drawImage(img, x, y, self.img_width, self.img_height)
canv.restoreState()
class PdfImageCache(object):
"""
Saves matplotlib figures to a temporary multi-page PDF file using the 'savefig'
method. When closed the images are extracted and saved to the attribute 'cache'.
The temporary PDF file is then deleted. The 'savefig' returns a PdfImage object
with a pointer to the 'cache' list and an index for the figure. Use of this
cache reduces duplicated resources in the reportlab generated PDF file.
Use is similar to matplotlib's PdfPages object. When not used as a context
manager, the 'close()' method must be explictly called before the reportlab
document is built.
"""
def __init__(self):
self.pdftempfile = '_temporary_pdf_image_cache_.pdf'
self.pdf = PdfPages(self.pdftempfile)
self.cache = []
self.count = 0
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
def close(self, *args):
self.pdf.close()
pages = PdfReader(self.pdftempfile).pages
pages = [pagexobj(x) for x in pages]
self.cache.extend(pages)
os.remove(self.pdftempfile)
def savefig(self, fig, width=200, height=200):
self.pdf.savefig(fig)
index = self.count
self.count += 1
return PdfImage(width=width, height=height, cache=self.cache, cacheindex=index)
def make_report_cached_figs(outfn):
"""
Makes a dummy report with nfig matplotlib plots using PdfImageCache
to reduce PDF file size.
"""
doc = SimpleDocTemplate(outfn)
style = styles["Normal"]
story = []
with PdfImageCache() as pdfcache:
fig = plt.figure(figsize=(5, 5))
plt.bar(div, acc, width = 0.8, color = 'silver', label = 'Year')
plt.bar(div, mes, width = 0.4, color = 'k', label = 'Month', alpha = 0.5)
plt.legend()
plt.xticks(rotation=60)
plt.close()
img0 = pdfcache.savefig(fig, width=185, height=135)
img0.drawOn(doc, 0, 100)
story.append(img0)
doc.build(story)
make_report_cached_figs("Test.pdf")
方法中,更具体地说,在第一个参数中。在上面的代码中,我得到以下错误:
drawOn
如果我将AttributeError: 'SimpleDocTemplate' object has no attribute 'saveState'
替换为doc = SimpleDocTemplate(outfn)
(我知道如果我要使用doc = canvas.Canvas(outfn)
,则应该做一些其他小的更改),我会遇到另一个错误:
canvas
我已经在官方文档和源代码中搜索了信息,但未成功。因此,如何设置图像的位置?(是否使用img = self.cache[self.cacheindex]
IndexError: list index out of range
方法)