Files
abot/utils/markdown_to_image.py
2025-05-16 09:42:57 +08:00

282 lines
11 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import markdown
from playwright.async_api import async_playwright
import os
import asyncio
# linux 下需要安装字体
# sudo apt-get install -y fonts-noto-cjk fonts-noto-cjk-extra
# 将 Markdown 字符串转换为 HTML
def md_str_to_html(md_content, output_html):
"""
将 Markdown 字符串转换为 HTML 文件,并添加支持中文和 Emoji 的样式。
:param md_content: 输入的 Markdown 字符串
:param output_html: 输出的 HTML 文件路径
"""
# 转换 Markdown 为 HTML启用额外功能如表格、代码高亮
html_content = markdown.markdown(md_content, extensions=['extra', 'codehilite'])
# 添加基本的 HTML 结构和样式,支持中文和 Emoji
css = """
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', 'Noto Sans CJK SC', 'Microsoft YaHei', sans-serif;
padding: 20px 25px;
line-height: 1.6;
max-width: 750px;
margin: 0 auto;
background-color: #f9f9f9;
color: #333;
border: 1px solid #f0f0f0;
font-size: 16px;
}
h1, h2, h3, h4, h5, h6 {
color: #222;
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.3;
}
h1 {
font-size: 2.2em;
padding-bottom: 12px;
border-bottom: 1px solid #eee;
text-align: center;
margin-bottom: 25px;
color: #1a1a1a;
}
h2 {
font-size: 1.8em;
padding-bottom: 10px;
margin-top: 30px;
border-bottom: 1px solid #eee;
color: #2c3e50;
}
h3 {
font-size: 1.5em;
margin-top: 25px;
padding-left: 12px;
border-left: 4px solid #ddd;
color: #34495e;
}
pre, code {
background-color: #f5f5f5;
padding: 12px;
border-radius: 4px;
font-family: 'Courier New', Courier, monospace;
font-size: 0.95em;
border: 1px solid #eee;
}
table {
border-collapse: collapse;
width: 100%;
margin: 18px 0;
background-color: white;
}
th, td {
border: 1px solid #eee;
padding: 10px 12px;
text-align: left;
}
th {
background-color: #fafafa;
font-weight: 600;
}
/* 确保 Emoji 正确渲染 */
span, p, li {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', 'Noto Sans CJK SC', 'Microsoft YaHei', sans-serif;
font-size: 16px;
}
p {
margin: 16px 0;
color: #333;
line-height: 1.8;
font-size: 16px;
}
ul, ol {
padding-left: 25px;
margin: 18px 0;
}
li {
margin: 8px 0;
line-height: 1.7;
font-size: 16px;
}
blockquote {
margin: 18px 0;
padding: 12px 18px;
background-color: #f8f8f8;
border-left: 5px solid #ddd;
color: #555;
font-size: 1em;
}
/* 强调样式 */
strong {
color: #222;
font-weight: 600;
}
/* 链接样式 */
a {
color: #3498db;
text-decoration: none;
}
a:hover {
text-decoration: underline;
color: #2980b9;
}
/* 星级评分样式 */
h3 em {
color: #fa8c16;
font-style: normal;
font-size: 1.1em;
}
/* 时间和标签样式 */
.time, .tag {
color: #777;
font-size: 0.95em;
}
/* 底部署名样式 */
.signature {
margin-top: 35px;
text-align: right;
color: #777;
font-size: 0.95em;
font-style: italic;
}
</style>
"""
# 写入 HTML 文件
with open(output_html, 'w', encoding='utf-8') as f:
f.write('<html><head>')
f.write('<meta charset="UTF-8">') # 确保 UTF-8 编码
f.write(css)
f.write('</head><body>')
f.write(html_content)
f.write('</body></html>')
# 使用 Playwright 将 HTML 渲染并截图(异步版)
async def html_to_image(html_file, output_image):
"""
使用 Playwright 加载 HTML 文件并截图(异步)。
"""
try:
async with async_playwright() as p:
# 检查系统类型
if os.name == 'nt': # Windows
possible_chrome_paths = [
r"C:\Users\Liu_WIN10\AppData\Local\Google\Chrome\Application\chrome.exe",
r"C:\Users\Liu-OPEN\AppData\Local\Google\Chrome\Application\chrome.exe",
r"C:\Program Files\Google\Chrome\Application\chrome.exe",
r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
]
browser_path = None
for path in possible_chrome_paths:
if os.path.exists(path):
browser_path = path
print(f"找到浏览器路径: {browser_path}")
break
if browser_path:
browser = await p.chromium.launch(executable_path=browser_path)
else:
print("未找到指定的Chrome浏览器路径尝试使用默认浏览器")
# 尝试安装 Playwright 浏览器
try:
import subprocess
print("正在安装 Playwright 浏览器...")
subprocess.run(["playwright", "install"], check=True)
print("Playwright 浏览器安装完成")
except Exception as install_error:
print(f"安装 Playwright 浏览器失败: {install_error}")
browser = await p.chromium.launch()
else: # Linux
try:
# 尝试安装 Playwright 浏览器
import subprocess
print("正在安装 Playwright 浏览器...")
subprocess.run(["playwright", "install"], check=True)
print("Playwright 浏览器安装完成")
except Exception as install_error:
print(f"安装 Playwright 浏览器失败: {install_error}")
browser = await p.chromium.launch()
page = await browser.new_page()
await page.goto(f'file://{os.path.abspath(html_file)}')
await page.set_viewport_size({"width": 750, "height": 800})
await page.wait_for_timeout(500)
await page.screenshot(path=output_image, full_page=True)
await browser.close()
except Exception as e:
print(f"浏览器操作失败: {e}")
if "Executable doesn't exist" in str(e):
print("请运行 'playwright install' 命令安装必要的浏览器组件")
raise
# 主函数:从字符串转换 Markdown 到图片(异步版)
async def convert_md_str_to_image(md_content, output_image):
"""
将 Markdown 字符串转换为图片(异步)。
"""
temp_html = 'temp_output.html'
md_str_to_html(md_content, temp_html)
await html_to_image(temp_html, output_image)
os.remove(temp_html)
print(f"图片已生成:{output_image}")
return os.path.abspath(output_image)
# 示例使用
if __name__ == "__main__":
# 示例 Markdown 字符串(包含中文和 Emoji
md_content = """
# 🌟「4KED康复训练群 - 2023年3月26日 总结」🌟
## 📊 今日数据快报
- **总消息数**:📩 165 条
- **最活跃时段**:🔥 11:00 - 12:00 (📈 25 条/小时)
- **聊天时段**:🕒 09:41 - 16:40
## 🌌 话题总结
### 1⃣ 【开源OA项目讨论】 ⭐⭐⭐⭐⭐
🕒 **聊天时段**09:42 - 09:54 (👥 5 人参与)
🔍 **话题回顾**
本次讨论围绕 **开源OA项目** 展开大家探讨目前市场上有哪些可选的项目。啧啧用户们的热情高涨啊菜提出想要了解免费OA项目的需求水牛和刘政则分享了过往的经验认为如今的开源项目越来越少比起大厂的项目很多选择都不够灵活。通过这样的讨论群友们互相支招真是一场“技术头脑风暴”
> **金句回顾**"真是心血来潮搞OA不是小事" —— [@啊菜]
📌 **额外信息**:讨论中提及了 **开源OA项目的网站链接**,有兴趣的可以去看看!
### 2⃣ 【智能医疗AI的落地方向】 ⭐⭐⭐⭐
🕒 **聊天时段**10:29 - 10:39 (👥 7 人参与)
🔍 **高能讨论**
在这一话题中,用户们讨论了 **医疗行业中AI大模型落地的方向**。纯洁善良互助有爱提出了医疗知识库和对话式AI的想法引发了其他用户的热烈回响纷纷分享了智能问诊、病例解析等方向。大家的参与热情可见一斑兴致勃勃讨论未来的医学科技趋势
> **精华总结**"基层干事的人最懂需求!" —— [@纯洁善良互助有爱]
### 3⃣ 【趣谈工作与生活】 ⭐⭐⭐
🕒 **聊天时段**10:00 - 11:00 (👥 8 人参与)
🔍 **讨论亮点**
在轻松愉快的氛围中,用户们围绕工作和生活进行了有趣的讨论,尤其是关于“带薪休假”的轻松话题,大家分享了自己的工作经验和休闲方式。水牛和刘政重返了一个“带薪拉屎”的幽默梗,令群聊气氛瞬间活跃,友好的互动让人忍俊不禁!
### 4⃣ 【电子产品的讨论】 ⭐⭐⭐
🕒 **聊天时段**12:00 - 14:00 (👥 6 人参与)
🔍 **精彩瞬间**
此话题围绕 **近期电子产品和技术讨论** 展开,参与者们活跃分享各自对电子产品的见解。用户们聊天中不乏对新技术的吐槽与调侃,《特斯拉在美国被砸事件》等话题也层出不穷,是一个技术与笑点并存的轻松话题!
> **幽默补充**"这不是数字经济就是电子的ED" —— [@水牛]
### 5⃣ 【现实生活和社会现象】 ⭐⭐⭐
🕒 **聊天时段**14:30 - 16:25 (👥 7 人参与)
🔍 **讨论小结**
用户们在沉着交谈的同时关注了现实生活中的社会现象。“开车的礼仪”、“不买卖积分”等成为了热门话题刘政和Y.Shuang的告诫触发了共鸣引发参与者们对生活中各种现象的探讨与反思令人深思。
## 🎖️ 今日荣誉榜
🏆 **群聊 MVP**[@水牛]
👑 **获奖理由**
✅ 发起多个热门话题
✅ 贡献多种幽默段子
✅ **金句输出**"带薪拉屎,太美了!"
✨ *本总结由 AI 自动生成,快来看看你今天是不是最靓的崽!🔥*
"""
spath = asyncio.run(convert_md_str_to_image(md_content, "output.png"))
print(spath)