dompdf icon indicating copy to clipboard operation
dompdf copied to clipboard

PDF Generation Too Slow

Open intellisoftwaremx opened this issue 8 months ago • 4 comments

Hello everyone.

Maybe I'm doing something wrong, but I'm generating a PDF based on a blade view in Laravel 10. It's a very simple view with only two images, not very large. The problem is that the generation process takes more than 20 seconds. If I remove the images, the generation is instantaneous.

The PDF is generated and saved to disk. As I mentioned, if I do it without the images, it generates it instantly; if I add the images, it takes more than 20 seconds to generate. Am I doing something wrong? Is there any way to speed up this process?

This is my generation code.

            $data = [
                'documento' => $documento,
                'documentoPasos' => $documentoPasos,
                'documentoPasoUsuarios' => $documentoPasoUsuarios,
                'documentoArchivos' => $documentoArchivos            
            ];

            // Generar el PDF con el tamaño y orientación correctos

            $options = new \Dompdf\Options();
            $options->set('isHtml5ParserEnabled', true);
            $options->set('isRemoteEnabled', true); // Permitir imágenes externas
            $options->set('defaultFont', 'Arial'); // Establecer la fuente
            $options->set('defaultPaperSize', 'letter');

            $pdf = new \Dompdf\Dompdf($options);
            $pdf->setPaper('letter', 'portrait');
            $pdf->loadHtml(view('operacion.documento.documento_auditoria_show', $data)->render());
            $pdf->render();

            // Grabo el PDF en disco temporalmente para firmarlo

            Storage::disk('local')->put('temp/documento_temporal_' . $documento->id . '.pdf', $pdf->output());

Thanks.

intellisoftwaremx avatar Jun 17 '25 14:06 intellisoftwaremx

Would need to see the actual HTML + CSS involved and possibly a sample of the images. Generally, the larger the image the more time required to process. Usage and system configuration is also important since processing is different depending on whether or not the image is used for a background and whether or not you have IMagick/GMagick enabled.

There are also some improvements we could make to PNG image processing as noted in #3359.

bsweeney avatar Jun 17 '25 14:06 bsweeney

Here's the blade:

@extends("common.master_report")

@section('title', 'Business Box BPM | Documento de Auditoria')

@section("report")

    @php

        use App\Models\Documento;
        use Carbon\Carbon;

        use function App\Helpers\BusinessBox\convertirMinutos;

    @endphp

    <style>

        body {
            font-size: 10px !important;
        }

        td {
            line-height: 0.9 !important;
        }

    </style>

    <div class="container-fluid" style="width: 100%">

        <table class="mt-2" style="width: 100%;">
            <tr>
                <td style="text-align: left;">
                    <img src="{{ asset('backend/assets/images/logo-pdf.png') }}" alt="logo-dark" height="60">
                </td>
                <td style="text-align: right;">
                    @if ($documento->estatus_id == ESTATUS_DOCUMENTO_AUTORIZADO)
                        <img src="{{ asset('backend/assets/images/sello_aprobado_redondo-pdf.png') }}" alt="sello-aprobado" height="160">
                    @else
                        <img src="{{ asset('backend/assets/images/sello_rechazado_redondo-pdf.png') }}" alt="sello-rechazado" height="160">
                    @endif
                </td>
            </tr>
        </table>

        <div class="text-center" style="font-size: 24px">
            <span>Documento de Auditoria</span>
        </div>

        <div class="table-responsive mt-3">

            {{-- Tabla para los datos del proceso --}}

            <table id="Informacion-proceso" style="width: 100%">

                <thead class="bg-light">

                    <tr>
                        <th width="33%"><strong>INFORMACION DEL PROCESO</strong></th>
                        <th width="33%"></th>
                        <th width="33%"></th>
                    </tr>

                </thead>

                <tbody>

                    <tr>
                        <td><strong>Empresa: </strong> {{ $documento->empresa->razon_social }}</td>
                        <td></td>
                        <td></td>
                    </tr>
                    <tr>
                        <td><strong>Documento ID: </strong> {{ $documento->id }}</td>
                        <td><strong>Folio: </strong> {{ $documento->folio }}</td>
                        <td><strong>Estatus: </strong> {{ $documento->estatus->descripcion }}</td>
                    </tr>
                    <tr>
                        <td colspan="2"><strong>Titulo: </strong> {{ $documento->titulo }}</td>
                        <td><strong>Origen del Documento: </strong>{{ $documento->documento_origen }}</td>
                    </tr>
                    <tr>
                        <td><strong>No. de Pasos: </strong> {{ $documento->documentoPasos->count() }}</td>
                        <td><strong>No. de Firmas: </strong>{{ Documento::numeroUsuariosFirmantes($documento->id) }}</td>
                        <td><strong>Usuario Solicitante:</strong></td>
                    </tr>
                    <tr>
                        <td><strong>Fecha de Creacion: </strong>{{ date("F j, Y @ H:i:s" , strtotime($documento->fecha_open)) }}</td>
                        <td></td>
                        <td>{{ $documento->userCreatedBy->name }}</td>
                    </tr>
                    <tr>
                        <td><strong>Fecha de Termino: </strong>{{ date("F j, Y @ H:i:s" , strtotime($documento->fecha_close)) }}</td>
                        <td></td>
                        <td>{{ $documento->userCreatedBy->puesto }}</td>
                    </tr>
                    <tr>
                        <td><strong>Tiempo Transcurrido:</strong> {{ convertirMinutos((strtotime($documento->fecha_close) - strtotime($documento->fecha_open)) / 60) }}</td>
                        <td></td>
                        <td>{{ $documento->userCreatedBy->email }}</td>
                    </tr>                       
                    <tr>
                        <td colspan="2"><strong>Comentarios: </strong> {{ $documento->comentarios }}</td>
                        <td><strong>Monto a Autorizar: </strong>{{"$ " . number_format($documento->monto_autorizar, 2, ".", ",") }}</td>
                    </tr>

                </tbody>

            </table>

            {{-- Tabla para los datos del documento fuente --}}

            <table id="Informacion-documento" class="mt-4" style="width: 100%">

                <thead class="bg-light">

                    <tr>
                        <th width="33%"><strong>INFORMACION DE LA DEFINICION</strong></th>
                        <th width="33%"></th>
                        <th width="33%"></th>
                    </tr>

                </thead>

                <tbody>

                    <tr>
                        <td colspan="2"><strong>Titulo: </strong>{{ $documento->definicion_titulo }}</td>
                        <td><strong>Categoria: </strong> {{ $documento->categoria->descripcion }}</td>
                    </tr>
                    <tr>
                        <td><strong>Tipo de Documento: </strong> {{ $documento->documentoTipo->descripcion }}</td>
                        <td><strong>Solicitud Autorizacion Tipo: </strong>{{ $documento->solicitudAutorizacionTipo->descripcion ?? "N/D" }}</td>
                        <td></td>
                    </tr>
                    <tr>
                        <td><strong>Habilitar Archivos: </strong> {{ $documento->definicion_habilitar_archivos ? "Si" : "No" }}</td>
                        <td><strong>Habilitar Comentarios: </strong>{{ $documento->definicion_habilitar_comentarios ? "Si" : "No" }}</td>
                        <td><strong>Nivel de Seguridad: </strong>{{ $documento->definicion_confidencial ? "Confidencial" : "Publico" }}</td>
                    </tr>
                    <tr>
                        <td colspan="2"><strong>Comentarios: </strong> {{ $documento->definicion_comentarios }}</td>
                        <td></td>
                    </tr>

                </tbody>

            </table>

            {{-- Despliego los archivos relacionados con el documento, si es que hay --}}
            
            @if($documento->documentoArchivos->count() > 0)

                <table id="Informacion-archivos" class="mt-4" style="width: 100%">

                    <thead class="bg-light">

                        <tr>
                            <th colspan="10">ARCHIVOS RELACIONADOS</th>
                        </tr>
                        <tr>
                            <th width="30px">Id</th>
                            <th width="70px">Paso</th>
                            <th width="60px">Folio</th>
                            <th width="60px">Version</th>
                            <th width="200px">Fecha</th>
                            <th>Titulo</th>
                            <th width="20px">Tipo</th>
                            <th class="text-end" width="100px">Tamaño</th>
                        </tr>

                    </thead>

                    {{-- Aqui se van a colocar las partidas dinamicamente --}}

                    <tbody>

                        @foreach ($documentoArchivos as $item)

                            <tr>
                                <td>{{ $item->id }}</td>
                                <td>{{ $item->documento_paso_id == 0 ? "Inicio" : $item->documento_paso_id }}</td>
                                <td>{{ $item->folio }}</td>
                                <td>{{ $item->version }}</td>
                                <td>{{ date("F j, Y @ H:i:s" , strtotime($item->created_at)) }}</td>
                                <td>{{ $item->titulo }}</td>
                                <td>{{ $item->archivoTipo->extension }}</td>
                                <td class="text-end">{{ number_format((($item->tamano / 1024) / 1024), 2, ".", ",") }} Mb.</td>
                            </tr>

                        @endforeach
                    
                    </tbody>

                </table>
            
            @endif

            {{-- Tabla para los datos de los pasos del documento --}}

            <table id="Informacion-pasos" class="mt-4" style="width: 100%">

                <thead class="bg-light">

                    <tr>
                        <th width="33%"><strong>INFORMACION DE LOS PASOS</strong></th>
                        <th width="33%"></th>
                        <th width="33%"></th>
                    </tr>

                </thead>

                <tbody>

                    @foreach ($documentoPasos->sortBy("paso") as $paso )

                        @php
                            
                            $tipoPaso = "Paso Asignacion Simple";

                            if ($paso->definicion_tipo_paso == TIPO_PASO_GRUPO_CUALQUIERA) {
                                $tipoPaso = "Paso Grupal Cualquiera";
                            } else if ($paso->definicion_tipo_paso == TIPO_PASO_GRUPO_TODOS) {
                                $tipoPaso = "Paso Grupal Todos";
                            }

                        @endphp

                        <tr>
                            <td><strong>Paso ID: </strong>{{ $paso->id }} / {{ $tipoPaso }}</td>
                            <td><strong>Tipo de Paso: </strong>{{ $paso->documento_tipo_paso == DOCUMENTO_TIPO_PASO_NORMAL ? "Normal" : "Reasignacion (Paso ID: " . $paso->referencia_reasignacion_id . ")" }}</td>
                            <td><strong>Estatus del Paso: </strong> {{ $paso->estatus_id == ESTATUS_PASO_EN_PROCESO || $paso->estatus_id == ESTATUS_PASO_PENDIENTE ? "Paso No Accionado" : $paso->estatus->descripcion }}</td>
                        </tr>                    

                        @if($paso->documentoPasoReasignacion->count() > 0)

                            @foreach($paso->documentoPasoReasignacion as $reasignacion)

                                <tr>
                                    <td><strong>Fecha Asigancion: </strong> {{ !is_null($reasignacion->fecha_asignacion) ? date("F j, Y @ H:i:s" , strtotime($reasignacion->fecha_asignacion)) : "N/D" }}</td>
                                    <td><strong>Fecha Reasignacion: </strong> {{ !is_null($reasignacion->fecha_reasignacion) ? date("F j, Y @ H:i:s" , strtotime($reasignacion->fecha_reasignacion)) : "N/D" }}</td>
                                    <td><strong>Tiempo Transcurrido: </strong> {{ $reasignacion->tiempo_transcurrido ? convertirMinutos($reasignacion->tiempo_transcurrido) : 0 }}</td>
                                </tr>
                                
                            @endforeach
                        
                        @endif

                        <tr>
                            <td><strong>Fecha Asignacion: </strong> {{ !is_null($paso->fecha_asignacion) ? date("F j, Y @ H:i:s" , strtotime($paso->fecha_asignacion)) : "N/D" }}</td>
                            <td><strong>Fecha Accion: </strong> {{ !is_null($paso->fecha_accion) ? date("F j, Y @ H:i:s" , strtotime($paso->fecha_accion)) : "N/D" }}</td>
                            <td><strong>Tiempo Transcurrido: </strong> {{ $paso->tiempo_transcurrido ? convertirMinutos($paso->tiempo_transcurrido) : 0 }}</td>
                        </tr>

                        <tr>
                            <td colspan="3" style="border-bottom: 0.5px solid #ccc;"></td>
                        </tr>

                        {{-- Se presentan los datos de los autorizadores del paso --}}
                        
                        @foreach ($documentoPasoUsuarios->filter(fn($usuarios) => $usuarios->documento_paso_id == $paso->id) as $usuario)

                            <tr>
                                <td><strong>{{ $usuario->usuario->name }}</strong></td>
                                <td><strong>Fecha Asignacion: </strong> {{ !is_null($usuario->fecha_asignacion) ? date("F j, Y @ H:i:s" , strtotime($usuario->fecha_asignacion)) : "N/D" }}</td>
                                <td><strong>Estatus Autorizador: </strong> {{ $usuario->estatus_id == ESTATUS_PASO_USUARIO_EN_PROCESO || $usuario->estatus_id == ESTATUS_PASO_USUARIO_PENDIENTE ? "Accion No Tomada" : $usuario->estatus->descripcion }}</td>
                            </tr>                    
                            <tr>
                                <td>{{ $usuario->usuario->puesto }}</td>
                                <td><strong>Fecha Accion: </strong> {{ !is_null($usuario->fecha_accion) ? date("F j, Y @ H:i:s" , strtotime($usuario->fecha_accion)) : "N/D" }}</td>
                            </tr>                    
                            <tr>
                                <td>{{ $usuario->usuario->email }}</td>
                                <td><strong>Tiempo Transcurrido: </strong> {{ !is_null($usuario->tiempo_transcurrido) ? convertirMinutos($usuario->tiempo_transcurrido) : "N/D" }}</td>
                            </tr>   

                            <tr>
                                <td colspan="2"><strong>Comentarios: </strong> {{ $usuario->comentarios }}</td>
                                <td></td>
                            </tr>

                        @endforeach

                        <tr>
                            <td colspan="3" style="height: 20px;"></td>
                        </tr>
                        
                    @endforeach

                </tbody>

            </table>

            <div style="width: 100%; border-top: 0.5px solid #ccc; margin: 10px 0;"></div>

            @php
                $printTimestamp = new DateTime("now");
            @endphp

            <div class="text-end mb-4"><i>Fecha de Impresion: {{ $printTimestamp->format("F j, Y, g:i a") }}</i></div>

        </div>
        
    </div>

@endsection

And these are the images.

Image

Image

Image

They are PNG with transparent backgroud.

intellisoftwaremx avatar Jun 17 '25 14:06 intellisoftwaremx

BTW, I have changed the images from PNG with transparency to JPG and it still took 22 sec to generate the PDF.

intellisoftwaremx avatar Jun 17 '25 15:06 intellisoftwaremx

There's nothing in particular about the structure of the document that would be causing slower generation. I'm not a laravel user, but I believe the asset function generates a full URL with domain, meaning PHP is going through your web server. This should not normally be a cause for concern but maybe in your case that's where the slow down is happening. Why, though, I'm not sure.

bsweeney avatar Jun 18 '25 15:06 bsweeney