nhance_partner_be/app/Helpers/policy_pdf_helper.php
2026-07-30 10:50:07 +05:30

205 lines
6.6 KiB
PHP

<?php
if (!function_exists('policy_md_upload_path')) {
/**
* Full path to the saved markdown file for a policy PDF file name.
*/
function policy_md_upload_path(string $pdfFileName): string
{
$mdFileName = preg_replace('/\.pdf$/i', '.md', $pdfFileName);
if ($mdFileName === $pdfFileName) {
$mdFileName .= '.md';
}
return WRITEPATH . 'uploads/policy/policy_md/' . $mdFileName;
}
}
if (!function_exists('policy_pdf_md_path')) {
/**
* Resolve markdown path using the same base name as the PDF file.
* e.g. 1764999905_abc.pdf -> policy_md/1764999905_abc.md
*/
function policy_pdf_md_path(string $pdfPath): string
{
return policy_md_upload_path(basename($pdfPath));
}
}
if (!function_exists('policy_md_is_valid')) {
/**
* Validate that markdown content looks like extracted policy text.
*/
function policy_md_is_valid(string $content): bool
{
$content = trim($content);
if ($content === '' || strlen($content) < 500) {
return false;
}
$errorMarkers = [
'pdftotext:',
'GLIBCXX_',
'CXXABI_',
'no version information available',
];
foreach ($errorMarkers as $marker) {
if (stripos($content, $marker) !== false) {
return false;
}
}
return true;
}
}
if (!function_exists('policy_pdf_extract_text')) {
/**
* Extract plain text from a PDF using pdftotext (poppler).
* Uses a clean environment so LAMPP lib paths do not break pdftotext.
*/
function policy_pdf_extract_text(string $pdfPath): string
{
$pdftotext = getenv('PDFTOTEXT_BIN') ?: '/usr/bin/pdftotext';
if (!is_executable($pdftotext) && $pdftotext === '/usr/bin/pdftotext') {
$which = trim((string) shell_exec('command -v pdftotext 2>/dev/null'));
if ($which !== '') {
$pdftotext = $which;
}
}
$command = sprintf(
'env -i PATH=/usr/bin:/bin LD_LIBRARY_PATH= %s -layout %s - 2>&1',
escapeshellcmd($pdftotext),
escapeshellarg($pdfPath)
);
$text = shell_exec($command);
if (!is_string($text)) {
return '';
}
$text = trim($text);
if (!policy_md_is_valid($text)) {
log_message('error', 'PDF text extraction failed or returned invalid content for: ' . $pdfPath);
return '';
}
return $text;
}
}
if (!function_exists('convert_policy_pdf_to_markdown')) {
/**
* Convert a policy PDF to markdown, save to policy_md/, then read back from storage.
*
* Always pass $originalPdfFileName (DB/storage name) when $pdfPath is an S3 temp download
* like /tmp/s3_<uniqid>_original.pdf so the MD key matches the original PDF basename.
*
* @return array{status: string, message: string, content?: string, md_path?: string, md_file_name?: string}
*/
function convert_policy_pdf_to_markdown(string $pdfPath, bool $forceRefresh = false, ?string $originalPdfFileName = null): array
{
if (!is_file($pdfPath)) {
return [
'status' => 'failed',
'message' => "PDF file not found at {$pdfPath}",
];
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $pdfPath);
finfo_close($finfo);
if ($mimeType !== 'application/pdf') {
return [
'status' => 'failed',
'message' => "Unsupported file type ({$mimeType}). Only PDF is supported.",
];
}
// Prefer the original storage/DB PDF name so S3 temp paths do not pollute the MD key.
$sourcePdfName = ($originalPdfFileName !== null && $originalPdfFileName !== '')
? basename($originalPdfFileName)
: basename($pdfPath);
$mdPath = policy_md_upload_path($sourcePdfName);
$mdFileName = basename($mdPath);
$mdDir = dirname($mdPath);
if (!is_dir($mdDir) && !mkdir($mdDir, 0777, true) && !is_dir($mdDir)) {
log_message('error', "Unable to create markdown directory at {$mdDir}; will rely on storage put only");
}
if (!$forceRefresh && policy_md_exists($mdFileName)) {
$cached = policy_read_md($mdFileName);
if ($cached !== '' && policy_md_is_valid($cached)) {
log_message('info', "Using cached policy markdown: {$mdFileName}");
return [
'status' => 'success',
'message' => 'Cached markdown loaded from storage',
'content' => $cached,
'md_path' => $mdPath,
'md_file_name' => $mdFileName,
];
}
log_message('info', "Invalid cached markdown detected, reconverting: {$mdFileName}");
}
$extractedText = policy_pdf_extract_text($pdfPath);
if ($extractedText === '') {
return [
'status' => 'failed',
'message' => 'PDF text extraction failed. The file may be scanned, image-based, or pdftotext is unavailable on the server.',
];
}
$markdown = "# Motor Policy Document\n\n" . $extractedText;
// Local write is best-effort (S3 driver may not have write access under writable/).
$localWritten = false;
if (is_dir($mdDir) || (is_dir(dirname($mdDir)) && @mkdir($mdDir, 0777, true))) {
$localWritten = @file_put_contents($mdPath, $markdown) !== false;
if (!$localWritten) {
log_message('error', "Local markdown write failed (continuing with storage put): {$mdPath}");
}
}
policy_put_md($mdFileName, $markdown);
$savedContent = $localWritten ? (file_get_contents($mdPath) ?: $markdown) : $markdown;
if (!policy_md_is_valid($savedContent)) {
if ($localWritten) {
@unlink($mdPath);
}
return [
'status' => 'failed',
'message' => 'Saved markdown file is invalid after conversion',
];
}
log_message('info', "Policy PDF converted to markdown: {$mdFileName} (local_written=" . ($localWritten ? '1' : '0') . ')');
return [
'status' => 'success',
'message' => 'PDF converted to markdown and saved to storage',
'content' => $savedContent,
'md_path' => $mdPath,
'md_file_name' => $mdFileName,
];
}
}