docx/alt-chunk-embed--html

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.

Library verdicts: python-docx: — docxjs: —

Metadata

Feature iddocx/alt-chunk-embed--html
Formatdocx
Categoryopc-packaging
Familydocx/alt-chunk-embed
Axis valuespayload=html
Spececma-376-5-part-1 § 17.17.2 w:altChunk

XPath assertions

ID / partPredicateXPathpython-docxdocxjs
altchunk-element-present-html
word/document.xml
match = ^rId[0-9]+$
document.xml must carry a <w:altChunk> element with an r:id attribute pointing at the import relationship.
//w:altChunk/@r:id
altchunk-rel-type-html
word/_rels/document.xml.rels
exist
The document rels part must declare a relationship of type aFChunk (camel-case as required by the spec and emitted by python-docx).
//r:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk']
altchunk-rel-internal-html
word/_rels/document.xml.rels
not-match = External
The aFChunk rel is internal (points at an embedded part) — TargetMode must be absent or explicitly 'Internal', never 'External'.
//r:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/aFChunk']/@TargetMode
altchunk-content-type-override-html
[Content_Types].xml
match = ^/word/afchunk1\.html$
[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).
//ct:Override[@ContentType='text/html']/@PartName

Render assertions

No render assertions declared.

Generator source

scripts/gen_alt_chunk_embed.py

#!/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())

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "docx/alt-chunk-embed--html",
  "kind": "literal",
  "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--html"
  },
  "generator": {
    "python": "scripts/gen_alt_chunk_embed.py",
    "arg_template": "--kind {payload.id} --out fixtures/docx/alt-chunk-embed--{payload.id}.docx"
  },
  "_expansion": {
    "parent_id": "docx/alt-chunk-embed",
    "bindings": {
      "payload": "html"
    }
  },
  "assertions": [
    {
      "id": "altchunk-element-present-html",
      "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-html",
      "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-html",
      "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-html",
      "part": "[Content_Types].xml",
      "namespaces": {
        "ct": "http://schemas.openxmlformats.org/package/2006/content-types"
      },
      "xpath": "//ct:Override[@ContentType='text/html']/@PartName",
      "must": "match",
      "value": "^/word/afchunk1\\.html$",
      "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)."
    }
  ]
}

Fixture

Download alt-chunk-embed--html.docx (18.8 KB)

Reference preview

No rendered reference is available for this case.

Spec 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.