gleich zum inhalt springen

Generación de documentos pdf desde symfony con LaTeX

Una de las funcionalidades más interesantes que se espera que tenga una aplicación de gestión y en particular una aplicación de gestión web, es la generación de documentación en función de la información almacenada por el sistema. El formato más útil y estandarizado para generar este tipo de documentos es en pdf, documentos que podremos enviar facilmente por correo electrónico o imprimir, manteniendo el formato y que podrán ser leídos por todos los usuarios que dispongan de un visor de documentos pdf, cosa bastante habitual hoy en día.

pdf

Al plantearnos el diseño de una aplicación web, surgen diferentes enfoques a la hora de soluciona el problema de la generación de documentos en pdf. La solución que proponemos nosotros aquí, y que hemos implementado en Quoriam se basa en hacer uso de LaTeX. LaTeX es un lenguaje de marcado muy extendido en el mundo académico y científico y que nos ofrece una gran potencia a la hora de generar documentación técnica. Una de las virtudes de LaTeX es la generación de manera rápida y sencilla de documentos en pdf a través de su compilador, que esta disponible en todas las distribuciones GNU/Linux (paquete tex-live) y también en sistemas Windows a través de MikTex.

Para generar un pdf en función de un documento LaTeX (en GNU/Linux), debemos hacer lo siguiente (los documentos LaTeX tienen extensión .tex):

$ pdflatex midocumento.tex

A continuación veremos como hemos implementado nosotros el uso de LaTeX desde una aplicación web basada en symfony 1.2:

Creamos una clase en el directorio lib/ de nuestro proyecto symfony, nosotros la hemos llamado latexCompiler y en esta clase implementamos un método estático (generatePDF) que recibe tres parámetros: el objeto symfony de la respuesta http ($response), el nombre del fichero de salida ($filename) y el código LaTeX que queremos compilar:

<?php                                                                                                                                                                                                         
class latexCompiler                                                                                                                                                                                           
{
  /**                                                                                                                                                                                                         
   * Generate PDF                                                                                                                                                                                             
   * @param $response Objeto de la respuesta para fijar que se devolverá un documento pdf                                                                                                                     
   * @param $fileName Nombre del fichero que se quiere devolver                                                                                                                                               
   * @param $latexCode Código LaTeX que se quiere compilar a pdf                                                                                                                                              
   */                                                                                                                                                                                                         
  public static function generatePDF($response, $fileName, $latexCode)                                                                                                                                        
  {                                                                                                                                                                                                           
    /* Crear fichero temporal en /tmp para código LaTeX */                                                                                                                                                    
    $tempfile = tempnam("/tmp","tex");                                                                                                                                                                        
    $fp  = fopen ($tempfile.'.tex',"w");                                                                                                                                                                      
    if (!$fp)                                                                                                                                                                                                 
      {                                                                                                                                                                                                       
        die ("Could not open ".$tempfile);                                                                                                                                                                    
      }
    /* Volcar código LaTeX en fichero */                                                                                                                                                                      
    fwrite($fp, $latexCode);                                                                                                                                                                                  
    fclose($fp);
                                                                                                                                                                                     
    /* Compilar LaTeX a PDF*/                                                                                                                                                                                 
    exec("cd /tmp; pdflatex -output-directory /tmp -interaction nonstopmode $tempfile.tex 2>&1");                                                                                                              

    /* Recoger log */          
    if (file_exists($tempfile.'.log'))                                                                                                                                                                        
      {                                                                                                                                                                                                       
        $log = file_get_contents("$tempfile.log");                                                                                                                                                            
      }
   /* Devolver pdf o error */                                                                                                                                                                                
    if (file_exists($tempfile.'.pdf'))                                                                                                                                                                        
      {                                                                                                                                                                                                       
        $response->setContentType('application/pdf');                                                                                                                                                         
        $response->addHttpMeta('cache-control', 'no-cache');                                                                                                                                                  
        $response->addHttpMeta('Expires', gmdate("D, d M Y H:i:s") . " GMT",time());                                                                                                                          
        $response->setHttpHeader('Content-Disposition', 'attachment; filename="'.$fileName.'.pdf"');                                                                                                          
        $response->setContent(file_get_contents($tempfile.'.pdf'));                                                                                                                                           
      }                                                                                                                                                                                                       
    else                                                                                                                                                                                                      
      {                                                                                                                                                                                                       
        $msg = '<h1>Ha ocurrido un error al generar su documento pdf:</h1>';                                                                                                                                  
        $msg .= '<h2>Log</h2><pre>'.$log.'</pre>';                                                                                                                                                            
        $msg .= '<h2>Tex-Source:</h2><pre>'.file_get_contents("$tempfile.tex").'</pre>';                                                                                                                      
        echo $msg;                                                                                                                                                                                            
      }                                                                                                                                                                                                       
  }                                                                                                                                                                                                           
}

La lógica natural de symfony divide una petición en acción y plantilla, para la generación de pdf omitiremos la plantilla y en su lugar usaremos los “partials” que nos proporciona symfony. A continuación vemos un ejemplo de una acción (executePdf) donde invocamos nuestro método estático para devolver un documento pdf:

<?php
class misaccionesActions extends sfActions                                                                                                                                                                          
{
  public function executePdf(sfWebRequest $request)                                                                                                                                                           
  {
    ...
    /* Procesa y recupera el código LaTeX  */                                                                                                                                                                 
    $latexCode =  $this->getPartial('latex');                                                                                                                                       

    /* Recupera la referencia de la respuesta para pasarla al generador de pdf*/                                                                                                                              
    $response = $this->getContext()->getResponse();                                                                                                                                                            

    /* Invoca el método estática para generar el pdf y devolverlo */                                                                                                                                          
   latexCompiler::generatePdf($response, 'mi documento pdf', $latexCode);                                                                                              

  return sfView::NONE;                                                                                                                                                                                        
  }

latex_logoEsta acción depende de un partial ‘latex’, que será un fichero que se encuentre en templates/_latex.php y que contendrá el código LaTeX y podrá tener código PHP para construirse dinámicamente. El código PHP será renderizado en el momento de la invocación de getPartial(), antes de esta invocación en la acción podremos definir estructuras de datos que alimentarán el código PHP del partial, como es habitual en symfony. Tras este paso tendremos en $latexCode código LaTeX puro que le pasaremos a nuestro método estático, junto con el objeto de la respuesta y el nombre del fichero. Es importante que la acción retorne con sfView:NONE, para que no vaya a buscar ningún template y simplemente se devuelva el fichero pdf que envía nuestra clase estática.

De esta manera tendremos de forma sencilla una herramienta para generar documentos pdf muy versátil y potente, lo único que debemos hacer es aprender un poco de LaTeX para rellenar nuestro partial. Podemos extender facilmente nuestro método estático de generación de pdf para que realice más comprobaciones, ejecute lógica adicional o genere el LaTeX en otros formatos soportados diferentes de PDF (dvi, html…).

Espero que os sea de utilidad.

Un saludo a todos

Manuel Zaforas

admin in latex, symfony on August 11 2009 » 0 comments

Modelos 3D de los robots colaborativos

Entre el aniversario de la empresa y la adaptación a la ISO 9001 apenas hemos tenido tiempo de avanzar con los proyectos. De momento os dejo estos renders de los prototipos de robot colaborativo que he preparado. Espero en breve empezar con las pruebas de hardware.

Un saludo a todos

Prototipo 3D 1

Prototipo 3D 2

Prototipo 3D 3

admin in Robótica on March 16 2009 » 0 comments

Arduino: Prototipando con Hardware Libre y robots colaborativos

Materiales: Arduino, Motores, Módulos GPRS ...

La semana pasada nos llegó a Quoriam todo el material que véis en la foto (via libellium, sparkfun y solarbotics). Los módulos GSM/GPRS son para desarrollos comerciales en los que estamos trabajando, pero el resto de cosas son para investigación. Aunque ya tenemos experiencia en el desarrollo con microcontroladores, esta es nuestra primera experiencia con Arduino. Hemos estado trabajando con ellos esta semana y la verdad es que es una maravilla, sencillo y rápido de prototipar, cómodo de manejar y hay disponibles toneladas de documentación y proyectos de la comunidad colgados por ahí.

Como esta semana hemos estado hasta arriba de trabajo no hemos podido dedicarle tiempo más que a los desarrollos para cliente y no a los de investigación con lo que mi proyecto de robótica no ha podido avanzar mucho más que en mi cabeza pero si que puedo ir contando un poco en que va a consistir.

Mi primer proyecto personal dentro de Quoriam va a tratar sobre robots colaborativos. Mi objetivo en primera instancia es desarrollar un robot muy pequeño, económico y versatil con el objetivo de poder tener varios de ellos trabajando juntos por un precio reducido. Una vez que tenga este primer prototipo me meteré con la comunicación y la colaboración. En primera instancia mi primera meta es desarrollar un robot lo más pequeño, económico e industrializable posible para poder contar con un buen número de ellos y ponerlos a trabajar juntos.

Sé que no he contado mucho en este artículo, en el próximo ya empezaré con la chica: Componentes elegidos y por qué, boceto del robót y primeras pruebas con el hardware; vamos lo realemente interesante.

Hasta la próxima entrega!

admin in Robótica on February 27 2009 » 0 comments

El laboratorio de Quoriam

Hoy da cominezo un nueva iniciativa de Quoriam Ingenieros: El laboratorio de Quoriam.

Nuestra intención con esta idea es compartir con la comunidad nuestros proyectos de investigación, nuestros desarrollos y en general todo aquello que creamos que puede ser útil o interesante para los que os gusta la informática desde todos sus puntos de vista. Con este post damos el pistoletazo de salida a El laboratorio de Quoriam, con la ilusión y la motivación de aportar y compartir.

La temática de este Blog es algo incierta, y lo seguirá siendo. Todo lo que aquí se escriba será fruto de la inquietud de alguno de los miembros de Quoriam Ingenieros que quiera compartir con vosotros sus proyectos personales dentro de la empresa. El que suscribe comenzará con algo de robótica y electrónica y con el tiempo veremos por que derroteros nos lleva esta pequeña aventura.

Un saludo a todos los amigos, clientes y curiosos que estéis leyendo estas palabras y esperamos veros por aquí de nuevo.

admin in Noticias del laboratorio on February 22 2009 » 0 comments