A slide inherits its colour/font/format scheme from the theme attached to its slide-master by default. A slide may opt out by attaching its own themeOverride part via an officeDocument/relationships/themeOverride relationship from the slide's rels. This family covers two cases — inherit (no themeOverride relationship present) and override (themeOverride1.xml part attached) — each backed by a distinct fixture pptx/slide-master-theme-override--<id>.pptx generated from the same gen_slide_master_theme_override.py script.
Library verdicts: python-pptx: — pptxjs: —
| Feature id | pptx/slide-master-theme-override--inherit-from-master |
|---|---|
| Format | pptx |
| Category | slide-layout |
| Family | pptx/slide-master-theme-override |
| Axis values | override=inherit-from-master |
| Spec | ecma-376-5-part-1 § 19.3.1.50 a:themeOverride |
| ID / part | Predicate | XPath | python-pptx | pptxjs |
|---|---|---|---|---|
theme-override-rel-inherit-from-masterppt/slides/_rels/slide1.xml.rels | match = ^0(\.0)?$Slide without a themeOverride: slide1.xml.rels must carry zero themeOverride relationships. | count(//*[local-name()='Relationship'][@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/themeOverride']) | — | — |
No render assertions declared.
scripts/gen_slide_master_theme_override.py
#!/usr/bin/env python3
"""Generate a single ``fixtures/pptx/slide-master-theme-override--<id>.pptx``.
Parameterised generator for the ``pptx/slide-master-theme-override``
manifest family. Two cases:
- ``inherit-from-master`` — standard single-slide presentation with no
themeOverride part. The slide inherits its colour/font/format scheme
from the theme attached to its slide-master. ``slide1.xml.rels``
carries zero themeOverride relationships.
- ``override-via-themeOverride`` — single-slide presentation with a
``ppt/theme/themeOverride1.xml`` part attached via an
``officeDocument/relationships/themeOverride`` relationship from the
slide. The override part carries a minimal but spec-valid
<a:themeOverride> body (clrScheme + fontScheme + fmtScheme).
python-pptx has no high-level themeOverride API, so the override part
is constructed directly via the OPC package machinery (Part +
slide.part.relate_to(..., RT.THEME_OVERRIDE)).
"""
from __future__ import annotations
import argparse
import datetime as _dt
from pathlib import Path
from pptx import Presentation
from pptx.opc.constants import CONTENT_TYPE as CT
from pptx.opc.constants import RELATIONSHIP_TYPE as RT
from pptx.opc.package import Part
from pptx.opc.packuri import PackURI
_REPO_ROOT = Path(__file__).resolve().parent.parent
_REPRODUCIBLE_DT = _dt.datetime(2000, 1, 1, 0, 0, 0)
# Minimal but spec-valid <a:themeOverride> body. themeOverride is a
# restricted subset of <a:theme> containing clrScheme + fontScheme +
# fmtScheme (ECMA-376 Part 1 clause 14.2.7 / 19.3.1.50). The override
# here inverts some accent colours vs. the default Office theme so the
# part is visibly distinct from theme1.xml if inspected.
_THEME_OVERRIDE_XML = (
b'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'
b'<a:themeOverride xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main">'
b'<a:clrScheme name="Override">'
b'<a:dk1><a:srgbClr val="000000"/></a:dk1>'
b'<a:lt1><a:srgbClr val="FFFFFF"/></a:lt1>'
b'<a:dk2><a:srgbClr val="1F497D"/></a:dk2>'
b'<a:lt2><a:srgbClr val="EEECE1"/></a:lt2>'
b'<a:accent1><a:srgbClr val="FF0000"/></a:accent1>'
b'<a:accent2><a:srgbClr val="00FF00"/></a:accent2>'
b'<a:accent3><a:srgbClr val="0000FF"/></a:accent3>'
b'<a:accent4><a:srgbClr val="FFFF00"/></a:accent4>'
b'<a:accent5><a:srgbClr val="FF00FF"/></a:accent5>'
b'<a:accent6><a:srgbClr val="00FFFF"/></a:accent6>'
b'<a:hlink><a:srgbClr val="0000FF"/></a:hlink>'
b'<a:folHlink><a:srgbClr val="800080"/></a:folHlink>'
b'</a:clrScheme>'
b'<a:fontScheme name="Override">'
b'<a:majorFont><a:latin typeface="Calibri"/><a:ea typeface=""/><a:cs typeface=""/></a:majorFont>'
b'<a:minorFont><a:latin typeface="Calibri"/><a:ea typeface=""/><a:cs typeface=""/></a:minorFont>'
b'</a:fontScheme>'
b'<a:fmtScheme name="Override">'
b'<a:fillStyleLst>'
b'<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>'
b'<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>'
b'<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>'
b'</a:fillStyleLst>'
b'<a:lnStyleLst><a:ln/><a:ln/><a:ln/></a:lnStyleLst>'
b'<a:effectStyleLst>'
b'<a:effectStyle><a:effectLst/></a:effectStyle>'
b'<a:effectStyle><a:effectLst/></a:effectStyle>'
b'<a:effectStyle><a:effectLst/></a:effectStyle>'
b'</a:effectStyleLst>'
b'<a:bgFillStyleLst>'
b'<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>'
b'<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>'
b'<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>'
b'</a:bgFillStyleLst>'
b'</a:fmtScheme>'
b'</a:themeOverride>'
)
def _write(kind: str, out: Path) -> None:
prs = Presentation()
# "Title Only" layout — a title placeholder on an otherwise plain slide.
slide = prs.slides.add_slide(prs.slide_layouts[5])
if slide.shapes.title is not None:
slide.shapes.title.text = f"Theme override: {kind}"
if kind == "override":
partname = PackURI("/ppt/theme/themeOverride1.xml")
package = prs.part.package
override_part = Part(
partname, CT.OFC_THEME_OVERRIDE, package, _THEME_OVERRIDE_XML
)
slide.part.relate_to(override_part, RT.THEME_OVERRIDE)
elif kind != "inherit":
raise ValueError(f"Unknown kind: {kind!r} (expected 'inherit' or 'override').")
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(
"--kind",
required=True,
choices=("inherit", "override"),
help="'inherit' for no themeOverride; 'override' for an attached themeOverride1.xml.",
)
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
_write(args.kind, out_path)
print(out_path)
return 0
if __name__ == "__main__":
raise SystemExit(main())
{
"$schema": "../manifest.schema.json",
"id": "pptx/slide-master-theme-override--inherit-from-master",
"kind": "literal",
"title": "Slide theme override",
"format": "pptx",
"category": "slide-layout",
"summary": "A slide inherits its colour/font/format scheme from the theme attached to its slide-master by default. A slide may opt out by attaching its own themeOverride part via an officeDocument/relationships/themeOverride relationship from the slide's rels. This family covers two cases — inherit (no themeOverride relationship present) and override (themeOverride1.xml part attached) — each backed by a distinct fixture pptx/slide-master-theme-override--<id>.pptx generated from the same gen_slide_master_theme_override.py script.",
"spec": {
"source": "ecma-376-5-part-1",
"clause": "19.3.1.50",
"element": "a:themeOverride",
"rnc_reference": "spec/ecma-376-5/part-1/rnc/dml-main.rnc",
"xsd_reference": "spec/ecma-376-5/part-1/xsd/dml-main.xsd",
"notes": "ECMA-376 Part 1 clause 14.2.7 / 19.3.1.50 defines <a:themeOverride> — a subset of <a:theme> carrying clrScheme + fontScheme + fmtScheme (no objectDefaults, no extraSchemeLst) — and its relationship type http://schemas.openxmlformats.org/officeDocument/2006/relationships/themeOverride. A slide, slideLayout, or notesSlide may carry exactly zero or one themeOverride relationship; when present it supersedes the master-attached theme for that part only. python-pptx does not expose a high-level themeOverride API; this generator constructs the override part and relationship directly via the OPC package API (Part + slide.part.relate_to)."
},
"fixtures": {
"machine": "pptx/slide-master-theme-override--inherit-from-master"
},
"generator": {
"python": "scripts/gen_slide_master_theme_override.py",
"arg_template": "--kind {override.kind} --out fixtures/pptx/slide-master-theme-override--{override.id}.pptx"
},
"_expansion": {
"parent_id": "pptx/slide-master-theme-override",
"bindings": {
"override": "inherit-from-master"
}
},
"assertions": [
{
"id": "theme-override-rel-inherit-from-master",
"part": "ppt/slides/_rels/slide1.xml.rels",
"namespaces": {
"r": "http://schemas.openxmlformats.org/package/2006/relationships"
},
"xpath": "count(//*[local-name()='Relationship'][@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/themeOverride'])",
"must": "match",
"value": "^0(\\.0)?$",
"description": "Slide without a themeOverride: slide1.xml.rels must carry zero themeOverride relationships."
}
]
}
No rendered reference is available for this case.