def _finalize_page(): canvas.setFont("DejaVu Serif", 20) canvas.drawCentredString(width // 2, height - cm(2), "%s - %s" % (room.roomname, lastdate.strftime(titledatefmt))) t = Table(tbldata, colWidths=[cm(3), width - cm(3) - 2 * table_horiz_margin]) t.setStyle(TableStyle(tblstyle)) w, h = t.wrapOn(canvas, width, height) t.drawOn(canvas, table_horiz_margin, height - cm(4) - h) canvas.showPage()
def draw_header(self): if self.preview: t = self.canvas.beginText() t.setTextOrigin(cm(6), cm(4)) t.setFont("DejaVu Serif Italic", 70) t.setFillColorRGB(0.9, 0.9, 0.9) t.textLine("PREVIEW PREVIEW") self.canvas.rotate(45) self.canvas.drawText(t) self.canvas.rotate(-45) if self.logo: im = Image(self.logo, width=cm(3), height=cm(3)) im.drawOn(self.canvas, cm(2), cm(25)) if self.headertext: t = self.canvas.beginText() t.setFillColor(colors.black) t.setFont("DejaVu Serif", 9) t.setTextOrigin(cm(6), cm(27.5)) self.textlines(t, self.headertext) self.canvas.drawText(t) if self.sendertext: self._draw_multiline_aligned(self.sendertext, cm(2), cm(23.5), cm(9), cm(4)) self._draw_multiline_aligned("To:\n%s" % self.recipient, cm(11), cm(23.5), cm(9), cm(4)) p = self.canvas.beginPath() p.moveTo(cm(2), cm(18.9)) p.lineTo(cm(19), cm(18.9)) self.canvas.drawPath(p)
def save(self): self.draw_header() self.canvas.drawCentredString(cm(10.5), cm(19), "REFUND NOTE {0} FOR INVOICE NUMBER {1}".format(self.refundid, self.invoicenum)) self.canvas.drawString(cm(2), cm(18), "Reason for refund: {0}".format(self.reason)) tblpaid = [ ["Amount paid"], ["Item", "Amount"], ["Amount", "{0:.2f} {1}".format(self.invoiceamount, settings.CURRENCY_SYMBOL)], ] tblrefunded = [ ["Amount refunded"], ["Item", "Amount"], ["Amount", "{0:.2f} {1}".format(self.refundamount, settings.CURRENCY_SYMBOL)], ] tblprevious = [ ["Amount previously refunded"], ["Item", "Amount"], ["Amount", "{0:.2f} {1}".format(self.previousamount, settings.CURRENCY_SYMBOL)], ] if self.invoicevat: tblpaid.extend([ ["VAT", "{0:.2f} {1}".format(self.invoicevat, settings.CURRENCY_SYMBOL)], ["", "{0:.2f} {1}".format(self.invoiceamount + self.invoicevat, settings.CURRENCY_SYMBOL)], ]) tblrefunded.extend([ ["VAT", "{0:.2f} {1}".format(self.refundvat, settings.CURRENCY_SYMBOL)], ["", "{0:.2f} {1}".format(self.refundamount + self.refundvat, settings.CURRENCY_SYMBOL)], ]) tblprevious .extend([ ["VAT", "{0:.2f} {1}".format(self.previousvat, settings.CURRENCY_SYMBOL)], ["", "{0:.2f} {1}".format(self.previousamount + self.previousvat, settings.CURRENCY_SYMBOL)], ]) style = [ ('SPAN', (0, 0), (1, 0)), ('BACKGROUND', (0, 0), (-1, 0), colors.lightgrey), ('ALIGN', (0, 0), (0, 0), 'CENTER'), ('ALIGN', (1, 1), (1, -1), 'RIGHT'), ('LINEBELOW', (0, 1), (-1, 1), 1, colors.black), ('OUTLINE', (0, 0), (-1, -1), 1, colors.black), ] if self.invoicevat: style.append( ('LINEABOVE', (-1, -1), (-1, -1), 2, colors.black), ) t = Table(tblpaid, [cm(10.5), cm(2.5), cm(1.5), cm(2.5)]) t.setStyle(TableStyle(style)) w, h = t.wrapOn(self.canvas, cm(10), cm(10)) t.drawOn(self.canvas, (self.canvas._pagesize[0] - w) // 2, cm(17) - h) if self.previousamount: t = Table(tblprevious, [cm(10.5), cm(2.5), cm(1.5), cm(2.5)]) t.setStyle(TableStyle(style)) w, h = t.wrapOn(self.canvas, cm(10), cm(10)) t.drawOn(self.canvas, (self.canvas._pagesize[0] - w) // 2, cm(17) - h * 2 - cm(1)) extraofs = h + cm(1) else: extraofs = 0 t = Table(tblrefunded, [cm(10.5), cm(2.5), cm(1.5), cm(2.5)]) t.setStyle(TableStyle(style)) w, h = t.wrapOn(self.canvas, cm(10), cm(10)) t.drawOn(self.canvas, (self.canvas._pagesize[0] - w) // 2, cm(17) - h * 2 - cm(1) - extraofs) self.canvas.drawCentredString(cm(10.5), cm(16.3) - h * 2 - cm(2) - extraofs, "This refund was issued {0}".format(timezone.localtime(self.refunddate).strftime("%B %d, %Y"))) if self.paymentmethod: self.canvas.drawCentredString(cm(10.5), cm(16.3) - h * 2 - cm(3) - extraofs, "Refunded to the original form of payment: {0}.".format(self.paymentmethod)) self.canvas.showPage() self.canvas.save() return self.pdfdata
def draw_footer(self): if not self.receipt and self.paymentterms: fullheight = len(self.paymentterms.splitlines()) * self.fontheight('DejaVu Serif', 6) t = self.canvas.beginText() t.setTextOrigin(cm(2), cm(1) + fullheight) t.setFont("DejaVu Serif", 6) self.textlines(t, self.paymentterms) self.canvas.drawText(t) if self.bankinfo: fullheight = 2 * self.fontheight('DejaVu Serif Bold', 9) + (2 + len(self.bankinfo.splitlines())) * self.fontheight('DejaVu Serif', 7) t = self.canvas.beginText() t.setTextOrigin(cm(13), cm(1) + fullheight) t.setFont("DejaVu Serif Bold", 9) t.textLine("Payment reference") t.setFont("DejaVu Serif", 7) t.textLine(self.paymentref) t.textLine("") t.setFont("DejaVu Serif Bold", 9) t.textLine("Bank references") t.setFont("DejaVu Serif", 7) self.textlines(t, self.bankinfo) self.canvas.drawText(t) if self.paymentlink: style = ParagraphStyle('temp') style.fontName = 'DejaVu Serif' style.fontSize = 5 p = Paragraph('Payment details and instructions:<br/><nobr><a href="{0}">{0}</a></nobr>'.format(self.paymentlink), style) p.wrapOn(self.canvas, cm(12), cm(2)) p.drawOn(self.canvas, cm(2), cm(3.5)) try: import qrencode (ver, size, qrimage) = qrencode.encode(self.paymentlink) qrimage = qrimage.resize((size * 4, size * 4)) self.canvas.drawImage(ImageReader(qrimage), cm(2), cm(1.8), cm(1.5), cm(1.5)) except ImportError: # If we don't have the qrcode module, we just don't bother drawing the # QR code for the link pass if self.reverse_vat: t = self.canvas.beginText() t.setTextOrigin(cm(2), cm(4.8)) t.setFont("DejaVu Serif", 6) self.textlines(t, "* Services subject to the reverse charge - VAT to be accounted for by the recipient as per Article 196 of Council Directive 2006/112/EC") self.canvas.drawText(t)
def save(self): # We can fit ROWS_PER_PAGE rows on one page. We might want to do something # cute to avoid a single row on it's own page in the future, but # for now, just split it evenly. for pagenum in range(0, (len(self.rows) - 1) // self.ROWS_PER_PAGE + 1): self.draw_header() islastpage = (pagenum == (len(self.rows) - 1) // self.ROWS_PER_PAGE) if len(self.rows) > self.ROWS_PER_PAGE: suffix = " (page %s/%s)" % (pagenum + 1, len(self.rows) // self.ROWS_PER_PAGE + 1) else: suffix = '' self.canvas.setFont('DejaVu Serif Bold', 12) self.canvas.setFillColor(colors.black) # Center between 2 and 19 is 10.5 self.canvas.drawCentredString(cm(10.5), cm(19), self.title) self.canvas.setFont('DejaVu Serif', 9) if self.invoicenum: if self.receipt: self.canvas.drawCentredString(cm(10.5), cm(18.5), "Receipt for invoice number %s%s" % (self.invoicenum, suffix)) else: self.canvas.drawCentredString(cm(10.5), cm(18.5), "Invoice number %s - %s%s" % (self.invoicenum, timezone.localtime(self.invoicedate).strftime("%B %d, %Y"), suffix)) self.canvas.setFont('DejaVu Serif Bold', 10) if self.receipt: self.canvas.drawString(cm(15), cm(28), "Receipt #%s" % self.invoicenum) else: self.canvas.drawString(cm(15), cm(28), "Invoice #%s" % self.invoicenum) if self.bankinfo: self.canvas.setFont('DejaVu Serif Bold', 8) self.canvas.drawString(cm(15), cm(27.5), "Payment ref: %s" % self.paymentref) else: self.canvas.drawCentredString(cm(10.5), cm(18.5), "Receipt - %s%s" % (timezone.localtime(self.invoicedate).strftime("%B %d, %Y"), suffix)) if pagenum == 0: firstcol = "Item" else: firstcol = "Item - continued from page %s" % pagenum if settings.EU_VAT: tbldata = [[firstcol, "Quantity", "Ex VAT", "VAT", "Incl VAT"]] else: tbldata = [[firstcol, "Quantity", "Price", "Total"]] tblcols = len(tbldata[0]) if settings.EU_VAT: tbldata.extend([(self.trimstring(title, cm(9.5), "DejaVu Serif", 8), count, "%.2f %s" % (cost, settings.CURRENCY_SYMBOL), vatrate and vatrate.shortstr or "No VAT", "%.2f %s" % ((cost * count) * (1 + (vatpercent / Decimal(100))), settings.CURRENCY_SYMBOL)) for title, cost, count, vatrate, vatpercent in self.rows[pagenum * self.ROWS_PER_PAGE:(pagenum + 1) * self.ROWS_PER_PAGE]]) else: tbldata.extend([(self.trimstring(title, cm(9.5), "DejaVu Serif", 8), count, "%.2f %s" % (cost, settings.CURRENCY_SYMBOL), "%.2f %s" % ((cost * count), settings.CURRENCY_SYMBOL)) for title, cost, count, vatrate, vatpercent in self.rows[pagenum * self.ROWS_PER_PAGE:(pagenum + 1) * self.ROWS_PER_PAGE]]) style = [ # Set global font size ('FONTSIZE', (0, 0), (-1, -1), 8), # Right-align all columnsexcept the first one (item name) ('ALIGN', (1, 0), (tblcols - 1, -1), 'RIGHT'), # Draw header line background in light gray and line under it ('BACKGROUND', (0, 0), (tblcols - 1, 0), colors.lightgrey), ('LINEBELOW', (0, 0), (-1, 0), 2, colors.black), # Draw an outline around the whole table ('OUTLINE', (0, 0), (-1, -1), 1, colors.black), ] if islastpage: totalexcl = sum([cost * count for title, cost, count, vatrate, vatpercent in self.rows]) if settings.EU_VAT: # When EU vat enabled, calculate total fields both with and without VAT, # and special-case the reverse-VAT situation. totalvat = sum([(cost * count * (vatpercent / Decimal(100))).quantize(Decimal('0.01')) for title, cost, count, vatrate, vatpercent in self.rows]) totalincl = sum([(cost * count * (1 + vatpercent / Decimal(100))).quantize(Decimal('0.01')) for title, cost, count, vatrate, vatpercent in self.rows]) if self.totalvat > 0 and totalvat != self.totalvat: raise Exception("Specified total VAT {0} does not match calculated VAT {1}".format(self.totalvat, totalvat)) if self.reverse_vat: if totalvat != 0: raise Exception("Can't use reverse VAT and specified VAT at the same time!") vathdr = 'Total VAT *' vatstr = "0 %s *" % (settings.CURRENCY_SYMBOL, ) else: vathdr = 'Total VAT' vatstr = '%.2f %s' % (totalvat, settings.CURRENCY_SYMBOL) tbldata.extend([ ('Total excl VAT', '', '', '', '%.2f %s' % (totalexcl, settings.CURRENCY_SYMBOL)), (vathdr, '', '', '', vatstr), ('Total incl VAT', '', '', '', '%.2f %s' % (totalincl, settings.CURRENCY_SYMBOL)), ]) style.extend([ # For the tree "total excl", "cat", "total incl" lines, span the # cells together and alight right, and draw a line above them. ('SPAN', (0, -3), (3, -3)), ('SPAN', (0, -2), (3, -2)), ('SPAN', (0, -1), (3, -1)), ('ALIGN', (0, -3), (0, -1), 'RIGHT'), ('LINEABOVE', (-4, -3), (-1, -3), 2, colors.black), ]) else: # No EU vat, so just a simple total tbldata.extend([ ('Total', '', '', '%.2f %s' % (totalexcl, settings.CURRENCY_SYMBOL)), ]) # Merge the cells of the total line together, right-align them, and # draw a line above them. style.extend([ ('SPAN', (0, -1), (2, -1)), ('ALIGN', (0, -1), (0, -1), 'RIGHT'), ('LINEABOVE', (-3, -1), (-1, -1), 2, colors.black), ]) else: tbldata.append([' Continued on page %s' % (pagenum + 2), '', '', '']) style.append(('ALIGN', (0, -1), (-1, -1), 'CENTER')) style.append(('FONT', (0, -1), (-1, -1), 'DejaVu Serif Italic')) t = Table(tbldata, [cm(9.5), cm(1.5), cm(2.5), cm(2), cm(2.5)]) t.setStyle(TableStyle(style)) w, h = t.wrapOn(self.canvas, cm(10), cm(10)) t.drawOn(self.canvas, cm(2), cm(18) - h) self.canvas.setFont('DejaVu Serif Bold', 10) if self.receipt: self.canvas.drawCentredString(cm(10.5), cm(17.3) - h, "This invoice was paid %s" % timezone.localtime(self.duedate).strftime("%B %d, %Y")) else: self.canvas.drawCentredString(cm(10.5), cm(17.3) - h, "This invoice is due: %s" % timezone.localtime(self.duedate).strftime("%B %d, %Y")) if self.bankinfo: self.canvas.setFont('DejaVu Serif', 8) self.canvas.drawCentredString(cm(10.5), cm(16.8) - h, "If paying with bank transfer, use payment reference %s" % self.paymentref) if islastpage: self.draw_footer() # Finish this page off, and optionally loop to another one self.canvas.showPage() # Last page is finished, flush the PDF output self.canvas.save() return self.pdfdata
def build_linear_pdf_schedule(conference, room, tracks, day, colored, pagesize, orientation, titledatefmt): q = Q(conference=conference, status=1, starttime__isnull=False, endtime__isnull=False) q = q & (Q(room=room) | Q(cross_schedule=True)) q = q & (Q(track__in=tracks) | Q(track__isnull=True)) if day: q = q & Q(starttime__range=(day.day, day.day + timedelta(days=1))) sessions = ConferenceSession.objects.select_related( 'track', 'room').filter(q).order_by('starttime') (width, height, canvas, resp) = _setup_canvas(pagesize, orientation) # Fetch and modify styles st_time = getSampleStyleSheet()['Normal'] st_time.fontName = "DejaVu Serif" st_time.fontSize = 10 st_time.spaceAfter = 8 st_title = getSampleStyleSheet()['Normal'] st_title.fontName = "DejaVu Serif" st_title.fontSize = 10 st_title.spaceAfter = 8 st_speakers = getSampleStyleSheet()['Normal'] st_speakers.fontName = "DejaVu Serif" st_speakers.fontSize = 10 st_speakers.spaceAfter = 8 table_horiz_margin = cm(2) default_tbl_style = [ ('VALIGN', (0, 0), (-1, -1), 'TOP'), ('BOX', (0, 0), (-1, -1), 0.5, colors.black), ('INNERGRID', (0, 0), (-1, -1), 0.5, colors.black), ('BOTTOMPADDING', (0, 0), (-1, -1), 15), ] # Loop over days, creating one page for each day lastdate = None tbldata = [] tblstyle = copy.copy(default_tbl_style) def _finalize_page(): canvas.setFont("DejaVu Serif", 20) canvas.drawCentredString( width // 2, height - cm(2), "%s - %s" % (room.roomname, lastdate.strftime(titledatefmt))) t = Table(tbldata, colWidths=[cm(3), width - cm(3) - 2 * table_horiz_margin]) t.setStyle(TableStyle(tblstyle)) w, h = t.wrapOn(canvas, width, height) t.drawOn(canvas, table_horiz_margin, height - cm(4) - h) canvas.showPage() for s in sessions: if timezone.localdate(s.starttime) != lastdate: if lastdate is not None: # New page for a new day! _finalize_page() lastdate = timezone.localdate(s.starttime) tbldata = [] tblstyle = copy.copy(default_tbl_style) if colored and s.track and s.track.fgcolor: st_title.textColor = st_speakers.textColor = s.track.fgcolor else: st_title.textColor = st_speakers.textColor = colors.black tstr = Paragraph( "%s - %s" % (timezone.localtime(s.starttime).strftime("%H:%M"), timezone.localtime(s.endtime).strftime("%H:%M")), st_time) if s.cross_schedule: # Just add a blank row for cross schedule things, so we get the time on there tbldata.extend([(tstr, '')]) else: tbldata.extend([(tstr, (Paragraph(s.title, st_title), Paragraph("<i>%s</i>" % s.speaker_list, st_speakers)))]) if colored and s.track and s.track.color: tblstyle.append(('BACKGROUND', (1, len(tbldata) - 1), (1, len(tbldata) - 1), s.track.color), ) _finalize_page() canvas.save() return resp
def build_complete_pdf_schedule(conference, tracks, day, colored, pagesize, orientation, pagesperday, titledatefmt): pagesperday = int(pagesperday) q = Q(conference=conference, status=1, starttime__isnull=False, endtime__isnull=False) q = q & (Q(room__isnull=False) | Q(cross_schedule=True)) q = q & (Q(track__in=tracks) | Q(track__isnull=True)) if day: q = q & Q(starttime__range=(day.day, day.day + timedelta(days=1))) sessions = list( ConferenceSession.objects.select_related( 'track', 'room').filter(q).order_by('starttime', 'room__sortkey', 'room__roomname')) (width, height, canvas, resp) = _setup_canvas(pagesize, orientation) groupedbyday = defaultdict(dict) lastday = None for s in sessions: d = timezone.localdate(s.starttime) if lastday != d: # New day! groupedbyday[d] = { 'first': s.starttime, 'last': s.endtime, 'sessions': [] } lastday = d groupedbyday[d]['last'] = s.endtime groupedbyday[d]['sessions'].append(s) for k, v in list(groupedbyday.items()): v['length'] = v['last'] - v['first'] v['rooms'] = set([s.room for s in v['sessions'] if s.room]) timestampstyle = ParagraphStyle('timestampstyle') timestampstyle.fontName = "DejaVu Serif" timestampstyle.fontSize = 8 # Now build one page for each day for d in sorted(groupedbyday.keys()): dd = groupedbyday[d] usableheight = height - 2 * cm(2) - cm(1) usablewidth = width - 2 * cm(2) pagesessions = [] currentpagesessions = [] if pagesperday > 1: # >1 page per day, so we try to find the breakpoints. We do this by locating # cross-schedule sessions at appropriate times, and including those both on # the previous and the current schedule. secondsperpage = dd['length'].seconds // pagesperday cross_sessions = [s for s in dd['sessions'] if s.cross_schedule] breakpoints = [] # For each breakpoint, find the closest one for p in range(1, pagesperday): breaktime = dd['first'] + timedelta(seconds=p * secondsperpage) breaksession = cross_sessions[min( list(range(len(cross_sessions))), key=lambda i: abs(cross_sessions[i].starttime - breaktime ))] if breaksession not in breakpoints: breakpoints.append(breaksession) for s in dd['sessions']: currentpagesessions.append(s) if s in breakpoints: pagesessions.append(currentpagesessions) # Make sure the breaking sessions itself is on both pages currentpagesessions = [ s, ] pagesessions.append(currentpagesessions) else: # For a single page schedule, just add all sessions to the first page. pagesessions.append(dd['sessions']) # Calculate the vertical size once for all pages, to make sure we get the same size on # all pages even if the content is different. We do this by picking the *smallest* size # required for any page (start at usableheight just to be sure it will always get replaced) unitspersecond = usableheight for p in pagesessions: u = usableheight / (p[-1].endtime - p[0].starttime).seconds if u < unitspersecond: unitspersecond = u # Only on the first page in multipage schedules canvas.setFont("DejaVu Serif", 20) canvas.drawCentredString(width // 2, height - cm(2), d.strftime(titledatefmt)) roomcount = len(dd['rooms']) roomwidth = usablewidth // roomcount # Figure out font size for the room title. Use the biggest one that will still # fit within the boxes. roomtitlefontsize = 20 for r in dd['rooms']: for fs in 16, 14, 12, 10, 8: fwidth = canvas.stringWidth(r.roomname, "DejaVu Serif", fs) if fwidth < roomwidth - mm(4): # Width at this size is small enough to work, so use it if fs < roomtitlefontsize: roomtitlefontsize = fs break canvas.setFont("DejaVu Serif", roomtitlefontsize) roompos = {} for r in sorted(dd['rooms'], key=lambda x: (x.sortkey, x.roomname)): canvas.rect(cm(2) + len(roompos) * roomwidth, height - cm(4), roomwidth, cm(1), stroke=1) canvas.drawCentredString( cm(2) + len(roompos) * roomwidth + roomwidth // 2, height - cm(4) + (cm(1) - roomtitlefontsize) // 2, r.roomname) roompos[r] = len(roompos) for ps in pagesessions: pagelength = (ps[-1].endtime - ps[0].starttime).seconds first = ps[0].starttime canvas.rect(cm(2), height - pagelength * unitspersecond - cm(4), roomcount * roomwidth, pagelength * unitspersecond, stroke=1) for s in ps: if s.cross_schedule: # Cross schedule rooms are very special... s_left = cm(2) thisroomwidth = roomcount * roomwidth else: s_left = cm(2) + roompos[s.room] * roomwidth thisroomwidth = roomwidth s_height = (s.endtime - s.starttime).seconds * unitspersecond s_top = height - (s.starttime - first ).seconds * unitspersecond - s_height - cm(4) if colored: if s.track and s.track.color: canvas.setFillColor(s.track.color) else: canvas.setFillColor(colors.white) canvas.rect(s_left, s_top, thisroomwidth, s_height, stroke=1, fill=colored) timestampstr = "%s-%s" % ( timezone.localtime(s.starttime).strftime("%H:%M"), timezone.localtime(s.endtime).strftime("%H:%M")) if colored and s.track and s.track.fgcolor: timestampstyle.textColor = s.track.fgcolor ts = Paragraph(timestampstr, timestampstyle) (tsaw, tsah) = ts.wrap(thisroomwidth - mm(2), timestampstyle.fontSize) ts.drawOn(canvas, s_left + mm(1), s_top + s_height - tsah - mm(1)) if s_height - tsah * 1.2 - mm(2) < tsah: # This can never fit, since it's smaller than our font size! # Instead, print as much as possible on the same row as the time tswidth = canvas.stringWidth(timestampstr, "DejaVu Serif", 8) title = s.title trunc = '' while title: t = title + trunc fwidth = canvas.stringWidth(t, "DejaVu Serif", 8) if fwidth < thisroomwidth - tswidth - mm(2): # Fits now! canvas.setFont("DejaVu Serif", 8) p = Paragraph(t, timestampstyle) (paw, pah) = p.wrap(thisroomwidth - tswidth - mm(2), timestampstyle.fontSize) p.drawOn(canvas, s_left + mm(1) + tswidth + mm(1), s_top + s_height - tsah - mm(1)) break else: title = title.rpartition(' ')[0] trunc = '..' continue try: for includespeaker in (True, False): title = s.title while title: for fs in (12, 10, 9, 8): sessionstyle = ParagraphStyle('sessionstyle') sessionstyle.fontName = "DejaVu Serif" sessionstyle.fontSize = fs if colored and s.track and s.track.fgcolor: sessionstyle.textColor = s.track.fgcolor speakersize = fs > 8 and 8 or fs - 1 if includespeaker: p = Paragraph( title + "<br/><font size=%s>%s</font>" % (speakersize, s.speaker_list), sessionstyle) else: p = Paragraph(title, sessionstyle) (aw, ah) = p.wrap(thisroomwidth - mm(2), s_height - tsah * 1.2 - mm(2)) if ah <= s_height - tsah * 1.2 - mm(2): # FIT! p.drawOn( canvas, s_left + mm(1), s_top + s_height - ah - tsah * 1.2 - mm(1)) raise StopIteration # Too big, so try to chop down the title and run again # (this is assuming our titles are reasonable length, or we could be # looping for a *very* long time) title = "%s.." % title.rpartition(' ')[0] if title == '..': title = '' except StopIteration: pass canvas.showPage() canvas.save() return resp