jueves, 21 de marzo de 2024

Usos. Textos

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. Esta es posiblemente la solución preferida por ser la más cómoda y sencilla de trabajar desde la programación; pero no siempre es posible (y a veces tampoco recomendable) trabajar de este modo: cuando el documento-modelo nos viene impuestos (por ejemplo, el modelo de informe o de dictamen), replicar mediante código sus formatos y diseños de tablas (entre otras características) resulta muy complicado y poco funcional. De hecho, a veces es hasta dudoso que obtengamos alguna ganancia (de tiempo de trabajo) automatizándolo.

En todo caso, lo que es evidente es 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.

Disponemos para ello de varias alternativas, tanto usando macros como creando código con OOo Basic. Vamos a ver alguna de estas opciones.

Los posicionamientos básicos son al inicio y al final del documento, y ya hemos visto que es factible crear macros simples que nos sitúan en una u otra posición. En formato macro ya creamos el código de posicionamiento al inicio del documento y del mismo modo, usando Grabar macro, podemos crear la macro de posicionamiento al final del documento (2). La parte de la instrucción en la que concretamente se expresan ambos posicionamientos es la siguiente (en negrita):

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

De forma análoga, las instrucciones alternativas basadas en OOo Basic, también forman parte de la sintaxis de escritura, y también se repite la diferencia inicio-fin por similitud con la macro de escritura en una de las dos formulaciones disponibles (para la instrucción de escritura de texto en OOo Basic ver aquí):

  • Al inicio: oText.insertString(oText.getStart(),sTexto & Chr(13), False)
  • Al final: oText.insertString(oText.getEnd(),sTexto & Chr(13), False)
Este sencillo script escribre al inicio del documento una palabra y otra al final (3):

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 instrucciones, 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 según nuestras necesidades (4).

Por el resultado previsible del uso de estas dos opciones (funciones en realidad), ambas son útiles cuando componemos el texto sobre un documento en blanco. Por esto mismo no se ajustan al trabajo con documentos-modelo (como el de informe, por ejemplo). En este caso, aunque podemos optar por desplazamientos calculados previamente, basados en macros de desplazamiento, el hecho es que esta opción resulta muy costosa y, por lo mismo, está sujeta a errores. 

El uso de tablas para facilitar el posicionamiento y el desplazamiento (5), resuelve ambos (posicionamiento y desplazamiento), tanto para el uso "manual" del documento como para automatizar su escritura, pero contamos con una alternativa mejor: los marcadores o BookMark (6).

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 procede a escribir texto en la posición que ocupa 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 este caso) 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 (7). 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 (8). 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, cunando é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 (9).


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)) (10).
  • 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 (11). 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 (12)

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) 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.

(4) 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 (pramací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.

(5) Que es la alternativa más lógica cuando creamos un documento-modelo para cumplimentar desde teclado.

(6) El uso marcadores no es incompatible con el de 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 cuando usemos ese documento.

(7) 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.

(8) 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.

(9) 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().

(10) 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.

(11) 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.

(12) 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

Nota: solo los miembros de este blog pueden publicar comentarios.