Merge branch 'reinstruct' of http://8.134.70.224:8300/jinye_huang/TravelContentCreator into reinstruct
This commit is contained in:
commit
6c4d10b157
@ -28,7 +28,7 @@ class PosterGenerateRequest(BaseModel):
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"templateId": "vibrant",
|
||||
"imagesBase64": ["base64_encoded_image_1", "base64_encoded_image_2"],
|
||||
"imagesBase64": "",
|
||||
"numVariations": 1,
|
||||
"forceLlmGeneration":False,
|
||||
"generatePsd": True,
|
||||
|
||||
@ -15,7 +15,7 @@ import importlib
|
||||
import base64
|
||||
import binascii
|
||||
from io import BytesIO
|
||||
from typing import List, Dict, Any, Optional, Type, Union, cast, Tuple
|
||||
from typing import List, Dict, Any, Optional, Type, Union, cast
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from PIL import Image
|
||||
@ -234,6 +234,19 @@ class PosterService:
|
||||
# 获取模板的默认尺寸,如果获取不到则使用标准尺寸
|
||||
template_size = getattr(template_handler, 'size', (900, 1200))
|
||||
|
||||
if images_base64 and len(images_base64) > 0:
|
||||
try:
|
||||
logger.info(f"🎯 接收到 {len(images_base64)} 张图片的base64数据")
|
||||
|
||||
# 处理第一张图片(目前模板只支持单张图片)
|
||||
# 未来可以扩展为处理多张图片
|
||||
first_image_base64 = images_base64[0] if len(images_base64) > 0 else ""
|
||||
|
||||
if not first_image_base64 or not first_image_base64.strip():
|
||||
raise ValueError("第一张图片的base64数据为空")
|
||||
|
||||
logger.info(f"📊 处理第一张图片,base64长度: {len(first_image_base64)}")
|
||||
|
||||
if images_base64 and len(images_base64) > 0:
|
||||
try:
|
||||
logger.info(f"🎯 接收到 {len(images_base64)} 张图片的base64数据")
|
||||
@ -277,8 +290,7 @@ class PosterService:
|
||||
elif file_header.startswith(b'\x89PNG'):
|
||||
logger.info("✅ 检测到PNG格式图片")
|
||||
else:
|
||||
logger.warning(f"⚠️ 未识别的图片格式,文件头: {file_header.hex()}")
|
||||
|
||||
logger.warning(f"⚠️ ⚠️ 未识别的图片格式,文件头: {file_header.hex()}")
|
||||
# 创建PIL Image对象
|
||||
image_io = BytesIO(image_bytes)
|
||||
images = Image.open(image_io)
|
||||
@ -703,7 +715,7 @@ class PosterService:
|
||||
|
||||
def _generate_fabric_json(self, content: Dict[str, Any], template_id: str, image_size: List[int], images: Image.Image = None) -> Dict[str, Any]:
|
||||
"""
|
||||
生成与VibrantTemplate视觉效果一致的Fabric.js JSON格式
|
||||
完全复制VibrantTemplate的渲染逻辑生成Fabric.js JSON
|
||||
|
||||
Args:
|
||||
content: 海报内容数据
|
||||
@ -712,33 +724,42 @@ class PosterService:
|
||||
images: 用户上传的图片
|
||||
|
||||
Returns:
|
||||
Dict: 扁平化的Fabric.js JSON格式数据
|
||||
Dict: 完全匹配VibrantTemplate的Fabric.js JSON格式数据
|
||||
"""
|
||||
try:
|
||||
fabric_objects = []
|
||||
|
||||
# 基础画布尺寸(VibrantTemplate使用900x1200,最终resize到1350x1800)
|
||||
canvas_width, canvas_height = image_size[0], image_size[1]
|
||||
|
||||
# 计算缩放比例以匹配VibrantTemplate的最终输出
|
||||
scale_ratio = min(canvas_width / 900, canvas_height / 1200)
|
||||
# VibrantTemplate的基础尺寸(900x1200)
|
||||
base_width, base_height = 900, 1200
|
||||
# 最终输出尺寸(1350x1800)
|
||||
final_width, final_height = image_size[0], image_size[1]
|
||||
|
||||
# 1. 用户上传的图片(最底层 - Level 0)
|
||||
if images and hasattr(images, 'width'):
|
||||
image_object = self._create_vibrant_image_object(images, canvas_width, canvas_height)
|
||||
# 按VibrantTemplate方式缩放到基础尺寸
|
||||
image_object = self._create_vibrant_image_object_precise(images, final_width, final_height)
|
||||
fabric_objects.append(image_object)
|
||||
else:
|
||||
# 占位符
|
||||
placeholder_object = self._create_placeholder_object(canvas_width, canvas_height)
|
||||
placeholder_object = self._create_placeholder_object(final_width, final_height)
|
||||
fabric_objects.append(placeholder_object)
|
||||
|
||||
# 2. 毛玻璃渐变背景(Level 1)
|
||||
gradient_start = self._calculate_gradient_start_position(canvas_height)
|
||||
gradient_object = self._create_gradient_background_object(canvas_width, canvas_height, gradient_start)
|
||||
# 2. 估算内容高度(复制VibrantTemplate逻辑)
|
||||
estimated_height = self._estimate_vibrant_content_height(content)
|
||||
|
||||
# 3. 动态检测渐变起始位置(复制VibrantTemplate逻辑)
|
||||
gradient_start = self._detect_vibrant_gradient_start_position(images, estimated_height, base_height)
|
||||
|
||||
# 4. 提取毛玻璃颜色(复制VibrantTemplate逻辑)
|
||||
glass_colors = self._extract_vibrant_glass_colors(images, gradient_start)
|
||||
|
||||
# 5. 创建精确的毛玻璃效果(Level 1)
|
||||
gradient_object = self._create_vibrant_glass_effect(final_width, final_height, gradient_start, glass_colors)
|
||||
fabric_objects.append(gradient_object)
|
||||
|
||||
# 3. VibrantTemplate风格的文字布局(Level 2)
|
||||
text_objects = self._create_vibrant_text_objects(content, canvas_width, canvas_height, gradient_start, scale_ratio)
|
||||
# 6. 按VibrantTemplate精确位置渲染文字(Level 2)
|
||||
# 缩放渐变起始位置到最终尺寸
|
||||
scaled_gradient_start = int(gradient_start * final_height / base_height)
|
||||
text_objects = self._create_vibrant_text_layout_precise(content, final_width, final_height, scaled_gradient_start)
|
||||
fabric_objects.extend(text_objects)
|
||||
|
||||
# 构建完整的Fabric.js JSON
|
||||
@ -749,8 +770,8 @@ class PosterService:
|
||||
"backgroundImage": None,
|
||||
"overlayImage": None,
|
||||
"clipPath": None,
|
||||
"width": canvas_width,
|
||||
"height": canvas_height,
|
||||
"width": final_width,
|
||||
"height": final_height,
|
||||
"viewportTransform": [1, 0, 0, 1, 0, 0],
|
||||
"backgroundVpt": True,
|
||||
"overlayVpt": True,
|
||||
@ -767,10 +788,19 @@ class PosterService:
|
||||
"perPixelTargetFind": False,
|
||||
"targetFindTolerance": 0,
|
||||
"skipOffscreen": True,
|
||||
"includeDefaultValues": True
|
||||
"includeDefaultValues": True,
|
||||
"metadata": {
|
||||
"template": "VibrantTemplate",
|
||||
"base_size": [base_width, base_height],
|
||||
"final_size": [final_width, final_height],
|
||||
"gradient_start": gradient_start,
|
||||
"scaled_gradient_start": scaled_gradient_start,
|
||||
"estimated_content_height": estimated_height,
|
||||
"glass_colors": glass_colors
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(f"成功生成扁平化Fabric.js JSON,包含 {len(fabric_objects)} 个独立对象")
|
||||
logger.info(f"成功生成VibrantTemplate精确Fabric.js JSON,包含 {len(fabric_objects)} 个对象")
|
||||
return fabric_json
|
||||
|
||||
except Exception as e:
|
||||
@ -1051,7 +1081,7 @@ class PosterService:
|
||||
}
|
||||
|
||||
def _create_gradient_background_object(self, canvas_width: int, canvas_height: int, gradient_start: int) -> Dict[str, Any]:
|
||||
"""创建模拟毛玻璃效果的渐变背景(简化版本,提高兼容性)"""
|
||||
"""创建VibrantTemplate风格的毛玻璃渐变背景"""
|
||||
return {
|
||||
"type": "rect",
|
||||
"version": "5.3.0",
|
||||
@ -1061,19 +1091,33 @@ class PosterService:
|
||||
"top": gradient_start,
|
||||
"width": canvas_width,
|
||||
"height": canvas_height - gradient_start,
|
||||
"fill": "rgba(0, 50, 120, 0.6)", # 简化为单一颜色,提高兼容性
|
||||
"fill": {
|
||||
"type": "linear",
|
||||
"coords": {
|
||||
"x1": 0,
|
||||
"y1": 0,
|
||||
"x2": 0,
|
||||
"y2": canvas_height - gradient_start
|
||||
},
|
||||
"colorStops": [
|
||||
{"offset": 0, "color": "rgba(0, 30, 80, 0.3)"},
|
||||
{"offset": 0.5, "color": "rgba(0, 50, 120, 0.7)"},
|
||||
{"offset": 1, "color": "rgba(0, 30, 80, 0.9)"}
|
||||
]
|
||||
},
|
||||
"stroke": "",
|
||||
"strokeWidth": 0,
|
||||
"angle": 0,
|
||||
"flipX": False,
|
||||
"flipY": False,
|
||||
"opacity": 0.8,
|
||||
"opacity": 0.85,
|
||||
"visible": True,
|
||||
"name": "glass_gradient",
|
||||
"data": {
|
||||
"type": "glass_effect",
|
||||
"layer": "background",
|
||||
"level": 1
|
||||
"level": 1,
|
||||
"effect": "vibrant_glass"
|
||||
},
|
||||
"selectable": False,
|
||||
"evented": False
|
||||
@ -1124,7 +1168,8 @@ class PosterService:
|
||||
"level": 2,
|
||||
"style": "vibrant_title",
|
||||
"target_width": title_target_width,
|
||||
"actual_width": title_actual_width
|
||||
"actual_width": title_actual_width,
|
||||
"font_path": "/assets/font/兰亭粗黑简.TTF"
|
||||
},
|
||||
"selectable": True,
|
||||
"evented": True
|
||||
@ -1196,7 +1241,8 @@ class PosterService:
|
||||
"level": 2,
|
||||
"style": "vibrant_subtitle",
|
||||
"target_width": subtitle_target_width,
|
||||
"actual_width": subtitle_actual_width
|
||||
"actual_width": subtitle_actual_width,
|
||||
"font_path": "/assets/font/兰亭粗黑简.TTF"
|
||||
},
|
||||
"selectable": True,
|
||||
"evented": True
|
||||
@ -1598,7 +1644,8 @@ class PosterService:
|
||||
"layer": "content",
|
||||
"level": 2,
|
||||
"target_width": price_target_width,
|
||||
"actual_width": price_actual_width
|
||||
"actual_width": price_actual_width,
|
||||
"font_path": "/assets/font/兰亭粗黑简.TTF"
|
||||
},
|
||||
"selectable": True,
|
||||
"evented": True
|
||||
@ -1696,3 +1743,307 @@ class PosterService:
|
||||
objects.append(ticket_obj)
|
||||
|
||||
return objects
|
||||
|
||||
def _estimate_vibrant_content_height(self, content: Dict[str, Any]) -> int:
|
||||
"""复制VibrantTemplate的内容高度估算逻辑"""
|
||||
standard_margin = 25
|
||||
title_height = 100
|
||||
subtitle_height = 80
|
||||
button_height = 40
|
||||
content_items = content.get("content_items", [])
|
||||
content_line_height = 32
|
||||
content_list_height = len(content_items) * content_line_height
|
||||
price_height = 90
|
||||
ticket_height = 60
|
||||
remarks = content.get("remarks", [])
|
||||
if isinstance(remarks, str):
|
||||
remarks = [remarks]
|
||||
remarks_height = len(remarks) * 25 + 10
|
||||
footer_height = 40
|
||||
total_height = (
|
||||
20 + title_height + standard_margin + subtitle_height + standard_margin +
|
||||
button_height + 15 + content_list_height + price_height + ticket_height +
|
||||
remarks_height + footer_height + 30
|
||||
)
|
||||
logger.info(f"VibrantTemplate估算内容高度: {total_height}")
|
||||
return total_height
|
||||
|
||||
def _detect_vibrant_gradient_start_position(self, image: Image.Image, estimated_height: int, base_height: int) -> int:
|
||||
"""复制VibrantTemplate的动态渐变起始位置检测"""
|
||||
if not image or not hasattr(image, 'width'):
|
||||
# 如果没有图像,使用估算位置
|
||||
bottom_margin = 60
|
||||
gradient_start = max(base_height - estimated_height - bottom_margin, base_height // 2)
|
||||
logger.info(f"无图像,使用估算渐变起始位置: {gradient_start}")
|
||||
return gradient_start
|
||||
|
||||
# 临时缩放图像到基础尺寸进行分析
|
||||
temp_image = image.resize((900, 1200), Image.LANCZOS)
|
||||
if temp_image.mode != 'RGB':
|
||||
temp_image = temp_image.convert('RGB')
|
||||
|
||||
width, height = temp_image.size
|
||||
center_x = width // 2
|
||||
gradient_start = None
|
||||
|
||||
# 从中央开始扫描,寻找亮度>50的像素
|
||||
for y in range(height // 2, height):
|
||||
try:
|
||||
pixel = temp_image.getpixel((center_x, y))
|
||||
if isinstance(pixel, (tuple, list)) and len(pixel) >= 3:
|
||||
brightness = sum(pixel[:3]) / 3
|
||||
if brightness > 50:
|
||||
gradient_start = max(y - 20, height // 2)
|
||||
logger.info(f"检测到亮度>50的像素位置: y={y}, brightness={brightness:.1f}")
|
||||
break
|
||||
except:
|
||||
continue
|
||||
|
||||
# 如果没有找到合适位置,使用估算位置
|
||||
if gradient_start is None:
|
||||
bottom_margin = 60
|
||||
gradient_start = max(height - estimated_height - bottom_margin, height // 2)
|
||||
logger.info(f"未找到合适像素,使用估算渐变起始位置: {gradient_start}")
|
||||
else:
|
||||
logger.info(f"动态检测到渐变起始位置: {gradient_start}")
|
||||
|
||||
return gradient_start
|
||||
|
||||
def _extract_vibrant_glass_colors(self, image: Image.Image, gradient_start: int) -> Dict[str, Tuple[int, int, int]]:
|
||||
"""复制VibrantTemplate的毛玻璃颜色提取逻辑"""
|
||||
if not image or not hasattr(image, 'width'):
|
||||
# 默认蓝色毛玻璃效果
|
||||
default_colors = {
|
||||
"top_color": (0, 5, 15),
|
||||
"bottom_color": (0, 25, 50)
|
||||
}
|
||||
logger.info(f"无图像,使用默认毛玻璃颜色: {default_colors}")
|
||||
return default_colors
|
||||
|
||||
# 临时缩放图像到基础尺寸进行颜色提取
|
||||
temp_image = image.resize((900, 1200), Image.LANCZOS)
|
||||
if temp_image.mode != 'RGB':
|
||||
temp_image = temp_image.convert('RGB')
|
||||
|
||||
width, height = temp_image.size
|
||||
top_samples, bottom_samples = [], []
|
||||
|
||||
# 在渐变起始位置+20px采样顶部颜色
|
||||
top_y = min(gradient_start + 20, height - 1)
|
||||
for x in range(0, width, 20):
|
||||
try:
|
||||
pixel = temp_image.getpixel((x, top_y))
|
||||
if sum(pixel) > 30: # 排除过暗像素
|
||||
top_samples.append(pixel)
|
||||
except:
|
||||
continue
|
||||
|
||||
# 在底部-50px采样底部颜色
|
||||
bottom_y = min(height - 50, height - 1)
|
||||
for x in range(0, width, 20):
|
||||
try:
|
||||
pixel = temp_image.getpixel((x, bottom_y))
|
||||
if sum(pixel) > 30: # 排除过暗像素
|
||||
bottom_samples.append(pixel)
|
||||
except:
|
||||
continue
|
||||
|
||||
# 计算平均颜色并降低亮度(复制VibrantTemplate逻辑)
|
||||
import numpy as np
|
||||
if top_samples:
|
||||
top_avg = np.mean(top_samples, axis=0)
|
||||
top_color = tuple(max(0, int(c * 0.1)) for c in top_avg) # 10%亮度
|
||||
else:
|
||||
top_color = (0, 5, 15)
|
||||
|
||||
if bottom_samples:
|
||||
bottom_avg = np.mean(bottom_samples, axis=0)
|
||||
bottom_color = tuple(max(0, int(c * 0.2)) for c in bottom_avg) # 20%亮度
|
||||
else:
|
||||
bottom_color = (0, 25, 50)
|
||||
|
||||
colors = {
|
||||
"top_color": top_color,
|
||||
"bottom_color": bottom_color
|
||||
}
|
||||
logger.info(f"提取毛玻璃颜色: 顶部={top_color}({len(top_samples)}样本), 底部={bottom_color}({len(bottom_samples)}样本)")
|
||||
return colors
|
||||
|
||||
def _create_vibrant_glass_effect(self, canvas_width: int, canvas_height: int, gradient_start: int, glass_colors: Dict) -> Dict[str, Any]:
|
||||
"""创建VibrantTemplate精确的毛玻璃效果"""
|
||||
# 缩放渐变起始位置到最终尺寸
|
||||
scaled_gradient_start = int(gradient_start * canvas_height / 1200)
|
||||
|
||||
top_color = glass_colors["top_color"]
|
||||
bottom_color = glass_colors["bottom_color"]
|
||||
|
||||
# 创建复杂的渐变效果,模拟VibrantTemplate的数学公式
|
||||
gradient_stops = []
|
||||
|
||||
# 生成多个渐变停止点以模拟复杂的数学渐变
|
||||
import math
|
||||
for i in range(10):
|
||||
ratio = i / 9 # 0到1
|
||||
# 复制VibrantTemplate的smooth_ratio公式
|
||||
smooth_ratio = 0.5 - 0.5 * math.cos(ratio * math.pi)
|
||||
|
||||
# 插值颜色
|
||||
r = int((1 - smooth_ratio) * top_color[0] + smooth_ratio * bottom_color[0])
|
||||
g = int((1 - smooth_ratio) * top_color[1] + smooth_ratio * bottom_color[1])
|
||||
b = int((1 - smooth_ratio) * top_color[2] + smooth_ratio * bottom_color[2])
|
||||
|
||||
# 复制VibrantTemplate的透明度计算
|
||||
alpha_smooth = ratio ** (1.1 / 1.5) # intensity=1.5
|
||||
alpha = 0.02 + 0.98 * alpha_smooth
|
||||
|
||||
gradient_stops.append({
|
||||
"offset": ratio,
|
||||
"color": f"rgba({r}, {g}, {b}, {alpha:.3f})"
|
||||
})
|
||||
|
||||
logger.info(f"创建毛玻璃效果: 起始位置={scaled_gradient_start}, 渐变点={len(gradient_stops)}")
|
||||
|
||||
return {
|
||||
"type": "rect",
|
||||
"version": "5.3.0",
|
||||
"originX": "left",
|
||||
"originY": "top",
|
||||
"left": 0,
|
||||
"top": scaled_gradient_start,
|
||||
"width": canvas_width,
|
||||
"height": canvas_height - scaled_gradient_start,
|
||||
"fill": {
|
||||
"type": "linear",
|
||||
"coords": {
|
||||
"x1": 0,
|
||||
"y1": 0,
|
||||
"x2": 0,
|
||||
"y2": canvas_height - scaled_gradient_start
|
||||
},
|
||||
"colorStops": gradient_stops
|
||||
},
|
||||
"stroke": "",
|
||||
"strokeWidth": 0,
|
||||
"angle": 0,
|
||||
"flipX": False,
|
||||
"flipY": False,
|
||||
"opacity": 0.85,
|
||||
"visible": True,
|
||||
"name": "vibrant_glass_effect",
|
||||
"data": {
|
||||
"type": "glass_effect",
|
||||
"layer": "background",
|
||||
"level": 1,
|
||||
"effect": "vibrant_template_precise"
|
||||
},
|
||||
"selectable": False,
|
||||
"evented": False
|
||||
}
|
||||
|
||||
def _create_vibrant_image_object_precise(self, images: Image.Image, canvas_width: int, canvas_height: int) -> Dict[str, Any]:
|
||||
"""创建VibrantTemplate精确的图像对象"""
|
||||
# 将PIL图像转换为base64
|
||||
image_base64 = self._image_to_base64(images)
|
||||
|
||||
# VibrantTemplate直接resize到画布大小
|
||||
return {
|
||||
"type": "image",
|
||||
"version": "5.3.0",
|
||||
"originX": "left",
|
||||
"originY": "top",
|
||||
"left": 0,
|
||||
"top": 0,
|
||||
"width": images.width,
|
||||
"height": images.height,
|
||||
"scaleX": canvas_width / images.width,
|
||||
"scaleY": canvas_height / images.height,
|
||||
"angle": 0,
|
||||
"flipX": False,
|
||||
"flipY": False,
|
||||
"opacity": 1,
|
||||
"visible": True,
|
||||
"src": f"data:image/png;base64,{image_base64}",
|
||||
"crossOrigin": "anonymous",
|
||||
"name": "vibrant_background_image",
|
||||
"data": {
|
||||
"type": "background_image",
|
||||
"layer": "image",
|
||||
"level": 0,
|
||||
"replaceable": True
|
||||
},
|
||||
"selectable": True,
|
||||
"evented": True
|
||||
}
|
||||
|
||||
def _create_vibrant_text_layout_precise(self, content: Dict[str, Any], canvas_width: int, canvas_height: int, gradient_start: int) -> List[Dict[str, Any]]:
|
||||
"""复制VibrantTemplate的精确文本布局逻辑"""
|
||||
text_objects = []
|
||||
|
||||
# 计算基础参数(缩放到最终尺寸)
|
||||
center_x = canvas_width // 2
|
||||
scale_factor = canvas_height / 1800 # 从1800缩放到最终高度
|
||||
|
||||
# 简化版本:使用固定边距
|
||||
margin_ratio = 0.1
|
||||
left_margin = int(canvas_width * margin_ratio)
|
||||
right_margin = int(canvas_width * (1 - margin_ratio))
|
||||
|
||||
# 标题(VibrantTemplate: gradient_start + 40)
|
||||
title_y = gradient_start + int(40 * scale_factor)
|
||||
if title := content.get("title"):
|
||||
title_size = int(80 * scale_factor)
|
||||
title_obj = {
|
||||
"type": "textbox",
|
||||
"version": "5.3.0",
|
||||
"originX": "center",
|
||||
"originY": "top",
|
||||
"left": center_x,
|
||||
"top": title_y,
|
||||
"width": right_margin - left_margin,
|
||||
"height": title_size + 20,
|
||||
"fill": "#ffffff",
|
||||
"stroke": "#001e50",
|
||||
"strokeWidth": 4,
|
||||
"fontFamily": "兰亭粗黑简, 微软雅黑, Microsoft YaHei, PingFang SC, Hiragino Sans GB, Arial Black, sans-serif",
|
||||
"fontWeight": "bold",
|
||||
"fontSize": title_size,
|
||||
"text": title,
|
||||
"textAlign": "center",
|
||||
"lineHeight": 1.1,
|
||||
"name": "vibrant_title_precise",
|
||||
"data": {"type": "title", "layer": "content", "level": 2},
|
||||
"selectable": True,
|
||||
"evented": True
|
||||
}
|
||||
text_objects.append(title_obj)
|
||||
|
||||
# 副标题
|
||||
subtitle_y = title_y + int(130 * scale_factor)
|
||||
if slogan := content.get("slogan"):
|
||||
subtitle_size = int(40 * scale_factor)
|
||||
subtitle_obj = {
|
||||
"type": "textbox",
|
||||
"version": "5.3.0",
|
||||
"originX": "center",
|
||||
"originY": "top",
|
||||
"left": center_x,
|
||||
"top": subtitle_y,
|
||||
"width": right_margin - left_margin,
|
||||
"height": subtitle_size + 15,
|
||||
"fill": "#ffffff",
|
||||
"shadow": "rgba(0, 0, 0, 0.7) 2px 2px 5px",
|
||||
"fontFamily": "兰亭粗黑简, 微软雅黑, Microsoft YaHei, PingFang SC, Hiragino Sans GB, Arial, sans-serif",
|
||||
"fontWeight": "normal",
|
||||
"fontSize": subtitle_size,
|
||||
"text": slogan,
|
||||
"textAlign": "center",
|
||||
"lineHeight": 1.2,
|
||||
"name": "vibrant_slogan_precise",
|
||||
"data": {"type": "slogan", "layer": "content", "level": 2},
|
||||
"selectable": True,
|
||||
"evented": True
|
||||
}
|
||||
text_objects.append(subtitle_obj)
|
||||
|
||||
logger.info(f"创建VibrantTemplate精确文本布局: {len(text_objects)}个对象")
|
||||
return text_objects
|
||||
Loading…
x
Reference in New Issue
Block a user