OpenOffice.org scripting
Pequeña introducción a la programación de scripts para OpenOffice.org. La receta incluye una macro para convertir a PDF desde línea de comandos.
Ingredientes
- openoffice.org
- python-uno
Introducción
OpenOffice.org está basado en una arquitectura de componentes llamada UNO, algo parecido a CORBA o ZeroC Ice. Hasta tal punto que los clientes y los componentes pueden estar en máquinas diferentes. Y también como en CORBA se pueden programar los componentes en varios lenguajes: OOBasic, C++, Java, JavaScript, BeanShell y… Python! El más popular de todos ellos es OOBasic, debido principalmente a que el propio OOo incorpora un IDE con depuración y todo. Pero como Basic es muy aburrido, yo elijo Python, que me gusta más :-)
Con UNO se pueden crear:
- Componentes, que se pueden añadir a la propia interfaz de OOo, con menús, cuadros de diálogo, etc.
- Aplicaciones, que utilizan el API de OOo y que normalmente necesitan que previamente exista una instancia de OOo en ejecución. Este HelloWorld funciona de ese modo:
import uno
localContext = uno.getComponentContext()
resolver = localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext)
ctx = resolver.resolve("uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext")
smgr = ctx.ServiceManager
desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
model = desktop.getCurrentComponent()
text = model.Text
cursor = text.createTextCursor()
text.insertString(cursor, "Hello World", 0)
ctx.ServiceManager
- Macros, que ejecuta el propio OOo. Se puede hacer desde “Tools→Macros→Execute macro” o desde línea de comando. Este otro “HelloWorld” que viene con el paquete Debian
python-uno
funciona como macro.
# HelloWorld python script for the scripting framework
def HelloWorldPython( ):
"""Prints the string 'Hello World(in Python)' into the current document"""
model = XSCRIPTCONTEXT.getDocument()
text = model.Text
tRange = text.End
tRange.String = "Hello World (in Python)"
return None
Conversor PDF
Partiendo de esta macro en OOBasic, la gran cantidad de ejemplos que hay en http://www.oooforum.org/, y sobre todo de la OOLib.py de Danny Brewer, más unas pequeñas modificaciones propias he escrito esta otra macro, que hace lo mismo, pero en Python (que mola más):
import os
def serviceManager():
return XSCRIPTCONTEXT.getComponentContext().getServiceManager()
# The CoreReflection object. It is cached in a global variable.
goCoreReflection = None
def getCoreReflection():
global goCoreReflection
if not goCoreReflection:
goCoreReflection = serviceManager().createInstance("com.sun.star.reflection.CoreReflection")
return goCoreReflection
def createUnoStruct(cTypeName):
"""Create a UNO struct and return it.
Similar to the function of the same name in OOo Basic."""
# Get the IDL class for the type name
oXIdlClass = getCoreReflection().forName(cTypeName)
# Create the struct.
oReturnValue, oStruct = oXIdlClass.createObject(None)
return oStruct
def pathnameToUrl(cPathname):
"""Convert a Windows or GNU/Linux pathname into an OOo URL."""
if len( cPathname ) > 1 and cPathname[1:2] == ":":
cPathname = "/" + cPathname[0] + "|" + cPathname[2:]
return "file://" + cPathname.replace("\\", "/")
def openURL(cUrl, tProperties=()):
"""Open or Create a document from it's URL."""
oDesktop = XSCRIPTCONTEXT.getDesktop()
return oDesktop.loadComponentFromURL(cUrl, "_blank", 0, tProperties)
def MakePropertyValue( cName=None, uValue=None, nHandle=None, nState=None ):
"""Create a com.sun.star.beans.PropertyValue struct and return it."""
oPropertyValue = createUnoStruct("com.sun.star.beans.PropertyValue")
if cName != None:
oPropertyValue.Name = cName
if uValue != None:
oPropertyValue.Value = uValue
if nHandle != None:
oPropertyValue.Handle = nHandle
if nState != None:
oPropertyValue.State = nState
return oPropertyValue
def main(dummy):
cSourceFile = os.environ['OOARG']
cSourceURL = pathnameToUrl( cSourceFile )
cTargetFile = os.path.splitext(cSourceFile)[0] + '.pdf'
cTargetURL = pathnameToUrl( cTargetFile )
# Open the source document.
# No filter necessary. OOo will figure it out from the .DOC extension.
oDoc = openURL(cSourceURL, (MakePropertyValue("Hidden", True),))
# Save the newly opened document.
oDoc.storeToURL(cTargetURL,
(MakePropertyValue("FilterName","writer_pdf_Export"),))
oDoc.dispose()
g_exportedScripts = main,
Tienes que escribir esto en un fichero que se llame ~/.openoffice.org2/user/Scripts/python/oo2pdf.py
.
¿Y cómo se usa?
Pues “simplemente” tienes que ejecutar este comando:
$ OOARG=/home/usuario/fichero.odt soffice -invisible "vnd.sun.star.script:oo2pdf.py\$main?language=Python&location=user"
Obviamente lo mejor es escribir otro scriptillo para ejecutar ese engendro de comando.
#!/usr/bin/python
import sys, os
os.system('OOARG=%s soffice -invisible "vnd.sun.star.script:oo2pdf.py\$main?language=Python&location=user"' % os.path.abspath(sys.argv[1]))
Lo metes en ~/bin/oo2pdf
, le das permisos de ejecución y lo puedes ejecutar con:
$ oo2pdf fichero.odt
También lo puedes transformar en un g-script para nautilus, pero lo realmente interesante es utilizarlo desde un Makefile
o similar para convertir de forma automatizada miles de .odt
de un plumazo. También convierte cualquier otro formato de OOo, incluido el .doc de MS (pero no se lo digas a nadie).
Ficheros
Estos ficheros los tienes en nuestro repositorio subversion
Problemas
El fichero de entrada se especifica en la variable de entorno $OOARG, que luego se lee desde el script. Eso es porque no he conseguido averiguar cómo pasar argumentos al script desde la línea de comandos de soffice
. Con OOBasic se puede hacer, pero está claro que OOo trata de modo muy diferente a las macros en OOBasic que las escritas en el resto de lenguajes. Si sabes cómo hacerlo, por favor, deja un comentario.