A slide that overrides its inherited master theme via a themeOverride part is a common inheritance-chain trap: the slide holds a relationship to themeOverride1.xml, the Content_Types entry registers it, and both must survive a load/save cycle. This family covers the positive case (override present) and the negative case (inherit from master — no override must materialise out of thin air).
Library verdicts: python-pptx: — pptxjs: —
| Feature id | pptx/round-trip-layout-inheritance--override-present |
|---|---|
| Format | pptx |
| Category | round-trip |
| Family | pptx/round-trip-layout-inheritance |
| Axis values | case=override-present |
| Spec | ecma-376-5-part-1 § 19.3.1.45 a:themeOverride |
| ID / part | Predicate | XPath | python-pptx | pptxjs |
|---|---|---|---|---|
slide-1-presentppt/slides/slide1.xml | existThe slide itself must still serialise after the round trip — baseline sanity. | //p:sld | — | — |
slide-layout-rel-preservedppt/slides/_rels/slide1.xml.rels | existThe slide -> slideLayout relationship is the first link in the inheritance chain and must survive. | //r:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout'] | — | — |
layout-master-rel-preservedppt/slideLayouts/_rels/slideLayout6.xml.rels | existThe slideLayout -> slideMaster relationship is the second link. A load/save that rebuilds parts but drops this rel breaks inheritance silently. | //r:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster'] | — | — |
theme-override-relationshipppt/slides/_rels/slide1.xml.rels | existPositive case: the themeOverride rel must be present after round-trip. Negative case: it must NOT have materialised. | //r:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/themeOverride'] | — | — |
No render assertions declared.
scripts/gen_round_trip_layout_inheritance_pptx.py
#!/usr/bin/env python3
"""Generate a pptx layout-inheritance round-trip fixture.
Load the source (a slide-master-theme-override W2-E fixture) via
python-pptx and save it to the named output. Deliberately shares its
body with :mod:`gen_round_trip_identity_pptx` — the distinguishing
behaviour is asserted by the feature manifest, not by the script.
"""
from __future__ import annotations
import argparse
import zipfile
from pathlib import Path
from pptx import Presentation # type: ignore[import-not-found]
def _rezip_with_epoch_timestamps(path: Path) -> None:
tmp = path.with_suffix(path.suffix + ".tmp")
with zipfile.ZipFile(path, "r") as src, zipfile.ZipFile(
tmp, "w", zipfile.ZIP_DEFLATED
) as dst:
for info in src.infolist():
data = src.read(info.filename)
info.date_time = (1980, 1, 1, 0, 0, 0)
dst.writestr(info, data)
tmp.replace(path)
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--source", required=True)
ap.add_argument("--out", required=True)
args = ap.parse_args()
out = Path(args.out)
out.parent.mkdir(parents=True, exist_ok=True)
Presentation(args.source).save(str(out))
_rezip_with_epoch_timestamps(out)
if __name__ == "__main__":
main()
{
"$schema": "../manifest.schema.json",
"id": "pptx/round-trip-layout-inheritance--override-present",
"kind": "literal",
"title": "Round-trip preserves themeOverride and layout inheritance chain",
"format": "pptx",
"category": "round-trip",
"summary": "A slide that overrides its inherited master theme via a themeOverride part is a common inheritance-chain trap: the slide holds a relationship to themeOverride1.xml, the Content_Types entry registers it, and both must survive a load/save cycle. This family covers the positive case (override present) and the negative case (inherit from master — no override must materialise out of thin air).",
"spec": {
"source": "ecma-376-5-part-1",
"clause": "19.3.1.45",
"element": "a:themeOverride",
"xsd_reference": "spec/ecma-376-5/part-1/xsd/pml.xsd",
"notes": "Borrows the existing slide-master-theme-override fixtures from W2-E. The override case asserts both the part and the relationship survive; the inherit case asserts they remain absent (a naive save that always emits a themeOverride would break the negative case)."
},
"fixtures": {
"machine": "pptx/round-trip-layout-inheritance--override-present"
},
"round_trip": {
"library": "python-pptx",
"source_fixture": "pptx/slide-master-theme-override--override-via-themeOverride"
},
"generator": {
"python": "scripts/gen_round_trip_layout_inheritance_pptx.py",
"arg_template": "--source fixtures/{case.source}.pptx --out fixtures/pptx/round-trip-layout-inheritance--{case.id}.pptx"
},
"_expansion": {
"parent_id": "pptx/round-trip-layout-inheritance",
"bindings": {
"case": "override-present"
}
},
"assertions": [
{
"id": "slide-1-present",
"part": "ppt/slides/slide1.xml",
"namespaces": {
"p": "http://schemas.openxmlformats.org/presentationml/2006/main"
},
"xpath": "//p:sld",
"must": "exist",
"description": "The slide itself must still serialise after the round trip — baseline sanity."
},
{
"id": "slide-layout-rel-preserved",
"part": "ppt/slides/_rels/slide1.xml.rels",
"namespaces": {
"r": "http://schemas.openxmlformats.org/package/2006/relationships"
},
"xpath": "//r:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout']",
"must": "exist",
"description": "The slide -> slideLayout relationship is the first link in the inheritance chain and must survive."
},
{
"id": "layout-master-rel-preserved",
"part": "ppt/slideLayouts/_rels/slideLayout6.xml.rels",
"namespaces": {
"r": "http://schemas.openxmlformats.org/package/2006/relationships"
},
"xpath": "//r:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster']",
"must": "exist",
"description": "The slideLayout -> slideMaster relationship is the second link. A load/save that rebuilds parts but drops this rel breaks inheritance silently."
},
{
"id": "theme-override-relationship",
"part": "ppt/slides/_rels/slide1.xml.rels",
"namespaces": {
"r": "http://schemas.openxmlformats.org/package/2006/relationships"
},
"xpath": "//r:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/themeOverride']",
"must": "exist",
"description": "Positive case: the themeOverride rel must be present after round-trip. Negative case: it must NOT have materialised."
}
]
}
Fixture file not found on disk.
No rendered reference is available for this case.