Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Create, edit, and inspect PowerPoint presentations with professional design and automated visual QA
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/add_slide.py
1"""Add a new slide to an unpacked PPTX directory.23Usage: python add_slide.py <unpacked_dir> <source>45The source can be:6- A slide file (e.g., slide2.xml) - duplicates the slide7- A layout file (e.g., slideLayout2.xml) - creates from layout89Examples:10python add_slide.py unpacked/ slide2.xml11# Duplicates slide2, creates slide5.xml1213python add_slide.py unpacked/ slideLayout2.xml14# Creates slide5.xml from slideLayout2.xml1516To see available layouts: ls unpacked/ppt/slideLayouts/1718Prints the <p:sldId> element to add to presentation.xml.19"""2021import re22import shutil23import sys24from pathlib import Path252627def get_next_slide_number(slides_dir: Path) -> int:28existing = [int(m.group(1)) for f in slides_dir.glob("slide*.xml")29if (m := re.match(r"slide(\d+)\.xml", f.name))]30return max(existing) + 1 if existing else 1313233def create_slide_from_layout(unpacked_dir: Path, layout_file: str) -> None:34slides_dir = unpacked_dir / "ppt" / "slides"35rels_dir = slides_dir / "_rels"36layouts_dir = unpacked_dir / "ppt" / "slideLayouts"3738layout_path = layouts_dir / layout_file39if not layout_path.exists():40print(f"Error: {layout_path} not found", file=sys.stderr)41sys.exit(1)4243next_num = get_next_slide_number(slides_dir)44dest = f"slide{next_num}.xml"45dest_slide = slides_dir / dest46dest_rels = rels_dir / f"{dest}.rels"4748slide_xml = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>49<p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">50<p:cSld>51<p:spTree>52<p:nvGrpSpPr>53<p:cNvPr id="1" name=""/>54<p:cNvGrpSpPr/>55<p:nvPr/>56</p:nvGrpSpPr>57<p:grpSpPr>58<a:xfrm>59<a:off x="0" y="0"/>60<a:ext cx="0" cy="0"/>61<a:chOff x="0" y="0"/>62<a:chExt cx="0" cy="0"/>63</a:xfrm>64</p:grpSpPr>65</p:spTree>66</p:cSld>67<p:clrMapOvr>68<a:masterClrMapping/>69</p:clrMapOvr>70</p:sld>'''71dest_slide.write_text(slide_xml, encoding="utf-8")7273rels_dir.mkdir(exist_ok=True)74rels_xml = f'''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>75<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">76<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/{layout_file}"/>77</Relationships>'''78dest_rels.write_text(rels_xml, encoding="utf-8")7980_add_to_content_types(unpacked_dir, dest)8182rid = _add_to_presentation_rels(unpacked_dir, dest)8384next_slide_id = _get_next_slide_id(unpacked_dir)8586print(f"Created {dest} from {layout_file}")87print(f'Add to presentation.xml <p:sldIdLst>: <p:sldId id="{next_slide_id}" r:id="{rid}"/>')888990def duplicate_slide(unpacked_dir: Path, source: str) -> None:91slides_dir = unpacked_dir / "ppt" / "slides"92rels_dir = slides_dir / "_rels"9394source_slide = slides_dir / source9596if not source_slide.exists():97print(f"Error: {source_slide} not found", file=sys.stderr)98sys.exit(1)99100next_num = get_next_slide_number(slides_dir)101dest = f"slide{next_num}.xml"102dest_slide = slides_dir / dest103104source_rels = rels_dir / f"{source}.rels"105dest_rels = rels_dir / f"{dest}.rels"106107shutil.copy2(source_slide, dest_slide)108109if source_rels.exists():110shutil.copy2(source_rels, dest_rels)111112rels_content = dest_rels.read_text(encoding="utf-8")113rels_content = re.sub(114r'\s*<Relationship[^>]*Type="[^"]*notesSlide"[^>]*/>\s*',115"\n",116rels_content,117)118dest_rels.write_text(rels_content, encoding="utf-8")119120_add_to_content_types(unpacked_dir, dest)121122rid = _add_to_presentation_rels(unpacked_dir, dest)123124next_slide_id = _get_next_slide_id(unpacked_dir)125126print(f"Created {dest} from {source}")127print(f'Add to presentation.xml <p:sldIdLst>: <p:sldId id="{next_slide_id}" r:id="{rid}"/>')128129130def _add_to_content_types(unpacked_dir: Path, dest: str) -> None:131content_types_path = unpacked_dir / "[Content_Types].xml"132content_types = content_types_path.read_text(encoding="utf-8")133134new_override = f'<Override PartName="/ppt/slides/{dest}" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>'135136if f"/ppt/slides/{dest}" not in content_types:137content_types = content_types.replace("</Types>", f" {new_override}\n</Types>")138content_types_path.write_text(content_types, encoding="utf-8")139140141def _add_to_presentation_rels(unpacked_dir: Path, dest: str) -> str:142pres_rels_path = unpacked_dir / "ppt" / "_rels" / "presentation.xml.rels"143pres_rels = pres_rels_path.read_text(encoding="utf-8")144145rids = [int(m) for m in re.findall(r'Id="rId(\d+)"', pres_rels)]146next_rid = max(rids) + 1 if rids else 1147rid = f"rId{next_rid}"148149new_rel = f'<Relationship Id="{rid}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/{dest}"/>'150151if f"slides/{dest}" not in pres_rels:152pres_rels = pres_rels.replace("</Relationships>", f" {new_rel}\n</Relationships>")153pres_rels_path.write_text(pres_rels, encoding="utf-8")154155return rid156157158def _get_next_slide_id(unpacked_dir: Path) -> int:159pres_path = unpacked_dir / "ppt" / "presentation.xml"160pres_content = pres_path.read_text(encoding="utf-8")161slide_ids = [int(m) for m in re.findall(r'<p:sldId[^>]*id="(\d+)"', pres_content)]162return max(slide_ids) + 1 if slide_ids else 256163164165def parse_source(source: str) -> tuple[str, str | None]:166if source.startswith("slideLayout") and source.endswith(".xml"):167return ("layout", source)168169return ("slide", None)170171172if __name__ == "__main__":173if len(sys.argv) != 3:174print("Usage: python add_slide.py <unpacked_dir> <source>", file=sys.stderr)175print("", file=sys.stderr)176print("Source can be:", file=sys.stderr)177print(" slide2.xml - duplicate an existing slide", file=sys.stderr)178print(" slideLayout2.xml - create from a layout template", file=sys.stderr)179print("", file=sys.stderr)180print("To see available layouts: ls <unpacked_dir>/ppt/slideLayouts/", file=sys.stderr)181sys.exit(1)182183unpacked_dir = Path(sys.argv[1])184source = sys.argv[2]185186if not unpacked_dir.exists():187print(f"Error: {unpacked_dir} not found", file=sys.stderr)188sys.exit(1)189190source_type, layout_file = parse_source(source)191192if source_type == "layout" and layout_file is not None:193create_slide_from_layout(unpacked_dir, layout_file)194else:195duplicate_slide(unpacked_dir, source)196