pptx/round-trip-layout-inheritance--override-present

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: —

Metadata

Feature idpptx/round-trip-layout-inheritance--override-present
Formatpptx
Categoryround-trip
Familypptx/round-trip-layout-inheritance
Axis valuescase=override-present
Spececma-376-5-part-1 § 19.3.1.45 a:themeOverride

XPath assertions

ID / partPredicateXPathpython-pptxpptxjs
slide-1-present
ppt/slides/slide1.xml
exist
The slide itself must still serialise after the round trip — baseline sanity.
//p:sld
slide-layout-rel-preserved
ppt/slides/_rels/slide1.xml.rels
exist
The 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-preserved
ppt/slideLayouts/_rels/slideLayout6.xml.rels
exist
The 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-relationship
ppt/slides/_rels/slide1.xml.rels
exist
Positive 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']

Render assertions

No render assertions declared.

Generator source

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()

Manifest (expanded)

{
  "$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

Fixture file not found on disk.

Reference preview

No rendered reference is available for this case.

Spec 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).