3dmodelmaker / src /front_card_image_historic_site.py
ikeda
add 3dmodelmaker sources
bac55b4
raw
history blame
6.33 kB
from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
# カード画像関連
card_frame_path_dict = {
'橙': 'data/cards/史跡カードフレーム(橙).png',
'白': 'data/cards/史跡カードフレーム(白).png',
'紫': 'data/cards/史跡カードフレーム(紫).png',
'緑': 'data/cards/史跡カードフレーム(緑).png',
'茶': 'data/cards/史跡カードフレーム(茶).png',
'赤': 'data/cards/史跡カードフレーム(赤).png',
'青': 'data/cards/史跡カードフレーム(青).png',
'黄': 'data/cards/史跡カードフレーム(黄).png',
'黒': 'data/cards/史跡カードフレーム(黒).png'
}
card_mark_path_dict = {
'区指定': 'data/cards/史跡カード指定マーク(区指定).png',
'国指定': 'data/cards/史跡カード指定マーク(国指定).png',
'市指定': 'data/cards/史跡カード指定マーク(市指定).png',
'府指定': 'data/cards/史跡カード指定マーク(府指定).png',
'村指定': 'data/cards/史跡カード指定マーク(村指定).png',
'町指定': 'data/cards/史跡カード指定マーク(町指定).png',
'県指定': 'data/cards/史跡カード指定マーク(県指定).png',
'道指定': 'data/cards/史跡カード指定マーク(道指定).png',
'都指定': 'data/cards/史跡カード指定マーク(都指定).png'
}
# カード画像各領域のピクセル位置情報
# 写真
PICTURE_LT_XY = (65, 188)
PICTURE_RB_XY = (802, 925)
PICTURE_SIZE = (PICTURE_RB_XY[0] - PICTURE_LT_XY[0], PICTURE_RB_XY[1] - PICTURE_LT_XY[1])
# タイトル
# ある程度の余白を作る。
TITLE_LT_XY = (65, 45)
TITLE_RB_XY = (647, 132) # マーク挿入部分と重ならないような位置
TITLE_SIZE = (TITLE_RB_XY[0] - TITLE_LT_XY[0], TITLE_RB_XY[1] - TITLE_LT_XY[1])
# 説明欄区切り線
DESCRIPTION_LINE_L_XY = (52, 1024)
DESCRIPTION_LINE_R_XY = (816, 1024)
# 史跡種類
HS_TYPE_LT_XY = (56, 972)
# 訪問難度
DIFFICULTY_LT_XY = (444, 972)
# 説明文本体
DESCRIPTION_LT_XY = (46, 1024)
DESCRIPTION_RB_XY = (810, 1174)
DESCRIPTION_SIZE = (DESCRIPTION_RB_XY[0] - DESCRIPTION_LT_XY[0], DESCRIPTION_RB_XY[1] - DESCRIPTION_LT_XY[1])
# フォント関連
# 明朝体
font_selif_path = 'data/fonts/SourceHanSerif-Bold.otf'
# ゴシック体
font_sanselif_path = 'data/fonts/SourceHanSans-Bold.otf'
def crop_center(pil_img, crop_width, crop_height):
img_width, img_height = pil_img.size
return pil_img.crop(((img_width - crop_width) // 2,
(img_height - crop_height) // 2,
(img_width + crop_width) // 2,
(img_height + crop_height) // 2))
def crop_max_square(pil_img):
return crop_center(pil_img, min(pil_img.size), min(pil_img.size))
def create_historic_site_card_image(img_bytearray, option_dict):
# 画像の読み込みとトリミング
picture_img = Image.open(img_bytearray)
picture_img = crop_max_square(picture_img)
picture_img = picture_img.resize(PICTURE_SIZE)
# カードの読み込み
card_img = Image.open(card_frame_path_dict[option_dict['色']])
# マークの追加
mark_image = Image.open(card_mark_path_dict[option_dict['マーク']])
card_img.paste(mark_image, mask=mark_image)
# 写真の埋め込み
card_img.paste(picture_img, PICTURE_LT_XY)
# 各種書き込み準備
card_imgdraw = ImageDraw.Draw(card_img)
# カード枠線の追加
card_imgdraw.line(( (0, 0), (card_img.size[0], 0) ), fill='black', width=15)
card_imgdraw.line(( (0, 0), (0, card_img.size[1]) ), fill='black', width=15)
card_imgdraw.line(( (card_img.size[0], 0), card_img.size), fill='black', width=15)
card_imgdraw.line(( (0, card_img.size[1]), card_img.size), fill='black', width=15)
# 説明欄区切り線の追加
card_imgdraw.line((DESCRIPTION_LINE_L_XY, DESCRIPTION_LINE_R_XY), fill='black', width=3)
# タイトル埋め込み
# (複数行対応は必要ならば対応)
title_font_size = 100
while True:
title_font = ImageFont.truetype(font_selif_path, title_font_size)
title_bbox = card_imgdraw.textbbox(TITLE_LT_XY , option_dict['タイトル'], title_font)
if (title_bbox[2] <= TITLE_RB_XY[0] and title_bbox[3] <= TITLE_RB_XY[1]) or title_font_size <= 30:
break
title_font_size -= 1
card_imgdraw.text((TITLE_LT_XY[0], int((TITLE_LT_XY[1] + TITLE_RB_XY[1]) / 2)), option_dict['タイトル'], fill='black', font=title_font, anchor='lm')
# 史跡種類埋め込み
hs_type_display = f'種類:{option_dict["史跡種類"]}'
hs_typefont = ImageFont.truetype(font_sanselif_path, 40)
card_imgdraw.text(HS_TYPE_LT_XY, hs_type_display, fill='black', font=hs_typefont, anchor='lt')
# 訪問難度埋め込み
difficulty = int(option_dict['訪問難度'])
difficulty = 1 if difficulty < 1 else 5 if difficulty > 5 else difficulty
difficulty_display = '訪問難度:' + '☆' * difficulty + '★' * (5 - difficulty)
difficulty_font = ImageFont.truetype(font_sanselif_path, 40)
card_imgdraw.text(DIFFICULTY_LT_XY, difficulty_display, fill='black', font=difficulty_font, anchor='lt')
# 説明文埋め込み
description_font = ImageFont.truetype(font_sanselif_path, 40)
description_list = []
description_length = len(option_dict['説明文'])
temp_start = 0
for i in range(description_length):
temp_end = i
description_line_bbox = card_imgdraw.textbbox((0, 0), option_dict['説明文'][temp_start:temp_end+1], description_font)
if description_line_bbox[2] > DESCRIPTION_SIZE[0]:
description_list.append(option_dict['説明文'][temp_start:temp_end])
temp_start = i
description_list.append(option_dict['説明文'][temp_start:])
description_display = '\n'.join(description_list)
card_imgdraw.text(DESCRIPTION_LT_XY, description_display, fill='black', font=description_font)
# バイナリデータの出力
output_img_bytearray = BytesIO()
card_img.convert('RGB').save(output_img_bytearray, "JPEG", quality=95)
output_img_bytearray.seek(0) # 画像の先頭にシークしないと空データになってしまう。
return output_img_bytearray