East-Asian typography controls: paragraph-level kinsoku, word_wrap, auto-spacing, plus run-level two-lines-in-one (w:eastAsianLayout/@w:combine) and vertical text direction (w:textDirection=tbRl on a table cell).
| Library | Pass | Fail | Pending |
|---|---|---|---|
python-docx | 0 | 0 | 8 |
docxjs | 0 | 0 | 8 |
case (8 values): kinsoku-on, word-wrap-off, auto-space-de-off, auto-space-dn-off, eal-two-lines-in-one, eal-vertical, cell-vertical-tbRl, section-vertical-tbRlscripts/gen_east_asian_layout.py — runs with --arg_template --case {case.id} --out fixtures/docx/east-asian-layout--{case.id}.docx
#!/usr/bin/env python3
"""Generate ``fixtures/docx/east-asian-layout--<case>.docx``.
Parameterised generator for the ``docx/east-asian-layout`` family.
Each case exercises one East-Asian typography knob and asserts the
corresponding OOXML element is emitted.
Case index:
- ``kinsoku-on`` — paragraph-level ``w:kinsoku``.
- ``word-wrap-off`` — paragraph-level ``w:wordWrap w:val="0"`` (allow
mid-word break in CJK).
- ``auto-space-de-off`` — paragraph-level ``w:autoSpaceDE w:val="0"``.
- ``auto-space-dn-off`` — paragraph-level ``w:autoSpaceDN w:val="0"``.
- ``eal-two-lines-in-one`` — run-level ``w:eastAsianLayout w:combine="1"``.
- ``eal-vertical`` — run-level ``w:eastAsianLayout w:vert="1"``.
- ``cell-vertical-tbRl`` — table cell ``w:textDirection w:val="tbRl"``.
- ``section-vertical-tbRl`` — section ``w:textDirection w:val="tbRl"``.
"""
from __future__ import annotations
import argparse
from pathlib import Path
from typing import Callable
from docx import Document
from docx.enum.table import WD_TEXT_DIRECTION
_REPO_ROOT = Path(__file__).resolve().parent.parent
def _case_kinsoku(doc):
p = doc.add_paragraph("漢字テスト")
p.paragraph_format.kinsoku = True
def _case_word_wrap_off(doc):
p = doc.add_paragraph("漢字テスト")
p.paragraph_format.word_wrap = False
def _case_auto_space_de_off(doc):
p = doc.add_paragraph("漢字 Latin")
p.paragraph_format.auto_space_de = False
def _case_auto_space_dn_off(doc):
p = doc.add_paragraph("漢字 123")
p.paragraph_format.auto_space_dn = False
def _case_eal_two_lines_in_one(doc):
p = doc.add_paragraph()
r = p.add_run("中文")
r.font.set_east_asian_layout(two_lines_in_one=True)
def _case_eal_vertical(doc):
p = doc.add_paragraph()
r = p.add_run("縦")
r.font.set_east_asian_layout(vertical_alignment=True)
def _case_cell_vertical_tbRl(doc):
table = doc.add_table(rows=1, cols=1)
cell = table.cell(0, 0)
cell.text = "縦書き"
cell.text_direction = WD_TEXT_DIRECTION.TB_RL
def _case_section_vertical_tbRl(doc):
doc.add_paragraph("Section vertical text direction")
doc.sections[0].text_direction = WD_TEXT_DIRECTION.TB_RL
_CASES: dict[str, Callable] = {
"kinsoku-on": _case_kinsoku,
"word-wrap-off": _case_word_wrap_off,
"auto-space-de-off": _case_auto_space_de_off,
"auto-space-dn-off": _case_auto_space_dn_off,
"eal-two-lines-in-one": _case_eal_two_lines_in_one,
"eal-vertical": _case_eal_vertical,
"cell-vertical-tbRl": _case_cell_vertical_tbRl,
"section-vertical-tbRl": _case_section_vertical_tbRl,
}
def _write(case: str, out: Path) -> None:
if case not in _CASES:
raise SystemExit(f"Unknown case id {case!r}; expected one of {sorted(_CASES)}")
doc = Document()
_CASES[case](doc)
out.parent.mkdir(parents=True, exist_ok=True)
doc.save(out, reproducible=True)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--case", required=True)
parser.add_argument("--out", required=True)
args = parser.parse_args()
out_path = Path(args.out)
if not out_path.is_absolute():
out_path = _REPO_ROOT / out_path
_write(args.case, out_path)
print(out_path)
return 0
if __name__ == "__main__":
raise SystemExit(main())
{
"$schema": "../manifest.schema.json",
"id": "docx/east-asian-layout",
"kind": "parameterised",
"title": "East-Asian typography layout",
"format": "docx",
"category": "text-formatting",
"summary": "East-Asian typography controls: paragraph-level kinsoku, word_wrap, auto-spacing, plus run-level two-lines-in-one (w:eastAsianLayout/@w:combine) and vertical text direction (w:textDirection=tbRl on a table cell).",
"spec": {
"source": "ecma-376-5-part-1",
"clause": "17.3.1.16",
"element": "w:kinsoku",
"rnc_reference": "spec/ecma-376-5/part-1/rnc/WordprocessingML.rnc",
"xsd_reference": "spec/ecma-376-5/part-1/xsd/wml.xsd",
"notes": "East-Asian typography in WordprocessingML splits across several elements and levels. On w:pPr: w:kinsoku (line-break rules forbidding certain characters at line start/end), w:wordWrap (when w:val=\"0\" / false, allow mid-word breaks in CJK — latin-script word-wrap is the default when absent), w:autoSpaceDE (auto-space CJK↔Latin), w:autoSpaceDN (auto-space CJK↔numerals). On w:rPr: w:eastAsianLayout (@w:combine=\"1\" → two-lines-in-one; @w:vert=\"1\" → vertical alignment within an inline run). On w:tcPr (table cells) and w:sectPr: w:textDirection (w:val=\"tbRl\" → vertical writing, top-to-bottom right-to-left). This family covers each of those five element positions."
},
"fixtures": {
"machine": "docx/east-asian-layout"
},
"generator": {
"python": "scripts/gen_east_asian_layout.py",
"arg_template": "--case {case.id} --out fixtures/docx/east-asian-layout--{case.id}.docx"
},
"parameters": {
"case": [
{
"id": "kinsoku-on",
"xpath": "//w:pPr/w:kinsoku",
"expect": "exist"
},
{
"id": "word-wrap-off",
"xpath": "//w:pPr/w:wordWrap[@w:val='0' or @w:val='false' or @w:val='off']",
"expect": "exist"
},
{
"id": "auto-space-de-off",
"xpath": "//w:pPr/w:autoSpaceDE[@w:val='0' or @w:val='false' or @w:val='off']",
"expect": "exist"
},
{
"id": "auto-space-dn-off",
"xpath": "//w:pPr/w:autoSpaceDN[@w:val='0' or @w:val='false' or @w:val='off']",
"expect": "exist"
},
{
"id": "eal-two-lines-in-one",
"xpath": "//w:rPr/w:eastAsianLayout[@w:combine='1' or @w:combine='true' or @w:combine='on']",
"expect": "exist"
},
{
"id": "eal-vertical",
"xpath": "//w:rPr/w:eastAsianLayout[@w:vert='1' or @w:vert='true' or @w:vert='on']",
"expect": "exist"
},
{
"id": "cell-vertical-tbRl",
"xpath": "//w:tcPr/w:textDirection[@w:val='tbRl']",
"expect": "exist"
},
{
"id": "section-vertical-tbRl",
"xpath": "//w:sectPr/w:textDirection[@w:val='tbRl']",
"expect": "exist"
}
]
},
"assertions_template": [
{
"id": "east-asian-{case.id}",
"part": "word/document.xml",
"namespaces": {
"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
},
"xpath": "{case.xpath}",
"must": "{case.expect}",
"description": "East-Asian typography element for case {case.id}."
}
]
}