DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Snippets has posted 5883 posts at DZone. View Full User Profile

Microsoft Word Document Merge Done With Ruby And Office Automation

06.19.2011
| 4522 views |
  • submit to reddit
         This is basic Microsoft Word merge program. You can probably get it done a lot easier with Word
 build in merge function with small difference. This program can also merge different
 pictures on different pages.
 
 Whole idea is to make a template documents with variable parts written as names 
 surrounded by square brackets [name] [company] [whatever]. Program creates work 
 document and replaces every occurring of [whatever] with values supplied as parameter.
 At the end work document is copied to resulting document, cleared and prepared for
 another round.

require 'win32ole'

###############################################################
# hash_data: Data is passed as array of hashes, where each element of array holds 
# data to be used on resulting page. 'name' => 'my Name', 'company' => 'Some company' etc...
###############################################################
 def word_merge(hash_data)
   begin
     #word.Visible = true
     path   = fso.GetAbsolutePathName('my_template.doc')
     doc    = word.Documents.Open(path)
     resdoc = word.Documents.Add
     work   = word.Documents.Add
     hash_data.each do |r|
       # define some dynamic data prior to put it on document
       r['date'] = date_with_words(Time.now)
       # Clear work document
       work.ActiveWindow.Selection.WholeStory
       work.ActiveWindow.Selection.Delete
       # Select everything in source document
       doc.ActiveWindow.Selection.WholeStory
       doc.ActiveWindow.Selection.Copy
       # Paste to work document
       work.ActiveWindow.Selection.Paste
       work.ActiveWindow.Selection.WholeStory
       # Initialize find 
       find = word.Selection.Find
       # for each key in hash table
       r.each do |key, value|
       # Go to top of document
         word.Selection.HomeKey('unit' => 6)
       # find text
         find.Text = "[#{key}]"
         # replace all
         while word.Selection.Find.Execute
           word.Selection.TypeText('text' => value)
         end
       end
       # picture is a variable of some id
       pic_name = "images/pic#{r[pic_id]}.bmp"
       if File.exist?(pic_name)
         path  = fso.GetAbsolutePathName(pic_name)
         # determine paragraph to which picture will be put relative to
         shape_canvas = work.Shapes.AddCanvas(10,65,101,51, work.Paragraphs(24).Range )
         # add picture to canvas
         shape_canvas.CanvasItems.AddPicture('FileName' => path, 'LinkToFile' => true,  'SaveWithDocument' => false, 'Left' => 1, 'Top' => 1, 'Width' => 100, 'Height' => 50)
       end
       # Document is ready. Select everything in work document
       work.ActiveWindow.Selection.WholeStory
       work.ActiveWindow.Selection.Copy
       # Paste to resulting doc
       word.Selection.EndKey('unit' => 6)
       resdoc.ActiveWindow.Selection.Paste
     end
     # save to pdf
     path  = fso.GetAbsolutePathName('result.pdf')
     resdoc.SaveAs(path, 17)
     # Since this is mostly done in batch, some reporting on errors is essential.    
   rescue Exception => exc
     text = ''
     exc.backtrace.each {|c| text << c + "\n" }
     text << exc.message
     File.open('trace.log','w+') { |f| f.write text }
   end
 # Clear everything behind  
   resdoc.close('SaveChanges' => 0) unless resdoc.nil?
   doc.close('SaveChanges' => 0) unless doc.nil?
   work.close('SaveChanges' => 0) unless work.nil?
   word.quit
 end