diumenge, 21 de novembre del 2010

Desplegament de llibreries de macros a l'OpenOffice.org amb un add-on

Una opció per a fer el desplegament "professional" de llibreries, components o extensions per a l'OpenOffice.org són els add-on. Esl add-on són extensions a l'OOo que presenten algun tipus d'integració amb la interfase d'usuari de l'OOo, per exemple menús o barra d'eines.

Per a explicar aquest mecanisme, desenvoluparé un wrapper per a l'aplicació de consola tesseract-ocr amb OOobasic i el desplegaré com un add-on.

Tesseract-ocr és un (antic però eficient) motor d'OCR (reconeixement òptic de caràcters) desenvolupat inicialment per HP i que actualment és mantingut per Google.

Primer de tot, per tant, cal instal·lar el tesseract-ocr. En Ubuntu Lucid Lynx es pot fer des del "Centre de Programari de l'Ubuntu" i s'obté la versió 2, i s'hi poden afegir els idiomes anglès i castellà. A Windows, es pot descarregar la versió 3 des de la mateixa web de Tesseract-ocr. En la versió 3, a més del castellà i l'anglès, també es pot afegir el català.

El cas és que Tesseract-ocr és una aplicació de consola a la que cal passar-li arguments per a fer-lo treballar.

albert@atenea:~$ tesseract
tesseract:Error:Usage:tesseract imagename outputbase [-l lang] [configfile [[+|-]varfile]...]

Per a facilitar l'ús del tesseract, doncs, faré una UI que permeti construir la invocació de línia d'ordres del tesseract i l'executaré en una shell. Com que el que es pretén és obtenir un text a partir d'una imatge per a treballar amb ell, sembla que una bona idea és crear un menú nou "OCR" al Writer de l'OpenOffice.org que en fer-hi click ens obri una GUI que analitzi la imatge i que un cop obtingut el text, l'importi al processador de texts.

A més, per a facilitar-ne la distribució, el que faré serà empaquetar la macro i el diàleg en un .oxt, un add-on, de forma que per a instal·lar-lo n'hi haurà prou amb obrir el fitxer .oxt amb l'OpenOffice.org Writer (o també directament amb la utilitat unopkg, o amb el gestor d'extensions).

Dit i fet. A "Les meves macros", creo una llibreria que anomeno "tesseract" i en el "Module1" hi afegeixo el següent codi.

REM ***** BASIC *****
REM basat en codi de http://www.oooforum.org/forum/viewtopic.phtml?t=9797


Dim sCarpeta as String
Dim sCarpetaImatges as String
Dim sImatge as String
Dim sNomFitxerText as String
Dim oDialeg1 as Object


' util per a depuració
sub main
ObrirDialeg
end sub


function EsLinux as Boolean
Dim sHome
EsLinux = false
sHome = Environ("HOME")
if left(sHome,1) = "/" then
EsLinux = true
end if
end function


' aquest és el mètode que cal invocar des del menú
Sub ObrirDialeg
' carrega la llibraria de diàlegs estandard
GlobalScope.DialogLibraries.LoadLibrary("tesseract")


' carrega el Diàleg "Dialog1"
oDialeg1 = CreateUnoDialog(DialogLibraries.tesseract.TesseractWrapperDialog)
oDialeg1.Execute()
end sub


' tria la carpeta on guardarà el text obtingut
' fent servir un diàleg del tipus FolderPicker
Sub TriaCarpeta( )
Dim oFolderPickerDlg as Object
Dim sCarpetaTriada as String


' carrega el diàleg
oFolderPickerDlg = createUnoService( "com.sun.star.ui.dialogs.FolderPicker" )


' estableix valors inicials si els té
If Len( sCarpeta ) > 0 Then
oFolderPickerDlg.setDisplayDirectory( ConvertToURL( sCarpeta ) )
EndIf


' executa el diàleg
oFolderPickerDlg.execute()


' guarda el valor
sCarpeta = ConvertFromURL(oFolderPickerDlg.getDirectory())


oDialeg1.getControl("txtCarpeta").text = sCarpeta
End sub


' tria la imatge a analitzar fent servir un diàleg del tipus FilePicker
Sub TriaImatge()
Dim oFilePickerDlg as Object
Dim sFitxers() as String


' XRayTool és una eina imprescindible per al desenvolupador d'OOo!!!
' GlobalScope.BasicLibraries.loadLibrary("XrayTool")


' carrega paràmetres incials al diàleg
oFilePickerDlg = createUnoService( "com.sun.star.ui.dTriaImatgeialogs.FilePicker" )
oFilePickerDlg.setMultiSelectionMode( False )


If Len( sCarpetaImatges ) > 0 Then
oFilePickerDlg.setDisplayDirectory( ConvertToURL( sCarpetaImatges ) )
End If


If Len( sImatge ) > 0 Then
oFilePickerDlg.setDefaultName( sImatge )
End If


' executa el diàleg
oFilePickerDlg.execute()


' obté la carpeta d'imatges
sCarpetaImatges = ConvertFromURL(oFilePickerDlg.getDisplayDirectory())


' obté la imatge triada
sFitxers = oFilePickerDlg.getFiles()
If UBound( sFitxers ) - LBound( sFitxers ) + 1 > 0 Then
sImatge = ConvertFromURL( sFitxers(0) )
Else
sImatge = ""
EndIf


' ajusta el camp de text
oDialeg1.getControl("txtImatge").text = sImatge
end sub


' Fa l'anàlisi OCR
Sub OCR
Dim sParametres as String
Dim sFitxerSortida as String


' obtè els paràmetres des dels camps de text
' per si l'usuari els ha modificat a ma
sNomFitxerText = oDialeg1.getControl("txtFitxerText").text
sImatge = oDialeg1.getControl("txtImatge").text
sCarpeta = oDialeg1.getControl("txtCarpeta").text


' missatge indicant el que va a fer
msgbox "Va a analitzar la imatge " & sImatge & " de la carpeta " & chr(13) & chr(10) & _
"i guardarà l'anàlisi a " & sCarpeta & " en el fitxer " & sNomFitxerText


' de l'OpenOffice.org Wiki:
' Shell(Pathname, Windowstyle, Param, bSync) de la Wiki OooBasic RTL d'OpenOffice
' Pathname
' the path of the program to be executed.


' In MS-Windows, use ConvertToURL(Pathname) otherwise the command will not work
' if Pathname contains spaces or national characters.
'
' Windowstyle
' the window in which the program is started.
' The following values are possible:
'
' * 0 - program receives the focus and is started in a concealed window.
' * 1 - program receives the focus and is started in a normal-sized window.
' * 2 - program receives the focus and is started in a minimized window.
' * 3 - program receives the focus and is started in a maximized window.
' * 4 - program is started in a normal-sized window, without receiving the focus.
' * 6 - program is started in a minimized window, the focus remains in the current window.
' * 10 - program is started in full screen mode.
'
' Param
' command line parameters to be transferred to the program to be started.
'
' bSync
' wait for shell command to finish flag
'
' * true - wait for shell command to finish
' * false - don't wait for shell command to finish


' Tesseract
' Usage:tesseract imagename outputbase [-l lang] [configfile [[+|-]varfile]...]


' posa els paràmetres corresponents a paths de fitxers entre cometes
' una millora seria afegir el llenguatge de l'anàlisi
' ara només considera el castellà (paràmetre -l spa)
sFitxerSortida = chr(34) & sCarpeta & "/" & sNomFitxerText & chr(34)
sParametres = chr(34) & sImatge & chr(34) & " " & sFitxerSortida & " -l spa"


' depenent de si és Linux o Windows invoca per una banda o altre
' en el cas de windows, cal que tesseract.exe estigui instal·lat
' a la carpeta c:\tesseract-ocr
if EsLinux() then
Shell("/usr/bin/tesseract", 6, sParametres, true)
else
Shell("c:\tesseract-ocr\tesseract.exe", 6, sParametres, true)
end if


' cal concatenar el .txt
If FileExists(sCarpeta & "/" & sNomFitxerText & ".txt") Then
MsgBox("Fet")
' importa el fitxer
ImportaFitxer(sCarpeta & "/" & sNomFitxerText & ".txt")
else
MsgBox("No s'ha generat el fitxer de sortida")
End If
End Sub


' El fitxer generat és text pla amb encoding UTF8
' per tant, cal activar aquest filtre d'importació
sub ImportaFitxer(sFitxerSortida as String)
Dim oMediaDescriptor(1) as new com.sun.star.beans.PropertyValue
sFileUrl = convertToUrl(sFitxerSortida)
oMediaDescriptor(0).name = "FilterName"
oMediaDescriptor(0).value = "Text (encoded)"
oMediaDescriptor(1).name = "FilterOptions"
oMediaDescriptor(1).value = "UTF8"
oDoc = StarDesktop.loadComponentFromURL(sFileURL, "_blank", 0, oMediaDescriptor)
end sub


' tanca el diàleg
Sub Sortir
oDialeg1.endExecute()
End sub


La interfase gràfica serà el següent formulari. faig un nou diàleg dins de la llibreria tesseract, i l'anomeno TesseractWrapperDialog.


De dalt a baix, els camps de text els anomeno: txtImatge, txtCarpeta i txtFitxerText

I associo els botons als procediments de la macro:

Botó "imatge a analitzar", al procediment TriaImatge.
Botó "carpeta on guardarà el fitxer de text", al procediment TriaCarpeta.
Botó "Fer l'OCR", al procediment OCR.
Botó "Sortir", al procediment Sortir.

Si ara executo directament el procediment ObrirDialeg, per exemple, des del menú de macros, ja podré provar el wrapper (tesseract en la versió 2 i sense llibreries addicionals no reconeix gaires formats, proveu amb BMP o amb TIFF NO comprimit)

Però el que de debò interessa en aquest exemple és preparar un add-on per a poder desplegar el wrapper.

El procediment general està explicat en el capítol Extensions de la Developer's guide.

Crear una carpeta en la que construiré el projecte d'add-on, dins d'aquesta carpeta creo una subcarpeta "tesseract". En aquesta subcarpeta hi copio els fitxers continguts a la carpeta $HOME/.openoffice.org/3/user/basic/tesseract

Hi trobo quatre fitxers: dialog.xlb, script.xlb, Module1.xba i TesseractWrapperDialog.xdl

Module1.xba i TesseractWrapperDialog.xdl corresponen respectivament al codi de la macro i a la definició en format xml del formulari del diàleg

script.xlb és la llista, en format xml, dels mòduls de la llibreria.
dialog.xlb és,. al seu torn, la llista dels diàlegs de la llibreria.

Em situo de nou a la carpeta superior, al mateix nivell que la carpeta tesseract creo una carpeta META-INF. En aquesta carpeta hi poso el manifest.xml que conté la llista de fitxers que s'estan desplegant:


<manifest:manifest>
<manifest:file-entry manifest:full-path="tesseract/" manifest:media-type="application/vnd.sun.star.basic-library">
<manifest:file-entry manifest:full-path="addon.xcu" manifest:media-type="application/vnd.sun.star.configuration-data">
</manifest:file-entry></manifest:file-entry>
</manifest:manifest>



En aquest manifest estic declarant que la carpeta tesseract conté una llibreria de macros i diàlegs d'OOoBasic. També estic declarant que hi ha un fitxer addon.xcu que conté la configuració de l'aplicació.

Addon.xcu s'ubica en l'arrel de la carpeta de projecte, és dir al mateix nivell que les carpetes teseract i META-INF.

El fitxer addon.xcu descriu que cal modificar la interfase gràfica del context de l'OpenOffice.org Writer per afegir-hi una entrada de menú "OCR" amb un item "Wrapper Tesseract OCR" que invoca la macro que obre el diàleg (amb la URL: macro:tesseract.Module1.ObrirDialeg).



<oor:component-data oor:name="Addons" oor:package="org.openoffice.Office" xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<node oor:name="AddonUI">
<node oor:name="OfficeMenuBar">
<node oor:name="com.serveisticiprogramarilliure.openoffice.ocr" oor:op="replace">
<prop oor:name="Title" oor:type="xs:string">
<value>OCR</value>
</prop>


<prop oor:name="Target" oor:type="xs:string">
<value>_self</value>
</prop>


<node oor:name="Submenu">
<node oor:name="m1" oor:op="replace">


<prop oor:name="URL" oor:type="xs:string">
<value>macro:tesseract.Module1.ObrirDialeg</value>
</prop>


<prop oor:name="Title" oor:type="xs:string">
<value>Wrapper Tesseract OCR</value>
</prop>


<prop oor:name="Target" oor:type="xs:string">
<value>_self</value>
</prop>


<prop oor:name="Context" oor:type="xs:string">
<value>com.sun.star.text.TextDocument</value>
</prop>


</node>
</node>
</node>
</node>
</node>
</oor:component-data>


Addicionalment, podríem afegir un fitxer de descripció description.xml

És dir

temporal-projecte (carpeta)
    addon.xcu
    META-INF (carpeta)
        manifest.xml
    tesseract (carpeta)
        dialog.xlb
        script.xlb
        Module1.xba
        TesseractWrapperDialog.xdl

Practicament ja està, ara només cal fer un zip amb el contingut de la carpeta temporal-projecte i renombrar aquest zip a .oxt

per provar que el .oxt es deplega correctament he d'eliminar primer la macro i el diàleg que he creat. Això puc fer-ho amb l'organitzador de macros.

Un cop eliminat els rastres, aleshores només cal obrir el fitxer .oxt amb l'OpenOffice.org. Al fer-ho s'activa el gestor d'extensions. També podria desplegar el .oxt directament des del gestor d'extensions i també des de la línia d'ordres amb l'eina unopkg.


Albert Baranguer
21/11/2010 - Barcelona 

Cap comentari:

Publica un comentari a l'entrada