PPTX icon resolver: skip degraded refs from stencil buckets

Symptom on the GCP→OCI PostgreSQL deck: the OCI PostgreSQL icon bbox
rendered as empty space — labels and surrounding ADs all visible,
but no icon where the postgres stencil should be.

Root cause: ``_lookup_icon_ref`` returned the first hit from
``stencils.database_icons`` without checking it was cloneable.
The bucket happens to ship one degraded entry — ``database`` is
``tag=sp`` with ``bbox=None`` (a label-strip leaf, not the real
icon group) — so ``_clone_translated_block`` emitted a zero-size
shape. The 19 viable refs for the same key in
``shape_library.entries`` were ignored because the early-return in
``database_icons`` won.

Fix: extract a shared ``_is_viable_ref`` predicate (slide_path +
child_index/node_path + non-empty bbox + supported tag) and apply
it before returning from BOTH stencil buckets (``database_icons``,
``sample_icon_refs``). On miss, fall through to
``shape_library_entries`` where ``_select_preferred_ref`` already
filtered the same way.

Affects every alias that routes to a degraded stencil entry —
``postgresql`` / ``postgres`` / ``oci_postgresql`` were the obvious
fallout because they all alias to ``database``. The Architecture
Center reconstructions on shape-library-only slugs were unaffected.

Verified by re-rendering examples/output-gcp-virginia-oci-postgres-
adbs-clone/*.pptx — the OCI PostgreSQL bbox now shows the icon
(teal stencil + "Database" intrinsic caption) consistently with
the ADB-S and Refreshable Clone bboxes next to it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
root
2026-04-27 11:03:23 -03:00
parent 7f8cba7f30
commit 198861f533

View File

@@ -966,24 +966,41 @@ class NativePPTXDiagramRenderer:
return candidates return candidates
def _lookup_icon_ref(self, key: str) -> dict | None: def _lookup_icon_ref(self, key: str) -> dict | None:
# Stencil buckets sometimes ship a degraded entry — e.g.
# database_icons['database'] is tag=sp with bbox=None, which
# would clone as a zero-size shape (the "OCI PostgreSQL bbox
# but no icon" symptom Diego hit on the GCP↔OCI deck). Skip
# any ref that fails the viability check and fall through to
# the next source (sample_icon_refs, then shape_library, where
# a usable grpSp ref usually exists).
database_icons = (self.index.get("stencils") or {}).get("database_icons") or {} database_icons = (self.index.get("stencils") or {}).get("database_icons") or {}
if key in database_icons: candidate = database_icons.get(key)
return database_icons[key] if candidate and self._is_viable_ref(candidate):
if key in self.sample_icon_refs: return candidate
return self.sample_icon_refs[key] sample = self.sample_icon_refs.get(key)
if sample and self._is_viable_ref(sample):
return sample
refs = self.shape_library_entries.get(key) or [] refs = self.shape_library_entries.get(key) or []
if refs: if refs:
return self._select_preferred_ref(refs) return self._select_preferred_ref(refs)
return None return None
@staticmethod
def _is_viable_ref(ref: dict) -> bool:
"""A ref must point at a real cloneable shape (slide path,
index/path-into-shape-tree, non-empty bbox, supported tag) for
``_clone_translated_block`` to emit visible geometry."""
if not ref or not ref.get("slide_path"):
return False
if ref.get("child_index") is None and not ref.get("node_path"):
return False
bbox = ref.get("bbox") or {}
if not bbox.get("cx") or not bbox.get("cy"):
return False
return ref.get("tag") in {"grpSp", "pic", "sp"}
def _select_preferred_ref(self, refs: list[dict]) -> dict | None: def _select_preferred_ref(self, refs: list[dict]) -> dict | None:
viable = [ viable = [ref for ref in refs if self._is_viable_ref(ref)]
ref for ref in refs
if (ref.get("child_index") is not None or ref.get("node_path"))
and ref.get("slide_path")
and (ref.get("bbox") or {}).get("cx")
and ref.get("tag") in {"grpSp", "pic", "sp"}
]
if not viable: if not viable:
return refs[0] if refs else None return refs[0] if refs else None