Skip to content

Commit 353c630

Browse files
committed
Also read/update headers and footers
Inspired by PR ruby-docx#73 we adapted the code to work on top of the current state.
1 parent e6e1e5d commit 353c630

File tree

1 file changed

+67
-7
lines changed

1 file changed

+67
-7
lines changed

lib/docx/document.rb

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ module Docx
2020
class Document
2121
attr_reader :xml, :doc, :zip, :styles
2222

23+
DOCUMENT_PATHS = {
24+
styles: "word/styles.xml",
25+
rels: "word/_rels/document.xml.rels",
26+
headers: "word/header*.xml",
27+
footers: "word/footer*.xml",
28+
numbering: "word/numbering.xml"
29+
}
30+
2331
def initialize(path_or_io, options = {})
2432
@replace = {}
2533

@@ -35,7 +43,12 @@ def initialize(path_or_io, options = {})
3543

3644
@document_xml = document.get_input_stream.read
3745
@doc = Nokogiri::XML(@document_xml)
46+
3847
load_styles
48+
load_headers
49+
load_footers
50+
load_numbering
51+
3952
yield(self) if block_given?
4053
ensure
4154
@zip.close
@@ -64,9 +77,12 @@ def paragraphs
6477
def bookmarks
6578
bkmrks_hsh = {}
6679
bkmrks_ary = @doc.xpath('//w:bookmarkStart').map { |b_node| parse_bookmark_from b_node }
80+
bkmrks_ary += @headers.values.map { |xml_doc| xml_doc.xpath('//w:bookmarkStart').map { |b_node| parse_bookmark_from b_node } }.flatten
81+
bkmrks_ary += @footers.values.map { |xml_doc| xml_doc.xpath('//w:bookmarkStart').map { |b_node| parse_bookmark_from b_node } }.flatten
6782
# auto-generated by office 2010
6883
bkmrks_ary.reject! { |b| b.name == '_GoBack' }
6984
bkmrks_ary.each { |b| bkmrks_hsh[b.name] = b }
85+
7086
bkmrks_hsh
7187
end
7288

@@ -166,24 +182,68 @@ def replace_entry(entry_path, file_contents)
166182
private
167183

168184
def load_styles
169-
@styles_xml = @zip.read('word/styles.xml')
170-
@styles = Nokogiri::XML(@styles_xml)
171-
@rels_xml = @zip.read('word/_rels/document.xml.rels')
172-
@rels = Nokogiri::XML(@rels_xml)
173-
rescue Errno::ENOENT => e
174-
warn e.message
175-
nil
185+
extract_single_document_from_path :styles
186+
extract_single_document_from_path :rels
187+
end
188+
189+
def load_headers
190+
extract_multiple_documents_from_globbed_path :headers
191+
end
192+
193+
def load_footers
194+
extract_multiple_documents_from_globbed_path :footers
195+
end
196+
197+
def load_numbering
198+
extract_single_document_from_path :numbering
199+
end
200+
201+
def extract_single_document_from_path(attr_name)
202+
path = DOCUMENT_PATHS[attr_name]
203+
if @zip.find_entry(path)
204+
xml_doc = @zip.read(path)
205+
self.instance_variable_set(:"@#{attr_name}", Nokogiri::XML(xml_doc))
206+
end
207+
end
208+
209+
def extract_multiple_documents_from_globbed_path(attr_name)
210+
glob_path = DOCUMENT_PATHS[attr_name]
211+
files = @zip.glob(glob_path).map { |h| h.name }
212+
filename_and_contents_pairs = files.map do |file|
213+
simple_file_name = file.sub(/^word\//, "").sub(/\.xml$/, "")
214+
[simple_file_name, Nokogiri::XML(@zip.read(file))]
215+
end
216+
hash = Hash[filename_and_contents_pairs]
217+
self.instance_variable_set(:"@#{attr_name}", hash)
176218
end
177219

178220
#--
179221
# TODO: Flesh this out to be compatible with other files
180222
# TODO: Method to set flag on files that have been edited, probably by inserting something at the
181223
# end of methods that make edits?
224+
# TODO: save document.xml or document2.xml ?
182225
#++
183226
def update
184227
replace_entry 'word/document.xml', doc.serialize(save_with: 0)
228+
update_multiple_documents :headers
229+
update_multiple_documents :footers
230+
update_single_document :numbering
231+
# also save styles? / rels?
185232
end
186233

234+
def update_multiple_documents(attr_name)
235+
self.instance_variable_get("@#{attr_name}").each do |simple_file_name, contents|
236+
replace_entry("word/#{simple_file_name}.xml", contents.serialize(:save_with => 0))
237+
end
238+
end
239+
240+
def update_single_document(attr_name)
241+
path = DOCUMENT_PATHS[attr_name]
242+
xml_document = self.instance_variable_get("@#{attr_name}")
243+
replace_entry path, xml_document.serialize(:save_with => 0) if xml_document
244+
end
245+
246+
187247
# generate Elements::Containers::Paragraph from paragraph XML node
188248
def parse_paragraph_from(p_node)
189249
Elements::Containers::Paragraph.new(p_node, document_properties)

0 commit comments

Comments
 (0)