CtrlK
BlogDocsLog inGet started
Tessl Logo

metis-strategy/metis-pptx

Create or edit PowerPoint presentations. Dual-mode skill: (1) Editing mode preserves existing templates via Open XML unpack/edit/repack when an existing .pptx is provided. (2) Generation mode creates new Metis-branded decks from a design system with 36 composable components and 5 layout grids. Includes brand extraction for client decks and visual QA via PowerPoint COM. Triggers on deck, slides, presentation, PPT, or any .pptx request.

72

Quality

90%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files

visual_qa.pyscripts/

"""Export PPTX slides as images using PowerPoint COM automation.

Uses Microsoft PowerPoint to render each slide as a high-fidelity PNG image
for visual quality assurance inspection.

Usage:
    python visual_qa.py <pptx_file> [output_dir] [--dpi 150] [--format png|jpg]

Examples:
    python visual_qa.py output.pptx qa_images/
    python visual_qa.py output.pptx --dpi 200
"""

import argparse
import os
import subprocess
import sys
import time


def _powerpoint_is_running():
    try:
        out = subprocess.check_output(
            ["tasklist", "/FI", "IMAGENAME eq POWERPNT.EXE", "/NH"],
            stderr=subprocess.DEVNULL, text=True,
        )
        return "POWERPNT.EXE" in out
    except Exception:
        return False


def export_slides(pptx_path, output_dir, dpi=150, fmt="PNG"):
    """Export each slide from a PPTX file as an image using PowerPoint COM.

    Args:
        pptx_path: Path to the PPTX file.
        output_dir: Directory to save exported images.
        dpi: Resolution in dots per inch (default 150).
        fmt: Image format - 'PNG' or 'JPG' (default 'PNG').

    Returns:
        List of exported image file paths.
    """
    try:
        import win32com.client
    except ImportError:
        print("Error: pywin32 is not installed. Install with: pip install pywin32",
              file=sys.stderr)
        sys.exit(1)

    pptx_path = os.path.abspath(pptx_path)
    output_dir = os.path.abspath(output_dir)

    if not os.path.exists(pptx_path):
        print(f"Error: {pptx_path} does not exist", file=sys.stderr)
        sys.exit(1)

    os.makedirs(output_dir, exist_ok=True)

    # Use DispatchEx to create a NEW isolated PowerPoint instance
    # (avoids interfering with any running PowerPoint)
    was_running = _powerpoint_is_running()
    ppt = None
    prs = None
    try:
        ppt = win32com.client.DispatchEx("PowerPoint.Application")
        ppt.DisplayAlerts = 0  # ppAlertsNone

        # Open read-only, headless (no window)
        prs = ppt.Presentations.Open(
            pptx_path,
            ReadOnly=True,
            Untitled=False,
            WithWindow=False,
        )

        # Calculate export dimensions from slide size and DPI
        # PowerPoint reports dimensions in points (72 points = 1 inch)
        slide_w_pts = prs.PageSetup.SlideWidth
        slide_h_pts = prs.PageSetup.SlideHeight
        scale = dpi / 72.0
        export_w = int(slide_w_pts * scale)
        export_h = int(slide_h_pts * scale)

        total = prs.Slides.Count
        exported = []
        ext = fmt.lower()

        for i in range(1, total + 1):
            slide = prs.Slides(i)
            out_file = os.path.join(output_dir, f"slide-{i}.{ext}")
            slide.Export(out_file, fmt, export_w, export_h)
            exported.append(out_file)
            print(f"  Exported slide {i}/{total}: {out_file}")

        print(f"\nExported {len(exported)} slides to {output_dir}")
        return exported

    except Exception as e:
        print(f"Error during export: {e}", file=sys.stderr)
        sys.exit(1)

    finally:
        try:
            if prs is not None:
                prs.Close()
        except Exception:
            pass
        try:
            if ppt is not None and not was_running:
                ppt.Quit()
        except Exception:
            pass
        # Give PowerPoint a moment to fully terminate
        time.sleep(1)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description="Export PPTX slides as images for visual QA"
    )
    parser.add_argument("pptx_file", help="PPTX file to export")
    parser.add_argument(
        "output_dir",
        nargs="?",
        default="qa_images",
        help="Output directory (default: qa_images/)",
    )
    parser.add_argument(
        "--dpi",
        type=int,
        default=150,
        help="Export resolution in DPI (default: 150)",
    )
    parser.add_argument(
        "--format",
        choices=["png", "jpg", "PNG", "JPG"],
        default="PNG",
        dest="fmt",
        help="Image format (default: PNG)",
    )
    args = parser.parse_args()

    export_slides(args.pptx_file, args.output_dir, dpi=args.dpi, fmt=args.fmt.upper())

SKILL.md

tile.json