예제 #1
0
    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()
예제 #2
0
    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)
예제 #3
0
    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
예제 #4
0
    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)
예제 #5
0
    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
예제 #6
0
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
예제 #7
0
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