I am working on a report that includes a mixture of tables and images. The images [ graphs, actually ] are saved to the filesystem in .png form.
The method that actually renders the PDF is:
def _render_report(report_data):
file_name = get_file_name() # generate random filename for report
rpt = Report(settings.MEDIA_ROOT + os.sep + file_name)
Story = []
for (an, sam, event), props in report_data.iteritems():
Story.append(Paragraph("%s - sample %s results for %s" % (an.name, sam.name, event.name), styles["Heading2"]))
data_list = [['Lab', 'Method', 'Instrument', 'Unit', 'Raw Result', 'Converted Result', 'Outlier']]
for (index, series) in props['frame'].iterrows():
data_list.append(_format([
Paragraph(Lab.objects.get(pk=series['labs']).name, styles['BodyText']),
Paragraph(Method.objects.get(pk=series['methods']).name, styles['BodyText']),
Paragraph(Instrument.objects.get(pk=series['instruments']).name, styles['BodyText']),
Paragraph(Unit.objects.get(pk=series['units']).name, styles['BodyText']),
series['raw_results'],
series['results'],
series['outlier']
]))
table = Table(data_list, colWidths=[45 * mm, 35 * mm, 35 * mm, 25 * mm, 35 * mm, 35 * mm, 35 * mm], repeatRows=1)
Story.append(table)
Story.append(PageBreak())
if props['graph'] is not None:
Story.append(Image("/tmp/%s" % props['graph'], width=10 * inch, height=6 * inch))
Story.append(PageBreak())
rpt.draw(Story, onFirstPage=setup_header_and_footer, onLaterPages=setup_header_and_footer)
return file_name
Background Information
- The page is set up as an A4, in landscape orientation
- My development environment is a virtualenv; PIL 1.1.7 and reportlab 2.6 are installed and functional
The "Report" class used above is simply a thin wrapper around
SimpleDocTemplate
that sets up some defaults but delegates to SimpleDocTemplate'sbuild
implementation. Its code is:class Report(object): def __init__(self, filename, doctitle="Report", docauthor="<default>", docsubject="<default>", doccreator="<default>", orientation="landscape", size=A4): DEFAULTS = { 'leftMargin' : 10 * mm, 'rightMargin' : 10 * mm, 'bottomMargin' : 15 * mm, 'topMargin' : 36 * mm, 'pagesize' : landscape(size) if orientation == "landscape" else portrait(size), 'title' : doctitle, 'author' : docauthor, 'subject' : docsubject, 'creator' : doccreator } self.doc = SimpleDocTemplate(filename, **DEFAULTS) def draw(self, flowables, onFirstPage=setup_header_and_footer, onLaterPages=setup_header_and_footer): self.doc.build(flowables, onFirstPage=setup_header_and_footer, onLaterPages=setup_header_and_footer, canvasmaker=NumberedCanvas)
What I have already looked at
- I have confirmed that the images exist on disk. The paths are fully qualified paths.
- PIL is installed, and is able to read the images correctly
- The space assigned to the image is adequate; I have confirmed this by calculation. Also, if I increase the image size, ReportLab complains about the Image Flowable being too large. The current dimension should fit.
- I have tested with and without the page breaks; they do not seem to make any difference
The Problem
The table, headings and page templates render OK but the images are blank. Earlier today, I had encountered this issue [ when setting up the templates used by this report ]. The workaround was to use canvas.drawInlineImage(...
in place of canvas.DrawImage(...
. It therefore looks as though there is an issue with my setup; I could use some pointers on how to debug it.
Update
I was able to apply a variant of the same workaround used in this linked question ( use canvas.drawInlineImage
in place of canvas.drawImage
. I subclassed `Image' as follows:
class CustomImage(Image):
"""
Override - to use inline image instead; inexplicable bug with non inline images
"""
def draw(self):
lazy = self._lazy
if lazy>=2: self._lazy = 1
self.canv.drawInlineImage(self.filename,
getattr(self,'_offs_x',0),
getattr(self,'_offs_y',0),
self.drawWidth,
self.drawHeight
)
if lazy>=2:
self._img = None
self._lazy = lazy
The only change from the "stock" Image class is in one line - using self.canv.drawInlineImage
where there was self.canvas.drawImage
before.
This "works" in the sense that the images are finally visible in my PDF. The reason why drawImage
is not working still remains a mystery.
I have tried @PedroRomano's suggestion ( to make sure the images RGBA ), and even tried JPEG images instead of PNGs. These did not make a difference.