Inicio Proyectos Librería NRtfTree Tutorial NRtfTree (2) – Conversor de RTF a XML

Tutorial NRtfTree (2) – Conversor de RTF a XML

por sgoliver

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.

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información. Aceptar Más Información

Política de Privacidad y Cookies