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)))
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:
Lipsum
class to generate a random descriptionLayoutElement
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: