# # Using the ReportLab toolkit from within Zope # # WARNING : The MyPDFDoc class deals with ReportLab's platypus framework, # while the MyPageTemplate class directly deals with ReportLab's # canvas, this way you know how to do with both... # # License : the ReportLab Toolkit's one # see : http://www.reportlab.com # # Author : Jerome Alet - alet@unice.fr # # import string, cStringIO try : from Shared.reportlab.platypus.paragraph import Paragraph from Shared.reportlab.platypus.doctemplate import * from Shared.reportlab.lib.units import inch from Shared.reportlab.lib import styles from Shared.reportlab.lib.utils import ImageReader except ImportError : from reportlab.platypus.paragraph import Paragraph from reportlab.platypus.doctemplate import * from reportlab.lib.units import inch from reportlab.lib import styles from reportlab.lib.utils import ImageReader class MyPDFDoc : class MyPageTemplate(PageTemplate) : """Our own page template.""" def __init__(self, parent) : """Initialise our page template.""" # # we must save a pointer to our parent somewhere self.parent = parent # Our doc is made of a single frame content = Frame(0.75 * inch, 0.5 * inch, parent.document.pagesize[0] - 1.25 * inch, parent.document.pagesize[1] - (1.5 * inch)) PageTemplate.__init__(self, "MyTemplate", [content]) # get all the images we need now, in case we've got # several pages this will save some CPU self.logo = self.getImageFromZODB("logo") def getImageFromZODB(self, name) : """Retrieves an Image from the ZODB, converts it to PIL, and makes it 0.75 inch high. """ try : # try to get it from ZODB logo = getattr(self.parent.context, name) except AttributeError : # not found ! return None # Convert it to PIL image = ImageReader(cStringIO.StringIO(str(logo.data))) (width, height) = image.getSize() # scale it to be 0.75 inch high multi = ((height + 0.0) / (0.75 * inch)) width = int(width / multi) height = int(height / multi) return ((width, height), image) def beforeDrawPage(self, canvas, doc) : """Draws a logo and an contribution message on each page.""" canvas.saveState() if self.logo is not None : # draws the logo if it exists ((width, height), image) = self.logo canvas.drawImage(image, inch, doc.pagesize[1] - inch, width, height) canvas.setFont('Times-Roman', 10) canvas.drawCentredString(inch + (doc.pagesize[0] - (1.5 * inch)) / 2, 0.25 * inch, "Contributed by Jerome Alet - alet@unice.fr") canvas.restoreState() def __init__(self, context, filename) : # save some datas self.context = context self.built = 0 self.objects = [] # we will build an in-memory document # instead of creating an on-disk file. self.report = cStringIO.StringIO() # initialise a PDF document using ReportLab's platypus self.document = BaseDocTemplate(self.report) # add our page template # (we could add more than one, but I prefer to keep it simple) self.document.addPageTemplates(self.MyPageTemplate(self)) # get the default style sheets self.StyleSheet = styles.getSampleStyleSheet() # then build a simple doc with ReportLab's platypus sometext = "A sample script to show how to use ReportLab from within Zope" url = self.escapexml(context.absolute_url()) urlfilename = self.escapexml(context.absolute_url() + '/%s' % filename) self.append(Paragraph("Using ReportLab from within Zope", self.StyleSheet["Heading3"])) self.append(Spacer(0, 10)) self.append(Paragraph("You launched it from : %s" % url, self.StyleSheet['Normal'])) self.append(Spacer(0, 40)) self.append(Paragraph("If possible, this report will be automatically saved as : %s" % urlfilename, self.StyleSheet['Normal'])) # generation du document PDF self.document.build(self.objects) self.built = 1 def __str__(self) : """Returns the PDF document as a string of text, or None if it's not ready yet.""" if self.built : return self.report.getvalue() else : return None def append(self, object) : """Appends an object to our platypus "story" (using ReportLab's terminology).""" self.objects.append(object) def escapexml(self, s) : """Escape some xml entities.""" s = string.strip(s) s = string.replace(s, "&", "&") s = string.replace(s, "<", "<") return string.replace(s, ">", ">") def rlzope(self) : """A sample external method to show people how to use ReportLab from within Zope.""" try: # # which file/object name to use ? # append ?name=xxxxx to rlzope's url to # choose another name filename = self.REQUEST.get("name", "dummy.pdf") if filename[-4:] != '.pdf' : filename = filename + '.pdf' # tell the browser we send some PDF document # with the requested filename # get the document's content itself as a string of text content = str(MyPDFDoc(self, filename)) # we will return it to the browser, but before that we also want to # save it into the ZODB into the current folder try : self.manage_addFile(id = filename, file = content, title = "A sample PDF document produced with ReportLab", precondition = '', content_type = "application/pdf") except : # it seems an object with this name already exists in the ZODB: # it's more secure to not replace it, since we could possibly # destroy an important PDF document of this name. pass self.REQUEST.RESPONSE.setHeader('Content-Type', 'application/pdf') self.REQUEST.RESPONSE.setHeader('Content-Disposition', 'attachment; filename=%s' % filename) except: import traceback, sys, cgi content = sys.stdout = sys.stderr = cStringIO.StringIO() self.REQUEST.RESPONSE.setHeader('Content-Type', 'text/html') traceback.print_exc() sys.stdout = sys.__stdout__ sys.stderr = sys.__stderr__ content = '
%s
' % cgi.escape(content.getvalue()) # then we also return the PDF content to the browser return content