caption.py
This module contains helper stuff to handle putting a caption on top of the final image.
do_caption(path_in, path_out, dict_cfg)
Make caption version of image
:param path_in: Path to original image :type path_in: Path :param path_out: Path to new image with caption :type path_out: Path :param dict_apod: The APOD dict :type dict_apod: dict :param dict_cap: Dictionary with caption properties :type dict_cap: dict
This method will create a new image from the original image, containing a caption box.
Source code in src/caption.py
def do_caption(path_in: Path, path_out: Path, dict_cfg: dict):
"""
Make caption version of image
:param path_in: Path to original image
:type path_in: Path
:param path_out: Path to new image with caption
:type path_out: Path
:param dict_apod: The APOD dict
:type dict_apod: dict
:param dict_cap: Dictionary with caption properties
:type dict_cap: dict
This method will create a new image from the original image, containing a
caption box.
"""
# sanity check
if not path_in.exists():
return
# get downloaded image and size
wp = Image.open(path_in)
wp_w, wp_h = wp.size
# --------------------------------------------------------------------------
# get top dict object
dict_cap = dict_cfg[K.S_KEY_CAPTION]
# --------------------------------------------------------------------------
# get font object
# get sub dict
dict_font = dict_cap[K.S_KEY_FONT]
# get font name and size
font_name = dict_font[K.S_KEY_FONT_NAME]
font_size = dict_font[K.S_KEY_FONT_SIZE]
# try to get specified font
try:
font = ImageFont.truetype(font_name, font_size)
# NB: also applies when font name = "" (still working on it)
except OSError:
# can't get font, use default
font = ImageFont.load_default(font_size)
# combine font color and transparency
font_color = []
font_color.extend(dict_font[K.S_KEY_FONT_COLOR])
font_color.append(dict_font[K.S_KEY_FONT_TRANS])
font_color = [int(item) for item in font_color]
font_color = tuple(font_color)
# --------------------------------------------------------------------------
# get text size
# get all lines in text and wrap/separate
lines = _get_lines(dict_cfg)
# put the lines list back together for printing
text = "".join(lines)
# create a dummy image of no size to get a draw
img_text = Image.new("RGB", (0, 0))
draw_text = ImageDraw.Draw(img_text)
# draw text to dummy image to get size
bbox = draw_text.textbbox((0, 0), text, font)
text_w = bbox[2] # (x, y, w, h) = width == 2
text_h = bbox[3] # (x, y, w, h) = height == 3
# --------------------------------------------------------------------------
# get box size
# get sub dict
dict_box = dict_cap[K.S_KEY_BOX]
# first, internal padding
box_pad = dict_box[K.S_KEY_BOX_PAD]
# add padding to longest line
box_w = text_w + (box_pad * 2)
box_h = text_h + (box_pad * 2)
# --------------------------------------------------------------------------
# make box
# get radius
box_rad = dict_box[K.S_KEY_BOX_RAD]
# combine box color and transparency
box_color = []
box_color.extend(dict_box[K.S_KEY_BOX_COLOR])
box_color.append(dict_box[K.S_KEY_BOX_TRANS])
box_color = [int(item) for item in box_color]
box_color = tuple(box_color)
# draw box
img_box = Image.new("RGBA", (int(box_w), int(box_h)), color=(0, 0, 0, 0))
draw = ImageDraw.Draw(img_box)
draw.rounded_rectangle(
[(0, 0), (int(box_w), int(box_h))],
radius=int(box_rad),
fill=box_color,
)
# draw text in box
draw.multiline_text((box_pad, box_pad), text, fill=font_color, font=font)
# --------------------------------------------------------------------------
# get sub dict
dict_pad = dict_cap[K.S_KEY_PAD]
# get initial pos (to prevent unbound)
pos_x = 0
pos_y = 0
# get different pads
pad_ext_l = dict_pad[K.S_KEY_PAD_L]
pad_ext_t = dict_pad[K.S_KEY_PAD_T]
pad_ext_r = dict_pad[K.S_KEY_PAD_R]
pad_ext_b = dict_pad[K.S_KEY_PAD_B]
# get combined center (actually x,y offset of box)
mid_x = (wp_w - box_w) // 2
mid_y = (wp_h - box_h) // 2
# get position of caption from pos
match dict_cap[K.S_KEY_CAPTION_POS]:
case 0: # top left
pos_x = pad_ext_l
pos_y = pad_ext_t
case 1: # top center
pos_x = max(pad_ext_l, mid_x)
pos_y = pad_ext_t
case 2: # top right
pos_x = wp_w - pad_ext_r - box_w
pos_y = pad_ext_t
case 3: # center left
pos_x = pad_ext_l
pos_y = max(pad_ext_t, mid_y)
case 4: # center center
pos_x = max(pad_ext_l, mid_x)
pos_y = max(pad_ext_t, mid_y)
case 5: # center right
pos_x = wp_w - pad_ext_r - box_w
pos_y = max(pad_ext_t, mid_y)
case 6: # bottom left
pos_x = pad_ext_l
pos_y = wp_h - pad_ext_b - box_h
case 7: # bottom center
pos_x = max(pad_ext_l, mid_x)
pos_y = wp_h - pad_ext_b - box_h
case 8: # bottom right
pos_x = wp_w - pad_ext_r - box_w
pos_y = wp_h - pad_ext_b - box_h
# --------------------------------------------------------------------------
# put caption on wallpaper at position
pos_x = int(pos_x)
pos_y = int(pos_y)
wp.paste(img_box, (pos_x, pos_y), mask=img_box)
# save captioned wallpaper
wp.save(path_out)