【Ruby】Microsoft Wordを操作する
MS Wordのワードファイル(.doc)を操作することって結構あると思います。
テンプレートになるファイルを用意して、そこにDBから取ってきた値を書きこんで、、みたいに。
Word2003からはXML形式(.docx)でで記述できるのでだいぶ楽になるのですが、
大人の事情でWord2000を使ってるところも多いですよね(´・ω・`)
素の.docを操作するには、COM/OLEという魔窟を探検しないといけません。
これが結構大変で、、
- Rubyの文献は少なく、、(´・ω・`)
- Microsoftのサイト(MSDN)で読めるAPIリファレンスは場所がころころ変わってリンク切れが多い、、(´・ω・`)
- その上、ようやく見つけたAPIリファレンスには返却値の型が書いてないとか親クラスがわかんないとか(´・ω・`)
魔物は深夜に牙を剥きます。。
なので、MyDocというラッパークラスを作りました。
MyDocを使えば、こんな簡単にWORDファイルを操作できます。
#(1)templete.docの先頭に「送付状.doc」をくっつけて、 #(2)Wordファイル内の「#サイト名#」という文字列を #「ふわふわ Ruby on Rails」に置き換えて保存するスクリプトだよ。 require 'mydoc' doc = MyDoc.new('template.doc') #(1) doc.concat('送付状.doc', MyDoc::BEGINPOS) #(2) doc.replace('サイト名', 'ふわふわ Ruby on Rails').save('ふわふわ Ruby on Rails.doc') #終了処理 doc.dtor
ね?簡単でしょ♪
じゃあソースコードをご紹介!、、、。なのですが。
このプログラムは仮想キー入力をバリバリ使ってるので、実行中にパソコン操作禁止ですw
動作中はボケーっとしてスローライフを楽しみましょう(´・ω・`)
※一応ちゃんと動きますよw
# -*- encoding: UTF-8 -*- #UTF8環境で動作します require 'win32ole' module Word; end class MyDoc attr_accessor :dir, :crrDir, :basename, :extname #文書末尾 ENDPOS = 1 #文書先頭 BEGINPOS = 0 TAG_BEGIN_CHAR = '#' TAG_END_CHAR = '#' def expand_path(str) @fso.getAbsolutePathName(str.tosjis).toutf8 end def initialize(path) @msword = WIN32OLE.new('Word.Application') @fso = WIN32OLE.new('Scripting.FileSystemObject') @wsh = WIN32OLE.new('Wscript.Shell') @msword.Visible = true @path = expand_path(path) @crrDir = crrDir = expand_path('.') @dir = File.dirname(@path) @basename = File.basename(@path, ".*") @extname = File.extname(@path) @msword.Documents.open(@path.tosjis) end #置換(テキストボックスは含まない) def replace(fromStr, toStr) text = "#{TAG_BEGIN_CHAR}#{fromStr}#{TAG_END_CHAR}".tosjis toStr = toStr.tosjis p "REPLACE from #{text} to #{toStr}" @msword.Selection.Find.ClearFormatting @msword.Selection.Find.Replacement.ClearFormatting @msword.Selection.Find.Text = text @msword.Selection.Find.Replacement.text = toStr @msword.Selection.Find.MatchCase = true @msword.Selection.Find.Execute(nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,2) end #WORDファイルの結合。pos:先頭0、末尾:1 def concat(wordFile, pos = ENDPOS) oth = MyDoc.new(wordFile) oth.selectAll.copy oth.dtor cursorTo(pos) case pos when BEGINPOS paste @wsh.SendKeys("^{ENTER}") else @wsh.sendKeys("^{ENTER}") paste end return self end #カーソル位置の移動。pos:先頭0、末尾:1 def cursorTo(pos = ENDPOS) activate case pos when BEGINPOS key = "{PGUP}" else #END key = "{PGDN}" end #[]todo 決め打ちは良くないが 10.times do |i| @wsh.SendKeys(key) sleep(0.1) end return self end def selectAll p 'selectAll' activate #もっと良い実装ありそうだけど p @basename @wsh.SendKeys("^a") sleep(0.1) return self end def copy activate p 'copy' #もっと良い実装ありそうだけど @wsh.SendKeys("^c") sleep(1) return self end def paste activate #もっと良い実装ありそうだけど @wsh.SendKeys("^v") sleep(1) return self end def save(path) activate abPath = expand_path(path) @msword.ActiveDocument.SaveAs(abPath.tosjis) end #タグのところを画像に変換する。必要になったら実装する。。 def replaceToImage raise 'NO IMPLEMENTATION ;-)' return self end def sanitize activate #ゴミのように残ってしまった#.*#を削除する sel = @msword.Selection.Range sel.Find.Text = "#{TAG_BEGIN_CHAR}*#{TAG_END_CHAR}" sel.Find.Replacement.text = '' sel.Find.MatchWildcards = true #引数「2」は'Replace'=>Word::WdReplaceAll相当 sel.Find.Execute(nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,2) end def activate @msword.Activate end #デストラクタ def dtor @msword.Quit end end
ホントはちゃんと書きたいのですけど。。
誰か協力して書きませんか!?魔窟探検の協力者募集!(ぇ