From 8270c8a049507d2de873923ed3734b9c116e7ed6 Mon Sep 17 00:00:00 2001 From: DevSoftTech Server Date: Wed, 15 Jul 2026 21:11:53 +0530 Subject: [PATCH] Fix discharge mPDF rendering for malformed HTML Normalize malformed discharge summary HTML before passing it to mPDF so browser-tolerated markup does not break PDF output. --- app/Controllers/Ipd_discharge.php | 104 ++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/app/Controllers/Ipd_discharge.php b/app/Controllers/Ipd_discharge.php index e8f7896..a7fad80 100644 --- a/app/Controllers/Ipd_discharge.php +++ b/app/Controllers/Ipd_discharge.php @@ -7047,9 +7047,113 @@ class Ipd_discharge extends BaseController $out = (string) preg_replace('/\bheader\s*:\s*html[_a-z0-9-]+\s*;?/i', '', $out); $out = (string) preg_replace('/\bfooter\s*:\s*html[_a-z0-9-]+\s*;?/i', '', $out); + if ($this->looksLikeMalformedDischargeHtml($out)) { + $out = $this->normalizeMalformedDischargeHtmlForMpdf($out); + } + return $this->forceValidUtf8($out); } + private function looksLikeMalformedDischargeHtml(string $html): bool + { + if ($html === '') { + return false; + } + + if (preg_match('/]*>\s*<(?:div|table|h[1-6]|ul|ol|p)\b/i', $html) === 1) { + return true; + } + + if (preg_match('/<(?!\/?(?:[a-z][a-z0-9:_-]*)(?:\s|\/?>)|!--|\?xml|!doctype)/i', $html) === 1) { + return true; + } + + return preg_match('/<\/(?:p|div|span|strong|em|i|b)>\s*<\/(?:p|div)>/i', $html) === 1; + } + + private function normalizeMalformedDischargeHtmlForMpdf(string $html): string + { + if ($html === '') { + return ''; + } + + $html = $this->escapeInvalidAngleBrackets($html); + + if (! class_exists('DOMDocument')) { + return $html; + } + + $previousUseInternalErrors = libxml_use_internal_errors(true); + libxml_clear_errors(); + + try { + $dom = new \DOMDocument('1.0', 'UTF-8'); + $flags = 0; + if (defined('LIBXML_NOWARNING')) { + $flags |= LIBXML_NOWARNING; + } + if (defined('LIBXML_NOERROR')) { + $flags |= LIBXML_NOERROR; + } + if (defined('LIBXML_COMPACT')) { + $flags |= LIBXML_COMPACT; + } + + $wrappedHtml = '
' + . $html + . '
'; + + $loaded = $dom->loadHTML('' . $wrappedHtml, $flags); + if ($loaded !== true) { + return $html; + } + + $normalized = ''; + $styleBlocks = []; + + $styleNodes = $dom->getElementsByTagName('style'); + foreach ($styleNodes as $styleNode) { + if (($styleNode->parentNode->nodeName ?? '') !== 'head') { + continue; + } + + $styleHtml = trim((string) $dom->saveHTML($styleNode)); + if ($styleHtml !== '') { + $styleBlocks[$styleHtml] = $styleHtml; + } + } + + $root = $dom->getElementById('__discharge_pdf_root__'); + if ($root instanceof \DOMElement) { + foreach ($root->childNodes as $childNode) { + $normalized .= (string) $dom->saveHTML($childNode); + } + } + + if ($normalized === '') { + return $html; + } + + return implode('', $styleBlocks) . $normalized; + } finally { + libxml_clear_errors(); + libxml_use_internal_errors($previousUseInternalErrors); + } + } + + private function escapeInvalidAngleBrackets(string $html): string + { + if ($html === '') { + return ''; + } + + return (string) preg_replace( + '/<(?!\/?(?:[a-z][a-z0-9:_-]*)(?:\s|\/?>)|!--|\?xml|!doctype)/i', + '<', + $html + ); + } + private function sanitizeCKEditorSpecialChars(string $html): string { if ($html === '') {