jueves, 21 de marzo de 2024

Textos. OOo Basic

Posicionamiento y desplazamiento mediante OOo Basic. (1)

Cuando trabajamos con textos podemos automatizar su composición partiendo de un documento en blanco y escribirlo totalmente desde código. Posiblemente esta sea la solución sencilla, pero no siempre es posible (y a veces tampoco recomendable) aplicarla. Cuando el documento-modelo nos viene impuesto (por ejemplo, el modelo de informe o de dictamen), replicarlo íntegramente mediante código (forma u contenido) resulta muy complicado y poco funciona, lo que compromete la viabilidad de su automatización. 


En esos  casos es evidente que tenemos que cambiar de estrategia: ahora es preferible trabajar sobre el documento-modelo y no sobre un documento en blanco; pero esto nos obliga a aprender cómo situarnos en posiciones concretas y como desplazarnos de una a otra. Algo de ello sabemos mediante macros, pero ¿desde OOo Basic?.

Los posicionamientos básicos son al inicio y al final del documento. Ya sabemos cómo hacerlo usando Grabar macro (2), como podemos ver en las instrucciones que siguen, concretamente lo indicado en negrita:

  • Al inicio:  dispatcher.executeDispatch(document, ".uno:GoToStartOfDoc", "", 0, Array())
  • Al final: dispatcher.executeDispatch(document, ".uno:GoToEndOfDoc", "", 0, Array())

Las instrucciones alternativas en OOo Basic repiten la diferencia inicio-fin (3):

  • Al inicio: oText.insertString(oText.getStart(),sTexto & Chr(13), False)
  • Al final: oText.insertString(oText.getEnd(),sTexto & Chr(13), False)
Y como ejemplo podemos observar este sencillo script, que escribe al inicio del documento una palabra y otra al final (4):

Sub EscribirInicioFinal

Dim oText As Object

Dim sTexto As String

oText = ThisComponent.Text

sTexto = "Patata"

oText.insertString(oText.getStart(),sTexto & Chr(13), False)

sTexto = "Coliflor"

oText.insertString(oText.getEnd(),sTexto & Chr(13), False)

End Sub

A pesar de la sencillez de estas funciones, su utilidad es mucha, ya que nos permite componer un texto de la extensión que deseemos, usando únicamente la posición final o la posición inicial (5).

Ambas funciones son útiles cuando componemos el texto sobre un documento en blanco, pero no se ajustan al trabajo con documentos-modelo. En este caso, aunque podemos optar por desplazamientos basados en macros de desplazamiento, esta alternativa no es viable por costosa y por estar sujeta a errores. Otra alternativa consiste en usar tablas para facilitar el posicionamiento y el desplazamiento (6), ya que resuelve ambos tanto en el uso "manual" del documento como en su automatización, pero contamos aun con una alternativa mejor: los marcadores o BookMark (7).

Este es un script en el que muestra las dos fases del procedimiento. En la primera fase se accede al marcador que se asigna a una variable-objeto (en este caso oMarca1) y en la segunda se escribe texto en la posición indicada por el marcador.
Sub IntertarTextoEnPosicionMarcador

Dim oMarca1 As Object

oMarca1 = ThisComponent.getBookmarks().getByName("Marcador1")

oMarca1.getAnchor.setString("Inserto texto")
End Sub

Del acceso al marcador se encarga la función getBookmarks() que precisa de una concreción del procedimiento mediante una segunda función, la cual, en nuestro ejemplo, se concreta como acceso por el nombre (getByName()). La primera función no precisa parámetro, pero sí la segunda (getByName("Marca1")). En resumen, mediante la primera instrucción asignamos a la variable objeto el acceso al marcador Marca1:

 oMarca1 = ThisComponent.getBookmarks().getByName("Marca1")

Mediante la segunda instrucción posicionamos un string en el punto del documento en que se inserta Marcador1. También podemos posicionar el contenido de una variable simple o resultante de la concatenación de varias. Del procedimiento "escribir" se encarga la función setString() a la que se pasa como parámetro el texto o la/s variable/s cuyo contenido queremos sea escrito en la posición del marcador.

oMarca1.getAnchor.setString("Inserto texto")

Partiendo de este script modelo básico, podemos crear script complejos que nos permitirán (no sin trabajo) complementar documentos-modelo igualmente complejos, con total precisión posicional.

Repito: este procedimiento está especialmente recomendado cuando el documento-modelo es de obligado uso o tiene un nivel de complejidad de formato que no resulta viable componer el documento directamente desde código. 

Como síntesis y ejemplo de lo explicado en este entrada vamos a trabajar con documento que suponemos prescriptivo (8). Partimos del supuesto de la existencia de un documento-modelo que consta de una parte prediseñada mediante tablas y un párrafo predefinido personalizable y dos párrafos cuyo contenido se concreta mediante código.

En la primera parte (tablas) utilizamos los marcadores como recurso para completar su contenido, en un caso (Tabla1accediendo al inicio de las celdas. Esta tabla no muestra su estructura, pero está formada por dos filas y dos columnas (los marcadores se simulan, puesto que, en realidad, no son visibles):


En las otras dos tablas, que sí se visualizan en su marco exterior, la ubicación de los marcadores no se basa en el posicionamiento en celdas, puesto que en realidad se trata de tablas de celda única (sin subdivisiones en filas y columnas); en realidad se ubican los marcadores del mismo modo que se hace en el primer párrafo: a continuación del nombre del campo. Ejemplo de la Tabla2:

La segunda forma de utilizar marcadores se ejemplifica en el párrafo que sigue da Tabla3 (Parrafo1). En él se definen implícitamente 4 campos, posicionados mediante los marcadores Marca7 a Marca10 (en este caso he optado por señalar su posición mediante |.


Es esta una forma de uso de los marcadores similar a la realizada en Tabla2 y Tabla3, aunque el equivalente en el uso manual de un documento-modelo se asemeja más a lo que denominaré texto-cloze (9). Si se tratara de un documento-modelo para uso "manual", esta podría ser su apariencia:


Personalmente considero que esta es la forma que mayor utilidad tiene en el uso de marcadores en cuando a automatizar la cumplimentación de un documento-modelo, cuando éste viene impuesto como tal. Cierto es que también es perfectamente sustituible por la composición del párrafo directamente mediante código, salvo que se utilicen diferentes formatos que hagan poco viable ese tipo de composición.

Para finalizar, los marcadores Marca11 y Marca12 se utilizan para ubicar los párrafos Parrafo2 y Parrafo3 que en este ejemplo se simplifican en cuanto al contenido, pero que en el uso real pueden ser tanto generados directamente mediante una entrada de texto como generados mediante un algoritmo específico, resultando entonces de la concatenación de string con variables (10).


Obsérvese que, en este caso, he considerado pertinente explicar el modo en que se prepara y trabaja con el texto, ya que es necesario para conocer cómo funciona el procedimiento, dada la relación entre éste y el algoritmo que se emplea para automatizar la cumplimentación del documento-modelo. 

Explicaré a continuación ese algoritmo, aunque es necesario tener descargado el documento que lo soporta para acceder al código y comprender mejor esta explicación. Por ello, y antes de proceder, te sugiero que descargues el documento original. Te dejo también acceso a su versión como plantilla, que es la que recomiendo utilizar en las prácticas con el documento para evitar el borrado de los marcadores, lo que inutiliza el propio algoritmo.

Este algoritmo tiene dos script: uno auxiliar (InterTextoEnPos(), en realidad una subrutina) y otro principal (Escribir). Inicio la explicación por el script principal.

Sub Escribir

Dim mConten (12) As String
Dim Conta As Integer
Dim sVar As String
 
mConten(0) = "EOE de Oviedo"
mConten(1) = "Fco Javier Alonso"
mConten(2) = "1123456"
mConten(3) = "Jaime López Pérez"
mConten(4) = "12/08/2017"
mConten(5) = "CP Barrio Nuevo"
mConten(6) = "2º de E. Primaria"
mConten(7) = "24/10/2020"
mConten(8) = "reunión con el equipo educativo"
mConten(9) = "el seguimiento del alumno en el área de matemáticas"
mConten(10) = "la tutora, el orientador y la profesora de PT"
mConten(11) = "La reunión transcurre conforme a lo planeado. Se abordan las dificultades de Jaime en el área de matemáticas, constatándose mejora en el cálculo (sumas y restas) y en la resolución de problemas simples (una operación)"
mConten(12) = "Refozar la práctica del cálculo (restas con llevadas) e introducir operaciones simples de multiplicación y división. Informar a la familia para mejorar la colaboración Centro-Familia. La tutora mantendrá reunión con los padres la semana próxima." 

 

For Conta = 0 To 12:
sVar = mConten(Conta)
InterTextoEnPos(sVar,Conta)
Next

End Sub

Este script se divide en tres partes: declaración de variables, asignación de valores a la matriz y bucle de llamada a la subrutina (script auxiliar).

  • La variable sVar nos sirve como mecanismo de paso de los valores de la matriz a la subrutina (parámetro 1); la matriz mConten(12) se define formada por 13 elementos (0-12) y sirve para contener los datos necesarios para cumplimentar el documento, y, por último la variable Conta es un contador que permite delimitar el bucle final y sirve, además, como parámetro 2 de la llamada a la subrutina.
  • En la segunda para asigno directamente valores a los elementos de la matriz mConten(12). Es un procedimiento simplificado para simplificar el ejemplo, ya que en un desarrollo del script que se pretenda función debería suponer el uso de procedimientos interactivos, que ahora me ahorro. Por ejemplo: mConten = InputBox("Nombre del SEO", "DATOS DEL SEO").
  • Lo interesante de usar una matriz en vez de un conjunto de variables es que, además de simplificar el procedimiento (no ahorramos escribir 13 variables), permite trabajar directamente con el bucle, según veremos. No obstante presenta una dificultad que hace recomendable elaborar previamente (fase de planificación del algoritmo) una tabla de relaciones entre los marcadores, el contenido del elemento y la propia identificación del elemento. Por ejemplo: Marca0 | Nombre SEO | mConten0
  • El bucle con el que finaliza el script es fundamental para entender el procedimiento. Gracias a él (y al uso de la matriz mConten()) podemos resolver el problema de cumplimentar el texto de forma sencilla y breve. Observa que utilizamos un bucle For sobre la base de la variable Conta y recurriendo a la asignación de los contenidos de la matriz, y de forma sucesiva, a la variable sVar. Para ello utilizamos Conta ahora como identificador del valor de posición de la matriz mConten (sVar = mConten(Conta)) (11).
  • Este bucle contiene la llamada a la subrutina (script auxiliar) a la que damos como valores paramétricos las dos variables asociadas al desarrollo del buche: InterTextoEnPos(sVar,Conta). La comprensión de este modo de proceder requiera comprender, a su vez, dicho script.
Paso ahora a explicar la subrutina o script auxiliar:

Sub InterTextoEnPos (sTexto As String, i As Integer)

Dim oMarca As Object

oMarca = ThisComponent.getBookmarks().getByName("Marca"+CStr(i))
oMarca.getAnchor.setString(sTexto)

End Sub

Esta subrutina InterTextoEnPos() recibe dos parámetros (sTexto As String, i As Integer) que se asocian a los parámetros especificados en el bucle final del script principal en la llamada a la propia subrutina (InterTextoEnPos(sVar,Conta)). 

sTexto -> sVar

Conta -> i 

Además requiere una variable objeto (oMarca) que es la que cumple la doble tarea de recibir como contenido cada uno de los marcadores del texto... 

oMarca = ThisComponent.getBookmarks().getByName("Marca"+CStr(i))

... y pasar los datos a las posiciones establecidas mediante los marcadores

oMarca.getAnchor.setString(sTexto) 

Si en esta segunda tareas la función setString() es clave, en la primera lo es la forma en que se expresa el parámetro de la función getByName("Marca"+CStr(i)) Es gracias a la expresión "Marca"+CStr(i) que podemos acceder no a un marcador concreto, sino al conjunto de los marcadores (12). El bucle For que empleamos en el script principal y la asociación establecida entre Conta -> i hacen posible este recorrido y su buen funcionamiento. De establecer el contenido concreto en cada posición (marcador) se encarga la asociación entre los dos primeros parámetros de la subrutina y su llamada desde el bucle, esto es: sTexto -> sVar (13)

Aunque esta entrada se ha alargado más de lo que podría parecer conveniente, creo que el resultado es satisfactorio, ya que me ha permitido explicar con detalle el funcionamiento del algoritmo. Dada la frecuencia con la que debemos trabajar con documentos-modelo de uso prescriptivo u obligado, lo aquí expuesto no permitirá automatizar la elaboración de este tipo de soportes documentales. Incluso el código generado se puede considerar a modo de plantilla para afrontar necesidades reales. Es necesario, eso sí, implementar un procedimiento funcional de interface, así como trabajar con más detalle cómo elaborar textos complejos como los asociados a los componentes 11 y 12 de la matriz. Pero ambas cuestiones ya han sido abordadas en las dos entradas que preceden a ésta en el esquema de contenidos que puedes encontrar en Usos. A ellas te remito.


NOTAS

(1) Esta entrada está basada y sustituye a una entrada publicada el 31/07/2023, titulada Writer. Posicionamiento y desplazamiento.
(2) De forma análoga podemos hacerlo respecto a las páginas y las líneas, así como en otros desplazamientos. Te aconsejo que crees una colección de macros simples específicamente pensados para dar respuesta a este objetivo para usarlas desde los script cuando sean necesarias. Posiblemente no sea de forma frecuente, porque, salvo situaciones excepcionales, siempre que sea posible es preferible generar el texto directamente mediante código, como ya dije.
(3) Para la instrucción de escritura de texto en OOo Basic [ver aquí]
(4) La posición inicio no da lugar a dudas, pero la posición final depende de si se ha establecido como tal en el documento, por ejemplo, si contiene texto previamente escrito. En caso de no haberse diferenciado ambas posiciones (esto es: en caso de un documento en blanco), la posición final sigue lógicamente a la inicial. Cuando sólo especificamos en el script la posición final, la primera vez que se usa el documento (en blanco), como es de suponer, la posición inicial coincide con la final.
(5) Lógicamente, también podemos combinar ambas, pero puede ser suficiente con utilizar únicamente una de ellas. Un uso práctico en el que ésta alternancia de posiciones es relevante es, por ejemplo, un registro cronológico de actuaciones. Según nos interese el orden cronológico directo (primacía)  o la recencia, deberemos utilizar una instrucción u otra: para la primacía usaremos getEnd(), de modo que las entradas queden ordenadas cronológicamente, mientras que para la recencia usaremos getStart() para el las entradas queden ordenadas en orden inverso al que fueron anotadas.
(6) El uso de tablas es posiblemente la opción más apropiada cuanto creamos un documento-modelo para cumplimentar desde teclado.
(7) Los marcadores no son incompatibles con las tablas, aunque no se limita a ellas, lo que les da gran versatilidad para componer documentos-modelo. Exigen, eso sí, el posicionamiento previo en el documento, el cual debe realizarse manual (algo lógico desde la perspectiva del documento-modelo), pero los marcadores son muy sensibles al borrado accidental. De la primera característica se deriva que exigen un trabajo previo (que a veces es tedioso) y de la segunda que es preferible convertir el documento-base en plantilla, evitando así errores de borrado accidental.
(8) El estatus de prescriptivo de un documento-modelo puede derivar tanto de haber sido planteado como tal por la Administración (vg. el modelo de informe) como de acuerdos o prácticas consolidadas del propio SEO. Repito que, en mi opinión, por simplicidad y para el segundo caso, pensando en la automatización de su cumplimentación, sería preferible que ese modelo estuviera diseñado de forma que sea posible cumplimentarlo sobre un documento en blanco y que todo el procedimiento fuera realizado desde código. No obstante debemos contar con la posibilidad de que, por exigencias del formato preferido, nos veamos "obligados" a trabajar con modelos documentales prediseñados, de forma similar a lo que frecuentemente plantea la Administración como prescripción.
(9) No es ésta una correcta definición de este tipo de texto, pero responde bien a la idea: un texto en el que los huecos (variables, en realidad) quedan señalados para ser complementados por el usuario. Como documento de uso "manual" por los componentes de los SEO, los datos a actualizar se pueden expresar mediante un nombre representativo o mediante guion bajo. En este segundo caso la información antecedente debe dejar clara la información que se solicita. Por eso, aunque pueda resultar redundante, he preferido explicitar el contenido en el modelo "manual" que se ilustra mediante la imagen que sigue a la propia de Parrafo1.
(10) Dado que no es ese el objetivo de esta entrada, he optado por la forma más simple de crear ambos párrafos: escribiendo un string como contenido asignado a las variables o componentes mConten11 y mConten12 de la matriz. Esta razón es la misma que me ha llevado a simplificar la asignación de datos al resto de los elementos de la matriz mConten().
(11) Queda claro que Conta (variable numérica) sustituye a los valores posicionales o índices 0 a 12, al tomar Conta esos valores en función del recorrido que realiza el bucle.
(12) Es la combinación del bucle For que empleamos en el script principal y la asociación establecida entre Conta -> i los que hacen posible este recorrido y su buen funcionamiento. De no ser de este modo, necesitaríamos generar en la subrutina tantas líneas de código como marcadores hubiéramos creado.
(13) Además de otras cosas, la sincronización entre la matriz mConten() y la colección de marcadores, incluyendo la forma simplificada en que se expresan es resultado de un proceso previo de planificación. Esta fase es fundamental y saltársela puede suponer errores en el funcionamiento, trabajo innecesario y pérdidas de tiempo.

No hay comentarios:

Publicar un comentario

Comenta esta entrada