From e2dd9deb9c8848c4264ca3ccab00caef58fcc9d8 Mon Sep 17 00:00:00 2001 From: Tatsuya Sato Date: Sun, 31 May 2026 19:57:28 +0900 Subject: [PATCH] Persist headers and footers on save Extends Document#update to write modified headers and footers back into the archive on save/stream, keyed by their original file name (so multiple and non-sequential header/footer files round-trip correctly). Builds on the header/footer reading support (#173, #174). Based on the original read/write implementation by @aashish in #42. Co-authored-by: aashish Co-Authored-By: Claude Opus 4.8 --- lib/docx/document.rb | 6 ++++++ spec/docx/document_spec.rb | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/lib/docx/document.rb b/lib/docx/document.rb index 39fe5ed..551eee0 100755 --- a/lib/docx/document.rb +++ b/lib/docx/document.rb @@ -245,6 +245,12 @@ def load_rels def update replace_entry 'word/document.xml', doc.serialize(save_with: 0) replace_entry 'word/styles.xml', styles_configuration.serialize(save_with: 0) + headers.each do |name, content| + replace_entry "word/#{name}.xml", content.serialize(save_with: 0) + end + footers.each do |name, content| + replace_entry "word/#{name}.xml", content.serialize(save_with: 0) + end end # generate Elements::Containers::Paragraph from paragraph XML node diff --git a/spec/docx/document_spec.rb b/spec/docx/document_spec.rb index 2825eb5..d942bc4 100755 --- a/spec/docx/document_spec.rb +++ b/spec/docx/document_spec.rb @@ -84,6 +84,27 @@ end end + describe 'updating headers and footers' do + before do + @doc = Docx::Document.open(@fixtures_path + '/multi_doc.docx') + @new_doc_path = @fixtures_path + '/multi_doc_saved.docx' + end + + after do + File.delete(@new_doc_path) if File.exist?(@new_doc_path) + end + + it 'persists changes to headers and footers after save' do + @doc.headers['header1'].at_xpath('//w:t').content = 'Edited header.' + @doc.footers['footer1'].at_xpath('//w:t').content = 'Edited footer.' + @doc.save(@new_doc_path) + + reopened = Docx::Document.open(@new_doc_path) + expect(reopened.headers['header1'].text).to eq 'Edited header.' + expect(reopened.footers['footer1'].text).to eq 'Edited footer.' + end + end + describe 'read tables' do before do @doc = Docx::Document.open(@fixtures_path + '/tables.docx')