3

I am trying to create a pdf document that includes a table. For each row of the table, a transaction will be recorded, and I would like the 4th column to include a paperclip image (or emoji) with a hyperlink to a document stored online.

I would also like the 6th column to include tag names with a specific background color associated with each tag name, ideally with the background as a rounded rectangle.

So far I am not able to put different backgrounds for a single cell. I have thought about creating one row per tag, and merge the cells for all the other columns, but I am not sure it would be the best option as sometimes several tags could fit in one line.

So far I have:

from datetime import datetime
from decimal import Decimal
from pathlib import Path
from borb.pdf.canvas.font.simple_font.true_type_font import TrueTypeFont
from borb.pdf.canvas.color.color import HexColor
from borb.pdf.canvas.layout.layout_element import Alignment
from borb.pdf.canvas.layout.table.fixed_column_width_table import FixedColumnWidthTable as Table
from borb.pdf.canvas.layout.table.table import TableCell
from borb.pdf.canvas.layout.text.paragraph import Paragraph
from borb.pdf.canvas.layout.annotation.remote_go_to_annotation import RemoteGoToAnnotation
from borb.pdf.canvas.geometry.rectangle import Rectangle
from borb.pdf.canvas.layout.emoji.emoji import Emojis

rubik = TrueTypeFont.true_type_font_from_file(Path(__file__).parent / "fonts/Rubik-Regular.ttf")
rubik_medium = TrueTypeFont.true_type_font_from_file(Path(__file__).parent / "fonts/Rubik-Medium.ttf")
background_color = HexColor("#FF5733")

def generate_table(transactions):
    table = Table(number_of_rows=1 + len(transactions), number_of_columns=6, horizontal_alignment=Alignment.JUSTIFIED, column_widths=[Decimal(60), Decimal(60), Decimal(220), Decimal(30), Decimal(50), Decimal(50),Decimal(80)])
    table.add(TableCell(Paragraph("Date", font=rubik_medium, font_size=Decimal(11), horizontal_alignment=Alignment.CENTERED)))
    table.add(TableCell(Paragraph("Account", font=rubik_medium, font_size=Decimal(11), horizontal_alignment=Alignment.CENTERED)))
    table.add(TableCell(Paragraph("Description", font=rubik_medium, font_size=Decimal(11), horizontal_alignment=Alignment.CENTERED)))
    table.add(TableCell(Paragraph("Attachment", font=rubik_medium, font_size=Decimal(11), horizontal_alignment=Alignment.CENTERED)))
    table.add(TableCell(Paragraph("Value", font=rubik_medium, font_size=Decimal(11), horizontal_alignment=Alignment.CENTERED)))
    table.add(TableCell(Paragraph("Tags", font=rubik_medium, font_size=Decimal(11), horizontal_alignment=Alignment.CENTERED)))
    for transaction in transactions:
        # Column 1
        table.add(TableCell(Paragraph(datetime.strftime(transaction[0], '%d/%m/%y'), font=rubik, font_size=Decimal(8))))
        # Column 2
        table.add(TableCell(Paragraph(transaction[1], font=rubik, font_size=Decimal(8), horizontal_alignment=Alignment.CENTERED)))
        # Column 3
        table.add(TableCell(Paragraph(transaction[2], font=rubik, font_size=Decimal(8), horizontal_alignment=Alignment.CENTERED)))
        # Basically I would need to somehow merge the following lines as my column 4
        table.add(TableCell(RemoteGoToAnnotation(Rectangle(Decimal(32), Decimal(32), Decimal(32), Decimal(32)), uri=transaction[3])))
        table.add(TableCell(Emojis.PAPERCLIP.value))
        # Column 5
        table.add(TableCell(Paragraph(f"{transaction[4]:.2f}", font=rubik, font_size=Decimal(8), horizontal_alignment=Alignment.CENTERED)))
        # Column 6
        table.add(TableCell(Paragraph("\n".join(transaction[5]), font=rubik, font_size=Decimal(8), border_radius_top_left=Decimal(10), border_radius_top_right=Decimal(10), border_radius_bottom_left=Decimal(10), border_radius_bottom_right=Decimal(10), background_color=background_color, horizontal_alignment=Alignment.CENTERED)))
Joris Schellekens
  • 8,483
  • 2
  • 23
  • 54
Blue Owl
  • 119
  • 12

1 Answers1

0

disclaimer: I am the author of borb.

import random
import typing
from _decimal import Decimal

from borb.pdf import PDF, Document, Page, FlexibleColumnWidthTable, Table, Paragraph, Lipsum, HeterogeneousParagraph, \
    ChunkOfText, HexColor, Color, PageLayout, SingleColumnLayout
from borb.pdf.canvas.layout.annotation.remote_go_to_annotation import RemoteGoToAnnotation
from borb.pdf.canvas.layout.emoji.emoji import Emojis
from borb.pdf.canvas.layout.layout_element import LayoutElement


def main():

    # empty Document
    doc: Document = Document()

    # add empty Page
    page: Page = Page()
    doc.add_page(page)

    # layout
    layout: PageLayout = SingleColumnLayout(page)

    # build Table
    t: Table = FlexibleColumnWidthTable(number_of_columns=6, number_of_rows=5)

    # header
    t.add(Paragraph("Date", font="Helvetica-Bold"))
    t.add(Paragraph("Account", font="Helvetica-Bold"))
    t.add(Paragraph("Description", font="Helvetica-Bold"))
    t.add(Paragraph("Attachment", font="Helvetica-Bold"))
    t.add(Paragraph("Value", font="Helvetica-Bold"))
    t.add(Paragraph("Tags", font="Helvetica-Bold"))

    # data
    elements_to_add_annotations_for: typing.List[LayoutElement] = []
    for _ in range(0, 4):
        t.add(Paragraph("04/01/1989"))
        t.add(Paragraph("Joris Schellekens"))

        # random description
        t.add(Paragraph(Lipsum.generate_lipsum_text(2), font_size=Decimal(5)))

        # emoji
        # we need to be able to access the position later, so we store the LayoutElement
        e: LayoutElement = Emojis.SMILE.value
        elements_to_add_annotations_for += [e]
        t.add(e)

        # random value
        t.add(Paragraph(random.choice(["Good", "Very good", "Above expectations"])))

        # random tags
        tags: typing.List[str] = []
        tags += [random.choice(["lorem", "ipsum", "dolor"])]
        tags += [random.choice(["sit", "amet", "consectetur"])]
        tags += [random.choice(["adipiscing", "elit", "sed"])]

        # define the colors for the tags
        possible_colors_for_tags: typing.List[Color] = [HexColor("ff595e"), HexColor("ffca3a"), HexColor("8ac926"), HexColor("1982c4"), HexColor("6a4c93")]
        colors: typing.List[Color] = [random.choice(possible_colors_for_tags) for _ in tags]
        t.add(HeterogeneousParagraph(chunks_of_text=[ChunkOfText(x,
                                                                 background_color=colors[i],
                                                                 border_color=colors[i],
                                                                 border_radius_bottom_left=Decimal(5),
                                                                 border_radius_bottom_right=Decimal(5),
                                                                 border_radius_top_left=Decimal(5),
                                                                 border_radius_top_right=Decimal(5),
                                                                 border_bottom=True,
                                                                 border_top=True,
                                                                 border_left=True,
                                                                 border_right=True) for i,x in enumerate(tags)]))

    # add Table to Page
    t.set_padding_on_all_cells(Decimal(5), Decimal(5), Decimal(5), Decimal(5))
    layout.add(t)

    # add Annotation(s)
    for e in elements_to_add_annotations_for:
        page.add_annotation(RemoteGoToAnnotation(e.get_previous_paint_box(), "https://www.borbpdf.com/"))

    # write
    with open("output.pdf", "wb") as fh:
        PDF.dumps(fh, doc)


if __name__ == "__main__":
    main()

Some of the finer details of this snippet:

  • I used the Lipsum class to generate a random description
  • By keeping track of the LayoutElement it becomes easy to add an Annotation later. Annotation objects require a bounding box, and LayoutElement keeps track of where it was painted.
  • HeterogeneousParagraph allows you to merge other (text-carrying) LayoutElement objects inside it. That is how I implemented the tags. I also set their borders, giving each a border_radius to really enhance the "tag feel".

The final PDF looks something like this:

enter image description here

Joris Schellekens
  • 8,483
  • 2
  • 23
  • 54