Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 9 additions & 32 deletions public/main/inc/lib/certificate.lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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'])) {
Expand All @@ -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
);
}

/**
Expand Down
43 changes: 41 additions & 2 deletions public/main/inc/lib/pdf.lib.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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
Expand Down Expand Up @@ -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.
*
Expand Down
Loading