Spaces:
Running
Running
File size: 6,334 Bytes
bac55b4 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
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
|