@@ -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