Aprende a generar archivos PDF en Symfony 3 con todas las funciones que wkhtmltopdf tiene para ofrecer usando KNPSnappyBundle.


¿Alguna vez ha intentado crear archivos PDF con gráficos complicados y sofisticados utilizando bibliotecas PHP como DomPdf o TCPDF, una tarea tan **** no? No hay muchas bibliotecas gratuitas del lado del servidor disponibles para generar gráficos, y las que están disponibles no crean gráficos tan agradables para ser incrustados como una imagen en nuestro PDF.

Una alternativa a los gráficos generados por el servidor es la generación de gráficos con Javascript (del lado del cliente). Hay muchas bibliotecas de Javascript que permiten crear gráficos sofisticados y puede recuperar su SVG (o una imagen base64) y devolverlo al servidor para incluirlos como una imagen en su PDF. Suena bien y funcional, sin embargo, esta solución tiene una desventaja. El SVG cambiará y causará problemas con las dimensiones más adelante en su PDF según la resolución del monitor de su usuario o las dimensiones de la ventana del navegador.

Algunas aplicaciones web, como solución,  generarán una vista html como respuesta en el navegador (que representará sus gráficos elegantes) y el usuario deberá usar (y saber cómo) el navegador para guardar (o imprimir) el contenido como PDF. . Sin embargo, como usuario, es posible que solo desee descargar un PDF con un solo botón, elija dónde desea guardarlo y eso es todo. No es una tarea fácil de hacer con bibliotecas como TCPDF, ¿no crees ?.

Problemas en todas partes, pero ahí es donde wkhtmltopdfresulta útil, ya que podrá escribir css, html y javascript de la misma manera que lo hace en un sitio web (con algunas limitaciones mínimas obviamente) y su PDF se verá increíble.

En este artículo aprenderás cómo implementar SnappyBundle en tu proyecto Symfony 3 y cómo generar archivos PDF de diferentes formas.

Requisitos

Necesitará estar wkhtmltopdfdisponible en su sistema y accesible en el símbolo del sistema. wkhtmltopdf es una herramienta de línea de comandos para convertir HTML en PDF y varios formatos de imagen utilizando el motor de renderizado Qt WebKit. Estos funcionan completamente "sin cabeza" y no requieren un servicio de visualización o visualización.

  • Windows: puede descargar un instalador para cada arquitectura (x86 y x64) en el área de instalación . Aunque puede cambiar la ruta del ejecutable wkhtmltopdf más adelante en el archivo config.yml , es recomendable tener wkhtmltopdf accesible como variable de entorno en su sistema (si no desea crear una variable de entorno para wkhtmltopdf, puede proporcionar la ruta completa al ejecutable más tarde). Puede leer cómo crear variables de entorno en Windows en este artículo .
  • Debian / Ubuntu: puede instalar la distribución de wkhtmltopdf directamente en la consola usando el siguiente comando:
$ sudo apt-get install wkhtmltopdf

Visite la página de inicio de wkhtmltopdf para obtener más información aquí .

1) Instalación y configuración de SnappyBundle

Snappy por sí mismo es un contenedor PHP (5.3+) para la utilidad de conversión wkhtmltopdf. Le permite generar archivos pdf o de imagen a partir de sus documentos html, utilizando el motor webkit. El KnpSnappyBundle  ofrece una integración simple para su proyecto Symfony .

Para instalar SnappyBundle en su proyecto, ejecute el siguiente comando del compositor:

composer require knplabs/knp-snappy-bundle

O agregue manualmente agregando el nombre del paquete en su archivo composer.json y luego ejecute composer install:

{
    "require": {
        "knplabs/knp-snappy-bundle": "~1.4"
    }
}

Cuando se complete la descarga, habilite el paquete agregando la siguiente línea a su kernel:

$bundles = [
    //..//
    new Knp\Bundle\SnappyBundle\KnpSnappyBundle(),
];      

Finalmente, solo necesita agregar la configuración básica en su config.ymlarchivo proporcionando y habilitando la ruta binaria de wkhtmltopdf.

Tenga en cuenta que, como se mencionó anteriormente, SnappyBundle requiere wkhtmltopdf para funcionar, por lo tanto, debemos proporcionar en la opción binaria de config.yml la ruta completa al ejecutable de wkhtmltopdf antes de usarlo; de lo contrario, enfrentará uno de los errores más conocidos:

Ruta binaria en Windows

Con el instalador predeterminado (y la configuración de instalación predeterminada) de wkhtmltopdf, debería haber una carpeta wkhtmltopdf / bin en los Archivos de programa de su partición principal con el ejecutable de wkhtmltopdf dentro, por lo que solo necesita proporcionar la ruta del siguiente ejemplo.

Sin embargo, si usó una instalación personalizada, simplemente cambie la ruta con la nueva que contiene el ejecutable wkhtmltopdf en la binarypropiedad.

# Windows configuration
knp_snappy:
    pdf:
        enabled:    true
        # If you have wkhtmltopdf as an environment variable you don't need to provide the
        # full path to the executable, use it in the same way as you use in the console
        #binary:  "wkhtmltopdf"
        binary:     "\"C:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltopdf.exe\""
        options:    []
    image:
        enabled:    true
        binary:     "\"C:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltoimage.exe\""
        options:    []

Ruta binaria en Linux / Unix como

Si instaló wkhtmltopdf usando el método apt-get, las rutas probablemente sean:

# app/config/config.yml
knp_snappy:
    pdf:
        enabled:    true
        binary:     /usr/local/bin/wkhtmltopdf
        options:    []
    image:
        enabled:    true
        binary:     /usr/local/bin/wkhtmltoimage
        options:    []

Cómo cambiar la configuración de generación de PDF

Puede cambiar dinámicamente la configuración de forma predeterminada en el archivo config.yml:

# config.yml
knp_snappy:
    pdf:
        enabled:    true
        binary:     "wkhtmltopdf"
        options:    
            no-outline: true
            page-size: LETTER
            # Recomendado para configurar UTF-8 como codificación predeterminada :)
            encoding: UTF-8

O dinámicamente en tu controlador (o servicio, etc.) usando PHP con el método setOption de snappy:

$snappy = $this->get('knp_snappy.pdf');
$snappy->setOption('no-outline', true);
$snappy->setOption('page-size','LETTER');
$snappy->setOption('encoding', 'UTF-8');

Puede ver una lista completa de todas las opciones disponibles para wkhtmltopdf en este documento .

Cambiar la ruta de caché de la generación de PDF

Snappy usa el método sys_get_temp_dir() de forma predeterminada para obtener la carpeta de archivos temporales del sistema para guardar los PDF, sin embargo, puede cambiar la ruta que desea cambiar con la propiedad carpeta_temporal:

# app/config/config.yml
knp_snappy:
    temporary_folder: %kernel.cache_dir%/snappy

El ejemplo anterior apuntaría a una carpeta en la carpeta de caché de su proyecto SF3 (var / cache / snappy).

2) Ejemplos

Renderizar PDF desde una URL web

El siguiente ejemplo representará la página de inicio de Our Code World en un PDF:

<?php

namespace sandboxBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;

class DefaultController extends Controller
{
    public function indexAction()
    {
        $snappy = $this->get('knp_snappy.pdf');
        $filename = 'myFirstSnappyPDF';
        $url = 'http://ourcodeworld.com';
        

        return new Response(
            $snappy->getOutput($url),
            200,
            array(
                'Content-Type'          => 'application/pdf',
                'Content-Disposition'   => 'inline; filename="'.$filename.'.pdf"'
            )
        );
    }
}

Renderizar PDF desde la URL de un proyecto (enrutamiento de Symfony)

Con 2 rutas en este ejemplo:

sandbox_homepage:
    path:     /
    defaults: { _controller: sandboxBundle:Default:index }
    
sandbox_pdfexample:
    path:     /pdf-example
    defaults: { _controller: sandboxBundle:Default:pdf }

Y el controlador:

<?php

namespace sandboxBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

class DefaultController extends Controller
{
    public function indexAction()
    {
        return $this->render('sandboxBundle:Default:index.html.twig',[
            
        ]);
    }
    
    /**
     *  Renderizar en un PDF la URL de sandbox_homepage
     * @return Response
     */
    public function pdfAction()
    {
        $snappy = $this->get('knp_snappy.pdf');
        $filename = 'myFirstSnappyPDF';
        
        // ¡usa la ruta absoluta!
        $pageUrl = $this->generateUrl('sandbox_homepage', array(), UrlGeneratorInterface::ABSOLUTE_URL);
        
        return new Response(
            $snappy->getOutput($pageUrl),
            200,
            array(
                'Content-Type'          => 'application/pdf',
                'Content-Disposition'   => 'inline; filename="'.$filename.'.pdf"'
            )
        );
    }
}

Nota: el host que se utiliza al generar una URL absoluta se detecta automáticamente mediante el objeto Request actual. Cuando se generan URL absolutas desde fuera del contexto web (por ejemplo, en un comando de consola), esto no funciona. Consulte Cómo generar URL desde la consola para aprender a resolver este problema.

Renderizar PDF desde el HTML de una vista de Twig

<?php

namespace sandboxBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;

class DefaultController extends Controller
{
    public function indexAction()
    {
        $snappy = $this->get('knp_snappy.pdf');
        
        $html = $this->renderView('sandboxBundle:Default:template.html.twig', array(
            //.. Envíe algunos datos a su vista si necesita//
        ));
        
        $filename = 'myFirstSnappyPDF';

        return new Response(
            $snappy->getOutputFromHtml($html),
            200,
            array(
                'Content-Type'          => 'application/pdf',
                'Content-Disposition'   => 'inline; filename="'.$filename.'.pdf"'
            )
        );
    }
}

Devolver respuesta en PDF para que se vea en el navegador

<?php

namespace sandboxBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;

class DefaultController extends Controller
{
    public function indexAction()
    {
        $snappy = $this->get('knp_snappy.pdf');
        
        $html = '<h1>Hello</h1>';
        
        $filename = 'myFirstSnappyPDF';

        return new Response(
            $snappy->getOutputFromHtml($html),
            200,
            array(
                'Content-Type'          => 'application/pdf',
                'Content-Disposition'   => 'inline; filename="'.$filename.'.pdf"'
            )
        );
    }
}

Devolver la respuesta en PDF como archivo adjunto (descargar la respuesta)

<?php

namespace sandboxBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;

class DefaultController extends Controller
{
    public function indexAction()
    {
        $snappy = $this->get('knp_snappy.pdf');
        
        $html = '<h1>Hello</h1>';
        
        $filename = 'myFirstSnappyPDF';

        return new Response(
            $snappy->getOutputFromHtml($html),
            200,
            array(
                'Content-Type'          => 'application/pdf',
                'Content-Disposition'   => 'attachment; filename="'.$filename.'.pdf"'
            )
        );
    }
}

Ejemplo básico de las principales características (y ventajas) de wkhtmltopdf

Simplemente trabaje de la forma en que lo hace en el navegador, wkhtmltopdfserá el encargado de convertirlo a pdf. En este ejemplo, vamos a generar un PDF con algunas características básicas como el uso de javascript, svg, etc.

<h1>{{title}}</h1>
<p>This is my awesome first PDF generated using Snappy in my Symfony 3 project.</p>
<p>UTF-8 Test : κ?σμε</p>
<p>Image : </p><br>
<img height="200" src="http://ourcodeworld.com/resources/img/ocw-empty.png"/>
<p>SVG Example : </p><br>
<div>
    <!-- No olvides darle ancho y alto a tu svg
        usar SVG plano directamente(<svg>content</svg>)
        EJEMPLO de SVG de tigre : https://upload.wikimedia.org/wikipedia/commons/f/fd/Ghostscript_Tiger.svg
    -->
</div>
<p>Canvas Example with Javascript: </p><br>
<div>
    <canvas id="myCanvas" width="300" height="200"/>
</div>
<hr>
<span id="dinamic-content"></span>
<script>
    document.getElementById("dinamic-content").innerHTML = 'This string has been appended using javascript';
    
    // Dibujar círculo sobre lienzo
    var c = document.getElementById("myCanvas");
    var ctx = c.getContext("2d");
    ctx.beginPath();
    ctx.arc(100,75,50,0,2*Math.PI);
    ctx.stroke();
</script>

Vamos a devolver el PDF generado al navegador con un simple controlador:

<?php

namespace sandboxBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;

class DefaultController extends Controller
{
    public function indexAction()
    {
        $snappy = $this->get('knp_snappy.pdf');
        
        $html = $this->renderView('sandboxBundle:Default:template.html.twig', array(
            'title' => 'Hello World !'
        ));
        
        $filename = 'myFirstSnappyPDF';

        return new Response(
            $snappy->getOutputFromHtml($html),
            200,
            array(
                'Content-Type'          => 'application/pdf',
                'Content-Disposition'   => 'inline; filename="'.$filename.'.pdf"'
            )
        );
    }
}

Y la salida en PDF debería verse así:

SnappyBundle PDF from a twig view - javascript

Conclusiones

  • Necesita wkhtmltopdf instalado en su máquina. Snappy es solo un contenedor que le facilita la generación de PDF fácilmente con un par de líneas de PHP. Si no eres amigo de las configuraciones de instalación, puedes usar una versión "portátil" en tu proyecto usando wkhtmltopdf como una dependencia del compositor .
  • wkhtmltopdfle permite crear imágenes (capturas de pantalla) a partir de URL web (o HTML simple) y SnappyBundle tiene un contenedor incluido para ello, lea más sobre esta función  en la documentación.
  • Gracias a wkhtmltopdf, puede usar Javascript para modificar su contenido HTML para generar un PDF, lo que resulta útil para mostrar cuadros o gráficos en un PDF con código del lado del cliente (bibliotecas como Highcharts, D3.js, etc.).

Que te diviertas ❤️!


Interesado en la programación desde los 14 años, Carlos es un programador autodidacta, fundador y autor de la mayoría de los artículos de Our Code World.

Conviertete en un programador más sociable

Patrocinadores