Mientras sigo trabajando en la próxima versión de NRtfTree y en la puesta al día de JRtfTree, hoy os hago llegar una aportación de Grijandermore, que ha traducido la librería NRtfTree al lenguaje Visual Basic .Net (vbNRtfTree).
Aunque esta traducción no era técnicamente necesaria dado que NRtfTree puede ser utilizada directamente desde cualquier lenguaje .Net (siempre bajo el .Net Framework 2.0 o superior), sí que puede ser interesante para aquellos que queráis modificarla o simplemente consultar su funcionamiento y prefiráis o tengáis más soltura con VB.Net que con C#.
Se trata de una traducción completa de la versión 0.3.xxxx.1 de NRtfTree. Es una versión no oficial de la librería, por lo que no ha sido revisada al 100%, pero sí puedo decir que pasa todas las pruebas unitarias de la última versión de NRtfTree.
Además, un aporte añadido es que Grijandermore además de traducir a VB.Net el código de la librería, también ha realizado una traducción al inglés de todos los comentarios y documentación, por lo que puede ser de gran ayuda a todos los usuarios y colaboradores del proyecto no conocedores del castellano.
En definitiva, un recurso más a la mano de todos los interesados en NRtfTree.
Podéis acceder a la página de traducciones no oficiales de NRtfTree para descargar los fuentes de esta versión.

Mientras sigo trabajando en la próxima versión de NRtfTree y en la puesta al día de JRtfTree, hoy os hago llegar una aportación de Terence L. Wallace (Arkitech EBC Corporation), que ha traducido la librería NRtfTree al lenguaje Visual Basic .Net (vbNRtfTree).

Aunque esta traducción no era técnicamente necesaria dado que NRtfTree puede ser utilizada directamente desde cualquier lenguaje .Net (siempre bajo el .Net Framework 2.0 o superior), sí que puede ser interesante para aquellos que queráis modificarla o simplemente consultar su funcionamiento y prefiráis o tengáis más soltura con VB.Net que con C#.

Se trata de una traducción completa de la versión 0.3.3503 de NRtfTree. Es una versión no oficial de la librería, por lo que no tendrá soporte por mi parte ni ha sido revisada al 100%, aunque sí puedo decir que pasa todas las pruebas unitarias de la última versión de NRtfTree.

Además, un aporte añadido es que Terence, además de traducir a VB.Net el código de la librería, también ha realizado una traducción al inglés de todos los comentarios y documentación, por lo que puede ser de gran ayuda a todos los usuarios y colaboradores del proyecto no conocedores del castellano.

En definitiva, un recurso más a la mano de todos los interesados en NRtfTree.

Podéis acceder a la página de versiones no oficiales de NRtfTree para descargar los fuentes de esta versión.

, , , , , , ,

Esta entrada forma parte de una serie de artículos dedicados a NRtfTree, la librería .NET para tratamiento de documentos RTF, entre los cuales podrás encontrar una descripción detallada de la librería, documentación técnica, ejemplos y tutoriales de uso que pueden ser de tu interés. No olvides consultar la página principal de NRtfTree para más información.

NRtfTree es una librería .NET escrita íntegramente en C# y dirigida a facilitar el tratamiento estructurado de documentos con formato RTF. NRtfTree se distribuye bajo licencia LGPL.

El formato RTF (Rich Text Format) permite la creación de texto enriquecido independiente de la plataforma en el que pueden insertarse gran cantidad de elementos extra como imágenes, tablas, listas, hipervínculos y otras muchas características propias de cualquier procesador de texto actual.

En el apartado anterior vimos cómo la estructura de un documento RTF resulta en principio un tanto encríptica y a priori nada fácil de manipular de forma automática. Aunque en el fondo esto no es así, si que es cierto que el desarrollo de analizadores a medida para este tipo de documentos es una tarea relativamente complicada o al menos laboriosa, y es en este aspecto donde NRtfTree puede resultar de gran ayuda.

Además, otro aspecto importante a considerar es que RTF es el formato utilizado internamente por el control RichTextBox proporcionado por Microsoft en la plataforma .NET. Este control permite incluir con suma facilidad en nuestras aplicaciones controles de edición de texto enriquecido. Sin embargo, no se proporcionan demasiadas facilidades a la hora de manipular el contenido RTF del control.

Las dos situaciones habituales cuando utilizamos el control RichTextBox serán:

  • Trasladar toda la información contenida en el mismo, formato incluido, a otro formato distinto.
  • Analizar la información para tomar decisiones como parte de la lógica de nuestra aplicación.

En ninguna de estas dos situaciones nos podrá ayudar demasiado la funcionalidad original del control y por tanto necesitamos de otros medios para afrontarlas.

En este punto es donde puede entenderse la utilidad de la librería que presentamos. NRtfTree permite analizar de forma automática un documento RTF y convertirlo a una estructura de datos interna manejable e intuitiva a través de la cual poder analizar y/o modificar el documento de una forma sencilla y eficaz sin tener que conocer a fondo los detalles del formato tratado.

La solución aportada por NRtfTree es muy similar a los modelos DOM y SAX utilizados con otros formatos como XML. Ambos modelos serán comentados detalladamente en el siguiente apartado de la documentación.

, , ,

Para los que como yo aún creen en el desarrollo de aplicaciones de escritorio con java, estos últimos días han aparecido algunas novedades al respecto.

Hoy voy a destacar las nuevas versiones de tres proyectos que creo interesantes a la hora de mejorar el aspecto y funcionalidad de nuestras aplicaciones.

En primer lugar Flamingo, que estrena su versión 4.0. Para quienes no conozcan este proyecto, Flamingo es una implementación Swing del control Ribbon (y sus controles asociados) incluido en las aplicaciones de Office 2007.  Podéis consultar una lista con las novedades de la nueva versión en este artículo del blog Pushing Pixels. En la siguiente imagen podéis ver el aspecto del control:

Control Ribbon Flamingo

Control Ribbon Flamingo

Una noticia algo más antigua pero que viene muy en relación a esta última es la nueva versión 0.9.5 de la librería de controles SwingX. Este proyecto no es más que una extensión del propio Swing, donde se pretenden mejorar muchos de los controles existentes e incluir algunos controles nuevos. Así, por ejemplo, SwingX añade funciones avanzadas de ordenación, filtrado, selección y resaltado a controles basicos de Swing como tablas (JTable), árboles (JTree) y listas (JList), añade nuevas funcionalidades a otros controles como cuadros de diálogo (JDialog) o paneles (JPanel), e incluye algunos controles nuevos no presentes en Swing como JXTreeTable, JXLoginPanel o JXDatePicker para la presentación de tablas jerarquizadas, controles de autenticación o cuadros de selección de fecha. Podéis encontrar más información en la web de SwingLabs y en su wiki. Un ejemplo del control JXTreeTable puede verse en la siguiente imagen:

Ejemplo JXTreeTable

Ejemplo JXTreeTable

Por último, el proyecto Substance publica su nueva versión 5.1 en la que mejora su rendimiento y retoca algunos de sus skins, entre otras mejoras. Substance nos proporciona uno de los más atractivos look&feel para aplicaciones Swing que existen actualmente, y aunque históricamente ha tenido algunos problemas de rendimiento parece que en la últimas revisiones se está mejorando bastante en ese sentido. Además, Substance tiene el valor añadido de incluir soporte para los otros dos proyectos comentados en esta entrada, Flamingo y SwingX, lo que significa que si utilizamos Substance junto a controles de estas librerías podremos estar seguros de que su aspecto será totalmente consistente con el resto de la interfaz de usuario. Podéis encontrar una lista con las últimas novedades en el blog Pushing Pixels. En la siguiente imagen podéis comprobar el aspecto de Flamingo bajo el L&F Substance utilizando el skin Office Silver (la primera imagen de esta entrada se correspondía con el skin Office Blue):

Flamingo bajo Substance Office Silver

Flamingo bajo Substance Office Silver

En cuanto a los requerimientos de los componentes mencionados cabe mencionar que Flamingo y Substance necesitan Java 6 o superior para su correcto funcionamiento mientras que a SwingX le basta Java 5.

, , , , ,

Librería CRtfTree

La librería CRtfTree es una traducción al lenguaje C++ de la librería NRtfTree.

Esta versión ha sido realizada y cedida amablemente por Nicolás Alonso. Se encuentra actualmente en una etapa muy temprana de su desarrollo aunque, por lo que he podido ver, las clases base ya están implementadas, de forma que ya puede resultar útil su uso.

Si en algún momento está disponible una versión más completa de CRtfTree la publicaré puntualmente en esta misma página.

En el fichero zip disponible para la descarga se proporciona el código fuente íntegro del proyecto escrito bajo Visual C++ 2003.

Descarga Tamaño Descripción
CRtfTree 491 Kb Fuentes completos de la librería CRtfTree (versión 13/01/2007).

Nota Importante:

Como he indicado anteriormente, esta versión de la librería no es propia por lo que no ofrezco soporte personalmente ni asumo ningún tipo de responsabilidad sobre la misma.

, , , ,

Descargas JRtfTree

En esta sección se mostrarán todas las descargas disponibles para el proyecto JRtfTree:

Versión v0.1.1 beta [143 Kb]

Fuentes completos de la librería JRtfTree v0.1.1b, fichero JAR, y documentación javadoc.

, , , ,

Librería JRtfTree

Índice de secciones

  1. Introducción
  2. Descargas

Enlaces relacionados

Introducción a JRtfTree

JRtfTree es una traducción al lenguaje Java de mi proyecto NRtfTree (C#). JRtfTree se distribuye bajo licencia GPL.

Se trata de una librería dirigida a facilitar el tratamiento estructurado de documentos con formato RTF.

El formato RTF (Rich Text Format) permite la creación de texto enriquecido en el que pueden insertarse una gran cantidad de elementos extra como imágenes, tablas, listas, hipervínculos y otras muchas características propias de cualquier procesador de texto actual.

La API ofrecida por JRtfTree permite de forma sencilla el análisis, lectura y modificación de documentos RTF, pudiendo utilizar para ello dos modelos distintos de ejecución análogos a los modelos SAX y DOM aplicados al formato XML.

Bajo el primero de los modelos, estilo DOM, JRtfTree permite analizar de forma automática un documento RTF tras convertirlo a una estructura de datos interna en forma de árbol, manejable e intuitiva, a través de la cual poder analizar y modificar el documento de una forma sencilla y eficaz sin tener que conocer a fondo los detalles del formato RTF.

Con el segundo de los modos de ejecución, estilo SAX, el documento RTF podrá analizarse de forma secuencial bajo un modelo basado en eventos, donde sólo habrá que implementar las actuaciones necesarias para tratar cada uno de los elementos leidos del documento RTF que sean relevantes para nuestra aplicación.

Mientras completo la sección de JRtfTree con más información específica para Java, puede conseguir más información sobre el proyecto visitando la página de NRtfTree, versión original de la librería en C#, que contiene información adicional y ejemplos que pueden ser aplicados muy fácilmente a la versión Java.

La versión disponible actualmente de JRtfTree (v0.1.1b) se encuentra aún en fase beta y abarca toda la funcionalidad de las clases base de la versión 0.2 de NRtfTree. Las clases de nodos especiales (imagen, objeto y grupo de información) se publicarán más adelante una vez estabilizada la versión actual.

, , , , ,

Descargas NRtfTree

Esta entrada forma parte de una serie de artículos dedicados a NRtfTree, la librería .NET para tratamiento de documentos RTF, entre los cuales podrás encontrar una descripción detallada de la librería, documentación técnica, ejemplos y tutoriales de uso que pueden ser de tu interés. No olvides consultar la página principal de NRtfTree para más información.

En esta sección se mostrarán todas las descargas disponibles para el proyecto NRtfTree:

Última Versión: 0.3 Final (0.3.3503)

Fuentes NRtfTree 0.3 Final (0.3.3503) [1.29 Mb]

Esta descarga incluye:

  • Fuentes completos de la librería NRtfTree v0.3.0 beta 2.
  • Fuentes completos de las aplicaciones de ejemplo de NRtfTree.
  • Documentación API de la librería NRtfTree en formato CHM y HTML.

Ejecutables NRtfTree v0.3 Final (0.3.3503) [131 Kb]

Esta descarga incluye:

  • Librería NRtfTree compilada.
  • Ejecutables de las aplicaciones de ejemplo.
  • Documentos de ejemplo.

Documentacion API NRtfTree 0.3 Formato CHM [300 Kb]

Esta descarga incluye:

  • Documentación de la librería NRtfTree 0.3 en formato CHM.

Documentacion API NRtfTree 0.3 Formato HTML [658 Kb]

Esta descarga incluye:

  • Documentación de la librería NRtfTree 0.3 en formato HTML.

Aunque no recomendable, también es posible descargar versiones anteriores de la librería:

Versión 0.3.0 beta 2

Fuentes NRtfTree 0.3.0 beta 2 [1.07 Mb]

Esta descarga incluye:

  • Fuentes completos de la librería NRtfTree v0.3.0 beta 2.
  • Fuentes completos de las aplicaciones de ejemplo de NRtfTree.
  • Documentación de la librería NRtfTree en formato CHM y HTML.

Ejecutables NRtfTree v0.3.0 beta 2 [243 Kb]

Esta descarga incluye:

  • Librería NRtfTree compilada.
  • Ejecutables de las aplicaciones de ejemplo.
  • Documentos de ejemplo.

Versión 0.3.0 beta 1

Librería NRtfTree v0.3.0 beta 1 [63 Kb]

Fuentes completos de la librería NRtfTree v0.3.0 beta 1 y de la aplicación de ejemplo.

Aplicación de ejemplo NRtfTree v0.3.0 beta 1 [548 Kb]

Ejecutables de la librería, aplicación de ejemplo y documentación HTML. Se incluyen tres documentos de ejemplo para utilizar con la demostración.


Versión 0.2.1

Librería NRtfTree v0.2.1 [45 Kb]

Fuentes completos de la librería NRtfTree v0.2.1 y de la aplicación de ejemplo.

Aplicación de ejemplo NRtfTree v0.2.1 [195 Kb]

Ejecutables de la librería y  la aplicación de ejemplo. Se incluyen tres documentos de ejemplo para utilizar con la demostración.

Documentación NRtfTree v0.2.1 [100 Kb]

Documentación en formato CHM de la librería [en español].


, , , ,

Esta entrada forma parte de una serie de artículos dedicados a NRtfTree, la librería .NET para tratamiento de documentos RTF, entre los cuales podrás encontrar una descripción detallada de la librería, documentación técnica, ejemplos y tutoriales de uso que pueden ser de tu interés. No olvides consultar la página principal de NRtfTree para más información.

En este segundo tutorial nos vamos a centrar en el tratamiento de documentos RTF utilizando la segunda variante de la librería NRtfTree, es decir, realizando un tratamiento basado en eventos al estilo SAX.

Para ilustrar esto pretendemos construir un pequeño conversor de RTF a algo parecido a un XML donde se marquen mediante etiquetas los fragmentos de texto con alguno de los tres formatos más básicos que conocemos: negrita, cursiva y subrayado. Obviamente este conversor no es nada útil pero pienso que su sencillez puede aclarar algunas dudas sobre la forma de proceder con este segundo módulo de la librería.

Como ejemplo práctico de lo que deberá hacer el conversor tenemos lo siguiente, dado el texto:

NRtfTree es una librería escrita íntegramente en C# para el tratamiento estructurado de documentos RTF.

debería convertirse en lo siguiente:

<documento>NRtfTree es una librería <u>escrita íntegramente en C#</u> para el <i>tratamiento estructurado</i> de <b>documentos RTF</b>.</documento>

Tratamiento SAX: Clases RtfReader y SARParser

El esquema de trabajo con estas dos clases será el siguiente:

  1. Se creará un clase heredada de SARParser donde se redefinirán todos los eventos de ésta última.
  2. Se creará un objeto RtfReader asociado a la clase anterior.
  3. Se cargará el documento RTF mediante el método LoadRtfFile() proporcionado por el objeto RtfReader.
  4. Se iniciará la lectura del documento mediante el método Parse() proporcionado por el objeto RtfReader.

Este último paso tendrá como efecto inmediato que se comience a llamar automáticamete a los métodos redefinidos del objeto heredado de SARParser a medida que se va leyendo el documento RTF. De esta forma, cada vez que se lea una palabra clave se llamará automáticamente al método RtfKeyword(), cada vez que se lea un fragmento de texto se llamará automáticamente al método RtfText() y de forma análoga para el resto de eventos.

El listado completo de eventos es el siguiente:

StartRtfDocument() Se ha comenzado a leer el documento RTF.
EndRtfDocument() Se ha terminado de leer el documento RTF.
StartRtfGroup() Se ha leido un comienzo de grupo RTF ( caracter ‘{‘ )
EndRtfGroup() Se ha leido un final de grupo RTF ( caracter ‘}’ )
RtfKeyword(key,hasParam,param) Se ha leido una palabra clave. Se reciben como parámetros la palabra clave, key, un indicador para saber si dicha palabra clave tiene algún parámetro, hasParam, y el parámetro en caso de existir, param.
RtfControl(key,hasParam,param) Se ha leido un símbolo de control. Se reciben como parámetros el símbolo de control, key, un indicador para saber si dicho símbolo de control tiene algún parámetro, hasParam, y el parámetro en caso de existir, param.
RtfText(text) Se ha leido un fragmento de texto. Se recibe como parámetro el texto leido, text. Se debe tener en cuenta que no sólo se considerará texto al texto real del documento, sino también al contenido por ejemplo en los llamados “destinations” en la especificación RTF.

Entendida esta forma de funcionamiento, queda claro que todas las acciones necesarias para nuestro conversor deberán implementarse dentro de la clase que heredemos de SARParser y distribuidas convenientemente entre los distintos eventos disponibles. Por tanto, pasemos directamente a los detalles de implementación de esta clase.

En primer lugar, nos fijaremos en las etiquetas <documento></documento> que debemos colocar al principio y final de nuestro fichero resultante. Estás etiquetas deben aparecer tan sólo una vez y en los extremos del documento por lo que el sitio ideal para escribirlas será dentro de los eventos StartDocument() y EndDocument(). Veamos cómo:

public class MiParser : SARParser
{
    public override void StartRtfDocument()
    {
        doc += "<documento>\r\n";
    }

    public override void EndRtfDocument()
    {
        doc += "\r\n</documento>";
    }

    [...]
}

Los estilos de formato nos vendrán indicados en el documento RTF mediante palabras clave, por lo que para detectar los cambios de formato que vamos a tener en cuenta para nuestro conversor y poder escribir las etiquetas correspondientes al documento de salida tendremos que redefinir el evento RtfKeyword() y realizar dentro de éste las acciones necesarias. Para llevar el control del formato actual definiremos tres variables booleanas que indiquen si se han iniciado los estilos negrita, cursiva o subrayado.

Por otro lado, hay que recordar que normalmente en un documento RTF existen muchos fragmentos de texto que realmente no aparecen en la representación final del RTF (por ejemplo en las palabras clave consideradas “destination”, para más detalles se puede consultar la especificación RTF). Estos fragmentos de texto no los queremos tener en cuenta en ningún sentido, por lo que deberemos llevar el control de cuándo comienza el texto real del documento, tras la primera palabra clave “\pard”. Para esto definiremos otra variable booleana desactivada por defecto, que sólo activaremos al encontrar el comienzo real del texto.

En caso de detectarse alguna de las palabras clave consideradas (\b, \i, \ul), se activará o desactivará su variable asociada según el valor del parámetro asociado y se escribirá al documento de salida la etiqueta correspondiente.

private bool enTexto        = false;
private bool negrita        = false;

private bool cursiva        = false;
private bool subrayado        = false;

[...]

public override void RtfKeyword(string key, bool hasParam, int param)
{
    if(key.Equals("pard"))
        enTexto = true;

    if(enTexto)
    {
        switch(key)
        {
            case "b":
                if(!hasParam || (hasParam && param == 1))
                {
                    doc += "<b>";
                    negrita = true;
                }
                else
                {
                    doc += "</b>";
                    negrita = false;
                }
                break;
            case "i":
                if(!hasParam || (hasParam && param == 1))
                {
                    doc += "<i>";
                    cursiva = true;
                }
                else
                {
                    doc += "</i>";
                    cursiva = false;
                }
                break;
            case "ul":
                doc += "<u>";
                subrayado = true;
                break;
            case "ulnone":
                doc += "</u>";
                subrayado = false;
                break;
        }
    }
}

En cuanto a los inicios y finales de grupo tan sólo tendremos que tener en cuenta éstos últimos. Esto es debido a que en RTF no hay por qué “cerrar” las etiquetas abiertas anteriormente como en HTML, es decir, que una etiuqeta \b no tiene por qué ir seguida de una \b0 para terminar de escribir en negrita, sino que la primera puede ir encerrada dentro de un grupo RTF y el formato definido dentro de un grupo sólo se aplica dentro de éste, independientemente del formato definido fuera de él. Por tanto, cada vez que detectmenos un final de grupo deberemos cerrar todas las etiquetas de nuestro documento de salida que estuvieran abiertas con anterioridad. Veamos el código:

public override void EndRtfGroup()
{
    if(enTexto)
    {
        if(negrita)
            doc += "</b>";

        if(cursiva)
            doc += "</i>";

        if(subrayado)
            doc += "</u>";

        negrita = false;
        cursiva = false;
        subrayado = false;
    }
}

Por último, sólo nos quedan los símbolos de control y los fragmentos de texto. Para nuestro conversor trataremos ambos igual dado que los únicos símbolos de control que vamos a tener en cuenta son los destinados a codificar caracteres especiales (en nuestro caso las vocales acentuadas y las ‘ñ’). Para ambos, lo único que haremos será escribir el fragmento de texto o el caracter especial al documento de salida, con la única diferencia de que para los caracteres especiales tendremos que hacer la conversión previa entre el valor del parámetro asociado al símbolo de control y el caracter en castellano:

public override void RtfControl(string key, bool hasParam, int param)
{
    if(key == "'") //Caracter especial
    {
        doc += translateAnsiCode(param);
    }
}

public override void RtfText(string text)
{
    if(enTexto)
    {
        doc += text;
    }
}

La implementación del método translateAnsiCode() es sumamente sencilla y puede consultarse en el código fuente proporcionado con la librería y su aplicación de demostración.

Vista la implementación de nuestra clase heredada de SARParser, ya sólo nos queda ver cómo cargar el documento RTF y lanzar la lectura del mismo para que se ejecuten las acciones definidas en los eventos de SARParser.

public string traducir()
{
    string res = "";

    //Construimos nuestro parser
    MiParser parser = new MiParser(res);

    //Construimos el RTFReader que tratará el documento a través del SARParser creado anteriormente.
    reader = new RtfReader(parser);

    //Cargamos el fichero RTF
    reader.LoadRtfFile(rutaRTF);

    //Comenzamos el análisis del documento
    reader.Parse();

    return parser.doc;
}

El método anterior se limita a seguir los cuatro pasos indicados al principio de este texto: crear un objeto de nuestra clase heredada, crear un objeto RtfReader asociándole el objeto anterior (se le pasa como parámetro al constructor), cargar el documento RTF mediante el método LoadRtfFile() y por último iniciar la lectura del documento mediante el método Parse().

El código fuente mostrado en este tutorial se proporciona como parte de la aplicación de ejemplo distribuida con la librería NRtfTree, la cual puede obtenerse desde la página de descargas.

Esta entrada forma parte de una serie de artículos dedicados a NRtfTree, la librería .NET para tratamiento de documentos RTF, entre los cuales podrás encontrar una descripción detallada de la librería, documentación técnica, ejemplos y tutoriales de uso que pueden ser de tu interés. No olvides consultar la página principal de NRtfTree para más información.

, , , , ,

Esta entrada forma parte de una serie de artículos dedicados a NRtfTree, la librería .NET para tratamiento de documentos RTF, entre los cuales podrás encontrar una descripción detallada de la librería, documentación técnica, ejemplos y tutoriales de uso que pueden ser de tu interés. No olvides consultar la página principal de NRtfTree para más información.

En este tutorial vamos a construir desde cero el pequeño conversor de RTF a HTML que se proporciona junto a la librería NRtfTree utilizando para ello la primera variante de la librería, es decir, utilizando un tratamiento al estilo DOM para parsear y recorrer el documento.

La conversión del documento RTF no será exhaustiva, sino que nuestro conversor se limitará a tradudir tan sólo algunas características de formato frecuentes:

  • Tipo de fuente.
  • Tamaño de fuente.
  • Color de fuente.
  • Estilo de fuente: negrita, cursiva y subrayado.

Tratamiento DOM: Clases RtfTree y RtfTreeNode

Esta variante quizá sea la más sencilla de utilizar para realizar fácilmente muchas tareas sobre un documento RTF. Esto se debe a que el documento se carga completo al principio de la ejecución y por tanto lo tenemos disponible en cualquier momento y lo podemos consultar tantas veces como se necesite haciendo uso de los métodos de navegación del árbol RTF construido. Sin embargo, hay circunstancias en las que esta aproximación al problema puede resultarnos poco viable en la práctica debido precisamente al hecho de cargar el documento completo en memoria, junto a todas las estructuras auxiliares necesarias para construir el árbol RTF, lo que dependiendo del tamaño del documento a procesar, puede causarnos problemas de espacio y eficiencia.

Nuestro conversor implementado con esta variante seguirá el siguiente esquema general de funcionamiento:

  1. Definirá e inicializará todas las estructuras necesarias y cargará el documento RTF.
  2. Obtendrá las tablas de colores y fuentes para poder consultarlas durante el resto de la ejecución.
  3. Buscará el principio del texto del documento, obviando así el resto de los datos de la cabecera.
  4. Recorrerá el árbol de forma recursiva a partir de ese nodo e irá traduciendo el texto encontrado a formato HTML. Para ello, deberá ir manteniedo y actualizando en todo momento el formato a aplicar al texto leido del documento RTF.

Para almacenar el formato de un fragmento de texto definiremos una clase auxiliar que encapsule los 6 parámetros de formato que tendrá en cuenta nuestro conversor:

public class EstadoRtf
{
    public bool negrita;        //Fuente negrita

    public bool cursiva;        //Fuente cursiva
    public bool subrayado;      //Fuente subrayada

    public string fuente;        //Tipo de fuente
    public int tamFuente;        //Tamaño de fuente
    public Color color;          //Color de fuente

    public EstadoRtf(string fue, int tam, Color col, bool neg, bool cur, bool sub)
    {
        fuente = fue;
        tamFuente = tam;
        color = col;

        negrita   = neg;
        cursiva   = cur;
        subrayado = sub;
    }
}

Por otro lado, a nuestra clase principal que contedrá los métodos para realizar la conversión la llamaremos TraductorRTF y contedrá los siguientes atributos privados:

public class TraductorRtf
{
    private RtfTree     tree;          //Analizador del documento RTF

    private string[]    tFuentes;      //Tabla de fuentes  (array de string)

    private Color[]     tColores;      //Tabla de colores  (array de Color)

    private ArrayList   estados;       //Pila de estados del documento (array de EstadoRtf)
    private EstadoRtf   estadoActual;  //Estado formato RTF actual

    [...]
}

El primero de los atributos (tree) corresponde al árbol RTF que construirá la librería al cargar el documento RTF. Los dos atributos siguientes se utilizarán para almacenar las tablas de fuentes (tFuentes) y colores (tColores) del documento. Por último, los dos atributos de estado se utilizarán para almacenar la pila de formatos (estados) por la que se va pasando durante el recorrido del árbol (necesaria debido al tratamiento recursivo que se realizará para hacer la traducción) y el formato actual (estadoActual) a medida que se traduce. Más tarde comprenderemos mejor la necesidad de estas dos variables.

Por su parte, el constructor de esta clase no tendrá ninguna dificultad, limitándose a inicializar la pila de estados y a cargar el documento para construir el árbol RTF:

public TraductorRtf(string rutaRTF)
{
    //Inicialización de la pila de estados (formatos RTF)

    estados = new ArrayList();

    //Carga del documento RTF
    tree = new RtfTree();
    tree.LoadRtfFile(rutaRTF);
}

Como puede observarse, para esta primera implementación del conversor utilizaremos la clase RtfTree para almacenar el documento. El método que utilizamos para cargarlo será LoadRtfFile(), al que le pasamos como parámetro la ruta del documento. Este método se encargará de parsear y construir internamente el árbol RTF que representará al documento cargado.

Una vez inicializadas las estruturas internas necesarias para realizar la conversión y cargado el documento RTF es el momento de iniciar el proceso de conversión. Para ello definiremos un primer método general denominado traducir() que realizará algunas acciones iniciales:

  1. Obtener la tabla de fuentes del documento.
  2. Obtener la tabla de colores del documento.
  3. Iniciar la traducción.

Veamos en primer lugar lo sencillo que resulta la implementación de este método utilizando los métodos proporcionados por la clase RtfTree:

public string traducir()
{
    string res = "";

    //Se extrae la tabla de fuentes del documento
    tFuentes = tree.GetFontTable();

    //Se extrae la tabla de colores del documento
    tColores = tree.GetColorTable();

    //Se lanza el proceso de traducción del documento a formato HTML

    res = traducirTexto();

    //Se devuelve el resultado del proceso
    return res;
}

Como se observa en la implementación, la obtención de las tablas de fuentes y colores del documento RTF se reduce a llamar a los métodos GetFontTable() y GetColorTable(), respectivamente. El primero de los métodos devolverá un array de cadenas de caracteres con los nombres de todas las fuentes contenidas en la cabecera del documento y, obviamente, en el mismo orden. Por su parte, el metodo para obtener la tabla de colores devolverá un array de elementos de tipo Color (que contiene un color definido en formato RGB) con todos los colores definidos en la cabecera del documento. Por último, se llamará al método traducirTexto() para comenzar la verdadera traducción del texto.

La implementación de este último método será la siguiente:

public string traducirTexto()
{
    //Cabecera del documento HTML resultante

    string res = "<html><head></head><body>";

    //Se establece el estado inicial por defecto
    estadoActual = new EstadoRtf((string)tFuentes[0],10,(Color)tColores[0],false,false,false);

    //Se aplica el formato inicial definido en 'estadoActual'

    inicioFormato();

    //Se busca el indice del primer nodo perteneciente al texto
    bool enc = false;        //Encontrado el comienzo del texto
    int i = 0;

    RtfTreeNode nodo = new RtfTreeNode();

    while(!enc && i < tree.RootNode.FirstChild.ChildNodes.Count)
    {
        nodo = tree.RootNode.FirstChild.ChildNodes[i];

        //El texto comenzará tras el primer token "pard"

        if(nodo.NodeKey == "pard")
        {
            enc = true;
        }

        i++;
    }

    //Se comenzará a traducir a partir del nodo en la posición 'primerNodoTexto'
    int primerNodoTexto = i - 1;

    //Inmersion
    res += traducirTexto(tree.RootNode.FirstChild, primerNodoTexto);

    //Se finaliza el estado inicial

    res += finFormato();

    //Finaliza el documento HTML
    res += "</body></html>";

    return res;
}

Este método comienza creando la cabecera del documento HTML que se devolverá como salida, que será siempre la misma. Posteriormente se almacena el estado de formato inicial con el que se comenzará a traducir el documento. Este estado inicial estará formado por el primer tipo de fuente almacenado en la tabla de fuentes, un tamaño arbitrario que en nuestro ejemplo hemos establecido a 10, el primer color de la tabla de colores del documento RTF, y todos los flags adicionales de estado desactivados (negrita, cursiva y subrayado).

En el documento HTML estableceremos los formatos mediante la etiqueta <font> y sus atributos face (nombre de la fuente), size (tamaño de la fuente) y color (color de la fuente). El resto de estilos se obtendrán utilizando las etiquetas <b> (fuente negrita), <i> (fuente cursiva) y <u> (fuente subrayada).

Para aplicar estos formatos a medida que vamos traduciendo el documento se definirá un método que escribirá todas las etiquetas HTML necesarias para comenzar el estilo definido por el atributo estadoActual y otro para finalizarlo. Por tanto, para ir cambiando el formato del HTML de salida tan sólo deberemos preocuparnos de ir actualizando el atributo estadoActual a medida que leemos el documento RTF y llamando a los métodos inicioFormato() y finFormato() en los lugares adecuados (normalmente al comienzo y final del documento y cada vez que encontremos un nodo de tipo TEXT, o lo que es lo mismo, cada vez que encontremos un fragmento de texto en el documento RTF y antes de escribirlo al HTML de salida). La implementación de estos dos métodos es muy sencilla y se mostrará más adelante.

Tras aplicar el formato inicial llamando a inicioFormato() el siguiente paso necesario será buscar el comienzo del texto del documento, desechando todo el contenido de la cabecera del RTF. Para ello haremos una búsqueda a través de todos los nodos de primer nivel del árbol RTF hasta encontrar el primer nodo cuya palabra clave sea “pard”. Para hacer esto implementamos una búsqueda secuencial sencilla sobre todos los nodos inmediatamente inferiores al nodo raíz del árbol.

Un detalle importante a tener en cuenta es que el nodo raíz real del árbol RTF no se corresponde con la propiedad RootNode de la clase RtfTree, sino con el primer nodo hijo de éste. La propieda RootNode no es más que un nodo ficticio (de tipo ROOT) utilizado únicamente como nodo de partida del árbol RTF real. Este nodo tendrá por tanto un único nodo hijo (raíz real del arbol RTF) de tipo GROUP que contendrá a su vez a todos los nodos de primer nivel del árbol, entre ellos todos los grupos de la cabecera del documento y el comienzo del texto del mismo. En la página de introducción a la librería NRtfTree se muestra una representación visual de un árbol RTF de ejemplo donde se pueden aclarar estos conceptos.

Los principales elementos implicados en la búsqueda son los siguientes:

RootNode     Nodo principal ficticio (tipo ROOT).
RootNode.FirstChild     Nodo raíz real del árbol RTF (tipo GROUP)
RootNode.FirstChild.ChildNodes     Nodos hijo del nodo raíz (array de objetos RtfTreeNode)
RootNode.FirstChild.ChildNodes.Count     Número de nodos hijo del nodo raíz.
RootNode.FirstChild.ChildNodes[i]     Hijo i-ésimo del nodo raíz.

Teniendo claro como acceder a todos estos elementos resulta relativamente sencillo implementar la búsqueda del primer nodo de tipo KEYWORD cuya palabra clave sea pard . El único cabo suelto hasta el momento es cómo obtener la palabra clave asociada a un nodo. Para esto no tendremos más que acceder a la propiedad NodeKey de un nodo RTF (clase RtfTreeNode).

La búsqueda anterior se ha implementado “a mano” para ilustrar la sencillez con la que puede recorrerse un árbol RTF utilizando los métodos y propiedades expuestas. Sin embargo, en la versión v0.2 de la librería se incluyen varios métodos de búsqueda en la clase RtfTreeNode que podían haber hecho aún más sencilla la búsqueda anterior (consultar en la documentación aportada con la librería el método SelectSingleChildNode()).

Una vez localizada la posición del primer nodo de texto se llamará al método extendido traducirTexto() pasándole esta vez un nuevo parámetro con la posición a partir de la cual comenzaremos a leer el texto a traducir.

Por último, tan sólo nos queda finalizar el formato inicial y escribir las etiquetas de cierre del documento HTML generado.

Y por fin llegamos al método que contiene el grueso de las acciones necesarias para la traducción. Este método recorrerá uno a uno todos los nodos del árbol a partir de la posición pasada como parámetro e irá realizando las actualizaciones necesarias de formato y la generación del documento HTML de salida. Para ello, durante el recorrido se realizará una primera discriminación de nodos por su tipo, identificando tres tipos de tareas principales a realizar:

  • Nodos de tipo KEYWORD –> Posible actualización del formato actual.
  • Nodos de tipo GROUP –> Almacenamiento del estado actual y establecimiento de un nuevo formato para el grupo.
  • Nodos de tipo TEXT y CONTROL –> Aplicación del formato actual al HTML y escritura del texto leido.

Expliquemos esto un poco mas. En primer lugar, el formato del documento RTF viene establecido por las diferentes palabras clave encargadas de ello. Sin embargo, como ya indicamos al comienzo de este texto, nuestro conversor no tendrá en cuenta todos las características de formato posibles en un documento RTF, sino tan sólo unas pocas de ellas. Por tanto, cada vez que nuestro conversor lea un nodo de tipo KEYWORD tendrá que obtener la palabra clave concreta contenida en él (propiedad NodeKey) y en caso de ser una de las elegidas realizar la actualización concreta del formato actual (atributo estadoActual). Los estilos sencillos (negrita, cursiva y subrayado) tan sólo implicarán la activación o desactivación de la propiedad equivalente en el atributo estadoActual, y los cambios de tipo o color de fuente harán necesaria la obtención de la fuente o color de la tabla correspondiente a partir del parámetro del nodo (propiedad Parameter). Esto queda implentado en el fragmento de código siguiente:

[...]
else if(nodo.NodeType == RTF_NODE_TYPE.KEYWORD)
{
    switch(nodo.NodeKey)
    {
        case "f":  //Tipo de fuente

            estadoActual.fuente = (string)tFuentes[nodo.Parameter];
            break;
        case "cf":  //Color de fuente
            estadoActual.color = (Color)tColores[nodo.Parameter];
            break;
        case "fs":    //Tamaño de fuente

            estadoActual.tamFuente = nodo.Parameter;
            break;
        case "b":    //Negrita
            if(!nodo.HasParameter || nodo.Parameter == 1)
                estadoActual.negrita = true;
            else

                estadoActual.negrita = false;
            break;
        case "i":    //Cursiva
            if(!nodo.HasParameter || nodo.Parameter == 1)
                estadoActual.cursiva = true;
            else

                estadoActual.cursiva = false;
            break;
        case "ul":    //Subrayado ON
            estadoActual.subrayado = true;
            break;
        case "ulnone":    //Subrayado OFF

            estadoActual.subrayado = false;
            break;
        case "par":    //Nuevo párrafo
            res += "<br>";
            break;
    }
}

[...]

En segundo lugar, si encontramos un nodo de tipo grupo las acciones a realizar serán las siguientes:

  1. Apilar el estado de formato actual (añadiendo el atributo estadoActual al array estados).
  2. Crear un nuevo estado actual por defecto.
  3. Traducir y formatear el texto contenido en el grupo, llamando recursivamente a traducirTexto() con la posición actual.
  4. Restaurar el estado anterior apilado.

Esto es necesario porque al texto contenido en un grupo RTF se le aplica únicamente el formato definido en ese mismo grupo, independietemente del formato del resto del documento. Sin embargo, al “salir” del grupo el formato debe seguir siendo el mismo que antes de “entrar” en él. Esto queda implementado de la siguiente manera:

if(nodo.NodeType == RTF_NODE_TYPE.GROUP)
{
    //Se apila el estado actual
    estados.Add(estadoActual);

    //Se crea un nueo estado inicial
    estadoActual = new EstadoRtf((string)tFuentes[0],10,(Color)tColores[0],false,false,false);    

    res += traducirTexto(nodo,0);

    //Se desapila el estado anterior

    estadoActual = (EstadoRtf)estados[estados.Count-1];
    estados.RemoveAt(estados.Count-1);
}

Por último, quedan por tratar los nodos de tipo TEXT o CONTROL. Dado que los únicos símbolos de control que vamos a tener en cuenta son los encargados de representar a caracteres especiales (en nuestro caso las vocales acentuadas y la letra ‘ñ’), que en definitiva siguen siendo texto, las acciones a realizar para ambos serán análogas, con la única diferencia de que para los nodos de tipo palabra clave el texto se obtendrá de la propiedad NodeKey y para los símbolos de control se obtendrá de la propiedad Parameter.

else if(nodo.NodeType == RTF_NODE_TYPE.CONTROL)
{
    if(nodo.NodeKey == "'")
    {
        res += inicioFormato();
        res += (char)nodo.Parameter;
        res += finFormato();
    }
}
else if(nodo.NodeType == RTF_NODE_TYPE.TEXT)
{
    res += inicioFormato();
    res += nodo.NodeKey;
    res += finFormato();
}

Los dos métodos utilizados para la aplicación y finalización del formato actual son los siguientes:

public string inicioFormato()
{
    string res = "";

    //Fuente (tipo, tamaño y color)

    res += "<font face='" + estadoActual.fuente + "' size='" + estadoActual.tamFuente/8 +
           "' color='" + toHTMLColor(estadoActual.color) + "'>";

    //Negrita, Cursiva, Subrayado
    if(estadoActual.negrita)
        res += "<b>";

    if(estadoActual.cursiva)
        res += "<i>";

    if(estadoActual.subrayado)
        res += "<u>";

    return res;
}

public string finFormato()
{
    string res = "";

    //Negrita, Cursiva, Subrayado
    if(estadoActual.negrita)
        res += "</b>";

    if(estadoActual.cursiva)
        res += "</i>";

    if(estadoActual.subrayado)
        res += "</u>";

    //Fuente

    res += "</font>";

    return res;
}

Como se puede observar la implementación es sumamente sencilla, limitándose a escribir en el HTML las etiquetas necesarias para reflejar el formato almacenado en el atributo estadoActual.

Y con esto finalizaría la implementación de nuestro conversor de RTF a HTML y ya sólo quedaría probarlo construyendo una pequeña aplicación de ejmplo como la distribuida con la librería. La llamada al conversor se haría como sigue:

TraductorRtf trad = new TraductorRtf("c:midocumento.rtf");

string documentoHTML = trad.traducir();

El código fuente mostrado en este tutorial se proporciona como parte de la aplicación de ejemplo distribuida con la librería NRtfTree, la cual puede obtenerse desde la página de descargas.

Esta entrada forma parte de una serie de artículos dedicados a NRtfTree, la librería .NET para tratamiento de documentos RTF, entre los cuales podrás encontrar una descripción detallada de la librería, documentación técnica, ejemplos y tutoriales de uso que pueden ser de tu interés. No olvides consultar la página principal de NRtfTree para más información.

, , , , ,

RTF y la librería NRtfTree

Esta entrada forma parte de una serie de artículos dedicados a NRtfTree, la librería .NET para tratamiento de documentos RTF, entre los cuales podrás encontrar una descripción detallada de la librería, documentación técnica, ejemplos y tutoriales de uso que pueden ser de tu interés. No olvides consultar la página principal de NRtfTree para más información.

El formato RTF (Rich Text Format) nace como un método de codificación de texto con formato e imágenes fácil de transferir entre distintos dispositivos, sistemas y aplicaciones. Con la especificación de este formato se pretende que un documento creado en un sistema determinado pueda consultarse y manipularse en cualquier otro contexto diferente sin las barreras impuestas por otros formatos propietarios.

Esta capacidad surge del hecho de que internamente un documento RTF se almacena como texto plano, en el que se incluye tanto la información del documento en sí como una serie de metadatos que informan del formato del documento y del resto de propiedades adicionales aportadas por RTF.

Un documento RTF se compone de cuatro elementos básicos: texto sin formato, palabras de control (también llamadas palabras clave), símbolos de control y grupos.

Una palabra de control es un comando RTF que se utilizará para dar información acerca del formato del documento o cualquier otro tipo de información adicional sobre el texto. Las palabras de control tienen la siguiente forma:

\PalabraControl<Delimitador>

donde PalabraControl puede ser cualquier combinación de letras en minúscula y <Delimitador> puede ser uno de los siguientes:

  1. Un espacio en blanco. En este caso éste se considerará parte de la palabra de control, es decir, no formará parte del texto del documento.
  2. Un dígito o un guión (“-”). Esto significará que existe un parámetro numérico que acompaña a la palabra de control. Este parámetro terminará al encontrarse un espacio o cualquier caracter no alfanumérico.
  3. Cualquier caracter no alfanumérco. En este caso este caracter no se considerará parte de la palabra de control.

Un símbolo de control se utilizará, entre otras cosas, para representar determinados caracteres especiales y tendrá la siguiente forma:

\SimboloControl<Parametro>

donde SimboloControl puede ser cualquier caracter no alfanumérico y <Parametro> (opcional) será cualquier valor alfanumérico. Además, es importante remarcar que los símbolos de control no van separados por ningún tipo de delimitador. Un ejemplo de símbolo de control puede ser \~ que representa un espacio indivisible.

Un grupo consiste en un conjunto de texto, palabras y símbolos de control encerrados entre llaves (‘{‘ y ‘}’) de forma que todo el texto del documento contenido en un grupo comparte las mismas propiedades. Además, existen grupos especiales que forman parte de la cabecera de un documento RTF y contienen información sobre las fuentes, colores y estilos, entre otros, contenidos en el documento.

Veamos un ejemplo de documento RTF donde aparecen todos los elementos comentados:

{\rtf1\ansi\ansicpg1252\deff0\deflang3082
{\fonttbl{\f0\fswiss\fcharset0 Arial;}{\f1\fmodern\fprq1\fcharset0 Courier New;}}
{\colortbl ;\red255\green0\blue0;\red0\green0\blue128;}
\viewkind4\uc1\pard\b\f0\fs20 NRTFTree v0.1\b0 es una librer\’eda \cf1\b\fs24 escrita en C# \cf0\b0\fs20 para el \cf2\i\fs24 tratamiento estructurado\fs20 \cf0\i0 de\f1\fs40 documentos RTF\f0\fs20 .\par}

El código RTF mostrado corresponde al texto con formato siguiente:

NRTFTree v0.1 es una librería escrita en C# para el tratamiento estructurado de documentos RTF.

La estructura general de un documento fuente RTF es la siguiente:

‘{‘ <Cabecera> <Contenido del documento> ‘}’

Por su parte, la cabecera del documento tendrá la siguiente estructura:

<Cabecera> –> \rtf <charset> \deff ? <fonttbl> <filetbl>? <colortbl>? <stylesheet>? <listtables>? <revtbl>?

con los siguientes significados:

<charset> : Tabla de caracteres utilizada en la codificación del documento.

\deff : Fuente por defecto.

<fonttbl>: Tabla de fuentes. Aquí se incluirán todas las fuentes utilizadas en el documento.

<filetbl>: Tabla de ficheros. Utilizada sólo cuando el documento incluye subdocumentos.

<colortbl>: Tabla de colores. Aquí se incluirán todos los colores de fuente utilizados en el documento.

<stylesheet>: Tabla de estilos. Aquí se incluirán los estilos utilizados en el documento.

<listtables>: Tablas de listas. Donde se incluye información sobre las listas, numeradas o no, contenidas en el documento.

<revtbl>: Tabla de revisiones. Que contendrá información sobre las modificaciones realizadas en el documento.

De todos estos elementos, tan sólo el conjunto de caracteres y la tabla de fuentes son obligatorios, y normalmente, tan sólo éstos junto con la tabla de colores serán los que aparezcan en la mayoría de documentos.

En el primer ejemplo podemos ver estas dos tablas habituales. Por un lado la tabla de fuentes donde se definen dos fuentes distintas Arial y Courier New:

{\fonttbl{\f0\fswiss\fcharset0 Arial;}{\f1\fmodern\fprq1\fcharset0 Courier New;}}

Y por otro lado tembién tenemos la tabla de colores, donde definimos tres colores, uno predeterminado que depende de la aplicación que trate el documento (indicado por el primer punto y coma) y otros dos definidos por sus valores RGB:

{\colortbl ;\red255\green0\blue0;\red0\green0\blue128;}

Por último, justo a continuación de la cabecera del documento, comienza el contenido del documento en sí, que irá acompañado de las palabras y símbolos de control que den información sobre su formato. Así por ejemplo, el fragmento siguiente:

\b\f0\fs20 NRTFTree v0.1

indica que el texto “NRTFTree v0.1” se escribirá en negrita (\b), con la fuente número 0 de la tabla de fuentes (\f0) y con tamaño de fuente 20 (\fs20).

Para una información más detallada sobre la especificación del formato RTF se puede consultar el documento oficial en el enlace que se indica al final de este artículo.

Librería NRtfTree

La librería de clases NRtfTree proporciona una serie de mecanismos generales para la lectura y manipulación de documentos RTF.

Como se ha podido comprobar en el apartado anterior, la estructura de un documento RTF resulta en principio un tanto encríptica y a priori nada fácil de manipular de forma automática. Aunque en el fondo esto no es así, si que es cierto que el desarrollo de analizadores a medida para este tipo de documentos es una tarea relativamente complicada o al menos laboriosa, y es en este aspecto donde NRtfTree puede resultar de gran ayuda.

Las estrategias utilizadas por NRtfTree para el análisis estruturado de documentos RTF son las mismas que las utilizadas para otro tipo de documentos y formatos muy extendidos como el XML. En el contexto del formato XML existen dos aproximaciones principales al problema del análisis de su contenido: DOM (Document Object Model) y SAX (Simple API for XML).

El primero de los métodos, DOM, consiste básicamente en la carga de un documento XML completo en una estructura de datos en forma de árbol que puede manipularse fácilmente mediante una serie de métodos para el recorrido y modificación del árbol.

La segunda de las soluciones, SAX, consiste en la lectura y análisis secuencial del documento XML, durante el cual se van lanzando una serie de eventos tratables por el desarrollador y que se corresponden con la lectura de cada uno de los elementos contenidos en el documento. Así, por ejemplo, cada vez que el analizador lee del documento una etiqueta de apertura de un elemento se lanza un evento que se podrá capturar para realizar la acciones oportunas.

El primero de los métodos es quizá el más flexible de ambos debido a que una vez cargado el documento completo éste puede recorrerse tantas veces como se desee y en el sentido que se necesite. En cambio, con SAX el documento es leido una sóla vez y las acciones realizadas deben ejecutarse a medida que se analiza el documento y sin tener la posibilidad de “volver atrás”.

Pues bien, NRtfTree transfiere estas dos mismas soluciones al campo del formato RTF, y de esta forma tendremos dos clases principales:

  • RtfTree y RtfTreeNode, que proporcionan los métodos necesarios para cargar y manipular un documento RTF en forma de estructura de árbol.
  • RtfReader y SARParser, que dan la posibilidad de realizar un tratamiento al estilo SAX de un documento RTF.

RtfTree y RtfTreeNode

Como hemos comentado, RtfTree nos va dar la posibilidad de tratar los distintos elementos de un documento RTF como si formaran parte de una estructura en forma de árbol. Esto facilitará en gran medida el análisis y modificación de un documento por parte de nuestras aplicaciones, siempre de forma muy general y sin imponer en ningún momento tratamientos o conversiones específicas del contenido.

Una vez cargado un documento en el árbol, cada nodo de éste representará un elemento del documento, que se diferenciarán por el tipo de nodo. De esta forma existirán 5 tipos de nodo:

ROOT      Nodo raíz del árbol.
KEYWORD   Palabra de control.
CONTROL   Símbolo de control.
TEXT      Texto del documento.
GROUP     Grupo RTF

Cada nodo del árbol irá acompañado además de su parámetro correspondiente en caso de exisitir, por lo que quedarán perfectamente separados todos los elementos del documento y su información asociada.

Como ejemplo, veamos el árbol creado al cargar el documento de la figura 1:

ROOT
  GROUP
    KEYWORD: rtf 1
    KEYWORD: ansi
    KEYWORD: ansicpg 1252
    KEYWORD: deff 0
    KEYWORD: deflang 3082
    GROUP
      KEYWORD: fonttbl
      GROUP
        KEYWORD: f 0
        KEYWORD: fswiss
        KEYWORD: fcharset 0
        TEXT: Arial;
      GROUP
        KEYWORD: f 1
        KEYWORD: fmodern
        KEYWORD: fprq 1
        KEYWORD: fcharset 0
        TEXT: Courier New;
    GROUP
      KEYWORD: colortbl
      TEXT: ;
      KEYWORD: red 255
      KEYWORD: green 0
      KEYWORD: blue 0
      TEXT: ;
      KEYWORD: red 0
      KEYWORD: green 0
      KEYWORD: blue 128
      TEXT: ;
    KEYWORD: viewkind 4
    KEYWORD: uc 1
    KEYWORD: pard
    KEYWORD: b
    KEYWORD: f 0
    KEYWORD: fs 20
    TEXT: NRTFTree v0.1
    KEYWORD: b 0
    TEXT: es una librer
    CONTROL: ' 237
    TEXT: a
    KEYWORD: cf 1
    KEYWORD: b
    KEYWORD: fs 24
    TEXT: escrita en C#
    KEYWORD: cf 0
    KEYWORD: b 0
    KEYWORD: fs 20
    TEXT: para el
    KEYWORD: cf 2
    KEYWORD: i
    KEYWORD: fs 24
    TEXT: tratamiento estructurado
    KEYWORD: fs 20
    TEXT:�
    KEYWORD: cf 0
    KEYWORD: i 0
    TEXT: de
    KEYWORD: f 1
    KEYWORD: fs 40
    TEXT: documentos RTF
    KEYWORD: f 0
    KEYWORD: fs 20
    TEXT: .
    KEYWORD: par

En la figura anterior se muestra cada elemento del documento RTF con su nivel dentro del árbol, su tipo de nodo, la palabra clave o símbolo de control correspondiente y su parámetro asociado en caso de existir.

Una vez cargado el documento, las clases RtfTree y RtfTreeNode proporcionan una serie de métodos sencillos con los que se puede recorrer el árbol y modificar su contenido añadiendo, modificando o eliminando nodos. Para una información más detallada de los métodos y propiedades disponibles se puede consultar la documentación proporcionada en formato CHM junto al código fuente de la misma.

RtfReader y SARParser

El objetivo de estas dos clases es proporcionar la posibilidad de realizar un tratamiento secuencial del documento RTF a medida que éste se va analizando.

El procedimiento general seguido para la utilización de esta técnica será el siguiente:

1. Implementar una clase derivada de SARParser donde habrá que redefinir, entre otros, los métodos correspondientes a cada uno de los eventos lanzados durante la lectura del documento RTF. Los eventos lanzados serán los siguientes:

  • Comienzo del documento.
  • Palabra de control leida.
  • Símbolo de control leido.
  • Texto leido.
  • Inicio de grupo leido.
  • Fin de grupo leido.
  • Fin del documento.

2. Crear una instancia de las clases RtfReader y de la clase anterior. El objeto RtfReader se encargará de cargar el documento y comenzar su lectura. En el constructor de éste se pasará como parámetro el objeto derivado de SARParser, de forma que le estamos indicando a RtfReader qué acciones tomar cada vez que se lance un evento determinado.

3. Comenzar la lectura del documento llamando al método Parse() del objeto RtfReader.

Enlaces relacionados

Especificación formato RTF versión 1.6 [http://latex2rtf.sourceforge.net/rtfspec.html]

, , , , , ,