Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
EOASkripts/src/mkimage.py
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
executable file
319 lines (238 sloc)
11.5 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8; mode: python -*- | |
"""Create an automatically generated dummy cover to be used during testing. | |
If available, the program uses metadata found in the publication. An | |
image for the cover is chosen randomly. | |
You can run the tests on this standalone module with | |
python3 -m doctest -v mkimage.py | |
""" | |
__version__ = "1.0" | |
__date__ = "20170323" | |
__author__ = "kthoden@mpiwg-berlin.mpg.de" | |
import os | |
import sys | |
import logging | |
import configparser | |
import textwrap | |
import argparse | |
from PIL import Image, ImageFont, ImageDraw | |
logging.basicConfig(level=logging.INFO, format=' %(asctime)s - %(levelname)s - %(message)s') | |
DIMENSIONS = (2000, 2844) # ratio of 0.703205791106515 | |
BACKGROUND = 0, 0, 0 | |
METADATA_DICT = {'eoa_series': 'Studies', 'eoa_number': '125', | |
'eoa_authors': ['Klaus Thoden'], 'eoa_title': | |
'Der ewige Testband', 'eoa_subtitle': | |
'Experimentell'} | |
SERIES_COLOURS = {"sources" : (40, 96, 49), | |
"studies" : (13, 40, 72), | |
"proceedings" : (173, 54, 50), | |
"textbooks" : (210, 182, 35)} | |
def get_cover_image(image_path): | |
"""Choose a random landscape image from publications in this volume""" | |
import random, glob | |
extensions = ("png", "jpg") | |
candidates = [] | |
for extension in extensions: | |
path = f"{image_path}/**/*.{extension}" | |
candidates.extend( | |
glob.glob( path, recursive = True) | |
) | |
for image in candidates: | |
tmp_image = Image.open(str(image)) | |
ratio = calculate_ratio(tmp_image) | |
if ratio < 1: | |
candidates.remove(image) | |
try: | |
chosen_image = random.choice(candidates) | |
except IndexError: | |
logging.error("No suitable image found. Exiting.") | |
sys.exit() | |
return chosen_image | |
# def get_cover_image ends here | |
def calculate_ratio(image_object): | |
"""Determine the aspect ratio of an image""" | |
width, height = image_object.size | |
ratio = float(width)/float(height) | |
return ratio | |
# def calculate_ratio ends here | |
def resize_image(image_object, max_size, dimension): | |
"""Resize an image, preserve ratio. | |
Takes three arguments, an image object, the maximal size and the | |
dimension (width or height). | |
https://stackoverflow.com/questions/273946/how-do-i-resize-an-image-using-pil-and-maintain-its-aspect-ratio | |
""" | |
width, height = image_object.size | |
if dimension == "height": | |
height_percent = (max_size/float(height)) | |
wsize = int((float(width)*float(height_percent))) | |
resized_image = image_object.resize((wsize, max_size), Image.ANTIALIAS) | |
elif dimension == "width": | |
width_percent = (max_size/float(width)) | |
hsize = int((float(height)*float(width_percent))) | |
resized_image = image_object.resize((max_size, hsize), Image.ANTIALIAS) | |
else: | |
print("You must either specify height or width as dimension. Exiting.") | |
sys.exit(0) | |
return resized_image | |
# def resize_image ends here | |
def format_authors(authors_list): | |
"""Format the list of authors | |
Input is the start and end point of the authors in a list. Return | |
both a formatted string and the pure list of authors. | |
>>> format_authors([]) | |
'' | |
>>> format_authors(["Un"]) | |
'Un' | |
>>> format_authors(["Un", "Dos"]) | |
'Un and Dos' | |
>>> format_authors(["Un", "Dos", "Tres"]) | |
'Un, Dos, and Tres' | |
""" | |
if len(authors_list) == 0: | |
authors_as_string = "" | |
elif len(authors_list) == 1: | |
authors_as_string = """%s""" % (authors_list[0]) | |
elif len(authors_list) == 2: | |
authors_as_string = """%s and %s""" % (authors_list[0], authors_list[1]) | |
# elif len(authors_list) > 2: | |
else: | |
authors_as_string = """%s""" % authors_list[0] | |
for author in range(1, len(authors_list) - 1): | |
authors_as_string += ", " + authors_list[author] | |
authors_as_string += ", and %s" % (authors_list[-1]) | |
return authors_as_string | |
# def format_authors ends here | |
def add_watermark(image, watermarkstring, big_red_font): | |
"""Add a string of text across the cover. Return a rotated image object""" | |
# https://codenhagen.wordpress.com/2015/12/04/putting-rotated-text-on-images-with-pillow-python/ | |
base_image = Image.open(image) | |
tmp_img = Image.new("RGBA", (DIMENSIONS[1], DIMENSIONS[0]), (0,0,0,0)) | |
font_colour = (255,0,0) | |
text_canvas = ImageDraw.Draw(tmp_img) | |
text_canvas.text((0, 0), watermarkstring, font=big_red_font, fill=font_colour) | |
slanted_image = tmp_img.rotate(60, expand=True) | |
# add third parameter as transparent mask | |
# https://stackoverflow.com/questions/5324647/how-to-merge-a-transparent-png-image-with-another-image-using-pil | |
base_image.paste(slanted_image, (200, 100), slanted_image) | |
base_image.save(image) | |
logging.info("Added a watermark to %s." % image) | |
# return slanted_image | |
# def add_watermark ends here | |
def centered(textstring, font_spec): | |
"""Return coordinates for a centered string. | |
>>> centered('Testband EOATeX', ImageFont.truetype(font="Times New Roman Bold", size=120)) | |
504.5 | |
""" | |
tmp_draw = ImageDraw.Draw(Image.new("RGB", DIMENSIONS, BACKGROUND)) | |
string_width, string_height = tmp_draw.textsize(textstring, font=font_spec) | |
coordinate = DIMENSIONS[0] / 2 - string_width / 2 | |
return coordinate | |
# def centered ends here | |
def create_cover(metadata_dict, image_directory, cover_filename, image_is_file, big_bold_font, medium_font, small_font): | |
"""Create a cover using PIL""" | |
img = Image.new("RGB", DIMENSIONS, BACKGROUND) | |
upper_part = Image.new("RGB", (DIMENSIONS[0], int(DIMENSIONS[1]/3)), SERIES_COLOURS[metadata_dict['eoa_series'].lower()]) | |
img.paste(upper_part, (0, 0)) | |
title_text = metadata_dict['eoa_title'] | |
subtitle_text = metadata_dict['eoa_subtitle'] | |
authors_text = format_authors(metadata_dict['eoa_authors']) | |
if len(metadata_dict['eoa_zusatz']) > 0: | |
authors_text += " {}".format(metadata_dict['eoa_zusatz']) | |
series_number_text = "{0} {1}".format(metadata_dict['eoa_series'], metadata_dict['eoa_number']) | |
if metadata_dict['eoa_series'].lower() == "sources": | |
press_text = "Edition Open Sources" | |
else: | |
press_text = "Max Planck Research Library for the History and Development of Knowledge" | |
if metadata_dict['eoa_series'].lower() == "textbooks": | |
fill_colour_top = (0, 0, 0) | |
else: | |
fill_colour_top = (255, 255, 255) | |
text_draw = ImageDraw.Draw(img) | |
# these will eventually also become candidates for multilines | |
# text_draw.text((centered(title_text, big_bold_font), 200), title_text, font=big_bold_font, fill=fill_colour_top) | |
title_text_lines = textwrap.wrap(title_text, width=30) | |
title_text_joined = "\n".join(title_text_lines) | |
ttcenter = centered(title_text_joined, big_bold_font) | |
text_draw.multiline_text((ttcenter, 200), title_text_joined, font=big_bold_font, fill=fill_colour_top, align="center") | |
if len(subtitle_text) > 0: | |
subtitle_lines = textwrap.wrap(subtitle_text, width=40) | |
subtitle_lines_joined = "\n".join(subtitle_lines) | |
subtitle_center = centered(subtitle_lines_joined, medium_font) | |
text_draw.multiline_text((subtitle_center, 350), subtitle_lines_joined, font=medium_font, fill=fill_colour_top, align="center") | |
authors_text_lines = textwrap.wrap(authors_text, width=50) | |
authors_text_joined = "\n".join(authors_text_lines) | |
ttcenter = centered(authors_text_joined, small_font) | |
text_draw.multiline_text((ttcenter, int(DIMENSIONS[1]/3)-200), authors_text_joined, font=small_font, fill=fill_colour_top, align="center") | |
# text_draw.text((centered(authors_text, small_font), int(DIMENSIONS[1]/3)-200), authors_text, font=small_font, fill=fill_colour_top) | |
press_text_lines = textwrap.wrap(press_text, width=40) | |
press_text_lines.append(series_number_text) | |
press_text_joined = "\n".join(press_text_lines) | |
ptcenter = centered(press_text_joined, small_font) | |
text_draw.multiline_text((ptcenter,DIMENSIONS[1]-400), press_text_joined, font=small_font, align="center") | |
if image_is_file == False: | |
image_on_cover = Image.open(get_cover_image(image_directory)) | |
else: | |
image_on_cover = Image.open(image_directory) | |
MAXIMUM_HEIGHT = 1200 | |
resized_image = resize_image(image_on_cover, MAXIMUM_HEIGHT, "height") | |
coord = DIMENSIONS[0]/2 - resized_image.width/2 | |
img.paste(resized_image, (int(coord), 1200)) | |
img.save(cover_filename) | |
logging.info("Wrote %s." % cover_filename) | |
# def create_cover ends here | |
def main(): | |
"""The main bit""" | |
parser = argparse.ArgumentParser() | |
parser.add_argument("image_dir_or_image", help="Path to directory with potential images or a single file.") | |
parser.add_argument("-c", "--config", help="File that contains the publication data.", default="publication.cfg") | |
parser.add_argument("-o", "--output", help="Name of output file.", default="Cover.jpg") | |
parser.add_argument("-nw", "--nowatermark", help="Do not add a watermark to the image.", action="store_true") | |
parser.add_argument("-lf", "--localfonts", help="Use local fonts, rather than those used in Docker", action="store_true") | |
args = parser.parse_args() | |
if args.localfonts: | |
big_red_font = ImageFont.truetype("Helvetica", 200) | |
big_bold_font = ImageFont.truetype(font="Times New Roman Bold", size=120) | |
medium_font = ImageFont.truetype(font="Times New Roman", size=100) | |
small_font = ImageFont.truetype(font="Times New Roman", size=80) | |
else: | |
big_red_font = ImageFont.truetype("/usr/share/fonts/truetype/msttcorefonts/comic.ttf", 200) | |
big_bold_font = ImageFont.truetype(font="/usr/share/fonts/truetype/msttcorefonts/timesbd.ttf", size=120) | |
medium_font = ImageFont.truetype(font="/usr/share/fonts/truetype/msttcorefonts/times.ttf", size=100) | |
small_font = ImageFont.truetype(font="/usr/share/fonts/truetype/msttcorefonts/times.ttf", size=80) | |
if args.config: | |
# if os.path.exists("publication.cfg"): | |
logging.info("Using %s as publication config" % (args.config)) | |
config = configparser.ConfigParser() | |
config.read(args.config) | |
list_of_authors = [] | |
for entry in range(0, 5): | |
author_label = "Author" + str(entry + 1) | |
tmp_author = config['Authors'][author_label] | |
if len(tmp_author) > 0: | |
list_of_authors.append(tmp_author) | |
METADATA_DICT.update({'eoa_series' : config['Technical']['Serie']}) | |
METADATA_DICT.update({'eoa_number' : config['Technical']['Number']}) | |
METADATA_DICT.update({'eoa_title' : config['Technical']['Title']}) | |
METADATA_DICT.update({'eoa_subtitle' : config['Technical']['Subtitle']}) | |
METADATA_DICT.update({'eoa_authors' : list_of_authors}) | |
METADATA_DICT.update({'eoa_zusatz' : config['Authors']['Zusatz']}) | |
else: | |
logging.info("Using the built-in metadata as publication config") | |
OUTFILE = args.output | |
IMAGES = args.image_dir_or_image | |
if os.path.isfile(IMAGES): | |
image_is_file = True | |
elif os.path.isdir(IMAGES): | |
image_is_file = False | |
else: | |
print("No valid image or directory given. Exiting.") | |
sys.exit() | |
create_cover(METADATA_DICT, IMAGES, OUTFILE, image_is_file, big_bold_font, medium_font, small_font) | |
if args.nowatermark: | |
logging.info("No watermark. Be careful. This is not meant to be an official cover.") | |
else: | |
add_watermark(OUTFILE, "Automatically generated cover.\nDo not use in production!", big_red_font) | |
# def main ends here | |
if __name__ == '__main__': | |
main() | |
# finis |