"""
HTML 渲染器模块
将元素对象渲染为 HTML 代码,用于浏览器预览。
"""
from pathlib import Path
from core.elements import TextElement, ImageElement, ShapeElement, TableElement
# 固定 DPI 用于单位转换
DPI = 96
class HtmlRenderer:
"""HTML 渲染器,将元素渲染为 HTML"""
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'
渲染错误: {str(e)}
'
return f'''
幻灯片 {index + 1}
{elements_html}
'''
def render_text(self, elem: TextElement):
"""
将文本元素转换为 HTML
Args:
elem: TextElement 对象
Returns:
str: HTML 代码
"""
style = f"""
left: {elem.box[0] * DPI}px;
top: {elem.box[1] * DPI}px;
width: {elem.box[2] * DPI}px;
height: {elem.box[3] * 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('<', '<').replace('>', '>')
return f'{content}
'
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] * DPI}px;
top: {elem.box[1] * DPI}px;
width: {elem.box[2] * DPI}px;
height: {elem.box[3] * 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''
def render_table(self, elem: TableElement):
"""
将表格元素转换为 HTML
Args:
elem: TableElement 对象
Returns:
str: HTML 代码
"""
table_style = f"""
left: {elem.position[0] * DPI}px;
top: {elem.position[1] * 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('<', '<').replace('>', '>')
cells_html += f'{cell_content} | '
rows_html += f'{cells_html}
'
return f''
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] * DPI}px;
top: {elem.box[1] * DPI}px;
width: {elem.box[2] * DPI}px;
height: {elem.box[3] * DPI}px;
"""
return f'
'