Skip to content

Commit 9f384a2

Browse files
satoryubramjclaude
committed
Scan bookmarks in headers and footers
Document#bookmarks previously only scanned the document body, so bookmarks placed in headers/footers were invisible and couldn't be edited. This extends the scan to headers and footers (keyed by name, like the body). Combined with header/footer write-back (#175), the existing Bookmark insertion API now works for headers and footers and persists on save. Based on the original header-bookmark idea by @bramj in #22 (extended here to cover footers as well). Co-authored-by: bramj <bramj@users.noreply.github.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 69ecd74 commit 9f384a2

3 files changed

Lines changed: 39 additions & 0 deletions

File tree

‎lib/docx/document.rb‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ def paragraphs
7070
def bookmarks
7171
bkmrks_hsh = {}
7272
bkmrks_ary = @doc.xpath('//w:bookmarkStart').map { |b_node| parse_bookmark_from b_node }
73+
# also scan headers and footers so their bookmarks can be read and edited
74+
bkmrks_ary += headers.values.flat_map { |h| h.xpath('//w:bookmarkStart').map { |b_node| parse_bookmark_from b_node } }
75+
bkmrks_ary += footers.values.flat_map { |f| f.xpath('//w:bookmarkStart').map { |b_node| parse_bookmark_from b_node } }
7376
# auto-generated by office 2010
7477
bkmrks_ary.reject! { |b| b.name == '_GoBack' }
7578
bkmrks_ary.each { |b| bkmrks_hsh[b.name] = b }

‎spec/docx/document_spec.rb‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,42 @@
105105
end
106106
end
107107

108+
describe 'bookmarks in headers and footers' do
109+
before do
110+
@doc = Docx::Document.open(@fixtures_path + '/multi_doc_bookmarks.docx')
111+
@new_doc_path = @fixtures_path + '/multi_doc_bookmarks_saved.docx'
112+
end
113+
114+
after do
115+
File.delete(@new_doc_path) if File.exist?(@new_doc_path)
116+
end
117+
118+
it 'includes bookmarks found in headers and footers' do
119+
expect(@doc.bookmarks['header_bookmark']).to_not be_nil
120+
expect(@doc.bookmarks['footer_bookmark']).to_not be_nil
121+
end
122+
123+
it 'allows inserting text at a header bookmark' do
124+
@doc.bookmarks['header_bookmark'].insert_text_after('Inserted ')
125+
expect(@doc.headers['header1'].text).to eq 'Inserted Hello from the header.'
126+
end
127+
128+
it 'allows inserting text at a footer bookmark' do
129+
@doc.bookmarks['footer_bookmark'].insert_text_after('Inserted ')
130+
expect(@doc.footers['footer1'].text).to eq 'Inserted Hello from the footer.'
131+
end
132+
133+
it 'persists header and footer bookmark edits after save' do
134+
@doc.bookmarks['header_bookmark'].insert_text_after('Inserted ')
135+
@doc.bookmarks['footer_bookmark'].insert_text_after('Inserted ')
136+
@doc.save(@new_doc_path)
137+
138+
reopened = Docx::Document.open(@new_doc_path)
139+
expect(reopened.headers['header1'].text).to eq 'Inserted Hello from the header.'
140+
expect(reopened.footers['footer1'].text).to eq 'Inserted Hello from the footer.'
141+
end
142+
end
143+
108144
describe 'read tables' do
109145
before do
110146
@doc = Docx::Document.open(@fixtures_path + '/tables.docx')
6 KB
Binary file not shown.

0 commit comments

Comments
 (0)