mPDF: normalize numeric font-weights across template PDF renderers

This commit is contained in:
DevSoftTech Server 2026-07-02 10:27:46 +05:30
parent 1abf572a14
commit 6288c626fc
5 changed files with 53 additions and 5 deletions

View File

@ -13,3 +13,27 @@
*
* @see: https://codeigniter.com/user_guide/extending/common.html
*/
if (! function_exists('mpdf_normalize_font_weight_css')) {
/**
* mPDF supports font-weight as `bold|normal` reliably.
* Convert numeric CSS weights (100..900) to mPDF-safe values.
*/
function mpdf_normalize_font_weight_css(string $html): string
{
if ($html === '') {
return '';
}
return (string) preg_replace_callback(
'/(font-weight\s*:\s*)([1-9]00)(\s*;?)/i',
static function (array $matches): string {
$weight = (int) ($matches[2] ?? 400);
$normalized = $weight >= 500 ? 'bold' : 'normal';
return (string) ($matches[1] ?? 'font-weight:') . $normalized . (string) ($matches[3] ?? ';');
},
$html
);
}
}

View File

@ -1501,6 +1501,9 @@ class Diagnosis extends BaseController
. $autoHeaderHtml
. $autoFooterHtml, $tokens);
$suffixHtml = $this->applyPdfTokens(trim((string) ($printSetting['mpdf_suffix_html'] ?? '')), $tokens);
$pdfHtml = mpdf_normalize_font_weight_css($pdfHtml);
$prefixHtml = mpdf_normalize_font_weight_css($prefixHtml);
$suffixHtml = mpdf_normalize_font_weight_css($suffixHtml);
$marginTop = $this->cmToMm($printSetting['page_margin_top_cm'] ?? 1.2, 1.2);
$marginBottom = $this->cmToMm($printSetting['page_margin_bottom_cm'] ?? 1.2, 1.2);
@ -1535,6 +1538,7 @@ class Diagnosis extends BaseController
if (! $headerHasTags) {
$plainHeader = $this->applyPdfTokens($rawHeaderHtml . $rawFirstPageHeaderHtml, $tokens);
$plainHeader = mpdf_normalize_font_weight_css($plainHeader);
if ($plainHeader !== '') {
$mpdf->SetHTMLHeader($plainHeader, 'O');
$mpdf->SetHTMLHeader($plainHeader, 'E');
@ -1546,6 +1550,7 @@ class Diagnosis extends BaseController
if ($plainFooter === '') {
$plainFooter = $this->applyPdfTokens($rawLastPageFooterHtml, $tokens);
}
$plainFooter = mpdf_normalize_font_weight_css($plainFooter);
if ($plainFooter !== '') {
$mpdf->SetHTMLFooter($plainFooter, 'O');
@ -2447,6 +2452,9 @@ class Diagnosis extends BaseController
. $autoHeaderHtml
. $autoFooterHtml, $tokens);
$suffixHtml = $this->applyPdfTokens(trim((string) ($printSetting['mpdf_suffix_html'] ?? '')), $tokens);
$pdfHtml = mpdf_normalize_font_weight_css($pdfHtml);
$prefixHtml = mpdf_normalize_font_weight_css($prefixHtml);
$suffixHtml = mpdf_normalize_font_weight_css($suffixHtml);
$mpdfTempDir = WRITEPATH . 'cache' . DIRECTORY_SEPARATOR . 'mpdf';
if (! is_dir($mpdfTempDir)) {
@ -2477,6 +2485,7 @@ class Diagnosis extends BaseController
if (! $headerHasTags) {
$plainHeader = $this->applyPdfTokens($rawHeaderHtml . $rawFirstPageHeaderHtml, $tokens);
$plainHeader = mpdf_normalize_font_weight_css($plainHeader);
if ($plainHeader !== '') {
$mpdf->SetHTMLHeader($plainHeader, 'O');
$mpdf->SetHTMLHeader($plainHeader, 'E');
@ -2488,6 +2497,7 @@ class Diagnosis extends BaseController
if ($plainFooter === '') {
$plainFooter = $this->applyPdfTokens($rawLastPageFooterHtml, $tokens);
}
$plainFooter = mpdf_normalize_font_weight_css($plainFooter);
if ($plainFooter !== '') {
$mpdf->SetHTMLFooter($plainFooter, 'O');

View File

@ -1335,6 +1335,7 @@ class DoctorDocument extends BaseController
}
$html = view('doctor_document/doc_letterhead_print', $data);
$html = mpdf_normalize_font_weight_css($html);
try {
$mpdf->WriteHTML($html, HTMLParserMode::DEFAULT_MODE);

View File

@ -6747,17 +6747,20 @@ class Ipd_discharge extends BaseController
$this->applyDischargeTemplateTokens(trim((string) ($templateSettings['header_html'] ?? '')), $templateTokenVars),
false
);
$headerHtml = mpdf_normalize_font_weight_css($headerHtml);
}
$footerHtml = $this->sanitizeDischargePdfHtml(
$this->applyDischargeTemplateTokens(trim((string) ($templateSettings['footer_html'] ?? '')), $templateTokenVars),
false
);
$footerHtml = mpdf_normalize_font_weight_css($footerHtml);
if ($footerHtml === '') {
$footerHtml = '<div style="font-family:freeserif,serif;font-size:9pt;color:#6b7280;text-align:right;">Page {PAGENO}/{nbpg}</div>';
}
$pdfHtml = $this->buildDischargePdfHtml($panelData, $renderedHtml, $withHeader, $templateName);
$pdfHtml = mpdf_normalize_font_weight_css($pdfHtml);
$mpdf = new Mpdf([
'mode' => 'utf-8',
@ -6819,6 +6822,7 @@ class Ipd_discharge extends BaseController
$safeBody = (string) preg_replace('/<style\b[^>]*>[\s\S]*?<\/style>/i', '', $safeBody);
$safeBody = (string) preg_replace('/<script\b[^>]*>[\s\S]*?<\/script>/i', '', $safeBody);
$safePdfHtml = $this->buildDischargePdfHtml($panelData, $safeBody, false, $templateName);
$safePdfHtml = mpdf_normalize_font_weight_css($safePdfHtml);
$fallbackPdf = new Mpdf([
'mode' => 'utf-8',

View File

@ -36,7 +36,7 @@ class Opd extends BaseController
private function configureOpdPlaceholderRenderOptionsFromRequest(): void
{
$headingStyle = strtolower(trim((string) $this->request->getGet('heading_style')));
if (!in_array($headingStyle, ['bold', 'highlight', 'plain'], true)) {
if (!in_array($headingStyle, ['bold', 'highlight', 'plain', 'underline'], true)) {
$headingStyle = 'bold';
}
@ -2209,6 +2209,7 @@ class Opd extends BaseController
$html = $this->applyOpdPdfTemplateTokens($customHtml, $tokenVars);
$html = $this->sanitizeOpdNamedHeaderFooterReferences($html);
$html = $this->applyOpdLongContentPaginationGuard($html);
$html = mpdf_normalize_font_weight_css($html);
if ($debugHtml) {
return $this->response
@ -2416,9 +2417,11 @@ class Opd extends BaseController
}
$html = $this->applyOpdLongContentPaginationGuard($html);
$html = mpdf_normalize_font_weight_css($html);
if ($rawHeaderHtml !== '') {
$renderedHeaderHtml = $this->applyOpdPdfTemplateTokens($rawHeaderHtml, $tokenVars);
$renderedHeaderHtml = mpdf_normalize_font_weight_css($renderedHeaderHtml);
if (preg_match('/<\s*(htmlpageheader|sethtmlpageheader)\b/i', $renderedHeaderHtml) === 1) {
$prefixHtml .= $renderedHeaderHtml;
} else {
@ -2429,6 +2432,7 @@ class Opd extends BaseController
if ($rawFooterHtml !== '') {
$renderedFooterHtml = $this->applyOpdPdfTemplateTokens($rawFooterHtml, $tokenVars);
$renderedFooterHtml = mpdf_normalize_font_weight_css($renderedFooterHtml);
if (preg_match('/<\s*(htmlpagefooter|sethtmlpagefooter)\b/i', $renderedFooterHtml) === 1) {
$prefixHtml .= $renderedFooterHtml;
} else {
@ -2438,6 +2442,7 @@ class Opd extends BaseController
}
if ($prefixHtml !== '') {
$prefixHtml = mpdf_normalize_font_weight_css($prefixHtml);
$mpdf->WriteHTML($prefixHtml, HTMLParserMode::HTML_HEADER_BUFFER);
}
@ -2478,6 +2483,7 @@ class Opd extends BaseController
. '<label for="heading_style">Heading Style</label>'
. '<select id="heading_style" name="heading_style">'
. '<option value="bold"' . ($defaultHeading === 'bold' ? ' selected' : '') . '>Bold</option>'
. '<option value="underline"' . ($defaultHeading === 'underline' ? ' selected' : '') . '>Bold + Underline (mPDF-safe)</option>'
. '<option value="highlight"' . ($defaultHeading === 'highlight' ? ' selected' : '') . '>Highlight</option>'
. '<option value="plain"' . ($defaultHeading === 'plain' ? ' selected' : '') . '>Plain</option>'
. '</select>'
@ -3733,11 +3739,14 @@ class Opd extends BaseController
$lineGapCss = number_format($lineGap, 2, '.', '');
$blockGapCss = number_format($blockGapMm, 2, '.', '');
$headingStyleCss = 'font-weight:700;font-size:inherit;letter-spacing:0.04em;text-transform:uppercase;color:#1a1a1a;';
// mPDF supports font-weight as bold|normal (not numeric weights like 700).
$headingStyleCss = 'font-weight:bold;font-size:inherit;letter-spacing:0.04em;text-transform:uppercase;color:#1a1a1a;';
if ($headingStyleMode === 'plain') {
$headingStyleCss = 'font-weight:400;font-size:inherit;letter-spacing:0.04em;text-transform:uppercase;color:#1a1a1a;';
$headingStyleCss = 'font-weight:normal;font-size:inherit;letter-spacing:0.04em;text-transform:uppercase;color:#1a1a1a;';
} elseif ($headingStyleMode === 'underline') {
$headingStyleCss = 'font-weight:bold;font-size:inherit;letter-spacing:0.04em;text-transform:uppercase;color:#1a1a1a;text-decoration:underline;';
} elseif ($headingStyleMode === 'highlight') {
$headingStyleCss = 'font-weight:700;font-size:inherit;letter-spacing:0.04em;text-transform:uppercase;color:#1a1a1a;background:#fff3cd;border:1px solid #f0d98a;border-radius:2px;padding:1px 4px;display:inline-block;';
$headingStyleCss = 'font-weight:bold;font-size:inherit;letter-spacing:0.04em;text-transform:uppercase;color:#1a1a1a;background-color:#fff3cd;border:0.3mm solid #f0d98a;border-radius:1mm;padding:0.2mm 1mm;display:inline-block;';
}
// mPDF-safe vertical spacer. Using a fixed-height block in mm is more reliable
@ -3752,7 +3761,7 @@ class Opd extends BaseController
// Heading: bold + uppercase, same font-size as surrounding content so
// heading and value stay visually matched (no mismatch from hardcoded px).
$headingHtml = '<strong style="' . $headingStyleCss . '">' . $label . ' :</strong>';
$headingHtml = '<b><span style="' . $headingStyleCss . '">' . $label . ' :</span></b>';
if ($lineBreakAfterLabel) {
// List-style sections (Complaint, Diagnosis …):