DOCX Header/Footer Processing#
Header and footer extraction for DOCX files lives in MsWordDocumentBackend._add_header_footer in docling/backend/msword_backend.py. It runs after the main body walk and appends header/footer content to the DoclingDocument as ContentLayer.FURNITURE .
Call site#
_add_header_footer is called from convert(), immediately after _walk_linear finishes the document body and before comments are processed :
doc, _ = self._walk_linear(self.docx_obj.element.body, doc)
self._add_header_footer(self.docx_obj, doc)
self._add_comments(self.docx_obj, doc)
Section iteration and different_first_page_header_footer#
The method iterates over every section exposed by python-docx . The key decision for each section:
for sec_idx, section in enumerate(docx_obj.sections):
if sec_idx > 0 and not section.different_first_page_header_footer:
continue
- Section 0 (the first section) is always processed.
- Sections 1+ are skipped unless
section.different_first_page_header_footerisTrue. This flag maps directly to the OOXML<w:titlePg>element in the section properties; when set, Word uses a distinct first-page header/footer separate from the default one.
When different_first_page_header_footer is True, the backend selects the first-page variant of the header/footer rather than the default :
hdr = (
section.first_page_header
if section.different_first_page_header_footer
else section.header
)
The same conditional applies to footers .
Content detection and parsing#
Before walking a header or footer, the method checks whether it actually contains content worth processing :
- Text paragraphs – non-empty paragraph text (
par) - Tables –
hdr.tables - Images (blip) – detected via
_has_blip(hdr._element) - Textboxes – detected with an XPath expression covering DrawingML, VML, and
wps:txbxformats
Only if any of these are present is a new GroupLabel.SECTION group ("page header" / "page footer") created, and _walk_linear is invoked on the header/footer XML element.
Image relationship resolution#
Header/footer images reference relationships in the header part, not the main document part. Before calling _walk_linear, the backend temporarily switches the active relationship context :
self.current_part = hdr.part
self._walk_linear(hdr._element, doc)
self.current_part = self.docx_obj.part
This fix (introduced in PR #3556) ensures _get_image_from_relationship resolves blip relationships from the correct part, preventing missing images in headers and footers.
State management#
The method saves and restores self.content_layer and self.parents[0] around all section processing to avoid polluting the body parsing state.
Accessing header/footer content in exports#
By default, CLI and API exports include only ContentLayer.BODY. To include headers and footers in output, set included_content_layers to include ContentLayer.FURNITURE when calling the Python SDK .