pptx/preserve-alternate-content

W6-B tombstone: a slide authored with a Markup-Compatibility block (<mc:AlternateContent> carrying an <mc:Choice Requires="a14"> subtree and an <mc:Fallback> subtree) must still carry both branches after python-pptx round-trips it. MC is how Office carries forward-looking features (a14:, p14:, p15:) without breaking older readers — losing either the Choice or the Fallback subtree silently destroys fidelity in either a modern or a legacy reader. The generator re-saves `shp-mc-alternate-content.pptx` from the python-pptx feature fixtures and the assertions confirm both branches survive on slide1.

Library verdicts: python-pptx: — pptxjs: —

Metadata

Feature idpptx/preserve-alternate-content
Formatpptx
Categoryround-trip-fidelity
Spececma-376-5-part-3 § 10.2.1 mc:AlternateContent

XPath assertions

ID / partPredicateXPathpython-pptxpptxjs
alternate-content-wrapper-survives
ppt/slides/slide1.xml
exist
Slide1 must still carry an <mc:AlternateContent> inside spTree after round-trip.
/p:sld/p:cSld/p:spTree/mc:AlternateContent
choice-branch-survives
ppt/slides/slide1.xml
exist
The <mc:Choice> branch must survive.
/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Choice
choice-requires-attribute-preserved
ppt/slides/slide1.xml
equal = a14
The Requires attribute (a14) on <mc:Choice> must be preserved verbatim.
/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Choice/@Requires
fallback-branch-survives
ppt/slides/slide1.xml
exist
The <mc:Fallback> branch must survive.
/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Fallback
choice-shape-preserved
ppt/slides/slide1.xml
exist
The shape inside the Choice branch must survive (python-pptx must not collapse the Choice tree into a single fallback shape).
/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Choice/p:sp

Render assertions

No render assertions declared.

Generator source

scripts/gen_preserve_alternate_content.py

#!/usr/bin/env python3
"""Generate the ``preserve-alternate-content`` tombstone fixture.

Tombstone generator: loads an Office-authored fixture containing
``<mc:AlternateContent>`` with ``mc:Choice`` / ``mc:Fallback`` children on
slide1, round-trips it through ``python-pptx``, and writes the re-saved
archive. The manifest then asserts that both the Choice and Fallback
branches still resolve on the saved slide.

Markup Compatibility (MC) is how PowerPoint carries forward-looking
features that older consumers cannot parse: the ``mc:Choice`` branch
requires a newer namespace (e.g. ``a14:`` Office 2010 drawingML); the
``mc:Fallback`` branch is a graceful-degradation tree renderable by any
reader. Stripping either branch on save is a silent fidelity bug.
"""

from __future__ import annotations

import argparse
import datetime as _dt
from pathlib import Path

from pptx import Presentation

_REPO_ROOT = Path(__file__).resolve().parent.parent
_REPRODUCIBLE_DT = _dt.datetime(2000, 1, 1, 0, 0, 0)

# Office-authored input that embeds <mc:AlternateContent> on slide1.
# See python-pptx/features/steps/test_files/shp-mc-alternate-content.pptx.
_DEFAULT_SOURCE = Path(
    "/home/ben/code/python-pptx/features/steps/test_files/"
    "shp-mc-alternate-content.pptx"
)


def _roundtrip(source: Path, out: Path) -> None:
    prs = Presentation(str(source))
    out.parent.mkdir(parents=True, exist_ok=True)
    prs.save(out, zip_date_time=_REPRODUCIBLE_DT)


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "--source",
        type=Path,
        default=_DEFAULT_SOURCE,
        help="Input .pptx containing <mc:AlternateContent> on slide1.",
    )
    parser.add_argument(
        "--out",
        required=True,
        help="Output fixture path (absolute or repo-relative).",
    )
    args = parser.parse_args()

    out_path = Path(args.out)
    if not out_path.is_absolute():
        out_path = _REPO_ROOT / out_path
    _roundtrip(args.source, out_path)
    print(out_path)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "pptx/preserve-alternate-content",
  "kind": "literal",
  "title": "Preserve <mc:AlternateContent> through python-pptx load+save",
  "format": "pptx",
  "category": "round-trip-fidelity",
  "summary": "W6-B tombstone: a slide authored with a Markup-Compatibility block (<mc:AlternateContent> carrying an <mc:Choice Requires=\"a14\"> subtree and an <mc:Fallback> subtree) must still carry both branches after python-pptx round-trips it. MC is how Office carries forward-looking features (a14:, p14:, p15:) without breaking older readers — losing either the Choice or the Fallback subtree silently destroys fidelity in either a modern or a legacy reader. The generator re-saves `shp-mc-alternate-content.pptx` from the python-pptx feature fixtures and the assertions confirm both branches survive on slide1.",
  "spec": {
    "source": "ecma-376-5-part-3",
    "clause": "10.2.1",
    "element": "mc:AlternateContent",
    "notes": "ECMA-376 Part 3 defines Markup Compatibility. <mc:AlternateContent> is a choice wrapper: exactly one <mc:Choice Requires=\"<prefix>\"> branch plus at most one <mc:Fallback>. Consumers that understand the Requires-listed namespaces render the Choice; others render the Fallback. python-pptx reads AlternateContent transparently via oxml; this tombstone verifies the wrapper and both branches still round-trip intact."
  },
  "fixtures": {
    "machine": "pptx/preserve-alternate-content"
  },
  "generator": {
    "python": "scripts/gen_preserve_alternate_content.py",
    "arg_template": "--out fixtures/pptx/preserve-alternate-content.pptx"
  },
  "assertions": [
    {
      "id": "alternate-content-wrapper-survives",
      "part": "ppt/slides/slide1.xml",
      "namespaces": {
        "p": "http://schemas.openxmlformats.org/presentationml/2006/main",
        "mc": "http://schemas.openxmlformats.org/markup-compatibility/2006"
      },
      "xpath": "/p:sld/p:cSld/p:spTree/mc:AlternateContent",
      "must": "exist",
      "description": "Slide1 must still carry an <mc:AlternateContent> inside spTree after round-trip."
    },
    {
      "id": "choice-branch-survives",
      "part": "ppt/slides/slide1.xml",
      "namespaces": {
        "p": "http://schemas.openxmlformats.org/presentationml/2006/main",
        "mc": "http://schemas.openxmlformats.org/markup-compatibility/2006"
      },
      "xpath": "/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Choice",
      "must": "exist",
      "description": "The <mc:Choice> branch must survive."
    },
    {
      "id": "choice-requires-attribute-preserved",
      "part": "ppt/slides/slide1.xml",
      "namespaces": {
        "p": "http://schemas.openxmlformats.org/presentationml/2006/main",
        "mc": "http://schemas.openxmlformats.org/markup-compatibility/2006"
      },
      "xpath": "/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Choice/@Requires",
      "must": "equal",
      "value": "a14",
      "description": "The Requires attribute (a14) on <mc:Choice> must be preserved verbatim."
    },
    {
      "id": "fallback-branch-survives",
      "part": "ppt/slides/slide1.xml",
      "namespaces": {
        "p": "http://schemas.openxmlformats.org/presentationml/2006/main",
        "mc": "http://schemas.openxmlformats.org/markup-compatibility/2006"
      },
      "xpath": "/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Fallback",
      "must": "exist",
      "description": "The <mc:Fallback> branch must survive."
    },
    {
      "id": "choice-shape-preserved",
      "part": "ppt/slides/slide1.xml",
      "namespaces": {
        "p": "http://schemas.openxmlformats.org/presentationml/2006/main",
        "mc": "http://schemas.openxmlformats.org/markup-compatibility/2006"
      },
      "xpath": "/p:sld/p:cSld/p:spTree/mc:AlternateContent/mc:Choice/p:sp",
      "must": "exist",
      "description": "The shape inside the Choice branch must survive (python-pptx must not collapse the Choice tree into a single fallback shape)."
    }
  ]
}

Fixture

Download preserve-alternate-content.pptx (27.7 KB)

Reference preview

No rendered reference is available for this case.

Spec notes

ECMA-376 Part 3 defines Markup Compatibility. <mc:AlternateContent> is a choice wrapper: exactly one <mc:Choice Requires="<prefix>"> branch plus at most one <mc:Fallback>. Consumers that understand the Requires-listed namespaces render the Choice; others render the Fallback. python-pptx reads AlternateContent transparently via oxml; this tombstone verifies the wrapper and both branches still round-trip intact.