Reputation: 3027
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 "Report" class used above is simply a thin wrapper around SimpleDocTemplate
that sets up some defaults but delegates to SimpleDocTemplate's build
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
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.
Upvotes: 3
Views: 2742
Reputation: 3027
I eventually brought this matter to a close by using a custom Image
subclass:
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
Saving the graphs in vector graphics formats eg EPS, saving as JPEG, saving as PNG with and without the alpha channel all did not seem to make a difference.
Upvotes: 1
Reputation: 43505
The optimal solution would be to generate your graphs in a vector format like postscript that is supported by reportlab. A lot of UNIX software can do this out of the box, and on windows you can use the excellent PDFCreator.
If you have to use raster images for your graphs, try converting your images to JPEG format. Those can be easily embedded in a PDF file using the DCTDecode filter. (This is e.g. what jpeg2pdf does.)
Upvotes: 0