109 lines
3.4 KiB
Python
109 lines
3.4 KiB
Python
|
#!/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)
|