pptx/text-bullet-style--bullet-dash

A slide's content-placeholder paragraph carries an explicit bullet via either <a:buChar char="..."/> (character bullets) or <a:buAutoNum type="..."/> (auto-numbered bullets). This manifest is a parameterised family: one manifest expands into ten concrete cases covering six auto-numbering schemes (arabic / roman / alpha in both cases) and four character bullets (filled, hollow, square, dash).

Library verdicts: python-pptx: — pptxjs: —

Metadata

Feature idpptx/text-bullet-style--bullet-dash
Formatpptx
Categoryslide-content
Familypptx/text-bullet-style
Axis valuesbullet=bullet-dash
Spececma-376-5-part-1 § 21.1.2.4 a:buChar|a:buAutoNum

XPath assertions

ID / partPredicateXPathpython-pptxpptxjs
bullet-present-bullet-dash
ppt/slides/slide1.xml
match = ^[1-9]\d*(\.0+)?$
At least one <a:buChar> or <a:buAutoNum> bullet element must be present on a paragraph's <a:pPr> (either kind satisfies the assertion; the specific kind is driven by the parameter axis).
count(//a:pPr/a:buChar) + count(//a:pPr/a:buAutoNum)

Render assertions

No render assertions declared.

Generator source

scripts/gen_text_bullet_style.py

#!/usr/bin/env python3
"""Generate a single ``fixtures/pptx/text-bullet-style--<id>.pptx``.

Parameterised generator: the ``features/pptx/text-bullet-style.json``
manifest expands to one case per bullet style, and the conformance
runner invokes this script once per case with
``--kind {autoNum|char} --value <token> --out <path>``.

Each invocation produces a single-slide presentation whose content
placeholder's first paragraph carries an explicit bullet. For
``--kind autoNum``, the paragraph's ``<a:pPr>`` gains
``<a:buAutoNum type="<value>"/>`` (e.g. ``arabicPeriod``,
``romanUcPeriod``). For ``--kind char``, it gains
``<a:buChar char="<value>"/>`` (e.g. ``•``, ``○``, ``▪``, ``-``).

python-pptx does not expose an API for bullet formatting, so the
elements are attached directly through lxml. Inherited bullet
elements (``a:buNone``, ``a:buChar``, ``a:buAutoNum``) that might
otherwise leak in from the layout style are removed first so the
resulting XML carries exactly one explicit bullet element.
"""

from __future__ import annotations

import argparse
import datetime as _dt
from pathlib import Path

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

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


def _write(kind: str, value: str, out: Path) -> None:
    if kind not in {"autoNum", "char"}:
        raise ValueError(f"--kind must be 'autoNum' or 'char', got {kind!r}")

    prs = Presentation()
    # Layout 1 is "Title and Content" — a title placeholder plus a body
    # content placeholder we can write bulleted text into.
    slide = prs.slides.add_slide(prs.slide_layouts[1])
    slide.shapes.title.text = "Bullet style"
    tf = slide.placeholders[1].text_frame
    tf.text = "Bullet item"

    # Target the first paragraph's <a:pPr>.
    p = tf.paragraphs[0]
    pPr = p._pPr
    if pPr is None:
        pPr = etree.SubElement(p._p, qn("a:pPr"))
        # <a:pPr> must be the first child of <a:p>.
        p._p.insert(0, pPr)

    # Clear any inherited bullet element so the fixture carries exactly
    # one explicit bullet choice.
    for tag in ("a:buNone", "a:buChar", "a:buAutoNum", "a:buBlip"):
        for existing in pPr.findall(qn(tag)):
            pPr.remove(existing)

    if kind == "autoNum":
        bu = etree.SubElement(pPr, qn("a:buAutoNum"))
        bu.set("type", value)
    else:  # char
        bu = etree.SubElement(pPr, qn("a:buChar"))
        bu.set("char", value)

    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=("autoNum", "char"),
        help="Bullet element kind: 'autoNum' for <a:buAutoNum> or 'char' for <a:buChar>.",
    )
    parser.add_argument(
        "--value",
        required=True,
        help=(
            "For --kind autoNum: the ST_TextAutonumberScheme token "
            "(e.g. arabicPeriod). For --kind char: the literal bullet "
            "character (e.g. •)."
        ),
    )
    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, args.value, out_path)
    print(out_path)
    return 0


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

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "pptx/text-bullet-style--bullet-dash",
  "kind": "literal",
  "title": "Text bullet style (buChar / buAutoNum)",
  "format": "pptx",
  "category": "slide-content",
  "summary": "A slide's content-placeholder paragraph carries an explicit bullet via either <a:buChar char=\"...\"/> (character bullets) or <a:buAutoNum type=\"...\"/> (auto-numbered bullets). This manifest is a parameterised family: one manifest expands into ten concrete cases covering six auto-numbering schemes (arabic / roman / alpha in both cases) and four character bullets (filled, hollow, square, dash).",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "21.1.2.4",
    "element": "a:buChar|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": "DrawingML bullet formatting on a paragraph's <a:pPr> is a choice of <a:buNone/>, <a:buChar char=\"x\"/>, <a:buAutoNum type=\"...\"/>, or <a:buBlip .../>. python-pptx does not expose an API for bullet formatting, so the generator attaches the element directly through lxml. The parameterised family covers ten representative bullet styles; each expanded case is backed by a distinct fixture pptx/text-bullet-style--<id>.pptx generated from the same gen_text_bullet_style.py script parameterised by --kind and --value. One assertion per case checks that at least one bullet element of either kind is present on an <a:pPr>, via an XPath count() union over a:buChar and a:buAutoNum."
  },
  "fixtures": {
    "machine": "pptx/text-bullet-style--bullet-dash"
  },
  "generator": {
    "python": "scripts/gen_text_bullet_style.py",
    "arg_template": "--kind {bullet.kind} --value \"{bullet.value}\" --out fixtures/pptx/text-bullet-style--{bullet.id}.pptx"
  },
  "_expansion": {
    "parent_id": "pptx/text-bullet-style",
    "bindings": {
      "bullet": "bullet-dash"
    }
  },
  "assertions": [
    {
      "id": "bullet-present-bullet-dash",
      "part": "ppt/slides/slide1.xml",
      "namespaces": {
        "p": "http://schemas.openxmlformats.org/presentationml/2006/main",
        "a": "http://schemas.openxmlformats.org/drawingml/2006/main"
      },
      "xpath": "count(//a:pPr/a:buChar) + count(//a:pPr/a:buAutoNum)",
      "must": "match",
      "value": "^[1-9]\\d*(\\.0+)?$",
      "description": "At least one <a:buChar> or <a:buAutoNum> bullet element must be present on a paragraph's <a:pPr> (either kind satisfies the assertion; the specific kind is driven by the parameter axis)."
    }
  ]
}

Fixture

Download text-bullet-style--bullet-dash.pptx (27.5 KB)

Reference preview

Reference (machine, page 1 PNG)

pptx/text-bullet-style--bullet-dash page 1 reference

Reference PDF


Download PDF Open in PDF.js

Spec notes

DrawingML bullet formatting on a paragraph's <a:pPr> is a choice of <a:buNone/>, <a:buChar char="x"/>, <a:buAutoNum type="..."/>, or <a:buBlip .../>. python-pptx does not expose an API for bullet formatting, so the generator attaches the element directly through lxml. The parameterised family covers ten representative bullet styles; each expanded case is backed by a distinct fixture pptx/text-bullet-style--<id>.pptx generated from the same gen_text_bullet_style.py script parameterised by --kind and --value. One assertion per case checks that at least one bullet element of either kind is present on an <a:pPr>, via an XPath count() union over a:buChar and a:buAutoNum.