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: —
| Feature id | docx/custom-xml-part |
|---|---|
| Format | docx |
| Category | advanced |
| Spec | ecma-376-5-part-1 § 22.5 ds:datastoreItem |
| ID / part | Predicate | XPath | python-docx | docxjs |
|---|---|---|---|---|
custom-xml-item-part-existscustomXml/item1.xml | existcustomXml/item1.xml must exist and carry a <b:Sources> root element (the default template's bibliography payload). | /b:Sources | — | — |
custom-xml-item-props-store-idcustomXml/itemProps1.xml | existcustomXml/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-documentword/document.xml | existThe 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}'] | — | — |
| ID | Predicate | Selector | python-docx | docxjs |
|---|---|---|---|---|
document-text-renders | css_selector/ existThe 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'] | — | — |
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())
{
"$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."
}
]
}
