5.6 KiB
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
.pyfiles 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
- Open with
Presentation(path); read slide size in inches. - Pull theme tokens (colors/fonts) directly from
ppt/theme/theme1.xmlviazipfile. - Walk each slide's shapes (recursing into groups), tallying tokens into
Counters. - Emit
styles,brands,template, andlayoutsections 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 totheme_color, and normalize to#RRGGBBortheme:<token>. -
Region/flow classification — bucket each shape's bbox center into
top/middle/bottom×left/center/right, and inferrow/column/gridfrom 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
- For each slide, build a root group bbox covering the full slide.
- Walk shapes recursively; groups become nested groups, leaf shapes become objects.
- Classify each object by
kind(text,table,image,chart,connector, …) and capture kind-specificcontentplusstyle. - Optionally write media to an asset dir, or base64-embed when no dir is given.
- Read speaker notes by resolving slide
_relsto theirnotesSlideparts.
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
textplus 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_imagewhen 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 viashape.part.related_part(rid). - Package media counts — open the
.pptxwithzipfileand count names underppt/media/andppt/embeddings/. - Notes — parse each
ppt/slides/_rels/slideN.xml.rels, follow thenotesSliderelationship, 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.