Files
website/image_poster.py

199 lines
6.4 KiB
Python
Executable File

#! /usr/bin/env python3
import argparse
import io
from PIL import Image, ImageDraw, ImageFont
import csv
import datetime as dt
from dateutil import relativedelta
from cairosvg import svg2png
CURRENT_TIME = dt.date.today()
NEXT_MONTH = CURRENT_TIME + relativedelta.relativedelta(months=1, day=1)
DAYS_OF_WEEK_SR = ("PON", "UTO", "SRE", "ČET", "PET", "SUB", "NED")
DAYS_OF_WEEK_EN = ("MON", "TUE", "WED", "THU", "FRI", "SAT", "SUn")
MONTHS_SR = ("Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul",
"Avgust", "Septembar", "Oktobar", "Novembar", "Decembar")
MONTHS_EN = ("January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December")
HEADER_SR = "Plan za {}"
SUBHEADER_SR = """
Ponedeljak u 19h u XECUT Jovana Ćirilova 15
Utorak u 18h u JAG3 Vatroslava Jagića 5
Svi dogadjaji su uvek besplatni
"""
HEADER_EN = "Plan for {}"
SUBHEADER_EN = """
Mondays at 19h at XECUT Jovana Ćirilova 15
Tuesdays at 18h at JAG3 Vatroslava Jagića 5
All events are always free
"""
def parseArgs(parser):
"""
Parse all arguments and return the list of argument values
"""
parser.add_argument("month", metavar="MM",
help="two digit number representing the month for which to generate poster", default="empty", nargs="?")
return parser.parse_args()
def load_events(csv_path: str, month: int) -> list[dict]:
monthafter = month + relativedelta.relativedelta(months=1, day=1)
events = []
with open(csv_path) as csv_file:
csv_reader = csv.reader(csv_file)
next(csv_reader, None)
for event in csv_reader:
event_date = event[0]
event_date_parsed = dt.datetime.strptime(
event_date, "%d-%m-%Y").date()
event_time = event[1]
event_title = event[3]
event_title_en = event[3]
if len(event) > 6:
event_title_en = event[6]
current_event = {"date": event_date_parsed,
"time": event_time,
"title": event_title.strip(),
"title_en": event_title_en.strip()}
if event_date_parsed >= month and event_date_parsed < monthafter:
events.append(current_event)
return events
def drawMesh(draw, img, fg, bg, font, W, H):
def drawCircle(x, y):
r = 50
draw.ellipse((x - r, y - r, x + r, y+r),
fill=fg, outline=(0, 0, 0), width=0)
LCX = 415 # logo center x
LCY = 4350 # logo center y
d = 190 # delta
drawCircle(LCX - d, LCY)
drawCircle(LCX, LCY)
drawCircle(LCX, LCY - d)
drawCircle(LCX, LCY + d)
drawCircle(LCX + d, LCY)
draw.line([(LCX - d, LCY), (LCX + d, LCY)], fill=fg, width=20, joint=None)
draw.line([(LCX, LCY), (LCX, LCY + d), (LCX + d, LCY),
(LCX, LCY - d)], fill=fg, width=20, joint=None)
draw.text((LCX - 1.7*d, LCY + 1.5*d), "dmz.rs", font=font, fill=fg)
mesh_svg = svg2png(url='site/img/mesh-light.svg')
mesh_svg_bytes = io.BytesIO(mesh_svg)
mesh_img = Image.open(mesh_svg_bytes)
if bg == (0, 0, 0):
pixdata = mesh_img.load()
for y in range(mesh_img.size[1]):
for x in range(mesh_img.size[0]):
if pixdata[x, y] != (0, 0, 0, 0):
pixdata[x, y] = (0, 100, 0, 255)
mesh_img = mesh_img.resize((W, H))
mesh_img.thumbnail((W, H), Image.Resampling.LANCZOS)
mesh_w, mesh_h = mesh_img.size
mesh_position = (W - mesh_w, H - mesh_h)
img.paste(mesh_img, mesh_position, mesh_img)
def drawPoster(events, bg, fg, month: int, en: bool):
fontFacade = ImageFont.truetype('./site/font/Facade-Sud.woff', size=365)
fontIosevka = ImageFont.truetype(
'./site/font/iosevka-regular.woff', size=200)
fontIosevkaSmall = ImageFont.truetype(
'./site/font/iosevka-regular.woff', size=150)
W = 3508
H = 4960
img = Image.new('RGB', (W, H), bg)
draw = ImageDraw.Draw(img)
drawMesh(draw, img, fg, bg, fontIosevka, W, H)
title = "DECENTRALA"
_, _, w, _ = draw.textbbox((0, 0), title, font=fontFacade)
draw.text(((W-w)/2, 165), title, font=fontFacade, fill=fg)
header = HEADER_EN if en else HEADER_SR
months = MONTHS_EN if en else MONTHS_SR
header = header.format(months[month.month - 1])
_, _, w, _ = draw.textbbox((0, 0), header, font=fontIosevka)
draw.text(((W-w)/2, 560), header, font=fontIosevka, fill=fg)
height = 890
sub_header = SUBHEADER_EN if en else SUBHEADER_SR
draw.text((165, height), sub_header,
font=fontIosevkaSmall, fill=fg)
height += 800
# Write list of events to sperate text file as well
textfile = open(f"poster{"_en" if en else ""}.txt", "w")
sub_header = SUBHEADER_EN if en else SUBHEADER_SR
textfile.write(sub_header.format(months[month.month - 1]))
days_of_week = DAYS_OF_WEEK_EN if en else DAYS_OF_WEEK_SR
# Loop to write events both to poster image and text file
for event in events:
# Add event to image poster
date = days_of_week[event["date"].weekday()]
day = event["date"].day
title = event["title_en"] if en else event["title"]
pad = " " if event["date"].day < 10 else ""
eventText = f"{date} {day}. {pad}{title}"
draw.text((165, height), eventText, font=fontIosevkaSmall, fill=fg)
height += 200
# Add event to textfile
textfile.write(eventText + "\n")
textfile.close()
return img
def main():
# Parse arguments
parser = argparse.ArgumentParser(
description="Generate images of the poster")
args = parseArgs(parser)
# Set month based on user input
month = NEXT_MONTH
if args.month.isdigit():
month = dt.date(CURRENT_TIME.year, int(args.month), 1)
elif args.month != "empty":
print("Month has to be specified as a number. I will use next month as the default")
# Load events and draw a poseter
events = load_events("dogadjaji.csv", month)
img = drawPoster(events, (0, 0, 0), (20, 250, 50), month, False)
img.save('poster_dark.png')
img = drawPoster(events, (255, 255, 255), (0, 0, 0), month, False)
img.save('poster_light.png')
img = drawPoster(events, (0, 0, 0), (20, 250, 50), month, True)
img.save('poster_dark_en.png')
img = drawPoster(events, (255, 255, 255), (0, 0, 0), month, True)
img.save('poster_light_en.png')
if __name__ == "__main__":
main()