示例#1
0
 def CreatePdfNum(self,s,num):
     x = 0
     salt = ''.join(random.sample(string.ascii_letters + string.digits, 16))
     #pdfmetrics.registerFont(TTFont('hei', 'hei.TTF'))
     #import testSubFun
     #testSubFun.testSubFunc('first')
     #设置页面大小
     c = canvas.Canvas('Result\\dlp%s.pdf' % num,pagesize=A4)
     xlength,ylength = A4
     #print('width:%d high:%d'%(xlength,ylength))
     #c.line(1,1,ylength/2,ylength)
     #设置文字类型及字号
     #c.setFont('hei',20)
     #生成一个table表格
     atable = [[1,2,3,4,5,6,7,8],[11,12,13,14,15,16,17,18]]
     t = Table(atable,50,20)
     t.setStyle(TableStyle([('ALIGN',(0,0),(3,1),'CENTER'),
                            ('INNERGRID',(0,0),(-1,-1),0.25,colors.black),
                            ('BOX',(0,0),(-1,-1),0.25,colors.black)]))
     textOb = c.beginText(1,ylength-10)
     indexVlaue = 0
     while(indexVlaue < ylength):
         textStr = u'''wo shi tupian---wo shi tupian--wo shi tupian--wo shi tupian%d'''%indexVlaue + salt
         #print('nextline,nextline%d'%indexVlaue)
         textOb.textLine(textStr)
         indexVlaue = indexVlaue + 1
         break
     c.drawText(textOb)
     
     #简单的图片载入
     imageValue = 'file\\dlp.png'
     c.drawImage(imageValue,97,97,650,650)
     #c.drawImage('file\\dlp.png',50,50,50,50)
     t.split(0,0)
     t.drawOn(c,100,1)
     c.showPage()
     #换页的方式不同的showPage
     while x < s:
         imageValue = 'file\\dlp.png'
         c.drawImage(imageValue,97,97,650,650)
         c.drawString(0,0,'tupian%s' % x)
         c.showPage()
         x = x + 1
     c.save()
示例#2
0
    def draw_course_info(self, y_pos):
        """
        Draws the main table containing the data items.
        """
        course_items_data = [[
            '', (_('Description')), (_('Quantity')),
            (_('List Price\nper item')), (_('Discount\nper item')),
            (_('Amount')), ''
        ]]
        for row_item in self.items_data:
            course_items_data.append([
                '',
                Paragraph(row_item['item_description'],
                          getSampleStyleSheet()['Normal']),
                row_item['quantity'], '{list_price:.2f} {currency}'.format(
                    list_price=row_item['list_price'], currency=self.currency),
                '{discount:.2f} {currency}'.format(
                    discount=row_item['discount'], currency=self.currency),
                '{item_total:.2f} {currency}'.format(
                    item_total=row_item['item_total'],
                    currency=self.currency), ''
            ])

        padding_width = 7 * mm
        desc_col_width = 60 * mm
        qty_col_width = 26 * mm
        list_price_col_width = 21 * mm
        discount_col_width = 21 * mm
        amount_col_width = 40 * mm
        course_items_table = Table(course_items_data, [
            padding_width, desc_col_width, qty_col_width, list_price_col_width,
            discount_col_width, amount_col_width, padding_width
        ],
                                   splitByRow=1,
                                   repeatRows=1)

        course_items_table.setStyle(
            TableStyle([
                #List Price, Discount, Amount data items
                ('ALIGN', (3, 1), (5, -1), 'RIGHT'),

                # Amount header
                ('ALIGN', (5, 0), (5, 0), 'RIGHT'),

                # Amount column (header + data items)
                ('RIGHTPADDING', (5, 0), (5, -1), 7 * mm),

                # Quantity, List Price, Discount header
                ('ALIGN', (2, 0), (4, 0), 'CENTER'),

                # Description header
                ('ALIGN', (1, 0), (1, -1), 'LEFT'),

                # Quantity data items
                ('ALIGN', (2, 1), (2, -1), 'CENTER'),

                # Lines below the header and at the end of the table.
                ('LINEBELOW', (0, 0), (-1, 0), 1.00, '#cccccc'),
                ('LINEBELOW', (0, -1), (-1, -1), 1.00, '#cccccc'),

                # Innergrid around the data rows.
                ('INNERGRID', (1, 1), (-2, -1), 0.50, '#cccccc'),

                # Entire table
                ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
                ('TOPPADDING', (0, 0), (-1, -1), 2 * mm),
                ('BOTTOMPADDING', (0, 0), (-1, -1), 2 * mm),
                ('TEXTCOLOR', (0, 0), (-1, -1), colors.black),
            ]))
        rendered_width, rendered_height = course_items_table.wrap(0, 0)
        table_left_padding = (self.page_width - rendered_width) / 2

        split_tables = course_items_table.split(
            0, self.first_page_available_height)
        if len(split_tables) > 1:
            # The entire Table won't fit in the available space and requires splitting.
            # Draw the part that can fit, start a new page
            # and repeat the process with the rest of the table.
            split_table = split_tables[0]
            __, rendered_height = split_table.wrap(0, 0)
            split_table.drawOn(self.pdf, table_left_padding,
                               y_pos - rendered_height)

            self.prepare_new_page()
            split_tables = split_tables[1].split(
                0, self.second_page_available_height)
            while len(split_tables) > 1:
                split_table = split_tables[0]
                __, rendered_height = split_table.wrap(0, 0)
                split_table.drawOn(
                    self.pdf, table_left_padding,
                    self.second_page_start_y_pos - rendered_height)

                self.prepare_new_page()
                split_tables = split_tables[1].split(
                    0, self.second_page_available_height)
            split_table = split_tables[0]
            __, rendered_height = split_table.wrap(0, 0)
            split_table.drawOn(self.pdf, table_left_padding,
                               self.second_page_start_y_pos - rendered_height)
        else:
            # Table will fit without the need for splitting.
            course_items_table.drawOn(self.pdf, table_left_padding,
                                      y_pos - rendered_height)

        if not self.is_on_first_page():
            y_pos = self.second_page_start_y_pos

        return y_pos - rendered_height - self.min_clearance
示例#3
0
class DelayedTable(Table):
    """A flowable that inserts a table for which it has the data.

    Needed so column widths can be determined after we know on what frame
    the table will be inserted, thus making the overal table width correct.

    """
    def __init__(self,
                 data,
                 colWidths,
                 style=None,
                 repeatrows=False,
                 splitByRow=True):
        self.data = data
        self._colWidths = colWidths
        if style is None:
            style = TableStyle([
                ('LEFTPADDING', (0, 0), (-1, -1), 0),
                ('RIGHTPADDING', (0, 0), (-1, -1), 0),
                ('TOPPADDING', (0, 0), (-1, -1), 0),
                ('BOTTOMPADDING', (0, 0), (-1, -1), 0),
            ])
        self.style = style
        self.t = None
        self.repeatrows = repeatrows
        self.hAlign = TA_CENTER
        self.splitByRow = splitByRow

    def wrap(self, w, h):
        # Create the table, with the widths from colWidths reinterpreted
        # if needed as percentages of frame/cell/whatever width w is.

        # _tw = w/sum(self.colWidths)
        def adjust(*args, **kwargs):
            kwargs['total'] = w
            return styles.adjustUnits(*args, **kwargs)

        # adjust=functools.partial(styles.adjustUnits, total=w)
        self.colWidths = [adjust(x) for x in self._colWidths]
        # colWidths = [_w * _tw for _w in self.colWidths]
        self.t = Table(
            self.data,
            colWidths=self.colWidths,
            style=self.style,
            repeatRows=self.repeatrows,
            splitByRow=True,
        )
        # splitByRow=self.splitByRow)
        self.t.hAlign = self.hAlign
        return self.t.wrap(w, h)

    def split(self, w, h):
        if self.splitByRow:
            if not self.t:
                self.wrap(w, h)
            return self.t.split(w, h)
        else:
            return []

    def drawOn(self, canvas, x, y, _sW=0):
        self.t.drawOn(canvas, x, y, _sW)

    def identity(self, maxLen=None):
        return "<%s at %s%s%s> containing: %s" % (
            self.__class__.__name__,
            hex(id(self)),
            self._frameName(),
            getattr(self, 'name', '') and
            (' name="%s"' % getattr(self, 'name', '')) or '',
            repr(self.data[0]),
        )[:180]
示例#4
0
    def draw_course_info(self, y_pos):
        """
        Draws the main table containing the data items.
        """
        course_items_data = [
            ['', (_('Description')), (_('Quantity')), (_('List Price\nper item')), (_('Discount\nper item')),
             (_('Amount')), '']
        ]
        for row_item in self.items_data:
            course_items_data.append([
                '',
                Paragraph(row_item['item_description'], getSampleStyleSheet()['Normal']),
                row_item['quantity'],
                '{currency}{list_price:.2f}'.format(list_price=row_item['list_price'], currency=self.currency),
                '{currency}{discount:.2f}'.format(discount=row_item['discount'], currency=self.currency),
                '{currency}{item_total:.2f}'.format(item_total=row_item['item_total'], currency=self.currency),
                ''
            ])

        padding_width = 7 * mm
        desc_col_width = 60 * mm
        qty_col_width = 26 * mm
        list_price_col_width = 21 * mm
        discount_col_width = 21 * mm
        amount_col_width = 40 * mm
        course_items_table = Table(
            course_items_data,
            [
                padding_width,
                desc_col_width,
                qty_col_width,
                list_price_col_width,
                discount_col_width,
                amount_col_width,
                padding_width
            ],
            splitByRow=1,
            repeatRows=1
        )

        course_items_table.setStyle(TableStyle([
            #List Price, Discount, Amount data items
            ('ALIGN', (3, 1), (5, -1), 'RIGHT'),

            # Amount header
            ('ALIGN', (5, 0), (5, 0), 'RIGHT'),

            # Amount column (header + data items)
            ('RIGHTPADDING', (5, 0), (5, -1), 7 * mm),

            # Quantity, List Price, Discount header
            ('ALIGN', (2, 0), (4, 0), 'CENTER'),

            # Description header
            ('ALIGN', (1, 0), (1, -1), 'LEFT'),

            # Quantity data items
            ('ALIGN', (2, 1), (2, -1), 'CENTER'),

            # Lines below the header and at the end of the table.
            ('LINEBELOW', (0, 0), (-1, 0), 1.00, '#cccccc'),
            ('LINEBELOW', (0, -1), (-1, -1), 1.00, '#cccccc'),

            # Innergrid around the data rows.
            ('INNERGRID', (1, 1), (-2, -1), 0.50, '#cccccc'),

            # Entire table
            ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
            ('TOPPADDING', (0, 0), (-1, -1), 2 * mm),
            ('BOTTOMPADDING', (0, 0), (-1, -1), 2 * mm),
            ('TEXTCOLOR', (0, 0), (-1, -1), colors.black),
        ]))
        rendered_width, rendered_height = course_items_table.wrap(0, 0)
        table_left_padding = (self.page_width - rendered_width) / 2

        split_tables = course_items_table.split(0, self.first_page_available_height)
        if len(split_tables) > 1:
            # The entire Table won't fit in the available space and requires splitting.
            # Draw the part that can fit, start a new page
            # and repeat the process with the rest of the table.
            split_table = split_tables[0]
            __, rendered_height = split_table.wrap(0, 0)
            split_table.drawOn(self.pdf, table_left_padding, y_pos - rendered_height)

            self.prepare_new_page()
            split_tables = split_tables[1].split(0, self.second_page_available_height)
            while len(split_tables) > 1:
                split_table = split_tables[0]
                __, rendered_height = split_table.wrap(0, 0)
                split_table.drawOn(self.pdf, table_left_padding, self.second_page_start_y_pos - rendered_height)

                self.prepare_new_page()
                split_tables = split_tables[1].split(0, self.second_page_available_height)
            split_table = split_tables[0]
            __, rendered_height = split_table.wrap(0, 0)
            split_table.drawOn(self.pdf, table_left_padding, self.second_page_start_y_pos - rendered_height)
        else:
            # Table will fit without the need for splitting.
            course_items_table.drawOn(self.pdf, table_left_padding, y_pos - rendered_height)

        if not self.is_on_first_page():
            y_pos = self.second_page_start_y_pos

        return y_pos - rendered_height - self.min_clearance
示例#5
0
print('width:%d high:%d' % (xlength, ylength))
#c.line(1,1,ylength/2,ylength)
#设置文字类型及字号
c.setFont('hei', 20)
#生成一个table表格
atable = [[1, 2, 3, 4], [5, 6, 7, 8]]
t = Table(atable, 50, 20)
t.setStyle(
    TableStyle([('ALIGN', (0, 0), (3, 1), 'CENTER'),
                ('INNERGRID', (0, 0), (-1, -1), 0.25, colors.black),
                ('BOX', (0, 0), (-1, -1), 0.25, colors.black)]))
textOb = c.beginText(1, ylength - 10)
indexVlaue = 0
while (indexVlaue < ylength):
    textStr = '''test 中文写入测试中文写入测试中文写入测试中文写入测试%d''' % indexVlaue
    #print('nextline,nextline%d'%indexVlaue)
    textOb.textLine(textStr)
    indexVlaue = indexVlaue + 1
    break
c.drawText(textOb)
#简单的图片载入
imageValue = 'test.png'
c.drawImage(imageValue, 97, 97, 300, 300)
c.drawImage('test.png', 50, 50, 50, 50)
t.split(0, 0)
t.drawOn(c, 100, 1)
c.showPage()
#换页的方式不同的showPage
c.drawString(0, 0, 'helloword')
c.showPage()
示例#6
0
def pdf(group, username, list, show_header=True, show_footer=True):
    """PDF version of list"""

    content = BytesIO()

    columns = list.column_set.all()
    accounts = list.accounts()

    margin = 0.5 * cm

    font_name = "Times-Roman"
    font_name_bold = "Times-Bold"
    font_size = 12
    font_size_small = 10
    font_size_min = 8

    head_height = 30  # pt
    foot_height = 15  # pt
    logo_height = 25  # pt

    font_size_name = font_size_small
    font_size_short_name = font_size_small
    font_size_balance = font_size_small

    if list.orientation == list.LANDSCAPE:
        height, width = A4
    else:
        width, height = A4

    grid_style = TableStyle(parent=GRID_STYLE)

    # Create canvas for page and set fonts
    p = canvas.Canvas(content, (width, height))

    show_logo = bool(group.logo and group.logo.storage.exists(group.logo.path))

    if show_logo:
        # Find scaling ratio
        ratio = group.logo.width / group.logo.height

        # Load logo with correct scaling
        logo = Image(
            group.logo.path, width=logo_height * ratio, height=logo_height
        )

    def draw_header():
        if not show_header:
            return

        if show_logo:
            logo.drawOn(
                p,
                width - margin - logo_height * ratio,
                height - margin - logo_height,
            )

        # Setup rest of header
        p.setFont(font_name, font_size)
        p.drawString(
            margin, height - margin - font_size, f"{group}: {list.name}"
        )
        p.setFont(font_name, font_size_small)
        p.drawString(
            margin,
            height - margin - font_size - font_size + 2,
            "%s: %s %s %s"
            % (_("Printed"), str(date.today()), _("by"), username),
        )

    footer = []
    if group.email:
        footer.append(group.email)
    if group.account_number:
        footer.append(group.get_account_number_display())
    if list.comment.strip():
        footer.append(list.comment.replace("\n", " ").replace("\r", " "))

    def draw_footer():
        if not show_footer:
            return

        p.drawString(margin, margin, " - ".join(footer))

        blacklisted_note = _("Blacklisted accounts are marked with: ")

        p.drawRightString(width - margin - 10, margin, blacklisted_note)
        p.setFillColor(BLACKLISTED_COLOR)
        p.rect(width - margin - 10, margin, 8, 8, fill=1, stroke=0)

        p.setFont(font_name, font_size)

    if not accounts:
        no_accounts_message = _("Sorry, this list is empty.")
        draw_header()
        p.drawString(
            margin,
            height - font_size - margin - head_height,
            no_accounts_message,
        )
        draw_footer()
        p.save()

        return content

    elif not columns:
        no_columns_message = _(
            "Sorry, this list isn't set up correctly, "
            "please add some columns."
        )
        draw_header()
        p.drawString(
            margin,
            height - font_size - margin - head_height,
            no_columns_message,
        )
        draw_footer()
        p.save()

        return content

    # Store col widths
    col_width = []
    header = [_("Name")]

    if list.account_width:
        col_width.append(list.account_width)

    if list.short_name_width:
        col_width.append(list.short_name_width)

    if list.account_width and list.short_name_width:
        header.append("")

    if list.balance_width:
        header.append(_("Balance"))
        col_width.append(list.balance_width)

    if list.short_name_width > 0 and list.account_width > 0:
        grid_style.add("SPAN", (0, 0), (1, 0))

    base_x = len(header)

    for c in columns:
        header.append(c.name)
        col_width.append(c.width)

    # Calculate relative col widths over to absolute points
    for i, w in enumerate(col_width):
        col_width[i] = (
            float(w)
            / float(
                (list.listcolumn_width or 0)
                + list.balance_width
                + (list.account_width or 0)
                + (list.short_name_width or 0)
            )
            * (width - 2 * margin)
        )

    # Intialise table with header
    data = [header]

    for i, a in enumerate(accounts):
        color = ALTERNATE_COLORS[(i + 1) % len(ALTERNATE_COLORS)]

        if list.double:
            i *= 2
            extra_row_height = 1
        else:
            extra_row_height = 0

        i += 1

        grid_style.add("BACKGROUND", (0, i), (-1, i + extra_row_height), color)

        row = []

        if list.account_width:
            row.append(a.name)

            # Check if we need to reduce col font size
            while (
                col_width[len(row) - 1]
                < p.stringWidth(row[-1], font_name, font_size_name) + 12
                and font_size_name > font_size_min
            ):
                font_size_name -= 1

        if list.short_name_width:
            short_name = a.short_name

            if not short_name and a.owner:
                short_name = a.owner.username

            row.append(short_name or a.name)

            # Check if we need to reduce col font size
            while (
                col_width[len(row) - 1]
                < p.stringWidth(row[-1], font_name, font_size_name) + 12
                and font_size_short_name > font_size_min
            ):
                font_size_short_name -= 1

        if list.balance_width:
            row.append("%d" % a.normal_balance())

            # XXX: currently warnings are only shown if balance is shown, this
            # if needs to be moved if you want to change that
            if a.needs_warning():
                grid_style.add(
                    "FONTNAME", (0, i), (base_x - 1, i), font_name_bold
                )
                grid_style.add(
                    "TEXTCOLOR",
                    (base_x - 1, i),
                    (base_x - 1, i),
                    WARN_TEXT_COLOR,
                )

            # Check if we need to reduce col font size
            while (
                col_width[len(row) - 1]
                < p.stringWidth(str(row[-1]), font_name, font_size_balance) + 12
                and font_size_balance > font_size_min
            ):
                font_size_balance -= 1

        if a.is_blocked():
            if list.balance_width:
                grid_style.add(
                    "TEXTCOLOR",
                    (base_x - 1, i),
                    (base_x - 1, i),
                    BLACKLISTED_TEXT_COLOR,
                )
                grid_style.add(
                    "FONTNAME", (0, i), (base_x - 1, i), font_name_bold
                )
            grid_style.add(
                "BACKGROUND",
                (base_x, i),
                (-1, i + extra_row_height),
                BLACKLISTED_COLOR,
            )

            row.extend([""] * len(header[base_x:]))

        else:
            row.extend(header[base_x:])

        data.append(row)

        if list.double:
            data.append([""] * len(row))

            grid_style.add("SPAN", (0, i), (0, i + extra_row_height))

            if list.balance_width:
                grid_style.add("SPAN", (1, i), (1, i + extra_row_height))

    grid_style.add("FONTSIZE", (0, 0), (-1, -1), font_size_small)

    # Set font size for names
    grid_style.add("FONTSIZE", (0, 1), (0, -1), font_size_name)
    grid_style.add("ALIGN", (0, 0), (-1, -1), "LEFT")
    grid_style.add("ALIGN", (base_x, 0), (-1, -1), "RIGHT")

    grid_style.add("FONTNAME", (0, 0), (-1, 0), font_name_bold)

    # Set font size for balance
    if list.balance_width:
        grid_style.add(
            "FONTSIZE", (base_x - 1, 1), (base_x - 1, -1), font_size_balance
        )
        grid_style.add("ALIGN", (base_x - 1, 1), (base_x - 1, -1), "RIGHT")

    grid_style.add("TEXTCOLOR", (base_x, 1), (-1, -1), FAINT_COLOR)

    if list.double:
        grid_style.add("TOPPADDING", (base_x, 1), (-1, -1), 2)
        grid_style.add("BOTTOMPADDING", (base_x, 1), (-1, -1), 2)

    grid_style.add("VALIGN", (0, 1), (-1, -1), "TOP")
    grid_style.add("GRID", (0, 0), (-1, -1), 0.25, BORDER_COLOR)

    # Create table
    t = Table(data, colWidths=col_width, style=grid_style, repeatRows=1)

    rest = None
    avail_w = width - 2 * margin
    avail_h = height - 2 * margin - head_height - foot_height

    while t:
        # Figure out how big table will be
        t_width, t_height = t.wrapOn(p, avail_w, avail_h)

        if not rest and t_height > height - 2 * margin - head_height:
            t, rest = t.split(avail_w, avail_h)
            continue

        # Draw on canvas
        draw_header()
        t.drawOn(p, margin, height - t_height - margin - head_height)
        draw_footer()

        if rest:
            # set t to the second table and reset rest
            t, rest = (rest, None)

            # Show new page
            p.showPage()
        else:
            # Leave loop
            break

    p.save()

    return content
示例#7
0
def generate_pdf(pdfname, InvoiceNo, create_date, carrier, address, totalfee,
                 tc, payment):
    #设置页面大小
    c = canvas.Canvas(pdfname + '.pdf', pagesize=A4)
    xlength, ylength = A4
    print('width:%d high:%d' % (xlength, ylength))
    #c.line(1,1,ylength/2,ylength)

    #设置文字类型及字号
    c.setFont('Helvetica', 12)

    #简单的图片载入
    imageValue = 'test.jpg'
    #c.drawImage(imageValue,97,97,300,300)
    c.drawImage('test.jpg', 50, 750, 200, 50)

    #text gengerating
    def set_text(x, y, text1):
        indexVlaue = 0
        set_text = c.beginText(x, y)
        while (indexVlaue < ylength):
            textStr = text1
            #print('nextline,nextline%d'%indexVlaue)
            set_text.textLine(textStr)
            indexVlaue = indexVlaue + 1
            break
        c.drawText(set_text)

    #tc = [['ELD Subscription & Data Plan','12/01/2018-12/01/2019 (12 month)','3','$1197','0']]
    atable = tc

    set_text(50, ylength - 110, "Invoice#: " + InvoiceNo)
    set_text(400, ylength - 110, "Created: " + create_date)
    set_text(50, ylength - 150, "Supplier: United Bus Technology Inc.")
    set_text(50, ylength - 165,
             "Address: 7926 Jones Branch Dr. Suite 630, McLean, VA 22102")
    set_text(50, ylength - 190, "Motor Carrier:" + carrier)
    set_text(50, ylength - 205, "Address:" + address)

    set_text(400, 280, "Total Fee: $" + totalfee)
    set_text(50, 250, "Thanks for purchase!")
    set_text(50, 235,
             "For any further questions do not hesitate to contact us!")
    set_text(50, 200, "Website: https://www.ubtshield.com/")
    set_text(50, 185, "Address: 7926 Jones Branch Dr., Suite 630, VA 22102")
    set_text(50, 170, "Phone: +1 202 800 6565")
    set_text(50, 155, "Email: [email protected]")
    if payment == "Paid":
        c.drawImage('paid.jpg', 400, 200, 120, 120)
    elif payment == "Not Paid":
        c.drawImage('notpaid.jpg', 400, 200, 120, 120)

    t = Table(atable, colWidths=[150, 170, 40, 60, 60])
    t.setStyle(
        TableStyle([
            ('ALIGN', (0, 0), (4, len(atable) - 1), 'CENTER'),
            ('INNERGRID', (0, 0), (-1, -1), 0.25, colors.black),
            ('BOX', (0, 0), (-1, -1), 0.25, colors.black),
        ]))
    t.split(0, 0)
    t.drawOn(c, 50, ylength - 400, 1)
    c.showPage()
    #换页的方式不同的showPage  c.drawString(0,0,'helloword')
    c.save()
示例#8
0
文件: pdf.py 项目: itkinside/ufs
def pdf(group, list, show_header=True, show_footer=True):
    """PDF version of list"""

    content = StringIO()

    columns = list.column_set.all()
    accounts = list.accounts()

    margin = 0.5*cm

    font_name = 'Times-Roman'
    font_name_bold = 'Times-Bold'
    font_size = 12
    font_size_small = 10
    font_size_min = 8

    head_height = 30  # pt
    foot_height = 15  # pt
    logo_height = 25  # pt

    font_size_name = font_size_small
    font_size_short_name = font_size_small
    font_size_balance = font_size_small

    if list.orientation == list.LANDSCAPE:
        height, width = A4
    else:
        width, height = A4

    # Create canvas for page and set fonts
    p = canvas.Canvas(content, (width, height))

    show_logo = bool(group.logo and group.logo.storage.exists(group.logo.path))

    if show_logo:
        # Find scaling ratio
        ratio = group.logo.width / group.logo.height

        # Load logo with correct scaling
        logo = Image(
            group.logo.path, width=logo_height*ratio, height=logo_height)

    def draw_header():
        if not show_header:
            return

        if show_logo:
            logo.drawOn(
                p,
                width - margin - logo_height*ratio,
                height - margin - logo_height)

        # Setup rest of header
        p.setFont(font_name, font_size)
        p.drawString(
            margin, height - margin - font_size,
            u'%s: %s' % (group, list.name))
        p.setFont(font_name, font_size_small)
        p.drawString(
            margin, height - margin - font_size - font_size + 2,
            u'%s: %s' % (_('Printed'), str(date.today())))

    footer = []
    if group.email:
        footer.append(group.email)
    if group.account_number:
        footer.append(group.get_account_number_display())
    if list.comment.strip():
        footer.append(list.comment.replace('\n', ' ').replace('\r', ' '))

    def draw_footer():
        if not show_footer:
            return

        p.drawString(margin, margin, u' - '.join(footer))

        blacklisted_note = _(u'Blacklisted accounts are marked with: ')

        p.drawRightString(width - margin - 10, margin, blacklisted_note)
        p.setFillColor(BLACKLISTED_COLOR)
        p.rect(width - margin - 10, margin, 8, 8, fill=1, stroke=0)

        p.setFont(font_name, font_size)

    if not accounts:
        no_accounts_message = _(u"Sorry, this list is empty.")
        draw_header()
        p.drawString(
            margin, height - font_size - margin - head_height,
            no_accounts_message)
        draw_footer()
        p.save()

        return content

    elif not columns:
        no_columns_message = _(
            u"Sorry, this list isn't set up correctly, "
            u"please add some columns.")
        draw_header()
        p.drawString(
            margin, height - font_size - margin - head_height,
            no_columns_message)
        draw_footer()
        p.save()

        return content

    # Store col widths
    col_width = []
    header = [_(u'Name')]

    if list.account_width:
        col_width.append(list.account_width)

    if list.short_name_width:
        col_width.append(list.short_name_width)

    if list.account_width and list.short_name_width:
        header.append('')

    if list.balance_width:
        header.append(_(u'Balance'))
        col_width.append(list.balance_width)

    if list.short_name_width > 0 and list.account_width > 0:
        GRID_STYLE.add('SPAN', (0, 0), (1, 0))

    base_x = len(header)

    for c in columns:
        header.append(c.name)
        col_width.append(c.width)

    # Calculate relative col widths over to absolute points
    for i, w in enumerate(col_width):
        col_width[i] = float(w) / float(
            (list.listcolumn_width or 0) + list.balance_width +
            (list.account_width or 0) + (list.short_name_width or 0)
        ) * (width - 2 * margin)

    # Intialise table with header
    data = [header]

    for i, a in enumerate(accounts):
        color = ALTERNATE_COLORS[(i+1) % len(ALTERNATE_COLORS)]

        if list.double:
            i *= 2
            extra_row_height = 1
        else:
            extra_row_height = 0

        i += 1

        GRID_STYLE.add('BACKGROUND', (0, i), (-1, i+extra_row_height), color)

        row = []

        if list.account_width:
            row.append(a.name)

            # Check if we need to reduce col font size
            while (
                    col_width[len(row)-1] <
                    p.stringWidth(row[-1], font_name, font_size_name) + 12
                    and font_size_name > font_size_min):
                font_size_name -= 1

        if list.short_name_width:
            short_name = a.short_name

            if not short_name and a.owner:
                short_name = a.owner.username

            row.append(short_name or a.name)

            # Check if we need to reduce col font size
            while (
                    col_width[len(row)-1] <
                    p.stringWidth(row[-1], font_name, font_size_name) + 12
                    and font_size_short_name > font_size_min):
                font_size_short_name -= 1

        if list.balance_width:
            row.append('%d' % a.normal_balance())

            # XXX: currently warnings are only shown if balance is shown, this
            # if needs to be moved if you want to change that
            if a.needs_warning():
                GRID_STYLE.add(
                    'FONTNAME', (0, i), (base_x - 1, i), font_name_bold)
                GRID_STYLE.add(
                    'TEXTCOLOR', (base_x - 1, i), (base_x - 1, i),
                    WARN_TEXT_COLOR)

            # Check if we need to reduce col font size
            while (
                    col_width[len(row)-1] <
                    p.stringWidth(str(row[-1]), font_name, font_size_balance) +
                    12
                    and font_size_balance > font_size_min):
                font_size_balance -= 1

        if a.is_blocked():
            if list.balance_width:
                GRID_STYLE.add(
                    'TEXTCOLOR', (base_x - 1, i), (base_x - 1, i),
                    BLACKLISTED_TEXT_COLOR)
                GRID_STYLE.add(
                    'FONTNAME', (0, i), (base_x - 1, i), font_name_bold)
            GRID_STYLE.add(
                'BACKGROUND', (base_x, i), (-1, i + extra_row_height),
                BLACKLISTED_COLOR)

            row.extend([''] * len(header[base_x:]))

        else:
            row.extend(header[base_x:])

        data.append(row)

        if list.double:
            data.append([''] * len(row))

            GRID_STYLE.add('SPAN', (0, i), (0, i + extra_row_height))

            if list.balance_width:
                GRID_STYLE.add('SPAN', (1, i), (1, i + extra_row_height))

    GRID_STYLE.add('FONTSIZE', (0, 0), (-1, -1), font_size_small)

    # Set font size for names
    GRID_STYLE.add('FONTSIZE', (0, 1), (0, -1), font_size_name)
    GRID_STYLE.add('ALIGN', (0, 0), (-1, -1), 'LEFT')
    GRID_STYLE.add('ALIGN', (base_x, 0), (-1, -1), 'RIGHT')

    GRID_STYLE.add('FONTNAME', (0, 0), (-1, 0), font_name_bold)

    # Set font size for balance
    if list.balance_width:
        GRID_STYLE.add(
            'FONTSIZE', (base_x - 1, 1), (base_x - 1, -1), font_size_balance)
        GRID_STYLE.add('ALIGN', (base_x - 1, 1), (base_x - 1, -1), 'RIGHT')

    GRID_STYLE.add('TEXTCOLOR', (base_x, 1), (-1, -1), FAINT_COLOR)

    if list.double:
        GRID_STYLE.add('TOPPADDING', (base_x, 1), (-1, -1), 2)
        GRID_STYLE.add('BOTTOMPADDING', (base_x, 1), (-1, -1), 2)

    GRID_STYLE.add('VALIGN', (0, 1), (-1, -1), 'TOP')
    GRID_STYLE.add('GRID', (0, 0), (-1, -1), 0.25, BORDER_COLOR)

    # Create table
    t = Table(data, colWidths=col_width, style=GRID_STYLE, repeatRows=1)

    rest = None
    avail_w = width-2*margin
    avail_h = height - 2*margin - head_height - foot_height

    while t:
        # Figure out how big table will be
        t_width, t_height = t.wrapOn(p, avail_w, avail_h)

        if not rest and t_height > height - 2*margin - head_height:
            t, rest = t.split(avail_w, avail_h)
            continue

        # Draw on canvas
        draw_header()
        t.drawOn(p, margin, height - t_height - margin - head_height)
        draw_footer()

        if rest:
            # set t to the second table and reset rest
            t, rest = (rest, None)

            # Show new page
            p.showPage()
        else:
            # Leave loop
            break

    p.save()

    return content
示例#9
0
class DelayedTable(Table):
    """A flowable that inserts a table for which it has the data.

    Needed so column widths can be determined after we know on what frame
    the table will be inserted, thus making the overal table width correct.

    """

    def __init__(self, data, colWidths, style=None, repeatrows=False, splitByRow=True):
        self.data = data
        self._colWidths = colWidths
        if style is None:
            style = TableStyle(
                [
                    ('LEFTPADDING', (0, 0), (-1, -1), 0),
                    ('RIGHTPADDING', (0, 0), (-1, -1), 0),
                    ('TOPPADDING', (0, 0), (-1, -1), 0),
                    ('BOTTOMPADDING', (0, 0), (-1, -1), 0),
                ]
            )
        self.style = style
        self.t = None
        self.repeatrows = repeatrows
        self.hAlign = TA_CENTER
        self.splitByRow = splitByRow

    def wrap(self, w, h):
        # Create the table, with the widths from colWidths reinterpreted
        # if needed as percentages of frame/cell/whatever width w is.

        # _tw = w/sum(self.colWidths)
        def adjust(*args, **kwargs):
            kwargs['total'] = w
            return styles.adjustUnits(*args, **kwargs)

        # adjust=functools.partial(styles.adjustUnits, total=w)
        self.colWidths = [adjust(x) for x in self._colWidths]
        # colWidths = [_w * _tw for _w in self.colWidths]
        self.t = Table(
            self.data,
            colWidths=self.colWidths,
            style=self.style,
            repeatRows=self.repeatrows,
            splitByRow=True,
        )

        self._set_max_page_height_on_cell_flowables(h)

        # splitByRow=self.splitByRow)
        self.t.hAlign = self.hAlign
        return self.t.wrap(w, h)

    def _set_max_page_height_on_cell_flowables(self, height):
        """Iterate over all cells in the table and set the maximum height onto the flowable.

        This is useful to the flowable if its data cannot be rendered over a page boundary (e.g. an image) as it can
        then resize itself to a valid height in its wrap() method.
        """

        def set_max_page_height_on_flowable(flowable, cell_style):
            if hasattr(flowable, 'max_page_height'):
                # Subtract padding from h only if it is a positive value
                top_padding = cell_style.topPadding if cell_style.topPadding > 0 else 0
                bottom_padding = (
                    cell_style.topPadding if cell_style.bottomPadding > 0 else 0
                )

                h = abs(height - top_padding - bottom_padding) - _FUZZ
                flowable.max_page_height = h

        for row_index, row in enumerate(self.data):
            for col_index, columns in enumerate(row):
                # columns is either a list or a cell (for a single cell table)
                if isinstance(columns, list):
                    for cell in columns:
                        set_max_page_height_on_flowable(
                            cell, self.t._cellStyles[row_index][col_index]
                        )
                else:
                    set_max_page_height_on_flowable(
                        columns, self.t._cellStyles[row_index][col_index]
                    )

    def split(self, w, h):
        if self.splitByRow:
            if not self.t:
                self.wrap(w, h)
            return self.t.split(w, h)
        else:
            return []

    def drawOn(self, canvas, x, y, _sW=0):
        self.t.drawOn(canvas, x, y, _sW)

    def identity(self, maxLen=None):
        return "<%s at %s%s%s> containing: %s" % (
            self.__class__.__name__,
            hex(id(self)),
            self._frameName(),
            getattr(self, 'name', '')
            and (' name="%s"' % getattr(self, 'name', ''))
            or '',
            repr(self.data[0]),
        )[:180]