{{-- Smart Report v2 - CBR meter card. Mirrors per-card layout from --}} {{-- SmartReportService::GetCbrPDF (lines 187-2572). All inner positions --}} {{-- are in mm relative to the (x, y) origin of the card. --}} {{-- v1 `continue`s before drawing anything when the package has no --}} {{-- eNDUSERPACKAGEREFERENCE (v1:241-245), so the card slot stays blank. --}} @if (!empty($Package->eNDUSERPACKAGEREFERENCE)) @php /** @var \App\Services\SmartReportV2Service $service */ $service = $service ?? app(\App\Services\SmartReportV2Service::class); $model = $service->buildCardViewModel($Package); $meter = $model['meter_offsets'] ?? ['top' => 1, 'left' => 9, 'width' => 45]; // Title bar font auto-sizing (v1 lines 296-318), computed by the service. $titleFontSize = $model['title_font_size'] ?? 10; // TCPDF Cell alignment -> CSS text-align. $alignMap = ['L' => 'left', 'C' => 'center', 'R' => 'right']; @endphp
{{-- Card background — v2 uses its own teal-recoloured copy so v1's shared result-ref.png stays pink (at x+6, width 84mm). --}} {{-- Test name (white text on the coloured header bar). LTR-isolate any --}} {{-- embedded Latin/symbol run ("(MCHC)", "(MCH)", …) so RTL order keeps --}} {{-- the parentheses in place instead of flipping them. --}} {{-- Card title is BOLD to match v1: setPatiantInfoWithAdvice leaves the --}} {{-- TCPDF font in bold and GetCbrPDF draws this Cell without resetting it. --}} @php // A very long title ("فايروس الايدز النوع 1&2 - الأجسام المضادة …") // overruns the 72mm title bar on a single line. For long titles drop a // step to 7pt and allow the text to wrap onto two lines inside the 7mm // bar; short titles keep the single-line 7mm line-box. $titleLen = mb_strlen((string) $model['test_name_ar'], 'UTF-8'); $titleWrap = $titleLen > 34; // Very long titles get 6pt so they fit the 72mm bar (wrapping to a // second line inside the 7mm bar when still too wide). $titlePt = $titleWrap ? ($titleLen > 44 ? 6 : 7) : $titleFontSize; @endphp
{!! $service->isolateLtrRuns($model['test_name_ar']) !!}
{{-- ── Meter island: gauge PNG + threshold labels as one positioned unit. ── --}} {{-- The island sits at the card origin (0,0), so every inner offset below is --}} {{-- byte-for-byte the card-relative value it always had; nudging the whole --}} {{-- gauge now means changing the island's top/left, not each element. --}}
{{-- Meter counter image picked by SmartReportV2Service --}} @if (!empty($model['meter_image']) && is_file($model['meter_image'])) @endif {{-- Threshold labels (numbers + LOW/MID/HIGH titles). --}} {{-- Each mirrors a TCPDF Cell($width, 7, TEXT, 0, 0, $align) call: --}} {{-- the cell is $width mm wide, 7 mm tall (text vertically centred), --}} {{-- anchored at the SetXY position. --}} @php // Collision pre-pass (QA: "132غير محدد" / "29.9غير كافي" on the 5-zone // vitamin gauges): v1's anchors let a zone title and a threshold number // share a line and overlap. Estimate each left-anchored title's width // (~0.185mm per char per pt) and, when a number on the same line starts // inside that span, slide the title left so it ends 0.8mm before the // number (mirrored for numbers on the title's left). $thLabels = $model['threshold_labels']; $numRe = '/^[\s0-9.,+\-<>=%]+$/u'; // The APEX title: when exactly ONE non-numeric label sits at the top of // the arc (top <= 6mm) it is the middle-zone title (e.g. مقاومة السكر, // Grayzone) and gets centred on the meter's midpoint. 5-zone gauges // have TWO high diagonal titles (غير محدد + طبيعي), which keep their // anchors. $apexIdx = null; $apexCount = 0; foreach ($thLabels as $k => $T) { $tt = trim((string) $T['text']); if ($tt !== '' && !preg_match($numRe, $tt) && (($T['top'] ?? 99) <= 6)) { $apexCount++; $apexIdx = $k; } } if ($apexCount !== 1) $apexIdx = null; foreach ($thLabels as $i => $L) { $lt = trim((string) $L['text']); if ($lt === '' || preg_match($numRe, $lt) || ($L['align'] ?? 'L') !== 'L' || ($L['width'] ?? 85) < 85) continue; // mirror $isZoneTitle's guard // Effective RENDER width, mirroring the branches below: 12+-char // titles render inside a 16mm wrap box; shorter ones use the // graduated single-line font (the model's font_size can be tiny, // e.g. 4pt, while the blade floors the render font at 5-5.5pt). $llen = mb_strlen($lt, 'UTF-8'); if ($llen > 11) { $lw = 16; } else { $eff = ($llen > 9) ? max(5, ($L['font_size'] ?? 7) - 2) : ($L['font_size'] ?? 7); $lw = $llen * $eff * 0.185; } foreach ($thLabels as $j => $N) { if ($i === $j) continue; $nt = trim((string) $N['text']); if ($nt === '' || !preg_match($numRe, $nt)) continue; if (abs(($L['top'] ?? 0) - ($N['top'] ?? 0)) > 2.5) continue; $numLeft = $N['left'] ?? 0; $numRight = $numLeft + mb_strlen($nt, 'UTF-8') * (($N['font_size'] ?? 7) * 0.16); $titleLeft = $thLabels[$i]['left'] ?? 0; if ($numLeft >= $titleLeft && $titleLeft + $lw > $numLeft - 0.8) { // number to the title's right: slide the title left $thLabels[$i]['left'] = max(0, $numLeft - $lw - 0.8); } elseif ($numLeft < $titleLeft && $numRight + 0.8 > $titleLeft) { // number to the title's left: slide the title right $thLabels[$i]['left'] = $numRight + 0.8; } } } @endphp @foreach ($thLabels as $thIdx => $label) @php $align = $label['align'] ?? 'L'; $width = $label['width'] ?? 85; // Numeric labels (thresholds like "5.6" or "-3") must render LTR // so the RTL page context cannot flip minus signs / decimals. $labelText = (string) $label['text']; $isNumericLabel = (bool) preg_match('/^[\s0-9.,+\-<>=%]+$/u', $labelText); // Zone TITLE labels (LOW/MID/HIGH — the coloured Arabic words under // the gauge, drawn left-anchored with a nominal 85mm "cell"). A long // title like "يمكن تأكيد الصحة" flows right from its anchor and // collides with the neighbouring zone label. Keep the exact left // anchor (so short labels are untouched) but shrink long ones a step // so they stay within their ~half of the gauge. $isZoneTitle = !$isNumericLabel && $width >= 85 && ($align === 'L'); $zoneLen = mb_strlen(trim($labelText), 'UTF-8'); $baseFont = $label['font_size'] ?? 7; // Long zone titles ("يمكن تأكيد الصحة", 12+ chars): a nowrap line — // however small the font — flows sideways from its anchor into the // gauge arc (QA: HIV card). WRAP those into a narrow 2-line block // that stays in the corner beside the arc instead. Moderately long // titles keep the single-line graduated shrink; short labels // ("مشبوه", "مرتفع") are untouched. $zoneWrap = $isZoneTitle && $zoneLen > 11; // Latin zone titles ("NEGATIVE" / "POSITIVE" / "Grayzone", QA): // the anchors were designed for short Arabic words — Latin at 7pt // was wide enough to reach into the arc feet. At 6pt the text // clears the artwork from its own anchor, so it stays on the SAME // line as the counter's base (aligned with the arc bottom, like // the Arabic labels). $isLatinZone = !$isNumericLabel && $width >= 85 && preg_match('/[A-Za-z]/', $labelText); // Symmetric ~2.5mm breathing room between each feet label and the // counter: left label steps 1.5mm out, right (centre-aligned) label // was ~2mm further out than the left so it nets to -0.5mm. $zoneShift = 0; if ($isLatinZone && ($label['top'] ?? 0) > 12) { $zoneShift = ($align === 'C') ? -0.5 : -1.5; } // Sole top-of-arc title (Arabic or Latin — مقاومة السكر, Grayzone): // centre it on the counter's apex instead of flowing from its anchor. $zoneMidCentre = ($apexIdx !== null && $thIdx === $apexIdx) || ($isLatinZone && ($label['top'] ?? 0) <= 12); if ($isZoneTitle) { if ($zoneLen > 20) $labelFont = max(4.5, $baseFont - 3.5); elseif ($zoneLen > 15) $labelFont = max(5.5, $baseFont - 1.5); elseif ($zoneLen > 11) $labelFont = max(5.5, $baseFont - 1.5); elseif ($zoneLen > 9) $labelFont = max(5, $baseFont - 2); else $labelFont = $baseFont; } else { $labelFont = $baseFont; } // Applies to Latin titles of ANY alignment (POSITIVE is align C), // so left- and centre-aligned labels share one size and baseline. if ($isLatinZone) $labelFont = min($labelFont, 6); @endphp @if ($zoneMidCentre)
{{ $labelText }}
@elseif ($zoneWrap) {{-- Wrapped 2-line zone title: fixed 16mm box, lines stack under the --}} {{-- zone anchor and stay clear of the meter arc. --}}
{{ $labelText }}
@else
{{ $labelText }}
@endif @endforeach
{{-- /.sr-meter --}} {{-- Numeric result: v1 draws SYMBOL DECIMAL UNIT in DejaVuSans 10pt --}} {{-- (9pt for long units), right-aligned Cell(14,46) at (x+70, y-5), --}} {{-- in the text colour left by the case branch. Skipped for 5/6/22/23. --}} @if ($model['show_value'] ?? true) {{-- Anchored by `right` (card is 100mm wide, cell right edge = x+84): TCPDF's Cell(14,46,'R') pins the RIGHT edge and overflows long values ("100 mg/day", "10000 pmol/L") to the LEFT. A CSS left+width box would overflow rightward instead, so anchor the right edge and let the shrink-to-fit box grow leftward. --}} @php // Drop a "unit" that is only punctuation (some rows store "." as the // unit → rendered as a stray trailing dot after the value). $cbrUnit = trim((string) ($model['result_unit'] ?? '')); if ($cbrUnit !== '' && !preg_match('/[\p{L}\p{N}%\/]/u', $cbrUnit)) $cbrUnit = ''; @endphp
{{ trim(($model['result_display'] ?? '') . ' ' . $cbrUnit) }}
@endif {{-- Textual result (GP/BP/OF cases 5/6/19/22/23): bold Cell(14,46,'R') --}} @if (!empty($model['value_label'])) {{-- Right-edge anchored like the numeric value: v1's Cell(w,46,'R') pins the right edge at left+width and overflows leftward. --}}
{{ $model['value_label']['text'] }}
@endif {{-- Qualitative result word (Positive / Negative / custom title) for the --}} {{-- GP/BP/19/22/23 ranges. Mirrors v1's right-side Cell at (x+55..70, y-5). --}} @if (!empty($model['qual_result']) && ($model['qual_result']['text'] ?? '') !== '')
{{ $model['qual_result']['text'] }}
@endif {{-- Short Arabic description (multiline). LTR-isolate embedded English --}} {{-- runs ("Syphilis – Treponema-Anti", "Screening: Total Abs", …) so the --}} {{-- RTL paragraph keeps them intact instead of scrambling them. --}}
{!! nl2br($service->isolateLtrRuns($model['description_ar'] ?? '')) !!}
@endif