📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-07-20 00:03:02 +00:00
parent 47ce7f78dc
commit 2c00adccd5
1216 changed files with 10376 additions and 90058 deletions
@@ -0,0 +1,150 @@
# 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.
```python
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 `Counter`s.
4. Emit `styles`, `brands`, `template`, and `layout` sections from the top-N tallies.
### Illustrative snippet
```python
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`:
```python
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
```python
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:
```python
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.