海报库弄完了
This commit is contained in:
parent
6704dd58e7
commit
27c2170239
@ -71,7 +71,7 @@ class PosterConfigManager:
|
|||||||
"id": template_id,
|
"id": template_id,
|
||||||
"name": template_info.get("name", template_id),
|
"name": template_info.get("name", template_id),
|
||||||
"description": template_info.get("description", ""),
|
"description": template_info.get("description", ""),
|
||||||
"size": template_info.get("size", [900, 1200]),
|
"size": template_info.get("size", [1350, 1800]),
|
||||||
"required_fields": template_info.get("required_fields", []),
|
"required_fields": template_info.get("required_fields", []),
|
||||||
"optional_fields": template_info.get("optional_fields", [])
|
"optional_fields": template_info.get("optional_fields", [])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,7 +80,7 @@ templates:
|
|||||||
vibrant:
|
vibrant:
|
||||||
name: "Vibrant活力模板"
|
name: "Vibrant活力模板"
|
||||||
description: "适合旅游景点、娱乐活动的活力海报模板"
|
description: "适合旅游景点、娱乐活动的活力海报模板"
|
||||||
size: [900, 1200]
|
size: [1350, 1800]
|
||||||
required_fields: ["title", "slogan", "price", "ticket_type", "content_items"]
|
required_fields: ["title", "slogan", "price", "ticket_type", "content_items"]
|
||||||
optional_fields: ["remarks", "tag", "pagination"]
|
optional_fields: ["remarks", "tag", "pagination"]
|
||||||
prompt_key: "vibrant"
|
prompt_key: "vibrant"
|
||||||
|
|||||||
@ -26,7 +26,7 @@ class TemplateInfo(BaseModel):
|
|||||||
"id": "vibrant",
|
"id": "vibrant",
|
||||||
"name": "Vibrant活力模板",
|
"name": "Vibrant活力模板",
|
||||||
"description": "适合旅游景点、娱乐活动的活力海报模板",
|
"description": "适合旅游景点、娱乐活动的活力海报模板",
|
||||||
"size": [900, 1200],
|
"size": [1350, 1800],
|
||||||
"required_fields": ["title", "slogan", "price", "ticket_type", "content_items"],
|
"required_fields": ["title", "slogan", "price", "ticket_type", "content_items"],
|
||||||
"optional_fields": ["remarks", "tag", "pagination"]
|
"optional_fields": ["remarks", "tag", "pagination"]
|
||||||
}
|
}
|
||||||
@ -189,7 +189,7 @@ class PosterGenerationResponse(BaseAPIResponse):
|
|||||||
"metadata": {
|
"metadata": {
|
||||||
"image_used": "/data/images/ocean_park.jpg",
|
"image_used": "/data/images/ocean_park.jpg",
|
||||||
"generation_method": "ai_generated",
|
"generation_method": "ai_generated",
|
||||||
"template_size": [900, 1200],
|
"template_size": [1350, 1800],
|
||||||
"processing_time": 3.2
|
"processing_time": 3.2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,7 +243,7 @@ class TemplateListResponse(BaseAPIResponse):
|
|||||||
"id": "vibrant",
|
"id": "vibrant",
|
||||||
"name": "Vibrant活力模板",
|
"name": "Vibrant活力模板",
|
||||||
"description": "适合旅游景点、娱乐活动的活力海报模板",
|
"description": "适合旅游景点、娱乐活动的活力海报模板",
|
||||||
"size": [900, 1200],
|
"size": [1350, 1800],
|
||||||
"required_fields": ["title", "slogan", "price"],
|
"required_fields": ["title", "slogan", "price"],
|
||||||
"optional_fields": ["remarks", "tag"]
|
"optional_fields": ["remarks", "tag"]
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@ -232,7 +232,7 @@ class PosterService:
|
|||||||
# # 3. 图片解码
|
# # 3. 图片解码
|
||||||
images = None
|
images = None
|
||||||
# 获取模板的默认尺寸,如果获取不到则使用标准尺寸
|
# 获取模板的默认尺寸,如果获取不到则使用标准尺寸
|
||||||
template_size = getattr(template_handler, 'size', (900, 1200))
|
template_size = getattr(template_handler, 'size', (1350, 1800))
|
||||||
|
|
||||||
if images_base64 and len(images_base64) > 0:
|
if images_base64 and len(images_base64) > 0:
|
||||||
try:
|
try:
|
||||||
@ -694,7 +694,7 @@ class PosterService:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# 获取图像尺寸
|
# 获取图像尺寸
|
||||||
image_size = [images.width, images.height] if hasattr(images, 'width') else [900, 1200]
|
image_size = [images.width, images.height] if hasattr(images, 'width') else [1350, 1800]
|
||||||
|
|
||||||
# 生成Fabric.js JSON数据
|
# 生成Fabric.js JSON数据
|
||||||
fabric_json = self._generate_fabric_json(content, template_id, image_size, images)
|
fabric_json = self._generate_fabric_json(content, template_id, image_size, images)
|
||||||
|
|||||||
@ -54,7 +54,7 @@ class UnifiedPosterService:
|
|||||||
id=template_id,
|
id=template_id,
|
||||||
name=template_info.get("name", template_id),
|
name=template_info.get("name", template_id),
|
||||||
description=template_info.get("description", ""),
|
description=template_info.get("description", ""),
|
||||||
size=template_info.get("size", [900, 1200]),
|
size=template_info.get("size", [1350, 1800]),
|
||||||
required_fields=template_info.get("required_fields", []),
|
required_fields=template_info.get("required_fields", []),
|
||||||
optional_fields=template_info.get("optional_fields", [])
|
optional_fields=template_info.get("optional_fields", [])
|
||||||
)
|
)
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
"topic_index": 0,
|
"topic_index": 0,
|
||||||
"variant_index": 1,
|
"variant_index": 1,
|
||||||
"template": "business",
|
"template": "business",
|
||||||
"size": [900, 1200],
|
"size": [1350, 1800],
|
||||||
"generate_text": false,
|
"generate_text": false,
|
||||||
"params": {
|
"params": {
|
||||||
"top_image_path": "resource/data/images/hotel1.jpg",
|
"top_image_path": "resource/data/images/hotel1.jpg",
|
||||||
@ -52,7 +52,7 @@
|
|||||||
"topic_index": 1,
|
"topic_index": 1,
|
||||||
"variant_index": 0,
|
"variant_index": 0,
|
||||||
"template": "collage",
|
"template": "collage",
|
||||||
"size": [900, 1200],
|
"size": [1350, 1800],
|
||||||
"generate_text": false,
|
"generate_text": false,
|
||||||
"params": {
|
"params": {
|
||||||
"image_paths": [
|
"image_paths": [
|
||||||
|
|||||||
Binary file not shown.
@ -157,7 +157,7 @@ class AIModelConfig(BaseConfig):
|
|||||||
|
|
||||||
class PosterConfig(BaseConfig):
|
class PosterConfig(BaseConfig):
|
||||||
"""海报生成配置"""
|
"""海报生成配置"""
|
||||||
target_size: List[int] = Field(default_factory=lambda: [900, 1200])
|
target_size: List[int] = Field(default_factory=lambda: [1350, 1800])
|
||||||
additional_images_enabled: bool = True
|
additional_images_enabled: bool = True
|
||||||
template_selection: str = "random" # random, business, vibrant, original
|
template_selection: str = "random" # random, business, vibrant, original
|
||||||
available_templates: List[str] = Field(default_factory=lambda: ["original", "business", "vibrant"])
|
available_templates: List[str] = Field(default_factory=lambda: ["original", "business", "vibrant"])
|
||||||
|
|||||||
@ -1164,7 +1164,7 @@ class ImageProcessor:
|
|||||||
class BaseTemplate(ABC):
|
class BaseTemplate(ABC):
|
||||||
"""海报模板基类"""
|
"""海报模板基类"""
|
||||||
|
|
||||||
def __init__(self, size: Tuple[int, int] = (900, 1200)):
|
def __init__(self, size: Tuple[int, int] = (1350, 1800)):
|
||||||
"""
|
"""
|
||||||
初始化基础模板
|
初始化基础模板
|
||||||
|
|
||||||
@ -1562,7 +1562,7 @@ class BaseTemplate(ABC):
|
|||||||
class VibrantTemplate(BaseTemplate):
|
class VibrantTemplate(BaseTemplate):
|
||||||
"""活力风格海报模板(基于海洋模块)"""
|
"""活力风格海报模板(基于海洋模块)"""
|
||||||
|
|
||||||
def __init__(self, size: Tuple[int, int] = (900, 1200)):
|
def __init__(self, size: Tuple[int, int] = (1350, 1800)):
|
||||||
"""
|
"""
|
||||||
初始化活力模板
|
初始化活力模板
|
||||||
|
|
||||||
@ -2384,7 +2384,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
"备注和标签支持",
|
"备注和标签支持",
|
||||||
"智能字体大小调整"
|
"智能字体大小调整"
|
||||||
],
|
],
|
||||||
"recommended_size": (900, 1200),
|
"recommended_size": (1350, 1800),
|
||||||
"final_size": (1350, 1800),
|
"final_size": (1350, 1800),
|
||||||
"style": "海洋活力风格",
|
"style": "海洋活力风格",
|
||||||
"theme_colors": list(self.ocean_config['ocean_colors'].keys()),
|
"theme_colors": list(self.ocean_config['ocean_colors'].keys()),
|
||||||
@ -2458,12 +2458,12 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
class BusinessTemplate(BaseTemplate):
|
class BusinessTemplate(BaseTemplate):
|
||||||
"""商务风格海报模板(基于酒店模块)"""
|
"""商务风格海报模板(基于酒店模块)"""
|
||||||
|
|
||||||
def __init__(self, size: Tuple[int, int] = (900, 1200)):
|
def __init__(self, size: Tuple[int, int] = (1350, 1800)):
|
||||||
"""
|
"""
|
||||||
初始化商务模板
|
初始化商务模板
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
size: 海报尺寸,默认为酒店模块的尺寸 (900, 1200)
|
size: 海报尺寸,默认为酒店模块的尺寸 (1350, 1800)
|
||||||
"""
|
"""
|
||||||
super().__init__(size)
|
super().__init__(size)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -22,7 +22,7 @@ class BaseTemplate(ABC):
|
|||||||
定义了模板的通用接口和基础功能。
|
定义了模板的通用接口和基础功能。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, size: Tuple[int, int] = (900, 1200), font_dir: str = "/root/autodl-tmp/TCC_RESTRUCT/assets/font"):
|
def __init__(self, size: Tuple[int, int] = (1350, 1800), font_dir: str = "/root/autodl-tmp/TCC_RESTRUCT/assets/font"):
|
||||||
self.width, self.height = size
|
self.width, self.height = size
|
||||||
self.size = size
|
self.size = size
|
||||||
self.image_processor = ImageProcessor()
|
self.image_processor = ImageProcessor()
|
||||||
|
|||||||
@ -26,7 +26,7 @@ class BusinessTemplate(BaseTemplate):
|
|||||||
特点是上下图片融合,中间为信息展示区,整体风格沉稳、专业。
|
特点是上下图片融合,中间为信息展示区,整体风格沉稳、专业。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, size: Tuple[int, int] = (900, 1200)):
|
def __init__(self, size: Tuple[int, int] = (1350, 1800)):
|
||||||
super().__init__(size)
|
super().__init__(size)
|
||||||
self.config = {
|
self.config = {
|
||||||
'total_parts': 4.0,
|
'total_parts': 4.0,
|
||||||
|
|||||||
@ -22,7 +22,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
活力风格模板,适用于色彩鲜艳、内容丰富的场景,如旅游、美食分享。
|
活力风格模板,适用于色彩鲜艳、内容丰富的场景,如旅游、美食分享。
|
||||||
特点是底部有毛玻璃效果的文案区域。
|
特点是底部有毛玻璃效果的文案区域。
|
||||||
"""
|
"""
|
||||||
def __init__(self, size: Tuple[int, int] = (900, 1200)):
|
def __init__(self, size: Tuple[int, int] = (1350, 1800)):
|
||||||
super().__init__(size)
|
super().__init__(size)
|
||||||
self.config = {
|
self.config = {
|
||||||
'colors': {
|
'colors': {
|
||||||
@ -38,7 +38,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
'glass_effect': {
|
'glass_effect': {
|
||||||
'max_opacity': 240,
|
'max_opacity': 240,
|
||||||
'blur_radius': 22,
|
'blur_radius': 22,
|
||||||
'transition_height': 80,
|
'transition_height': 120,
|
||||||
'intensity_multiplier': 1.5
|
'intensity_multiplier': 1.5
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
**kwargs):
|
**kwargs):
|
||||||
"""
|
"""
|
||||||
生成Vibrant风格海报,支持统一渲染
|
生成Vibrant风格海报,支持统一渲染
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
images (List): 主图
|
images (List): 主图
|
||||||
content (Optional[Dict[str, Any]]): 包含所有文本信息的字典
|
content (Optional[Dict[str, Any]]): 包含所有文本信息的字典
|
||||||
@ -148,8 +148,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
canvas = self._create_composite_image(main_image, gradient_start, theme_color)
|
canvas = self._create_composite_image(main_image, gradient_start, theme_color)
|
||||||
canvas = self._render_texts(canvas, content, gradient_start)
|
canvas = self._render_texts(canvas, content, gradient_start)
|
||||||
|
|
||||||
final_image = canvas.resize((1350, 1800), Image.LANCZOS)
|
return canvas
|
||||||
return final_image
|
|
||||||
|
|
||||||
def _get_default_content(self) -> Dict[str, Any]:
|
def _get_default_content(self) -> Dict[str, Any]:
|
||||||
"""获取默认的测试内容"""
|
"""获取默认的测试内容"""
|
||||||
@ -174,24 +173,24 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
|
|
||||||
def _estimate_content_height(self, content: Dict[str, Any]) -> int:
|
def _estimate_content_height(self, content: Dict[str, Any]) -> int:
|
||||||
"""预估内容高度"""
|
"""预估内容高度"""
|
||||||
standard_margin = 25
|
standard_margin = 38
|
||||||
title_height = 100
|
title_height = 150
|
||||||
subtitle_height = 80
|
subtitle_height = 120
|
||||||
button_height = 40
|
button_height = 60
|
||||||
content_items = content.get("content_items", [])
|
content_items = content.get("content_items", [])
|
||||||
content_line_height = 32
|
content_line_height = 48
|
||||||
content_list_height = len(content_items) * content_line_height
|
content_list_height = len(content_items) * content_line_height
|
||||||
price_height = 90
|
price_height = 135
|
||||||
ticket_height = 60
|
ticket_height = 90
|
||||||
remarks = content.get("remarks", [])
|
remarks = content.get("remarks", [])
|
||||||
if isinstance(remarks, str):
|
if isinstance(remarks, str):
|
||||||
remarks = [remarks]
|
remarks = [remarks]
|
||||||
remarks_height = len(remarks) * 25 + 10
|
remarks_height = len(remarks) * 38 + 15
|
||||||
footer_height = 40
|
footer_height = 60
|
||||||
total_height = (
|
total_height = (
|
||||||
20 + title_height + standard_margin + subtitle_height + standard_margin +
|
20 + title_height + standard_margin + subtitle_height + standard_margin +
|
||||||
button_height + 15 + content_list_height + price_height + ticket_height +
|
button_height + 23 + content_list_height + price_height + ticket_height +
|
||||||
remarks_height + footer_height + 30
|
remarks_height + footer_height + 45
|
||||||
)
|
)
|
||||||
return total_height
|
return total_height
|
||||||
|
|
||||||
@ -211,7 +210,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
if gradient_start is None:
|
if gradient_start is None:
|
||||||
bottom_margin = 60
|
bottom_margin = 90
|
||||||
gradient_start = max(height - estimated_height - bottom_margin, height // 2)
|
gradient_start = max(height - estimated_height - bottom_margin, height // 2)
|
||||||
return gradient_start
|
return gradient_start
|
||||||
|
|
||||||
@ -297,7 +296,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
return top_color, bottom_color
|
return top_color, bottom_color
|
||||||
|
|
||||||
def _calculate_optimal_font_size_enhanced(self, text: str, target_width: int,
|
def _calculate_optimal_font_size_enhanced(self, text: str, target_width: int,
|
||||||
max_size: int = 120, min_size: int = 10) -> Tuple[int, int]:
|
max_size: int = 180, min_size: int = 15) -> Tuple[int, int]:
|
||||||
"""
|
"""
|
||||||
计算文本的最佳字体大小,使其宽度接近目标宽度(增强版本,与demo一致)
|
计算文本的最佳字体大小,使其宽度接近目标宽度(增强版本,与demo一致)
|
||||||
|
|
||||||
@ -382,7 +381,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
|
|
||||||
left_margin, right_margin = self._calculate_content_margins(content, width, center_x)
|
left_margin, right_margin = self._calculate_content_margins(content, width, center_x)
|
||||||
|
|
||||||
footer_y = height - 30
|
footer_y = height - 45
|
||||||
self._render_footer(draw, content, footer_y, left_margin, right_margin)
|
self._render_footer(draw, content, footer_y, left_margin, right_margin)
|
||||||
|
|
||||||
title_y = gradient_start + 40
|
title_y = gradient_start + 40
|
||||||
@ -404,7 +403,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
title_text = content.get("title", "")
|
title_text = content.get("title", "")
|
||||||
title_target_width = int(width * 0.95)
|
title_target_width = int(width * 0.95)
|
||||||
title_size, title_width = self._calculate_optimal_font_size_enhanced(
|
title_size, title_width = self._calculate_optimal_font_size_enhanced(
|
||||||
title_text, title_target_width, min_size=40, max_size=130
|
title_text, title_target_width, min_size=60, max_size=195
|
||||||
)
|
)
|
||||||
title_x = center_x - title_width // 2
|
title_x = center_x - title_width // 2
|
||||||
|
|
||||||
@ -412,26 +411,26 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
slogan_text = content.get("slogan", "")
|
slogan_text = content.get("slogan", "")
|
||||||
subtitle_target_width = int(width * 0.9)
|
subtitle_target_width = int(width * 0.9)
|
||||||
subtitle_size, subtitle_width = self._calculate_optimal_font_size_enhanced(
|
subtitle_size, subtitle_width = self._calculate_optimal_font_size_enhanced(
|
||||||
slogan_text, subtitle_target_width, max_size=50, min_size=20
|
slogan_text, subtitle_target_width, max_size=75, min_size=30
|
||||||
)
|
)
|
||||||
subtitle_x = center_x - subtitle_width // 2
|
subtitle_x = center_x - subtitle_width // 2
|
||||||
|
|
||||||
# 计算内容区域边距 - 减小额外的边距,让内容区域更宽
|
# 计算内容区域边距 - 减小额外的边距,让内容区域更宽
|
||||||
padding = 20 # 从30减小到20
|
padding = 30 # 从30减小到20,现在按1.5倍调整到30
|
||||||
content_left_margin = min(title_x, subtitle_x) - padding
|
content_left_margin = min(title_x, subtitle_x) - padding
|
||||||
content_right_margin = max(title_x + title_width, subtitle_x + subtitle_width) + padding
|
content_right_margin = max(title_x + title_width, subtitle_x + subtitle_width) + padding
|
||||||
|
|
||||||
# 确保边距不超出合理范围,但允许更宽的内容区域
|
# 确保边距不超出合理范围,但允许更宽的内容区域
|
||||||
content_left_margin = max(40, content_left_margin)
|
content_left_margin = max(60, content_left_margin)
|
||||||
content_right_margin = min(width - 40, content_right_margin)
|
content_right_margin = min(width - 60, content_right_margin)
|
||||||
|
|
||||||
# 如果内容区域太窄,强制使用更宽的区域
|
# 如果内容区域太窄,强制使用更宽的区域
|
||||||
min_content_width = int(width * 0.75) # 至少使用75%的宽度
|
min_content_width = int(width * 0.75) # 至少使用75%的宽度
|
||||||
current_width = content_right_margin - content_left_margin
|
current_width = content_right_margin - content_left_margin
|
||||||
if current_width < min_content_width:
|
if current_width < min_content_width:
|
||||||
extra_width = min_content_width - current_width
|
extra_width = min_content_width - current_width
|
||||||
content_left_margin = max(30, content_left_margin - extra_width // 2)
|
content_left_margin = max(45, content_left_margin - extra_width // 2)
|
||||||
content_right_margin = min(width - 30, content_right_margin + extra_width // 2)
|
content_right_margin = min(width - 45, content_right_margin + extra_width // 2)
|
||||||
|
|
||||||
return content_left_margin, content_right_margin
|
return content_left_margin, content_right_margin
|
||||||
|
|
||||||
@ -450,7 +449,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
title_text = content.get("title", "默认标题")
|
title_text = content.get("title", "默认标题")
|
||||||
title_target_width = int((right - left) * 0.98)
|
title_target_width = int((right - left) * 0.98)
|
||||||
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
title_text, title_target_width, max_size=140, min_size=40
|
title_text, title_target_width, max_size=210, min_size=60
|
||||||
)
|
)
|
||||||
title_font = self.text_renderer._load_default_font(title_size)
|
title_font = self.text_renderer._load_default_font(title_size)
|
||||||
|
|
||||||
@ -463,14 +462,14 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
subtitle_text = content.get("slogan", "")
|
subtitle_text = content.get("slogan", "")
|
||||||
subtitle_target_width = int((right - left) * 0.95)
|
subtitle_target_width = int((right - left) * 0.95)
|
||||||
subtitle_size, subtitle_actual_width = self._calculate_optimal_font_size_enhanced(
|
subtitle_size, subtitle_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
subtitle_text, subtitle_target_width, max_size=75, min_size=20
|
subtitle_text, subtitle_target_width, max_size=113, min_size=30
|
||||||
)
|
)
|
||||||
subtitle_font = self.text_renderer._load_default_font(subtitle_size)
|
subtitle_font = self.text_renderer._load_default_font(subtitle_size)
|
||||||
|
|
||||||
sub_text_w, sub_text_h = self.text_renderer.get_text_size(subtitle_text, subtitle_font)
|
sub_text_w, sub_text_h = self.text_renderer.get_text_size(subtitle_text, subtitle_font)
|
||||||
subtitle_x = center_x - sub_text_w // 2
|
subtitle_x = center_x - sub_text_w // 2
|
||||||
|
|
||||||
title_spacing = 30
|
title_spacing = 45
|
||||||
|
|
||||||
# 在标题下方添加装饰线
|
# 在标题下方添加装饰线
|
||||||
line_y = y + text_h + 5
|
line_y = y + text_h + 5
|
||||||
@ -490,7 +489,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
button_text = content.get("content_button", "套餐内容")
|
button_text = content.get("content_button", "套餐内容")
|
||||||
button_width, _ = self.text_renderer.get_text_size(button_text, button_font)
|
button_width, _ = self.text_renderer.get_text_size(button_text, button_font)
|
||||||
button_width += 40
|
button_width += 40
|
||||||
button_height = 50
|
button_height = 75
|
||||||
self.text_renderer.draw_rounded_rectangle(draw=draw, position=(x, y), size=(button_width, button_height), radius=20, fill_color=(0, 140, 210, 180), outline_color=(255, 255, 255, 255), outline_width=1)
|
self.text_renderer.draw_rounded_rectangle(draw=draw, position=(x, y), size=(button_width, button_height), radius=20, fill_color=(0, 140, 210, 180), outline_color=(255, 255, 255, 255), outline_width=1)
|
||||||
draw.text((x + 20, y + (button_height - 30) // 2), button_text, font=button_font, fill=(255, 255, 255))
|
draw.text((x + 20, y + (button_height - 30) // 2), button_text, font=button_font, fill=(255, 255, 255))
|
||||||
|
|
||||||
@ -513,7 +512,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
price_text = str(content.get('price', '')) # 确保价格是字符串类型
|
price_text = str(content.get('price', '')) # 确保价格是字符串类型
|
||||||
price_target_width = int((right_margin - x) * 0.7)
|
price_target_width = int((right_margin - x) * 0.7)
|
||||||
price_size, price_actual_width = self._calculate_optimal_font_size_enhanced(
|
price_size, price_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
price_text, price_target_width, max_size=120, min_size=40
|
price_text, price_target_width, max_size=180, min_size=60
|
||||||
)
|
)
|
||||||
|
|
||||||
price_font = self.text_renderer._load_default_font(price_size)
|
price_font = self.text_renderer._load_default_font(price_size)
|
||||||
@ -533,7 +532,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
ticket_text = content.get("ticket_type", "")
|
ticket_text = content.get("ticket_type", "")
|
||||||
ticket_target_width = int((right_margin - x) * 0.7)
|
ticket_target_width = int((right_margin - x) * 0.7)
|
||||||
ticket_size, ticket_actual_width = self._calculate_optimal_font_size_enhanced(
|
ticket_size, ticket_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
ticket_text, ticket_target_width, max_size=60, min_size=30
|
ticket_text, ticket_target_width, max_size=90, min_size=45
|
||||||
)
|
)
|
||||||
ticket_font = self.text_renderer._load_default_font(ticket_size)
|
ticket_font = self.text_renderer._load_default_font(ticket_size)
|
||||||
ticket_x = right_margin - ticket_actual_width
|
ticket_x = right_margin - ticket_actual_width
|
||||||
@ -596,10 +595,10 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
# === 第一步:创建与常规模式完全相同的最终结果作为Reference ===
|
# === 第一步:创建与常规模式完全相同的最终结果作为Reference ===
|
||||||
canvas = self._create_composite_image(main_image, gradient_start, theme_color)
|
canvas = self._create_composite_image(main_image, gradient_start, theme_color)
|
||||||
canvas = self._render_texts(canvas, content, gradient_start)
|
canvas = self._render_texts(canvas, content, gradient_start)
|
||||||
final_canvas = canvas.resize((1350, 1800), Image.LANCZOS)
|
final_canvas = canvas
|
||||||
|
|
||||||
# === 第二步:为PSD创建可编辑的图层版本 ===
|
# === 第二步:为PSD创建可编辑的图层版本 ===
|
||||||
final_size = (1350, 1800)
|
final_size = self.size
|
||||||
psd = PSDImage.new("RGBA", final_size, color=(0, 0, 0, 0))
|
psd = PSDImage.new("RGBA", final_size, color=(0, 0, 0, 0))
|
||||||
logger.info(f"创建PSD文档,尺寸: {final_size}")
|
logger.info(f"创建PSD文档,尺寸: {final_size}")
|
||||||
|
|
||||||
@ -690,7 +689,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
|
|
||||||
# 4. 左栏内容层
|
# 4. 左栏内容层
|
||||||
title_y = gradient_start + 40
|
title_y = gradient_start + 40
|
||||||
subtitle_height = 80 + 30 # 预估副标题高度
|
subtitle_height = 120 + 30 # 预估副标题高度
|
||||||
content_start_y = title_y + subtitle_height + 30
|
content_start_y = title_y + subtitle_height + 30
|
||||||
content_area_width = right_margin - left_margin
|
content_area_width = right_margin - left_margin
|
||||||
left_column_width = int(content_area_width * 0.5)
|
left_column_width = int(content_area_width * 0.5)
|
||||||
@ -702,7 +701,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
layers["Right Content"] = self._create_right_column_layer(content, content_start_y, right_column_x, right_margin, canvas_size)
|
layers["Right Content"] = self._create_right_column_layer(content, content_start_y, right_column_x, right_margin, canvas_size)
|
||||||
|
|
||||||
# 6. 页脚层
|
# 6. 页脚层
|
||||||
footer_y = height - 30
|
footer_y = height - 45
|
||||||
layers["Footer Info"] = self._create_footer_layer(content, footer_y, left_margin, right_margin, canvas_size)
|
layers["Footer Info"] = self._create_footer_layer(content, footer_y, left_margin, right_margin, canvas_size)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -724,7 +723,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
left_margin, right_margin = self._calculate_content_margins(content, width, center_x)
|
left_margin, right_margin = self._calculate_content_margins(content, width, center_x)
|
||||||
|
|
||||||
# 页脚
|
# 页脚
|
||||||
footer_y = height - 30
|
footer_y = height - 45
|
||||||
self._render_footer(draw, content, footer_y, left_margin, right_margin)
|
self._render_footer(draw, content, footer_y, left_margin, right_margin)
|
||||||
|
|
||||||
# 标题和副标题
|
# 标题和副标题
|
||||||
@ -759,7 +758,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
left_margin, right_margin = self._calculate_content_margins(content, width, center_x)
|
left_margin, right_margin = self._calculate_content_margins(content, width, center_x)
|
||||||
|
|
||||||
# 页脚
|
# 页脚
|
||||||
footer_y = height - 30
|
footer_y = height - 45
|
||||||
self._render_footer(draw, content, footer_y, left_margin, right_margin)
|
self._render_footer(draw, content, footer_y, left_margin, right_margin)
|
||||||
|
|
||||||
# 标题和副标题
|
# 标题和副标题
|
||||||
@ -797,7 +796,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
title_text = content.get("title", "默认标题")
|
title_text = content.get("title", "默认标题")
|
||||||
title_target_width = int((right_margin - left_margin) * 0.98)
|
title_target_width = int((right_margin - left_margin) * 0.98)
|
||||||
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
title_text, title_target_width, max_size=140, min_size=40
|
title_text, title_target_width, max_size=210, min_size=60
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
title_font = ImageFont.truetype(self.chinese_font_path, title_size)
|
title_font = ImageFont.truetype(self.chinese_font_path, title_size)
|
||||||
@ -818,7 +817,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
if subtitle_text:
|
if subtitle_text:
|
||||||
subtitle_target_width = int((right_margin - left_margin) * 0.95)
|
subtitle_target_width = int((right_margin - left_margin) * 0.95)
|
||||||
subtitle_size, subtitle_actual_width = self._calculate_optimal_font_size_enhanced(
|
subtitle_size, subtitle_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
subtitle_text, subtitle_target_width, max_size=75, min_size=20
|
subtitle_text, subtitle_target_width, max_size=113, min_size=30
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
subtitle_font = ImageFont.truetype(self.chinese_font_path, subtitle_size)
|
subtitle_font = ImageFont.truetype(self.chinese_font_path, subtitle_size)
|
||||||
@ -836,7 +835,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
footer_canvas = Image.new('RGBA', canvas_size, (0, 0, 0, 0))
|
footer_canvas = Image.new('RGBA', canvas_size, (0, 0, 0, 0))
|
||||||
footer_draw = ImageDraw.Draw(footer_canvas)
|
footer_draw = ImageDraw.Draw(footer_canvas)
|
||||||
|
|
||||||
footer_y = height - 30
|
footer_y = height - 45
|
||||||
try:
|
try:
|
||||||
footer_font = ImageFont.truetype(self.chinese_font_path, 18)
|
footer_font = ImageFont.truetype(self.chinese_font_path, 18)
|
||||||
except:
|
except:
|
||||||
@ -899,7 +898,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
|
|
||||||
# 使用指定的中文字体
|
# 使用指定的中文字体
|
||||||
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
title_text, title_target_width, max_size=140, min_size=40
|
title_text, title_target_width, max_size=210, min_size=60
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -941,7 +940,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
|
|
||||||
subtitle_target_width = int((right_margin - left_margin) * 0.95)
|
subtitle_target_width = int((right_margin - left_margin) * 0.95)
|
||||||
subtitle_size, subtitle_actual_width = self._calculate_optimal_font_size_enhanced(
|
subtitle_size, subtitle_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
subtitle_text, subtitle_target_width, max_size=75, min_size=20
|
subtitle_text, subtitle_target_width, max_size=113, min_size=30
|
||||||
)
|
)
|
||||||
|
|
||||||
# 使用指定的中文字体
|
# 使用指定的中文字体
|
||||||
@ -981,7 +980,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
title_text = content.get("title", "默认标题")
|
title_text = content.get("title", "默认标题")
|
||||||
title_target_width = int((right_margin - left_margin) * 0.98)
|
title_target_width = int((right_margin - left_margin) * 0.98)
|
||||||
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
title_text, title_target_width, max_size=140, min_size=40
|
title_text, title_target_width, max_size=210, min_size=60
|
||||||
)
|
)
|
||||||
|
|
||||||
# 使用中文字体计算标题尺寸
|
# 使用中文字体计算标题尺寸
|
||||||
@ -1027,7 +1026,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
bbox = button_font.getbbox(button_text)
|
bbox = button_font.getbbox(button_text)
|
||||||
button_text_width = bbox[2] - bbox[0]
|
button_text_width = bbox[2] - bbox[0]
|
||||||
button_width = button_text_width + 40
|
button_width = button_text_width + 40
|
||||||
button_height = 50
|
button_height = 75
|
||||||
|
|
||||||
# 绘制简单的矩形按钮
|
# 绘制简单的矩形按钮
|
||||||
draw.rounded_rectangle([x, y, x + button_width, y + button_height],
|
draw.rounded_rectangle([x, y, x + button_width, y + button_height],
|
||||||
@ -1070,7 +1069,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
if price_text:
|
if price_text:
|
||||||
price_target_width = int((right_margin - x) * 0.7)
|
price_target_width = int((right_margin - x) * 0.7)
|
||||||
price_size, price_actual_width = self._calculate_optimal_font_size_enhanced(
|
price_size, price_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
price_text, price_target_width, max_size=120, min_size=40
|
price_text, price_target_width, max_size=180, min_size=60
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -1101,7 +1100,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
if ticket_text:
|
if ticket_text:
|
||||||
ticket_target_width = int((right_margin - x) * 0.7)
|
ticket_target_width = int((right_margin - x) * 0.7)
|
||||||
ticket_size, ticket_actual_width = self._calculate_optimal_font_size_enhanced(
|
ticket_size, ticket_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
ticket_text, ticket_target_width, max_size=60, min_size=30
|
ticket_text, ticket_target_width, max_size=90, min_size=45
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -1228,7 +1227,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
title_text = content.get("title", "")
|
title_text = content.get("title", "")
|
||||||
title_target_width = int((right_margin - left_margin) * 0.98)
|
title_target_width = int((right_margin - left_margin) * 0.98)
|
||||||
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
title_text, title_target_width, max_size=140, min_size=40
|
title_text, title_target_width, max_size=210, min_size=60
|
||||||
)
|
)
|
||||||
title_x = center_x - title_actual_width // 2
|
title_x = center_x - title_actual_width // 2
|
||||||
title_y = gradient_start + 40
|
title_y = gradient_start + 40
|
||||||
@ -1237,7 +1236,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
subtitle_text = content.get("slogan", "")
|
subtitle_text = content.get("slogan", "")
|
||||||
subtitle_target_width = int((right_margin - left_margin) * 0.95)
|
subtitle_target_width = int((right_margin - left_margin) * 0.95)
|
||||||
subtitle_size, subtitle_actual_width = self._calculate_optimal_font_size_enhanced(
|
subtitle_size, subtitle_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
subtitle_text, subtitle_target_width, max_size=75, min_size=20
|
subtitle_text, subtitle_target_width, max_size=113, min_size=30
|
||||||
)
|
)
|
||||||
subtitle_x = center_x - subtitle_actual_width // 2
|
subtitle_x = center_x - subtitle_actual_width // 2
|
||||||
subtitle_y = title_y + 100 + 30 # 标题高度 + 间距
|
subtitle_y = title_y + 100 + 30 # 标题高度 + 间距
|
||||||
@ -1252,14 +1251,14 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
price_text = str(content.get('price', ''))
|
price_text = str(content.get('price', ''))
|
||||||
price_target_width = int((right_margin - right_column_x) * 0.7)
|
price_target_width = int((right_margin - right_column_x) * 0.7)
|
||||||
price_size, price_actual_width = self._calculate_optimal_font_size_enhanced(
|
price_size, price_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
price_text, price_target_width, max_size=120, min_size=40
|
price_text, price_target_width, max_size=180, min_size=60
|
||||||
)
|
)
|
||||||
|
|
||||||
# 票种布局计算
|
# 票种布局计算
|
||||||
ticket_text = content.get("ticket_type", "")
|
ticket_text = content.get("ticket_type", "")
|
||||||
ticket_target_width = int((right_margin - right_column_x) * 0.7)
|
ticket_target_width = int((right_margin - right_column_x) * 0.7)
|
||||||
ticket_size, ticket_actual_width = self._calculate_optimal_font_size_enhanced(
|
ticket_size, ticket_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||||
ticket_text, ticket_target_width, max_size=60, min_size=30
|
ticket_text, ticket_target_width, max_size=90, min_size=45
|
||||||
)
|
)
|
||||||
|
|
||||||
layout_params = {
|
layout_params = {
|
||||||
@ -1315,10 +1314,9 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
"""使用统一布局参数渲染PNG"""
|
"""使用统一布局参数渲染PNG"""
|
||||||
canvas = self._create_composite_image(main_image, gradient_start, theme_color)
|
canvas = self._create_composite_image(main_image, gradient_start, theme_color)
|
||||||
canvas = self._render_texts(canvas, content, gradient_start)
|
canvas = self._render_texts(canvas, content, gradient_start)
|
||||||
final_image = canvas.resize((1350, 1800), Image.LANCZOS)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"image": final_image,
|
"image": canvas,
|
||||||
"layout_params": layout_params,
|
"layout_params": layout_params,
|
||||||
"format": "png"
|
"format": "png"
|
||||||
}
|
}
|
||||||
@ -1327,7 +1325,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
theme_color: Optional[str], gradient_start: int,
|
theme_color: Optional[str], gradient_start: int,
|
||||||
layout_params: Dict[str, Any]) -> Dict[str, Any]:
|
layout_params: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""使用统一布局参数渲染JSON"""
|
"""使用统一布局参数渲染JSON"""
|
||||||
final_width, final_height = 1350, 1800
|
final_width, final_height = self.size
|
||||||
fabric_objects = []
|
fabric_objects = []
|
||||||
|
|
||||||
# 1. 背景图片
|
# 1. 背景图片
|
||||||
@ -1454,7 +1452,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
"height": 100,
|
"height": 100,
|
||||||
"text": layout_params["title_text"],
|
"text": layout_params["title_text"],
|
||||||
"fontSize": layout_params["title_size"],
|
"fontSize": layout_params["title_size"],
|
||||||
"fontFamily": "Arial Black",
|
"fontFamily": "兰亭粗黑简",
|
||||||
"fontWeight": "bold",
|
"fontWeight": "bold",
|
||||||
"fill": "white",
|
"fill": "white",
|
||||||
"textAlign": "center",
|
"textAlign": "center",
|
||||||
@ -1479,7 +1477,7 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
"height": 80,
|
"height": 80,
|
||||||
"text": layout_params["subtitle_text"],
|
"text": layout_params["subtitle_text"],
|
||||||
"fontSize": layout_params["subtitle_size"],
|
"fontSize": layout_params["subtitle_size"],
|
||||||
"fontFamily": "Arial",
|
"fontFamily": "兰亭粗黑简",
|
||||||
"fill": "white",
|
"fill": "white",
|
||||||
"textAlign": "center",
|
"textAlign": "center",
|
||||||
"lineHeight": 1.3,
|
"lineHeight": 1.3,
|
||||||
@ -1511,9 +1509,9 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
"y2": 0,
|
"y2": 0,
|
||||||
"stroke": "rgba(215, 215, 215, 0.3)",
|
"stroke": "rgba(215, 215, 215, 0.3)",
|
||||||
"strokeWidth": 3,
|
"strokeWidth": 3,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"selectable": False,
|
"selectable": False,
|
||||||
"evented": False
|
"evented": False
|
||||||
@ -1533,33 +1531,33 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
"strokeWidth": 1,
|
"strokeWidth": 1,
|
||||||
"rx": 20,
|
"rx": 20,
|
||||||
"ry": 20,
|
"ry": 20,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"selectable": False,
|
"selectable": False,
|
||||||
"evented": False
|
"evented": False
|
||||||
}
|
}
|
||||||
text_objects.append(button_obj)
|
text_objects.append(button_obj)
|
||||||
|
|
||||||
button_text_obj = {
|
button_text_obj = {
|
||||||
"type": "textbox",
|
"type": "textbox",
|
||||||
"left": layout_params["left_margin"] + 20,
|
"left": layout_params["left_margin"] + 20,
|
||||||
"top": layout_params["content_start_y"] + 10,
|
"top": layout_params["content_start_y"] + 10,
|
||||||
"width": 160,
|
"width": 160,
|
||||||
"height": 30,
|
"height": 30,
|
||||||
"text": button_text,
|
"text": button_text,
|
||||||
"fontSize": 30,
|
"fontSize": 45,
|
||||||
"fontFamily": "Arial",
|
"fontFamily": "兰亭粗黑简",
|
||||||
"fontWeight": "bold",
|
"fontWeight": "bold",
|
||||||
"fill": "white",
|
"fill": "white",
|
||||||
"textAlign": "center",
|
"textAlign": "center",
|
||||||
"lineHeight": 1.0,
|
"lineHeight": 1.0,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1
|
"scaleY": 1
|
||||||
}
|
}
|
||||||
text_objects.append(button_text_obj)
|
text_objects.append(button_text_obj)
|
||||||
|
|
||||||
# 5. 左栏内容列表
|
# 5. 左栏内容列表
|
||||||
@ -1573,14 +1571,14 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
"width": layout_params["left_column_width"],
|
"width": layout_params["left_column_width"],
|
||||||
"height": 35,
|
"height": 35,
|
||||||
"text": f"• {item}",
|
"text": f"• {item}",
|
||||||
"fontSize": 28,
|
"fontSize": 42,
|
||||||
"fontFamily": "Arial",
|
"fontFamily": "兰亭粗黑简",
|
||||||
"fill": "white",
|
"fill": "white",
|
||||||
"textAlign": "left",
|
"textAlign": "left",
|
||||||
"lineHeight": 1.2,
|
"lineHeight": 1.2,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1
|
"scaleY": 1
|
||||||
}
|
}
|
||||||
text_objects.append(item_obj)
|
text_objects.append(item_obj)
|
||||||
@ -1596,15 +1594,15 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
"height": 80,
|
"height": 80,
|
||||||
"text": layout_params["price_text"],
|
"text": layout_params["price_text"],
|
||||||
"fontSize": layout_params["price_size"],
|
"fontSize": layout_params["price_size"],
|
||||||
"fontFamily": "Arial Black",
|
"fontFamily": "兰亭粗黑简",
|
||||||
"fontWeight": "bold",
|
"fontWeight": "bold",
|
||||||
"fill": "white",
|
"fill": "white",
|
||||||
"textAlign": "right",
|
"textAlign": "right",
|
||||||
"lineHeight": 1.0,
|
"lineHeight": 1.0,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"shadow": {
|
"shadow": {
|
||||||
"color": "rgba(0, 0, 0, 0.5)",
|
"color": "rgba(0, 0, 0, 0.5)",
|
||||||
"blur": 2,
|
"blur": 2,
|
||||||
@ -1616,20 +1614,20 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
|
|
||||||
# CNY起后缀
|
# CNY起后缀
|
||||||
suffix_obj = {
|
suffix_obj = {
|
||||||
"type": "textbox",
|
"type": "textbox",
|
||||||
"left": price_x + layout_params["price_width"],
|
"left": price_x + layout_params["price_width"],
|
||||||
"top": layout_params["content_start_y"] + 50,
|
"top": layout_params["content_start_y"] + 50,
|
||||||
"width": 60,
|
"width": 60,
|
||||||
"height": 30,
|
"height": 30,
|
||||||
"text": "CNY起",
|
"text": "CNY起",
|
||||||
"fontSize": int(layout_params["price_size"] * 0.3),
|
"fontSize": int(layout_params["price_size"] * 0.3),
|
||||||
"fontFamily": "Arial",
|
"fontFamily": "兰亭粗黑简",
|
||||||
"fill": "white",
|
"fill": "white",
|
||||||
"textAlign": "left",
|
"textAlign": "left",
|
||||||
"lineHeight": 1.0,
|
"lineHeight": 1.0,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1
|
"scaleY": 1
|
||||||
}
|
}
|
||||||
text_objects.append(suffix_obj)
|
text_objects.append(suffix_obj)
|
||||||
@ -1646,10 +1644,10 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
"y2": 0,
|
"y2": 0,
|
||||||
"stroke": "rgba(255, 255, 255, 0.3)",
|
"stroke": "rgba(255, 255, 255, 0.3)",
|
||||||
"strokeWidth": 2,
|
"strokeWidth": 2,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"selectable": False,
|
"selectable": False,
|
||||||
"evented": False
|
"evented": False
|
||||||
}
|
}
|
||||||
@ -1659,22 +1657,22 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
if layout_params["ticket_text"]:
|
if layout_params["ticket_text"]:
|
||||||
ticket_x = layout_params["right_margin"] - layout_params["ticket_width"]
|
ticket_x = layout_params["right_margin"] - layout_params["ticket_width"]
|
||||||
ticket_obj = {
|
ticket_obj = {
|
||||||
"type": "textbox",
|
"type": "textbox",
|
||||||
"left": ticket_x,
|
"left": ticket_x,
|
||||||
"top": layout_params["content_start_y"] + 115,
|
"top": layout_params["content_start_y"] + 115,
|
||||||
"width": layout_params["ticket_width"],
|
"width": layout_params["ticket_width"],
|
||||||
"height": 60,
|
"height": 60,
|
||||||
"text": layout_params["ticket_text"],
|
"text": layout_params["ticket_text"],
|
||||||
"fontSize": layout_params["ticket_size"],
|
"fontSize": layout_params["ticket_size"],
|
||||||
"fontFamily": "Arial",
|
"fontFamily": "兰亭粗黑简",
|
||||||
"fontWeight": "bold",
|
"fontWeight": "bold",
|
||||||
"fill": "white",
|
"fill": "white",
|
||||||
"textAlign": "right",
|
"textAlign": "right",
|
||||||
"lineHeight": 1.0,
|
"lineHeight": 1.0,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1,
|
"scaleY": 1,
|
||||||
"shadow": {
|
"shadow": {
|
||||||
"color": "rgba(0, 0, 0, 0.5)",
|
"color": "rgba(0, 0, 0, 0.5)",
|
||||||
"blur": 2,
|
"blur": 2,
|
||||||
@ -1696,8 +1694,8 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
"width": layout_params["right_margin"] - layout_params["right_column_x"],
|
"width": layout_params["right_margin"] - layout_params["right_column_x"],
|
||||||
"height": 20,
|
"height": 20,
|
||||||
"text": remark,
|
"text": remark,
|
||||||
"fontSize": 16,
|
"fontSize": 24,
|
||||||
"fontFamily": "Arial",
|
"fontFamily": "兰亭粗黑简",
|
||||||
"fill": "rgba(255, 255, 255, 0.8)",
|
"fill": "rgba(255, 255, 255, 0.8)",
|
||||||
"textAlign": "right",
|
"textAlign": "right",
|
||||||
"lineHeight": 1.0,
|
"lineHeight": 1.0,
|
||||||
@ -1712,44 +1710,44 @@ class VibrantTemplate(BaseTemplate):
|
|||||||
footer_y = layout_params["footer_y"]
|
footer_y = layout_params["footer_y"]
|
||||||
if tag := content.get("tag"):
|
if tag := content.get("tag"):
|
||||||
tag_obj = {
|
tag_obj = {
|
||||||
"type": "textbox",
|
"type": "textbox",
|
||||||
"left": layout_params["left_margin"],
|
"left": layout_params["left_margin"],
|
||||||
"top": footer_y,
|
"top": footer_y,
|
||||||
"width": 200,
|
"width": 200,
|
||||||
"height": 25,
|
"height": 25,
|
||||||
"text": tag,
|
"text": tag,
|
||||||
"fontSize": 18,
|
"fontSize": 27,
|
||||||
"fontFamily": "Arial",
|
"fontFamily": "兰亭粗黑简",
|
||||||
"fill": "white",
|
"fill": "white",
|
||||||
"textAlign": "left",
|
"textAlign": "left",
|
||||||
"lineHeight": 1.0,
|
"lineHeight": 1.0,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1
|
"scaleY": 1
|
||||||
}
|
}
|
||||||
text_objects.append(tag_obj)
|
text_objects.append(tag_obj)
|
||||||
|
|
||||||
if pagination := content.get("pagination"):
|
if pagination := content.get("pagination"):
|
||||||
pagination_obj = {
|
pagination_obj = {
|
||||||
"type": "textbox",
|
"type": "textbox",
|
||||||
"left": layout_params["right_margin"] - 200,
|
"left": layout_params["right_margin"] - 200,
|
||||||
"top": footer_y,
|
"top": footer_y,
|
||||||
"width": 200,
|
"width": 200,
|
||||||
"height": 25,
|
"height": 25,
|
||||||
"text": pagination,
|
"text": pagination,
|
||||||
"fontSize": 18,
|
"fontSize": 27,
|
||||||
"fontFamily": "Arial",
|
"fontFamily": "兰亭粗黑简",
|
||||||
"fill": "white",
|
"fill": "white",
|
||||||
"textAlign": "right",
|
"textAlign": "right",
|
||||||
"lineHeight": 1.0,
|
"lineHeight": 1.0,
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"angle": 0,
|
"angle": 0,
|
||||||
"scaleX": 1,
|
"scaleX": 1,
|
||||||
"scaleY": 1
|
"scaleY": 1
|
||||||
}
|
}
|
||||||
text_objects.append(pagination_obj)
|
text_objects.append(pagination_obj)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"创建精确文本对象失败: {e}")
|
logger.error(f"创建精确文本对象失败: {e}")
|
||||||
|
|
||||||
|
|||||||
@ -1040,7 +1040,7 @@ class ImageCollageCreator:
|
|||||||
参数:
|
参数:
|
||||||
input_dir: 输入图片目录路径
|
input_dir: 输入图片目录路径
|
||||||
style: 拼贴样式,如不指定则随机选择
|
style: 拼贴样式,如不指定则随机选择
|
||||||
target_size: 目标尺寸,默认为(900, 1200)
|
target_size: 目标尺寸,默认为(1350, 1800)
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
tuple: (拼贴图, 选择的图片名称列表),如果创建失败则返回(None, [])
|
tuple: (拼贴图, 选择的图片名称列表),如果创建失败则返回(None, [])
|
||||||
@ -1049,7 +1049,7 @@ class ImageCollageCreator:
|
|||||||
try:
|
try:
|
||||||
# 设置默认尺寸为3:4比例
|
# 设置默认尺寸为3:4比例
|
||||||
if target_size is None:
|
if target_size is None:
|
||||||
target_size = (900, 1200) # 3:4比例
|
target_size = (1350, 1800) # 3:4比例
|
||||||
|
|
||||||
# 如果没有指定样式,随机选择一种
|
# 如果没有指定样式,随机选择一种
|
||||||
if style is None or style not in self.collage_styles:
|
if style is None or style not in self.collage_styles:
|
||||||
@ -1914,7 +1914,7 @@ class PosterNotesCreator:
|
|||||||
np.random.seed(seed)
|
np.random.seed(seed)
|
||||||
|
|
||||||
# 调用core.simple_collage模块处理图像
|
# 调用core.simple_collage模块处理图像
|
||||||
target_size = (900, 1200) # 3:4比例
|
target_size = (1350, 1800) # 3:4比例
|
||||||
collage_images, used_image_filenames = process_collage(
|
collage_images, used_image_filenames = process_collage(
|
||||||
temp_dir,
|
temp_dir,
|
||||||
style=style,
|
style=style,
|
||||||
@ -2070,7 +2070,7 @@ class PosterNotesCreator:
|
|||||||
np.random.seed(seed)
|
np.random.seed(seed)
|
||||||
|
|
||||||
# 调用core.simple_collage模块处理图像
|
# 调用core.simple_collage模块处理图像
|
||||||
target_size = (900, 1200) # 3:4比例
|
target_size = (1350, 1800) # 3:4比例
|
||||||
collage_images, used_image_filenames = process_collage(
|
collage_images, used_image_filenames = process_collage(
|
||||||
temp_dir,
|
temp_dir,
|
||||||
style=style,
|
style=style,
|
||||||
@ -3274,13 +3274,13 @@ def select_additional_images(
|
|||||||
collage_style
|
collage_style
|
||||||
)
|
)
|
||||||
|
|
||||||
def process_directory(directory_path, style=None, target_size=(900, 1200), output_count=1):
|
def process_directory(directory_path, style=None, target_size=(1350, 1800), output_count=1):
|
||||||
"""
|
"""
|
||||||
处理指定目录中的图片,创建指定数量的拼贴图。
|
处理指定目录中的图片,创建指定数量的拼贴图。
|
||||||
|
|
||||||
参数:
|
参数:
|
||||||
directory_path: 包含图片的目录路径
|
directory_path: 包含图片的目录路径
|
||||||
target_size: 拼贴图目标尺寸,默认为 (900, 1200)
|
target_size: 拼贴图目标尺寸,默认为 (1350, 1800)
|
||||||
output_count: 需要生成的拼贴图数量,默认为 1
|
output_count: 需要生成的拼贴图数量,默认为 1
|
||||||
|
|
||||||
返回:
|
返回:
|
||||||
@ -3371,7 +3371,7 @@ def main():
|
|||||||
logging.info("方法 1: 使用 process_directory 函数生成拼贴图...")
|
logging.info("方法 1: 使用 process_directory 函数生成拼贴图...")
|
||||||
collages_1, used_image_names_1 = process_directory(
|
collages_1, used_image_names_1 = process_directory(
|
||||||
directory_path=test_directory,
|
directory_path=test_directory,
|
||||||
target_size=(900, 1200), # 默认 3:4 比例
|
target_size=(1350, 1800), # 默认 3:4 比例
|
||||||
output_count=2 # 创建 2 张不同的拼贴图
|
output_count=2 # 创建 2 张不同的拼贴图
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
BIN
test_size_adjustment_result.png
Normal file
BIN
test_size_adjustment_result.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 205 KiB |
Binary file not shown.
@ -185,7 +185,7 @@ class ImageProcessor:
|
|||||||
return image
|
return image
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_collage_from_paths(image_paths: List[str], target_size: Tuple[int, int] = (900, 1200)) -> Optional[Image.Image]:
|
def create_collage_from_paths(image_paths: List[str], target_size: Tuple[int, int] = (1350, 1800)) -> Optional[Image.Image]:
|
||||||
"""
|
"""
|
||||||
从图像路径列表创建拼图
|
从图像路径列表创建拼图
|
||||||
|
|
||||||
@ -257,7 +257,7 @@ class ImageProcessor:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def process_images_for_poster(image_paths: List[str],
|
def process_images_for_poster(image_paths: List[str],
|
||||||
target_size: Tuple[int, int] = (900, 1200),
|
target_size: Tuple[int, int] = (1350, 1800),
|
||||||
create_collage: bool = False) -> Dict[str, Any]:
|
create_collage: bool = False) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
处理图像用于海报生成
|
处理图像用于海报生成
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user