pptx/mc-alternate-content--show-media-in-preview

A family covering PowerPoint's pattern of advertising post-2007 features via <p:extLst>/<p:ext uri="{GUID}">, with the payload wrapped in <mc:AlternateContent> so older clients can fall back to an empty <mc:Fallback> and drop the feature silently. Two cases: (1) the Word-2016 chart-tracking creationId (p188 namespace), (2) the Word-2010 showMediaInPreview setting (p14 namespace).

Library verdicts: python-pptx: — pptxjs: —

Metadata

Feature idpptx/mc-alternate-content--show-media-in-preview
Formatpptx
Categorymarkup-compatibility
Familypptx/mc-alternate-content
Axis valuescase=show-media-in-preview
Spececma-376-5-part-3 § 10.2 mc:AlternateContent

XPath assertions

ID / partPredicateXPathpython-pptxpptxjs
extlst-ext-present-show-media-in-preview
ppt/slides/slide1.xml
exist
The slide must carry a <p:extLst> containing at least one <p:ext> wrapper.
//p:extLst/p:ext
ext-uri-attribute-show-media-in-preview
ppt/slides/slide1.xml
equal = {D42F4F17-F0D6-40B4-B0DA-55EDBFD4C8EE}
The <p:ext> must carry the Microsoft-allocated GUID URI identifying the extension.
//p:extLst/p:ext/@uri
alternate-content-inside-ext-show-media-in-preview
ppt/slides/slide1.xml
exist
The extension payload must be wrapped in <mc:AlternateContent>.
//p:extLst/p:ext/mc:AlternateContent
choice-requires-show-media-in-preview
ppt/slides/slide1.xml
equal = p14
<mc:Choice Requires="..."> must name the namespace prefix the modern branch relies on.
//p:extLst/p:ext/mc:AlternateContent/mc:Choice/@Requires
fallback-present-show-media-in-preview
ppt/slides/slide1.xml
exist
An <mc:Fallback> (empty or otherwise) must be present so readers lacking the Requires-listed extension have a defined branch to select.
//p:extLst/p:ext/mc:AlternateContent/mc:Fallback

Render assertions

No render assertions declared.

Generator source

scripts/gen_pptx_mc_alternate_content.py

#!/usr/bin/env python3
"""Generate ``fixtures/pptx/mc-alternate-content--<case>.pptx``.

Parameterised generator for the ``pptx/mc-alternate-content`` feature
family. Each case authors an ``<p:extLst>`` on the slide carrying an
``<p:ext uri="{GUID}">`` whose payload is wrapped in an
``<mc:AlternateContent>``. PowerPoint uses this pattern to expose
Word-2013+ / Word-2019+ features that older clients would otherwise
reject.

Cases:

* ``creation-id``: ``<p:ext uri="{BB962C8B-B14F-4119-B1C7-8E2F5F9F6F7A}">``
  carrying a ``p188:creationId`` per Word 2016+. The ``<mc:Choice>``
  declares ``Requires="p188"`` and contains the creationId element; the
  ``<mc:Fallback>`` is empty, which is the canonical "ignore this
  feature" fallback.

* ``show-media-in-preview``: a ``p14:`` extension advertising a Word
  2010 feature, with a ``<mc:Choice Requires="p14">`` branch holding
  ``<p14:showMediaInPreview val="1"/>`` and an empty fallback.
"""

from __future__ import annotations

import argparse
import datetime as _dt
from pathlib import Path

from pptx import Presentation
from pptx.oxml.ns import qn
from lxml import etree

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

_MC_NS = "http://schemas.openxmlformats.org/markup-compatibility/2006"
_P_NS = "http://schemas.openxmlformats.org/presentationml/2006/main"
_P14_NS = "http://schemas.microsoft.com/office/powerpoint/2010/main"
_P188_NS = "http://schemas.microsoft.com/office/powerpoint/2016/overrides"


def _creation_id_ext_xml() -> bytes:
    """<p:extLst> with a creationId (Word 2016+) in <mc:Choice Requires="p188">."""
    return f"""
    <p:extLst xmlns:p="{_P_NS}" xmlns:mc="{_MC_NS}" xmlns:p188="{_P188_NS}">
      <p:ext uri="{{BB962C8B-B14F-4119-B1C7-8E2F5F9F6F7A}}">
        <mc:AlternateContent>
          <mc:Choice Requires="p188">
            <p188:creationId val="2837716234"/>
          </mc:Choice>
          <mc:Fallback/>
        </mc:AlternateContent>
      </p:ext>
    </p:extLst>
    """.strip().encode("utf-8")


def _show_media_in_preview_ext_xml() -> bytes:
    """<p:extLst> with a p14:showMediaInPreview in <mc:Choice Requires="p14">."""
    return f"""
    <p:extLst xmlns:p="{_P_NS}" xmlns:mc="{_MC_NS}" xmlns:p14="{_P14_NS}">
      <p:ext uri="{{D42F4F17-F0D6-40B4-B0DA-55EDBFD4C8EE}}">
        <mc:AlternateContent>
          <mc:Choice Requires="p14">
            <p14:showMediaInPreview val="1"/>
          </mc:Choice>
          <mc:Fallback/>
        </mc:AlternateContent>
      </p:ext>
    </p:extLst>
    """.strip().encode("utf-8")


_BUILDERS = {
    "creation-id": _creation_id_ext_xml,
    "show-media-in-preview": _show_media_in_preview_ext_xml,
}


def _write(case: str, out: Path) -> None:
    prs = Presentation()
    slide = prs.slides.add_slide(prs.slide_layouts[0])
    slide.shapes.title.text = "Markup-compatibility demo"
    # Inject the <p:extLst> directly onto <p:sld>. It is the last
    # element in the sequence (p:cSld, p:clrMapOvr, p:transition,
    # p:timing, p:extLst), so a plain append() respects schema order
    # on a freshly-created slide that has no trailing siblings.
    ext_lst = etree.fromstring(_BUILDERS[case]())
    slide.element.append(ext_lst)
    out.parent.mkdir(parents=True, exist_ok=True)
    prs.save(out, zip_date_time=_REPRODUCIBLE_DT)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--case", required=True, choices=sorted(_BUILDERS))
    parser.add_argument("--out", required=True)
    args = parser.parse_args()
    out_path = Path(args.out)
    if not out_path.is_absolute():
        out_path = _REPO_ROOT / out_path
    _write(args.case, out_path)
    print(out_path)
    return 0


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

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "pptx/mc-alternate-content--show-media-in-preview",
  "kind": "literal",
  "title": "Markup Compatibility: mc:AlternateContent inside a slide extLst",
  "format": "pptx",
  "category": "markup-compatibility",
  "summary": "A family covering PowerPoint's pattern of advertising post-2007 features via <p:extLst>/<p:ext uri=\"{GUID}\">, with the payload wrapped in <mc:AlternateContent> so older clients can fall back to an empty <mc:Fallback> and drop the feature silently. Two cases: (1) the Word-2016 chart-tracking creationId (p188 namespace), (2) the Word-2010 showMediaInPreview setting (p14 namespace).",
  "spec": {
    "source": "ecma-376-5-part-3",
    "clause": "10.2",
    "element": "mc:AlternateContent",
    "notes": "ECMA-376 Part 3 §10.2 lets producers stash namespace-versioned extensions inside Part-1 <extLst>/<ext uri=\"{GUID}\"> wrappers. The URI is a Microsoft-allocated GUID; the payload opens with <mc:AlternateContent> whose <mc:Choice Requires=\"...\"> branch names the namespace prefix a reader needs (p14 for Word 2010, p15 for Word 2013, p188 for Word 2016). Empty <mc:Fallback/> is the canonical 'ignore this feature' shape — older clients that match only the fallback discard the ext. Round-tripping the extension intact matters: if python-pptx's PARSE_TREE silently drops <mc:AlternateContent> inside <p:ext>, the attached feature (chart tracking IDs, smart-art version markers, ink, ...) is lost on re-save."
  },
  "fixtures": {
    "machine": "pptx/mc-alternate-content--show-media-in-preview"
  },
  "generator": {
    "python": "scripts/gen_pptx_mc_alternate_content.py",
    "arg_template": "--case {case.id} --out fixtures/pptx/mc-alternate-content--{case.id}.pptx"
  },
  "_expansion": {
    "parent_id": "pptx/mc-alternate-content",
    "bindings": {
      "case": "show-media-in-preview"
    }
  },
  "assertions": [
    {
      "id": "extlst-ext-present-show-media-in-preview",
      "part": "ppt/slides/slide1.xml",
      "namespaces": {
        "p": "http://schemas.openxmlformats.org/presentationml/2006/main"
      },
      "xpath": "//p:extLst/p:ext",
      "must": "exist",
      "description": "The slide must carry a <p:extLst> containing at least one <p:ext> wrapper."
    },
    {
      "id": "ext-uri-attribute-show-media-in-preview",
      "part": "ppt/slides/slide1.xml",
      "namespaces": {
        "p": "http://schemas.openxmlformats.org/presentationml/2006/main"
      },
      "xpath": "//p:extLst/p:ext/@uri",
      "must": "equal",
      "value": "{D42F4F17-F0D6-40B4-B0DA-55EDBFD4C8EE}",
      "description": "The <p:ext> must carry the Microsoft-allocated GUID URI identifying the extension."
    },
    {
      "id": "alternate-content-inside-ext-show-media-in-preview",
      "part": "ppt/slides/slide1.xml",
      "namespaces": {
        "p": "http://schemas.openxmlformats.org/presentationml/2006/main",
        "mc": "http://schemas.openxmlformats.org/markup-compatibility/2006"
      },
      "xpath": "//p:extLst/p:ext/mc:AlternateContent",
      "must": "exist",
      "description": "The extension payload must be wrapped in <mc:AlternateContent>."
    },
    {
      "id": "choice-requires-show-media-in-preview",
      "part": "ppt/slides/slide1.xml",
      "namespaces": {
        "p": "http://schemas.openxmlformats.org/presentationml/2006/main",
        "mc": "http://schemas.openxmlformats.org/markup-compatibility/2006"
      },
      "xpath": "//p:extLst/p:ext/mc:AlternateContent/mc:Choice/@Requires",
      "must": "equal",
      "value": "p14",
      "description": "<mc:Choice Requires=\"...\"> must name the namespace prefix the modern branch relies on."
    },
    {
      "id": "fallback-present-show-media-in-preview",
      "part": "ppt/slides/slide1.xml",
      "namespaces": {
        "p": "http://schemas.openxmlformats.org/presentationml/2006/main",
        "mc": "http://schemas.openxmlformats.org/markup-compatibility/2006"
      },
      "xpath": "//p:extLst/p:ext/mc:AlternateContent/mc:Fallback",
      "must": "exist",
      "description": "An <mc:Fallback> (empty or otherwise) must be present so readers lacking the Requires-listed extension have a defined branch to select."
    }
  ]
}

Fixture

Download mc-alternate-content--show-media-in-preview.pptx (27.6 KB)

Reference preview

No rendered reference is available for this case.

Spec notes

ECMA-376 Part 3 §10.2 lets producers stash namespace-versioned extensions inside Part-1 <extLst>/<ext uri="{GUID}"> wrappers. The URI is a Microsoft-allocated GUID; the payload opens with <mc:AlternateContent> whose <mc:Choice Requires="..."> branch names the namespace prefix a reader needs (p14 for Word 2010, p15 for Word 2013, p188 for Word 2016). Empty <mc:Fallback/> is the canonical 'ignore this feature' shape — older clients that match only the fallback discard the ext. Round-tripping the extension intact matters: if python-pptx's PARSE_TREE silently drops <mc:AlternateContent> inside <p:ext>, the attached feature (chart tracking IDs, smart-art version markers, ink, ...) is lost on re-save.