Set a run's typeface on a specific w:rFonts script slot (ascii or hAnsi) across ten common font families.
Library verdicts: python-docx: — docxjs: —
| Feature id | docx/font-family-param--tahoma--ascii |
|---|---|
| Format | docx |
| Category | text-formatting |
| Family | docx/font-family-param |
| Axis values | font=tahoma, slot=ascii |
| Spec | ecma-376-5-part-1 § 17.3.2.25 w:rFonts |
| ID / part | Predicate | XPath | python-docx | docxjs |
|---|---|---|---|---|
rfonts-ascii-is-tahomaword/document.xml | equal = TahomaThe selected w:rFonts slot must carry the expected typeface name. | //w:r[w:t[normalize-space()='Font test text']]/w:rPr/w:rFonts/@w:ascii | — | — |
No render assertions declared.
scripts/gen_font_family_param.py
#!/usr/bin/env python3
"""Generate a ``fixtures/docx/font-family-param--<font>--<slot>.docx`` fixture.
Parameterised generator for the ``docx/font-family-param`` feature family.
One invocation writes a single fixture with the given font name applied to
a single ``w:rFonts`` slot attribute (``w:ascii`` or ``w:hAnsi``).
Usage::
python scripts/gen_font_family_param.py \
--font "Arial" \
--slot ascii \
--out fixtures/docx/font-family-param--arial--ascii.docx
``--font`` is the typeface name written into the script-slot attribute.
``--slot`` is the local name of the ``w:rFonts`` attribute to set; the two
slots exercised by this family are:
- ``ascii`` -> ``w:ascii`` (Latin script)
- ``hAnsi`` -> ``w:hAnsi`` (high-ANSI aka Western European extended)
python-docx's ``run.font.name`` sets the ascii + hAnsi + cs + eastAsian
slots together, so this generator bypasses that helper and manipulates
``w:rFonts`` directly via :class:`OxmlElement` in order to exercise a
single slot per fixture.
"""
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
def _write(font: str, slot: str, out: Path) -> None:
doc = Document()
paragraph = doc.add_paragraph()
run = paragraph.add_run("Font test text")
# python-docx's run.font.name sets ascii+hAnsi+cs+eastAsian together — use
# OxmlElement directly so we can exercise a single slot.
rPr = run._r.get_or_add_rPr()
existing = rPr.find(qn("w:rFonts"))
if existing is not None:
rPr.remove(existing)
rFonts = OxmlElement("w:rFonts")
rFonts.set(qn(f"w:{slot}"), font)
rPr.append(rFonts)
out.parent.mkdir(parents=True, exist_ok=True)
doc.save(out, reproducible=True)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
"--font",
required=True,
help="Font family name to write into the slot attribute, e.g. 'Arial'.",
)
parser.add_argument(
"--slot",
required=True,
help="w:rFonts slot local-name to populate, e.g. 'ascii' or 'hAnsi'.",
)
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.font, args.slot, out_path)
print(out_path)
return 0
if __name__ == "__main__":
raise SystemExit(main())
{
"$schema": "../manifest.schema.json",
"id": "docx/font-family-param--tahoma--ascii",
"kind": "literal",
"title": "Font family (parameterised)",
"format": "docx",
"category": "text-formatting",
"summary": "Set a run's typeface on a specific w:rFonts script slot (ascii or hAnsi) across ten common font families.",
"spec": {
"source": "ecma-376-5-part-1",
"clause": "17.3.2.25",
"element": "w:rFonts",
"rnc_reference": "spec/ecma-376-5/part-1/rnc/WordprocessingML.rnc",
"xsd_reference": "spec/ecma-376-5/part-1/xsd/wml.xsd",
"notes": "<w:rFonts/> carries per-script font slot attributes: w:ascii, w:hAnsi, w:cs, w:eastAsia (plus theme-named w:asciiTheme, etc.). This parameterised manifest expands into 10 fonts x 2 slots = 20 fixtures, each populating a single slot via OxmlElement so the ascii and hAnsi slots can be asserted independently (python-docx's run.font.name normally sets all four at once). The literal companion manifest docx/font-family remains unchanged for the pre-parameterised shape."
},
"fixtures": {
"machine": "docx/font-family-param--tahoma--ascii"
},
"generator": {
"python": "scripts/gen_font_family_param.py",
"arg_template": "--font \"{font.name}\" --slot {slot.attr} --out fixtures/docx/font-family-param--{font.id}--{slot.id}.docx"
},
"_expansion": {
"parent_id": "docx/font-family-param",
"bindings": {
"font": "tahoma",
"slot": "ascii"
}
},
"assertions": [
{
"id": "rfonts-ascii-is-tahoma",
"part": "word/document.xml",
"namespaces": {
"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
},
"xpath": "//w:r[w:t[normalize-space()='Font test text']]/w:rPr/w:rFonts/@w:ascii",
"must": "equal",
"value": "Tahoma",
"description": "The selected w:rFonts slot must carry the expected typeface name."
}
]
}
