#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 布局B: 文字居中叠加在图片上 适用场景: 攻略、活动、大标题海报 """ from typing import Tuple from PIL import Image, ImageDraw from .base import BaseLayout from ..schemas.content import PosterContent from ..schemas.theme import Theme from ..renderers.text import TextRenderer class OverlayCenterLayout(BaseLayout): """居中叠加布局""" # 字号配置 TITLE_SIZE = 100 SUBTITLE_SIZE = 36 TAG_SIZE = 26 def calculate_content_height(self, content: PosterContent, theme: Theme) -> int: """计算内容区域高度""" height = 0 # 标题 title_font = TextRenderer.get_title_font(self.TITLE_SIZE) _, title_h = TextRenderer.measure_text(content.title, title_font) height += title_h + 32 # 含装饰线 # 副标题 if content.subtitle: subtitle_font = TextRenderer.get_body_font(self.SUBTITLE_SIZE) _, sub_h = TextRenderer.measure_text(content.subtitle, subtitle_font) height += sub_h + 40 height += 80 # padding return height def generate(self, content: PosterContent, theme: Theme) -> Image.Image: """生成海报""" # 创建渐变背景 canvas = self.create_gradient_background(theme) # 如果有真实图片 if content.image: img = content.image.copy().resize(self.size, Image.LANCZOS) canvas = img.convert("RGBA") # 暗化叠加 canvas = self.effect.darken_overlay(canvas, alpha=60) draw = ImageDraw.Draw(canvas) text_white = theme.text_rgb accent = theme.accent_rgb # 字体 title_font = TextRenderer.get_title_font(self.TITLE_SIZE) subtitle_font = TextRenderer.get_body_font(self.SUBTITLE_SIZE) tag_font = TextRenderer.get_body_font(self.TAG_SIZE) # 计算内容高度和位置 content_height = self.calculate_content_height(content, theme) center_y = (self.height - content_height) // 2 # === 装饰线 (标题上方) === line_w = 80 self.shape.draw_decorator_line( draw, ((self.width - line_w) // 2, center_y), line_w, (*accent, 200) ) center_y += 32 # === 标题 (居中,自适应大小) === content_width = self.width - self.MARGIN * 2 adaptive_font = TextRenderer.get_adaptive_title_font( content.title, content_width, base_size=self.TITLE_SIZE, min_size=64 ) title_w, title_h = TextRenderer.measure_text(content.title, adaptive_font) title_x = (self.width - title_w) // 2 TextRenderer.draw_text_with_shadow( draw, (title_x, center_y), content.title, adaptive_font, theme.text, shadow_color=(0, 0, 0, 80), offset=(3, 3) ) center_y += title_h + 24 # === 副标题 (居中) === if content.subtitle: sub_w, sub_h = TextRenderer.measure_text(content.subtitle, subtitle_font) sub_x = (self.width - sub_w) // 2 draw.text((sub_x, center_y), content.subtitle, font=subtitle_font, fill=(*text_white, 200)) center_y += sub_h + 40 # === 装饰线 (副标题下方) === self.shape.draw_decorator_line( draw, ((self.width - line_w) // 2, center_y), line_w, (*accent, 200) ) # === 底部标签 (只在有价格时显示) === if content.tags and content.price: tag_y = self.height - 120 # 计算总宽度 total_w = sum( TextRenderer.measure_text(f"#{t}", tag_font)[0] + 30 for t in content.tags ) - 30 tag_x = (self.width - total_w) // 2 for tag in content.tags: tag_text = f"#{tag}" tag_w, _ = TextRenderer.measure_text(tag_text, tag_font) self.shape.draw_rounded_rect(draw, (tag_x - 10, tag_y, tag_x + tag_w + 10, tag_y + 42), radius=21, fill=(*accent, 50)) draw.text((tag_x, tag_y + 8), tag_text, font=tag_font, fill=text_white) tag_x += tag_w + 30 return canvas