Scripting LibreOffice with Python

Scripting LibreOffice with Python

We were recently asked to automate some editing tasks for the Spotlight English editors who use LibreOffice Writer to prepare their episode copy.

With LibreOffice’s UNO (Universal Network Objects) component model, which has bindings to many programming languages, we were quite spoiled for choice. We could have gone with JavaScript, Basic, Python and the Java-like BeanShell scripting languages. Python was our favourite in that bunch and once you get going it is a very powerful and productive stack to work with. However, it was difficult to find information on how to set up a development environment. It was also a challenge to find good examples of how to code against the extensive API. So, here is what we learned:

Set up a project folder

With any project we like to have our code under version control so we can collaborate and roll back to earlier versions. So, the first step is to set up a project folder where we can edit, build, test and deploy our code. And after the project is delivered we can easily archive the folder.

Our production macros will go into scriptlight.py which we will later embed into our document. dev.py will have some useful scaffolding methods that we want to call on while we are developing the code.

Quick feedback loop

To run a macro in LibreOffice, the scripting file must be in a special system folder or embedded into the document. We could put a symlink to our scriptlight folder in that system folder and configure a keyboard shortcut or toolbar button to trigger it, but it turns out that when we make changes to the macro, the script has to be reloaded by closing and opening the document. This won’t do.

Happily, LibreOffice can expose it’s API to the shell by running with an open socket. Let’s try it.

First we launch LibreOffice Writer with a new document and an open socket to communicate with from the Python shell:

or

Next we launch the copy of Python which is included in LibreOffice:

To start controlling our document, we type in the following:

This should now insert our message into the open document.

Run from a file

When the script is not running through a socket connection, there is a shorter way to get hold of the active document or “model”, but while we have not embedded the script we have to use the steps above. Let’s package this as a function in our dev.py file

Now in scriptlight.py we can have the following:

To run your script against the open document you issue this command:

or

Ah, now we are in business. We can make a small change to the script and hit up arrow enter in terminal to test it.

Some things to note: you can expose any function in the script to be available for calling from the document by including the name in the list on the last line. Note the inconsistent use of the comma. If you had two functions it would look like this:

Exploring the API

The API docs are not the easiest to work with:

  1. There is no one canonical place to look. Both OpenOffice and LibreOffice have docs that point to each other.
  2. The docs have code samples in many different languages and most of them use the interface-orientated architecture that is unnecessarily indirect

To help with this a little we have these functions in dev: dev.printObjectProperties and dev.printInterfaces which will list any object’s properties and the interfaces they implement respectively.

Deployment

When we have developed and tested our macro via the convenient socket interface, we are ready to deploy it into a template or document. The first deploy step is to comment out all uses of the dev helper script. Then push the macro into a newly prepared LibreOffice document file. It turns out that a LibreOffice document is actually a zipped folder with various content, formatting and meta data files inside. To add a macro file, we must add the file to a special folder inside the zip and register the new file in the zip manifest. This can be done manually with a long recipe or like this:

Once the script is deployed, you can open the document and the macro should show up under the run macro menu. Typically you will right-click on a visible toolbar and select Customize toolbar… from the context menu to assign your macro to a toobar button or a shortcut key.
Running the embedded script is a lot faster than running it through a socket, but you might not notice the difference if your script isn’t doing a lot of work.

speng

Rather than adding a load of command aliases to our bash profile with every new project, we like to make a small bash script with intuitive shortcuts to all our repeating CLI commands for the project. This way we can file it away with our project once it is done and if we ever need to come back to the project months later, we don’t have to go read up again how to start, build or deploy the project. So for this project we picked “speng” SPotlight ENGlish:

This is how mine looks:

It might look scary and overkill to write all that just to save a few keystrokes, but once you have a template like this it is easy to adapt for any project and it is a great way to document what you need to do next time you pull the project from the shelf.
You will see a refence to push_macro.py which does the deployment for us. Check it out in this gist.

Further reading

* Official API docs – good luck
* Christopher Bourez’s blog – excellent article with lots of good examples. Especially his Calc macros.
* Jamie Boyle’s Cookbook – learned some good debugging tips from this guide

Tags:
Jannie Theunissen
jannie@onesheep.org
No Comments

Post A Comment