From a8210ebb8c705cbb1c5a18335b696dd22902a790 Mon Sep 17 00:00:00 2001 From: bramj Date: Wed, 5 Nov 2014 16:14:51 +0100 Subject: [PATCH] added headers to document - document now has access to @headers, a hash in format :header_file_path => :nokogiri_object - search for document bookmarks also includes bookmarks in headers - headers are also saved upon document save - some tests for this stuff --- .gitignore | 3 ++- lib/docx/document.rb | 6 +++++- spec/docx/document_spec.rb | 31 +++++++++++++++++++++++++++++++ spec/fixtures/headers.docx | Bin 0 -> 18948 bytes 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 spec/fixtures/headers.docx diff --git a/.gitignore b/.gitignore index 78ada7e..8e9c133 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ .bundle Gemfile.lock pkg/* -doc/ \ No newline at end of file +doc/ +vendor/ruby diff --git a/lib/docx/document.rb b/lib/docx/document.rb index a5722d3..38c3bba 100755 --- a/lib/docx/document.rb +++ b/lib/docx/document.rb @@ -18,7 +18,7 @@ module Docx # puts d.text # end class Document - attr_reader :xml, :doc, :zip, :styles + attr_reader :xml, :doc, :zip, :styles, :headers def initialize(path, &block) @replace = {} @@ -27,6 +27,8 @@ def initialize(path, &block) @doc = Nokogiri::XML(@document_xml) @styles_xml = @zip.read('word/styles.xml') @styles = Nokogiri::XML(@styles_xml) + @header_paths = @zip.glob('word/header*.xml').map { |h| h.name } + @headers = Hash[@header_paths.map { |h| [h, ::Nokogiri::XML(@zip.read(h))] }] if block_given? yield self @zip.close @@ -57,6 +59,7 @@ def paragraphs def bookmarks bkmrks_hsh = Hash.new bkmrks_ary = @doc.xpath('//w:bookmarkStart').map { |b_node| parse_bookmark_from b_node } + bkmrks_ary += @headers.values.map { |h| h.xpath('//w:bookmarkStart').map { |b_node| parse_bookmark_from b_node } }.flatten # auto-generated by office 2010 bkmrks_ary.reject! {|b| b.name == "_GoBack" } bkmrks_ary.each {|b| bkmrks_hsh[b.name] = b } @@ -129,6 +132,7 @@ def replace_entry(entry_path, file_contents) #++ def update replace_entry "word/document.xml", doc.serialize(:save_with => 0) + @headers.each { |path, h| replace_entry(path, h.serialize(:save_with => 0)) } end # generate Elements::Containers::Paragraph from paragraph XML node diff --git a/spec/docx/document_spec.rb b/spec/docx/document_spec.rb index d038d66..e60aefd 100755 --- a/spec/docx/document_spec.rb +++ b/spec/docx/document_spec.rb @@ -422,5 +422,36 @@ end end end + + describe 'headers' do + before do + @doc = Docx::Document.open(@fixtures_path + '/headers.docx') + end + + it 'should read the document' do + expect(@doc.headers.size).to eq(3) + end + + it 'should read bookmarks' do + expect(@doc.bookmarks.size).to eq(2) + expect(@doc.bookmarks['header_bookmark']).to_not eq(nil) + end + + it 'should be able to make changes in a header bookmark' do + @doc.bookmarks['header_bookmark'].insert_text_after('testing') + expect(@doc.headers['word/header2.xml'].to_html).to include('testing') + end + + it 'should save changes in a header bookmark' do + @doc.bookmarks['header_bookmark'].insert_text_after('testing') + expect(@doc.headers['word/header2.xml'].to_html).to include('testing') + + @new_doc_path = @fixtures_path + '/new_save.docx' + @doc.save(@new_doc_path) + @new_doc = Docx::Document.open(@new_doc_path) + expect(@doc.headers['word/header2.xml'].to_html).to include('testing') + File.exists?(@new_doc_path) if File.delete(@new_doc_path) + end + end end diff --git a/spec/fixtures/headers.docx b/spec/fixtures/headers.docx new file mode 100644 index 0000000000000000000000000000000000000000..a7a2a7c5f39869ece3a271940c60f8c643589a8b GIT binary patch literal 18948 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 w^#=?7=EVJq|EInE3k?J$B=oo2|6zR