Skip to content
Open

2025 #27

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions Config.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

TEST = False

EDITION='2024'
EDITION='2025'
EDITIONS_FOLDER = 'editions'

DB_PATH_T = 'hackeps-'+EDITION+'/dev/users'
Expand Down Expand Up @@ -34,16 +34,17 @@
BOLD_FONT_FILE = 'SpaceMono-Bold.ttf'
FONT_PATH = path.join(RES_PATH, FONT_FOLDER, FONT_FILE)
BOLD_FONT_PATH = path.join(RES_PATH, FONT_FOLDER, BOLD_FONT_FILE)
TYPE_FONT_SIZE = 150
NAME_FONT_SIZE = 249
TYPE_FONT_SIZE = 40
NAME_FONT_SIZE = 60
FONT_COLOR = (0,0,0)
WHITE_FONT_COLOR = (255,255,255)
WHITE_FONT_COLOR = (84,49,26)
DARK_FONT_COLOR = (35,35,35)
BROWN_COLOR = (101, 67, 33)
TYPE_FONT = ImageFont.truetype(FONT_PATH, TYPE_FONT_SIZE)
NAME_FONT = ImageFont.truetype(FONT_PATH, NAME_FONT_SIZE)
BOLD_TYPE_FONT = ImageFont.truetype(BOLD_FONT_PATH, TYPE_FONT_SIZE)
BOLD_NAME_FONT = ImageFont.truetype(BOLD_FONT_PATH, NAME_FONT_SIZE)
# FONT = ImageFont.truetype("Symbola.ttf", 60, encoding='unic')

MAIN_COLOR = (31, 33, 36)
BAK_COLOR = (196, 222, 211)
BAK_COLOR = (119, 177, 201)
84 changes: 68 additions & 16 deletions batch_pdfer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,87 @@
from fpdf import FPDF
import glob
import os

image_directory = r'./_out_/'
extensions = ('*.jpg','*.png','*.gif')

# --- Configurable layout / scaling parameters ---
# Convert pixels to millimeters. Increase this to make images larger.
# Typical values: 0.0271 (small), 0.0847 (larger). Default here is 0.0847 for bigger images.
PIXEL_TO_MM = 0.0847
# Page layout: number of images per row/column
IMAGES_PER_ROW = 3
IMAGES_PER_COL = 3
# Page margins (mm) and gap between images (mm)
MARGIN_MM = 0
GAP_MM = 1
# Target badge size (mm) — set to your measured lanyard size
TARGET_WIDTH_MM = 95
TARGET_HEIGHT_MM = 70
# If True, compute IMAGES_PER_ROW / IMAGES_PER_COL automatically based on the target and page size
AUTO_LAYOUT = False
# How much of the target area the image should fill (0.0-1.0)
CELL_FILL = 0.95
# Allow upscaling images if they're smaller than the target
UPSCALE = True
# Global multiplier for fine adjustments
GLOBAL_SCALE = 1.0
# -------------------------------------------------

pdf = FPDF()
imagelist=[]
imagelist = []
for ext in extensions:
imagelist.extend(glob.glob(os.path.join(image_directory,ext)))
imagelist.extend(glob.glob(os.path.join(image_directory, ext)))

pdf_size = {'P': {'w': 210, 'h': 297}, 'L': {'w': 297, 'h': 210}}

for index, imageFile in enumerate(imagelist):
cover = Image.open(imageFile)
width, height = cover.size
px_w, px_h = cover.size

# convert pixels to mm
width_mm = float(px_w * PIXEL_TO_MM)
height_mm = float(px_h * PIXEL_TO_MM)

# convert pixel in mm with 1px=0.0847 mm
width, height = float(width * 0.0271), float(height * 0.0271)
# determine orientation by image aspect
orientation = 'P' if width_mm < height_mm else 'L'

# given we are working with A4 format size
pdf_size = {'P': {'w': 210, 'h': 297}, 'L': {'w': 297, 'h': 210}}
page_w = pdf_size[orientation]['w']
page_h = pdf_size[orientation]['h']

# get page orientation from image size
orientation = 'P' if width < height else 'L'
# If AUTO_LAYOUT and target specified, compute how many target cells fit on the page
if AUTO_LAYOUT and TARGET_WIDTH_MM and TARGET_HEIGHT_MM:
IMAGES_PER_ROW = max(1, int((page_w - 2 * MARGIN_MM + GAP_MM) // (TARGET_WIDTH_MM + GAP_MM)))
IMAGES_PER_COL = max(1, int((page_h - 2 * MARGIN_MM + GAP_MM) // (TARGET_HEIGHT_MM + GAP_MM)))

# make sure image size is not greater than the pdf format size
width = width if width < pdf_size[orientation]['w'] else pdf_size[orientation]['w']
height = height if height < pdf_size[orientation]['h'] else pdf_size[orientation]['h']
# compute available cell size for each image
cell_w = (page_w - 2 * MARGIN_MM - (IMAGES_PER_ROW - 1) * GAP_MM) / IMAGES_PER_ROW
cell_h = (page_h - 2 * MARGIN_MM - (IMAGES_PER_COL - 1) * GAP_MM) / IMAGES_PER_COL

if index % 9 == 0:
# If a target size is set, try to fit the image inside the target area first
if TARGET_WIDTH_MM and TARGET_HEIGHT_MM:
target_w = TARGET_WIDTH_MM * GLOBAL_SCALE * CELL_FILL
target_h = TARGET_HEIGHT_MM * GLOBAL_SCALE * CELL_FILL
base_scale = min(target_w / width_mm, target_h / height_mm)
else:
base_scale = min(cell_w / width_mm, cell_h / height_mm)

if not UPSCALE:
base_scale = min(1.0, base_scale)

render_w = width_mm * base_scale
render_h = height_mm * base_scale

# add new page when needed
images_per_page = IMAGES_PER_ROW * IMAGES_PER_COL
if index % images_per_page == 0:
pdf.add_page(orientation=orientation)

pdf.image(imageFile, 7+(width+1) * (index%3), 7+(height+1)*((index//3)%3), width, height)
col = index % IMAGES_PER_ROW
row = (index // IMAGES_PER_ROW) % IMAGES_PER_COL

x = MARGIN_MM + (render_w + GAP_MM) * col
y = MARGIN_MM + (render_h + GAP_MM) * row

pdf.image(imageFile, x, y, render_w, render_h)

pdf.output(image_directory + "file.pdf", "F")
pdf.output(os.path.join(image_directory, "file.pdf"), "F")
2 changes: 1 addition & 1 deletion models/assistant.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def generate_qr(self, crypt_id=False):

def generate_card(self, rgb_back=(255, 255, 255), template=Config.BAK_PATH_CONTESTANT):
self.card = Image.open(template)
tools.draw_text(self.card, self.type, Card.TYPE_POS, Config.TYPE_FONT, Config.WHITE_FONT_COLOR)
tools.draw_text(self.card, self.type, Card.TYPE_POS, Config.TYPE_FONT, Config.BROWN_COLOR)
if self.type == '':
self.smallen()

Expand Down
10 changes: 5 additions & 5 deletions models/card.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
class Card:
QR_PIX_SIZE = 1
QR_POS = (711*3, 315*3-333//3)
_QR_SIZE = 331*3
QR_POS = (711*1 - 100, 315*2-900//3)
_QR_SIZE = 331*1
QR_SIZE = (_QR_SIZE, _QR_SIZE)
QR_BORDER_SIZE = 2
TYPE_POS = (1050*3, 150*3)
NAME_POS = (1050*3, 40*3)
NICK_POS = (984*3, 40*3)
TYPE_POS = (1063*(1/2), 720*(0.8/5))
NAME_POS = (1063*(1/2), 720*(0.2/5))
NICK_POS = (1063*(2/4), 720*(2/5))
13 changes: 12 additions & 1 deletion models/company.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ def __init__(self, name:str, image):
self.logo = image

def generate_card(self, rgb_back=(255, 255, 255)):
print("Generating card for company:", self.name)
self.card = Image.open(Config.BAK_PATH_EMPRESA)
tools.draw_text(self.card, self.type, Card.TYPE_POS, Config.TYPE_FONT, Config.DARK_FONT_COLOR)
if self.type == '':
self.smallen()
image = tools.translate_image(self.logo)
image = tools.scale(image, Card.QR_SIZE, mask_col=(255,204,77))
image = tools.scale(image, Card.QR_SIZE)
self.card.paste(image, Card.QR_POS)
tools.centrate_text_relative(self.card, self.name.strip(),Config.BOLD_NAME_FONT, Card.NAME_POS, Card.QR_SIZE * 3, Config.DARK_FONT_COLOR)
self.smallen()
Expand Down Expand Up @@ -55,4 +56,14 @@ def get_data(name=None):
break
if Config.TEST or (name is not None and comp['name'] == name):
break

tier3=get_by_tier(3)
for comp in tier3:
for _ in range(5):
if name is None or comp['name'] == name:
res.append(Company(comp['name'], comp['image']))
if Config.TEST:
break
if Config.TEST or (name is not None and comp['name'] == name):
break
return res
7 changes: 4 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ google-cloud-storage==2.8.0
google-resumable-media==2.4.1
googleapis-common-protos==1.59.0
# grpcio==1.25.0
grpcio==1.53.0
grpcio==1.76.0
httplib2==0.22.0
idna==3.4
msgpack==1.0.5
Pillow==9.5.0
Pillow==12.0.0
# pkg-resources==0.0.0
# protobuf==3.15.0
protobuf==4.22.1
Expand All @@ -29,4 +29,5 @@ requests==2.28.2
rsa==4.9
six==1.16.0
uritemplate==3.0.0
urllib3==1.26.15
urllib3==1.26.15
cairosvg>=2.0.0
Binary file added resources/editions/2025/images/agrotecnio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/editions/2025/images/bonarea.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/editions/2025/images/eurecat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/editions/2025/images/ingroup.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/editions/2025/images/leitat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/editions/2025/images/logogran.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/editions/2025/images/paeria.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/editions/2025/images/restbai.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/editions/2025/images/udl.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/editions/2025/plantilles/mentor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 39 additions & 1 deletion resources/editions/data_sample.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,32 @@
{
"empty": 5,
"organizers": [
{"name":"Ton Llucià Senserrich"}
{"name":"Pol Llinàs"},
{"name":"Naïm Saadi"},
{"name":"Lluc Vivet"},
{"name":"Lotfi Bouakel (LOLO)"},
{"name":"Ton Llucià "},
{"name":"Joel Ros "},
{"name":"Gerard Tersa "},
{"name":"Alba Serrano "},
{"name":"Aida Chavero "},
{"name":"Martina López "},
{"name":"Anaïs Garrofé "},
{"name":"Júlia Calvo "},
{"name":"Aleix Rosinach "},
{"name":"Andreu Puig-gròs "},
{"name":"Enric Zhang "},
{"name":"David Garcia "},
{"name":"Nataly Jaya "},
{"name":"Toni López "},
{"name":"Òscar van de Crommert"},
{"name":"Carles Sànchez"},
{"name":"Gerard Ballebrera"},
{"name":"Albert Sorribes"},
{"name":"Bru Pallàs "},
{"name":"Norbert Aguilera"},
{"name":"Aleix Bertran"}

],
"volunteers": [
{"name":"Ton Llucià Senserrich"}
Expand All @@ -11,5 +36,18 @@
],
"guests": [
{"name":"Ton Llucià Senserrich","type":"HACKER","qr":true}
],
"mentors": [
{"name":"David Sarrat","qr":true},
{"name":"Sergi Vila","qr":true},
{"name":"Alba Lamas","qr":true},
{"name":"Josep","qr":true},
{"name":"Jordi Onrubia","qr":true},
{"name":"Cangrejo #1"},
{"name":"Cangrejo #2"},
{"name":"Cangrejo #3"},
{"name":"ivansaureu15"},
{"name":"Josep Maria Salvia"},
{"name":"Salce"}
]
}
Loading