1
0
Files
PPTX/docs/development/extending.md
lanyuanxiaoyao 124ef0e5ce refactor: 重构文档结构,采用渐进式信息披露模式
将 README.md 拆分为多个专题文档,减少认知负荷:
- 用户文档迁移到 docs/ (用户指南、元素、模板、参考等)
- 开发文档迁移到 docs/development/ (架构、模块、规范)
- README.md 精简至 ~290 行,仅保留概览和导航
- 删除 README_DEV.md,内容已迁移
- 归档 OpenSpec 变更 refactor-docs-progressive-disclosure
2026-03-06 15:11:36 +08:00

281 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
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.
# 扩展指南
本文档说明如何扩展 yaml2pptx 的功能。
## 添加新元素类型
假设要添加 `VideoElement`
### 1. 在 core/elements.py 中定义数据类
```python
from dataclasses import dataclass, field
@dataclass
class VideoElement:
type: str = 'video'
src: str = ''
box: list = field(default_factory=lambda: [1, 1, 4, 3])
autoplay: bool = False
def __post_init__(self):
if not self.src:
raise ValueError("视频元素必须指定 src")
if len(self.box) != 4:
raise ValueError("box 必须包含 4 个数字")
```
### 2. 在工厂函数中添加分支
```python
def create_element(elem_dict: dict):
elem_type = elem_dict.get('type')
if elem_type == 'text':
return TextElement(**elem_dict)
elif elem_type == 'image':
return ImageElement(**elem_dict)
# ... 其他类型 ...
elif elem_type == 'video':
return VideoElement(**elem_dict)
else:
raise ValueError(f"Unknown element type: {elem_type}")
```
### 3. 在 PptxGenerator 中实现渲染方法
```python
def _render_element(self, slide, elem, base_path):
# ... 其他类型 ...
elif isinstance(elem, VideoElement):
self._render_video(slide, elem, base_path)
def _render_video(self, slide, elem: VideoElement, base_path):
"""实现视频渲染逻辑"""
movie = slide.shapes.add_movie(
str(Path(base_path) / elem.src),
left=Inches(elem.box[0]),
top=Inches(elem.box[1]),
width=Inches(elem.box[2]),
height=Inches(elem.box[3])
)
if elem.autoplay:
movie.click.action = pp.action.Action(pyppote.xmlns.namespace('p').MSO_ANIMATION_VIDEO_CLICK)
```
### 4. 在 HtmlRenderer 中实现渲染方法
```python
def render_slide(self, slide_data, index, base_path=None):
elements_html = ""
for elem in slide_data:
# ... 其他类型 ...
elif isinstance(elem, VideoElement):
elements_html += self.render_video(elem, base_path)
return self.SLIDE_TEMPLATE.format(content=elements_html)
def render_video(self, elem: VideoElement, base_path):
"""实现 HTML 视频渲染"""
src_path = str(Path(base_path) / elem.src) if base_path else elem.src
autoplay_attr = "autoplay" if elem.autoplay else ""
return f'''
<video src="{src_path}" {autoplay_attr}
style="position:absolute; left:{elem.box[0]*96}px; top:{elem.box[1]*96}px;
width:{elem.box[2]*96}px; height:{elem.box[3]*96}px;">
</video>
'''
```
### 5. 更新验证器
如果需要验证视频文件:
```python
# validators/resource.py
def validate_video(self, src, slide_index, elem_index):
"""检查视频文件是否存在"""
video_path = self.base_dir / src
if not video_path.exists():
return ValidationIssue(
level="ERROR",
message=f"视频文件不存在: {src}",
location=f"[幻灯片 {slide_index + 1}, 元素 {elem_index + 1}]",
code="VIDEO_FILE_NOT_FOUND"
)
return None
```
## 添加新渲染器
假设要添加 PDF 渲染器:
### 1. 创建 renderers/pdf_renderer.py
```python
from core.elements import TextElement, ImageElement, ShapeElement, TableElement
class PdfRenderer:
def __init__(self, size="16:9"):
# 初始化 PDF 库
self.size = size
# ...
def add_slide(self, slide_data, base_path=None):
"""添加页面"""
# 实现...
pass
def _render_element(self, page, elem, base_path):
"""渲染元素到 PDF 页面"""
if isinstance(elem, TextElement):
self._render_text(page, elem)
elif isinstance(elem, ImageElement):
self._render_image(page, elem, base_path)
elif isinstance(elem, ShapeElement):
self._render_shape(page, elem)
elif isinstance(elem, TableElement):
self._render_table(page, elem)
def _render_text(self, page, elem):
"""渲染文本到 PDF"""
# 实现...
pass
def _render_image(self, page, elem, base_path):
"""渲染图片到 PDF"""
# 实现...
pass
def _render_shape(self, page, elem):
"""渲染形状到 PDF"""
# 实现...
pass
def _render_table(self, page, elem):
"""渲染表格到 PDF"""
# 实现...
pass
def save(self, output_path):
"""保存 PDF 文件"""
# 实现...
pass
```
### 2. 在 yaml2pptx.py 中添加 PDF 模式
```python
from renderers.pdf_renderer import PdfRenderer
def main():
# ... 解析参数 ...
if args.pdf:
# PDF 生成模式
generator = PdfRenderer(size=args.size)
# ... 渲染逻辑
```
### 3. 添加命令行参数
```python
def parse_args():
parser = argparse.ArgumentParser(description='YAML to PPTX converter')
subparsers = parser.add_subparsers(dest='command', help='子命令')
# ... 其他命令 ...
# PDF 命令
pdf_parser = subparsers.add_parser('pdf', help='生成 PDF')
pdf_parser.add_argument('input', help='输入的 YAML 文件')
pdf_parser.add_argument('output', help='输出的 PDF 文件', nargs='?')
pdf_parser.add_argument('--template', help='模板库文件路径')
pdf_parser.add_argument('--size', default='16:9', choices=['16:9', '4:3'])
return parser.parse_args()
```
## 添加新的验证规则
### 1. 在 validators/ 中创建新的验证器
```python
# validators/custom.py
class CustomValidator:
def __init__(self):
pass
def validate(self, presentation):
"""执行自定义验证"""
issues = []
# 验证逻辑
return issues
```
### 2. 在主验证器中集成
```python
# validators/validator.py
class Validator:
def __init__(self, ...):
# ...
self.custom_validator = CustomValidator()
def validate_presentation(self, presentation):
# ...
# 调用自定义验证器
custom_issues = self.custom_validator.validate(presentation)
result.infos.extend(custom_issues)
```
## 测试新功能
### 1. 创建测试文件
```python
# tests/unit/test_video_element.py
import pytest
from core.elements import VideoElement, create_element
def test_create_video_element():
elem_dict = {
'type': 'video',
'src': 'test.mp4',
'box': [1, 1, 4, 3],
'autoplay': True
}
elem = create_element(elem_dict)
assert isinstance(elem, VideoElement)
assert elem.autoplay is True
def test_video_element_without_src():
with pytest.raises(ValueError, match="必须指定 src"):
VideoElement(src='', box=[1, 1, 4, 3])
```
### 2. 运行测试
```bash
uv run pytest tests/unit/test_video_element.py -v
```
## 提交变更
1. 更新相关文档
2. 添加测试
3. 运行完整测试套件
4. 提交 Pull Request
## 相关文档
- [架构设计](architecture.md) - 代码结构
- [Elements 模块](modules/elements.md) - 元素抽象层
[返回开发文档索引](../README.md)