Skip to content

Commit 516d87e

Browse files
AngelFQCclaude
andauthored
Refactor: Reusable single-page PDF rendering method in PDF class
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 8468df8 commit 516d87e

2 files changed

Lines changed: 50 additions & 34 deletions

File tree

public/main/inc/lib/certificate.lib.php

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
/* For licensing terms, see /license.txt */
44

5-
use Chamilo\CoreBundle\Component\Mpdf\SafeMpdfHttpClient;
65
use Chamilo\CoreBundle\Entity\GradebookCategory;
76
use Chamilo\CoreBundle\Entity\PersonalFile;
87
use Chamilo\CoreBundle\Entity\ResourceFile;
@@ -1007,32 +1006,7 @@ public function generateCustomCertificate(string $fileName = ''): string
10071006
public function generatePdfFromCustomCertificate(): void
10081007
{
10091008
$orientation = api_get_setting('certificate.certificate_pdf_orientation');
1010-
1011-
$pdfOrientation = 'landscape';
1012-
if (!empty($orientation)) {
1013-
$pdfOrientation = $orientation;
1014-
}
1015-
1016-
$pageFormat = 'landscape' === $pdfOrientation ? 'A4-L' : 'A4';
1017-
1018-
// Instanciate mPDF directly to avoid blank pages generated by the default
1019-
// PDF class: format_pdf() sets mirrorMargins=1 (book layout) and the
1020-
// constructor hard-codes margin_header=8 / margin_footer=8 even when
1021-
// headers and footers are empty, which causes mPDF to insert blank
1022-
// odd/even pages around the single certificate page.
1023-
$mpdf = new \Mpdf\Mpdf([
1024-
'tempDir' => Container::getCacheDir(),
1025-
'mode' => 'utf-8',
1026-
'format' => $pageFormat,
1027-
'orientation' => $pdfOrientation,
1028-
'margin_left' => 0,
1029-
'margin_right' => 0,
1030-
'margin_top' => 0,
1031-
'margin_bottom' => 0,
1032-
'margin_header' => 0,
1033-
'margin_footer' => 0,
1034-
], SafeMpdfHttpClient::container());
1035-
$mpdf->mirrorMargins = 0;
1009+
$pdfOrientation = !empty($orientation) ? $orientation : 'landscape';
10361010

10371011
// Safety: ensure HTML content is present; fetch from Resource if needed.
10381012
if (empty($this->certificate_data['file_content'])) {
@@ -1048,11 +1022,14 @@ public function generatePdfFromCustomCertificate(): void
10481022
}
10491023
}
10501024

1051-
@$mpdf->WriteHTML((string) $this->certificate_data['file_content']);
1052-
1053-
$pdfName = api_replace_dangerous_char(get_lang('Certificates'));
1054-
$mpdf->Output($pdfName.'.pdf', \Mpdf\Output\Destination::DOWNLOAD);
1055-
exit;
1025+
// Single-page render with SSRF-guarded mPDF lives in the PDF class; it
1026+
// skips format_pdf()'s book layout to avoid blank pages around the
1027+
// single certificate page.
1028+
PDF::singlePageHtmlToPdfDownload(
1029+
(string) $this->certificate_data['file_content'],
1030+
get_lang('Certificates'),
1031+
$pdfOrientation
1032+
);
10561033
}
10571034

10581035
/**

public/main/inc/lib/pdf.lib.php

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public function __construct(
5252
$params['right'] = $params['right'] ?? 15;
5353
$params['top'] = $params['top'] ?? 30;
5454
$params['bottom'] = $params['bottom'] ?? 30;
55+
$params['margin_header'] = $params['margin_header'] ?? 8;
5556
$params['margin_footer'] = $params['margin_footer'] ?? 8;
5657

5758
$this->params['filename'] = $params['filename'] ?? api_get_local_time();
@@ -77,8 +78,8 @@ public function __construct(
7778
'margin_right' => $params['right'],
7879
'margin_top' => $params['top'],
7980
'margin_bottom' => $params['bottom'],
80-
'margin_header' => 8,
81-
'margin_footer' => 8,
81+
'margin_header' => $params['margin_header'],
82+
'margin_footer' => $params['margin_footer'],
8283
];
8384

8485
// Default value is 96 set in the mpdf library file config.php
@@ -538,6 +539,44 @@ public function content_to_pdf(
538539
return $output_file;
539540
}
540541

542+
/**
543+
* Renders single-page HTML (e.g. a certificate) to a downloadable PDF.
544+
*
545+
* Reuses the SSRF-guarded mPDF instance built by the constructor, with all
546+
* margins set to 0 and without calling format_pdf(): this deliberately
547+
* skips the book-layout decoration (mirrorMargins, header/footer margins)
548+
* that would otherwise insert blank pages around a single-page document.
549+
*
550+
* @param string $html HTML content to render
551+
* @param string $fileName Output file name (without extension)
552+
* @param string $orientation 'landscape' or 'portrait'
553+
*
554+
* @throws MpdfException
555+
*/
556+
public static function singlePageHtmlToPdfDownload(
557+
string $html,
558+
string $fileName,
559+
string $orientation = 'landscape'
560+
): void {
561+
$pageFormat = 'landscape' === $orientation ? 'A4-L' : 'A4';
562+
$mpdfOrientation = 'landscape' === $orientation ? 'L' : 'P';
563+
564+
$pdf = new self($pageFormat, $mpdfOrientation, [
565+
'left' => 0,
566+
'right' => 0,
567+
'top' => 0,
568+
'bottom' => 0,
569+
'margin_header' => 0,
570+
'margin_footer' => 0,
571+
]);
572+
$pdf->pdf->mirrorMargins = 0;
573+
574+
@$pdf->pdf->WriteHTML($html);
575+
576+
$pdf->pdf->Output(api_replace_dangerous_char($fileName).'.pdf', Destination::DOWNLOAD);
577+
exit;
578+
}
579+
541580
/**
542581
* Gets the watermark from the platform or a course.
543582
*

0 commit comments

Comments
 (0)