Skip to main content
Glama

Office MCP Server

by walkingzzzy
ppt_basic.py8.31 kB
"""PowerPoint 基础操作模块.""" from pathlib import Path from typing import Any, Optional from pptx import Presentation from pptx.util import Inches from loguru import logger from office_mcp_server.config import config from office_mcp_server.utils.file_manager import FileManager class PowerPointBasicOperations: """PowerPoint 基础操作类.""" def __init__(self) -> None: """初始化基础操作类.""" self.file_manager = FileManager() def create_presentation( self, filename: str, title: str = "", template_path: Optional[str] = None ) -> dict[str, Any]: """创建 PowerPoint 演示文稿. Args: filename: 文件名 title: 演示标题(可选) template_path: 模板文件路径(可选,如果提供则基于模板创建) """ try: file_path = self.file_manager.validate_file_path(filename) self.file_manager.validate_file_extension(filename, [".pptx"]) output_path = config.paths.output_dir / file_path.name self.file_manager.ensure_directory(output_path.parent) # 如果提供了模板路径,基于模板创建 if template_path: template_file = Path(template_path) if not template_file.exists(): # 尝试在 output_dir 中查找模板 template_file = config.paths.output_dir / template_path if not template_file.exists(): raise FileNotFoundError(f"模板文件不存在: {template_path}") prs = Presentation(str(template_file)) logger.info(f"基于模板创建演示文稿: {template_file}") else: prs = Presentation() # 添加标题页(仅在非模板模式下) if title and not template_path: title_slide_layout = prs.slide_layouts[0] slide = prs.slides.add_slide(title_slide_layout) title_shape = slide.shapes.title title_shape.text = title prs.save(str(output_path)) logger.info(f"PowerPoint 演示文稿创建成功: {output_path}") return { "success": True, "message": "PowerPoint 演示文稿创建成功", "filename": str(output_path), "title": title, "from_template": bool(template_path), } except Exception as e: logger.error(f"创建 PowerPoint 演示文稿失败: {e}") return {"success": False, "message": f"创建失败: {str(e)}"} def add_slide( self, filename: str, layout_index: int = 1, title: str = "" ) -> dict[str, Any]: """添加幻灯片.""" try: file_path = config.paths.output_dir / filename self.file_manager.validate_file_path(file_path, must_exist=True) prs = Presentation(str(file_path)) slide_layout = prs.slide_layouts[layout_index] slide = prs.slides.add_slide(slide_layout) # 设置标题 if title and slide.shapes.title: slide.shapes.title.text = title prs.save(str(file_path)) logger.info(f"幻灯片添加成功: {file_path}") return { "success": True, "message": "幻灯片添加成功", "filename": str(file_path), "slide_count": len(prs.slides), } except Exception as e: logger.error(f"添加幻灯片失败: {e}") return {"success": False, "message": f"添加失败: {str(e)}"} def delete_slide(self, filename: str, slide_index: int) -> dict[str, Any]: """删除幻灯片.""" try: file_path = config.paths.output_dir / filename self.file_manager.validate_file_path(file_path, must_exist=True) prs = Presentation(str(file_path)) if slide_index >= len(prs.slides): raise ValueError(f"幻灯片索引 {slide_index} 超出范围") # 删除幻灯片 rId = prs.slides._sldIdLst[slide_index].rId prs.part.drop_rel(rId) del prs.slides._sldIdLst[slide_index] prs.save(str(file_path)) logger.info(f"幻灯片删除成功: {file_path}") return { "success": True, "message": "幻灯片删除成功", "filename": str(file_path), "remaining_slides": len(prs.slides), } except Exception as e: logger.error(f"删除幻灯片失败: {e}") return {"success": False, "message": f"删除失败: {str(e)}"} def move_slide( self, filename: str, from_index: int, to_index: int ) -> dict[str, Any]: """移动幻灯片位置.""" try: file_path = config.paths.output_dir / filename self.file_manager.validate_file_path(file_path, must_exist=True) prs = Presentation(str(file_path)) slide_count = len(prs.slides) if from_index >= slide_count or to_index >= slide_count: raise ValueError(f"幻灯片索引超出范围") # 移动幻灯片 slides = list(prs.slides._sldIdLst) slide = slides.pop(from_index) slides.insert(to_index, slide) # 更新幻灯片列表 prs.slides._sldIdLst.clear() for s in slides: prs.slides._sldIdLst.append(s) prs.save(str(file_path)) logger.info(f"幻灯片移动成功: {file_path}") return { "success": True, "message": f"幻灯片从位置 {from_index} 移动到位置 {to_index}", "filename": str(file_path), } except Exception as e: logger.error(f"移动幻灯片失败: {e}") return {"success": False, "message": f"移动失败: {str(e)}"} def duplicate_slide(self, filename: str, slide_index: int) -> dict[str, Any]: """复制幻灯片.""" try: file_path = config.paths.output_dir / filename self.file_manager.validate_file_path(file_path, must_exist=True) prs = Presentation(str(file_path)) if slide_index >= len(prs.slides): raise ValueError(f"幻灯片索引 {slide_index} 超出范围") # 获取源幻灯片 source_slide = prs.slides[slide_index] # 创建新幻灯片 blank_slide_layout = source_slide.slide_layout dest_slide = prs.slides.add_slide(blank_slide_layout) # 复制所有形状 for shape in source_slide.shapes: el = shape.element newel = el.__class__(el) dest_slide.shapes._spTree.insert_element_before(newel, 'p:extLst') prs.save(str(file_path)) logger.info(f"幻灯片复制成功: {file_path}") return { "success": True, "message": f"幻灯片 {slide_index} 已复制", "filename": str(file_path), "total_slides": len(prs.slides), } except Exception as e: logger.error(f"复制幻灯片失败: {e}") return {"success": False, "message": f"复制失败: {str(e)}"} def get_presentation_info(self, filename: str) -> dict[str, Any]: """获取演示文稿信息.""" try: file_path = config.paths.output_dir / filename self.file_manager.validate_file_path(file_path, must_exist=True) prs = Presentation(str(file_path)) slide_count = len(prs.slides) logger.info(f"获取演示文稿信息成功: {file_path}") return { "success": True, "filename": str(file_path), "slide_count": slide_count, } except Exception as e: logger.error(f"获取演示文稿信息失败: {e}") return {"success": False, "message": f"获取失败: {str(e)}"}

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/walkingzzzy/office-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server