A `<w:altChunk>` body element imports a foreign-format payload (HTML, XHTML, RTF, TXT) that is stored as a separate package part, wired up via an aFChunk relationship and a content-type Override.
| Feature ID | Axis bindings | python-docx | docxjs |
|---|---|---|---|
docx/alt-chunk-embed--html | payload=html | — | — |
docx/alt-chunk-embed--xhtml | payload=xhtml | — | — |
| Library | Pass | Fail | Pending |
|---|---|---|---|
python-docx | 0 | 0 | 2 |
docxjs | 0 | 0 | 2 |
payload (2 values): html, xhtmlscripts/gen_alt_chunk_embed.py — runs with --arg_template --kind {payload.id} --out fixtures/docx/alt-chunk-embed--{payload.id}.docx
#!/usr/bin/env python3
"""Generate ``fixtures/docx/alt-chunk-embed--<id>.docx`` fixtures.
A minimal document with one introductory paragraph followed by a single
``<w:altChunk>`` referencing an embedded HTML or XHTML payload. python-docx
serialises the payload as a new package part under ``word/afchunk<N>.<ext>``,
emits an ``aFChunk`` relationship in the document-part rels file, and
registers the part's MIME type as an ``<Override>`` in ``[Content_Types].xml``.
Manifest: ``features/docx/alt-chunk-embed.json``
"""
from __future__ import annotations
import argparse
from pathlib import Path
from docx import Document
_REPO_ROOT = Path(__file__).resolve().parent.parent
_PAYLOADS: dict[str, tuple[bytes, str]] = {
"html": (
b"<html><body><p>Hello from embedded HTML</p></body></html>",
"text/html",
),
"xhtml": (
b'<?xml version="1.0" encoding="UTF-8"?>'
b'<html xmlns="http://www.w3.org/1999/xhtml">'
b"<body><p>Hello from embedded XHTML</p></body></html>",
"application/xhtml+xml",
),
}
def _write(kind: str, out: Path) -> None:
if kind not in _PAYLOADS:
raise SystemExit(f"Unknown --kind {kind!r}; expected one of {sorted(_PAYLOADS)}")
payload, content_type = _PAYLOADS[kind]
doc = Document()
doc.add_paragraph("Before altChunk")
doc.add_alt_chunk(payload, content_type=content_type)
out.parent.mkdir(parents=True, exist_ok=True)
doc.save(out, reproducible=True)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--kind", required=True, choices=sorted(_PAYLOADS), help="Payload flavour")
parser.add_argument("--out", required=True, help="Output fixture path (repo-relative or absolute)")
args = parser.parse_args()
out_path = Path(args.out)
if not out_path.is_absolute():
out_path = _REPO_ROOT / out_path
_write(args.kind, out_path)
print(out_path)
return 0
if __name__ == "__main__":
raise SystemExit(main())
{
"$schema": "../manifest.schema.json",
"id": "docx/alt-chunk-embed",
"kind": "parameterised",
"title": "Alternate-format import (altChunk)",
"format": "docx",
"category": "opc-packaging",
"summary": "A `<w:altChunk>` body element imports a foreign-format payload (HTML, XHTML, RTF, TXT) that is stored as a separate package part, wired up via an aFChunk relationship and a content-type Override.",
"spec": {
"source": "ecma-376-5-part-1",
"clause": "17.17.2",
"element": "w:altChunk",
"xsd_reference": "spec/ecma-376-5/part-1/xsd/wml.xsd",
"notes": "WordprocessingML §17.17.2: `<w:altChunk r:id=\"rIdN\"/>` refers via OPC relationship type http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk (note the camel-case `aFChunk` spelling the spec prescribes and that python-docx emits) to a part whose ContentType is registered as an Override in [Content_Types].xml. Word expands the altChunk when it opens the document. This family parameterises over two payload MIME types (text/html, application/xhtml+xml) to exercise the Default-vs-Override content-type plumbing: neither `.html` nor `.xhtml` is covered by a Default extension, so both MUST be registered via Override."
},
"fixtures": {
"machine": "docx/alt-chunk-embed"
},
"generator": {
"python": "scripts/gen_alt_chunk_embed.py",
"arg_template": "--kind {payload.id} --out fixtures/docx/alt-chunk-embed--{payload.id}.docx"
},
"parameters": {
"payload": [
{
"id": "html",
"content_type": "text/html",
"part_name": "/word/afchunk1.html",
"part_name_pattern": "^/word/afchunk1\\.html$"
},
{
"id": "xhtml",
"content_type": "application/xhtml+xml",
"part_name": "/word/afchunk1.xhtml",
"part_name_pattern": "^/word/afchunk1\\.xhtml$"
}
]
},
"assertions_template": [
{
"id": "altchunk-element-present-{payload.id}",
"part": "word/document.xml",
"namespaces": {
"w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
"r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
},
"xpath": "//w:altChunk/@r:id",
"must": "match",
"value": "^rId[0-9]+$",
"description": "document.xml must carry a <w:altChunk> element with an r:id attribute pointing at the import relationship."
},
{
"id": "altchunk-rel-type-{payload.id}",
"part": "word/_rels/document.xml.rels",
"namespaces": {
"r": "http://schemas.openxmlformats.org/package/2006/relationships"
},
"xpath": "//r:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk']",
"must": "exist",
"description": "The document rels part must declare a relationship of type aFChunk (camel-case as required by the spec and emitted by python-docx)."
},
{
"id": "altchunk-rel-internal-{payload.id}",
"part": "word/_rels/document.xml.rels",
"namespaces": {
"r": "http://schemas.openxmlformats.org/package/2006/relationships"
},
"xpath": "//r:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk']/@TargetMode",
"must": "not-match",
"value": "External",
"description": "The aFChunk rel is internal (points at an embedded part) — TargetMode must be absent or explicitly 'Internal', never 'External'."
},
{
"id": "altchunk-content-type-override-{payload.id}",
"part": "[Content_Types].xml",
"namespaces": {
"ct": "http://schemas.openxmlformats.org/package/2006/content-types"
},
"xpath": "//ct:Override[@ContentType='{payload.content_type}']/@PartName",
"must": "match",
"value": "{payload.part_name_pattern}",
"description": "[Content_Types].xml must carry an Override entry binding the altChunk payload part to its MIME type. Default extension-based lookup is insufficient because neither .html nor .xhtml appears as a Default; an Override is mandatory (OPC §10.1.2.3)."
}
]
}