修改了样式,未指定字体文件
This commit is contained in:
parent
ec8667eeea
commit
4f89cbf150
Binary file not shown.
@ -10,7 +10,7 @@ import math
|
||||
from typing import Dict, Any, Optional, Tuple
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from PIL import Image, ImageDraw, ImageFont, ImageFilter
|
||||
|
||||
from .base_template import BaseTemplate
|
||||
from ..utils import ColorExtractor
|
||||
@ -206,7 +206,7 @@ class VibrantTemplate(BaseTemplate):
|
||||
color_tuple = tuple(int(c) for c in color) + (alpha,)
|
||||
draw.line([(0, y), (self.width, y)], fill=color_tuple)
|
||||
|
||||
return self.image_processor.apply_blur(overlay, radius=enhanced_blur)
|
||||
return overlay.filter(ImageFilter.GaussianBlur(radius=enhanced_blur))
|
||||
|
||||
def _extract_glass_colors_from_image(self, image: Image.Image, gradient_start: int) -> tuple:
|
||||
"""从图片中提取用于毛玻璃背景的颜色"""
|
||||
@ -231,6 +231,84 @@ class VibrantTemplate(BaseTemplate):
|
||||
|
||||
return top_color, bottom_color
|
||||
|
||||
def _calculate_optimal_font_size_enhanced(self, text: str, target_width: int,
|
||||
max_size: int = 120, min_size: int = 10) -> Tuple[int, int]:
|
||||
"""
|
||||
计算文本的最佳字体大小,使其宽度接近目标宽度(增强版本,与demo一致)
|
||||
|
||||
返回:
|
||||
(字体大小, 实际文本宽度)
|
||||
"""
|
||||
# 二分查找最佳字体大小
|
||||
low = min_size
|
||||
high = max_size
|
||||
best_size = min_size
|
||||
best_width = 0
|
||||
tolerance = 0.08 # 容差值,使文本宽度更接近目标值
|
||||
|
||||
# 首先尝试最大字体大小
|
||||
try:
|
||||
font = self.text_renderer._load_default_font(max_size)
|
||||
max_width, _ = self.text_renderer.get_text_size(text, font)
|
||||
except:
|
||||
max_width = target_width * 2 # 如果出错,设置一个大值
|
||||
|
||||
# 如果最大字体大小下的宽度仍小于目标宽度的108%,直接使用最大字体
|
||||
if max_width < target_width * (1 + tolerance):
|
||||
best_size = max_size
|
||||
best_width = max_width
|
||||
else:
|
||||
# 记录最接近目标宽度的字体大小
|
||||
closest_size = min_size
|
||||
closest_diff = target_width
|
||||
|
||||
while low <= high:
|
||||
mid = (low + high) // 2
|
||||
try:
|
||||
font = self.text_renderer._load_default_font(mid)
|
||||
width, _ = self.text_renderer.get_text_size(text, font)
|
||||
except:
|
||||
width = target_width * 2 # 如果出错,设置一个大值
|
||||
|
||||
# 计算与目标宽度的差距
|
||||
diff = abs(width - target_width)
|
||||
|
||||
# 更新最接近的字体大小
|
||||
if diff < closest_diff:
|
||||
closest_diff = diff
|
||||
closest_size = mid
|
||||
|
||||
# 如果宽度在目标宽度的允许范围内,认为找到了最佳匹配
|
||||
if target_width * (1 - tolerance) <= width <= target_width * (1 + tolerance):
|
||||
best_size = mid
|
||||
best_width = width
|
||||
break
|
||||
|
||||
# 如果当前宽度小于目标宽度,尝试更大的字体
|
||||
if width < target_width:
|
||||
if width > best_width:
|
||||
best_width = width
|
||||
best_size = mid
|
||||
low = mid + 1
|
||||
else:
|
||||
# 如果当前宽度大于目标宽度,尝试更小的字体
|
||||
high = mid - 1
|
||||
|
||||
# 如果没有找到在容差范围内的字体大小,使用最接近的字体大小
|
||||
if best_width == 0:
|
||||
best_size = closest_size
|
||||
|
||||
# 确保返回的宽度是使用最终字体计算的实际宽度
|
||||
try:
|
||||
best_font = self.text_renderer._load_default_font(best_size)
|
||||
final_width, _ = self.text_renderer.get_text_size(text, best_font)
|
||||
except:
|
||||
final_width = best_width
|
||||
|
||||
logger.info(f"文本'{text[:min(10, len(text))]}...'的最佳字体大小: {best_size},目标宽度: {target_width},实际宽度: {final_width},差距: {abs(final_width-target_width)}")
|
||||
|
||||
return best_size, final_width
|
||||
|
||||
def _render_texts(self, canvas: Image.Image, content: Dict[str, Any], gradient_start: int) -> Image.Image:
|
||||
"""渲染所有文本元素,采用双栏布局"""
|
||||
draw = ImageDraw.Draw(canvas)
|
||||
@ -256,27 +334,41 @@ class VibrantTemplate(BaseTemplate):
|
||||
return canvas
|
||||
|
||||
def _calculate_content_margins(self, content: Dict[str, Any], width: int, center_x: int) -> Tuple[int, int]:
|
||||
"""计算内容区域的左右边距"""
|
||||
"""计算内容区域的左右边距(增强版本,与demo一致)"""
|
||||
# 计算标题位置
|
||||
title_text = content.get("title", "")
|
||||
title_size=self.text_renderer.calculate_optimal_font_size(title_text,int(width * 0.95),max_size=130)
|
||||
title_width,title_height=self.text_renderer.get_text_size(title_text,self.text_renderer._load_default_font(title_size))
|
||||
title_target_width = int(width * 0.95)
|
||||
title_size, title_width = self._calculate_optimal_font_size_enhanced(
|
||||
title_text, title_target_width, min_size=40, max_size=130
|
||||
)
|
||||
title_x = center_x - title_width // 2
|
||||
|
||||
|
||||
# 计算副标题位置
|
||||
slogan_text = content.get("slogan", "")
|
||||
subtitle_size=self.text_renderer.calculate_optimal_font_size(slogan_text,int(width * 0.9),max_size=50)
|
||||
subtitle_width,subtitle_height=self.text_renderer.get_text_size(slogan_text,self.text_renderer._load_default_font(subtitle_size))
|
||||
subtitle_target_width = int(width * 0.9)
|
||||
subtitle_size, subtitle_width = self._calculate_optimal_font_size_enhanced(
|
||||
slogan_text, subtitle_target_width, max_size=50, min_size=20
|
||||
)
|
||||
subtitle_x = center_x - subtitle_width // 2
|
||||
|
||||
padding = 20
|
||||
left_margin = max(40, min(title_x, subtitle_x) - padding)
|
||||
right_margin = min(width - 40, max(title_x + title_width, subtitle_x + subtitle_width) + padding)
|
||||
|
||||
if (right_margin - left_margin) < (min_width := int(width * 0.75)):
|
||||
extra = min_width - (right_margin - left_margin)
|
||||
left_margin = max(30, left_margin - extra // 2)
|
||||
right_margin = min(width - 30, right_margin + extra // 2)
|
||||
|
||||
return left_margin, right_margin
|
||||
|
||||
# 计算内容区域边距 - 减小额外的边距,让内容区域更宽
|
||||
padding = 20 # 从30减小到20
|
||||
content_left_margin = min(title_x, subtitle_x) - padding
|
||||
content_right_margin = max(title_x + title_width, subtitle_x + subtitle_width) + padding
|
||||
|
||||
# 确保边距不超出合理范围,但允许更宽的内容区域
|
||||
content_left_margin = max(40, content_left_margin)
|
||||
content_right_margin = min(width - 40, content_right_margin)
|
||||
|
||||
# 如果内容区域太窄,强制使用更宽的区域
|
||||
min_content_width = int(width * 0.75) # 至少使用75%的宽度
|
||||
current_width = content_right_margin - content_left_margin
|
||||
if current_width < min_content_width:
|
||||
extra_width = min_content_width - current_width
|
||||
content_left_margin = max(30, content_left_margin - extra_width // 2)
|
||||
content_right_margin = min(width - 30, content_right_margin + extra_width // 2)
|
||||
|
||||
return content_left_margin, content_right_margin
|
||||
|
||||
def _render_footer(self, draw: ImageDraw.Draw, content: Dict[str, Any], y: int, left: int, right: int):
|
||||
"""渲染页脚文本"""
|
||||
@ -288,11 +380,13 @@ class VibrantTemplate(BaseTemplate):
|
||||
draw.text((right - width, y), pagination, font=font, fill=(255, 255, 255))
|
||||
|
||||
def _render_title_subtitle(self, draw: ImageDraw.Draw, content: Dict[str, Any], y: int, center_x: int, left: int, right: int) -> int:
|
||||
"""渲染标题和副标题"""
|
||||
"""渲染标题和副标题(增强版本,与demo一致)"""
|
||||
# 标题
|
||||
title_text = content.get("title", "默认标题")
|
||||
title_target_width = int((right - left) * 0.98)
|
||||
title_size=self.text_renderer.calculate_optimal_font_size(title_text,title_target_width,max_size=140,min_size=40)
|
||||
title_size, title_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
title_text, title_target_width, max_size=140, min_size=40
|
||||
)
|
||||
title_font = self.text_renderer._load_default_font(title_size)
|
||||
|
||||
text_w, text_h = self.text_renderer.get_text_size(title_text, title_font)
|
||||
@ -303,7 +397,9 @@ class VibrantTemplate(BaseTemplate):
|
||||
# 副标题 (slogan)
|
||||
subtitle_text = content.get("slogan", "")
|
||||
subtitle_target_width = int((right - left) * 0.95)
|
||||
subtitle_size=self.text_renderer.calculate_optimal_font_size(subtitle_text,subtitle_target_width,max_size=75,min_size=20)
|
||||
subtitle_size, subtitle_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
subtitle_text, subtitle_target_width, max_size=75, min_size=20
|
||||
)
|
||||
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)
|
||||
@ -348,31 +444,34 @@ class VibrantTemplate(BaseTemplate):
|
||||
draw.text((x, item_y), " " + item, font=font, fill=(255, 255, 255))
|
||||
|
||||
def _render_right_column(self, draw: ImageDraw.Draw, content: Dict[str, Any], y: int, x: int, right_margin: int):
|
||||
"""渲染右栏内容:价格、票种和备注"""
|
||||
"""渲染右栏内容:价格、票种和备注(增强版本,与demo一致)"""
|
||||
price_text = content.get('price', '')
|
||||
price_size=self.text_renderer.calculate_optimal_font_size(price_text,int((right_margin - x) * 0.7),max_size=120,min_size=40)
|
||||
price_width,_=self.text_renderer.get_text_size(price_text,self.text_renderer._load_default_font(price_size))
|
||||
price_target_width = int((right_margin - x) * 0.7)
|
||||
price_size, price_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
price_text, price_target_width, max_size=120, min_size=40
|
||||
)
|
||||
|
||||
price_font = self.text_renderer._load_default_font(price_size)
|
||||
|
||||
suffix_font = self.text_renderer._load_default_font(int(price_size * 0.3))
|
||||
_, price_height = self.text_renderer.get_text_size(price_text, price_font)
|
||||
suffix_width, suffix_height = self.text_renderer.get_text_size("CNY起", suffix_font)
|
||||
|
||||
price_x = right_margin - price_width - suffix_width
|
||||
price_x = right_margin - price_actual_width - suffix_width
|
||||
self.text_renderer.draw_text_with_shadow(draw, (price_x, y), price_text, price_font)
|
||||
|
||||
suffix_y = y + price_height - suffix_height
|
||||
draw.text((price_x + price_width, suffix_y), "CNY起", font=suffix_font, fill=(255, 255, 255))
|
||||
draw.text((price_x + price_actual_width, suffix_y), "CNY起", font=suffix_font, fill=(255, 255, 255))
|
||||
|
||||
underline_y = y + price_height + 18
|
||||
draw.line([(price_x - 10, underline_y), (right_margin, underline_y)], fill=(255, 255, 255, 80), width=2)
|
||||
|
||||
ticket_text = content.get("ticket_type", "")
|
||||
ticket_size=self.text_renderer.calculate_optimal_font_size(ticket_text,int((right_margin - x) * 0.7),max_size=60,min_size=30)
|
||||
ticket_width,_=self.text_renderer.get_text_size(ticket_text,self.text_renderer._load_default_font(ticket_size))
|
||||
ticket_target_width = int((right_margin - x) * 0.7)
|
||||
ticket_size, ticket_actual_width = self._calculate_optimal_font_size_enhanced(
|
||||
ticket_text, ticket_target_width, max_size=60, min_size=30
|
||||
)
|
||||
ticket_font = self.text_renderer._load_default_font(ticket_size)
|
||||
ticket_x = right_margin - ticket_width
|
||||
ticket_x = right_margin - ticket_actual_width
|
||||
ticket_y = y + price_height + 35
|
||||
self.text_renderer.draw_text_with_shadow(draw, (ticket_x, ticket_y), ticket_text, ticket_font)
|
||||
_, ticket_height = self.text_renderer.get_text_size(ticket_text, ticket_font)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user