Files
playbook/antigravity-awesome-skills/skills/pptx-deck-creation/references/reference-deck-analysis-patterns.md
T
2026-07-20 00:03:02 +00:00

5.6 KiB
Raw Blame History

Reference-Deck Analysis Patterns

It describes how to approach PPTX extraction and style analysis with python-pptx, using short illustrative snippets — not a packaged module to copy wholesale.

  • Do not treat these snippets as bundled runtime modules.
  • Do not import from this file or recreate .py files under this skill as packaged resources.
  • When a task needs extraction or style analysis, write task-local code with python-pptx, using these patterns as reference.

Shared constants

Most helpers depend on two values: the EMU-per-inch conversion factor and the DrawingML namespace used when reading raw OOXML.

EMU_PER_INCH = 914400
DRAWING_NS = "{http://schemas.openxmlformats.org/drawingml/2006/main}"


def _inches(value: int) -> float:
    return round(int(value or 0) / EMU_PER_INCH, 4)

Style master: compact design context

Goal: summarize a reference deck into a compact "style master" (colors, fonts, font sizes, shape styles, layout/region usage) suitable for prompting a generator. Counters drive a most_common ranking; results are truncated by max_items.

Approach

  1. Open with Presentation(path); read slide size in inches.
  2. Pull theme tokens (colors/fonts) directly from ppt/theme/theme1.xml via zipfile.
  3. Walk each slide's shapes (recursing into groups), tallying tokens into Counters.
  4. Emit styles, brands, template, and layout sections from the top-N tallies.

Illustrative snippet

from collections import Counter

def analyze(presentation) -> dict:
    colors: Counter[str] = Counter()
    fonts: Counter[str] = Counter()
    for slide in presentation.slides:
        for shape in _iter_shapes(slide.shapes):
            colors.update(_shape_colors(shape).values())
            fonts.update(_text_styles(shape)["fonts"])
    return {
        "colors": [{"value": v, "count": c} for v, c in colors.most_common(10)],
        "fonts": [{"value": v, "count": c} for v, c in fonts.most_common(10)],
    }

Key helper patterns

  • Recursive shape walk — yield each shape, then recurse when it exposes .shapes:

    def _iter_shapes(shapes):
        for shape in shapes:
            yield shape
            if hasattr(shape, "shapes"):
                yield from _iter_shapes(shape.shapes)
    
  • Color normalization — read color.rgb, fall back to theme_color, and normalize to #RRGGBB or theme:<token>.

  • Region/flow classification — bucket each shape's bbox center into top/middle/bottom × left/center/right, and infer row / column / grid from the spread of centers.

  • Neutral filtering — treat colors with low channel spread (max-min <= 18) as neutrals so brand accents rank above grays.

Extractor: structured deck capture

Goal: turn a deck into a structured tree of slides → groups → objects, with optional media extraction and a parallel list of raw OOXML render elements.

Approach

  1. For each slide, build a root group bbox covering the full slide.
  2. Walk shapes recursively; groups become nested groups, leaf shapes become objects.
  3. Classify each object by kind (text, table, image, chart, connector, …) and capture kind-specific content plus style.
  4. Optionally write media to an asset dir, or base64-embed when no dir is given.
  5. Read speaker notes by resolving slide _rels to their notesSlide parts.

Illustrative snippet

def _bbox(shape) -> dict:
    return {
        "x": _inches(getattr(shape, "left", 0)),
        "y": _inches(getattr(shape, "top", 0)),
        "width": _inches(getattr(shape, "width", 0)),
        "height": _inches(getattr(shape, "height", 0)),
    }

def _kind(shape, shape_type: str) -> str:
    if getattr(shape, "has_table", False):
        return "table"
    if getattr(shape, "has_chart", False):
        return "chart"
    if "picture" in shape_type or getattr(shape, "image", None):
        return "image"
    if getattr(shape, "has_text_frame", False) and shape.text.strip():
        return "text"
    return "shape"

Kind-specific content notes

  • text — capture text plus rich paragraphs (runs with font size, bold, italic, color, hyperlink, paragraph alignment/level).
  • table — emit rows, plus row/col counts, widths/heights, banding flags, and merged-cell origins (span_rows / span_cols).
  • image — record alt text, crop fractions, and either a written asset path or base64 blob; flag missing_embedded_image when the blob is absent.
  • chart — record chart_type, optional title, categories, and series values.
  • line / connector — derive endpoints from the bbox plus flip flags, and read arrow head/tail types from the <a:ln> element.

Media & notes helpers

  • Embedded relationship ids — iterate the shape element and collect attributes ending in }embed, then resolve via shape.part.related_part(rid).
  • Package media counts — open the .pptx with zipfile and count names under ppt/media/ and ppt/embeddings/.
  • Notes — parse each ppt/slides/_rels/slideN.xml.rels, follow the notesSlide relationship, and join <a:t> text nodes.

Safe attribute access

python-pptx raises on many optional properties (fills, colors, line styles). Wrap reads in a small guard so extraction degrades gracefully:

def _safe_attr(value, name):
    if value is None:
        return None
    try:
        return getattr(value, name)
    except (AttributeError, TypeError, ValueError):
        return None

Apply the same defensive pattern around fill.type, line.width, color.rgb, shape.image, and chart/table accessors, since any of them can fail on real-world decks.