使用Canvas绘制图像和SimpleDocTemplate
我正在用ReportLab在Django视图中生成PDF文件,这些PDF很简单,包含头部、内容和底部。
我使用了SimpleDocTemplate,它非常合适,可以用来绘制内容中的表格,头部和底部是通过以下方式绘制的:
build([data], onFirstPage=drawPageFrame, onLaterPages=drawPageFrame).
我的问题是,如何像使用Canvas.drawImage(...)那样绘制一张图片?我需要一张“浮动”的图片……可以放在我想要的位置,然而在SimpleDocTemplate中我没有Canvas对象来做到这一点。
我搜索后发现了这个:
Platypus布局使用的是流动对象(flowables)。打包器通常会在每个流动对象被包装、拆分或绘制时,将属性canv设置到它们上面,也就是在包装、拆分和绘制的方法中。在这些方法内部,你可以通过self的canv属性访问到canvas。
这个怎么用呢?
嗯,还有更多东西需要测试:
flowables.Macro
flowables.CallerMacro
# -*- coding: utf-8 -*- from reportlab.lib.pagesizes import A4, landscape, portrait from reportlab.lib.styles import getSampleStyleSheet from reportlab.platypus import Table, Flowable, SimpleDocTemplate, Paragraph, Spacer, Image from reportlab.lib import randomtext from reportlab import platypus import os,random styles = getSampleStyleSheet() path = os.path.realpath(os.path.dirname(__file__)) def drawPageFrame(canvas, doc): canvas.saveState() canvas.drawImage(path+"/ujiPDF.jpg",50,50,57,57) canvas.restoreState() doc = SimpleDocTemplate("salida.pdf",pagesize=A4) elementos = [] com = 'canvas.drawImage("'+path+'/ujiPDF.jpg",100,100,57,57)' print com elementos.append(platypus.flowables.Macro('canvas.saveState()')) print platypus.flowables.Macro(com) elementos.append(platypus.flowables.Macro(com)) elementos.append(platypus.flowables.Macro('canvas.restoreState()')) para = Paragraph(randomtext.randomText(randomtext.PYTHON,20), styles["Normal"]) elementos.append(para) doc.build(elementos,onFirstPage=drawPageFrame, onLaterPages=drawPageFrame)
这是宏观的方法……干净的退出,但没有第二张图片。
3 个回答
在platypus里有一个叫做Image的类。只需要使用 from reportlab.platypus import Image
这行代码,就可以访问这个类了。它的工作方式和其他类一样,使用 smth.append(Image(filename))
这行代码可以把图片添加到你需要放进pdf里的对象中。这个信息可以在 Tyler Lessman的个人网站 找到。
如果我理解得没错,你并不需要重新创建整个文档模板,你只是想要一个可以放进SimpleDocTemplate的流动对象。你可以通过简单地继承Flowable类来实现这一点。
-> 特别是因为很多人常常询问如何将matplotlib对象放入reportlab,我将展示如何用matplotlib生成一个图,然后使用修改过的流动对象把这个图放入SimpleDocTemplate中(而不需要把文件保存到磁盘上)。这个概念适用于任何文件或你可以放入cStringIO的东西。
-> 下面的代码确实允许你把图放在文字上面(只需把高度从负值改为正值,就能把图推到该部分顶部的想象线之上)。
关键的概念是,每个Flowable本身也包含一个可以绘图的画布。
import matplotlib.pyplot as plt
import cStringIO
from reportlab.lib.units import inch, cm
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, Table
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
import reportlab.lib, reportlab.platypus
class flowable_fig(reportlab.platypus.Flowable):
def __init__(self, imgdata):
reportlab.platypus.Flowable.__init__(self)
self.img = reportlab.lib.utils.ImageReader(imgdata)
def draw(self):
self.canv.drawImage(self.img, 0, 0, height = -2*inch, width=4*inch)
# http://www.reportlab.com/apis/reportlab/2.4/pdfgen.html
doc = SimpleDocTemplate(("report.pdf"),pagesize=letter,
rightMargin=72,leftMargin=72,
topMargin=72,bottomMargin=18)
Story=[]
styles=getSampleStyleSheet()
ptext = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi massa dolor, vulputate quis elit sed, sagittis consectetur erat. Sed lobortis nisi eros, eu maximus enim iaculis ac. Vestibulum sagittis urna nec interdum aliquam. Pellentesque ornare velit ut ante ullamcorper, vulputate accumsan nisi vulputate. Fusce consectetur dolor quam. Phasellus hendrerit, ligula vel consectetur pretium, lorem neque dapibus eros, ornare suscipit ipsum dolor id nisl. Sed vel orci id leo efficitur lobortis sit amet id risus. Nullam euismod, ipsum a posuere scelerisque, ante lorem ultrices nibh, ut feugiat metus ex congue enim. Nam lobortis, metus id pellentesque feugiat, arcu orci rutrum felis, quis luctus urna nisl at nulla. Donec eu eros pharetra dolor congue facilisis at ac magna. Nullam eu ultricies metus. Sed sodales, libero viverra pellentesque tempus, magna purus convallis nibh, eu condimentum tortor erat tincidunt turpis. Vestibulum scelerisque tincidunt egestas. Nullam commodo diam nisl, sed consequat ex sagittis eu.'
Story.append(Paragraph(ptext, styles["Normal"]))
fig = plt.figure(figsize=(10, 3))
plt.plot([1,2,3,4])
plt.ylabel('This is a boring plot')
imgdata = cStringIO.StringIO()
fig.savefig(imgdata, format='png')
imgdata.seek(0) # rewind the data
pic = flowable_fig(imgdata)
Story.append(pic)
doc.build(Story)
你可以扩展这个最小的代码片段,完成你想要的任何功能,并且可以往Story变量中添加尽可能多的元素(其实它只是一个流动对象的列表)。这里的魔力在于,我们给文档构建器提供了一个Flowable,它使用Flowable自己的画布来绘制图形。
编辑:我差点忘了,因为这让我们可以完全使用drawImage,所以我们也可以让那张图片或图表有透明度,这样其他东西就可以透过它看到。关于drawImage的详细信息可以在文档中找到。
你最好的选择是创建一个SimpleDocTemplate或BaseDocTemplate的子类。在这个子类的构建方法里,你可以使用画布。如果你想利用SimpleDocTemplate做的所有事情,可以尝试直接从site-packages/reportlab/platypus/doctemplate.py
复制它的内容。