pptx/bullet-list

A slide's content (body) placeholder carries three paragraphs of text, each rendered as a bullet via the layout's list style.

Library verdicts: python-pptx: — pptxjs: fail

Metadata

Feature idpptx/bullet-list
Formatpptx
Categoryslide-content
Spececma-376-5-part-1 § 21.1.2 a:p

XPath assertions

ID / partPredicateXPathpython-pptxpptxjs
body-placeholder-has-three-paragraphs
ppt/slides/slide1.xml
exist
The content (body) placeholder's text body must carry at least three <a:p> paragraphs.
//p:sp[p:nvSpPr/p:nvPr/p:ph[@idx='1']]/p:txBody[count(a:p)>=3]
body-placeholder-item-one
ppt/slides/slide1.xml
exist
The content placeholder must contain a paragraph with text 'Item one'.
//p:sp[p:nvSpPr/p:nvPr/p:ph[@idx='1']]//a:p/a:r/a:t[normalize-space()='Item one']
body-placeholder-item-two
ppt/slides/slide1.xml
exist
The content placeholder must contain a paragraph with text 'Item two'.
//p:sp[p:nvSpPr/p:nvPr/p:ph[@idx='1']]//a:p/a:r/a:t[normalize-space()='Item two']
body-placeholder-item-three
ppt/slides/slide1.xml
exist
The content placeholder must contain a paragraph with text 'Item three'.
//p:sp[p:nvSpPr/p:nvPr/p:ph[@idx='1']]//a:p/a:r/a:t[normalize-space()='Item three']

Render assertions

IDPredicateSelectorpython-pptxpptxjs
bullet-list-three-itemscss_selector/ equal-count
Three bullet-item DOM nodes must be rendered for the content placeholder. `.oox-page .oox-paragraph` is the cross-format shared-class selector (W9-F) emitted by docxjs/pptxjs/xlsxjs; the remaining selectors are kept as back-compat aliases.
.oox-page .oox-paragraph, ul li, .pptx-list li, [data-placeholder-type='body'] li, [data-placeholder] pfail
bullet-list-item-onecss_selector/ match-text = Item one
First rendered bullet item must contain 'Item one'. Fallback text-match in case the selector-count assertion fails on a renderer using non-list markup.
.oox-page .oox-paragraph, ul li, .pptx-list li, [data-placeholder-type='body'] li, [data-placeholder] pfail
bullet-list-item-twocss_selector/ match-text = Item two
Second rendered bullet item must contain 'Item two'.
.oox-page .oox-paragraph:nth-of-type(2), ul li:nth-of-type(2), .pptx-list li:nth-of-type(2), [data-placeholder-type='body'] li:nth-of-type(2), [data-placeholder] p:nth-of-type(2)fail
bullet-list-item-threecss_selector/ match-text = Item three
Third rendered bullet item must contain 'Item three'.
.oox-page .oox-paragraph:nth-of-type(3), ul li:nth-of-type(3), .pptx-list li:nth-of-type(3), [data-placeholder-type='body'] li:nth-of-type(3), [data-placeholder] p:nth-of-type(3)fail
slide-countcss_selector/ equal-count
Exactly 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]

Generator source

scripts/gen_bullet_list.py

#!/usr/bin/env python3
"""Generate ``fixtures/pptx/bullet-list.pptx``.

A one-slide presentation whose content (body) placeholder carries three
paragraphs — each rendered as a bullet by the layout's list style.
Designed to satisfy the assertions in ``features/pptx/bullet-list.json``.
"""

from __future__ import annotations

from pathlib import Path

from pptx import Presentation

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-list.pptx"


def main() -> int:
    prs = Presentation()
    # slide_layouts[1] is the default title + content layout; its body
    # placeholder (idx=1) inherits a bulleted list style from the master.
    slide = prs.slides.add_slide(prs.slide_layouts[1])
    slide.shapes.title.text = "Bullets"

    content_tf = slide.placeholders[1].text_frame
    content_tf.text = "Item one"
    content_tf.add_paragraph().text = "Item two"
    content_tf.add_paragraph().text = "Item three"

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

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "pptx/bullet-list",
  "title": "Bulleted list in content placeholder",
  "format": "pptx",
  "category": "slide-content",
  "summary": "A slide's content (body) placeholder carries three paragraphs of text, each rendered as a bullet via the layout's list style.",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "21.1.2",
    "element": "a:p",
    "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 emits the content placeholder as <p:ph idx=\"1\"/> (no type attribute). Bullet formatting is inherited from the slide layout's list-style rather than carried explicitly on each <a:p>. Any renderer that honours layout-level list styles will render each paragraph as a bullet."
  },
  "fixtures": {
    "machine": "pptx/bullet-list",
    "office": "pptx/bullet-list"
  },
  "generator": {
    "python": "scripts/gen_bullet_list.py"
  },
  "assertions": [
    {
      "id": "body-placeholder-has-three-paragraphs",
      "part": "ppt/slides/slide1.xml",
      "namespaces": {
        "p": "http://schemas.openxmlformats.org/presentationml/2006/main",
        "a": "http://schemas.openxmlformats.org/drawingml/2006/main"
      },
      "xpath": "//p:sp[p:nvSpPr/p:nvPr/p:ph[@idx='1']]/p:txBody[count(a:p)>=3]",
      "must": "exist",
      "description": "The content (body) placeholder's text body must carry at least three <a:p> paragraphs."
    },
    {
      "id": "body-placeholder-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": "//p:sp[p:nvSpPr/p:nvPr/p:ph[@idx='1']]//a:p/a:r/a:t[normalize-space()='Item one']",
      "must": "exist",
      "description": "The content placeholder must contain a paragraph with text 'Item one'."
    },
    {
      "id": "body-placeholder-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": "//p:sp[p:nvSpPr/p:nvPr/p:ph[@idx='1']]//a:p/a:r/a:t[normalize-space()='Item two']",
      "must": "exist",
      "description": "The content placeholder must contain a paragraph with text 'Item two'."
    },
    {
      "id": "body-placeholder-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": "//p:sp[p:nvSpPr/p:nvPr/p:ph[@idx='1']]//a:p/a:r/a:t[normalize-space()='Item three']",
      "must": "exist",
      "description": "The content placeholder must contain a paragraph with text 'Item three'."
    }
  ],
  "render_assertions": [
    {
      "id": "bullet-list-three-items",
      "kind": "css_selector",
      "selector": ".oox-page .oox-paragraph, ul li, .pptx-list li, [data-placeholder-type='body'] li, [data-placeholder] p",
      "must": "equal-count",
      "count": 3,
      "description": "Three bullet-item DOM nodes must be rendered for the content placeholder. `.oox-page .oox-paragraph` is the cross-format shared-class selector (W9-F) emitted by docxjs/pptxjs/xlsxjs; the remaining selectors are kept as back-compat aliases."
    },
    {
      "id": "bullet-list-item-one",
      "kind": "css_selector",
      "selector": ".oox-page .oox-paragraph, ul li, .pptx-list li, [data-placeholder-type='body'] li, [data-placeholder] p",
      "must": "match-text",
      "value": "Item one",
      "description": "First rendered bullet item must contain 'Item one'. Fallback text-match in case the selector-count assertion fails on a renderer using non-list markup."
    },
    {
      "id": "bullet-list-item-two",
      "kind": "css_selector",
      "selector": ".oox-page .oox-paragraph:nth-of-type(2), ul li:nth-of-type(2), .pptx-list li:nth-of-type(2), [data-placeholder-type='body'] li:nth-of-type(2), [data-placeholder] p:nth-of-type(2)",
      "must": "match-text",
      "value": "Item two",
      "description": "Second rendered bullet item must contain 'Item two'."
    },
    {
      "id": "bullet-list-item-three",
      "kind": "css_selector",
      "selector": ".oox-page .oox-paragraph:nth-of-type(3), ul li:nth-of-type(3), .pptx-list li:nth-of-type(3), [data-placeholder-type='body'] li:nth-of-type(3), [data-placeholder] p:nth-of-type(3)",
      "must": "match-text",
      "value": "Item three",
      "description": "Third rendered bullet item must contain 'Item 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."
    }
  ]
}

Fixture

Download bullet-list.pptx (27.5 KB)

Reference preview

Reference (machine, page 1 PNG)

pptx/bullet-list page 1 reference

Reference PDF


Download PDF Open in PDF.js

Spec notes

python-pptx emits the content placeholder as <p:ph idx="1"/> (no type attribute). Bullet formatting is inherited from the slide layout's list-style rather than carried explicitly on each <a:p>. Any renderer that honours layout-level list styles will render each paragraph as a bullet.