pythonpdfborb

Image with hyperlink in `borb` table


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)))

Solution

  • 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:

    The final PDF looks something like this:

    enter image description here