Section or table-cell <w:textDirection> carries the flow direction of text via @w:val (ST_TextDirection).
Library verdicts: python-docx: — docxjs: —
| Feature id | docx/text-direction--lrtbv |
|---|---|
| Format | docx |
| Category | page-layout |
| Family | docx/text-direction |
| Axis values | direction=lrtbv |
| Spec | ecma-376-5-part-1 § 17.4.34 w:textDirection |
| ID / part | Predicate | XPath | python-docx | docxjs |
|---|---|---|---|---|
text-direction-lrtbvword/document.xml | equal = lrTbVw:textDirection@w:val on either the section or a table cell must equal 'lrTbV' for the lrtbv case. | //w:sectPr/w:textDirection/@w:val | //w:tcPr/w:textDirection/@w:val | — | — |
No render assertions declared.
scripts/gen_text_direction.py
#!/usr/bin/env python3
"""Generate ``fixtures/docx/text-direction*.docx`` fixtures.
Parameterised generator for the ``docx/text-direction`` family.
Emits a minimal document whose default section carries a
``<w:textDirection w:val="..."/>`` child inside ``<w:sectPr>`` with
the value supplied on the command line. The manifest expands this
into six concrete fixtures, one per ``ST_TextDirection`` enumerator
Word emits in the Transitional namespace (``lrTb``, ``tbRl``,
``btLr``, ``lrTbV``, ``tbRlV``, ``tbLrV``).
Note: the ECMA-376 Part 1 ``ST_TextDirection`` simple type
(clause 17.18.93) lists the Strict-style two-letter tokens ``tb``,
``rl``, ``lr``, ``tbV``, ``rlV``, ``lrV``. Word writes the longer
Transitional tokens (``lrTb`` etc.) and readers accept them; we
match Word.
"""
from __future__ import annotations
import argparse
from pathlib import Path
from docx import Document
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
_REPO_ROOT = Path(__file__).resolve().parent.parent
_VALID_DIRECTIONS = {"lrTb", "tbRl", "btLr", "lrTbV", "tbRlV", "tbLrV"}
def _write(direction: str, out: Path) -> None:
doc = Document()
doc.add_paragraph("Text-direction test")
section = doc.sections[-1]
sectPr = section._sectPr
# Remove any existing textDirection so we write a clean single child.
existing = sectPr.find(qn("w:textDirection"))
if existing is not None:
sectPr.remove(existing)
td = OxmlElement("w:textDirection")
td.set(qn("w:val"), direction)
sectPr.append(td)
out.parent.mkdir(parents=True, exist_ok=True)
doc.save(out, reproducible=True)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
"--direction",
default="lrTb",
help="ST_TextDirection enumerator (Transitional: lrTb, tbRl, btLr, lrTbV, tbRlV, tbLrV)",
)
parser.add_argument(
"--out",
default=str(_REPO_ROOT / "fixtures" / "docx" / "text-direction.docx"),
)
args = parser.parse_args()
if args.direction not in _VALID_DIRECTIONS:
raise SystemExit(
f"Invalid --direction {args.direction!r}; expected one of "
f"{sorted(_VALID_DIRECTIONS)}"
)
out_path = Path(args.out)
if not out_path.is_absolute():
out_path = _REPO_ROOT / out_path
_write(args.direction, out_path)
print(out_path)
return 0
if __name__ == "__main__":
raise SystemExit(main())
{
"$schema": "../manifest.schema.json",
"id": "docx/text-direction--lrtbv",
"kind": "literal",
"title": "Text direction",
"format": "docx",
"category": "page-layout",
"summary": "Section or table-cell <w:textDirection> carries the flow direction of text via @w:val (ST_TextDirection).",
"spec": {
"source": "ecma-376-5-part-1",
"clause": "17.4.34",
"element": "w:textDirection",
"rnc_reference": "spec/ecma-376-5/part-1/rnc/WordprocessingML.rnc",
"xsd_reference": "spec/ecma-376-5/part-1/xsd/wml.xsd",
"notes": "<w:textDirection w:val=\"...\"/> sets the flow direction of text within its containing element. It may appear inside <w:sectPr> (affecting a whole section) or inside <w:tcPr> (affecting a single table cell). The ECMA-376 ST_TextDirection simple type lists the Strict tokens 'tb', 'rl', 'lr', 'tbV', 'rlV', 'lrV'. Microsoft Word writes, and readers accept, the longer Transitional tokens: 'lrTb' (left-to-right, top-to-bottom — the default), 'tbRl' (top-to-bottom, right-to-left, e.g. vertical Japanese), 'btLr' (bottom-to-top, left-to-right), 'lrTbV' (left-to-right, top-to-bottom, vertical), 'tbRlV' (top-to-bottom, right-to-left, vertical) and 'tbLrV' (top-to-bottom, left-to-right, vertical). This manifest matches Word. It is a parameterised family: one manifest file expands into six concrete test cases, each targeting a distinct fixture docx/text-direction--<id>.docx produced from the same gen_text_direction.py script. For simplicity, the generator applies <w:textDirection> at section level (inside <w:sectPr>) rather than per table cell."
},
"fixtures": {
"machine": "docx/text-direction--lrtbv"
},
"generator": {
"python": "scripts/gen_text_direction.py",
"arg_template": "--direction {direction.val} --out fixtures/docx/text-direction--{direction.id}.docx"
},
"_expansion": {
"parent_id": "docx/text-direction",
"bindings": {
"direction": "lrtbv"
}
},
"assertions": [
{
"id": "text-direction-lrtbv",
"part": "word/document.xml",
"namespaces": {
"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
},
"xpath": "//w:sectPr/w:textDirection/@w:val | //w:tcPr/w:textDirection/@w:val",
"must": "equal",
"value": "lrTbV",
"description": "w:textDirection@w:val on either the section or a table cell must equal 'lrTbV' for the lrtbv case."
}
]
}
