from PIL import Image, ImageDraw, ImageFont from io import BytesIO from src.constants import front_card_img_dict # カード画像各領域のピクセル位置情報 # 写真 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_LT_XY = (46, 972) 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_card_image(model_no, 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(front_card_img_dict[model_no]) # 写真の埋め込み card_img.paste(picture_img, PICTURE_LT_XY) # 各種書き込み準備 card_imgdraw = ImageDraw.Draw(card_img) # タイトル埋め込み # (複数行対応は必要ならば対応) 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') # 説明文埋め込み 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