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

6.9 KiB
Raw Permalink Blame History

扩展指南

本文档说明如何扩展 yaml2pptx 的功能。

添加新元素类型

假设要添加 VideoElement

1. 在 core/elements.py 中定义数据类

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. 在工厂函数中添加分支

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 中实现渲染方法

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 中实现渲染方法

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. 更新验证器

如果需要验证视频文件:

# 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

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 模式

from renderers.pdf_renderer import PdfRenderer

def main():
    # ... 解析参数 ...
    if args.pdf:
        # PDF 生成模式
        generator = PdfRenderer(size=args.size)
        # ... 渲染逻辑

3. 添加命令行参数

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/ 中创建新的验证器

# validators/custom.py

class CustomValidator:
    def __init__(self):
        pass

    def validate(self, presentation):
        """执行自定义验证"""
        issues = []
        # 验证逻辑
        return issues

2. 在主验证器中集成

# 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. 创建测试文件

# 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. 运行测试

uv run pytest tests/unit/test_video_element.py -v

提交变更

  1. 更新相关文档
  2. 添加测试
  3. 运行完整测试套件
  4. 提交 Pull Request

相关文档

返回开发文档索引