Crear pdfs en cakephp utilizando TCPDF
Viernes, 5 Septiembre 2008
En nuestra experiencia en desarrollo web, se nos ha planteado proyectos que tenían como uno de sus requisitos la creación de un archivo pdf generado automáticamente, por ejemplo una factura de compra o un informe de ventas.
En un principio, comenzamos utilizando fpdf, una clase en php, totalmente gratuíta, que permite crear automáticamente archivos pdf y que además tenía un helper para cakephp 1.1.
Esta clase funcionaba increíblemente bien, pero como todos los proyectos que no se actualizan regularmente (hasta hace poco llevaban más de 4 años sin realizar cambios), se quedan desfasados.
Tenía un gran número de desventajas como que no aceptaba codificación UTF-8, o lo complejo que resultaba su uso.
De este proyecto surgió en 2002 una rama, TCPDF. Este proyecto ,también libre y gratuíto, en un principio solucionaba carencias del fpdf, pero finalmente se ha convertido en una versión mucho más potente, con gran cantidad de posibilidades como por ejemplo el soporte de UTF-8 o idiomas de derecha-izquierda, permite la utilización de códigos html e incluye una lista bastante amplia de fuentes libres, que podremos utilizar en nuestro pdf.
Para incluirlo en uno de nuestros proyectos y empezar a utilizarlo hay que llevar a cabo una serie de pasos bastante sencillos (añadir nuevas funcionalidades a un proyecto cakePHP se ha facilitado en la versión 1.2), en primer lugar bajar la última versión de TCPDF, descomprimirla en el directorio /app/vendors de nuestra aplicación.
El resultado debería ser /app/vendors/tcpdf/ conteniendo al menos, tcpdf.php y los directorios tcpdf/config y tcpdf/fonts
En segundo lugar tendremos que crear un layout para los pdf’s en /app/views/layouts/pdf.ctp, con este contenido
header("Content-type: application/pdf");
echo $content_for_layout;
En el controlador que queramos crear los pdf’s, crearemos una función para ello
function pdf()
{
Configure::write('debug',0);
$this->layout = 'pdf'; //this will use the pdf.ctp layout
// Operaciones que deseamos realizar y variables que pasaremos a la vista.
$this->render();
}
Por último una vista para esta función, con el siguiente contenido:
App::import('Vendor','tcpdf');
$tcpdf = new TCPDF();
$textfont = 'freesans';
$tcpdf->SetCreator(PDF_CREATOR);
$tcpdf->SetAuthor("autor");
$tcpdf->SetTitle("Título");
$tcpdf->SetSubject("Tutorial TCPDF en cakePHP");
$tcpdf->SetKeywords("TCPDF, PDF, cakePHP, ejemplo");
$tcpdf->setPrintHeader(false);
$tcpdf->setPrintFooter(false);
$tcpdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
$tcpdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM);
$tcpdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
$tcpdf->setLanguageArray($l);
$tcpdf->AliasNbPages();
$tcpdf->AddPage();
$tcpdf->SetFont("freesans", "BI", 20);
$tcpdf->Cell(0,10,"Hola mundo",1,1,'C');
$tcpdf->Output("ejemplo.pdf", "I");
En este ejemplo se creará un pdf muy sencillo con un cuadrado con el texto hola mundo dentro.
Las posibilidades de TCPDF son muchas, para comprobar lo que es capaz de hacer, es recomendable ojear los ejemplos contenidos en la página y la documentación de la clase, que explica el funcionamiento de todos sus métodos.
En nuestra página de pruebas de cakephp hay un ejemplo sencillo de funcionamiento.
Actualización (9/2/2009) :
Errores frecuentes:
“TCPDF error: Some data has already been output to browser, can’t send PDF file”: Este error se produce cuando cakePHP envía algún dato antes de crear el pdf y enviarlo, esto puede ocurrir por una serie de razones que habrá que comprobar:
- Realizar un debug o un echo, en la vista o controlador.Esto crea salida de datos por lo que se genera el error, para eso añadimos en la primera línea de la función del controlador Configure::write(’debug’,0); , función que lo que hace es cambiar el nivel de debug a modo producción, donde no se muestra ningún mensaje, ni de error ni warnings.
- Existencia de caracteres de salto de línea o espacios después del símbolo de fín de archivo php, ?>. Para esto hay que comprobar todos los ficheros de nuestro proyecto y borrar todos los caracteres o saltos de línea que haya al final del fichero. Esta suele ser la razón más habitual para este error por lo que debemos comprobar todos los ficheros antes de tirar la toalla con tcpdf.

No. 1 — Julio 14th, 2009 a 6:57 am
Me alegro. Pensé que la función render ya tomaría correctamente ese segundo argumento como layout. En este caso entonces creo que ya no será necesario que se lo pases a la función.
No. 2 — Julio 14th, 2009 a 1:12 pm
Hola nuria, elimine el parametro que me decias y si funciona igual, la verdad no entiendo por que no estaba tomando el layout en el render…..
No. 3 — Julio 14th, 2009 a 2:05 pm
¿Qué tal Darwin?.
El parámetro layout de $this->layout y de $this->render no es el mismo.
En el primer caso, es un parámetro del controlador y con el se indica el layout que se utilizará como base para mostrar los contenidos generados por todas las funciones del controlador.
En el segundo caso es un parámetro de la vista, con este se indica el layout que se utilizará para renderizar el contenido generado por la vista en concreto.
La idea es que habrá un layout general, que es el del controlador ($this->layout) y en el que caso del tcpdf llevará una cabecerá indicando que es un fichero pdf y un layout de las vistas, que se indicará en $this->render, que estará contenido en el primero.
El layout de $this->render, es útil en ciertas ocasiones, por ejemplo en una función ajax, en este caso querremos que al renderizarse no utilice el layout general. Por ello le pasaremos como parámetro el nombre de un layout que sea un archivo que únicamente muestre el contenido generado, sin cabeceras u otros componentes html.
No se si he conseguido explicarme. La idea en un principio es un poco compleja y algo dificil de entender.
Un saludo.
No. 4 — Julio 17th, 2009 a 1:40 pm
hola nuria gracias por tus comentario cada dia se aprende algo mas, horita estoy tratando de hacer que un select se cargue dependiendo de la seleccion de otro, por ejemplo tengo un select pais donde se encuentran los paises que tengo cargado en la base de datos, lo que quiero es que al seleccionar uno, en otro select que llamo estados se carguen los estados de ese pais claro esta tambien estan guardados en la base de datos. si tienes sugerencias o algun codigo que me pueda servir por favor publicalo, me seria muy util…….. Gracias de antemano
No. 5 — Julio 18th, 2009 a 1:24 am
¿Qué tal, Darwin?.
Existe una entrada en el blog que explica como se puede hacer esto que explicas. Échale un ojo a ver si te sirve y no dudes en preguntarnos todas las dudas que te surjan.
No. 6 — Julio 20th, 2009 a 6:38 pm
Gracias bernal, me sirvio de mucho el ejemplo que publicas, seguramente me leeras mas a menudo por aqui, por que a medida que voy avanzando en el proyecto siempre me surgen dudas y mas dudas…….
Please continue discussion on the forum: link