455 lines
15 KiB
Python
Executable File
455 lines
15 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""
|
||
高级图片生成器
|
||
使用ImageMagick创建专业的技术资讯图片
|
||
"""
|
||
|
||
import os
|
||
import subprocess
|
||
import tempfile
|
||
from datetime import datetime
|
||
import json
|
||
|
||
class AdvancedImageGenerator:
|
||
"""高级图片生成器"""
|
||
|
||
def __init__(self):
|
||
self.temp_dir = "/tmp/xiaohongshu_images"
|
||
os.makedirs(self.temp_dir, exist_ok=True)
|
||
|
||
def create_tech_news_image(self, title, source, content_preview=None, style="modern"):
|
||
"""
|
||
创建技术资讯图片
|
||
|
||
Args:
|
||
title: 文章标题
|
||
source: 文章来源
|
||
content_preview: 内容预览(可选)
|
||
style: 图片风格(modern, minimal, gradient, tech)
|
||
|
||
Returns:
|
||
图片文件路径
|
||
"""
|
||
print(f"🎨 创建技术资讯图片 (风格: {style})...")
|
||
|
||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||
output_file = os.path.join(self.temp_dir, f"tech_news_{timestamp}.jpg")
|
||
|
||
# 根据风格选择生成方法
|
||
if style == "modern":
|
||
return self._create_modern_style(title, source, content_preview, output_file)
|
||
elif style == "minimal":
|
||
return self._create_minimal_style(title, source, output_file)
|
||
elif style == "gradient":
|
||
return self._create_gradient_style(title, source, output_file)
|
||
elif style == "tech":
|
||
return self._create_tech_style(title, source, output_file)
|
||
else:
|
||
return self._create_modern_style(title, source, content_preview, output_file)
|
||
|
||
def _create_modern_style(self, title, source, content_preview, output_file):
|
||
"""现代风格图片"""
|
||
try:
|
||
# 基础图片
|
||
cmd = [
|
||
'convert', '-size', '800x600',
|
||
'gradient:#1a237e-#4a148c', # 深蓝到紫色渐变
|
||
'-fill', 'white',
|
||
'-stroke', 'none',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(cmd, check=True)
|
||
|
||
# 添加标题
|
||
title_cmd = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans-Bold',
|
||
'-pointsize', '36',
|
||
'-fill', 'white',
|
||
'-gravity', 'North',
|
||
'-annotate', '+0+50', f'📰 技术资讯',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(title_cmd, check=True)
|
||
|
||
# 添加分隔线
|
||
line_cmd = [
|
||
'convert', output_file,
|
||
'-stroke', '#4fc3f7',
|
||
'-strokewidth', '3',
|
||
'-draw', 'line 50,120 750,120',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(line_cmd, check=True)
|
||
|
||
# 添加文章标题(分多行)
|
||
title_lines = self._split_text(title, 30)
|
||
for i, line in enumerate(title_lines[:3]):
|
||
y_offset = 150 + i * 40
|
||
text_cmd = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans',
|
||
'-pointsize', '24',
|
||
'-fill', 'white',
|
||
'-gravity', 'NorthWest',
|
||
'-annotate', f'+50+{y_offset}', line,
|
||
output_file
|
||
]
|
||
subprocess.run(text_cmd, check=True)
|
||
|
||
# 添加来源和时间
|
||
info_text = f"来源: {source} | 时间: {datetime.now().strftime('%Y-%m-%d %H:%M')}"
|
||
info_cmd = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans',
|
||
'-pointsize', '18',
|
||
'-fill', '#b0bec5',
|
||
'-gravity', 'SouthWest',
|
||
'-annotate', '+50+50', info_text,
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(info_cmd, check=True)
|
||
|
||
# 添加自动化标识
|
||
auto_cmd = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans',
|
||
'-pointsize', '16',
|
||
'-fill', '#4fc3f7',
|
||
'-gravity', 'SouthEast',
|
||
'-annotate', '+50+50', 'IT资讯自动化系统',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(auto_cmd, check=True)
|
||
|
||
print(f"✅ 现代风格图片创建成功: {output_file}")
|
||
return output_file
|
||
|
||
except Exception as e:
|
||
print(f"❌ 创建现代风格图片失败: {e}")
|
||
return self._create_fallback_image(title, source, output_file)
|
||
|
||
def _create_minimal_style(self, title, source, output_file):
|
||
"""极简风格图片"""
|
||
try:
|
||
# 纯色背景
|
||
cmd = [
|
||
'convert', '-size', '800x600',
|
||
'xc:#f5f5f5', # 浅灰色背景
|
||
'-fill', '#333333',
|
||
'-stroke', 'none',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(cmd, check=True)
|
||
|
||
# 添加标题
|
||
title_cmd = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans-Bold',
|
||
'-pointsize', '32',
|
||
'-fill', '#2196F3', # 蓝色
|
||
'-gravity', 'North',
|
||
'-annotate', '+0+80', '技术资讯',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(title_cmd, check=True)
|
||
|
||
# 添加文章标题
|
||
title_lines = self._split_text(title, 35)
|
||
for i, line in enumerate(title_lines[:2]):
|
||
y_offset = 180 + i * 45
|
||
text_cmd = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans',
|
||
'-pointsize', '26',
|
||
'-fill', '#333333',
|
||
'-gravity', 'Center',
|
||
'-annotate', f'+0+{y_offset}', line,
|
||
output_file
|
||
]
|
||
subprocess.run(text_cmd, check=True)
|
||
|
||
# 添加底部信息
|
||
info_cmd = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans',
|
||
'-pointsize', '18',
|
||
'-fill', '#666666',
|
||
'-gravity', 'South',
|
||
'-annotate', '+0+80', f'{source} • {datetime.now().strftime("%Y-%m-%d")}',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(info_cmd, check=True)
|
||
|
||
print(f"✅ 极简风格图片创建成功: {output_file}")
|
||
return output_file
|
||
|
||
except Exception as e:
|
||
print(f"❌ 创建极简风格图片失败: {e}")
|
||
return self._create_fallback_image(title, source, output_file)
|
||
|
||
def _create_gradient_style(self, title, source, output_file):
|
||
"""渐变风格图片"""
|
||
try:
|
||
# 创建渐变背景
|
||
cmd = [
|
||
'convert', '-size', '800x600',
|
||
'gradient:#1565C0-#0D47A1', # 蓝色渐变
|
||
'-fill', 'white',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(cmd, check=True)
|
||
|
||
# 添加技术图标
|
||
icon_cmd = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans',
|
||
'-pointsize', '80',
|
||
'-fill', 'rgba(255,255,255,0.2)',
|
||
'-gravity', 'Center',
|
||
'-annotate', '+0+0', '💻',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(icon_cmd, check=True)
|
||
|
||
# 添加标题
|
||
title_cmd = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans-Bold',
|
||
'-pointsize', '34',
|
||
'-fill', 'white',
|
||
'-gravity', 'Center',
|
||
'-annotate', '+0-100', '技术前沿',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(title_cmd, check=True)
|
||
|
||
# 添加文章标题
|
||
title_lines = self._split_text(title, 25)
|
||
for i, line in enumerate(title_lines[:2]):
|
||
y_offset = -20 + i * 45
|
||
text_cmd = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans',
|
||
'-pointsize', '22',
|
||
'-fill', 'white',
|
||
'-gravity', 'Center',
|
||
'-annotate', f'+0+{y_offset}', line,
|
||
output_file
|
||
]
|
||
subprocess.run(text_cmd, check=True)
|
||
|
||
# 添加来源
|
||
source_cmd = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans',
|
||
'-pointsize', '18',
|
||
'-fill', 'rgba(255,255,255,0.8)',
|
||
'-gravity', 'South',
|
||
'-annotate', '+0+100', f'来源: {source}',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(source_cmd, check=True)
|
||
|
||
print(f"✅ 渐变风格图片创建成功: {output_file}")
|
||
return output_file
|
||
|
||
except Exception as e:
|
||
print(f"❌ 创建渐变风格图片失败: {e}")
|
||
return self._create_fallback_image(title, source, output_file)
|
||
|
||
def _create_tech_style(self, title, source, output_file):
|
||
"""科技风格图片"""
|
||
try:
|
||
# 创建科技感背景
|
||
cmd = [
|
||
'convert', '-size', '800x600',
|
||
'xc:#0a192f', # 深科技蓝
|
||
'-fill', '#64ffda', # 科技绿
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(cmd, check=True)
|
||
|
||
# 添加网格线
|
||
grid_cmd = [
|
||
'convert', output_file,
|
||
'-stroke', 'rgba(100,255,218,0.1)',
|
||
'-strokewidth', '1',
|
||
'-draw', 'rectangle 50,50 750,550',
|
||
'-draw', 'line 400,50 400,550',
|
||
'-draw', 'line 50,300 750,300',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(grid_cmd, check=True)
|
||
|
||
# 添加二进制代码背景
|
||
binary_bg = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans-Mono',
|
||
'-pointsize', '14',
|
||
'-fill', 'rgba(100,255,218,0.05)',
|
||
'-gravity', 'Center',
|
||
'-annotate', '+0+0', '01010101\n10101010\n01010101',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(binary_bg, check=True)
|
||
|
||
# 添加主标题
|
||
main_title = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans-Bold',
|
||
'-pointsize', '28',
|
||
'-fill', '#64ffda',
|
||
'-gravity', 'North',
|
||
'-annotate', '+0+80', 'TECH NEWS',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(main_title, check=True)
|
||
|
||
# 添加文章标题
|
||
title_lines = self._split_text(title, 30)
|
||
for i, line in enumerate(title_lines[:3]):
|
||
y_offset = 150 + i * 35
|
||
text_cmd = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans',
|
||
'-pointsize', '20',
|
||
'-fill', '#ccd6f6',
|
||
'-gravity', 'NorthWest',
|
||
'-annotate', f'+100+{y_offset}', line,
|
||
output_file
|
||
]
|
||
subprocess.run(text_cmd, check=True)
|
||
|
||
# 添加底部信息
|
||
info_cmd = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans-Mono',
|
||
'-pointsize', '16',
|
||
'-fill', '#8892b0',
|
||
'-gravity', 'SouthWest',
|
||
'-annotate', '+100+80', f'SOURCE: {source}',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(info_cmd, check=True)
|
||
|
||
time_cmd = [
|
||
'convert', output_file,
|
||
'-font', 'DejaVu-Sans-Mono',
|
||
'-pointsize', '16',
|
||
'-fill', '#8892b0',
|
||
'-gravity', 'SouthEast',
|
||
'-annotate', '+100+80', f'TIME: {datetime.now().strftime("%H:%M:%S")}',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(time_cmd, check=True)
|
||
|
||
print(f"✅ 科技风格图片创建成功: {output_file}")
|
||
return output_file
|
||
|
||
except Exception as e:
|
||
print(f"❌ 创建科技风格图片失败: {e}")
|
||
return self._create_fallback_image(title, source, output_file)
|
||
|
||
def _create_fallback_image(self, title, source, output_file):
|
||
"""回退图片(最简单的图片)"""
|
||
try:
|
||
cmd = [
|
||
'convert', '-size', '800x600',
|
||
'xc:#2196F3', # 蓝色背景
|
||
'-fill', 'white',
|
||
'-pointsize', '30',
|
||
'-gravity', 'Center',
|
||
'-draw', f'text 0,0 "{title[:50]}"',
|
||
'-pointsize', '20',
|
||
'-draw', f'text 0,100 "来源: {source}"',
|
||
output_file
|
||
]
|
||
|
||
subprocess.run(cmd, check=True)
|
||
print(f"✅ 回退图片创建成功: {output_file}")
|
||
return output_file
|
||
|
||
except Exception as e:
|
||
print(f"❌ 回退图片也失败: {e}")
|
||
return None
|
||
|
||
def _split_text(self, text, max_length):
|
||
"""分割文本为多行"""
|
||
words = text.split()
|
||
lines = []
|
||
current_line = []
|
||
|
||
for word in words:
|
||
if len(' '.join(current_line + [word])) <= max_length:
|
||
current_line.append(word)
|
||
else:
|
||
if current_line:
|
||
lines.append(' '.join(current_line))
|
||
current_line = [word]
|
||
|
||
if current_line:
|
||
lines.append(' '.join(current_line))
|
||
|
||
return lines
|
||
|
||
def create_image_from_article(self, article_data, style="modern"):
|
||
"""从文章数据创建图片"""
|
||
title = article_data.get('title', '技术资讯')
|
||
source = article_data.get('source', 'IT资讯')
|
||
content_preview = article_data.get('content_preview')
|
||
|
||
return self.create_tech_news_image(title, source, content_preview, style)
|
||
|
||
def test_image_generator():
|
||
"""测试图片生成器"""
|
||
print("🧪 测试图片生成器...")
|
||
|
||
generator = AdvancedImageGenerator()
|
||
|
||
# 测试数据
|
||
test_article = {
|
||
'title': 'Elastic Releases Version 9.3.0 With Enhanced AI Tools and OTel Support',
|
||
'source': 'InfoQ',
|
||
'content_preview': 'Elastic发布了9.3.0版本,增强了AI工具和OTel支持...'
|
||
}
|
||
|
||
# 测试不同风格
|
||
styles = ['modern', 'minimal', 'gradient', 'tech']
|
||
|
||
for style in styles:
|
||
print(f"\n测试 {style} 风格...")
|
||
image_path = generator.create_tech_news_image(
|
||
test_article['title'],
|
||
test_article['source'],
|
||
test_article['content_preview'],
|
||
style
|
||
)
|
||
|
||
if image_path:
|
||
print(f"✅ 图片: {image_path}")
|
||
# 显示图片信息
|
||
subprocess.run(['ls', '-la', image_path])
|
||
else:
|
||
print(f"❌ 创建失败")
|
||
|
||
if __name__ == "__main__":
|
||
test_image_generator() |