Skip to content

Fix header/footer dropped after save/stream when parts are stored out of order#6

Merged
gokulavasan-io merged 1 commit into
masterfrom
bugfix/header-footer-reindex-on-save
Jul 14, 2026
Merged

Fix header/footer dropped after save/stream when parts are stored out of order#6
gokulavasan-io merged 1 commit into
masterfrom
bugfix/header-footer-reindex-on-save

Conversation

@gokulavasan-io

@gokulavasan-io gokulavasan-io commented Jul 13, 2026

Copy link
Copy Markdown

Changes

Document#update (runs on every #save / #stream) rewrites each header/footer part, but it targets the output file by array positionword/header#{index + 1}.xml — while fetch_headers/fetch_footers read 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 that document.xml references 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

File Change
lib/docx/document.rb 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 of word/header#{i+1}.xml.

Tests

File Change
spec/docx/document_spec.rb Streaming regression test: builds a docx with header2.xml stored before header1.xml, streams it, asserts header2.xml still holds its own content. Red without the fix, green with it.

Architecture / Flow

A .docx is a zip; headers are separate files. document.xml points at one as the "default".

  glob('word/header*.xml')  →  [header2, header3, header1]   (zip order, NOT numeric)
  fetch_headers array:          idx0      idx1      idx2
                                content   empty     empty

BEFORE (write by index → wrong file):
  idx0 → header1.xml  ← header2's CONTENT
  idx1 → header2.xml  ← header3's EMPTY     ← document.xml's default → now blank ✗
  idx2 → header3.xml  ← header1's EMPTY

AFTER (write by captured name → same file it came from):
  idx0 → header2.xml  ← content   ✓
  idx1 → header3.xml  ← empty     ✓
  idx2 → header1.xml  ← empty     ✓

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

  • Normally-ordered docs: @header_names[i] == "word/header#{i+1}.xml" → byte-identical output (no-op).
  • Only the out-of-order case changes — corrupt → correct. Cannot regress a working document.
  • Full gem suite passes (137 examples).

Before:
Screenshot 2026-07-14 at 11 53 57 AM

After:
Screenshot 2026-07-14 at 11 57 04 AM

#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
gokulavasan-io force-pushed the bugfix/header-footer-reindex-on-save branch from be72a1d to 8caf0c4 Compare July 14, 2026 06:23
@gokulavasan-io gokulavasan-io changed the title Fix header/footer reindex corruption on save/stream Fix header/footer dropped after save/stream when parts are stored out of order Jul 14, 2026
@gokulavasan-io
gokulavasan-io merged commit 3b0d1a2 into master Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants