db_onedev = $this->load->database('onedev', true); $this->load->library('ibl_patient_decrypt'); } public function index() { $this->sys_ok([ 'message' => 'Use /tools/inform_consent/pdf?T_OrderHeaderID=' ]); } public function pdf() { try { $orderHeaderId = intval($this->input->get('T_OrderHeaderID', true)); if ($orderHeaderId <= 0) { $orderHeaderId = intval($this->input->get('PID', true)); } if ($orderHeaderId <= 0) { $this->sys_error('T_OrderHeaderID mandatory'); return; } $patient = $this->get_patient_data($orderHeaderId); if (!$patient) { $this->sys_error('Order not found'); return; } $consent = $this->get_consent_content(); $printBy = trim((string)$this->input->get('username', true)); if ($printBy === '') { $printBy = 'ADMIN'; } $printDate = date('M j, Y g:i A'); require_once APPPATH . 'third_party/fpdf/fpdf.php'; $pdf = new FPDF('P', 'mm', 'A4'); $pdf->SetAutoPageBreak(false); $pdf->SetMargins(10, 10, 10); $remainingBlocks = $this->render_first_page($pdf, $patient, $consent, $printBy, $printDate); $this->render_second_page($pdf, $patient, $remainingBlocks, $printBy, $printDate); header('Content-Type: application/pdf'); header('Content-Disposition: inline; filename="inform_consent_' . $orderHeaderId . '.pdf"'); echo $pdf->Output('S'); } catch (Exception $e) { $this->sys_error($e->getMessage()); } } private function get_patient_data($orderHeaderId) { $cacheId = $this->ibl_patient_decrypt->populate_cache_by_order($orderHeaderId); $sql = "SELECT h.T_OrderHeaderID, h.T_OrderHeaderDate, DATE_FORMAT(h.T_OrderHeaderDate, '%d-%m-%Y') as tanggal, DATE_FORMAT(h.T_OrderHeaderDate, '%H:%i') as jam, p.M_PatientID, p.M_PatientNoReg, p.M_PatientPrefix, p.M_PatientSuffix, t.M_TitleName, s.M_SexCode, p.M_PatientName_enc, p.M_PatientDOB_enc, p.M_PatientPOB_enc, p.M_PatientDOB, p.M_PatientHP_enc, pa.M_PatientAddressDescription_enc, IFNULL(c.M_CompanyName,'') as company_name, ( SELECT ps.Patient_SignatureUrl FROM patient_signature ps WHERE ps.Patient_SignatureM_PatientID = p.M_PatientID AND ps.Patient_SignatureIsActive = 'Y' ORDER BY ps.Patient_SignatureID DESC LIMIT 1 ) AS signature_url FROM t_orderheader h JOIN m_patient p ON p.M_PatientID = h.T_OrderHeaderM_PatientID LEFT JOIN m_title t ON t.M_TitleID = p.M_PatientM_TitleID LEFT JOIN m_sex s ON s.M_SexID = p.M_PatientM_SexID LEFT JOIN m_company c ON c.M_CompanyID = h.T_OrderHeaderM_CompanyID LEFT JOIN m_patientaddress pa ON pa.M_PatientAddressM_PatientID = p.M_PatientID AND pa.M_PatientAddressNote = 'Utama' AND pa.M_PatientAddressIsActive = 'Y' WHERE h.T_OrderHeaderID = ? LIMIT 1"; $qry = $this->db_onedev->query($sql, [$orderHeaderId]); if (!$qry) return false; $row = $qry->row_array(); if (!$row) return false; $row = $this->ibl_patient_decrypt->decrypt_row($row); $title = trim($row['M_TitleName'] ?? ''); $cacheRow = $this->db_onedev->query( "SELECT ppc_name FROM patient_print_cache WHERE ppc_order_id = ? ORDER BY ppc_id DESC LIMIT 1", [$orderHeaderId] )->row_array(); $printName = trim($cacheRow['ppc_name'] ?? ''); $row['patient_name'] = trim( ($title ? $title . '. ' : '') . trim($row['M_PatientPrefix'] ?? '') . ' ' . trim($printName !== '' ? $printName : ($row['M_PatientName'] ?? '')) . ' ' . trim($row['M_PatientSuffix'] ?? '') ); $row['dob'] = $row['M_PatientDOB']; $row['pob'] = $row['M_PatientPOB'] ?? ''; $row['phone'] = $row['M_PatientHP'] ?? ''; $row['alamat'] = $row['M_PatientAddressDescription'] ?? ''; $this->ibl_patient_decrypt->delete_cache($cacheId); return $row; } private function get_consent_content() { $sql = "SELECT M_InformConsentContent FROM m_informconsent WHERE M_InformConsentType = 'umum' AND M_InformConsentIsActive = 'Y' LIMIT 1"; $qry = $this->db_onedev->query($sql); if (!$qry || !$qry->row_array()) { return ''; } return (string)$qry->row_array()['M_InformConsentContent']; } private function render_first_page($pdf, $patient, $consentHtml, $printBy, $printDate) { $pdf->AddPage(); $this->render_header($pdf); $pdf->SetFont('Arial', 'B', 10); $pdf->SetFillColor(220, 220, 220); $pdf->SetXY(10, 42); $pdf->Cell(190, 8, 'PENGKAJIAN DATA UMUM PASIEN', 1, 1, 'C', true); $pdf->SetFont('Arial', '', 8); $pdf->SetX(10); $pdf->Cell(145, 6, 'Tanggal : ' . $patient['tanggal'], 1, 0, 'L'); $pdf->Cell(45, 6, 'Jam : ' . $patient['jam'], 1, 1, 'L'); $pdf->SetFont('Arial', 'B', 10); $pdf->SetX(10); $pdf->Cell(190, 8, 'IDENTITAS PASIEN', 1, 1, 'L'); $pdf->SetFont('Arial', 'I', 8); $pdf->SetX(10); $pdf->Cell(190, 5, '(Diisi sesuai Kartu Tanda Pengenal Pasien yang masih berlaku : KTP/ KK/ SIM/ Kartu Pelajar/ dsb)', 1, 1, 'L'); $boxStart = $pdf->GetY(); $pdf->SetFont('Arial', '', 8); $this->kv($pdf, 'Nomor RM', $patient['M_PatientNoReg']); $this->kv($pdf, 'Nama', trim(preg_replace('/\s+/', ' ', $patient['patient_name']))); $this->kv($pdf, 'Jenis Kelamin', $patient['M_SexCode']); $this->kv($pdf, 'Tempat/Tgl Lahir', trim($patient['pob']) . ' / ' . trim($patient['dob'])); $this->kv($pdf, 'Alamat', $patient['alamat']); $this->kv($pdf, 'No. Telepon', $patient['phone']); $this->kv($pdf, 'Status Pembiayaan', 'Umum / BPJS / PT..................................................'); $boxEnd = $pdf->GetY(); $pdf->Rect(10, $boxStart, 190, max(8, $boxEnd - $boxStart)); $pdf->Ln(4); $pdf->SetFont('Arial', 'B', 10); $pdf->SetFillColor(220, 220, 220); $pdf->SetX(10); $pdf->Cell(190, 7, 'PERSETUJUAN UMUM/ GENERAL CONSENT', 1, 1, 'C', true); $pdf->SetFont('Arial', 'BI', 8); $pdf->SetX(10); $pdf->Cell(190, 6, 'PASIEN/WALI PASIEN MEMBACA, MEMAHAMI DAN MENGISI INFORMASI BERIKUT', 1, 1, 'C'); $pdf->SetFont('Arial', '', 8); $pdf->SetX(10); $pdf->Cell(190, 6, 'Yang bertanda tangan di bawah ini, saya :', 1, 1, 'L'); $box2Start = $pdf->GetY(); $this->kv($pdf, 'Nama', trim(preg_replace('/\s+/', ' ', $patient['patient_name']))); $this->kv($pdf, 'Jenis Kelamin', $patient['M_SexCode']); $this->kv($pdf, 'Tempat/Tgl Lahir', trim($patient['pob']) . ' / ' . trim($patient['dob'])); $this->kv($pdf, 'Alamat', $patient['alamat']); $this->kv($pdf, 'No.Telepon', $patient['phone']); $this->kv($pdf, 'Bertindak atas', 'Diri Saya Sendiri/ Wali / Orang Tua / Anak / Saudara .*'); $contentBoxStart = $box2Start; $pdf->Ln(3); $pdf->SetFont('Arial', 'B', 8); $pdf->SetX(18); $pdf->Cell(182, 5, '1. PERSETUJUAN UNTUK PEMERIKSAAN DAN PERAWATAN', 0, 1, 'L'); $blocks = $this->html_to_blocks($consentHtml); $lineH = 4.6; $splitIndex = 0; $pdf->SetFont('Arial', '', 8); for ($i = 0; $i < count($blocks); $i++) { $block = $blocks[$i]; $blockH = $this->measure_block_height($pdf, $block, 22, 176, $lineH); if (($pdf->GetY() + $blockH) > $this->pageContentBottom) { break; } $this->render_block($pdf, $block, 22, 176, $lineH); $splitIndex = $i + 1; } $contentBoxEnd = min($this->pageContentBottom, $pdf->GetY() + 2); $pdf->Rect(10, $contentBoxStart, 190, max(10, $contentBoxEnd - $contentBoxStart)); $this->draw_print_footer($pdf, $printBy, $printDate, 1, 2); return array_slice($blocks, $splitIndex); } private function render_second_page($pdf, $patient, $remainingBlocks, $printBy, $printDate) { $pdf->AddPage(); $this->render_header($pdf); $boxStartY = 42; $pdf->SetY($boxStartY); $pdf->SetFont('Arial', '', 8); $lineH = 4.6; foreach ($remainingBlocks as $block) { $blockH = $this->measure_block_height($pdf, $block, 22, 176, $lineH); if (($pdf->GetY() + $blockH) > 248) { break; } $this->render_block($pdf, $block, 22, 176, $lineH); } $top = $pdf->GetY() + 2; $signTop = $top; $signBoxHeight = 44; $signBottom = $signTop + $signBoxHeight; $pdf->Rect(12, $signTop, 186, $signBoxHeight); $rightX = 132; $rightW = 64; $pdf->SetFont('Arial', '', 9); $pdf->SetXY($rightX, $signTop + 2); $pdf->Cell($rightW, 6, 'Semarang, ' . $patient['tanggal'], 0, 1, 'L'); $pdf->SetXY(14, $signBottom - 29); $pdf->Cell(40, 6, 'Petugas', 0, 1, 'C'); $pdf->SetXY($rightX, $signBottom - 29); $pdf->Cell($rightW, 6, 'Pasien/ Wali Pasien', 0, 1, 'L'); $pdf->SetXY(16, $signBottom - 9); $pdf->Cell(62, 6, '........................................', 0, 1, 'L'); $signaturePath = $this->resolve_signature_path(isset($patient['signature_url']) ? $patient['signature_url'] : ''); if ($signaturePath !== null) { $pdf->Image($signaturePath, $rightX - 6, $signBottom - 23, 42, 12); } $this->draw_full_name_block($pdf, $rightX, $signBottom - 9, $rightW, trim(preg_replace('/\s+/', ' ', $patient['patient_name']))); $boxEndY = min($this->pageContentBottom, $signBottom + 2); $pdf->Rect(10, $boxStartY, 190, max(10, $boxEndY - $boxStartY)); $this->draw_print_footer($pdf, $printBy, $printDate, 2, 2); } private function draw_print_footer($pdf, $printBy, $printDate, $page, $total) { $y = $this->footerY; $pdf->SetFont('Times', '', 7); $pdf->SetXY(14, $y); $pdf->Cell(22, 4, 'Print Oleh :', 0, 0, 'L'); $pdf->Cell(50, 4, $this->safe_text($printBy), 0, 0, 'L'); $pdf->Cell(10, 4, (string)$page, 0, 0, 'C'); $pdf->Cell(6, 4, '/', 0, 0, 'C'); $pdf->Cell(10, 4, (string)$total, 0, 0, 'C'); $date = $this->safe_text($printDate); $dateW = $pdf->GetStringWidth($date); $rightMargin = 196; $xDate = max(120, $rightMargin - $dateW); $pdf->SetXY($xDate, $y); $pdf->Cell($dateW + 1, 4, $date, 0, 1, 'L'); } private function render_header($pdf) { $logoPath = APPPATH . '../assets/images/logo-ibl.png'; if (is_file($logoPath)) { $pdf->Image($logoPath, 14, 10, 56, 12); } else { $pdf->SetFont('Arial', 'B', 16); $pdf->SetTextColor(220, 0, 0); $pdf->SetXY(14, 12); $pdf->Cell(80, 8, 'KLINIK IBL', 0, 1, 'L'); $pdf->SetTextColor(0, 0, 0); } $pdf->SetFont('Arial', '', 10); $pdf->SetXY(170, 20); $pdf->Cell(20, 6, 'RM. 01', 0, 1, 'L'); } private function kv($pdf, $label, $value) { $x = 12; $labelW = 35; $colonW = 4; $valueW = 149; $pdf->SetX($x); $pdf->Cell($labelW, 5, $this->safe_text($label), 0, 0, 'L'); $pdf->Cell($colonW, 5, ':', 0, 0, 'C'); $pdf->MultiCell($valueW, 5, $this->safe_text($value), 0, 'L'); } private function html_to_blocks($html) { $input = (string)$html; $input = str_replace(['\\r\\n', '\\n', '\\r'], "\n", $input); $input = str_replace(["\r\n", "\r"], "\n", $input); if (!class_exists('DOMDocument')) { return $this->html_to_blocks_fallback($input); } libxml_use_internal_errors(true); $dom = new DOMDocument('1.0', 'UTF-8'); $wrapped = '' . $input . ''; $ok = $dom->loadHTML(mb_convert_encoding($wrapped, 'HTML-ENTITIES', 'UTF-8')); libxml_clear_errors(); if (!$ok) { return []; } $blocks = []; $ctx = ['indent' => 0, 'listType' => null, 'index' => 1]; $body = $dom->getElementsByTagName('body')->item(0); if (!$body) { return []; } foreach ($body->childNodes as $node) { $this->walk_html_node($node, $blocks, $ctx); } return $blocks; } private function html_to_blocks_fallback($input) { $blocks = []; $s = (string)$input; $s = preg_replace('/\\\\r\\\\n|\\\\n|\\\\r/', "\n", $s); $s = preg_replace('//i', "\n", $s); $s = preg_replace('/<\\/(div|p|h[1-6]|li)>/i', "$0\n", $s); preg_match_all('/<(h[1-6]|p|li)[^>]*>(.*?)<\\/\\1>/is', $s, $matches, PREG_SET_ORDER); foreach ($matches as $m) { $tag = strtolower($m[1]); $inner = (string)$m[2]; // remove nested list blocks inside current node to avoid duplicate render $inner = preg_replace('/<(ul|ol)[^>]*>.*?<\\/\\1>/is', '', $inner); $plain = trim(preg_replace('/\\s+/', ' ', html_entity_decode(strip_tags($inner), ENT_QUOTES | ENT_HTML5, 'UTF-8'))); if ($plain === '' || $plain === ':') { continue; } $indent = 0; $style = ''; $text = $plain; if ($tag === 'h1' || $tag === 'h2' || $tag === 'h3' || $tag === 'h4' || $tag === 'h5' || $tag === 'h6') { $style = 'B'; $indent = 0; } elseif ($tag === 'li') { $indent = 8; $text = '• ' . $plain; } elseif ($tag === 'p') { if (stripos($inner, ' $text, 'indent' => $indent, 'style' => $style ]; } if (empty($blocks)) { $lines = preg_split('/\\n+/', trim(html_entity_decode(strip_tags($s), ENT_QUOTES | ENT_HTML5, 'UTF-8'))); foreach ($lines as $line) { $line = trim(preg_replace('/\\s+/', ' ', (string)$line)); if ($line === '') { continue; } $blocks[] = ['text' => $line, 'indent' => 0, 'style' => '']; } } return $blocks; } private function walk_html_node($node, &$blocks, $ctx) { if (!$node) { return; } if ($node->nodeType === XML_TEXT_NODE) { $text = trim(preg_replace('/\s+/', ' ', html_entity_decode($node->nodeValue, ENT_QUOTES | ENT_HTML5, 'UTF-8'))); if ($text !== '') { $blocks[] = [ 'text' => $text, 'indent' => $ctx['indent'], 'style' => '' ]; } return; } if ($node->nodeType !== XML_ELEMENT_NODE) { return; } $tag = strtolower($node->nodeName); if ($tag === 'br') { return; } if (in_array($tag, ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], true)) { $text = $this->node_plain_text($node); if ($text !== '') { $blocks[] = [ 'text' => $text, 'indent' => max(0, $ctx['indent']), 'style' => 'B' ]; } return; } if ($tag === 'p') { $text = $this->node_plain_text($node); if ($text !== '') { $isBold = $this->node_has_tag($node, 'strong'); $blocks[] = [ 'text' => $text, 'indent' => max(0, $ctx['indent']), 'style' => $isBold ? 'B' : '' ]; } return; } if ($tag === 'ul' || $tag === 'ol') { $childCtx = [ 'indent' => $ctx['indent'] + 8, 'listType' => $tag, 'index' => 1 ]; foreach ($node->childNodes as $child) { $this->walk_html_node($child, $blocks, $childCtx); if ($tag === 'ol' && $child->nodeType === XML_ELEMENT_NODE && strtolower($child->nodeName) === 'li') { $childCtx['index']++; } } return; } if ($tag === 'li') { $prefix = '• '; if ($ctx['listType'] === 'ol') { $prefix = $ctx['index'] . '. '; } $firstText = $this->node_direct_text($node); if ($firstText !== '') { $blocks[] = [ 'text' => $prefix . $firstText, 'indent' => max(0, $ctx['indent']), 'style' => '' ]; } foreach ($node->childNodes as $child) { if ($child->nodeType === XML_ELEMENT_NODE) { $childTag = strtolower($child->nodeName); if ($childTag === 'ul' || $childTag === 'ol' || $childTag === 'p') { $nestedCtx = [ 'indent' => $ctx['indent'] + 8, 'listType' => $childTag === 'ol' ? 'ol' : ($childTag === 'ul' ? 'ul' : null), 'index' => 1 ]; $this->walk_html_node($child, $blocks, $nestedCtx); } } } return; } foreach ($node->childNodes as $child) { $this->walk_html_node($child, $blocks, $ctx); } } private function node_plain_text($node) { $txt = html_entity_decode($node->textContent, ENT_QUOTES | ENT_HTML5, 'UTF-8'); $txt = preg_replace('/\s+/', ' ', (string)$txt); return trim($txt); } private function node_direct_text($node) { $parts = []; foreach ($node->childNodes as $child) { if ($child->nodeType === XML_TEXT_NODE) { $t = trim(preg_replace('/\s+/', ' ', html_entity_decode($child->nodeValue, ENT_QUOTES | ENT_HTML5, 'UTF-8'))); if ($t !== '') { $parts[] = $t; } } if ($child->nodeType === XML_ELEMENT_NODE && strtolower($child->nodeName) === 'strong') { $t = $this->node_plain_text($child); if ($t !== '') { $parts[] = $t; } } } return trim(implode(' ', $parts)); } private function node_has_tag($node, $tagName) { if (!$node || !$node->hasChildNodes()) { return false; } foreach ($node->childNodes as $child) { if ($child->nodeType === XML_ELEMENT_NODE && strtolower($child->nodeName) === strtolower($tagName)) { return true; } if ($this->node_has_tag($child, $tagName)) { return true; } } return false; } private function measure_block_height($pdf, $block, $baseX, $baseW, $lineH) { $indent = isset($block['indent']) ? floatval($block['indent']) : 0; $text = isset($block['text']) ? (string)$block['text'] : ''; $w = max(20, $baseW - $indent); $txt = $this->safe_text($text); $lines = $this->nb_lines($pdf, $w, $txt); if ($lines < 1) { $lines = 1; } return $lines * $lineH; } private function nb_lines($pdf, $w, $txt) { $text = (string)$txt; if ($text === '') { return 1; } $maxWidth = max(1.0, $w - 1.0); $paragraphs = explode("\n", str_replace("\r", '', $text)); $lines = 0; foreach ($paragraphs as $p) { $p = trim($p); if ($p === '') { $lines++; continue; } $words = preg_split('/\s+/', $p); $line = ''; foreach ($words as $word) { $candidate = ($line === '') ? $word : ($line . ' ' . $word); if ($pdf->GetStringWidth($candidate) <= $maxWidth) { $line = $candidate; } else { if ($line !== '') { $lines++; $line = $word; } else { $lines++; $line = ''; } } } if ($line !== '') { $lines++; } } return max(1, $lines); } private function render_block($pdf, $block, $baseX, $baseW, $lineH) { $indent = isset($block['indent']) ? floatval($block['indent']) : 0; $text = isset($block['text']) ? (string)$block['text'] : ''; $style = isset($block['style']) ? (string)$block['style'] : ''; $w = max(20, $baseW - $indent); $pdf->SetFont('Arial', $style, 8); $pdf->SetX($baseX + $indent); $pdf->MultiCell($w, $lineH, $this->safe_text($text), 0, 'L'); } private function safe_text($text) { $t = (string)$text; return iconv('UTF-8', 'windows-1252//TRANSLIT', $t); } private function draw_full_name_block($pdf, $x, $yBottom, $w, $name) { $text = $this->safe_text($name); $pdf->SetFont('Arial', '', 9); $lines = $this->wrap_text_lines($pdf, $text, $w); if (empty($lines)) { $lines = ['-']; } $lineH = 4.6; $blockH = count($lines) * $lineH; $topY = $yBottom - $blockH + 5.5; if ($topY < 10) { $topY = 10; } $pdf->SetXY($x, $topY); foreach ($lines as $i => $line) { $pdf->Cell($w, $lineH, $line, 0, 2, 'L'); } } private function wrap_text_lines($pdf, $text, $w) { $words = preg_split('/\s+/', trim((string)$text)); $lines = []; $line = ''; foreach ($words as $word) { if ($word === '') { continue; } $try = ($line === '') ? $word : ($line . ' ' . $word); if ($pdf->GetStringWidth($try) <= $w) { $line = $try; } else { if ($line !== '') { $lines[] = $line; } $line = $word; } } if ($line !== '') { $lines[] = $line; } return $lines; } private function resolve_signature_path($signatureUrl) { $url = trim((string)$signatureUrl); if ($url === '') { return null; } if (preg_match('#^https?://#i', $url)) { $tmp = sys_get_temp_dir() . '/sig_' . md5($url) . '.png'; if (!is_file($tmp)) { $ctx = stream_context_create([ 'http' => ['timeout' => 3], 'ssl' => ['verify_peer' => false, 'verify_peer_name' => false] ]); $bin = @file_get_contents($url, false, $ctx); if ($bin !== false && strlen($bin) > 0) { @file_put_contents($tmp, $bin); } } return is_file($tmp) ? $tmp : null; } if (strpos($url, '/') === 0) { if (strpos($url, '/one-media/') === 0) { $absMedia = rtrim($this->get_media_root(), '/') . $url; if (is_file($absMedia)) { return $absMedia; } $httpHost = isset($_SERVER['HTTP_HOST']) ? trim((string)$_SERVER['HTTP_HOST']) : ''; if ($httpHost === '') { $httpHost = '10.9.20.31'; } $httpUrl = 'http://' . $httpHost . $url; return $this->resolve_signature_path($httpUrl); } $abs = FCPATH . ltrim($url, '/'); if (is_file($abs)) { return $abs; } } $abs2 = FCPATH . 'assets/uploads/signature/' . ltrim($url, '/'); if (is_file($abs2)) { return $abs2; } return is_file($url) ? $url : null; } private function get_media_root() { if ($this->mediaRoot !== null) { return $this->mediaRoot; } $defaultRoot = '/home/one/project/one/'; $sql = "SELECT S_SystemsMediaUrl FROM conf_systems WHERE S_SystemsID > 0 LIMIT 1"; $qry = $this->db_onedev->query($sql); if ($qry && $qry->row()) { $root = trim((string)$qry->row()->S_SystemsMediaUrl); if ($root !== '') { $this->mediaRoot = rtrim($root, '/') . '/'; return $this->mediaRoot; } } $this->mediaRoot = $defaultRoot; return $this->mediaRoot; } }