AutoIT automation of Open Office
A friend of mine asked for some help automating Open Office. They wanted 2 scripts:
- One to open all ODT files in a directory
- A second one to iterate through all ODT files, save them then close out of Open Office
Since I'd never done anything in AutoIt before I had to spend time getting familiar with what the scripting engine could do. After that I had to find the appropriate scripting hooks into OpenOffice to be able to accomplish the tasks. Fortunately OpenOffice has the appropriate hooks to allow for scripting and I was able to finish the scripts.
References
- Automating OpenOffice.org [openoffice.org]
- OOo COM UDF [autoitscript.com]
- Accessing Data in Arrays [autoitscript.com]
- Using Openoffice as PDF & HTML Converter [autoitscript.com]
- openoffice_test.au3 [autoitscript.com]
- UNO automation with a binary (executable) [wiki.openoffice.org]
- OpenOffice interface XDesktop [openoffice.org]
AutoIt
AuoIt is a windows script harness with a syntax like BASIC which can be used to automate tasks on Windows systems. I think it is popular in part because it bills itself as being easy to learn while also being lightweight and easy to deploy. From my short experience with the language I don't think the syntax is any more difficult to learn than any other scripting language that I've used.
I was under the impression before jumping into this project that AutoIt had more innate capability to reach into programs and perform actions. As I went along I found references to UDFs (User Defined Functions) which users of AutoIt have created to allow for easy scripting of windows applications. Trying to find an OpenOffice Writer UDF proved difficult so I ended up having to hack-together my own script from documentation fragments across several websites and from an example UDF created for Open Office Calc.
Open Files Script
The script needed to:
- Iterate through all .odt files in a directory
- Open the odt files using the default handler (OpenOffice in this case)
To pull this off I leveraged _FileListToArray and ShellExecute commands from AutoIt to do the iteration and open the files. The core of the script looks like this:
#include < file.au3> $path = "C:\Users\yourName\Projects\AutoIt\OdtFiles" $odtsInDirectory = _FileListToArray($path, "*.odt", 1); For $i = 1 To $odtsInDirectory[0] ShellExecute($path & "\" & $odtsInDirectory[$i]) Next
The _FileListToArray function can filter files so only .odt files will be executed by shellExecute
ODT Save and Close Files
Opening the files was the easy part as AutoIt leverages windows shell extension binding to open files using the appropriate program. Saving a specific set of odt files and then closing open office required digging into the Open Office automation hooks.
This script needed to:
- Iterate through the list of ODT files that were already opened
- Save the file
- Close out of OpenOffice
For me to get the script to work properly I had to find solutions for a few things:
- Interacting with the Open Office script host
- Converting the paths of the open odt files to a form that the Open Office script host can understand
- Finding the API Arguments for loadComponentFromURL which let me bind to already opened files
- Finding a way to make an Open Office PropertyValue which I can pass to loadComponentFromURL (it's a required parameter)
I was able to pull functions to convert file path and get the 'PropertyValue' that I needed from this autoit forum post. After that I needed to get my Open Office automation commands working in the right order. For reference, this is what I found the sequence to be:
- Instantiate a com.sun.star.ServiceManager object
- Instantiate a com.sun.star.frame.Desktop object from the Service Manager
- Iterate and get the list of odt files that were opened in the 'open' script (Filtering to avoid the .~ files)
- Convert each valid odt file to a path that Open Office automation can use
- When using the Desktop.loadComponentFromURL method, pass in "_default" to make sure you bind to the already open document. Also be sure to pass in an Array with a 'PropertyValue' represented inside
- Once you have the document loaded, use the .store() method to 'save' the file
- Once all desired files are saved, close the desktop by using the Desktop.terminate() method.
Here's the script:
; ; Required for _FileListToArray #include < file.au3> ; ; Required for debugging alert boxes #include < MsgBoxConstants.au3> ; ; Instantiate Open Office Objects $OO_ServiceManager = ObjCreate("com.sun.star.ServiceManager") $OO_Desktop = $OO_ServiceManager.createInstance("com.sun.star.frame.Desktop") ; ; Don't hide the existing windows Local $inputargs[1] = [_MakePropertyValue("Hidden", False)] $path = "C:\Users\rion\Stashdot\Projects\AutoIt\" $odtsInDirectory = _FileListToArray($path, "*.odt", 1); If @error = 1 Then MsgBox($MB_SYSTEMMODAL, "", "Path was invalid.") Exit EndIf If @error = 4 Then MsgBox($MB_SYSTEMMODAL, "", "No file(s) were found.") Exit EndIf For $i = 1 To $odtsInDirectory[0] $infile = _Convert2URL($path & "\" & $odtsInDirectory[$i]) $splits = StringSplit($path & "\" & $odtsInDirectory[$i], "\") if StringLeft($splits[$splits[0]], 1) = "." Then ;MsgBox($MB_SYSTEMMODAL, "skipping", $splits[$splits[0]], 3); debug, see which files we're skipping ContinueLoop EndIf ; the "_default" says to find the already opened document ; the inputargs are above and make sure the window stays open $alreadyOpenedDocument = $OO_Desktop.loadComponentFromURL($infile, "_default", 0, $inputargs) ;MsgBox($MB_SYSTEMMODAL, "About to save", "", 3); debug to see which files are about to be saved MsgBox($MB_SYSTEMMODAL, $infile, "", 3); ; save the file $alreadyOpenedDocument.store(); Next ; Close Open Office $OO_Desktop.terminate(); ; ; [Taken From] ; forum ; http://www.oooforum.org/forum/viewtopic.phtml?p=73715&highlight=filters#73715 ; sun programming manual for basic http://docs.sun.com/app/docs/doc/819-1326?l=de ;convert to OO name convention ; Func _Convert2URL($fname) ; no http found ? If StringInStr($fname, "http") == 0 Then $fname = StringReplace($fname, ":", "|") $fname = StringReplace($fname, " ", "%20") $fname = "file:///" & StringReplace($fname, "\", "/") EndIf Return $fname EndFunc ;==>_Convert2URL ; Taken from forum ; http://www.oooforum.org/forum/viewtopic.phtml?p=73715&highlight=filters#73715 ; sun programming manual for basic http://docs.sun.com/app/docs/doc/819-1326?l=de Func _MakePropertyValue($cName, $uValue) Local $Pstruc $Pstruc = $OO_ServiceManager.Bridge_GetStruct("com.sun.star.beans.PropertyValue") $Pstruc.Name = $cName $Pstruc.Value = $uValue ; ($uValue) ; MsgBox(0,"", $Pstruc.Value) Return $Pstruc EndFunc ;==>_MakePropertyValue
I had to figure out string split and starts with in order to really filter out the .~ temp files. Once that was figured out the rest of the script came together.