docx/style-builtin

Apply each built-in Word paragraph style (Normal, Title, Heading 4-9, List Bullet, Quote, Caption, ...) via <w:pPr><w:pStyle w:val="<StyleId>"/></w:pPr>.

Cases (32)

Feature IDAxis bindingspython-docxdocxjs
docx/style-builtin--body-textstyle=body-text
docx/style-builtin--body-text-2style=body-text-2
docx/style-builtin--body-text-3style=body-text-3
docx/style-builtin--captionstyle=caption
docx/style-builtin--footerstyle=footer
docx/style-builtin--headerstyle=header
docx/style-builtin--heading-4style=heading-4
docx/style-builtin--heading-5style=heading-5
docx/style-builtin--heading-6style=heading-6
docx/style-builtin--heading-7style=heading-7
docx/style-builtin--heading-8style=heading-8
docx/style-builtin--heading-9style=heading-9
docx/style-builtin--intense-quotestyle=intense-quote
docx/style-builtin--liststyle=list
docx/style-builtin--list-2style=list-2
docx/style-builtin--list-3style=list-3
docx/style-builtin--list-bulletstyle=list-bullet
docx/style-builtin--list-bullet-2style=list-bullet-2
docx/style-builtin--list-bullet-3style=list-bullet-3
docx/style-builtin--list-continuestyle=list-continue
docx/style-builtin--list-continue-2style=list-continue-2
docx/style-builtin--list-continue-3style=list-continue-3
docx/style-builtin--list-numberstyle=list-number
docx/style-builtin--list-number-2style=list-number-2
docx/style-builtin--list-number-3style=list-number-3
docx/style-builtin--list-paragraphstyle=list-paragraph
docx/style-builtin--macro-textstyle=macro-text
docx/style-builtin--no-spacingstyle=no-spacing
docx/style-builtin--quotestyle=quote
docx/style-builtin--subtitlestyle=subtitle
docx/style-builtin--titlestyle=title
docx/style-builtin--toc-headingstyle=toc-heading

Aggregate

LibraryPassFailPending
python-docx0032
docxjs0032

Parameter axes

Spec notes

Built-in paragraph styles ship in Word's default styles.xml and are referenced from a paragraph via <w:pPr><w:pStyle w:val="<StyleId>"/></w:pPr>. The style_id is the whitespace-collapsed camel/pascal-case identifier (e.g. 'Heading4', 'BodyText2', 'TOCHeading') while the display name shown in Word's UI may include spaces ('Heading 4', 'Body Text 2', 'TOC Heading'). This manifest enumerates every paragraph-type style that ships in python-docx's default document template, excluding (1) the three that already have dedicated literal manifests (docx/heading-1, docx/heading-2, docx/heading-3), and (2) the 'Normal' style, which is the default and is never emitted as an explicit <w:pStyle> by python-docx (there is nothing to assert against). Clause 17.7 of ECMA-376 Part 1 covers style definitions in general; 17.7.4.17 specifies w:pStyle specifically, and 17.9.21 lists the reserved built-in style identifiers.

Generator source

scripts/gen_style_builtin.py — runs with --arg_template --style-id {style.style_id} --out fixtures/docx/style-builtin--{style.id}.docx

#!/usr/bin/env python3
"""Generate ``fixtures/docx/style-builtin--<style-id>.docx`` fixtures.

Parameterised generator driven by ``features/docx/style-builtin.json``.
For each built-in Word paragraph style, produce a single-paragraph
document whose paragraph carries
``<w:pPr><w:pStyle w:val="<StyleId>"/></w:pPr>``.

The generator accepts the *display* name of the style (what Word shows
in its Styles pane, e.g. ``Heading 4``, ``Body Text 2``, ``TOC Heading``)
because ``Document.add_paragraph(style=...)`` in python-docx looks up
styles by display name — passing the camel/pascal-case ``style_id``
raises a deprecation warning. The manifest's ``arg_template`` therefore
passes ``--style-id <StyleId>`` (the XML identifier), and this script
translates to the display name via ``doc.styles[StyleId]``.
"""

from __future__ import annotations

import argparse
from pathlib import Path

from docx import Document

_REPO_ROOT = Path(__file__).resolve().parent.parent


def _write(style_id: str, out: Path) -> None:
    doc = Document()
    # python-docx indexes styles by display name; the default template
    # populates the Styles part with a style whose `style_id` attribute
    # is the camel/pascal-case XML identifier we want to resolve. We
    # scan once instead of using `doc.styles[style_id]` because that
    # path raises a DeprecationWarning in modern python-docx.
    display_name = None
    for style in doc.styles:
        if getattr(style, "style_id", None) == style_id:
            display_name = style.name
            break
    if display_name is None:
        raise SystemExit(
            f"Built-in style {style_id!r} not found in the default template"
        )
    doc.add_paragraph("Style sample text", style=display_name)
    out.parent.mkdir(parents=True, exist_ok=True)
    doc.save(out, reproducible=True)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--style-id",
        required=True,
        help="Built-in style identifier (e.g. 'Heading4', 'TOCHeading').",
    )
    parser.add_argument("--out", required=True, help="Output .docx path.")
    args = parser.parse_args()

    out_path = Path(args.out)
    if not out_path.is_absolute():
        out_path = _REPO_ROOT / out_path
    _write(args.style_id, out_path)
    print(out_path)
    return 0


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

Manifest (parent, unexpanded)

{
  "$schema": "../manifest.schema.json",
  "id": "docx/style-builtin",
  "kind": "parameterised",
  "title": "Built-in paragraph styles",
  "format": "docx",
  "category": "styles",
  "summary": "Apply each built-in Word paragraph style (Normal, Title, Heading 4-9, List Bullet, Quote, Caption, ...) via <w:pPr><w:pStyle w:val=\"<StyleId>\"/></w:pPr>.",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "17.7",
    "element": "w:pStyle",
    "rnc_reference": "spec/ecma-376-5/part-1/rnc/WordprocessingML.rnc",
    "xsd_reference": "spec/ecma-376-5/part-1/xsd/wml.xsd",
    "notes": "Built-in paragraph styles ship in Word's default styles.xml and are referenced from a paragraph via <w:pPr><w:pStyle w:val=\"<StyleId>\"/></w:pPr>. The style_id is the whitespace-collapsed camel/pascal-case identifier (e.g. 'Heading4', 'BodyText2', 'TOCHeading') while the display name shown in Word's UI may include spaces ('Heading 4', 'Body Text 2', 'TOC Heading'). This manifest enumerates every paragraph-type style that ships in python-docx's default document template, excluding (1) the three that already have dedicated literal manifests (docx/heading-1, docx/heading-2, docx/heading-3), and (2) the 'Normal' style, which is the default and is never emitted as an explicit <w:pStyle> by python-docx (there is nothing to assert against). Clause 17.7 of ECMA-376 Part 1 covers style definitions in general; 17.7.4.17 specifies w:pStyle specifically, and 17.9.21 lists the reserved built-in style identifiers."
  },
  "fixtures": {
    "machine": "docx/style-builtin"
  },
  "generator": {
    "python": "scripts/gen_style_builtin.py",
    "arg_template": "--style-id {style.style_id} --out fixtures/docx/style-builtin--{style.id}.docx"
  },
  "parameters": {
    "style": [
      {
        "id": "header",
        "style_id": "Header",
        "display": "Header"
      },
      {
        "id": "footer",
        "style_id": "Footer",
        "display": "Footer"
      },
      {
        "id": "heading-4",
        "style_id": "Heading4",
        "display": "Heading 4"
      },
      {
        "id": "heading-5",
        "style_id": "Heading5",
        "display": "Heading 5"
      },
      {
        "id": "heading-6",
        "style_id": "Heading6",
        "display": "Heading 6"
      },
      {
        "id": "heading-7",
        "style_id": "Heading7",
        "display": "Heading 7"
      },
      {
        "id": "heading-8",
        "style_id": "Heading8",
        "display": "Heading 8"
      },
      {
        "id": "heading-9",
        "style_id": "Heading9",
        "display": "Heading 9"
      },
      {
        "id": "no-spacing",
        "style_id": "NoSpacing",
        "display": "No Spacing"
      },
      {
        "id": "title",
        "style_id": "Title",
        "display": "Title"
      },
      {
        "id": "subtitle",
        "style_id": "Subtitle",
        "display": "Subtitle"
      },
      {
        "id": "list-paragraph",
        "style_id": "ListParagraph",
        "display": "List Paragraph"
      },
      {
        "id": "body-text",
        "style_id": "BodyText",
        "display": "Body Text"
      },
      {
        "id": "body-text-2",
        "style_id": "BodyText2",
        "display": "Body Text 2"
      },
      {
        "id": "body-text-3",
        "style_id": "BodyText3",
        "display": "Body Text 3"
      },
      {
        "id": "list",
        "style_id": "List",
        "display": "List"
      },
      {
        "id": "list-2",
        "style_id": "List2",
        "display": "List 2"
      },
      {
        "id": "list-3",
        "style_id": "List3",
        "display": "List 3"
      },
      {
        "id": "list-bullet",
        "style_id": "ListBullet",
        "display": "List Bullet"
      },
      {
        "id": "list-bullet-2",
        "style_id": "ListBullet2",
        "display": "List Bullet 2"
      },
      {
        "id": "list-bullet-3",
        "style_id": "ListBullet3",
        "display": "List Bullet 3"
      },
      {
        "id": "list-number",
        "style_id": "ListNumber",
        "display": "List Number"
      },
      {
        "id": "list-number-2",
        "style_id": "ListNumber2",
        "display": "List Number 2"
      },
      {
        "id": "list-number-3",
        "style_id": "ListNumber3",
        "display": "List Number 3"
      },
      {
        "id": "list-continue",
        "style_id": "ListContinue",
        "display": "List Continue"
      },
      {
        "id": "list-continue-2",
        "style_id": "ListContinue2",
        "display": "List Continue 2"
      },
      {
        "id": "list-continue-3",
        "style_id": "ListContinue3",
        "display": "List Continue 3"
      },
      {
        "id": "macro-text",
        "style_id": "MacroText",
        "display": "macro"
      },
      {
        "id": "quote",
        "style_id": "Quote",
        "display": "Quote"
      },
      {
        "id": "caption",
        "style_id": "Caption",
        "display": "Caption"
      },
      {
        "id": "intense-quote",
        "style_id": "IntenseQuote",
        "display": "Intense Quote"
      },
      {
        "id": "toc-heading",
        "style_id": "TOCHeading",
        "display": "TOC Heading"
      }
    ]
  },
  "assertions_template": [
    {
      "id": "style-pstyle-present-{style.id}",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:p/w:pPr/w:pStyle",
      "must": "exist",
      "description": "The sample paragraph must carry a <w:pStyle> inside its <w:pPr>."
    },
    {
      "id": "style-is-{style.id}",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:p/w:pPr/w:pStyle/@w:val",
      "must": "equal",
      "value": "{style.style_id}",
      "description": "The pStyle value must equal the built-in style identifier '{style.style_id}' (display name: '{style.display}')."
    }
  ]
}