Crear archivos PDF con CakePHP

En este post voy a comentar lo que he hecho para crear archivos PDF con CakePHP.

Los ingredientes para esta “receta” son:

1.- La librería TCPDF que se encarga de generar los archivos PDF.
2.- Un layout
3.- Una vista

Para implementar esta funcionalidad en vuestro desarrollo en CakePHP es muy sencillo y da muy buenos resultados.

  • Tendréis que descargar la última versión de la librería TCPDF que como he dicho es la encargada de generar los documentos PDF.
  • Tendréis que crear la carpeta app/vendors/tcpdf y extraer los ficheros en esta ruta. Al menos necesitareis copiar el archivo tcpdf.php y las carpetas tcpdf/config y tcpdf/fonts.
  • Por defecto la librería TCPDF crea unas cabeceras y pie definidas en los métodos header() y footer(), lo que hacemos es sobrescribir estos métodos creando una clase en app/vendors/tcpdf que extienda las propiedades y métodos de la librería TCPDF. Para ello creamos el archivo app/vendors/tcpdf/xtcpdf.php

    <?php
    App::import('Vendor','tcpdf/tcpdf');
    
    class XTCPDF  extends TCPDF
    {
    
        var $xheadertext  = 'PDF creado using CakePHP y TCPDF';
        var $xheadercolor = array(0,0,200);
        var $xfootertext  = 'Copyright © %d XXXXXXXXXXX. All rights reserved.';
        var $xfooterfont  = PDF_FONT_NAME_MAIN ;
        var $xfooterfontsize = 8 ;
    
        function Header()
        {
    
            list($r, $b, $g) = $this->xheadercolor;
            $this->setY(10);
            $this->SetFillColor($r, $b, $g);
            $this->SetTextColor(0 , 0, 0);
            $this->Cell(0,20, '', 0,1,'C', 1);
            $this->Text(15,26,$this->xheadertext );
        }
    
        function Footer()
        {
            $year = date('Y');
            $footertext = sprintf($this->xfootertext, $year);
            $this->SetY(-20);
            $this->SetTextColor(0, 0, 0);
            $this->SetFont($this->xfooterfont,'',$this->xfooterfontsize);
            $this->Cell(0,8, $footertext,'T',1,'C');
        }
    }
    ?>
    

    Por supuesto se puede modificar el valor de las variables para customizarlas a vuestro modo. Hay muchos mas parametros y variables que se pueden añadir y customizar en la documentación de TCPDF.

  • A continuación creamos el layout en app/views/layouts/pdf.ctp donde se renderizará el archivo pdf generado.
  • <?php
    header("Content-type: application/pdf");
    echo $content_for_layout;
    ?>
    
  • Ahora toca la parte del controlador. En el controlador de vuestra aplicación donde se vayan a mostrar o descargar los pdf tendreis que añadir una nueva función, yo la he llamado descargar, la podéis llamar como queráis.
  • function descargar($id = null)
        {
            if (!$id)
            {
                $this->Session->setFlash('no has seleccionado ningun pdf.');
                $this->redirect(array('action'=>'index'));
            }
            // Sobrescribimos para que no aparezcan los resultados de debuggin
            // ya que sino daria un error al generar el pdf.
            Configure::write('debug',0);
            $resultado = $this->MiControlador->findById($id);
            $this->set("datos_pdf",$resultado);
            $this->layout = 'pdf'; //esto usara el layout pdf.ctp
            $this->render();
        }
    

    Esta función es de ejemplo, podreis desarrollar la lógica que necesitéis para obtener la información. Pero lo realmente importante es la renderizacion de los datos en el nuevo layout.

     $this->layout = 'pdf';
    $this->render();
    
  • Es el paso en que hay que mostrar los datos obtenidos mediante nuestro controlador y renderizarlos en la vista. Para ello tenemos que crear nuestra vista. app/views/mi_aplicacion/descargar.ctp
  • <?php
    App::import('Vendor','xtcpdf');
    $tcpdf = new XTCPDF();
    $textfont = 'freesans';
    
    $tcpdf->SetAuthor("");
    $tcpdf->SetAutoPageBreak( false );
    $tcpdf->setHeaderFont(array($textfont,'',10));
    $tcpdf->xheadercolor = array(255,255,255);
    $tcpdf->xheadertext = 'Fecha: '. date('d-m-Y',time());
    $tcpdf->xfootertext = 'www.pedroventura.com';
    
    // Ahora imprimimos el contenido de la pagina en una posición determinada
    // estos datos son un ejemplo, y en mi ejemplo hay un pequeño texto y una imagen.
    $tcpdf->AddPage();
    $tcpdf->SetTextColor(0, 0, 0);
    $tcpdf->SetFont($textfont,'B',10);
    $tcpdf->Cell(10,20,'Nombre:', 0, 0);
    // configuramos la calidad de JPEG
    $tcpdf->setJPEGQuality(100);
    $tcpdf->Image($imagen, 0, 50, 200, 200, '', '', '', false, 150);  
    
    // se pueden asignar mas datos, ver la documentación de TCPDF
    
    echo $tcpdf->Output('mi_archivo.pdf', 'I'); //el pdf se muestra en el navegador
    //echo $tcpdf->Output('mi_archivo.pdf', 'I'); //el pdf se descarga
    
    ?>
    

    en el segundo parámetro del Output yo tengo ‘I’, esto es que el PDF se generará en una nueva ventana del navegador. Si ponemos el parámetro: ‘D’ el pdf generado se descargará.

fuente: http://bakery.cakephp.org/articles/view/creating-pdf-files-with-cakephp-and-tcpdf

Tags de búsquedas:

cakephp pdf, pdf cakephp, $tcpdf->output(, Printing from CakePHP to PDF Header, print to pdf cakephp, generar pdfs en cakephp, generar pdf con cakephp, exportar pdf cakephp, ejemplo exportar a excel cakephp, crear un excel desde cakephp con jquery

Posts Relacionados:


37 Comentarios en “Crear archivos PDF con CakePHP”

  1. Mpadron dice:

    Buenos dias, felicitaciones por tu blogs y los diferentes notas contenida en ella, una pregunta a ver como me puede ayudar:
    Tengo un reporte en TCPDF, en donde la primera hoja quiero imprimir una cabezera y a partir de la segunda en adelenate otra, trato de hacerla de esta manera:
    public function Header($segundaHoja,$banco,$cliente,$Cta_cte,$fecha_finals,$ano) {
    if ($segundaHoja===true) {
    // *** replace the following parent::Header() with your code for TOC page
    //parent::Header();
    $this->Image(‘/var/www/SISCONVAPOST/app/webroot/img/LogoUnerg.jpg’,10,10,40,20);
    $this->Cell(180,3,”Respublica Bolivariana de Venzuela”,0,1,’C');
    $this->Cell(180,4,”Universidad Rómulo Gallegos”,0,1,’C');
    $this->Cell(180,5,”San Juan de los Morros Ed. Guárico”,0,1,’C');

    } else {
    // *** replace the following parent::Header() with your code for normal pages
    //parent::Header();
    //$this->SetX(10);

    $this->Cell(180,6,$banco,0,1,’C');
    $this->Cell(180,7,$cliente,0,1,’C');
    $this->Cell(180,8,”Cta. Cte. Nº.: “.$Cta_cte,0,1,’C');
    $this->Cell(180,7,”Depositos Originales Correspondiente al mes “.$fecha_finals.” de “.$ano,0,1,’C');

    }

    Pero no sale, como quiero, a partir de la 3 hojas no sale ningun encabezado, sera q me podrias orientar. gracias

  2. Leonardo dice:

    Felicitaciones por tu blog. Relamente tienes una forma de explicar las cosas y una dedicacion digna de destacar. Muchas gracias por tu tiempo y tu generosidad.

  3. sebastian dice:

    hola pedro,hice tu ejemplo para mi tabla cotizaciones que quiero exportar a pdf
    y aparece
    Error del servidor
    El sitio web ha detectado un error al recuperar la Es posible que esta esté inactiva debido a tareas de mantenimiento o que se haya configurado de forma incorrecta.

    cambio el debug a 3 y aparece:

    Fatal error: Class ‘XTCPDF’ not found in

    ojala puedas ayudarme

  4. Dayan dice:

    Hola despues de buscar mucho encontre tu tutorial, y esta genial!!! muchas gracias me funciona a la perfeccion, pero tengo un pequeño problema, como hago para que en el documento me muestre mas de una tupla de la base de datos?, por ejemplo como sacar un reporte de todos los usuarios que nacieron en 1990… Gracias de antemano…

    • Pedro Ventura dice:

      Pues esto es tema de mysql. Tendrá que mirarte la documentación de mysql, pero será algo similar a esto:

      SELECT *
      FROM table
      WHERE DATE >= '01/01/1990' AND DATE <= '30/12/1990'
      
      • Dayan dice:

        Mil gracias por responder… ya solucione ese tema y ya muestro los datos de mi bd con querys a la misma… todo bien!!! hasta ahi… , es decir localmente funciona a mil maravillas hasta redimencione el tamaño de la hoja 1/2 carta para que me imprima notas de entrega, recibos, etc., pero cuando subí la pagina al servidor y probe, me sale el siguiente error:

        TCPDF ERROR: Some data has already been output to browser, can’t send PDF file

        No me muestra los pdf como cuando funciona localmente…

        Te agradeceria alguna sugerencia para solucionar aquello, ya revise lo de los espacios antes y despues de ….

        Gracias de antemano…

        • Pedro Ventura dice:

          Puede que sea que en local tienes algún reporte de errores y en producción no. Prueba a poner al principio del php

          error_reporting(0);
          

          Sino, esto me ha pasado a veces que hay algún espacio en blanco en el fichero, normalmente al final de éste y da este problema

  5. Isra dice:

    Wooow gracias por este tuto, es el mejor que encontre de verdad me sirvio mucho funciona a la perfeccion

  6. elvis dice:

    Muy bueno el tuto, yo había probado con dompdf pero me daba algunos errores al rederizar la vista, pero con tcpdf me esta funcionando de maravilla aunque sea mas moroso crear el pdf.

  7. Juan dice:

    Primero que nada: Gracias por este articulo tan útil! segui las instrucciones y logre ver el archivo pdf (aunque no se mostró la imagen pero si el texto). Ahora quiero visualizar los datos del objeto Post los cuales extraigo de una tabla pero no se el procedimiento completo. En tu ejemplo usas
    $resultado = $this->Post->findById($id);
    $this->set(‘datos_pdf’,$resultado);
    Pero no entiendo como debo usar datos_pdf en el archivo cargar.ctp si me puedes aclarar esta duda te lo agradecería.

    • Pedro Ventura dice:

      Buenas Juan,

      Gracias por tu comentario!

      Bueno te explico, lo que yo tengo es un ejemplo. Que puede ser algo que no necesitas, simplemente es ilustrativo. Por lo que es posible que tu no necesites asignar datos a la vista, o si… eso ya es lo que tu necesites.

      Ahora bien te explico el ejemplo, lo que hago es recoger los datos del Modelo Post cuyo identificador sea $id. Bien.
      Esto que se ha obtenido va a ser un array y lo seteo en la vista. con

      $this->set(‘datos_pdf’,$resultado);

      Si tienes dudas lo primero que puedes hacer es un

      debug($datos_pdf); o var_dump($datos_pdf); 

      en la vista, para comprobar que se ha seteado bien los datos y llegan a la vista. Si tienes problemas y te da errores, prueba a renderizar otro layout que no sea el de pdf, en la función del controller en vez de poner

        $this->layout = 'pdf';

      pon

        $this->layout = 'default';

      o cualquier otro layout que tengas creado para tu aplicación.

      Y luego ya desde la vista vas colocando los datos de $datos_pdf donde necesites y en la posición que necesites en el html o en el pdf.

    • Juan dice:

      Ya lo resolvi utilizando $datos_pdf como sigue:
      $tcpdf->Cell(60,20,$datos_pdf['Post']['title'], 0, 10);

      Ahora tengo otra duda. Cuando utilizo cualquiera de los ejemplos de http://www.tcpdf.org/examples.php obtengo el siguiente error:Fatal error: require_once() [function.require]: Failed opening required ‘../tcpdf.php’

      • Juan dice:

        Pedro y muchas gracias por tomar un poco de tu tiempo para aclarar en forma tan explicita la duda que tenia!

      • Juan dice:

        Pedro encontre una manera (forma absoluta) de usar los ejemplos de http://www.tcpdf.org/examples.php:
        Tomo cualquiera de los ejemplos y lo agreo como una accion del controlador Post, en este caso la llame imprimir.ctp; luego sustituyo tanto en require_once(‘../config/lang/eng.php’) como en
        require_once(‘../tcpdf.php’); colocando la ruta completa donde se encuentran respectivamente tcpdf.php y eng.php asi:
        require_once(‘Unidad:/ruta/completa/config/lang/eng.php’);
        require_once(‘Unidad:/ruta/completa/tcpdf/tcpdf.php’); (funciono usando ‘/’ o usando ‘\’ aunque creo que debe haber una manera mas elegante de indicar la ruta en forma relativa).

        • Pedro dice:

          Lo puedes hacer con el require_once como haces, pero lo suyo es incluirlo como Vendor, App::import(‘Vendor’,'tcpdf/tcpdf’);

          Luego para la barra, utiliza la constante DS, que sirve para pintar los separadores.
          require_once(‘Unidad:’.DS.’ruta’.DS.’completa’.DS.’tcpdf’.DS.’tcpdf.php’);

          • Juan dice:

            Gracias por esa recomendacion. Estoy tratando de implementarla. Sin embargo se me esta presentando un problema con algunos ejemplos, cuando los trato de usar me aparece el siguiente error: “el archivo no empieza por ‘ %pdf-’”; he buscado una solucion a través del buscador de google pero ninguna me ha funcionado. Estoy usando la ultima version de Wampserver (2.1) y las versiones 6 y 9 de Adobe Reader… ¿Tienes alguna idea que me sugieras Pedro? Gracias!

          • Pedro Ventura dice:

            Eso mismo me pasó hace mucho tiempo, pero no recuerdo muy bien como lo solucioné… vaya tenía que haber documentado ese error en su día.

            Creo que puede venir por que tienes algun espacio en blanco, o algo relacionado en la vista, como te digo no recuerdo muy bien.

            Si quieres usa este código de ejemplo que te paso, que es el que tengo en una de mis vistas y que funciona ok

            <?php
            App::import('Vendor','xtcpdf');
            $tcpdf = new XTCPDF();
            $textfont = 'freesans'; // looks better, finer, and more condensed than 'dejavusans'
            
            $tcpdf->SetAuthor("");
            $tcpdf->SetAutoPageBreak( false );
            $tcpdf->setHeaderFont(array($textfont,'',10));
            $tcpdf->xheadercolor = array(255,255,255);
            $tcpdf->xheadertext = '';
            $tcpdf->xfootertext = '';
            
            // Now you position and print your page content
            // example:
            $tcpdf->AddPage();
            $tcpdf->SetTextColor(0, 0, 0);
            // test Cell stretching
            // set JPEG quality
            
            $htmlcontent = '<img src="http://www.pedroventura.com/images/logo.jpg&quot; /><p>Destinatario:</p>';
            
            // output the HTML content
            $tcpdf->writeHTML($htmlcontent);
            //$tcpdf->setJPEGQuality(100);
            //$tcpdf->Image('http://www.todopapas.com/img/logo_tpp.jpg');
            //$tcpdf->Cell(0, 10, 'Destinatario:', 0, 1, null, 0, '', 0);
            $tcpdf->Cell(0, 10, strtoupper($datos_user['User']['nombre']).' '.strtoupper($datos_user['User']['apellidos']), 0, 1, null, 0, '', 0);
            $tcpdf->Cell(0, 10, strtoupper($datos_user['User']['direccion']), 0, 1, null, 0, '', 0);
            $tcpdf->Cell(0, 10, strtoupper($datos_user['User']['localidad']), 0, 1, null, 0, '', 0);
            $tcpdf->Cell(0, 10, $datos_user['User']['cp'].' '.strtoupper($datos_user['GeoIntProvincia']['provincias_nombre']), 0, 1, null, 0, '', 0);
            $tcpdf->Cell(0, 10, $datos_user['User']['tlf'], 0, 1, null, 0, '', 0);
            
            echo $tcpdf->Output('filename.pdf', 'I');
            
            ?>
            
          • Juan dice:

            Ya resolvi el problema “el archivo no empieza por ‘ %pdf-’ “: Cuando copiaba y pegaba el codigo de los ejemplos dentro del imprimir.ctp se insertaba una linea en blanco con caracteres extraños. Solo repeti la operacion de copiar y pegar en forma “limpia” sin pegar espacios o carecteres extraños al final de linea. Pedro todavia tengo dudas para usar el App::import(‘Vendor’,’tcpdf/tcpdf’) que me sugeristes pero estoy en eso.

          • Pedro Ventura dice:

            me alegro!! bueno como tenia la sensación tenia que ser algún carácter raro o espacio en blanco. Sobre lo del App::Import, de que tienes dudas? no te entiendo muy bien. App::Import en cakephp no es mas que un include() de php. No te lies mucho con esto es muy simple.

          • Juan dice:

            Que tal Pedro, como estas? Con respecto al último comentario que me enviastes sobre el uso del App::import() le he “hechado cabeza” y sigo con dudas sobre su uso. El problema comienza con las instrucciones require_once(‘../config/lang/eng.php’);
            require_once(‘../tcpdf.php’); que como te comente no me funcionaban, asi que coloque en ambas las rutas completas: require_once(‘ruta/completa/config/lang/eng.php’);
            require_once(‘ruta/completa/tcpdf.php’); y asi si corrio. Tu me sugeristes usar App::import(‘Vendor’,’tcpdf/tcpdf’); las use para sustituir ambas instrucciones pero no se que partes de las rutas debo colocar (si es que tengo que colocar las rutas). En resumen solo me ha funcionado combinando las instrucciones asi tal cual como siguen: App::import(‘Vendor’,'tcpdf’); require_once(‘ruta/completa/tcpdf/config/lang/eng.php’); ¿Debo usar App::import para apuntar al archivo eng.php o la carpeta que lo contiene? y ¿Cómo indicar su ubicacion al App::import? Gracias!

          • Pedro Ventura dice:

            Buenas Juan,

            Bueno a ver creo que estas algo confuso.
            Tal cual lo tienes ahora esta perfecto!!!

            App::Import() te dije que es parecido a un require, pero bueno es algo más no te lo aclaré bien. Se usa para incluir librerías y clases. Por lo que para incluir un php que no es un clase no funciona, y me imagino que en tu /config/lang/eng.php no tendrás ninguna clase, y por esto cakephp no está incluyendo bien este archivo.

            Entonces como lo tienes esta perfecto. Con App::Import importas la clase de tcpdf.php y con el requiere lo que tienes en /config/lang/eng.php

            Por otro lado, te pregunto que es lo que tienes en /config/lang/eng.php??? Son textos? Porque si son textos para diferenciarlos de una versión en ingles, español, portugués, etc.. usa el sistema de internalizacion que trae cakephp “locale” que es una implementacion de los archivos .po de internacionalizacion.

          • Juan dice:

            Oye Pedro muchas gracias de verdad! No me habia fijado en el contenido de ese archivo. Voy a leer un poco más sobre el sistema de internacionalizacion de cakephp. Seguimos en contacto…

  8. mayuli dice:

    hola Sr Pedro Ventura, tendrá algún ejemplo pero para excel.xls? o alguna recomendación?

  9. Nicolas dice:

    No he podido ver el pdf, cuando intento generarlo, no tira ningun error pero la pagina queda en blanco, probe variar los parametros de memoria que han explicado arriba pero sigo sin resultados, Muchas gracias!

    • admin dice:

      Le pusiste el header de pdf header(“Content-type: application/pdf”); ??

      Tienes el debug a 3? Mira el log de errores de apache que seguro te tiene que estar devolviendo algun error

      • Nicolas dice:

        Si activo debug en el controlador me tira esto:
        Fatal error: Call to a member function charset() on a non-object in N:\www\hostcontrol\app\views\layouts\admin.ctp on line 4

        en el log de apache:[Fri Sep 17 09:28:45 2010] [error] [client 127.0.0.1] PHP Fatal error: Call to a member function charset() on a non-object in N:\\www\\hostcontrol\\app\\views\\layouts\\admin.ctp on line 4, referer: http://hostcontrol.local/admin/people

        Y si le puse el header por lo que decias antes, gracias

        • admin dice:

          seguro te está fallando una linea en el layout: admin.ctp que será algo así

          echo $html->charset();
          

          En tu controlador has incluido el helper de html??

          var $helpers = array('Html');
          
  10. Alan dice:

    Cual es la ruta exacta para ver la descargar del PDF.
    intento con web.com/mi_aplicacion/descargar y nada…

    Gracias

    • admin dice:

      pues la ruta depende, será miweb.com/controller/function que tengas. En este caso la function es descargar.
      Mete en el boostrap.php

      Configure::write('debug',3);

      para ver que error te está dando.
      Eso… o puedes meter

      function __construct()
          {
              parent::__construct();
              configure::write('debug',3);
          }
      

      en el controlador donde has integrado esto del pdf

      Primero hay que localizar el error u obtener más pistas del error que te pueda estar dando… sabes es una pregunta muy poco concreta

      también puedes mirar en el error_log de apache

  11. Alevsk dice:

    Hola primero que nada gracias por el tutorial, he buscado en google un par de horas y no consigo encontrar una solucion, segui tu tutorial pero me da un error al generar el pdf

    TCPDF ERROR: Some data has already been output, can’t send PDF file

    Ya revise todos mis archivos en busca de saltos antes de , tambien aplice el Configure::write(‘debug’,0); en el controlador y sigue dando el error

    gracias

    • admin dice:

      pues ese error parece algo de los header, le has puesto bien la cabecera??
      lo tienes que renderizar en el layout pdf
      < ?php
      header("Content-type: application/pdf");
      echo $content_for_layout;
      ?>

      un problema común por el que da un error de headers es que haya algún espacio en blanco al final del controller…

    • Adolfo dice:

      Hola, tengo el mismo problema, lograste encontrar la solución? de antemano gracias!

      • Adolfo dice:

        Es extraño, pero al cambiar la I o la D en el view por una S, el archivo se ve, saludos

        echo $tcpdf->Output(‘mi_archivo.pdf’, ‘S’);

  12. Dementrio Macias dice:

    Muy bien el articulo traducido, muchas gracias.
    solo tuve un inconveniente, que la memoria era superada por este script, así que hacia falta corregir esto en la configuración del PHP, con:
    Al inicio del script “app/views/mi_aplicacion/descargar.ctp”

    ini_set(“memory_limit”,”12M”); // se incrementa tamaño hasta que sea el correcto

Deja tu comentario

Nombre:

Email (no será publicado)

Website

Comentario

Colabora, añade +1 a mi blog!!