A content placeholder carries three paragraphs, each bulleted as an auto-numbered arabic-period list item via <a:buAutoNum type="arabicPeriod"/>.
Library verdicts: python-pptx: — pptxjs: fail
| Feature id | pptx/bullet-numbered |
|---|---|
| Format | pptx |
| Category | slide-content |
| Spec | ecma-376-5-part-1 § 21.1.2.4 a:buAutoNum |
| ID / part | Predicate | XPath | python-pptx | pptxjs |
|---|---|---|---|---|
numbered-bullet-auto-num-existsppt/slides/slide1.xml | existAt least one paragraph's <a:pPr> must carry <a:buAutoNum type="arabicPeriod"/>. | //a:pPr/a:buAutoNum[@type='arabicPeriod'] | — | — |
numbered-bullet-item-oneppt/slides/slide1.xml | existA numbered paragraph must contain the text 'One'. | //a:p[a:pPr/a:buAutoNum[@type='arabicPeriod']]/a:r/a:t[normalize-space()='One'] | — | fail |
numbered-bullet-item-twoppt/slides/slide1.xml | existA numbered paragraph must contain the text 'Two'. | //a:p[a:pPr/a:buAutoNum[@type='arabicPeriod']]/a:r/a:t[normalize-space()='Two'] | — | fail |
numbered-bullet-item-threeppt/slides/slide1.xml | existA numbered paragraph must contain the text 'Three'. | //a:p[a:pPr/a:buAutoNum[@type='arabicPeriod']]/a:r/a:t[normalize-space()='Three'] | — | fail |
| ID | Predicate | Selector | python-pptx | pptxjs |
|---|---|---|---|---|
numbered-bullet-three-items | css_selector/ equal-countThree numbered list-item DOM nodes must be rendered for the content placeholder. | ol li, [data-bullet-type='numbered'] | — | fail |
numbered-bullet-item-one | css_selector/ match-text = OneFirst rendered list item must contain 'One'. Fallback text-match for renderers using non-list markup. | ol li, [data-bullet-type='numbered'], section[data-slide] p, section.slide p | — | fail |
numbered-bullet-item-two | css_selector/ match-text = TwoSecond rendered list item must contain 'Two'. | ol li:nth-of-type(2), [data-bullet-type='numbered']:nth-of-type(2), section[data-slide] p:nth-of-type(2), section.slide p:nth-of-type(2) | — | fail |
numbered-bullet-item-three | css_selector/ match-text = ThreeThird rendered list item must contain 'Three'. | ol li:nth-of-type(3), [data-bullet-type='numbered']:nth-of-type(3), section[data-slide] p:nth-of-type(3), section.slide p:nth-of-type(3) | — | fail |
slide-count | css_selector/ equal-countExactly one slide container must be rendered. Catches renderers that emit zero slides (hard failure) or duplicate the slide. | section.pptx-slide, .pptx-slide, section[data-slide], .slide, [data-slide] | — | — |
scripts/gen_bullet_numbered.py
#!/usr/bin/env python3
"""Generate ``fixtures/pptx/bullet-numbered.pptx``.
A one-slide presentation whose content placeholder carries three
paragraphs, each bulleted as an arabic-period auto-number via
<a:buAutoNum type="arabicPeriod"/>. python-pptx does not expose an API
for bullet auto-numbering, so the generator attaches the element
directly through lxml. Designed to satisfy the assertions in
``features/pptx/bullet-numbered.json``.
"""
from __future__ import annotations
from pathlib import Path
from lxml import etree
from pptx import Presentation
from pptx.oxml.ns import qn
import datetime as _dt
_REPRODUCIBLE_DT = _dt.datetime(2000, 1, 1, 0, 0, 0)
_REPO_ROOT = Path(__file__).resolve().parent.parent
_OUT = _REPO_ROOT / "fixtures" / "pptx" / "bullet-numbered.pptx"
def main() -> int:
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[1])
slide.shapes.title.text = "Numbered list"
body = slide.placeholders[1].text_frame
body.text = "One"
body.add_paragraph().text = "Two"
body.add_paragraph().text = "Three"
# Attach <a:buAutoNum type="arabicPeriod"/> to each paragraph's <a:pPr>.
for p in body.paragraphs:
pPr = p._pPr
if pPr is None:
pPr = etree.SubElement(p._p, qn("a:pPr"))
p._p.insert(0, pPr)
autoNum = etree.SubElement(pPr, qn("a:buAutoNum"))
autoNum.set("type", "arabicPeriod")
_OUT.parent.mkdir(parents=True, exist_ok=True)
prs.save(_OUT, zip_date_time=_REPRODUCIBLE_DT)
print(_OUT)
return 0
if __name__ == "__main__":
raise SystemExit(main()){
"$schema": "../manifest.schema.json",
"id": "pptx/bullet-numbered",
"title": "Numbered (auto-num) bullet list",
"format": "pptx",
"category": "slide-content",
"summary": "A content placeholder carries three paragraphs, each bulleted as an auto-numbered arabic-period list item via <a:buAutoNum type=\"arabicPeriod\"/>.",
"spec": {
"source": "ecma-376-5-part-1",
"clause": "21.1.2.4",
"element": "a:buAutoNum",
"rnc_reference": "spec/ecma-376-5/part-1/rnc/DrawingML.rnc",
"xsd_reference": "spec/ecma-376-5/part-1/xsd/dml-text.xsd",
"notes": "python-pptx does not expose an API for setting bullet auto-numbering, so the generator attaches <a:buAutoNum type=\"arabicPeriod\"/> to each paragraph's <a:pPr> directly via lxml. The three paragraphs carry 'One', 'Two', 'Three'."
},
"fixtures": {
"machine": "pptx/bullet-numbered",
"office": "pptx/bullet-numbered"
},
"generator": {
"python": "scripts/gen_bullet_numbered.py"
},
"assertions": [
{
"id": "numbered-bullet-auto-num-exists",
"part": "ppt/slides/slide1.xml",
"namespaces": {
"p": "http://schemas.openxmlformats.org/presentationml/2006/main",
"a": "http://schemas.openxmlformats.org/drawingml/2006/main"
},
"xpath": "//a:pPr/a:buAutoNum[@type='arabicPeriod']",
"must": "exist",
"description": "At least one paragraph's <a:pPr> must carry <a:buAutoNum type=\"arabicPeriod\"/>."
},
{
"id": "numbered-bullet-item-one",
"part": "ppt/slides/slide1.xml",
"namespaces": {
"p": "http://schemas.openxmlformats.org/presentationml/2006/main",
"a": "http://schemas.openxmlformats.org/drawingml/2006/main"
},
"xpath": "//a:p[a:pPr/a:buAutoNum[@type='arabicPeriod']]/a:r/a:t[normalize-space()='One']",
"must": "exist",
"description": "A numbered paragraph must contain the text 'One'."
},
{
"id": "numbered-bullet-item-two",
"part": "ppt/slides/slide1.xml",
"namespaces": {
"p": "http://schemas.openxmlformats.org/presentationml/2006/main",
"a": "http://schemas.openxmlformats.org/drawingml/2006/main"
},
"xpath": "//a:p[a:pPr/a:buAutoNum[@type='arabicPeriod']]/a:r/a:t[normalize-space()='Two']",
"must": "exist",
"description": "A numbered paragraph must contain the text 'Two'."
},
{
"id": "numbered-bullet-item-three",
"part": "ppt/slides/slide1.xml",
"namespaces": {
"p": "http://schemas.openxmlformats.org/presentationml/2006/main",
"a": "http://schemas.openxmlformats.org/drawingml/2006/main"
},
"xpath": "//a:p[a:pPr/a:buAutoNum[@type='arabicPeriod']]/a:r/a:t[normalize-space()='Three']",
"must": "exist",
"description": "A numbered paragraph must contain the text 'Three'."
}
],
"render_assertions": [
{
"id": "numbered-bullet-three-items",
"kind": "css_selector",
"selector": "ol li, [data-bullet-type='numbered']",
"must": "equal-count",
"count": 3,
"description": "Three numbered list-item DOM nodes must be rendered for the content placeholder."
},
{
"id": "numbered-bullet-item-one",
"kind": "css_selector",
"selector": "ol li, [data-bullet-type='numbered'], section[data-slide] p, section.slide p",
"must": "match-text",
"value": "One",
"description": "First rendered list item must contain 'One'. Fallback text-match for renderers using non-list markup."
},
{
"id": "numbered-bullet-item-two",
"kind": "css_selector",
"selector": "ol li:nth-of-type(2), [data-bullet-type='numbered']:nth-of-type(2), section[data-slide] p:nth-of-type(2), section.slide p:nth-of-type(2)",
"must": "match-text",
"value": "Two",
"description": "Second rendered list item must contain 'Two'."
},
{
"id": "numbered-bullet-item-three",
"kind": "css_selector",
"selector": "ol li:nth-of-type(3), [data-bullet-type='numbered']:nth-of-type(3), section[data-slide] p:nth-of-type(3), section.slide p:nth-of-type(3)",
"must": "match-text",
"value": "Three",
"description": "Third rendered list item must contain 'Three'."
},
{
"id": "slide-count",
"kind": "css_selector",
"selector": "section.pptx-slide, .pptx-slide, section[data-slide], .slide, [data-slide]",
"must": "equal-count",
"count": 1,
"description": "Exactly one slide container must be rendered. Catches renderers that emit zero slides (hard failure) or duplicate the slide."
}
]
}
