feat: markdown render support

This commit is contained in:
Soulter
2023-06-10 09:27:02 +00:00
parent e6770d2b12
commit 9a2dffe299
3 changed files with 256 additions and 8 deletions
+6 -6
View File
@@ -306,22 +306,22 @@ class Command:
notice = json.loads(resp)["notice"]
except BaseException as e:
notice = ""
msg = ''
msg = "# Help Center\n## 指令列表\n"
# msg = "Github项目名QQChannelChatGPT, 有问题提交issue, 欢迎Star\n【指令列表】\n"
for key, value in commands.items():
msg += key + ": " + value + "\n"
msg += f"`{key}` - {value}\n"
# plugins
if cached_plugins != None:
plugin_list_info = "\n".join([f"{k}: \n名称: {v['info']['name']}\n简介: {v['info']['desc']}\n" for k, v in cached_plugins.items()])
plugin_list_info = "\n".join([f"`{k}` {v['info']['name']}\n简介: \n{v['info']['desc']}\n" for k, v in cached_plugins.items()])
if plugin_list_info.strip() != "":
msg += "\n【插件列表】\n"
msg += "## 插件列表\n> 使用plugin v 插件名 查看插件帮助\n"
msg += plugin_list_info
msg += "\n*使用plugin v 插件名 查看插件帮助"
msg += notice
if platform == gu.PLATFORM_GOCQ:
try:
p = gu.create_text_image("【Help Center】", msg)
# p = gu.create_text_image("【Help Center】", msg)
p = gu.create_markdown_image(msg)
return [Image.fromFileSystem(p)]
except BaseException as e:
gu.log(str(e))
-1
View File
@@ -31,7 +31,6 @@ class QQ:
source,
res,
image_mode: bool = False):
# image_mode parameter is [deprecated].
if not self.is_start:
raise Exception("管理员未启动GOCQ平台")
+250 -1
View File
@@ -3,6 +3,8 @@ import time
import socket
from PIL import Image, ImageDraw, ImageFont
import os
import re
import requests
PLATFORM_GOCQ = 'gocq'
PLATFORM_QQCHAN = 'qqchan'
@@ -138,6 +140,219 @@ def word2img(title: str, text: str, max_width=30, font_size=20):
return image
def word2img_markdown(markdown: str, max_width=35, font_size=25):
if os.path.exists("resources/fonts/genshin.ttf"):
font_path = "resources/fonts/genshin.ttf"
elif os.path.exists("QQChannelChatGPT/resources/fonts/genshin.ttf"):
font_path = "QQChannelChatGPT/resources/fonts/genshin.ttf"
elif os.path.exists("C:/Windows/Fonts/simhei.ttf"):
font_path = "C:/Windows/Fonts/simhei.ttf"
elif os.path.exists("/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"):
font_path = "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"
else:
raise Exception("找不到字体文件")
# try to render markdown
def render_markdown(markdown_text, image_width=800, image_height=600, font_size=16, font_color=(0, 0, 0), bg_color=(255, 255, 255)):
# pre_process, get height of each line
pre_lines = markdown_text.split('\n')
height = 0
pre_in_code = False
# pre_codes = []
for line in pre_lines:
line = line.strip()
if pre_in_code and not line.startswith("```"):
height += font_size+2
# pre_codes.append(line)
continue
if line.startswith("#"):
header_level = line.count("#")
height += 58 - header_level * 4
elif line.startswith("-"):
height += font_size+5
elif line.startswith(">"):
height += font_size+10
elif line.startswith("```"):
if pre_in_code:
pre_in_code = False
# pre_codes = []
height += 10
else:
pre_in_code = True
height += 5
elif re.search(r"`(.*?)`", line):
height += font_size+20
else:
height += font_size+5
print("Pre process done, height: ", height)
image_height = height
# 创建空白图像
image = Image.new('RGB', (image_width, image_height), bg_color)
draw = ImageDraw.Draw(image)
# # get all the emojis unicode in the markdown text
# unicode_text = markdown_text.encode('unicode_escape').decode()
# # print(unicode_text)
# unicode_emojis = re.findall(r'\\U\w{8}', unicode_text)
# emoji_base_url = "https://abs.twimg.com/emoji/v1/72x72/{unicode_emoji}.png"
if os.path.exists("resources/fonts/genshin.ttf"):
font_path = "resources/fonts/genshin.ttf"
elif os.path.exists("QQChannelChatGPT/resources/fonts/genshin.ttf"):
font_path = "QQChannelChatGPT/resources/fonts/genshin.ttf"
elif os.path.exists("C:/Windows/Fonts/simhei.ttf"):
font_path = "C:/Windows/Fonts/simhei.ttf"
elif os.path.exists("/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"):
font_path = "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"
else:
raise Exception("找不到字体文件")
# backup
if os.path.exists("resources/fonts/simhei.ttf"):
font_path1 = "resources/fonts/simhei.ttf"
elif os.path.exists("QQChannelChatGPT/resources/fonts/simhei.ttf"):
font_path1 = "QQChannelChatGPT/resources/fonts/simhei.ttf"
else:
font_path1 = font_path
# 加载字体
font = ImageFont.truetype(font_path, font_size)
# 设置初始位置
x, y = 10, 10
# 解析Markdown文本
lines = markdown_text.split("\n")
in_code_block = False
code_block_start_y = 0
code_block_codes = []
for line in lines:
if in_code_block and not line.startswith("```"):
code_block_codes.append(line)
y += font_size + 2
continue
line = line.strip()
# y += 28 - header_level * 4 + 20
if line.startswith("#"):
# unicode_emojis = re.findall(r'\\U0001\w{4}', line)
# for unicode_emoji in unicode_emojis:
# line = line.replace(unicode_emoji, "")
# unicode_emoji = ""
# if len(unicode_emojis) > 0:
# unicode_emoji = unicode_emojis[0]
# 处理标题
header_level = line.count("#")
line = line.strip("#").strip()
font_size = 28 - header_level * 4
# if unicode_emoji != "":
# emoji_url = emoji_base_url.format(unicode_emoji=unicode_emoji[-5:])
# emoji = Image.open(requests.get(emoji_url, stream=True).raw)
# emoji = emoji.resize((font_size, font_size))
# image.paste(emoji, (x, y))
# x += font_size
font = ImageFont.truetype(font_path, font_size)
draw.text((x, y), line, font=font, fill=font_color)
# material design color: blue 500
draw.line((x, y + font_size + 8, image_width - 10, y + font_size + 8), fill=(230, 230, 230), width=3)
y += font_size + 30
# y += font_size + 10
elif line.startswith(">"):
# 处理引用
quote_text = line.strip(">")
# quote_width = image_width - 20 # 引用框的宽度为图像宽度减去左右边距
# quote_height = font_size + 10 # 引用框的高度为字体大小加上上下边距
# quote_box = (x, y, x + quote_width, y + quote_height)
# draw.rounded_rectangle(quote_box, radius=5, fill=(230, 230, 230), width=2) # 使用灰色填充矩形框作为引用背景
draw.line((x, y, x, y + font_size + 10), fill=(230, 230, 230), width=5)
font = ImageFont.truetype(font_path, font_size)
draw.text((x + 5, y + 5), quote_text, font=font, fill=(180, 180, 180))
y += font_size + 10
# y += 16+5
elif line.startswith("-"):
# 处理列表
list_text = line.strip("-").strip()
font_size = 16
font = ImageFont.truetype(font_path, font_size)
draw.text((x, y), " · " + list_text, font=font, fill=font_color)
y += font_size + 5
# y += 5+10+font_size*line.count
elif line.startswith("```"):
if not in_code_block:
code_block_start_y = y+5
in_code_block = True
else:
# print(code_block_codes)
in_code_block = False
codes = "\n".join(code_block_codes)
code_block_codes = []
draw.rounded_rectangle((x, code_block_start_y, image_width - 10, y+15), radius=5, fill=(240, 240, 240), width=2)
font = ImageFont.truetype(font_path1, 16)
draw.text((x + 10, code_block_start_y + 5), codes, font=font, fill=font_color)
y += 15
# y += font_size+10
elif re.search(r"`(.*?)`", line):
# 处理行内代码
code_regex = r"`(.*?)`"
parts_inline = re.findall(code_regex, line)
# print(parts_inline)
parts = re.split(code_regex, line)
# print(parts)
for part in parts:
# the judge has a tiny bug.
# when line is like "hi`hi`". all the parts will be in parts_inline.
if part in parts_inline:
code_text = part.strip("`")
font_size = 16
code_width = font.getsize(code_text)[0] + 10
code_height = font_size + 10
code_box = (x, y-5, x + code_width, y + code_height)
font = ImageFont.truetype(font_path, font_size)
draw.rounded_rectangle(code_box, radius=5, fill=(230, 230, 230), width=2) # 使用灰色填充矩形框作为引用背景
draw.text((x+5, y), code_text, font=font, fill=font_color)
x += code_width
else:
font_size = 16
font = ImageFont.truetype(font_path, font_size)
draw.text((x, y), part, font=font, fill=font_color)
x += font.getsize(part)[0]
y += font_size + 20
x = 10
else:
# 处理普通文本
font_size = 16
font = ImageFont.truetype(font_path, font_size)
draw.text((x, y), line, font=font, fill=font_color)
y += font_size + 5
return image
def save_temp_img(img: Image) -> str:
if not os.path.exists("temp"):
os.makedirs("temp")
@@ -175,4 +390,38 @@ def create_text_image(title: str, text: str, max_width=30, font_size=20):
p = save_temp_img(img)
return p
except Exception as e:
raise e
raise e
def create_markdown_image(text: str):
'''
markdown文本转图片。
返回:文件路径
'''
try:
img = render_markdown(text)
p = save_temp_img(img)
return p
except Exception as e:
raise e
def test_markdown():
# 示例使用
markdown_text = """
# Help Center
## 指令列表
`/help` - 显示帮助中心
`/start` - 开始使用
`/about` - 关于
`/feedback` - 反馈
## Plugins列表
`/plugins` - 显示插件列表
`/plugins enable <plugin_name>` - 启用插件
`/plugins disable <plugin_name>` - 禁用插件
> Hi, thanks for using this bot. If you have any questions, please contact me.
"""
image = render_markdown(markdown_text)
image.show() # 显示渲染后的图像