diff --git a/public/main/inc/lib/certificate.lib.php b/public/main/inc/lib/certificate.lib.php index 4a0502d0604..172b3ef3878 100644 --- a/public/main/inc/lib/certificate.lib.php +++ b/public/main/inc/lib/certificate.lib.php @@ -2,7 +2,6 @@ /* For licensing terms, see /license.txt */ -use Chamilo\CoreBundle\Component\Mpdf\SafeMpdfHttpClient; use Chamilo\CoreBundle\Entity\GradebookCategory; use Chamilo\CoreBundle\Entity\PersonalFile; use Chamilo\CoreBundle\Entity\ResourceFile; @@ -1007,32 +1006,7 @@ public function generateCustomCertificate(string $fileName = ''): string public function generatePdfFromCustomCertificate(): void { $orientation = api_get_setting('certificate.certificate_pdf_orientation'); - - $pdfOrientation = 'landscape'; - if (!empty($orientation)) { - $pdfOrientation = $orientation; - } - - $pageFormat = 'landscape' === $pdfOrientation ? 'A4-L' : 'A4'; - - // Instanciate mPDF directly to avoid blank pages generated by the default - // PDF class: format_pdf() sets mirrorMargins=1 (book layout) and the - // constructor hard-codes margin_header=8 / margin_footer=8 even when - // headers and footers are empty, which causes mPDF to insert blank - // odd/even pages around the single certificate page. - $mpdf = new \Mpdf\Mpdf([ - 'tempDir' => Container::getCacheDir(), - 'mode' => 'utf-8', - 'format' => $pageFormat, - 'orientation' => $pdfOrientation, - 'margin_left' => 0, - 'margin_right' => 0, - 'margin_top' => 0, - 'margin_bottom' => 0, - 'margin_header' => 0, - 'margin_footer' => 0, - ], SafeMpdfHttpClient::container()); - $mpdf->mirrorMargins = 0; + $pdfOrientation = !empty($orientation) ? $orientation : 'landscape'; // Safety: ensure HTML content is present; fetch from Resource if needed. if (empty($this->certificate_data['file_content'])) { @@ -1048,11 +1022,14 @@ public function generatePdfFromCustomCertificate(): void } } - @$mpdf->WriteHTML((string) $this->certificate_data['file_content']); - - $pdfName = api_replace_dangerous_char(get_lang('Certificates')); - $mpdf->Output($pdfName.'.pdf', \Mpdf\Output\Destination::DOWNLOAD); - exit; + // Single-page render with SSRF-guarded mPDF lives in the PDF class; it + // skips format_pdf()'s book layout to avoid blank pages around the + // single certificate page. + PDF::singlePageHtmlToPdfDownload( + (string) $this->certificate_data['file_content'], + get_lang('Certificates'), + $pdfOrientation + ); } /** diff --git a/public/main/inc/lib/pdf.lib.php b/public/main/inc/lib/pdf.lib.php index ae784246c10..d150015543c 100644 --- a/public/main/inc/lib/pdf.lib.php +++ b/public/main/inc/lib/pdf.lib.php @@ -52,6 +52,7 @@ public function __construct( $params['right'] = $params['right'] ?? 15; $params['top'] = $params['top'] ?? 30; $params['bottom'] = $params['bottom'] ?? 30; + $params['margin_header'] = $params['margin_header'] ?? 8; $params['margin_footer'] = $params['margin_footer'] ?? 8; $this->params['filename'] = $params['filename'] ?? api_get_local_time(); @@ -77,8 +78,8 @@ public function __construct( 'margin_right' => $params['right'], 'margin_top' => $params['top'], 'margin_bottom' => $params['bottom'], - 'margin_header' => 8, - 'margin_footer' => 8, + 'margin_header' => $params['margin_header'], + 'margin_footer' => $params['margin_footer'], ]; // Default value is 96 set in the mpdf library file config.php @@ -538,6 +539,44 @@ public function content_to_pdf( return $output_file; } + /** + * Renders single-page HTML (e.g. a certificate) to a downloadable PDF. + * + * Reuses the SSRF-guarded mPDF instance built by the constructor, with all + * margins set to 0 and without calling format_pdf(): this deliberately + * skips the book-layout decoration (mirrorMargins, header/footer margins) + * that would otherwise insert blank pages around a single-page document. + * + * @param string $html HTML content to render + * @param string $fileName Output file name (without extension) + * @param string $orientation 'landscape' or 'portrait' + * + * @throws MpdfException + */ + public static function singlePageHtmlToPdfDownload( + string $html, + string $fileName, + string $orientation = 'landscape' + ): void { + $pageFormat = 'landscape' === $orientation ? 'A4-L' : 'A4'; + $mpdfOrientation = 'landscape' === $orientation ? 'L' : 'P'; + + $pdf = new self($pageFormat, $mpdfOrientation, [ + 'left' => 0, + 'right' => 0, + 'top' => 0, + 'bottom' => 0, + 'margin_header' => 0, + 'margin_footer' => 0, + ]); + $pdf->pdf->mirrorMargins = 0; + + @$pdf->pdf->WriteHTML($html); + + $pdf->pdf->Output(api_replace_dangerous_char($fileName).'.pdf', Destination::DOWNLOAD); + exit; + } + /** * Gets the watermark from the platform or a course. *