1
0

feat: 添加图片适配模式支持

- 支持四种图片适配模式:stretch、contain、cover、center
- 支持背景色填充功能(contain 和 center 模式)
- 支持文档级 DPI 配置(metadata.dpi)
- PPTX 渲染器集成 Pillow 实现高质量图片处理
- HTML 渲染器使用 CSS object-fit 实现相同效果
- 添加完整的单元测试、集成测试和端到端测试
- 更新 README 文档和架构文档
- 模块化设计:utils/image_utils.py 图片处理工具模块
- 添加图片配置验证器:validators/image_config.py
- 向后兼容:未指定 fit 时默认使用 stretch 模式
This commit is contained in:
2026-03-04 10:29:21 +08:00
parent 16ca9d77cd
commit 19d6661381
32 changed files with 2310 additions and 57 deletions

View File

@@ -10,23 +10,28 @@ from pptx.util import Inches, Pt
from pptx.enum.text import PP_ALIGN
from pptx.enum.shapes import MSO_SHAPE
from pptx.dml.color import RGBColor
from PIL import Image
import tempfile
from core.elements import TextElement, ImageElement, ShapeElement, TableElement
from loaders.yaml_loader import YAMLError
from utils import hex_to_rgb
from utils.image_utils import inches_to_pixels, apply_fit_mode
class PptxGenerator:
"""PPTX 生成器,封装 python-pptx 操作"""
def __init__(self, size='16:9'):
def __init__(self, size='16:9', dpi=96):
"""
初始化 PPTX 生成器
Args:
size: 幻灯片尺寸("16:9""4:3"
dpi: DPI 配置,默认 96
"""
self.prs = PptxPresentation()
self.dpi = dpi
# 设置幻灯片尺寸
if size == '16:9':
@@ -148,9 +153,39 @@ class PptxGenerator:
# 获取位置和尺寸
x, y, w, h = [Inches(v) for v in elem.box]
# 添加图片
# 获取 fit 模式,默认为 stretch
fit = elem.fit if elem.fit else 'stretch'
try:
slide.shapes.add_picture(str(img_path), x, y, width=w, height=h)
# stretch 模式:直接使用 python-pptx 的原生处理
if fit == 'stretch':
slide.shapes.add_picture(str(img_path), x, y, width=w, height=h)
else:
# 其他模式:使用 Pillow 处理图片
# 打开图片
with Image.open(img_path) as img:
# 转换 box 尺寸为像素
box_width_px = int(inches_to_pixels(elem.box[2], self.dpi))
box_height_px = int(inches_to_pixels(elem.box[3], self.dpi))
box_size = (box_width_px, box_height_px)
# 应用 fit 模式
processed_img = apply_fit_mode(
img, box_size, fit, elem.background
)
# 保存处理后的图片到临时文件
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
processed_img.save(tmp.name, 'PNG')
tmp_path = tmp.name
try:
# 添加处理后的图片到幻灯片
slide.shapes.add_picture(tmp_path, x, y, width=w, height=h)
finally:
# 清理临时文件
Path(tmp_path).unlink(missing_ok=True)
except Exception as e:
raise YAMLError(f"添加图片失败: {img_path}: {str(e)}")