fix(opd): preserve template settings and flow while compacting font sizes for large prescriptions
This commit is contained in:
parent
8850620931
commit
524a991098
|
|
@ -2199,7 +2199,7 @@ class Opd extends BaseController
|
|||
$tokenVars = $this->buildOpdPdfTokenVars($data);
|
||||
$html = $this->applyOpdPdfTemplateTokens($customHtml, $tokenVars);
|
||||
$html = $this->sanitizeOpdNamedHeaderFooterReferences($html);
|
||||
$html = $this->applyOpdLongContentPaginationGuard($html, 8.0, 210.0);
|
||||
$html = $this->applyOpdLongContentPaginationGuard($html);
|
||||
$html = mpdf_normalize_font_weight_css($html);
|
||||
|
||||
if ($debugHtml) {
|
||||
|
|
@ -2407,8 +2407,7 @@ class Opd extends BaseController
|
|||
$rawFooterHtml = '';
|
||||
}
|
||||
|
||||
$resolvedPaperWidthMm = $format === 'LETTER' ? 215.9 : ($format === 'A5' ? 148.0 : ($format === 'CUSTOM' ? (float) $customWidthMm : 210.0));
|
||||
$html = $this->applyOpdLongContentPaginationGuard($html, (float) $marginTop, $resolvedPaperWidthMm);
|
||||
$html = $this->applyOpdLongContentPaginationGuard($html);
|
||||
$html = mpdf_normalize_font_weight_css($html);
|
||||
|
||||
if ($rawHeaderHtml !== '') {
|
||||
|
|
@ -2760,23 +2759,19 @@ class Opd extends BaseController
|
|||
|
||||
$medicalHtml = '';
|
||||
if (!empty($rxMeds)) {
|
||||
$medCount = count($rxMeds);
|
||||
// Idea 1: Dynamic compact styling for prescriptions with >= 6 medicines
|
||||
// Strictly enforce readability floor: font-size must never drop below 9.5px (or 8.5px for Hindi subtext).
|
||||
$isCompactMeds = ($medCount >= 6);
|
||||
$tblFontSize = $isCompactMeds ? '10px' : '12px';
|
||||
$tblCellPadding = $isCompactMeds ? '2.5px 5px' : '6px 8px';
|
||||
$tblHeaderPadding = $isCompactMeds ? '3px 5px' : '6px 8px';
|
||||
$tblMarginY = $isCompactMeds ? '2px' : '5px';
|
||||
$isCompactMeds = count($rxMeds) >= 8;
|
||||
$tableFontSize = $isCompactMeds ? '10px' : '12px';
|
||||
$cellPadding = $isCompactMeds ? '2.5px 5px' : '6px 8px';
|
||||
$tableMargin = $isCompactMeds ? '2px' : '5px';
|
||||
$genericFontSize = $isCompactMeds ? '8.5px' : '10px';
|
||||
$hindiFontSize = $isCompactMeds ? '8.5px' : '11px';
|
||||
$hindiLineHeight = $isCompactMeds ? '1.22' : '1.4';
|
||||
$localFontSize = $isCompactMeds ? '8.5px' : '11px';
|
||||
$localLineHeight = $isCompactMeds ? '1.2' : '1.4';
|
||||
|
||||
$medicalHtml .= '<table width="100%" style="border-collapse:collapse;font-size:' . $tblFontSize . ';border:1px solid #cce5ff;margin-top:' . $tblMarginY . ';margin-bottom:' . $tblMarginY . ';">';
|
||||
$medicalHtml .= '<table width="100%" style="border-collapse:collapse;font-size:' . $tableFontSize . ';border:1px solid #cce5ff;margin-top:' . $tableMargin . ';margin-bottom:' . $tableMargin . ';">';
|
||||
$medicalHtml .= '<thead><tr style="background:#eef6fc;border-bottom:2px solid #b8daff;">'
|
||||
. '<th style="padding:' . $tblHeaderPadding . ';text-align:left;width:5%;font-weight:bold;color:#000;">#</th>'
|
||||
. '<th style="padding:' . $tblHeaderPadding . ';text-align:left;width:40%;font-weight:bold;color:#000;">Medicine</th>'
|
||||
. '<th style="padding:' . $tblHeaderPadding . ';text-align:left;width:55%;font-weight:bold;color:#000;">Directions</th>'
|
||||
. '<th style="padding:' . $cellPadding . ';text-align:left;width:5%;font-weight:bold;color:#000;">#</th>'
|
||||
. '<th style="padding:' . $cellPadding . ';text-align:left;width:40%;font-weight:bold;color:#000;">Medicine</th>'
|
||||
. '<th style="padding:' . $cellPadding . ';text-align:left;width:55%;font-weight:bold;color:#000;">Directions</th>'
|
||||
. '</tr></thead><tbody>';
|
||||
|
||||
$whenCodeDescMap = [
|
||||
|
|
@ -3041,11 +3036,11 @@ class Opd extends BaseController
|
|||
$localDirectionsText = !empty($dirLocalParts) ? implode(' | ', array_values(array_unique($dirLocalParts))) : '';
|
||||
|
||||
$medicalHtml .= '<tr style="border-bottom:1px solid #e2e8f0;">'
|
||||
. '<td style="padding:' . $tblCellPadding . ';vertical-align:middle;text-align:left;">' . $i . '</td>'
|
||||
. '<td style="padding:' . $tblCellPadding . ';vertical-align:middle;text-align:left;">' . $nameHtml . '</td>'
|
||||
. '<td style="padding:' . $tblCellPadding . ';vertical-align:middle;text-align:left;">'
|
||||
. '<td style="padding:' . $cellPadding . ';vertical-align:middle;text-align:left;">' . $i . '</td>'
|
||||
. '<td style="padding:' . $cellPadding . ';vertical-align:middle;text-align:left;">' . $nameHtml . '</td>'
|
||||
. '<td style="padding:' . $cellPadding . ';vertical-align:middle;text-align:left;">'
|
||||
. '<div>' . esc($directionsText) . '</div>'
|
||||
. ($localDirectionsText !== '' ? ('<div style="font-size:' . $hindiFontSize . ';color:#444;line-height:' . $hindiLineHeight . ';margin-top:1px;" lang="hi">' . esc($localDirectionsText) . '</div>') : '')
|
||||
. ($localDirectionsText !== '' ? ('<div style="font-size:' . $localFontSize . ';color:#444;line-height:' . $localLineHeight . ';margin-top:1px;" lang="hi">' . esc($localDirectionsText) . '</div>') : '')
|
||||
. '</td>'
|
||||
. '</tr>';
|
||||
}
|
||||
|
|
@ -3952,15 +3947,11 @@ class Opd extends BaseController
|
|||
}
|
||||
|
||||
$rxMeds = is_array($data['rx_medicines'] ?? null) ? $data['rx_medicines'] : [];
|
||||
$isCompactMeds = count($rxMeds) >= 6;
|
||||
$isCompactMeds = count($rxMeds) >= 8;
|
||||
|
||||
$headingStyleMode = (string) ($this->opdPlaceholderRenderOptions['heading_style'] ?? 'bold');
|
||||
$lineGap = (float) ($this->opdPlaceholderRenderOptions['line_gap'] ?? 1.6);
|
||||
$blockGapMm = (float) ($this->opdPlaceholderRenderOptions['block_gap_mm'] ?? 2.8);
|
||||
if ($isCompactMeds) {
|
||||
$lineGap = min($lineGap, 1.35);
|
||||
$blockGapMm = min($blockGapMm, 1.5);
|
||||
}
|
||||
$lineGap = (float) ($this->opdPlaceholderRenderOptions['line_gap'] ?? ($isCompactMeds ? 1.35 : 1.6));
|
||||
$blockGapMm = (float) ($this->opdPlaceholderRenderOptions['block_gap_mm'] ?? ($isCompactMeds ? 1.5 : 2.8));
|
||||
$lineGapCss = number_format($lineGap, 2, '.', '');
|
||||
$blockGapCss = number_format($blockGapMm, 2, '.', '');
|
||||
|
||||
|
|
@ -4671,7 +4662,7 @@ class Opd extends BaseController
|
|||
return $html;
|
||||
}
|
||||
|
||||
private function applyOpdLongContentPaginationGuard(string $html, float $pageMarginTopMm = 10.0, float $paperWidthMm = 210.0): string
|
||||
private function applyOpdLongContentPaginationGuard(string $html): string
|
||||
{
|
||||
if ($html === '') {
|
||||
return '';
|
||||
|
|
@ -4687,138 +4678,21 @@ class Opd extends BaseController
|
|||
$lineBreakCount = substr_count(strtolower($html), '<br');
|
||||
$rowCount = substr_count(strtolower($html), '<tr');
|
||||
|
||||
// Trigger pagination guard when content threatens single-page overflow:
|
||||
// >= 8 rows, >= 18 line breaks, or text length > 900
|
||||
// When content has 8 or more rows, or substantial length, apply compact font size CSS only.
|
||||
// Flow the setting naturally: never alter .RxPlace position, top, left, width, or page margins.
|
||||
$looksLong = $rowCount >= 8 || $lineBreakCount >= 18 || $textLength > 900;
|
||||
if (! $looksLong) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
// Parse print area dimensions (top, left, width) from .RxPlace CSS
|
||||
$dim = [
|
||||
'top' => 90.0,
|
||||
'left' => 10.0,
|
||||
'width' => 195.0,
|
||||
];
|
||||
|
||||
$convertMm = static function (float $val, string $unit, float $baseMm = 210.0): float {
|
||||
$unit = strtolower(trim($unit));
|
||||
if ($unit === 'cm') {
|
||||
return $val * 10.0;
|
||||
}
|
||||
if ($unit === 'in') {
|
||||
return $val * 25.4;
|
||||
}
|
||||
if ($unit === 'px') {
|
||||
return $val * 0.264583;
|
||||
}
|
||||
if ($unit === 'pt') {
|
||||
return $val * 0.352778;
|
||||
}
|
||||
if ($unit === '%') {
|
||||
return ($val / 100.0) * $baseMm;
|
||||
}
|
||||
return $val; // default mm
|
||||
};
|
||||
|
||||
if (preg_match('/(\.(?:RxPlace|rxplace)\s*\{[^}]+\})/is', $html, $blockMatch)) {
|
||||
$body = $blockMatch[1];
|
||||
if (preg_match('/(?:^|[;\{\s])top\s*:\s*([0-9\.]+)\s*(mm|cm|px|in|pt|%)?/i', $body, $m)) {
|
||||
$dim['top'] = $convertMm((float) $m[1], $m[2] ?? 'mm', 297.0);
|
||||
}
|
||||
if (preg_match('/(?:^|[;\{\s])left\s*:\s*([0-9\.]+)\s*(mm|cm|px|in|pt|%)?/i', $body, $m)) {
|
||||
$dim['left'] = $convertMm((float) $m[1], $m[2] ?? 'mm', $paperWidthMm);
|
||||
}
|
||||
if (preg_match('/(?:^|[;\{\s])width\s*:\s*([0-9\.]+)\s*(mm|cm|px|in|pt|%)?/i', $body, $m)) {
|
||||
$unit = $m[2] ?? 'mm';
|
||||
if ($unit !== '%' || (float) $m[1] < 99.0) {
|
||||
$dim['width'] = $convertMm((float) $m[1], $unit, $paperWidthMm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sanitize print area and calculate right margin to preserve width ratio
|
||||
$leftMm = max(0.0, min($paperWidthMm - 30.0, $dim['left']));
|
||||
$widthMm = max(30.0, min($paperWidthMm - $leftMm, $dim['width']));
|
||||
$rightMarginMm = max(0.0, $paperWidthMm - $leftMm - $widthMm);
|
||||
|
||||
// Check existing @page margin-top in document
|
||||
$existingPageTopMm = 0.0;
|
||||
if (preg_match_all('/@page\s*\{[^}]*?margin-top\s*:\s*([0-9\.]+)\s*(mm|cm|px|in|pt)?/i', $html, $allTm, PREG_SET_ORDER)) {
|
||||
$lastTm = end($allTm);
|
||||
$existingPageTopMm = $convertMm((float) $lastTm[1], $lastTm[2] ?? 'mm', 297.0);
|
||||
}
|
||||
if ($existingPageTopMm <= 0.0 && $pageMarginTopMm > 0.0) {
|
||||
$existingPageTopMm = $pageMarginTopMm;
|
||||
}
|
||||
|
||||
// Top clearance spacer height on Page 1 (only the delta needed to reach the template's target top)
|
||||
$spacerHeightMm = max(0.0, $dim['top'] - $existingPageTopMm);
|
||||
|
||||
// In-place update margin-left and margin-right in any existing @page blocks
|
||||
if (preg_match('/@page\s*\{/i', $html)) {
|
||||
if (preg_match('/(@page\s*\{[^}]*?margin-left\s*:\s*)([^;\}]+)/is', $html)) {
|
||||
$html = preg_replace('/(@page\s*\{[^}]*?margin-left\s*:\s*)([^;\}]+)/is', '${1}' . round($leftMm, 1) . 'mm', $html);
|
||||
}
|
||||
if (preg_match('/(@page\s*\{[^}]*?margin-right\s*:\s*)([^;\}]+)/is', $html)) {
|
||||
$html = preg_replace('/(@page\s*\{[^}]*?margin-right\s*:\s*)([^;\}]+)/is', '${1}' . round($rightMarginMm, 1) . 'mm', $html);
|
||||
}
|
||||
}
|
||||
|
||||
// Neutralize position: absolute inside .RxPlace declaration directly in existing style blocks
|
||||
// (mPDF strips !important, so replacing the rule in-place ensures it does not override)
|
||||
$html = preg_replace(
|
||||
'/(\.(?:RxPlace|rxplace)[^{]*\{)([^}]+)(\})/is',
|
||||
'$1 position: static; width: 100%; max-width: 100%; margin: 0; padding: 0; $3',
|
||||
$html
|
||||
);
|
||||
|
||||
// Insert top clearance spacer right before .RxPlace so Page 1 reserves space for letterhead,
|
||||
// but subsequent pages start cleanly at the top of the continuation sheet
|
||||
if ($spacerHeightMm > 0.5) {
|
||||
$topSpacer = '<div style="display:block;height:' . round($spacerHeightMm, 1) . 'mm;line-height:0;font-size:0;margin:0;padding:0;"> </div>';
|
||||
$html = preg_replace('/(<div\s+class=["\'][^"\']*\brxplace\b[^"\']*["\']>)/i', $topSpacer . "\n$1", $html, 1);
|
||||
}
|
||||
|
||||
// Preserve existing named header/footer references if present in the document
|
||||
$extraPageRules = '';
|
||||
if (preg_match('/(?<![a-zA-Z0-9_-])header\s*:\s*(html_[a-zA-Z0-9_-]+)/i', $html, $hm)) {
|
||||
$extraPageRules .= 'header: ' . trim($hm[1]) . '; ';
|
||||
}
|
||||
if (preg_match('/(?<![a-zA-Z0-9_-])footer\s*:\s*(html_[a-zA-Z0-9_-]+)/i', $html, $fm)) {
|
||||
$extraPageRules .= 'footer: ' . trim($fm[1]) . '; ';
|
||||
}
|
||||
|
||||
// Append overriding CSS at the end so mPDF cascade respects pagination rules,
|
||||
// maintaining the exact left margin, printable width ratio, and column alignment
|
||||
$guardCss = "\n" . '<style>'
|
||||
. '@page {'
|
||||
. 'margin-left: ' . round($leftMm, 1) . 'mm !important;'
|
||||
. 'margin-right: ' . round($rightMarginMm, 1) . 'mm !important;'
|
||||
. $extraPageRules
|
||||
. '}'
|
||||
. '.RxPlace, .rxplace {'
|
||||
. 'position: static !important;'
|
||||
. 'top: auto !important;'
|
||||
. 'left: auto !important;'
|
||||
. 'right: auto !important;'
|
||||
. 'bottom: auto !important;'
|
||||
. 'width: 100% !important;'
|
||||
. 'max-width: 100% !important;'
|
||||
. 'height: auto !important;'
|
||||
. 'max-height: none !important;'
|
||||
. 'overflow: visible !important;'
|
||||
. 'margin: 0 !important;'
|
||||
. 'padding: 0 !important;'
|
||||
. '}'
|
||||
. 'table { page-break-inside: auto !important; width: 100% !important; }'
|
||||
. 'tr, td, th { page-break-inside: avoid !important; }'
|
||||
. 'thead { display: table-header-group !important; }'
|
||||
. 'br { margin-bottom: 2px !important; }'
|
||||
. 'table p { margin-bottom: 3px !important; }'
|
||||
$guardCss = '<style>'
|
||||
. '.RxPlace table, .rxplace table { font-size: 10px !important; margin-top: 2px !important; margin-bottom: 2px !important; }'
|
||||
. '.RxPlace th, .rxplace th, .RxPlace td, .rxplace td { font-size: 10px !important; padding: 2.5px 5px !important; }'
|
||||
. '.RxPlace table p, .rxplace table p, .RxPlace p, .rxplace p { font-size: 9.5px !important; line-height: 1.25 !important; margin-top: 0 !important; margin-bottom: 2px !important; }'
|
||||
. '.RxPlace br, .rxplace br { margin-bottom: 2px !important; }'
|
||||
. '</style>';
|
||||
|
||||
return $html . $guardCss;
|
||||
return $html . "\n" . $guardCss;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -345,12 +345,19 @@ $toLocalText = static function (string $input): string {
|
|||
|
||||
<div class="rx-title">Rx :</div>
|
||||
<?php if (!empty($rx_medicines ?? [])) : ?>
|
||||
<table class="rx-table" cellspacing="0" cellpadding="0" style="border:1px solid #cce5ff;">
|
||||
<?php
|
||||
$isCompactMeds = count($rx_medicines ?? []) >= 8;
|
||||
$cellPadding = $isCompactMeds ? '2.5px 5px' : '6px 8px';
|
||||
$genericFontSize = $isCompactMeds ? '8.5px' : '9.5px';
|
||||
$localFontSize = $isCompactMeds ? '8.5px' : '9.5px';
|
||||
$localLineHeight = $isCompactMeds ? '1.2' : '1.4';
|
||||
?>
|
||||
<table class="rx-table" cellspacing="0" cellpadding="0" style="border:1px solid #cce5ff;<?= $isCompactMeds ? 'font-size:10px;' : '' ?>">
|
||||
<thead>
|
||||
<tr style="background:#eef6fc;border-bottom:2px solid #b8daff;">
|
||||
<th style="width:5%;padding:6px 8px;font-weight:bold;color:#000;">#</th>
|
||||
<th style="width:40%;padding:6px 8px;font-weight:bold;color:#000;">Medicine</th>
|
||||
<th style="width:55%;padding:6px 8px;font-weight:bold;color:#000;">Directions</th>
|
||||
<th style="width:5%;padding:<?= $cellPadding ?>;font-weight:bold;color:#000;">#</th>
|
||||
<th style="width:40%;padding:<?= $cellPadding ?>;font-weight:bold;color:#000;">Medicine</th>
|
||||
<th style="width:55%;padding:<?= $cellPadding ?>;font-weight:bold;color:#000;">Directions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -459,17 +466,17 @@ $toLocalText = static function (string $input): string {
|
|||
$localDirectionsText = !empty($dirLocalParts) ? implode(' | ', array_values(array_unique($dirLocalParts))) : '';
|
||||
?>
|
||||
<tr style="border-bottom:1px solid #e2e8f0;">
|
||||
<td style="padding:6px 8px;vertical-align:middle;"><?= esc((string) ($idx + 1)) ?></td>
|
||||
<td style="padding:6px 8px;vertical-align:middle;">
|
||||
<td style="padding:<?= $cellPadding ?>;vertical-align:middle;"><?= esc((string) ($idx + 1)) ?></td>
|
||||
<td style="padding:<?= $cellPadding ?>;vertical-align:middle;">
|
||||
<strong><?= esc($fullMedName) ?></strong>
|
||||
<?php if ($generic !== '') : ?>
|
||||
<div style="font-size:9.5px;color:#555;font-weight:normal;margin-top:2px;"><?= esc($generic) ?></div>
|
||||
<div style="font-size:<?= $genericFontSize ?>;color:#555;font-weight:normal;margin-top:1px;"><?= esc($generic) ?></div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td style="padding:6px 8px;vertical-align:middle;">
|
||||
<td style="padding:<?= $cellPadding ?>;vertical-align:middle;">
|
||||
<div><?= esc($directionsText) ?></div>
|
||||
<?php if ($localDirectionsText !== '') : ?>
|
||||
<div style="font-size:9.5px;color:#444;line-height:1.4;margin-top:2px;" lang="hi"><?= esc($localDirectionsText) ?></div>
|
||||
<div style="font-size:<?= $localFontSize ?>;color:#444;line-height:<?= $localLineHeight ?>;margin-top:1px;" lang="hi"><?= esc($localDirectionsText) ?></div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user