From 848d8d663fcb27f75836c7b59c5fa9b18ed15772 Mon Sep 17 00:00:00 2001 From: mahdahar <89adham@gmail.com> Date: Tue, 3 Feb 2026 07:26:41 +0700 Subject: [PATCH] feat: Extend report access to CS role and refactor report generation This commit expands report generation capabilities to Customer Service (CS) role and refactors the report system for better maintainability and PDF support. Changes Summary: Access Control: - Extended report access from Lab, Admin, Superuser to include CS role (filter: 0,1,2,4) - Removed separate CS-only print routes, consolidated into unified report routes - Routes now support /report/:num, /report/:num/eng, /report/:num/print, /report/:num/print/eng Controller Refactoring (ReportController): - Refactored generate() and print() methods to share common renderReport() logic - Removed separate preview() method - preview now handled via preview parameter - Added ispdf parameter support for PDF generation mode - Print functionality now logs audit events to AUDIT_REQUESTS table Database Queries (ReportHelper): - Improved SQL queries with explicit aliases for better readability and maintainability - Fixed date formatting issue: changed date_format() to date() with strtotime() - Added getValData() method to retrieve validation user information (VAL1USER, VAL2USER) - Added null coalescing operators (?? '') for safer array access View Updates (report/template.php): - Conditional CSS loading: uses pdf.css when ispdf=1, otherwise style.css - Removed "PREVIEW ONLY - DO NOT PRINT" watermark - Conditional header/footer images - only display when generating PDF - Added validation user display: "Val1 By : {user} | Val2 By : {user}" - Replaced signature placeholder with "This result is valid without signature" statement - Improved footer layout spacing Styling Adjustments (public/assets/report/style.css): - Adjusted margins for better print layout: dinfo (2cm), dresult (17.5cm), footer (2cm) - Increased footer width from 17cm to 18cm - Added responsive image classes: .img and .img-footer with max-width: 100% - Set footer image max-height to 2.5cm Security: - Maintained role-based access control with proper authentication checks - All database queries use parameterized statements (no interpolation) - Print actions still logged to AUDIT_REQUESTS for audit trail --- app/Config/Routes.php | 14 +-- app/Controllers/ReportController.php | 38 ++++--- app/Libraries/ReportHelper.php | 153 ++++++++++++++++----------- app/Views/report/template.php | 53 +++++----- public/assets/report/style.css | 10 +- 5 files changed, 146 insertions(+), 122 deletions(-) diff --git a/app/Config/Routes.php b/app/Config/Routes.php index ea41e2b..3195bf9 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -92,18 +92,12 @@ $routes->group('cs', ['filter' => 'role:4'], function ($routes) { $routes->get('/dummypage', 'Home::dummyPage'); -// Report generation - Lab, Admin, Superuser -$routes->group('report', ['filter' => 'role:0,1,2'], function ($routes) { +// Report generation - Lab, Admin, Superuser, CS +$routes->group('report', ['filter' => 'role:0,1,2,4'], function ($routes) { $routes->get('(:num)', 'ReportController::generate/$1'); - $routes->get('(:num)/preview', 'ReportController::preview/$1'); $routes->get('(:num)/eng', 'ReportController::generate/$1/1'); - $routes->get('(:num)/preview/eng', 'ReportController::preview/$1/1'); -}); - -// Print access for CS role only -$routes->group('report/print', ['filter' => 'role:4'], function ($routes) { - $routes->get('(:num)', 'ReportController::print/$1'); - $routes->get('(:num)/eng', 'ReportController::print/$1/1'); + $routes->get('(:num)/print', 'ReportController::print/$1'); + $routes->get('(:num)/print/eng', 'ReportController::print/$1/1'); }); // Keep backward compatibility - updated filter diff --git a/app/Controllers/ReportController.php b/app/Controllers/ReportController.php index 8800077..c84e3fd 100644 --- a/app/Controllers/ReportController.php +++ b/app/Controllers/ReportController.php @@ -15,7 +15,25 @@ class ReportController extends BaseController helper(['url', 'text']); } - public function generate($accessnumber, $eng = 0, $preview = 0) + public function generate($accessnumber, $eng = 0, $ispdf = 0) + { + if ($ispdf == 0) { + $ispdf = $this->request->getVar('ispdf') ?? 0; + } + + return $this->renderReport($accessnumber, $eng, $ispdf, false); + } + + public function print($accessnumber, $eng = 0, $ispdf = 0) + { + if ($ispdf == 0) { + $ispdf = $this->request->getVar('ispdf') ?? 0; + } + + return $this->renderReport($accessnumber, $eng, $ispdf, true); + } + + private function renderReport($accessnumber, $eng, $ispdf, $shouldLog) { $userroleid = session()->get('userroleid'); if (!in_array($userroleid, [0, 1, 2, 4])) { @@ -23,31 +41,17 @@ class ReportController extends BaseController } $data = $this->reportHelper->getReportData($accessnumber, $eng); - $data['preview'] = $preview; $data['eng'] = $eng; $data['accessnumber'] = $accessnumber; + $data['ispdf'] = $ispdf; - if ($preview == 0) { + if ($shouldLog == true) { $this->logPrintAudit($accessnumber, $data['status']); } return view('report/template', $data); } - public function preview($accessnumber, $eng = 0) - { - return $this->generate($accessnumber, $eng, 1); - } - - public function print($accessnumber, $eng = 0) - { - $userroleid = session()->get('userroleid'); - if ($userroleid != 4) { - return $this->response->setStatusCode(403)->setJSON(['message' => 'Unauthorized']); - } - return $this->generate($accessnumber, $eng, 0); - } - private function logPrintAudit($accessnumber, $status) { $sql = "INSERT INTO GDC_CMOD.dbo.AUDIT_REQUESTS(ACCESSNUMBER, STEPDATE, STEPTYPE, STEPSTATUS) diff --git a/app/Libraries/ReportHelper.php b/app/Libraries/ReportHelper.php index d548d37..76a4bbb 100644 --- a/app/Libraries/ReportHelper.php +++ b/app/Libraries/ReportHelper.php @@ -31,7 +31,7 @@ class ReportHelper } $valBy = $this->getValBy($accessnumber); - + $valData = $this->getValData($accessnumber); return [ 'hostnumber' => $hostnumber, 'result' => $result, @@ -43,6 +43,8 @@ class ReportHelper 'noSample' => $noSample, 'status' => $status, 'valBy' => $valBy, + 'val1User' => $valData['VAL1USER'] ?? '', + 'val2User' => $valData['VAL2USER'] ?? '', 'date' => date('d-m-Y H:i') ]; } @@ -70,22 +72,30 @@ class ReportHelper private function getData2(string $accessnumber): string { - $sql = "SELECT R.EXTERNALORDERNUMBER, FORMAT(SR.COLLECTIONDATE,'dd-MM-yyyy'), P.NAME, RIGHT(P.PATNUMBER,16), - dmg.DMG_CADDRESS, P.TELEPHON, P.EMAIL, + $sql = "SELECT R.EXTERNALORDERNUMBER AS EXTERNALORDERNUMBER, + FORMAT(SR.COLLECTIONDATE,'dd-MM-yyyy') AS REQDATE, + P.NAME AS NAME, + RIGHT(P.PATNUMBER,16) AS PATNUM, + dmg.DMG_CADDRESS AS DMG_CADDRESS, + P.TELEPHON AS TELEPHON, + P.EMAIL AS EMAIL, CASE WHEN P.SEX=1 THEN 'Male' WHEN P.SEX=2 THEN 'Female' ELSE 'Unknown' - END, + END AS SEX, CASE WHEN FORMAT(P.BIRTHDATE,'MMdd')=FORMAT(R.COLLECTIONDATE,'MMdd') THEN DATEDIFF(YEAR,P.BIRTHDATE, R.COLLECTIONDATE) ELSE FLOOR(DATEDIFF(DAY, P.BIRTHDATE, R.COLLECTIONDATE) / 365.25) - END, + END AS AGE, CASE WHEN DATEPART(day,R.COLLECTIONDATE) >= DATEPART(day,P.BIRTHDATE) THEN DATEDIFF(MONTH,P.BIRTHDATE, R.COLLECTIONDATE)%12 ELSE DATEDIFF(MONTH, P.BIRTHDATE, DATEADD(MONTH,-1,R.COLLECTIONDATE))%12 - END, - RO.COMMENTTEXT, dmg.DMG_CCITY, P.BIRTHDATE, T.SHORTTEXT + END AS AGEMONTHS, + RO.COMMENTTEXT AS COMMENTTEXT, + dmg.DMG_CCITY AS DMG_CCITY, + P.BIRTHDATE AS BIRTHDATE, + T.SHORTTEXT AS SHORTTEXT FROM REQUESTS R LEFT JOIN SP_REQUESTS SR ON SR.SP_ACCESSNUMBER=R.ACCESSNUMBER LEFT JOIN PATIENTS P ON P.PATID=R.PATID @@ -97,20 +107,20 @@ class ReportHelper $row = $this->db->query($sql, [$accessnumber])->getRowArray(); $regno = $row['EXTERNALORDERNUMBER'] ?? ''; - $reqdate = isset($row[1]) ? $row[1] : ''; + $reqdate = $row['REQDATE'] ?? ''; $pname = $row['NAME'] ?? ''; - $pnum = $row[2] ?? ''; + $pnum = $row['PATNUM'] ?? ''; $paddress = $row['DMG_CADDRESS'] ?? ''; $pphone = $row['TELEPHON'] ?? ''; $pemail = $row['EMAIL'] ?? ''; - $psex = $row[3] ?? ''; - $pAge = $row[4] ?? ''; - $pAgeM = $row[5] ?? ''; + $psex = $row['SEX'] ?? ''; + $pAge = $row['AGE'] ?? ''; + $pAgeM = $row['AGEMONTHS'] ?? ''; $rcomment = $row['COMMENTTEXT'] ?? ''; $pcity = $row['DMG_CCITY'] ?? ''; $pdob = ''; if (isset($row['BIRTHDATE']) && $row['BIRTHDATE'] != null) { - $pdob = date_format($row['BIRTHDATE'], 'd-m-Y'); + $pdob = date('d-m-Y', strtotime($row['BIRTHDATE'])); } $title = $row['SHORTTEXT'] ?? ''; if ($title != '') { @@ -202,7 +212,9 @@ class ReportHelper $_italic = ["UTRI","ITALIC","PLSFC", "PLSOV", "PLSML", "PLSVI"]; - $sql = "SELECT DC.FULLTEXT, DT.TESTCODE, T.VALIDATIONSTATUS, + $sql = "SELECT DC.FULLTEXT AS CHAPTER, + DT.TESTCODE AS TESTCODE, + T.VALIDATIONSTATUS AS VALIDATIONSTATUS, RESULT = CASE WHEN T.RESTYPE=0 THEN 'Pending' WHEN T.RESTYPE=4 AND T.RESVALUE='' AND T.RESSTATUS=1 THEN '.' @@ -217,13 +229,22 @@ class ReportHelper END ELSE T.RESVALUE END, - T.MINIMUM, T.MAXIMUM, - DT.FULLTEXT, - DT.RESPRECISION, DT.RESPRECISION2, DT.OPERAND, DT.SOFTCONVERSION, DT.UNITS, DT.UNITS2, T.RESTYPE, VI.FULLTEXT, + T.MINIMUM AS MINIMUM, + T.MAXIMUM AS MAXIMUM, + DT.FULLTEXT AS FULLTEXT, + DT.RESPRECISION AS PRECISION1, + DT.RESPRECISION2 AS PRECISION2, + DT.OPERAND AS OPERAND, + DT.SOFTCONVERSION AS SOFTCONVERSION, + DT.UNITS AS U1, + DT.UNITS2 AS U2, + T.RESTYPE AS RESTYPE, + VI.FULLTEXT AS I, CASE WHEN TC.COMMENTTEXT IS NULL THEN DX2.FULLTEXT ELSE TC.COMMENTTEXT - END, T.RERUN + END AS RESCOM, + T.RERUN AS RERUN FROM TESTS T JOIN DICT_TESTS DT ON DT.TESTID=T.TESTID LEFT JOIN DICT_TEXTS DX ON DX.TEXTID=T.CODEDRESULTID @@ -248,25 +269,25 @@ class ReportHelper $RERUN = 1; foreach ($rows as $row) { - $CHAPTER = $row[0]; - $TESTCODE = $row[1]; - $VALIDATIONSTATUS = $row[2]; - $R1 = $row[3]; + $CHAPTER = $row['CHAPTER']; + $TESTCODE = $row['TESTCODE']; + $VALIDATIONSTATUS = $row['VALIDATIONSTATUS']; + $R1 = $row['RESULT']; if ($R1 == '****') { $R1 = '-'; } - $L1 = $row[4]; - $H1 = $row[5]; - $FULLTEXT = $row[6]; - $PRECISION1 = $row[7]; - $PRECISION2 = $row[8]; - $OPERAND = $row[9]; - $SOFTCONVERSION = $row[10]; - $U1 = $row[11]; - $U2 = $row[12]; - $RESTYPE = $row[13]; - $I = $row[14]; - $RESCOM = $row[15]; + $L1 = $row['MINIMUM']; + $H1 = $row['MAXIMUM']; + $FULLTEXT = $row['FULLTEXT']; + $PRECISION1 = $row['PRECISION1']; + $PRECISION2 = $row['PRECISION2']; + $OPERAND = $row['OPERAND']; + $SOFTCONVERSION = $row['SOFTCONVERSION']; + $U1 = $row['U1']; + $U2 = $row['U2']; + $RESTYPE = $row['RESTYPE']; + $I = $row['I']; + $RESCOM = $row['RESCOM']; if ($eng == 1) { $ICHAPTER = substr($CHAPTER, strpos($CHAPTER, '#E') + 2, strrpos($CHAPTER, '#E') - strpos($CHAPTER, '#E') - 2); @@ -299,7 +320,7 @@ class ReportHelper $raw[$i] .= $ITEXT; } - $RERUN = $row[16]; + $RERUN = $row['RERUN']; } else { if (substr($R1, 0, 2) == '< ' && is_numeric(substr($R1, 2, strlen($R1)))) { $r1 = substr($R1, 2, strlen($R1)); @@ -512,13 +533,13 @@ class ReportHelper if ($posR1 != $posR12) { $ITEXT = $this->f_repl($ITEXT, $R1.' '.$F, $posR12); } - $ITEXT = $this->f_repl($ITEXT, $L1, $posL1); + $ITEXT = $this->f_repl($ITEXT, $L1 ?? '', $posL1); if ($posL1 != $posL12) { - $ITEXT = $this->f_repl($ITEXT, $L1, $posL12); + $ITEXT = $this->f_repl($ITEXT, $L1 ?? '', $posL12); } - $ITEXT = $this->f_repl($ITEXT, $H1, $posH1); + $ITEXT = $this->f_repl($ITEXT, $H1 ?? '', $posH1); if ($posH1 != $posH12) { - $ITEXT = $this->f_repl($ITEXT, $H1, $posH12); + $ITEXT = $this->f_repl($ITEXT, $H1 ?? '', $posH12); } if (isset($R2)) { $ITEXT = $this->f_repl($ITEXT, $R2.' '.$F, $posR2); @@ -527,16 +548,16 @@ class ReportHelper } } if (isset($L2)) { - $ITEXT = $this->f_repl($ITEXT, $L2, $posL2); + $ITEXT = $this->f_repl($ITEXT, $L2 ?? '', $posL2); } if ($posL2 != $posL22) { - $ITEXT = $this->f_repl($ITEXT, $L2, $posL22); + $ITEXT = $this->f_repl($ITEXT, $L2 ?? '', $posL22); } if (isset($H2)) { - $ITEXT = $this->f_repl($ITEXT, $H2, $posH2); + $ITEXT = $this->f_repl($ITEXT, $H2 ?? '', $posH2); } if ($posH2 != $posH22) { - $ITEXT = $this->f_repl($ITEXT, $H2, $posH22); + $ITEXT = $this->f_repl($ITEXT, $H2 ?? '', $posH22); } if ($I == 'Negative' || $I == 'Negatif') { $I1 = "Negatif"; @@ -544,16 +565,16 @@ class ReportHelper $ITEXT = $this->f_repl($ITEXT, $I1, $posI1); $ITEXT = $this->f_repl($ITEXT, $I2, $posI2); } else { - $ITEXT = $this->f_repl($ITEXT, $I, $posI1); - $ITEXT = $this->f_repl($ITEXT, $I, $posI2); + $ITEXT = $this->f_repl($ITEXT, $I ?? '', $posI1); + $ITEXT = $this->f_repl($ITEXT, $I ?? '', $posI2); } - $ITEXT = $this->f_repl($ITEXT, $U1, $posU1); - $ITEXT = $this->f_repl($ITEXT, $U2, $posU2); + $ITEXT = $this->f_repl($ITEXT, $U1 ?? '', $posU1); + $ITEXT = $this->f_repl($ITEXT, $U2 ?? '', $posU2); } elseif (in_array($RESTYPE, [2, 0, 5])) { if (strlen($RESCOM) < 2) { if ($TESTCODE == 'BUCRR') { - $ITEXT = $this->f_repl($ITEXT, $R1, $posR1); - $ITEXT = $this->f_repl($ITEXT, $R1, $posR2); + $ITEXT = $this->f_repl($ITEXT, $R1 ?? '', $posR1); + $ITEXT = $this->f_repl($ITEXT, $R1 ?? '', $posR2); } else { $ITEXT = substr($ITEXT, 0, $posR1); $ITEXT .= $R1." "; @@ -618,7 +639,7 @@ class ReportHelper private function getOthers(string $accessnumber, int $eng): string { - $sql = "SELECT DT.FULLTEXT FROM TESTS T + $sql = "SELECT DT.FULLTEXT AS FULLTEXT FROM TESTS T LEFT JOIN REQUESTS R ON R.REQUESTID=T.REQUESTID LEFT JOIN DICT_TESTS DT ON DT.TESTID=T.TESTID WHERE R.ACCESSNUMBER=? AND ISNUMERIC(DT.TESTCODE)=1 @@ -629,7 +650,7 @@ class ReportHelper $i = 1; $raw = ""; foreach ($rows as $row) { - $text = $row[0]; + $text = $row['FULLTEXT']; if ($eng == 1) { $text = substr($text, strpos($text, '#E') + 2, strrpos($text, '#E') - strpos($text, '#E') - 2); } else { @@ -654,25 +675,25 @@ class ReportHelper private function getCollData(string $accessnumber): string { $collData = ""; - $sql = "SELECT DISTINCT FORMAT(COLLECTIONDATE,'dd-MM-yyyy'), FORMAT(COLLECTIONDATE,'HH:mm'), x = STUFF( + $sql = "SELECT DISTINCT FORMAT(COLLECTIONDATE,'dd-MM-yyyy') AS COLLDATE, FORMAT(COLLECTIONDATE,'HH:mm') AS COLLTIME, STUFF( (SELECT ', ' + dst.SHORTTEXT FROM GDC_CMOD.dbo.TUBES t1 LEFT JOIN glendb.dbo.DICT_SAMPLES_TYPES dst ON t1.TUBENUMBER=dst.SAMPCODE WHERE t1.ACCESSNUMBER=t.ACCESSNUMBER AND FORMAT(t1.COLLECTIONDATE,'dd-MM-yyyy HH:mm')=FORMAT(t.COLLECTIONDATE,'dd-MM-yyyy HH:mm') FOR XML PATH('')), - 1,1, '') + 1,1, '') AS SAMPLES FROM GDC_CMOD.dbo.TUBES t WHERE t.ACCESSNUMBER=? AND STATUS=1"; $stmt = $this->db->query($sql, [$accessnumber]); $rows = $stmt->getResultArray(); $date1 = ''; foreach ($rows as $row) { - if ($date1 == $row[0]) { - $collData .= $row[1].$row[2].'. '; + if ($date1 == $row['COLLDATE']) { + $collData .= $row['COLLTIME'].$row['SAMPLES'].'. '; } else { - $collData .= $row[0].' '.$row[1].$row[2].'. '; + $collData .= $row['COLLDATE'].' '.$row['COLLTIME'].$row['SAMPLES'].'. '; } - $date1 = $row[0]; + $date1 = $row['COLLDATE']; } return $collData; } @@ -680,7 +701,7 @@ class ReportHelper private function getRecvData(string $accessnumber): string { $recvData = ""; - $sql = "SELECT ds.SHORTTEXT, FORMAT(st.COLLECTIONDATE,'dd-MM-yyyy'), FORMAT(st.COLLECTIONDATE,'HH:mm') FROM SP_TUBES st + $sql = "SELECT ds.SHORTTEXT AS SHORTTEXT, FORMAT(st.COLLECTIONDATE,'dd-MM-yyyy') AS RECVDATE, FORMAT(st.COLLECTIONDATE,'HH:mm') AS RECVTIME FROM SP_TUBES st LEFT JOIN DICT_SAMPLES_TYPES ds ON ds.SAMPCODE=st.SAMPLETYPE WHERE st.SP_ACCESSNUMBER=? AND st.TUBESTATUS=4"; $stmt = $this->db->query($sql, [$accessnumber]); @@ -689,9 +710,9 @@ class ReportHelper $date1 = ''; $time1 = ''; foreach ($rows as $row) { - $x = $row[0]; - $date = $row[1]; - $time = $row[2]; + $x = $row['SHORTTEXT']; + $date = $row['RECVDATE']; + $time = $row['RECVTIME']; if ($date1 == $date) { if ($time1 == $time) { $recvData .= $x.'. '; @@ -709,7 +730,7 @@ class ReportHelper private function getNoSample(string $accessnumber): string { - $sql = "SELECT DST.SHORTTEXT FROM SP_TUBES ST + $sql = "SELECT DST.SHORTTEXT AS SHORTTEXT FROM SP_TUBES ST LEFT JOIN DICT_SAMPLES_TYPES DST ON DST.SAMPCODE=ST.TUBETYPE WHERE ST.SP_ACCESSNUMBER=? AND ST.TUBESTATUS<>4"; $stmt = $this->db->query($sql, [$accessnumber]); @@ -717,7 +738,7 @@ class ReportHelper $noSample = ''; foreach ($rows as $row) { - $sample = $row[0]; + $sample = $row['SHORTTEXT']; $noSample .= " $sample No Sample \r"; } return $noSample; @@ -756,4 +777,10 @@ class ReportHelper } return $valBy; } + + private function getValData(string $accessnumber): array { + $sql = "SELECT TOP 1 * FROM GDC_CMOD.dbo.CM_REQUESTS WHERE ACCESSNUMBER=?"; + $row = $this->db->query($sql, [$accessnumber])->getRowArray(); + return $row ?? []; + } } diff --git a/app/Views/report/template.php b/app/Views/report/template.php index 2dd6169..36db370 100644 --- a/app/Views/report/template.php +++ b/app/Views/report/template.php @@ -5,14 +5,14 @@ Lab Report - <?= esc($accessnumber) ?> '> - '> + + '> + + '> + - -
PREVIEW ONLY - DO NOT PRINT
- -
- ' class='img'/> + + ' class='img'/> +
@@ -77,25 +79,24 @@ $i = 1;
- ' class='img img-footer'/> + + ' class='img img-footer'/> +
- -
PREVIEW ONLY - DO NOT PRINT
+ + ' class='img'/> - ' class='img'/> -
@@ -130,21 +129,19 @@ endforeach; Status :
Collected on 
 Received on 
+Val1 By :  | Val2 By  
 Page / Printed By :  
-
-
-(__________________)
-Authorised Signature
-                        
+
”This result is valid without signature.”
-
- ' class='img img-footer'/> + + ' class='img img-footer'/> + diff --git a/public/assets/report/style.css b/public/assets/report/style.css index ce674be..ee2eabc 100644 --- a/public/assets/report/style.css +++ b/public/assets/report/style.css @@ -5,9 +5,9 @@ body { -webkit-print-color-adjust:exact; } /*width: 210mm; height: 297mm;*/ width: 210mm; height: 297mm; } -#dinfo { float:right; margin:4cm 0.8cm 0 0; } -#dresult { float:left; margin: 0.2cm 0.8cm 0 1.5cm; height: 16.5cm; } -#footer { float:left; margin:0cm 2cm 0 1cm; height:3cm; } +#dinfo { float:right; margin:2cm 0.8cm 0 0; } +#dresult { float:left; margin: 0.2cm 0.8cm 0 1.5cm; height: 17.5cm; } +#footer { float:left; margin:0cm 2cm 0 1.5cm; height:2cm; } table {border-collapse:collapse;} td {vertical-align:top;} @@ -18,12 +18,14 @@ th,td { line-height:1.3;} .flag { float:right; top:0; font-weight:bold; } .result { table-layout:fixed; border:solid 1px black; width:100%; } .textC { font-size:7pt; } -.footer {width : 17cm; } +.footer {width : 18cm; } .footer td {vertical-align:bottom;} td.right { text-align: right; } #notes { margin: 5mm 0 0 10mm; } .footer-img { visibility:hidden; } +.img { max-width: 100%; height: auto; } +.img-footer { max-width: 100%; height: auto; max-height: 2.5cm; } pre.small {font-size:6pt;} \ No newline at end of file