docx/paragraph-alignment-param--start

Paragraph horizontal alignment (w:jc) exercised across all six ST_Jc values commonly emitted by Word: left, center, right, both, distribute, start.

Library verdicts: python-docx: — docxjs: pass

Metadata

Feature iddocx/paragraph-alignment-param--start
Formatdocx
Categoryparagraph-layout
Familydocx/paragraph-alignment-param
Axis valuesjc=start
Spececma-376-5-part-1 § 17.3.1.13 w:jc

XPath assertions

ID / partPredicateXPathpython-docxdocxjs
jc-val-is-start
word/document.xml
equal = start
The paragraph's <w:jc> must declare the expected ST_Jc value.
//w:p[w:r/w:t[normalize-space()='Start aligned']]/w:pPr/w:jc/@w:val

Render assertions

IDPredicateSelectorpython-docxdocxjs
paragraph-alignment-start-text-presentcss_selector/ match-text = Start aligned
The rendered DOM must contain the fixture text.
.docx-wrapper ppass
paragraph-alignment-start-computedcomputed_style = ^(start|left)$ style=text-align
Computed text-align on the paragraph must match the expected CSS value(s) for this jc.
.docx-wrapper ppass

Generator source

scripts/gen_paragraph_alignment_param.py

#!/usr/bin/env python3
"""Generate a ``fixtures/docx/paragraph-alignment-param--<id>.docx`` fixture.

Parameterised generator for the ``docx/paragraph-alignment-param`` feature
family. One invocation writes a single fixture for the given ``w:jc`` value.

Usage::

    python scripts/gen_paragraph_alignment_param.py \
        --jc center \
        --text "Center aligned" \
        --out fixtures/docx/paragraph-alignment-param--center.docx

The ``--jc`` argument takes any ``w:jc`` ST_Jc value from ECMA-376-5 Part 1
clause 17.18.44 / 17.3.1.13. Values handled explicitly in this family:

- ``left``       -> WD_ALIGN_PARAGRAPH.LEFT
- ``center``     -> WD_ALIGN_PARAGRAPH.CENTER
- ``right``      -> WD_ALIGN_PARAGRAPH.RIGHT
- ``both``       -> WD_ALIGN_PARAGRAPH.JUSTIFY
- ``distribute`` -> WD_ALIGN_PARAGRAPH.DISTRIBUTE
- ``start``      -> no python-docx enum member; set via OxmlElement directly

Any other string is accepted as an escape hatch and written straight into
``w:jc/@w:val`` with OxmlElement, so additional Jc values (``end``,
``mediumKashida``, etc.) can be exercised without code changes.
"""

from __future__ import annotations

import argparse
from pathlib import Path

from docx import Document
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml import OxmlElement
from docx.oxml.ns import qn

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

# w:jc values that map cleanly onto python-docx's WD_ALIGN_PARAGRAPH.
_ENUM_MAP = {
    "left": WD_ALIGN_PARAGRAPH.LEFT,
    "center": WD_ALIGN_PARAGRAPH.CENTER,
    "right": WD_ALIGN_PARAGRAPH.RIGHT,
    "both": WD_ALIGN_PARAGRAPH.JUSTIFY,
    "distribute": WD_ALIGN_PARAGRAPH.DISTRIBUTE,
}


def _set_jc_raw(paragraph, val: str) -> None:
    """Force ``w:pPr/w:jc@w:val`` to ``val`` via direct XML manipulation.

    Used for ST_Jc values that WD_ALIGN_PARAGRAPH doesn't enumerate (e.g.
    ``start``, ``end``, ``mediumKashida``).
    """
    pPr = paragraph._p.get_or_add_pPr()
    for existing in pPr.findall(qn("w:jc")):
        pPr.remove(existing)
    jc = OxmlElement("w:jc")
    jc.set(qn("w:val"), val)
    pPr.append(jc)


def _write(jc_val: str, text: str, out: Path) -> None:
    doc = Document()
    paragraph = doc.add_paragraph(text)
    if jc_val in _ENUM_MAP:
        paragraph.alignment = _ENUM_MAP[jc_val]
    else:
        _set_jc_raw(paragraph, jc_val)
    out.parent.mkdir(parents=True, exist_ok=True)
    doc.save(out, reproducible=True)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--jc",
        required=True,
        help="ST_Jc value to emit as <w:jc w:val=...>, e.g. left/center/right/both/distribute/start.",
    )
    parser.add_argument(
        "--text",
        required=True,
        help="Paragraph text content.",
    )
    parser.add_argument(
        "--out",
        required=True,
        help="Repo-relative or absolute output path for the .docx.",
    )
    args = parser.parse_args()

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


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

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "docx/paragraph-alignment-param--start",
  "kind": "literal",
  "title": "Paragraph alignment (parameterised)",
  "format": "docx",
  "category": "paragraph-layout",
  "summary": "Paragraph horizontal alignment (w:jc) exercised across all six ST_Jc values commonly emitted by Word: left, center, right, both, distribute, start.",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "17.3.1.13",
    "element": "w:jc",
    "rnc_reference": "spec/ecma-376-5/part-1/rnc/WordprocessingML.rnc",
    "xsd_reference": "spec/ecma-376-5/part-1/xsd/wml.xsd",
    "notes": "Horizontal alignment is carried by <w:jc w:val=\"...\"/> inside <w:pPr>. ST_Jc (clause 17.18.44) enumerates left, center, right, both, distribute, start, end, mediumKashida, and kashida. This parameterised manifest covers the six Word-interoperable values; literal companion manifests (docx/alignment-{left,center,right,justify}) remain as the pre-parameterised shape."
  },
  "fixtures": {
    "machine": "docx/paragraph-alignment-param--start"
  },
  "generator": {
    "python": "scripts/gen_paragraph_alignment_param.py",
    "arg_template": "--jc {jc.val} --text \"{jc.text}\" --out fixtures/docx/paragraph-alignment-param--{jc.id}.docx"
  },
  "_expansion": {
    "parent_id": "docx/paragraph-alignment-param",
    "bindings": {
      "jc": "start"
    }
  },
  "assertions": [
    {
      "id": "jc-val-is-start",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:p[w:r/w:t[normalize-space()='Start aligned']]/w:pPr/w:jc/@w:val",
      "must": "equal",
      "value": "start",
      "description": "The paragraph's <w:jc> must declare the expected ST_Jc value."
    }
  ],
  "render_assertions": [
    {
      "id": "paragraph-alignment-start-text-present",
      "kind": "css_selector",
      "selector": ".docx-wrapper p",
      "must": "match-text",
      "value": "Start aligned",
      "description": "The rendered DOM must contain the fixture text."
    },
    {
      "id": "paragraph-alignment-start-computed",
      "kind": "computed_style",
      "selector": ".docx-wrapper p",
      "style_property": "text-align",
      "value": "^(start|left)$",
      "description": "Computed text-align on the paragraph must match the expected CSS value(s) for this jc."
    }
  ]
}

Fixture

Download paragraph-alignment-param--start.docx (18.6 KB)

Reference preview

Reference (machine, page 1 PNG)

docx/paragraph-alignment-param--start page 1 reference

Reference PDF


Download PDF Open in PDF.js

Spec notes

Horizontal alignment is carried by <w:jc w:val="..."/> inside <w:pPr>. ST_Jc (clause 17.18.44) enumerates left, center, right, both, distribute, start, end, mediumKashida, and kashida. This parameterised manifest covers the six Word-interoperable values; literal companion manifests (docx/alignment-{left,center,right,justify}) remain as the pre-parameterised shape.