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.

Referencias

Python UNO



blog comments powered by Disqus