Index: main/waeup.kofa/trunk/src/waeup/kofa/applicants/pdf.py
===================================================================
--- main/waeup.kofa/trunk/src/waeup/kofa/applicants/pdf.py	(revision 8057)
+++ main/waeup.kofa/trunk/src/waeup/kofa/applicants/pdf.py	(revision 8059)
@@ -20,28 +20,23 @@
 """
 import grok
-from datetime import datetime
-from reportlab.pdfgen import canvas
+from reportlab.lib.styles import getSampleStyleSheet
 from reportlab.lib.units import cm
-from reportlab.lib.pagesizes import A4
-from reportlab.lib.styles import getSampleStyleSheet
-from reportlab.platypus import Frame, Paragraph, Image, Table, Spacer
-from reportlab.platypus.tables import TableStyle
-from zope.i18n import translate
+from reportlab.platypus import Paragraph, Image, Table, Spacer
 from zope.component import getUtility
 from zope.formlib.form import setUpEditWidgets
+from zope.i18n import translate
 from zope.publisher.browser import TestRequest
-from waeup.kofa.widgets.datewidget import FriendlyDateDisplayWidget
 from waeup.kofa.applicants.interfaces import IApplicant, IApplicantsUtils
 from waeup.kofa.browser import DEFAULT_PASSPORT_IMAGE_PATH
+from waeup.kofa.browser.interfaces import IPDFCreator
 from waeup.kofa.interfaces import IExtFileStore, IPDF, IKofaUtils
 from waeup.kofa.interfaces import MessageFactory as _
-
-SLIP_STYLE = TableStyle(
-    [('VALIGN',(0,0),(-1,-1),'TOP')]
-    )
-
+from waeup.kofa.widgets.datewidget import FriendlyDateDisplayWidget
 
 class PDFApplicationSlip(grok.Adapter):
-
+    """Create a PDF application slip for applicants.
+    """
+    # XXX: Many things in here are reusable. We might want to split
+    # things. Possibly move parts to IPDFCreator?
     grok.context(IApplicant)
     grok.implements(IPDF)
@@ -129,49 +124,16 @@
         return data
 
-    def __call__(self, view=None):
-        """Return a PDF representation of the context applicant.
-
-        If no `view` is given, the course admitted field will be an
-        empty string and author will be set as ``'unknown'``.
-
-        If a `view` is given, author will be set as the calling
-        principal.
-        """
-        pdf = canvas.Canvas('application_slip.pdf',pagesize=A4)
-        pdf.setTitle(self.title)
-        pdf.setSubject('Application')
-        if view is None:
-            pdf.setAuthor('unknown')
-        if view is not None:
-            pdf.setAuthor('%s (%s)' % (view.request.principal.title,
-                view.request.principal.id))
-        pdf.setCreator('Kofa')
-        width, height = A4
-        style = getSampleStyleSheet()
-        pdf.line(1*cm,height-(1.8*cm),width-(1*cm),height-(1.8*cm))
-
-        story = []
-        frame_header = Frame(1*cm,1*cm,width-(1.7*cm),height-(1.7*cm))
-        header_title = getattr(grok.getSite(), 'name', u'Sample University')
-        story.append(Paragraph(header_title, style["Heading1"]))
-        frame_header.addFromList(story,pdf)
-
-        story = []
-        frame_body = Frame(1*cm,1*cm,width-(2*cm),height-(3.5*cm))
-        story.append(Paragraph(self.title, style["Heading2"]))
-        story.append(Spacer(1, 18))
-        for msg in self.context.history.messages:
-            f_msg = '<font face="Courier" size=10>%s</font>' % msg
-            story.append(Paragraph(f_msg, style["Normal"]))
-        story.append(Spacer(1, 24))
-
-        # Setup table data
-        data = []
-        # Insert passport photograph
-        data = self._addPassportImage(data)
-        # Render widget fields
+    def _getWidgetsTable(self, view):
+        """Return a reportlab table buildt of widgets.
+        """
+        style = getSampleStyleSheet()
+        table_data = []
+        table_style = [('LEFTPADDING', (0,0), (0,-1), 0),
+                       ('VALIGN', (0,0), (-1,-1), 'TOP'),
+                       ]
+        row_num = 0
         widgets = self._setUpWidgets()
         portal_language = getUtility(IKofaUtils).PORTAL_LANGUAGE
-        for widget in widgets: # self.widgets:
+        for widget in widgets:
             separators = getUtility(IApplicantsUtils).SEPARATORS_DICT
             if separators and separators.get(widget.name):
@@ -180,5 +142,9 @@
                     target_language=portal_language))
                 f_headline = Paragraph(f_headline, style["Normal"])
-                data.append([f_headline ])
+                table_data.append([f_headline ])
+                table_style.append(('SPAN', (0,row_num), (-1,row_num)),)
+                table_style.append(
+                    ('TOPPADDING', (0,row_num), (-1,row_num), 12),)
+                row_num += 1
             f_label = '<font size=12>%s</font>:' % translate(
                 widget.label.strip(), 'waeup.kofa',
@@ -191,5 +157,6 @@
             f_text = f_text.replace('\n', '')
             f_text = Paragraph(f_text, style["Normal"])
-            data.append([f_label,f_text])
+            table_data.append([f_label,f_text])
+            row_num += 1
         adm_translation = translate(_('Admitted Course of Study:'),
                 'waeup.kofa', target_language=portal_language)
@@ -199,14 +166,17 @@
         f_label = Paragraph(f_label, style["Normal"])
         f_text = Paragraph(f_text, style["Normal"])
-        data.append([f_label,f_text])
+        table_data.append([f_label,f_text])
+        row_num += 1
 
         # Add dept. and faculty if applicable
-        data = self._addDeptAndFaculty(data)
+        table_data = self._addDeptAndFaculty(table_data)
 
         # Create table
-        table = Table(data,style=SLIP_STYLE, colWidths=[10*cm, 8.5*cm])
-        story.append(table)
-
-        # Add some comments
+        table = Table(table_data,style=table_style)
+        table.hAlign = 'LEFT'
+        return table
+
+    def _addComments(self, data):
+        style = getSampleStyleSheet()
         if self.context.state == 'created':
             comment1 = _(
@@ -214,5 +184,6 @@
                 ' and enter your new credentials:' +
                 ' user name= ${a}, password = ${b}.</font>', mapping = {
-                'a':self.context.student_id, 'b':self.context.application_number}
+                    'a':self.context.student_id,
+                    'b':self.context.application_number}
                 )
             comment2 = _(
@@ -222,15 +193,40 @@
             comment1 = Paragraph(comment1, style["Normal"])
             comment2 = Paragraph(comment2, style["Normal"])
-            story.append(Spacer(1, 18))
-            story.append(comment1)
-            story.append(comment2)
-
-        frame_body.addFromList(story,pdf)
-
-        story = []
-        frame_footer = Frame(1*cm,0,width-(2*cm),1*cm)
-        timestamp = datetime.now().strftime("%d/%m/%Y %H:%M:%S")
-        f_text = '<font size=10>%s</font>' % timestamp
-        story.append(Paragraph(f_text, style["Normal"]))
-        frame_footer.addFromList(story,pdf)
-        return pdf.getpdfdata()
+            data.extend([Spacer(1, 18), comment1, comment2])
+        return data
+
+    def __call__(self, view=None):
+        """Return a PDF representation of the context applicant.
+
+        If no `view` is given, the course admitted field will be an
+        empty string and author will be set as ``'unknown'``.
+
+        If a `view` is given, author will be set as the calling
+        principal.
+        """
+        doc_title = self.title.replace('-', '\n')
+        style = getSampleStyleSheet()
+        data = []
+
+        # append history
+        for msg in self.context.history.messages:
+            f_msg = '<font face="Courier" size=10>%s</font>' % msg
+            data.append(Paragraph(f_msg, style["Normal"]))
+        data.append(Spacer(1, 20))
+
+        # append photograph
+        img_path = getattr(
+            getUtility(IExtFileStore).getFileByContext(self.context),
+            'name', DEFAULT_PASSPORT_IMAGE_PATH)
+        doc_img = Image(img_path, width=4*cm, height=3*cm, kind='bound')
+        doc_img.hAlign='LEFT'
+        data.append(doc_img)
+
+        # append widgets
+        data.append(self._getWidgetsTable(view))
+
+        # append comments
+        data = self._addComments(data)
+
+        creator = getUtility(IPDFCreator)
+        return creator.create_pdf(data, None, doc_title)
