You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
Text-ins-Bild/main.py

108 lines
3.4 KiB

#!/usr/bin/python3
import argparse
import re
import subprocess
from pathlib import Path
from typing import Tuple
OUTPUT_DIR = Path('output')
IMAGE_SIZE_PATTERN = re.compile(r'^\d+x\d+$')
def upper_first_letter(text: str) -> str:
if not text:
return text
return text[0].upper() + text[1:]
def format_filename(filename: str) -> Tuple[str, str]:
parts = re.split(r'[-_.]', filename)
# split filename into artist and image name
# example content of parts: ['001', 'davinci', 'lona', 'lisa', 'jpg']
artist = parts[1]
name = ' '.join(parts[2:-1])
return upper_first_letter(artist), upper_first_letter(name)
def get_image_size(file: Path) -> Tuple[int, int]:
# get the image size in pixel
# example output from identify command: 1920x1080
progress = subprocess.run(['identify', '-format', '%[fx:w]x%[fx:h]', str(file)], stdout=subprocess.PIPE)
result = progress.stdout.decode('utf-8')
if not re.fullmatch(IMAGE_SIZE_PATTERN, result):
return 0, 0
width, height = result.split('x')
return int(width), int(height)
def wrap_text(name: str, image_width: int) -> str:
# split name into it words
words = name.split(' ')
# considering that a letter has 50 pixels on average
word_widths = [(len(word) * 50) + 16 for word in words]
# calculate if the image width is sufficient for the text
if sum(word_widths) < image_width:
return name
start_index = 0
new_name = words[0]
for index, word_width in enumerate(word_widths, start=1):
if index == 1:
continue
sentence_width = sum(word_widths[start_index:index])
if sentence_width > image_width:
print(' -- text wrapped', end='')
start_index = index - 1
new_name += '\n'
else:
new_name += ' '
new_name += words[index - 1]
return new_name
def convert_image(file: Path):
artist, name = format_filename(file.name)
print(f'\nFilename: {file.name} -> Artist: {artist} -- Name: {name}', end='')
# check if the image is in portrait mode
is_portrait = False
width, height = get_image_size(file)
if height > width:
print(' -- Portrait', end='')
is_portrait = True
name = wrap_text(name, width)
# run ImageMagick
args = [
'convert', str(file), '-fill', '#fff',
'-font', './font/Lato-Regular.ttf', '-pointsize', '90', '-gravity', 'NorthWest' if is_portrait else 'SouthWest',
'-stroke', '#000a', '-strokewidth', '10',
'-annotate', '+30+20', name,
'-stroke', 'none',
'-annotate', '+30+20', name,
'-font', './font/BonheurRoyale-Regular.ttf', '-pointsize', '120', '-gravity', 'SouthEast',
'-stroke', '#000a', '-strokewidth', '10',
'-annotate', '+40+5', artist,
'-stroke', 'none',
'-annotate', '+40+5', artist,
str(Path(OUTPUT_DIR, file.name)),
]
subprocess.Popen(args)
def main(source_dir: Path):
# scan for all .jpg files
source_files = source_dir.glob('*.jpg')
for file in source_files:
convert_image(file)
print() # print a new line before exit
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Text ins Bild')
parser.add_argument('source_dir', type=Path, help='source directory with images')
args = parser.parse_args()
source_dir = args.source_dir
main(source_dir)