Generar PDF en Laravel

Índice de contenidos

Introducción

Para generar archivos PDFs desde PHP existen muchas librerías como: mPDF, FPDF, html2fpdf, Dompdf, TCPDF, FPDI, Zend_Pdf y otros. En este articulo voy a explicar como generar PDF en Laravel con la librería mPDF, porque ya tengo un buen tiempo utilizando esta librería y no he tenido muchos problemas para generar archivos PDFs.

En este ejemplo voy a realizar un diseño de una boleta de venta, el diseño sería así:

generar PDF en Laravel

Y también haremos el diseño físico, para que se pueda imprimir en una hoja pre-impresa.

generar PDF en Laravel

Entorno de trabajo

  • PHP 7.1
  • Servidor Web Apache/2.4.6
  • Framework Laravel 5.5 funcionando correctamente
  • Sistema Operativo CentOS Linux release 7.3 (Vagrant)
  • MPDF 7.0

Instalar Mpdf en Laravel

Desde la terminal ejecutamos el siguiente comando.

composer require mpdf/mpdf

Crear el controlador

Ahora creamos nuestro controlador

php artisan make:controller PdfController

El contenido del archivo PdfController.php

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Mpdf\Mpdf;
 
class PdfController extends Controller
{
    public function getIndex()
    {
        return view('pdf.index');
    }
    public function getGenerar(Request $request)
    {
        $accion = $request->get('accion');
        $tipo = $request->get('tipo');
        return $this->pdf($accion,$tipo);
    }
    public function pdf($accion='ver',$tipo='digital')
    {
        $ruc = "10072486893";
        $numero = "00000412";
        $nombres = "DAVID OLIVARES PEÑA";
        $dia = "09";
        $mes = "04";
        $ayo = "17";
        $direccion = "Lima Perú";
        $dni = "23918745";
        $total = 0;
        $articulos = [
            [
                "cantidad" => 3,
                "descripcion" => "COCINA A GAS",
                "precio" => 400.00,
                "importe" => 1200,
            ],
            [
                "cantidad" => 1,
                "descripcion" => "PLANCHA",
                "precio" => 85.00,
                "importe" => 85.00,
            ],
        ];
        foreach ($articulos as $key => $value) {
            $total += $value["importe"];
            $articulos[$key]["precio"] = number_format($value["precio"],2,'.',' ');;
            $articulos[$key]["importe"] = number_format($value["importe"],2,'.',' ');;
 
        }
        $total = number_format($total,2,'.',' ');
 
        $data['ruc'] = $ruc;
        $data['numero'] = $numero;
        $data['nombres'] = $nombres;
        $data['dia'] = $dia;
        $data['mes'] = $mes;
        $data['ayo'] = $ayo;
        $data['direccion'] = $direccion;
        $data['dni'] = $dni;
        $data['articulos'] = $articulos;
        $data['total'] = $total;
        $data['tipo'] = $tipo;
 
        if($accion=='html'){
            return view('pdf.generar',$data);
        }else{
            $html = view('pdf.generar',$data)->render();
        }
        $namefile = 'boleta_de_venta_'.time().'.pdf';
 
        $defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults();
        $fontDirs = $defaultConfig['fontDir'];
 
        $defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults();
        $fontData = $defaultFontConfig['fontdata'];
        $mpdf = new Mpdf([
            'fontDir' => array_merge($fontDirs, [
                public_path() . '/fonts',
            ]),
            'fontdata' => $fontData + [
                'arial' => [
                    'R' => 'arial.ttf',
                    'B' => 'arialbd.ttf',
                ],
            ],
            'default_font' => 'arial',
            // "format" => "A4",
            "format" => [264.8,188.9],
        ]);
        // $mpdf->SetTopMargin(5);
        $mpdf->SetDisplayMode('fullpage');
        $mpdf->WriteHTML($html);
        // dd($mpdf);
        if($accion=='ver'){
            $mpdf->Output($namefile,"I");
        }elseif($accion=='descargar'){
            $mpdf->Output($namefile,"D");
        }
    }
}

En nuestro route agregamos lo siguiente

Route::get('pdf','PdfController@getIndex');
Route::get('pdf/generar','PdfController@getGenerar');

Crear la vista

Vista resources/views/pdf/index.blade.php es una página que tendrá los link para ver los PDFs, son 4 links, 2 para ver el PDF en el navegador web la versión digital y física y otros 2 para descargar los PDFs.

<!DOCTYPE html>
<html>
<head>
    <title>PDF</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <h3>GENERAR PDF CON LARAVEL</h3>
        <ul>
            <li>
                <a target="_blank" href="{{ action('PdfController@getGenerar',['accion'=>'ver','tipo'=>'digital']) }}">Ver PDF digital</a>
            </li>
            <li>
                <a target="_blank" href="{{ action('PdfController@getGenerar',['accion'=>'ver','tipo'=>'fisico']) }}">Ver PDF físico</a>
            </li>
            <li>
                <a target="_blank" href="{{ action('PdfController@getGenerar',['accion'=>'descargar','tipo'=>'digital']) }}">Descargar PDF digital</a>
            </li>
            <li>
                <a target="_blank" href="{{ action('PdfController@getGenerar',['accion'=>'descargar','tipo'=>'fisico']) }}">Descargar PDF físico</a>
            </li>
        </ul>
    </div>
</body>
</html>

Vista resources/views/pdf/generar.blade.php el diseño del PDF

<!DOCTYPE html>
<html>
<head>
    <title>BOLETA DE VENTA</title>
    <style type="text/css">
    body{
        font-size: 16px;
        font-family: "Arial";
    }
    table{
        border-collapse: collapse;
    }
    td{
        padding: 6px 5px;
        font-size: 15px;
    }
    .h1{
        font-size: 21px;
        font-weight: bold;
    }
    .h2{
        font-size: 18px;
        font-weight: bold;
    }
    .tabla1{
        margin-bottom: 20px;
    }
    .tabla2 {
        margin-bottom: 20px;
    }
    .tabla3{
        margin-top: 15px;
    }
    .tabla3 td{
        border: 1px solid #000;
    }
    .tabla3 .cancelado{
        border-left: 0;
        border-right: 0;
        border-bottom: 0;
        border-top: 1px dotted #000;
        width: 200px;
    }
    .emisor{
        color: red;
    }
    .linea{
        border-bottom: 1px dotted #000;
    }
    .border{
        border: 1px solid #000;
    }
    .fondo{
        background-color: #dfdfdf;
    }
    .fisico{
        color: #fff;
    }
    .fisico td{
        color: #fff;
    }
    .fisico .border{
        border: 1px solid #fff;
    }
    .fisico .tabla3 td{
        border: 1px solid #fff;
    }
    .fisico .linea{
        border-bottom: 1px dotted #fff;
    }
    .fisico .emisor{
        color: #fff;
    }
    .fisico .tabla3 .cancelado{
        border-top: 1px dotted #fff;
    }
    .fisico .text{
        color: #000;
    }
    .fisico .fondo{
        background-color: #fff;
    }
    @if($tipo=='fisico')
    #logo{
        display: none;
    }
    @endif
</style>
</head>
<body>
    <div class="@if($tipo=='fisico') fisico @endif">
        <table width="100%" class="tabla1">
            <tr>
                <td width="73%" align="center"><img id="logo" src="{{ asset('images/logo_dmc.png') }}" alt="" width="255" height="57"></td>
                <td width="27%" rowspan="3" align="center" style="padding-right:0">
                    <table width="100%">
                        <tr>
                            <td height="50" align="center" class="border"><span class="h2">RUC: {{ $ruc }}</span></td>
                        </tr>
                        <tr>
                            <td height="40" align="center" class="border fondo"><span class="h1">BOLETA DE VENTA</span></td>
                        </tr>
                        <tr>
                            <td height="50" align="center" class="border">001-<span class="text">{{ $numero }}</span></td>
                        </tr>
                    </table>
                </td>
            </tr>
            <tr>
                <td align="center">Jr. Santander Nro. 340 Jesus María - Lima</td>
            </tr>
            <tr>
                <td align="center">Telf.: (01) 364-2547 Cel.: 985-748514</td>
            </tr>
        </table>
        <table width="100%" class="tabla2">
            <tr>
                <td width="11%">Señor (es):</td>
                <td width="37%" class="linea"><span class="text">{{ $nombres }}</span></td>
                <td width="5%">&nbsp;</td>
                <td width="13%">&nbsp;</td>
                <td width="4%">&nbsp;</td>
                <td width="7%" align="center" class="border fondo"><strong>DÍA</strong></td>
                <td width="8%" align="center" class="border fondo"><strong>MES</strong></td>
                <td width="7%" align="center" class="border fondo"><strong>AÑO</strong></td>
            </tr>
            <tr>
                <td>Dirección:</td>
                <td class="linea"><span class="text">{{ $direccion }}</span></td>
                <td>DNI:</td>
                <td class="linea"><span class="text">{{ $dni }}</span></td>
                <td>&nbsp;</td>
                <td align="center" class="border"><span class="text">{{ $dia }}</span></td>
                <td align="center" class="border"><span class="text">{{ $mes }}</span></td>
                <td align="center" class="border"><span class="text">{{ $ayo }}</span></td>
            </tr>
        </table>
        <table width="100%" class="tabla3">
            <tr>
                <td align="center" class="fondo"><strong>CANT.</strong></td>
                <td align="center" class="fondo"><strong>DESCRIPCIÓN</strong></td>
                <td align="center" class="fondo"><strong>P. UNITARIO</strong></td>
                <td align="center" class="fondo"><strong>IMPORTE</strong></td>
            </tr>
            @for ($i = 0; $i < 6; $i++)
            @if (array_key_exists($i, $articulos))
            <tr>
                <td width="7%" align="center"><span class="text">{{ $articulos[$i]['cantidad'] }}</span></td>
                <td width="59%"><span class="text">{{ $articulos[$i]['descripcion'] }}</span></td>
                <td width="16%" align="right"><span class="text">{{ $articulos[$i]['precio'] }}</span></td>
                <td width="18%" align="right"><span class="text">{{ $articulos[$i]['importe'] }}</span></td>
            </tr>
            @else
            <tr>
                <td width="7%">&nbsp;</td>
                <td width="59%">&nbsp;</td>
                <td width="16%">&nbsp;</td>
                <td width="18%" align="left">&nbsp;</td>
            </tr>
            @endif
            @endfor
            <tr>
                <td style="border:0;">&nbsp;</td>
                <td style="border:0;">&nbsp;</td>
                <td align="right"><strong>TOTAL S/.</strong></td>
                <td align="right"><span class="text">{{ $total }}</span></td>
            </tr>
            <tr>
                <td style="border:0;">&nbsp;</td>
                <td align="center" style="border:0;">
                    <table width="200" border="0" cellpadding="0" cellspacing="0">
                        <tr>
                            <td align="center" class="cancelado">CANCELADO</td>
                        </tr>
                    </table>
                </td>
                <td style="border:0;">&nbsp;</td>
                <td align="center" style="border:0;" class="emisor"><strong>EMISOR</strong></td>
            </tr>
        </table>
    </div>
</body>
</html>

Probar el ejemplo

Ahora ingresamos a http://localhost/pdf veremos unos enlaces para ver y descargar los archivos PDFs.

Generar PDF en Laravel

Agregar un tipo de letra al PDF

Vamos agregar la fuente Arial, para eso vamos a ubicar los archivos arial.ttf y arialbd.ttf en la carpeta public/fonts, luego debemos establecer la ruta completa public_path() . '/fonts' en la opción fontDir.
En la opción fontdata le agregamos los nombres de los archivos arial.ttf y arialbd.ttf. Las opciones R y B son los estilos Regular y Bold del tipo de letra.

        $defaultConfig = (new \Mpdf\Config\ConfigVariables())->getDefaults();
        $fontDirs = $defaultConfig['fontDir'];
 
        $defaultFontConfig = (new \Mpdf\Config\FontVariables())->getDefaults();
        $fontData = $defaultFontConfig['fontdata'];
        $mpdf = new Mpdf([
            'fontDir' => array_merge($fontDirs, [
                public_path() . '/fonts',
            ]),
            'fontdata' => $fontData + [
                'arial' => [
                    'R' => 'arial.ttf',
                    'B' => 'arialbd.ttf',
                ],
            ],
            'default_font' => 'arial',
            "format" => "A4",            
        ]);

Dimensiones del archivo PDF

En la opción format agregamos la dimensión del archivo PDF, los valores predeterminados pueden ser: A4, A3, A2, etc.. El A4 por defecto es de orientación portrait, A4-L sería orientación landscape.
También podemos pasarle un array con el ancho y altura en milimetros "format" => [264.8,188.9]

Descargar el archivo PDF

Para descargar el archivo le pasamos la opción D

$mpdf->Output("mi_archivo.pdf","D");

¿Cómo guardar un archivo PDF?

Para guardar el archivo le pasamos la opción F

$mpdf->Output("/path/mi_archivo.pdf","F");

Agregar una nueva página al archivo PDF

Cada vez que usamos la función $mpdf->AddPage(); se agrega una nueva página al archivo PDF.

$mpdf = new \Mpdf\Mpdf();
$mpdf->WriteHTML('Introducción');
$mpdf->AddPage();
$mpdf->WriteHTML('Aquí nueva página');

Establecer Header y Footer

Si nuestro diseño tiene un Header y Footer que se repite en todas las páginas del archivo PDF, podemos usar las siguientes funciones:

$mpdf->SetHTMLHeader('Código HTML');
$mpdf->SetHTMLFooter('Código HTML');

Conclusión

Tenemos muchas librerías para generar PDF en Laravel, en lo particular MPDF es mi preferido, con otras librerías he tenido problemas, pero con MPDF no he tenido problemas, la única recomendación es no usar atributos CSS complejas, ni usar muchas clases por ejemplo: .wrapper .text li a, ser más simples en el uso de código CSS.

(Visited 25.662 times, 1 visits today)
Puedes saltar al final y dejar una respuesta. Hacer ping no está permitido actualmente.