#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 布局抽象基类 """ from abc import ABC, abstractmethod from typing import Tuple, Optional from PIL import Image, ImageDraw from ..schemas.content import PosterContent from ..schemas.theme import Theme from ..renderers.text import TextRenderer from ..renderers.shape import ShapeRenderer from ..renderers.effect import EffectRenderer class BaseLayout(ABC): """ 所有布局的抽象基类 布局负责: 1. 计算内容区域位置和尺寸 2. 协调各渲染器绘制内容 3. 生成最终海报图像 4. 记录元素位置 (用于 Fabric.js) """ # 默认尺寸 (小红书 3:4) DEFAULT_SIZE = (1080, 1440) # 默认边距 MARGIN = 48 PADDING = 32 def __init__(self, size: Tuple[int, int] = None): self.width, self.height = size or self.DEFAULT_SIZE self.size = (self.width, self.height) # 渲染器 self.text = TextRenderer() self.shape = ShapeRenderer() self.effect = EffectRenderer() # 元素记录 (用于 Fabric.js) self._fabric_objects = [] def _reset_objects(self): """重置元素记录""" self._fabric_objects = [] def _add_object(self, obj: dict): """添加元素""" self._fabric_objects.append(obj) def get_fabric_objects(self) -> list: """获取 Fabric.js 对象列表""" return self._fabric_objects.copy() @abstractmethod def generate(self, content: PosterContent, theme: Theme) -> Image.Image: """ 生成海报 Args: content: 海报内容 theme: 主题配置 Returns: 生成的海报图像 """ pass @abstractmethod def calculate_content_height(self, content: PosterContent, theme: Theme) -> int: """ 计算内容区域高度 (用于动态适配) Args: content: 海报内容 theme: 主题配置 Returns: 内容区域高度 """ pass def create_canvas(self, background: Tuple[int, int, int] = (255, 255, 255)) -> Image.Image: """创建画布""" return Image.new("RGBA", self.size, (*background, 255)) def create_gradient_background(self, theme: Theme) -> Image.Image: """创建渐变背景""" colors = theme.gradient_rgb return self.effect.create_gradient(self.size, colors[0], colors[1]) def paste_with_alpha(self, canvas: Image.Image, layer: Image.Image, pos: Tuple[int, int]): """粘贴带透明度的图层""" canvas.paste(layer, pos, layer) @property def layout_name(self) -> str: """布局名称""" return self.__class__.__name__.replace("Layout", "").lower()