1
0
Files
PPTX/renderers/html_renderer.py
lanyuanxiaoyao f34405be36 feat: 移除图片适配模式功能
移除图片 fit 和 background 参数支持,简化图片渲染逻辑。系统恢复到直接使用 python-pptx 原生图片添加功能,图片将被拉伸到指定尺寸。

变更内容:
- 移除 ImageElement 的 fit 和 background 字段
- 移除 metadata.dpi 配置
- 删除 utils/image_utils.py 图片处理工具模块
- 删除 validators/image_config.py 验证器
- 简化 PPTX 和 HTML 渲染器的图片处理逻辑
- HTML 渲染器使用硬编码 DPI=96(Web 标准)
- 删除相关测试文件(单元测试、集成测试、e2e 测试)
- 更新规格文档和用户文档
- 保留 Pillow 依赖用于未来可能的图片处理需求

影响:
- 删除 11 个文件
- 修改 10 个文件
- 净减少 1558 行代码
- 所有 402 个测试通过

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-03-04 14:23:12 +08:00

180 lines
5.6 KiB
Python

"""
HTML 渲染器模块
将元素对象渲染为 HTML 代码,用于浏览器预览。
"""
from pathlib import Path
from core.elements import TextElement, ImageElement, ShapeElement, TableElement
class HtmlRenderer:
"""HTML 渲染器,将元素渲染为 HTML"""
def __init__(self):
"""
初始化 HTML 渲染器
"""
self.dpi = 96 # 硬编码 Web 标准 DPI
def render_slide(self, slide_data, index, base_path):
"""
渲染单个幻灯片为 HTML
Args:
slide_data: 包含 background 和 elements 的字典
index: 幻灯片索引
base_path: 基础路径
Returns:
str: 幻灯片的 HTML 代码
"""
elements_html = ""
bg_style = ""
if slide_data.get("background"):
bg = slide_data["background"]
if "color" in bg:
bg_style = f"background: {bg['color']};"
for elem in slide_data.get("elements", []):
try:
if isinstance(elem, TextElement):
elements_html += self.render_text(elem)
elif isinstance(elem, ShapeElement):
elements_html += self.render_shape(elem)
elif isinstance(elem, TableElement):
elements_html += self.render_table(elem)
elif isinstance(elem, ImageElement):
elements_html += self.render_image(elem, base_path)
except Exception as e:
elements_html += (
f'<div class="element" style="color: red;">渲染错误: {str(e)}</div>'
)
return f'''
<div class="slide" style="{bg_style}">
<div class="slide-number">幻灯片 {index + 1}</div>
{elements_html}
</div>
'''
def render_text(self, elem: TextElement):
"""
将文本元素转换为 HTML
Args:
elem: TextElement 对象
Returns:
str: HTML 代码
"""
style = f"""
left: {elem.box[0] * self.dpi}px;
top: {elem.box[1] * self.dpi}px;
width: {elem.box[2] * self.dpi}px;
height: {elem.box[3] * self.dpi}px;
font-size: {elem.font.get("size", 16)}pt;
color: {elem.font.get("color", "#000000")};
text-align: {elem.font.get("align", "left")};
{"font-weight: bold;" if elem.font.get("bold") else ""}
{"font-style: italic;" if elem.font.get("italic") else ""}
display: flex;
align-items: center;
white-space: normal;
overflow-wrap: break-word;
"""
content = (
elem.content.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
)
return f'<div class="element text-element" style="{style}">{content}</div>'
def render_shape(self, elem: ShapeElement):
"""
将形状元素转换为 HTML
Args:
elem: ShapeElement 对象
Returns:
str: HTML 代码
"""
border_radius = {
"rectangle": "0",
"ellipse": "50%",
"rounded_rectangle": "8px",
}.get(elem.shape, "0")
style = f"""
left: {elem.box[0] * self.dpi}px;
top: {elem.box[1] * self.dpi}px;
width: {elem.box[2] * self.dpi}px;
height: {elem.box[3] * self.dpi}px;
background: {elem.fill if elem.fill else "transparent"};
border-radius: {border_radius};
"""
if elem.line:
style += f"""
border: {elem.line.get("width", 1)}pt solid {elem.line.get("color", "#000000")};
"""
return f'<div class="element shape-element" style="{style}"></div>'
def render_table(self, elem: TableElement):
"""
将表格元素转换为 HTML
Args:
elem: TableElement 对象
Returns:
str: HTML 代码
"""
table_style = f"""
left: {elem.position[0] * self.dpi}px;
top: {elem.position[1] * self.dpi}px;
"""
rows_html = ""
for i, row in enumerate(elem.data):
cells_html = ""
for cell in row:
cell_style = f"font-size: {elem.style.get('font_size', 14)}pt;"
if i == 0:
if "header_bg" in elem.style:
cell_style += f"background: {elem.style['header_bg']};"
if "header_color" in elem.style:
cell_style += f"color: {elem.style['header_color']};"
cell_content = str(cell).replace("<", "&lt;").replace(">", "&gt;")
cells_html += f'<td style="{cell_style}">{cell_content}</td>'
rows_html += f"<tr>{cells_html}</tr>"
return f'<table class="element table-element" style="{table_style}">{rows_html}</table>'
def render_image(self, elem: ImageElement, base_path):
"""
将图片元素转换为 HTML
Args:
elem: ImageElement 对象
base_path: 基础路径
Returns:
str: HTML 代码
"""
img_path = Path(base_path) / elem.src if base_path else Path(elem.src)
# 基础样式
style = f"""
left: {elem.box[0] * self.dpi}px;
top: {elem.box[1] * self.dpi}px;
width: {elem.box[2] * self.dpi}px;
height: {elem.box[3] * self.dpi}px;
"""
return f'<img class="element image-element" src="file://{img_path.absolute()}" style="{style}">'