修改了扁平结构

This commit is contained in:
jinye_huang 2025-08-04 12:17:54 +08:00
parent 5acbe8d7b7
commit d15a72e489

View File

@ -631,7 +631,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]: def _generate_fabric_json(self, content: Dict[str, Any], template_id: str, image_size: List[int], images: Image.Image = None) -> Dict[str, Any]:
""" """
生成支持多级分层的Fabric.js JSON格式 生成扁平化的Fabric.js JSON格式不使用group每个对象独立成层
Args: Args:
content: 海报内容数据 content: 海报内容数据
@ -640,7 +640,7 @@ class PosterService:
images: 用户上传的图片 images: 用户上传的图片
Returns: Returns:
Dict: 支持多级分层的Fabric.js JSON格式数据 Dict: 扁平化的Fabric.js JSON格式数据
""" """
try: try:
fabric_objects = [] fabric_objects = []
@ -648,21 +648,27 @@ class PosterService:
# 基础画布尺寸 # 基础画布尺寸
canvas_width, canvas_height = image_size[0], image_size[1] canvas_width, canvas_height = image_size[0], image_size[1]
# 1. 图片层组 (最底层 - 用户上传的图片) # 1. 用户上传的图片(最底层 - Level 0
image_group = self._create_image_layer(images, canvas_width, canvas_height) if images and hasattr(images, 'width'):
fabric_objects.append(image_group) image_object = self._create_image_object(images, canvas_width, canvas_height)
fabric_objects.append(image_object)
else:
# 占位符
placeholder_object = self._create_placeholder_object(canvas_width, canvas_height)
fabric_objects.append(placeholder_object)
# 2. 背景层组 (第二层) # 2. 背景层Level 1
background_group = self._create_background_layer(canvas_width, canvas_height, template_id) if template_id:
fabric_objects.append(background_group) background_object = self._create_background_object(canvas_width, canvas_height)
fabric_objects.append(background_object)
# 3. 内容层组 (中间层) # 3. 内容文字层Level 2
content_group = self._create_content_layer(content, canvas_width, canvas_height) text_objects = self._create_text_objects(content, canvas_width, canvas_height)
fabric_objects.append(content_group) fabric_objects.extend(text_objects)
# 4. 装饰层组 (顶层) # 4. 装饰层Level 3
decoration_group = self._create_decoration_layer(content, canvas_width, canvas_height) decoration_objects = self._create_decoration_objects(canvas_width, canvas_height)
fabric_objects.append(decoration_group) fabric_objects.extend(decoration_objects)
# 构建完整的Fabric.js JSON # 构建完整的Fabric.js JSON
fabric_json = { fabric_json = {
@ -693,7 +699,7 @@ class PosterService:
"includeDefaultValues": True "includeDefaultValues": True
} }
logger.info(f"成功生成多级分层Fabric.js JSON包含 {len(fabric_objects)}层组") logger.info(f"成功生成扁平化Fabric.js JSON包含 {len(fabric_objects)}独立对象")
return fabric_json return fabric_json
except Exception as e: except Exception as e:
@ -706,12 +712,9 @@ class PosterService:
"height": image_size[1] "height": image_size[1]
} }
def _create_image_layer(self, images: Image.Image, canvas_width: int, canvas_height: int) -> Dict[str, Any]: def _create_image_object(self, images: Image.Image, canvas_width: int, canvas_height: int) -> Dict[str, Any]:
"""创建图片层组(最底层)""" """创建用户上传的图片对象(最底层)"""
image_objects = [] # 将PIL图像转换为base64
if images and hasattr(images, 'width'):
# 将PIL图像转换为base64以便在Fabric.js中使用
image_base64 = self._image_to_base64(images) image_base64 = self._image_to_base64(images)
# 计算图片的缩放比例,保持宽高比 # 计算图片的缩放比例,保持宽高比
@ -726,7 +729,7 @@ class PosterService:
left = (canvas_width - scaled_width) / 2 left = (canvas_width - scaled_width) / 2
top = (canvas_height - scaled_height) / 2 top = (canvas_height - scaled_height) / 2
image_objects.append({ return {
"type": "image", "type": "image",
"version": "5.3.0", "version": "5.3.0",
"originX": "left", "originX": "left",
@ -747,24 +750,19 @@ class PosterService:
"name": "user_uploaded_image", "name": "user_uploaded_image",
"data": { "data": {
"type": "user_image", "type": "user_image",
"layer": "image",
"level": 0,
"replaceable": True, "replaceable": True,
"original_size": [image_width, image_height], "original_size": [image_width, image_height],
"scale_ratio": scale "scale_ratio": scale
}, },
"selectable": True, "selectable": True,
"evented": True, "evented": True
"moveCursor": "move", }
"cornerStyle": "circle",
"cornerSize": 12, def _create_placeholder_object(self, canvas_width: int, canvas_height: int) -> Dict[str, Any]:
"transparentCorners": False, """创建图片占位符对象"""
"cornerColor": "#4dabf7", return {
"cornerStrokeColor": "#ffffff",
"borderColor": "#4dabf7",
"borderScaleFactor": 2
})
else:
# 如果没有图片,创建一个占位符
image_objects.append({
"type": "rect", "type": "rect",
"version": "5.3.0", "version": "5.3.0",
"originX": "left", "originX": "left",
@ -777,45 +775,26 @@ class PosterService:
"stroke": "#dee2e6", "stroke": "#dee2e6",
"strokeWidth": 2, "strokeWidth": 2,
"strokeDashArray": [10, 5], "strokeDashArray": [10, 5],
"angle": 0,
"flipX": False,
"flipY": False,
"opacity": 1,
"visible": True,
"name": "image_placeholder", "name": "image_placeholder",
"data": { "data": {
"type": "placeholder", "type": "placeholder",
"layer": "image",
"level": 0,
"replaceable": True, "replaceable": True,
"placeholder_text": "点击上传图片" "placeholder_text": "点击上传图片"
}, },
"selectable": True, "selectable": True,
"evented": True "evented": True
})
return {
"type": "group",
"version": "5.3.0",
"originX": "left",
"originY": "top",
"left": 0,
"top": 0,
"width": canvas_width,
"height": canvas_height,
"angle": 0,
"scaleX": 1,
"scaleY": 1,
"flipX": False,
"flipY": False,
"opacity": 1,
"visible": True,
"name": "image_layer",
"data": {"layer": "image", "level": 0, "replaceable": True},
"objects": image_objects,
"selectable": False,
"evented": True
} }
def _create_background_layer(self, canvas_width: int, canvas_height: int, template_id: str) -> Dict[str, Any]: def _create_background_object(self, canvas_width: int, canvas_height: int) -> Dict[str, Any]:
"""创建背景层组""" """创建半透明背景对象"""
background_objects = [] return {
# 主背景
background_objects.append({
"type": "rect", "type": "rect",
"version": "5.3.0", "version": "5.3.0",
"originX": "left", "originX": "left",
@ -824,269 +803,86 @@ class PosterService:
"top": 0, "top": 0,
"width": canvas_width, "width": canvas_width,
"height": canvas_height, "height": canvas_height,
"fill": "transparent", "fill": "rgba(255, 255, 255, 0.8)",
"stroke": None, "stroke": None,
"name": "main_background",
"selectable": False,
"evented": False
})
# 渐变背景(可选)
if template_id:
background_objects.append({
"type": "rect",
"version": "5.3.0",
"originX": "left",
"originY": "top",
"left": 0,
"top": 0,
"width": canvas_width,
"height": canvas_height,
"fill": {
"type": "linear",
"coords": {
"x1": 0,
"y1": 0,
"x2": 0,
"y2": canvas_height
},
"colorStops": [
{"offset": 0, "color": "#ffffff", "opacity": 0.8},
{"offset": 1, "color": "#f8f9fa", "opacity": 0.9}
]
},
"name": "gradient_background",
"selectable": False,
"evented": False
})
return {
"type": "group",
"version": "5.3.0",
"originX": "left",
"originY": "top",
"left": 0,
"top": 0,
"width": canvas_width,
"height": canvas_height,
"angle": 0, "angle": 0,
"scaleX": 1,
"scaleY": 1,
"flipX": False, "flipX": False,
"flipY": False, "flipY": False,
"opacity": 1, "opacity": 0.8,
"visible": True, "visible": True,
"name": "background_layer", "name": "background_overlay",
"data": {"layer": "background", "level": 1}, "data": {
"objects": background_objects, "type": "background",
"layer": "background",
"level": 1
},
"selectable": False, "selectable": False,
"evented": False "evented": False
} }
def _create_content_layer(self, content: Dict[str, Any], canvas_width: int, canvas_height: int) -> Dict[str, Any]: def _create_text_objects(self, content: Dict[str, Any], canvas_width: int, canvas_height: int) -> List[Dict[str, Any]]:
"""创建内容层组,包含多个子分层""" """创建文本对象列表"""
content_objects = [] text_objects = []
# 标题组 # 文本元素配置
title_group = self._create_title_group(content, canvas_width) text_configs = {
if title_group["objects"]: 'title': {'fontSize': 48, 'top': 100, 'fontWeight': 'bold', 'fill': '#2c3e50'},
content_objects.append(title_group) 'slogan': {'fontSize': 24, 'top': 180, 'fontWeight': 'normal', 'fill': '#7f8c8d'},
'content': {'fontSize': 18, 'top': 250, 'fontWeight': 'normal', 'fill': '#34495e'},
# 正文组 'price': {'fontSize': 36, 'top': 400, 'fontWeight': 'bold', 'fill': '#e74c3c'},
body_group = self._create_body_group(content, canvas_width) 'remarks': {'fontSize': 14, 'top': 500, 'fontWeight': 'normal', 'fill': '#95a5a6'}
if body_group["objects"]:
content_objects.append(body_group)
# 价格信息组
price_group = self._create_price_group(content, canvas_width)
if price_group["objects"]:
content_objects.append(price_group)
return {
"type": "group",
"version": "5.3.0",
"originX": "left",
"originY": "top",
"left": 0,
"top": 0,
"width": canvas_width,
"height": canvas_height,
"angle": 0,
"scaleX": 1,
"scaleY": 1,
"flipX": False,
"flipY": False,
"opacity": 1,
"visible": True,
"name": "content_layer",
"data": {"layer": "content", "level": 2},
"objects": content_objects
} }
def _create_title_group(self, content: Dict[str, Any], canvas_width: int) -> Dict[str, Any]: for key, config in text_configs.items():
"""创建标题分组""" if key in content and content[key]:
title_objects = [] text_content = content[key]
# 主标题
if content.get('title'):
title_objects.append({
"type": "textbox",
"version": "5.3.0",
"originX": "left",
"originY": "top",
"left": 50,
"top": 80,
"width": canvas_width - 100,
"height": 80,
"fill": "#2c3e50",
"fontFamily": "Arial, sans-serif",
"fontWeight": "bold",
"fontSize": 48,
"text": str(content['title']),
"textAlign": "center",
"name": "main_title",
"data": {"type": "title", "priority": "high"}
})
# 副标题
if content.get('slogan'):
title_objects.append({
"type": "textbox",
"version": "5.3.0",
"originX": "left",
"originY": "top",
"left": 50,
"top": 170,
"width": canvas_width - 100,
"height": 40,
"fill": "#7f8c8d",
"fontFamily": "Arial, sans-serif",
"fontWeight": "normal",
"fontSize": 24,
"text": str(content['slogan']),
"textAlign": "center",
"name": "subtitle",
"data": {"type": "subtitle", "priority": "medium"}
})
return {
"type": "group",
"version": "5.3.0",
"originX": "left",
"originY": "top",
"left": 0,
"top": 0,
"width": canvas_width,
"height": 250,
"name": "title_group",
"data": {"section": "title", "level": 3},
"objects": title_objects
}
def _create_body_group(self, content: Dict[str, Any], canvas_width: int) -> Dict[str, Any]:
"""创建正文分组"""
body_objects = []
if content.get('content'):
text_content = content['content']
if isinstance(text_content, list): if isinstance(text_content, list):
text_content = '\n'.join(text_content) text_content = '\n'.join(text_content)
elif not isinstance(text_content, str):
text_content = str(text_content)
body_objects.append({ text_object = {
"type": "textbox", "type": "textbox",
"version": "5.3.0", "version": "5.3.0",
"originX": "left", "originX": "left",
"originY": "top", "originY": "top",
"left": 50, "left": 50,
"top": 280, "top": config['top'],
"width": canvas_width - 100, "width": canvas_width - 100,
"height": 120, "height": 60 if key != 'content' else 120,
"fill": "#34495e", "fill": config['fill'],
"fontFamily": "Arial, sans-serif", "fontFamily": "Arial, sans-serif",
"fontWeight": "normal", "fontWeight": config['fontWeight'],
"fontSize": 18, "fontSize": config['fontSize'],
"text": str(text_content), "text": text_content,
"textAlign": "left", "textAlign": "center" if key in ['title', 'slogan', 'price'] else "left",
"lineHeight": 1.4, "lineHeight": 1.2,
"name": "main_content", "angle": 0,
"data": {"type": "content", "priority": "medium"} "flipX": False,
}) "flipY": False,
"opacity": 1,
return { "visible": True,
"type": "group", "name": f"text_{key}",
"version": "5.3.0", "data": {
"originX": "left", "type": "text",
"originY": "top", "layer": "content",
"left": 0, "level": 2,
"top": 250, "content_type": key,
"width": canvas_width, "priority": "high" if key in ['title', 'price'] else "medium"
"height": 150, },
"name": "body_group", "selectable": True,
"data": {"section": "body", "level": 3}, "evented": True
"objects": body_objects
} }
text_objects.append(text_object)
def _create_price_group(self, content: Dict[str, Any], canvas_width: int) -> Dict[str, Any]: return text_objects
"""创建价格信息分组"""
price_objects = []
if content.get('price'): def _create_decoration_objects(self, canvas_width: int, canvas_height: int) -> List[Dict[str, Any]]:
# 价格背景 """创建装饰对象列表"""
price_objects.append({
"type": "rect",
"version": "5.3.0",
"originX": "left",
"originY": "top",
"left": 40,
"top": 420,
"width": canvas_width - 80,
"height": 80,
"fill": "#e74c3c",
"rx": 10,
"ry": 10,
"name": "price_background"
})
# 价格文字
price_objects.append({
"type": "textbox",
"version": "5.3.0",
"originX": "left",
"originY": "top",
"left": 50,
"top": 440,
"width": canvas_width - 100,
"height": 40,
"fill": "#ffffff",
"fontFamily": "Arial, sans-serif",
"fontWeight": "bold",
"fontSize": 36,
"text": str(content['price']),
"textAlign": "center",
"name": "price_text",
"data": {"type": "price", "priority": "high"}
})
return {
"type": "group",
"version": "5.3.0",
"originX": "left",
"originY": "top",
"left": 0,
"top": 400,
"width": canvas_width,
"height": 100,
"name": "price_group",
"data": {"section": "price", "level": 3},
"objects": price_objects
}
def _create_decoration_layer(self, content: Dict[str, Any], canvas_width: int, canvas_height: int) -> Dict[str, Any]:
"""创建装饰层组"""
decoration_objects = [] decoration_objects = []
# 装饰边框 # 装饰边框
decoration_objects.append({ border_object = {
"type": "rect", "type": "rect",
"version": "5.3.0", "version": "5.3.0",
"originX": "left", "originX": "left",
@ -1099,12 +895,24 @@ class PosterService:
"stroke": "#3498db", "stroke": "#3498db",
"strokeWidth": 3, "strokeWidth": 3,
"strokeDashArray": [10, 5], "strokeDashArray": [10, 5],
"angle": 0,
"flipX": False,
"flipY": False,
"opacity": 1,
"visible": True,
"name": "decoration_border", "name": "decoration_border",
"selectable": False "data": {
}) "type": "decoration",
"layer": "decoration",
"level": 3
},
"selectable": False,
"evented": False
}
decoration_objects.append(border_object)
# 角落装饰 # 角落装饰
decoration_objects.append({ corner_object = {
"type": "circle", "type": "circle",
"version": "5.3.0", "version": "5.3.0",
"originX": "center", "originX": "center",
@ -1113,30 +921,20 @@ class PosterService:
"top": 50, "top": 50,
"radius": 20, "radius": 20,
"fill": "#f39c12", "fill": "#f39c12",
"name": "corner_decoration",
"selectable": False
})
return {
"type": "group",
"version": "5.3.0",
"originX": "left",
"originY": "top",
"left": 0,
"top": 0,
"width": canvas_width,
"height": canvas_height,
"angle": 0, "angle": 0,
"scaleX": 1,
"scaleY": 1,
"flipX": False, "flipX": False,
"flipY": False, "flipY": False,
"opacity": 1, "opacity": 1,
"visible": True, "visible": True,
"name": "decoration_layer", "name": "corner_decoration",
"data": {"layer": "decoration", "level": 3}, "data": {
"objects": decoration_objects, "type": "decoration",
"selectable": False "layer": "decoration",
"level": 3
},
"selectable": False,
"evented": False
} }
decoration_objects.append(corner_object)
return decoration_objects