Fix header/footer dropped after save/stream when parts are stored out of order#6
Merged
Merged
Conversation
#update wrote header/footer parts back by positional index
(word/header{i+1}.xml), but glob returns zip entries in central-directory
order which is not always sorted. When parts were stored out of numeric
order, content was written into the wrong file and the referenced part
was blanked. Capture each part's real filename at fetch and write back
to it.
gokulavasan-io
force-pushed
the
bugfix/header-footer-reindex-on-save
branch
from
July 14, 2026 06:23
be72a1d to
8caf0c4
Compare
sumankula
approved these changes
Jul 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Changes
Document#update(runs on every#save/#stream) rewrites each header/footer part, but it targets the output file by array position —word/header#{index + 1}.xml— whilefetch_headers/fetch_footersread the parts via@zip.glob, which returns entries in the zip's central-directory order (not guaranteed numeric). When a document stores its parts out of numeric order, each part is written to the wrong file; the part thatdocument.xmlreferences as the default header ends up holding a different (often empty) part, so the header/footer disappears in the rendered/converted output.The fix captures each part's real filename at fetch time and writes each part back to that filename instead of a positional index. It is a no-op for normally-ordered documents and only corrects the out-of-order case.
Lib
fetch_headers/fetch_footers: capture@header_names/@footer_names(glob order) and read parts by name.#update: write each header/footer back to@header_names[i]/@footer_names[i]instead ofword/header#{i+1}.xml.Tests
header2.xmlstored beforeheader1.xml, streams it, assertsheader2.xmlstill holds its own content. Red without the fix, green with it.Architecture / Flow
Impact
Any caller that saves/streams a docx with section-differentiated headers/footers (different-first-page / odd-even) stored out of numeric order — e.g. offer-letter generation, which calls
replace_placeholders→#stream→#update. Reproduced end-to-end on a real offer template: header blank before the fix, fully rendered after.Safety
@header_names[i] == "word/header#{i+1}.xml"→ byte-identical output (no-op).Before:

After:
