nhance/app/Libraries/AbhiMisPdfService.php
2026-08-07 10:19:31 +05:30

162 lines
5.3 KiB
PHP

<?php
namespace App\Libraries;
use Dompdf\Dompdf;
use Dompdf\Options;
/**
* Renders ABHI MIS HTML page to PDF via DomPDF (same layout as online report).
*/
class AbhiMisPdfService
{
/**
* Build full MIS PDF from report payload using the same view as the online page.
*
* @param array<string,mixed> $report
* @return array{status:bool,message?:string,content?:string,filename?:string}
*/
public function generateFromReport(array $report, int $policyId): array
{
$vm = $report['view_model'] ?? [];
$policyNo = trim((string) ($report['header']['policy_number'] ?? (string) $policyId));
$charts = [];
try {
$chartSvc = new AbhiMisChartImageService();
$charts = $chartSvc->buildAll(is_array($vm) ? $vm : []);
$html = view('claims_mis_abhi', [
'report' => $report,
'view_model' => $vm,
'policy_id' => $policyId,
'pdf_url' => '',
'embed' => true,
'charts' => $charts,
]);
return $this->generate($html, $policyNo);
} finally {
foreach ($charts as $path) {
if (is_string($path) && is_file($path)) {
@unlink($path);
}
}
}
}
/**
* @return array{status:bool,message?:string,content?:string,filename?:string}
*/
public function generate(string $html, string $policyNo): array
{
$safePolicy = preg_replace('/[^A-Za-z0-9_\-]+/', '_', $policyNo) ?: 'policy';
$filename = 'ABH_MIS_' . $safePolicy . '_' . date('Ymd') . '.pdf';
try {
$options = new Options();
$options->set('isHtml5ParserEnabled', true);
$options->set('isRemoteEnabled', true);
$options->set('isFontSubsettingEnabled', true);
$options->set('defaultFont', 'DejaVu Sans');
$options->setChroot([
rtrim(ROOTPATH, '/\\'),
rtrim(FCPATH, '/\\'),
rtrim(WRITEPATH, '/\\'),
sys_get_temp_dir(),
]);
$dompdf = new Dompdf($options);
$dompdf->loadHtml($this->prepareHtml($html));
$dompdf->setPaper('A4', 'landscape');
$dompdf->render();
$content = $dompdf->output();
if ($content === '' || $content === null) {
return ['status' => false, 'message' => 'PDF generation failed: empty output.'];
}
return [
'status' => true,
'content' => $content,
'filename' => $filename,
];
} catch (\Throwable $e) {
log_message('error', 'AbhiMisPdfService::generate failed | ' . $e->getMessage());
return ['status' => false, 'message' => 'PDF generation failed: ' . $e->getMessage()];
}
}
private function prepareHtml(string $html): string
{
$html = preg_replace('#<script\b[^>]*>.*?</script>#is', '', $html) ?? $html;
$html = preg_replace('/[\x{1F300}-\x{1FAFF}]/u', '', $html) ?? $html;
// Replace CSS custom properties DomPDF cannot resolve.
$vars = [
'var(--red-dark)' => '#9c0c22',
'var(--red)' => '#C8102E',
'var(--navy)' => '#152A54',
'var(--blue-dark)' => '#1B3F8C',
'var(--blue)' => '#2E86DE',
'var(--orange)' => '#E8862C',
'var(--purple)' => '#6A3D9A',
'var(--gold)' => '#D9A441',
'var(--bg)' => '#EFEFF2',
'var(--panel)' => '#FFFFFF',
'var(--line)' => '#E2E2E6',
'var(--text)' => '#26262B',
'var(--muted)' => '#6B6B75',
];
$html = str_replace(array_keys($vars), array_values($vars), $html);
$html = preg_replace('#:root\s*\{[^}]*\}#is', '', $html) ?? $html;
// Embed local images as data URIs.
$html = preg_replace_callback(
'#(<img\b[^>]*\bsrc=["\'])([^"\']+)(["\'][^>]*>)#i',
function (array $m): string {
$src = html_entity_decode($m[2], ENT_QUOTES);
$dataUri = $this->toDataUri($src);
return $m[1] . ($dataUri ?: $src) . $m[3];
},
$html
) ?? $html;
return $html;
}
private function toDataUri(string $path): ?string
{
$path = trim($path);
if ($path === '' || str_starts_with($path, 'data:')) {
return null;
}
if (preg_match('#^file://#i', $path)) {
$path = preg_replace('#^file://#i', '', $path) ?? $path;
}
if (!is_file($path) || !is_readable($path)) {
return null;
}
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
$mime = match ($ext) {
'png' => 'image/png',
'jpg', 'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'webp' => 'image/webp',
default => 'application/octet-stream',
};
$bytes = @file_get_contents($path);
if ($bytes === false || $bytes === '') {
return null;
}
return 'data:' . $mime . ';base64,' . base64_encode($bytes);
}
}