import os import textwrap from typing import Tuple from PIL import Image, ImageDraw, ImageFont, UnidentifiedImageError from poizonstore.settings import BASE_DIR def create_preview(source_image: str, size=None, price_rub=None, title_lines=None): # Create image preview_width, preview_height = 800, 600 hor_padding = 15 vert_padding = 50 left_block_width = 270 # minimal width canvas_img = Image.new('RGBA', (preview_width, preview_height), color='white') draw = ImageDraw.Draw(canvas_img) def get_font(font_size): font_path = os.path.join(BASE_DIR, 'static', 'preview_image_font.ttf') return ImageFont.truetype(font_path, size=font_size) def draw_text(xy: Tuple[float, float], text: str, font: ImageFont, multiline_width: int = None, **kwargs): x, y = xy block_width = 0 block_height = 0 if multiline_width is None: text = [text] else: text = textwrap.wrap(text, width=multiline_width) for line in text: _, _, line_width, line_height = font.getbbox(line) draw.text((x, y), line, font=font, **kwargs) y += line_height block_width = max(block_width, line_width) block_height += line_height return block_width, block_height def resize_with_ar(image, size): max_w, max_h = size if image.height > image.width: factor = max_h / image.height else: factor = max_w / image.width return image.resize((int(image.width * factor), int(image.height * factor))) if not os.path.isfile(source_image): return None # Draw top text top_font = get_font(20) text = 'Заказ в Poizon Store' block_size = draw_text((hor_padding, vert_padding), text, font=top_font, fill='black') left_block_width = max(left_block_width, block_size[0] + hor_padding) # Draw title title_x, title_y = hor_padding, vert_padding + 30 if title_lines: title_font = get_font(40) for title_line in title_lines: width, height = draw_text((title_x, title_y), title_line, font=title_font, multiline_width=12, fill='black') title_y += height left_block_width = max(left_block_width, width + title_x) # Draw size if size: size_text = str(size) size_font = get_font(20) size_padding = 7 _, _, line_width, line_height = size_font.getbbox(size_text) rect_x, rect_y = hor_padding, title_y + 30 rect_width, rect_height = (line_width + size_padding * 2), (line_height + size_padding * 2) rect_width, rect_height = max(40, rect_width), max(40, rect_height) # size text is slightly inside a rect size_x, size_y = (rect_x + rect_width/2), (rect_y + rect_height/2) draw.rectangle((rect_x, rect_y, rect_x + rect_width, rect_y + rect_height), fill='black', width=2) draw_text((size_x, size_y), size_text, font=size_font, fill='white', anchor='mm') left_block_width = max(left_block_width, rect_width) # Draw price if price_rub: price_text = f"{int(price_rub)} ₽" price_font = get_font(50) price_x, price_y = hor_padding + 15, preview_height - 100 block_size = draw_text((price_x, price_y), price_text, font=price_font, fill='black') left_block_width = max(left_block_width, block_size[0] + price_x) # Draw goods image img2_box_w = preview_width - left_block_width - hor_padding * 2 img2_box_y = preview_height - vert_padding * 2 try: with Image.open(source_image).convert("RGBA") as img2: img2 = resize_with_ar(img2, (img2_box_w, img2_box_y)) img2_x = left_block_width + int(img2_box_w / 2) - int(img2.width / 2) img2_y = 50 + int(img2_box_y / 2) - int(img2.height / 2) canvas_img.paste(img2, (img2_x, img2_y), mask=img2) # Debug only # img2_box = (img2_x, img2_y, img2_x + img2.width, img2_y + img2.height) # draw.rectangle(img2_box, outline='red', width=2) return canvas_img.convert('RGB') except UnidentifiedImageError: return None def concat_not_null_values(*values, separator=' '): return separator.join([v for v in values if v is not None])