Automágica: durante 2017 estoy trabajando bastante en Automágica, mi software para editar libros: Más información - Posts relacionados

Generar archivos rtf en forma dinámica desde Django

En mi anterior artículo les conté sobre pyrtf-ng, una librería para generar archivos rtf en forma fácil desde un programa escrito en Python. Fue la primer alternativa que manejamos a la hora de plantearnos el problema de generar archivos rtf en forma dinámica desde Django.

En este artículo les cuento el aproach que finalmente adoptamos. Si la naturaleza dinámica del documento que queremos generar radica en que ciertas partes del texto tendrán valores dinámicos o ciertas partes pueden estar o no dependiendo de alguna condición, lo que podemos hacer es utilizar un sistema de templates. El formato rtf es un lenguaje de marcas (es texto, no binario!). Por lo que fácilmente podemos usar el subsistema de templates de Django para lograr nuestro objetivo.

Un ejemplo de lo que queremos obtener

Simplificando, si en el sistema la variable nombre vale "Juan" y la variable tratamiento es False, el resultado esperado en el archivo rtf es:

Hola Juan

Si la variable nombre vale "Raúl", la variable tratamiento es "Sr." el resultado esperado en el archivo rtf es:

Hola Sr. Raúl

Para lograrlo necesitamos un archivo hola.rtf (puede tener cualquier nombre y cualquier extensión, pero estos parecen apropiados para el ejemplo) ubicado en algún directorio accesible por el subsistema de templates. Tip: crear un archivo rtf con OpenOffice Writer o WordPad y luego editarlo con un editor de texto para agregar las marcas necesarias para que Django lo procese.

El template

Así se ve el template del ejemplo cuando lo abrimos con un editor de texto, luego de agregar las marcas correspondientes:

{% load filtros %}{\rtf1\ansi\deff0\adeflang1025

{\fonttbl{\f0\froman\fprq2\fcharset0 Bitstream Vera Sans;}{\f1\froman\fprq2\fcharset0 Bitstream Vera Sans;}{\f2\fswiss\fprq2\fcharset0 Bitstream Vera Sans;}{\f3\fnil\fprq2\fcharset0 Bitstream Vera Sans;}}

{\colortbl;\red0\green0\blue0;\red128\green128\blue128;}

[....]

{\rtlch \ltrch\loch\f0\fs24\lang11274\i0\b0 Hola {% if tratamiento %}{{ tratamiento }} {% endif %}{{ nombre|rtf }}}

\par }

El encabezado suele ser largo (esto depende del programa utilizado para crear el documento, con WordPad se obtienen los encabezados más cortos). Las partes importantes están resaltadas en negrita.

El bloque load carga el filtro de nombre rtf que luego es usado. Notar que este bloque debe incluirse en la misma línea que la primer línea del documento o en alguna línea siguiente, pero nunca en la primer línea del archivo y solo. Esto provocará una línea en blanco al principio del documento resultante, un error de sintaxis en el formato rtf, y por lo tanto no podrá ser interpretado por los procesadores de palabras.

Un filtro para codificar caracteres Unicode en rtf

Como rtf no soporta nativamente caracteres Unicode (tildes y otros caracteres no ascii), estos deben ser codificados para ser correctamente interpretados. El siguiente filtro hace esa tarea, es un string filter de Django:


@register.filter(name='rtf')

@stringfilter

def rtf(value):

    if isinstance(value, UnicodeType):

        return "".join(["u%s?" % str(ord(c)) for c in value])

    return value


Soporte Unicode para pyrtf-ng

pyrtf-ng es un módulo para Python que permite generar archivos en formato RTF en forma dinámica. Esta semana empecé a probarlo con la idea de usarlo para generar documentos de texto con formato (Word-like) en forma dinámica.

Al intentar instalarlo tuve algunos problemas , se los comenté al autor y le sugerí soluciones. Rápidamente estas modificaciones estuvieron hechas en el repositorio del proyecto.

Una vez que tuve la librería instalada y los ejemplos corriendo, empecé a modificar algunos para experimentar un poco. Cuando quise generar un documento que contenga una palabra con tilde (cómo canción), el programa tiró una horrible excepción. Esto hacía que desechara totalmente la idea de usar este software.

Leí sobre la codificación utilizada por el formato RTF y descubrí que no soporta Unicode en forma nativa, pero si mediante una secuencia de escape de la forma: \uxxxx? en dónde xxxx es un entero con signo de 16 bits correspondiente al caracter que se quiere representar. Ejemplo: á.

Pedí instrucciones al autor de la librería, quien me indicó que partes del código tendría que revisar par incluir la funcionalidad de soportar Unicode. Luego de algunas horas empapándome en la arquitectura de clases de pyrtf, tenía un parche listo para ser aplicado e incorporar la funcionalidad. Hoy fue incluido en la versión en desarrollo de la librería.

Código

Un pequeño snippet que muestra el algoritmo básico para pasar de un string Unicode a su representación codificada en RTF:

encoded = ''.join(['\u%s?' % str(ord(b)) for b in base])

Datos

  • pyrtf-ng es mantenido por Duncan Mc Greggor y está basado en PyRTF, un proyecto que ya no es mantenido.
  • Se encuentra en activo desarrollo: modificación del estilo del código y refactoring.
  • También puede ser modificado mediante bazaar en lauchpad.