docx/tab-stop-alignment--right--none

Exercise the five ST_TabJc alignments (left, center, right, decimal, bar) combined with no leader and dot leader.

Library verdicts: python-docx: — docxjs: —

Metadata

Feature iddocx/tab-stop-alignment--right--none
Formatdocx
Categoryparagraph-layout
Familydocx/tab-stop-alignment
Axis valuesalignment=right, leader=none
Spececma-376-5-part-1 § 17.9.37 w:tab

XPath assertions

ID / partPredicateXPathpython-docxdocxjs
tabs-element-present-right-none
word/document.xml
exist
The paragraph must carry a <w:tabs> child inside <w:pPr>.
//w:p[w:r/w:t[contains(normalize-space(), 'Tab aligned text')]]/w:pPr/w:tabs
tab-alignment-is-right
word/document.xml
equal = right
The w:tab element must carry the expected w:val alignment.
//w:p[w:r/w:t[contains(normalize-space(), 'Tab aligned text')]]/w:pPr/w:tabs/w:tab/@w:val
tab-pos-is-2880-right-none
word/document.xml
equal = 2880
The tab stop must be positioned at 2880 twips (2 inches).
//w:p[w:r/w:t[contains(normalize-space(), 'Tab aligned text')]]/w:pPr/w:tabs/w:tab/@w:pos
tab-leader-is-none
word/document.xml
equal = none
The w:tab element must carry the expected w:leader attribute.
//w:p[w:r/w:t[contains(normalize-space(), 'Tab aligned text')]]/w:pPr/w:tabs/w:tab/@w:leader

Render assertions

No render assertions declared.

Generator source

scripts/gen_tab_stop_alignment.py

#!/usr/bin/env python3
"""Generate a ``fixtures/docx/tab-stop-alignment--<align>--<leader>.docx`` fixture.

Parameterised generator for the ``docx/tab-stop-alignment`` feature
family. One invocation writes a single fixture with a single custom
tab stop at 2 inches (2880 twips), using the requested ST_TabJc
alignment and ST_TabTlc leader character.

Usage::

    python scripts/gen_tab_stop_alignment.py \\
        --alignment decimal \\
        --leader dot \\
        --out fixtures/docx/tab-stop-alignment--decimal--dot.docx

``--alignment`` is one of ``left``, ``center``, ``right``, ``decimal``,
``bar`` (ST_TabJc enumerants exercised by this family).

``--leader`` is one of ``none`` or ``dot`` (ST_TabTlc enumerants
exercised by this family).
"""

from __future__ import annotations

import argparse
from pathlib import Path

from docx import Document
from docx.enum.text import WD_TAB_ALIGNMENT, WD_TAB_LEADER
from docx.oxml.ns import qn
from docx.shared import Inches

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

_ALIGNMENTS = {
    "left": WD_TAB_ALIGNMENT.LEFT,
    "center": WD_TAB_ALIGNMENT.CENTER,
    "right": WD_TAB_ALIGNMENT.RIGHT,
    "decimal": WD_TAB_ALIGNMENT.DECIMAL,
    "bar": WD_TAB_ALIGNMENT.BAR,
}

_LEADERS = {
    "none": WD_TAB_LEADER.SPACES,
    "dot": WD_TAB_LEADER.DOTS,
}


def _write(alignment: str, leader: str, out: Path) -> None:
    try:
        wd_alignment = _ALIGNMENTS[alignment]
    except KeyError as exc:
        raise ValueError(
            f"Unknown alignment {alignment!r}; expected one of {sorted(_ALIGNMENTS)}"
        ) from exc
    try:
        wd_leader = _LEADERS[leader]
    except KeyError as exc:
        raise ValueError(
            f"Unknown leader {leader!r}; expected one of {sorted(_LEADERS)}"
        ) from exc

    doc = Document()
    paragraph = doc.add_paragraph()
    tab_stop = paragraph.paragraph_format.tab_stops.add_tab_stop(
        Inches(2), wd_alignment, wd_leader
    )
    # python-docx treats WD_TAB_LEADER.SPACES as the spec default and
    # omits the w:leader attribute. Force it to be written so the
    # assertion can compare the serialised value for every case.
    tab_stop._tab.set(qn("w:leader"), leader)
    paragraph.add_run("\tTab aligned text")
    out.parent.mkdir(parents=True, exist_ok=True)
    doc.save(out, reproducible=True)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--alignment",
        required=True,
        help=f"Tab stop alignment, one of {sorted(_ALIGNMENTS)}.",
    )
    parser.add_argument(
        "--leader",
        required=True,
        help=f"Tab stop leader, one of {sorted(_LEADERS)}.",
    )
    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.alignment, args.leader, out_path)
    print(out_path)
    return 0


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

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "docx/tab-stop-alignment--right--none",
  "kind": "literal",
  "title": "Tab stop alignment and leader",
  "format": "docx",
  "category": "paragraph-layout",
  "summary": "Exercise the five ST_TabJc alignments (left, center, right, decimal, bar) combined with no leader and dot leader.",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "17.9.37",
    "element": "w:tab",
    "rnc_reference": "spec/ecma-376-5/part-1/rnc/WordprocessingML.rnc",
    "xsd_reference": "spec/ecma-376-5/part-1/xsd/wml.xsd",
    "notes": "<w:tabs> on <w:pPr> holds one or more <w:tab> children. Each <w:tab> carries w:val (ST_TabJc alignment), w:pos (position in twips), and optional w:leader (ST_TabTlc leader character). This family parameterises 5 alignments x 2 leaders = 10 fixtures at w:pos=2880 (2in). The 'none' leader is serialised by python-docx as the literal attribute value 'none' (python-docx emits the attribute even for the spec default); the dot leader is serialised as 'dot'. Generator uses paragraph.paragraph_format.tab_stops.add_tab_stop with WD_TAB_ALIGNMENT and WD_TAB_LEADER."
  },
  "fixtures": {
    "machine": "docx/tab-stop-alignment--right--none"
  },
  "generator": {
    "python": "scripts/gen_tab_stop_alignment.py",
    "arg_template": "--alignment {alignment.val} --leader {leader.val} --out fixtures/docx/tab-stop-alignment--{alignment.id}--{leader.id}.docx"
  },
  "_expansion": {
    "parent_id": "docx/tab-stop-alignment",
    "bindings": {
      "alignment": "right",
      "leader": "none"
    }
  },
  "assertions": [
    {
      "id": "tabs-element-present-right-none",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:p[w:r/w:t[contains(normalize-space(), 'Tab aligned text')]]/w:pPr/w:tabs",
      "must": "exist",
      "description": "The paragraph must carry a <w:tabs> child inside <w:pPr>."
    },
    {
      "id": "tab-alignment-is-right",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:p[w:r/w:t[contains(normalize-space(), 'Tab aligned text')]]/w:pPr/w:tabs/w:tab/@w:val",
      "must": "equal",
      "value": "right",
      "description": "The w:tab element must carry the expected w:val alignment."
    },
    {
      "id": "tab-pos-is-2880-right-none",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:p[w:r/w:t[contains(normalize-space(), 'Tab aligned text')]]/w:pPr/w:tabs/w:tab/@w:pos",
      "must": "equal",
      "value": "2880",
      "description": "The tab stop must be positioned at 2880 twips (2 inches)."
    },
    {
      "id": "tab-leader-is-none",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:p[w:r/w:t[contains(normalize-space(), 'Tab aligned text')]]/w:pPr/w:tabs/w:tab/@w:leader",
      "must": "equal",
      "value": "none",
      "description": "The w:tab element must carry the expected w:leader attribute."
    }
  ]
}

Fixture

Download tab-stop-alignment--right--none.docx (18.6 KB)

Reference preview

Reference (machine, page 1 PNG)

docx/tab-stop-alignment--right--none page 1 reference

Reference PDF


Download PDF Open in PDF.js

Spec notes

<w:tabs> on <w:pPr> holds one or more <w:tab> children. Each <w:tab> carries w:val (ST_TabJc alignment), w:pos (position in twips), and optional w:leader (ST_TabTlc leader character). This family parameterises 5 alignments x 2 leaders = 10 fixtures at w:pos=2880 (2in). The 'none' leader is serialised by python-docx as the literal attribute value 'none' (python-docx emits the attribute even for the spec default); the dot leader is serialised as 'dot'. Generator uses paragraph.paragraph_format.tab_stops.add_tab_stop with WD_TAB_ALIGNMENT and WD_TAB_LEADER.