OpenOffice形式に変換する

OpenOffice形式(ODF:.odt, .ods, .odpなど)はZip圧縮された複数のXMLファイルなので、テキストを抽出したりするのが容易です。
一方、以前のMS Office形式(.doc, .xls, .pptなど)はバイナリ形式なのでテキスト抽出などは困難です。
なのでOpenOffice形式への変換方法などを調べました。

目的

MS Office形式(.doc, .xls, .pptなど)からOpenOffice形式(ODF:.odt, .ods, .odpなど)に変換したい。

欲しい機能

  • Linux(Ubuntu)で使用できる
  • バッチ処理ができる。(GUIを操作したくない)
  • 新規にツールなどをインストールしたくない
  • 処理が速い

解決策

下記の参考サイトを参考にして、OpenOfficeを使用してファイル形式を変換することにしました。
OpenOfficeにファイル変換用マクロを登録し、そのマクロを呼び出してファイル形式を変換します。
以下の2つのファイルを編集して、ファイル変換用マクロを登録します。

1. ~/.openoffice.org/3/user/basic/Standard/Export.xba

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
<script:module xmlns:script="http://openoffice.org/2000/script" script:name="Export" script:language="StarBasic">
Function MakePropertyValue( Optional cName As String, Optional uValue ) _
   As com.sun.star.beans.PropertyValue
   Dim oPropertyValue As New com.sun.star.beans.PropertyValue
   If Not IsMissing( cName ) Then
      oPropertyValue.Name = cName
   EndIf
   If Not IsMissing( uValue ) Then
      oPropertyValue.Value = uValue
   EndIf
   MakePropertyValue() = oPropertyValue
End Function


Sub SaveAsOOO( cInFile, cOutFile )
    On Error Goto ErrorHandler
    cInURL = ConvertToURL( cInFile )
    oDoc = StarDesktop.loadComponentFromURL( cInURL, &quot;_blank&quot;, 0, _
               Array( MakePropertyValue( &quot;Hidden&quot;, True ), ) )
    cOutURL = ConvertToURL( cOutFile )
    oDoc.storeAsURL( cOutURL, Array() )
    oDoc.close( True )
    Exit Sub
    
ErrorHandler:
	Resume Next
End Sub

</script:module>

下記の参考サイトのソースをベースに作成しました。SaveAsOOOにはエラーハンドラを追加しました。これを追加しておかないと、ファイルが見つからないなど、何らかのエラーが生じた際にメッセージボックスが表示されてしまい、バッチ処理には向きません。

2. ~/.openoffice.org/3/user/basic/Standard/script.xlb

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd">
<library:library xmlns:library="http://openoffice.org/2000/library" library:name="Standard" library:readonly="false" library:passwordprotected="false">
 <library:element library:name="Export"/>
</library:library>

登録したマクロをシェルから呼ぶには以下のようにします。

ooffice -invisible -nologo 'macro:///Standard.Export.SaveAsOOO("/tmp/example.ppt", "/tmp/example.odp")'

仕様かどうかはわかりませんが、-nologoを付けないと処理がバックグラウンドで行われるようです。

ソースコード

上記のマクロを呼び出すpythonコードを作成しました。

import os, stat


def convert_from(from_path, to_path):
    updated_at = os.stat(to_path)[stat.ST_MTIME] if os.path.exists(to_path) else None
    command = 'ooffice -invisible -nologo '\
        '\'macro:///Standard.Export.SaveAsOOO("{0}", "{1}")\''
    # TODO escape from_path and to_path
    os.system(command.format(from_path, to_path))
    if not os.path.exists(to_path):
        raise Exception, 'Failed to convert to OpenOffice format, not exists'
    if os.stat(to_path)[stat.ST_MTIME] == updated_at:
        raise Exception, 'Failed to convert to OpenOffice format, not updated'


def _test():
    import tempfile
    tempfd, temppath = tempfile.mkstemp()
    convert_from('/tmp/validfile.ppt', temppath)
    os.remove(temppath)

    tempfd, temppath = tempfile.mkstemp()
    #convert_from('/tmp/invalidfile.ppt', temppath)
    os.remove(temppath)

    temppath = '/tmp/out.odp'
    convert_from('/tmp/validfile.ppt', temppath)
    os.remove(temppath)

    #convert_from('/tmp/invalidfile.ppt', temppath)
    #os.remove(temppath)

if __name__ == '__main__':
    _test()

変換が正常に行われたかどうかは、リターンコードでは判別できません。
出力ファイルが存在しない、もしくは出力ファイルが更新されていないときは変換に失敗したと判断します。