A Python library that simplifies editing Google Slides through SML (Slide Markup Language) - an XML-based markup optimized for programmatic and LLM-driven slide editing.
Instead of working with complex Google Slides API requests, extraslide lets you:
- Pull a presentation as SML to a file
- Modify the SML (programmatically, with an LLM, or in an editor)
- Diff and apply changes - the library handles the API calls
from extraslide import SlidesClient
client = SlidesClient(gateway_url="https://your-gateway.example.com")
url = "https://docs.google.com/presentation/d/your-presentation-id/edit"
# Pull presentation to file
client.pull(url, "presentation.sml")
# Edit the file (manually, programmatically, or with an LLM)...
# Preview changes (dry run)
requests = client.diff("presentation.sml", "presentation_edited.sml")
for req in requests:
print(req)
# Apply changes to Google Slides
client.apply(url, "presentation.sml", "presentation_edited.sml")Google Slides API operations are complex. SML provides:
- Compact representation: All slide content in readable XML
- LLM-friendly: Language models efficiently edit XML markup
- Diff-based updates: Only changed elements are updated
- Complete coverage: Supports all major Google Slides element types
from extraslide import SlidesClient
client = SlidesClient(gateway_url="https://your-gateway.example.com")Fetches a Google Slides presentation and saves it as SML to a file.
client.pull("https://docs.google.com/presentation/d/ID/edit", "presentation.sml")Dry-run that returns the batchUpdate requests that would be generated without applying them.
requests = client.diff("presentation.sml", "presentation_edited.sml")
for req in requests:
print(f"{req['type']}: {req}")Applies changes to the presentation and returns the API response.
result = client.apply(url, "presentation.sml", "presentation_edited.sml")For programmatic use, string-based variants are available with the _s suffix:
Fetches a presentation and returns it as an SML string.
sml = client.pull_s("https://docs.google.com/presentation/d/ID/edit")Diffs two SML strings and returns the batchUpdate requests.
requests = client.diff_s(original_sml, edited_sml)Applies SML string changes to the presentation.
result = client.apply_s(url, original_sml, edited_sml)SML uses an HTML-inspired syntax with Tailwind-style utility classes:
<Presentation id="abc123" title="My Presentation" w="720pt" h="405pt">
<Images>
<Img id="img1" url="https://example.com/image.png"/>
</Images>
<Masters>
<Master id="m1" name="Simple Light">
<!-- Master slide elements -->
</Master>
</Masters>
<Layouts>
<Layout id="l1" master="m1" name="Title Slide">
<!-- Layout template elements -->
</Layout>
</Layouts>
<Slides>
<Slide id="s1" layout="l1" master="m1">
<TextBox id="title1" class="x-100 y-50 w-500 h-80">
<P class="text-align-center">
<T class="text-size-36 font-weight-bold text-color-#333333">
Slide Title
</T>
</P>
</TextBox>
<Rect id="shape1" class="x-100 y-200 w-300 h-150 fill-#4285f4 stroke-#000000 stroke-w-1"/>
<Image id="img1" src="img1" class="x-450 y-200 w-200 h-150"/>
</Slide>
<Slide id="s2" layout="l1" master="m1">
<!-- Second slide -->
</Slide>
</Slides>
</Presentation>| Element | Description |
|---|---|
<TextBox> |
Text container with paragraphs |
<Rect>, <RoundRect>, <Ellipse> |
Basic shapes |
<Triangle>, <Diamond>, <Pentagon>, <Hexagon> |
Polygons |
<Star5>, <Heart>, <Cloud> |
Special shapes |
<Image> |
Images (references <Img> in <Images>) |
<Line> |
Lines and connectors |
<Table> |
Tables with rows and cells |
<Video> |
Embedded videos |
<WordArt> |
Styled text art |
<SheetsChart> |
Embedded Google Sheets charts |
<Group> |
Grouped elements |
All styling uses utility classes:
<!-- Position and size (in points) -->
<Rect class="x-100 y-200 w-300 h-150"/>
<!-- Fill colors (with optional opacity) -->
<Rect class="fill-#4285f4"/>
<Rect class="fill-#4285f4/80"/> <!-- 80% opacity -->
<!-- Strokes -->
<Rect class="stroke-#d1d5db stroke-w-2"/>
<!-- Text styling -->
<T class="text-size-24 text-color-#333333 font-family-roboto font-weight-bold"/>
<!-- Paragraph alignment -->
<P class="text-align-center"/>
<!-- Content alignment within shapes -->
<TextBox class="content-middle"/>Text uses a paragraph (<P>) and text run (<T>) structure:
<TextBox id="tb1" class="x-50 y-100 w-400 h-200">
<P class="text-align-left">
<T class="text-size-18">Regular text </T>
<T class="text-size-18 font-weight-bold">bold text </T>
<T class="text-size-18 font-style-italic">italic text</T>
</P>
<P class="text-align-left">
<T class="text-size-14">Second paragraph</T>
</P>
</TextBox>from extraslide import SlidesClient
client = SlidesClient(gateway_url="https://gateway.example.com")
url = "https://docs.google.com/presentation/d/ID/edit"
# Pull to file
client.pull(url, "presentation.sml")
# Read, modify, and write back
from pathlib import Path
sml = Path("presentation.sml").read_text()
modified = sml.replace("{{company_name}}", "Acme Corp")
modified = modified.replace("{{date}}", "2024-01-15")
Path("presentation_edited.sml").write_text(modified)
# Apply changes
client.apply(url, "presentation.sml", "presentation_edited.sml")from extraslide import SlidesClient
client = SlidesClient(gateway_url="https://gateway.example.com")
url = "https://docs.google.com/presentation/d/ID/edit"
# Pull presentation
client.pull(url, "presentation.sml")
# Read SML for LLM editing
from pathlib import Path
original = Path("presentation.sml").read_text()
# Send SML to an LLM for editing
prompt = f"""Edit this presentation SML to:
1. Change the title to "Q4 Results"
2. Update the subtitle to "Financial Overview"
{original}
Return only the modified SML."""
modified = llm.generate(prompt)
Path("presentation_edited.sml").write_text(modified)
# Preview before applying
requests = client.diff("presentation.sml", "presentation_edited.sml")
print(f"Will apply {len(requests)} changes")
# Apply changes
client.apply(url, "presentation.sml", "presentation_edited.sml")from extraslide import SlidesClient
client = SlidesClient(gateway_url="https://gateway.example.com")
url = "https://docs.google.com/presentation/d/ID/edit"
# Pull and edit
client.pull(url, "presentation.sml")
from pathlib import Path
sml = Path("presentation.sml").read_text()
modified = sml.replace("Draft", "Final")
Path("presentation_edited.sml").write_text(modified)
# See what would change without applying
requests = client.diff("presentation.sml", "presentation_edited.sml")
for req in requests:
print(f" {req}")
# Apply if satisfied
if input("Apply changes? (y/n): ").lower() == "y":
client.apply(url, "presentation.sml", "presentation_edited.sml")After applying changes, always re-pull the SML before making more edits:
# First edit
client.pull(url, "original.sml")
# ... edit to edited.sml ...
client.apply(url, "original.sml", "edited.sml")
# For next edit, re-pull (SML is now stale)
client.pull(url, "original.sml") # Required!
# ... edit to edited.sml ...
client.apply(url, "original.sml", "edited.sml")pip install extraslide- Python 3.9+
- A gateway service for Google API authentication
- SML Syntax - Complete SML specification
- Reconciliation - How diffs are computed and applied
- Google Slides API - Underlying API reference
Under Development
This library is functional but still evolving. The API may change.