docx/custom-xml-part

The package contains a /customXml/item1.xml data part (bibliography sources list) and a sibling /customXml/itemProps1.xml declaring the store-item id; a rich-text content control in the document references the item via <w:dataBinding w:storeItemID='{...}'>, which is what keeps the custom XML part from being pruned at save time.

Library verdicts: python-docx: — docxjs: —

Metadata

Feature iddocx/custom-xml-part
Formatdocx
Categoryadvanced
Spececma-376-5-part-1 § 22.5 ds:datastoreItem

XPath assertions

ID / partPredicateXPathpython-docxdocxjs
custom-xml-item-part-exists
customXml/item1.xml
exist
customXml/item1.xml must exist and carry a <b:Sources> root element (the default template's bibliography payload).
/b:Sources
custom-xml-item-props-store-id
customXml/itemProps1.xml
exist
customXml/itemProps1.xml must declare a {GUID} store-item id on a <ds:datastoreItem> root.
/ds:datastoreItem[@ds:itemID='{EF278816-EC6F-A645-907D-7F25AECB1D4A}']
data-binding-in-document
word/document.xml
exist
The main document must carry a content control whose <w:dataBinding> references the custom XML store-item id — this is what prevents Word-style save pruning of the custom-xml part.
//w:sdt/w:sdtPr/w:dataBinding[@w:storeItemID='{EF278816-EC6F-A645-907D-7F25AECB1D4A}']

Render assertions

IDPredicateSelectorpython-docxdocxjs
document-text-renderscss_selector/ exist
The document body must render. Custom XML parts and data bindings are inherently invisible to rendering — renderers generally ignore them. This assertion just verifies the document is non-empty. Machine-side assertions above carry the real coverage.
.docx-wrapper p, .docx-wrapper [class*='paragraph']

Generator source

scripts/gen_custom_xml_part.py

#!/usr/bin/env python3
"""Generate ``fixtures/docx/custom-xml-part.docx``.

A document that retains the default-template custom XML data part
(``/customXml/item1.xml`` — an empty bibliography sources list) by
referencing it from a content control's ``<w:dataBinding>``. Without
such a reference the python-docx save path prunes the customXml
relationship (mirroring Word's "emit the minimum" behaviour), so the
part would disappear from the saved package.

Uses the fork's ContentControl + data-binding APIs
(python-docx >= 2026.05.0).
"""

from __future__ import annotations

from pathlib import Path

from docx import Document
from docx.content_controls import ContentControlType

_REPO_ROOT = Path(__file__).resolve().parent.parent
_OUT = _REPO_ROOT / "fixtures" / "docx" / "custom-xml-part.docx"

# GUID of the default-template bibliography item (customXml/itemProps1.xml).
_BIB_STORE_ITEM_ID = "{EF278816-EC6F-A645-907D-7F25AECB1D4A}"
_BIB_NS = "http://schemas.openxmlformats.org/officeDocument/2006/bibliography"


def main() -> int:
    doc = Document()
    doc.add_paragraph(
        "This document carries a custom XML data part (a bibliography "
        "sources list) bound to the content control below."
    )

    cc = doc.add_content_control(
        ContentControlType.RICH_TEXT,
        tag="bib-binding",
        title="Bibliography sources",
    )
    # Pin the sdt @w:id value so the fixture is byte-reproducible;
    # python-docx mints a random 32-bit int otherwise.
    cc._sdt.sdt_id = 1  # pyright: ignore[reportPrivateUsage]
    cc.set_data_binding(
        xpath="/b:Sources",
        prefix_mappings=f"xmlns:b='{_BIB_NS}'",
        store_item_id=_BIB_STORE_ITEM_ID,
    )

    _OUT.parent.mkdir(parents=True, exist_ok=True)
    doc.save(_OUT, reproducible=True)
    print(_OUT)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "docx/custom-xml-part",
  "title": "Custom XML data part with data-binding",
  "format": "docx",
  "category": "advanced",
  "summary": "The package contains a /customXml/item1.xml data part (bibliography sources list) and a sibling /customXml/itemProps1.xml declaring the store-item id; a rich-text content control in the document references the item via <w:dataBinding w:storeItemID='{...}'>, which is what keeps the custom XML part from being pruned at save time.",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "22.5",
    "element": "ds:datastoreItem",
    "rnc_reference": "spec/ecma-376-5/part-1/rnc/shared-customXmlDataProperties.rnc",
    "xsd_reference": "spec/ecma-376-5/part-1/xsd/shared-customXmlDataProperties.xsd",
    "notes": "Custom XML data parts (\"CustomXml\") hold arbitrary XML payloads referenced from a document via customXml relationships. A typical pair is /customXml/itemN.xml (content-type application/xml) carrying the payload, plus /customXml/itemPropsN.xml (content-type .../customXmlProperties+xml) declaring a {GUID} store-item id and optional schema references via <ds:schemaRef>. Word's python-docx default template ships a bibliography payload but prunes it at save time unless a <w:sdt><w:sdtPr><w:dataBinding w:storeItemID='...'/> in the main document references it. The fixture adds such a content control to keep the part alive."
  },
  "fixtures": {
    "machine": "docx/custom-xml-part"
  },
  "generator": {
    "python": "scripts/gen_custom_xml_part.py"
  },
  "assertions": [
    {
      "id": "custom-xml-item-part-exists",
      "part": "customXml/item1.xml",
      "namespaces": {
        "b": "http://schemas.openxmlformats.org/officeDocument/2006/bibliography"
      },
      "xpath": "/b:Sources",
      "must": "exist",
      "description": "customXml/item1.xml must exist and carry a <b:Sources> root element (the default template's bibliography payload)."
    },
    {
      "id": "custom-xml-item-props-store-id",
      "part": "customXml/itemProps1.xml",
      "namespaces": {
        "ds": "http://schemas.openxmlformats.org/officeDocument/2006/customXml"
      },
      "xpath": "/ds:datastoreItem[@ds:itemID='{EF278816-EC6F-A645-907D-7F25AECB1D4A}']",
      "must": "exist",
      "description": "customXml/itemProps1.xml must declare a {GUID} store-item id on a <ds:datastoreItem> root."
    },
    {
      "id": "data-binding-in-document",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:sdt/w:sdtPr/w:dataBinding[@w:storeItemID='{EF278816-EC6F-A645-907D-7F25AECB1D4A}']",
      "must": "exist",
      "description": "The main document must carry a content control whose <w:dataBinding> references the custom XML store-item id — this is what prevents Word-style save pruning of the custom-xml part."
    }
  ],
  "render_assertions": [
    {
      "id": "document-text-renders",
      "kind": "css_selector",
      "selector": ".docx-wrapper p, .docx-wrapper [class*='paragraph']",
      "must": "exist",
      "description": "The document body must render. Custom XML parts and data bindings are inherently invisible to rendering — renderers generally ignore them. This assertion just verifies the document is non-empty. Machine-side assertions above carry the real coverage."
    }
  ]
}

Fixture

Download custom-xml-part.docx (19.8 KB)

Reference preview

Reference (machine, page 1 PNG)

docx/custom-xml-part page 1 reference

Reference PDF


Download PDF Open in PDF.js

Spec notes

Custom XML data parts ("CustomXml") hold arbitrary XML payloads referenced from a document via customXml relationships. A typical pair is /customXml/itemN.xml (content-type application/xml) carrying the payload, plus /customXml/itemPropsN.xml (content-type .../customXmlProperties+xml) declaring a {GUID} store-item id and optional schema references via <ds:schemaRef>. Word's python-docx default template ships a bibliography payload but prunes it at save time unless a <w:sdt><w:sdtPr><w:dataBinding w:storeItemID='...'/> in the main document references it. The fixture adds such a content control to keep the part alive.