docx/heading-2

A second-level heading paragraph styled with the built-in Heading 2 style.

Library verdicts: python-docx: — docxjs: pass

Metadata

Feature iddocx/heading-2
Formatdocx
Categoryheadings
Spececma-376-5-part-1 § 17.9.2 w:pStyle

XPath assertions

ID / partPredicateXPathpython-docxdocxjs
heading-2-pstyle-present
word/document.xml
exist
The heading paragraph must declare a pStyle inside its pPr.
//w:p[w:r/w:t[normalize-space()='Heading 2 Subtitle']]/w:pPr/w:pStyle
heading-2-pstyle-val
word/document.xml
equal = Heading2
The pStyle value for a level-2 heading must be 'Heading2'.
//w:p[w:r/w:t[normalize-space()='Heading 2 Subtitle']]/w:pPr/w:pStyle/@w:val

Render assertions

IDPredicateSelectorpython-docxdocxjs
heading-2-renderedcss_selector/ match-text = Heading 2 Subtitle
A heading-2 DOM node (h2, .docx-heading-2, or Heading2-classed element) must contain the fixture text. Falls back to any paragraph since renderer conventions vary.
.docx-wrapper h2, .docx-wrapper .docx-heading-2, .docx-wrapper [class*='Heading2'], .docx-wrapper ppass
heading-2-has-semantic-rolecss_selector/ exist
Accessibility: the Heading 2 paragraph must surface either as a semantic <h2> element OR as an element with role='heading' and aria-level='2'. Either convention is accepted; a renderer that emits only a styled <p> or <div> with no heading role/level fails this assertion — that's the intended signal.
.docx-wrapper h2, .docx-wrapper [role='heading'][aria-level='2'], .docx-wrapper [role='heading'][aria-level="2"]

Generator source

scripts/gen_heading_2.py

#!/usr/bin/env python3
"""Generate ``fixtures/docx/heading-2.docx``.

A minimal document exercising the ``w:pStyle`` paragraph-style reference
for the built-in Heading 2 style. Satisfies the assertions in
``features/docx/heading-2.json``.
"""

from __future__ import annotations

from pathlib import Path

from docx import Document

_REPO_ROOT = Path(__file__).resolve().parent.parent
_OUT = _REPO_ROOT / "fixtures" / "docx" / "heading-2.docx"


def main() -> int:
    doc = Document()
    doc.add_heading("Heading 2 Subtitle", level=2)
    _OUT.parent.mkdir(parents=True, exist_ok=True)
    doc.save(_OUT, reproducible=True)
    print(_OUT)
    return 0


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

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "docx/heading-2",
  "title": "Heading 2",
  "format": "docx",
  "category": "headings",
  "summary": "A second-level heading paragraph styled with the built-in Heading 2 style.",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "17.9.2",
    "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": "Heading style is applied via <w:pPr><w:pStyle w:val=\"Heading2\"/></w:pPr>. The XML pStyle value is 'Heading2' (whitespace removed from the friendly style name)."
  },
  "fixtures": {
    "machine": "docx/heading-2",
    "office": "docx/heading-2"
  },
  "generator": {
    "python": "scripts/gen_heading_2.py"
  },
  "assertions": [
    {
      "id": "heading-2-pstyle-present",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:p[w:r/w:t[normalize-space()='Heading 2 Subtitle']]/w:pPr/w:pStyle",
      "must": "exist",
      "description": "The heading paragraph must declare a pStyle inside its pPr."
    },
    {
      "id": "heading-2-pstyle-val",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:p[w:r/w:t[normalize-space()='Heading 2 Subtitle']]/w:pPr/w:pStyle/@w:val",
      "must": "equal",
      "value": "Heading2",
      "description": "The pStyle value for a level-2 heading must be 'Heading2'."
    }
  ],
  "render_assertions": [
    {
      "id": "heading-2-rendered",
      "kind": "css_selector",
      "selector": ".docx-wrapper h2, .docx-wrapper .docx-heading-2, .docx-wrapper [class*='Heading2'], .docx-wrapper p",
      "must": "match-text",
      "value": "Heading 2 Subtitle",
      "description": "A heading-2 DOM node (h2, .docx-heading-2, or Heading2-classed element) must contain the fixture text. Falls back to any paragraph since renderer conventions vary."
    },
    {
      "id": "heading-2-has-semantic-role",
      "kind": "css_selector",
      "selector": ".docx-wrapper h2, .docx-wrapper [role='heading'][aria-level='2'], .docx-wrapper [role='heading'][aria-level=\"2\"]",
      "must": "exist",
      "description": "Accessibility: the Heading 2 paragraph must surface either as a semantic <h2> element OR as an element with role='heading' and aria-level='2'. Either convention is accepted; a renderer that emits only a styled <p> or <div> with no heading role/level fails this assertion — that's the intended signal."
    }
  ]
}

Fixture

Download heading-2.docx (18.6 KB)

Reference preview

Reference (machine, page 1 PNG)

docx/heading-2 page 1 reference

Reference PDF


Download PDF Open in PDF.js

Spec notes

Heading style is applied via <w:pPr><w:pStyle w:val="Heading2"/></w:pPr>. The XML pStyle value is 'Heading2' (whitespace removed from the friendly style name).