1
0
Files
PPTX/yaml2pptx.py
lanyuanxiaoyao 83ff827ad1 feat: add YAML validation with check command and auto-validation
Implements comprehensive validation before PPTX conversion to catch errors early. Includes element-level validation (colors, fonts, table consistency) and system-level validation (geometry, resources). Supports standalone check command and automatic validation during conversion.
2026-03-02 18:14:45 +08:00

206 lines
6.7 KiB
Python
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.
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.8"
# dependencies = [
# "python-pptx",
# "pyyaml",
# "flask",
# "watchdog",
# ]
# ///
"""
YAML to PPTX Converter
将 YAML 格式的演示文稿源文件转换为 PPTX 文件
使用方法:
# 子命令模式(推荐)
uv run yaml2pptx.py convert input.yaml output.pptx
uv run yaml2pptx.py check input.yaml
# 传统模式(向后兼容)
uv run yaml2pptx.py input.yaml output.pptx
uv run yaml2pptx.py input.yaml # 自动生成 input.pptx
"""
import sys
import argparse
from pathlib import Path
# 导入模块
from utils import log_info, log_success, log_error, log_progress
from loaders.yaml_loader import YAMLError
from core.presentation import Presentation
from renderers.pptx_renderer import PptxGenerator
from preview.server import start_preview_server
from validators import Validator
def parse_args():
"""解析命令行参数"""
# 检查是否使用了子命令
if len(sys.argv) > 1 and sys.argv[1] in ['check', 'convert']:
# 使用子命令解析
parser = argparse.ArgumentParser(
description="将 YAML 格式的演示文稿源文件转换为 PPTX 文件"
)
subparsers = parser.add_subparsers(dest='command')
# check 子命令
check_parser = subparsers.add_parser('check', help='验证 YAML 文件')
check_parser.add_argument("input", type=str, help="输入的 YAML 文件路径")
check_parser.add_argument("--template-dir", type=str, default=None, help="模板文件目录路径")
# convert 子命令
convert_parser = subparsers.add_parser('convert', help='转换 YAML 为 PPTX')
convert_parser.add_argument("input", type=str, help="输入的 YAML 文件路径")
convert_parser.add_argument("output", type=str, nargs="?", help="输出的 PPTX 文件路径(可选,默认为输入文件名.pptx")
convert_parser.add_argument("--template-dir", type=str, default=None, help="模板文件目录路径")
convert_parser.add_argument("--preview", action="store_true", help="启动浏览器预览模式")
convert_parser.add_argument("--port", type=int, default=None, help="预览服务器端口")
convert_parser.add_argument("--no-check", action="store_true", help="跳过自动验证")
return parser.parse_args()
else:
# 使用传统解析(转换命令)- 向后兼容
parser = argparse.ArgumentParser(
description="将 YAML 格式的演示文稿源文件转换为 PPTX 文件"
)
parser.add_argument("input", type=str, help="输入的 YAML 文件路径")
parser.add_argument("output", type=str, nargs="?", help="输出的 PPTX 文件路径(可选,默认为输入文件名.pptx")
parser.add_argument("--template-dir", type=str, default=None, help="模板文件目录路径")
parser.add_argument("--preview", action="store_true", help="启动浏览器预览模式")
parser.add_argument("--port", type=int, default=None, help="预览服务器端口")
parser.add_argument("--no-check", action="store_true", help="跳过自动验证")
args = parser.parse_args()
args.command = None # 标记为非子命令(向后兼容模式)
return args
def handle_check(args):
"""处理 check 子命令"""
try:
input_path = Path(args.input)
# 处理模板目录
template_dir = None
if args.template_dir:
template_dir = Path(args.template_dir)
# 执行验证
validator = Validator()
result = validator.validate(input_path, template_dir)
# 输出验证结果
print(result.format_output())
# 返回退出码
if result.has_errors():
sys.exit(1)
else:
sys.exit(0)
except Exception as e:
log_error(f"验证失败: {str(e)}")
import traceback
traceback.print_exc()
sys.exit(1)
def main():
"""主函数:加载 YAML → 渲染幻灯片 → 生成 PPTX"""
try:
args = parse_args()
# 处理 check 子命令
if args.command == 'check':
handle_check(args)
return
# 处理 convert 子命令或传统模式(向后兼容)
# convert 子命令和无子命令的行为相同
if args.command == 'convert' or args.command is None:
handle_convert(args)
return
except YAMLError as e:
log_error(str(e))
sys.exit(1)
except Exception as e:
log_error(f"未知错误: {str(e)}")
import traceback
traceback.print_exc()
sys.exit(1)
def handle_convert(args):
"""处理 convert 子命令或传统转换模式"""
# 检查是否提供了输入文件
if not args.input:
log_error("错误: 缺少输入文件参数")
sys.exit(1)
# 处理输入文件路径
input_path = Path(args.input)
# 预览模式
if args.preview:
start_preview_server(input_path, args.template_dir, args.port)
return
# PPTX 生成模式
# 处理输出文件路径
if args.output:
output_path = Path(args.output)
else:
# 自动生成输出文件名
output_path = input_path.with_suffix(".pptx")
log_info(f"开始转换: {input_path}")
log_info(f"输出文件: {output_path}")
# 自动验证(除非使用 --no-check
if not args.no_check:
log_info("验证 YAML 文件...")
template_dir = Path(args.template_dir) if args.template_dir else None
validator = Validator()
result = validator.validate(input_path, template_dir)
# 如果有错误,输出并终止
if result.has_errors():
print("\n" + result.format_output())
log_error("验证失败,转换已终止")
sys.exit(1)
# 如果有警告,输出但继续
if result.warnings:
print("\n" + result.format_output())
print() # 空行
# 1. 加载演示文稿
log_info("加载演示文稿...")
pres = Presentation(input_path, templates_dir=args.template_dir)
# 2. 创建 PPTX 生成器
log_info(f"创建演示文稿 ({pres.size})...")
generator = PptxGenerator(pres.size)
# 3. 渲染所有幻灯片
slides_data = pres.data.get('slides', [])
total_slides = len(slides_data)
for i, slide_data in enumerate(slides_data, 1):
log_progress(i, total_slides, f"处理幻灯片")
rendered_slide = pres.render_slide(slide_data)
generator.add_slide(rendered_slide, input_path.parent)
# 4. 保存 PPTX 文件
log_info("保存 PPTX 文件...")
generator.save(output_path)
log_success(f"转换完成: {output_path}")
if __name__ == "__main__":
main()