feat: 添加 pilmoji 依赖并支持渲染 emoji 表情

- 在 requirements.txt 中添加 pilmoji==2.0.5 依赖
- 重构 ImageRenderer 类,使用 Pilmoji 替代 PIL 的原生 text 方法以支持渲染 emoji 表情
- 将字体路径配置提取为类常量 DEFAULT_FONT_PATHS,并包含 Windows 和 Linux 的默认路径
- 初始化方法中的 font_paths 参数默认为 None,自动使用默认字体路径列表
This commit is contained in:
2026-04-04 22:27:00 +08:00
parent 45fbb947da
commit 2214e22b80
2 changed files with 19 additions and 12 deletions

View File

@@ -1,5 +1,6 @@
"""图片生成模块 - 将文本渲染为图片""" """图片生成模块 - 将文本渲染为图片"""
from PIL import Image, ImageDraw, ImageFont from PIL import Image, ImageDraw, ImageFont
from pilmoji import Pilmoji
import io import io
import base64 import base64
from typing import Tuple from typing import Tuple
@@ -7,6 +8,14 @@ from typing import Tuple
class ImageRenderer: class ImageRenderer:
"""图片渲染器""" """图片渲染器"""
DEFAULT_FONT_PATHS = [
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
"/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf",
"C:/Windows/Fonts/msyh.ttc",
"C:/Windows/Fonts/simhei.ttf",
"C:/Windows/Fonts/seguiemj.ttf",
]
def __init__( def __init__(
self, self,
@@ -16,10 +25,7 @@ class ImageRenderer:
line_spacing: float = 1.4, line_spacing: float = 1.4,
bg_color: Tuple[int, int, int] = (252, 252, 252), bg_color: Tuple[int, int, int] = (252, 252, 252),
text_color: Tuple[int, int, int] = (0, 0, 0), text_color: Tuple[int, int, int] = (0, 0, 0),
font_paths: list = [ font_paths: list = None
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", # 中文
"/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf" # emoji
]
): ):
""" """
初始化图片渲染器 初始化图片渲染器
@@ -39,7 +45,7 @@ class ImageRenderer:
self.line_spacing = line_spacing self.line_spacing = line_spacing
self.bg_color = bg_color self.bg_color = bg_color
self.text_color = text_color self.text_color = text_color
self.font_paths = font_paths or [] self.font_paths = font_paths or self.DEFAULT_FONT_PATHS.copy()
# 加载字体 # 加载字体
self.font = self._load_font() self.font = self._load_font()
@@ -167,10 +173,10 @@ class ImageRenderer:
image = Image.new('RGB', (self.width, height), color=self.bg_color) image = Image.new('RGB', (self.width, height), color=self.bg_color)
draw = ImageDraw.Draw(image) draw = ImageDraw.Draw(image)
# 绘制标题(加粗效果通过增大字体实现)
title_x = self.padding title_x = self.padding
title_y = self.padding title_y = self.padding
draw.text((title_x, title_y), title, font=title_font, fill=self.text_color) with Pilmoji(image) as pilmoji:
pilmoji.text((title_x, title_y), title, font=title_font, fill=self.text_color)
# 绘制分割线 # 绘制分割线
divider_y = title_y + title_height + 10 divider_y = title_y + title_height + 10
@@ -180,12 +186,12 @@ class ImageRenderer:
width=divider_height width=divider_height
) )
# 绘制文本内容
y = divider_y + divider_spacing + self.padding y = divider_y + divider_spacing + self.padding
for line in all_lines: with Pilmoji(image) as pilmoji:
if line: # 跳过空行 for line in all_lines:
draw.text((self.padding, y), line, font=self.font, fill=self.text_color) if line:
y += total_line_height pilmoji.text((self.padding, y), line, font=self.font, fill=self.text_color)
y += total_line_height
# 转换为字节流 # 转换为字节流
img_byte_arr = io.BytesIO() img_byte_arr = io.BytesIO()

View File

@@ -49,6 +49,7 @@ nonebot_plugin_learning_chat==0.4.0
nonebot_plugin_withdraw==0.4.1 nonebot_plugin_withdraw==0.4.1
packaging==24.2 packaging==24.2
pillow==11.0.0 pillow==11.0.0
pilmoji==2.0.5
pipx==1.7.1 pipx==1.7.1
platformdirs==4.3.6 platformdirs==4.3.6
playwright==1.49.1 playwright==1.49.1