@if(!empty($showHero))
@php $heroAnim = \App\Models\Setting::get('hero_animation_style', 'circles'); @endphp
@if($heroAnim !== 'clean')
@endif @switch($heroAnim) @case('circles')
@break @case('rain')
@break @case('waves')
@break @case('particles')
@break @case('parallax')
@break @default @endswitch @php $dateStr = $activity->date ? (method_exists($activity->date, 'format') ? $activity->date->format('d M Y') : (string) $activity->date) : null; @endphp

{{ data_get($activity,'title') ?? data_get($activity,'name') ?? 'Judul Kegiatan' }}

@if(!empty($activity->location))

{{ $activity->location }}

@endif @if(!empty($dateStr))

{{ $dateStr }}

@endif @if(!empty($activity->description))

{{ \Illuminate\Support\Str::limit(strip_tags($activity->description), 200) }}

@endif
@endif @if(empty($showHero)) @php $isValid = isset($participants) && count($participants) > 0; $p = $isValid ? $participants->first() : null; @endphp
Verifikasi Sertifikat
@if($isValid) {{ optional(optional($p)->user)->name ?? 'Peserta' }} @if(!empty($p->certificate_id)) ID: {{ $p->certificate_id }} @endif @else Sertifikat tidak ditemukan atau tidak valid @endif
@if($isValid) Sertifikat Asli @else Tidak Valid @endif
@endif @php $widthCmDefault = (float) data_get($certificateSetting, 'card.width_cm', 21); $heightCmDefault = (float) data_get($certificateSetting, 'card.height_cm', 29.7); $bgFilenameGlobal = data_get($certificateSetting, 'card.background'); if (!$bgFilenameGlobal) { $bgFilenameGlobal = \Illuminate\Support\Facades\DB::table('certificate_backgrounds') ->where('activity_id', $activity->id) ->orderBy('id', 'desc') ->value('filename'); } if (!function_exists('image_to_base64_data_uri')) { function image_to_base64_data_uri($path) { if (!file_exists($path) || !is_readable($path)) { return null; } $type = mime_content_type($path); if ($type === false) { $type = 'image/' . pathinfo($path, PATHINFO_EXTENSION); } $data = file_get_contents($path); return 'data:' . $type . ';base64,' . base64_encode($data); } } $certificatesPerPage = max(1, (int)($cols * $rows)); @endphp @if(!empty($showHero) || request()->has('debug'))
@endif
@foreach($participants->chunk($certificatesPerPage) as $pageIndex => $page) @if($pageIndex > 0)
@endif @foreach($page->chunk($cols) as $rowIndex => $row)
@foreach($row as $peserta) @php $profileParticipant = optional(optional($peserta)->user)->profile; $provinceParticipant = optional($profileParticipant->province)->name ?? ($profileParticipant->other_province ?? null); $regencyParticipant = optional($profileParticipant->regency)->name ?? ($profileParticipant->other_regency ?? null); $districtParticipant = optional($profileParticipant->district)->name ?? ($profileParticipant->other_district ?? null); // Apply certificate settings & Fit to Paper Logic (same as print_certificates_html) $origWidthCm = (float) data_get($certificateSetting, 'card.width_cm', 8.6); $origHeightCm = (float) data_get($certificateSetting, 'card.height_cm', 15); $widthCm = $origWidthCm; $heightCm = $origHeightCm; $paper = data_get($printSettings ?? [], 'paper', 'A4'); $orientation = data_get($printSettings ?? [], 'orientation', 'landscape'); $paperUpper = strtoupper($paper); $paperDims = [21.0, 29.7]; // default A4 cm switch ($paperUpper) { case 'A3': $paperDims = [29.7, 42.0]; break; case 'A4': $paperDims = [21.0, 29.7]; break; case 'A5': $paperDims = [14.8, 21.0]; break; case 'LETTER': $paperDims = [21.59, 27.94]; break; case 'LEGAL': $paperDims = [21.59, 35.56]; break; case 'F4': $paperDims = [21.5, 33.0]; break; case 'IDCARD': $paperDims = [5.398, 8.56]; break; } if ($orientation === 'landscape') { $paperDims = [$paperDims[1], $paperDims[0]]; } $marginCss = data_get($printSettings ?? [], 'margin', '0cm'); $marginVal = 0.0; if (is_string($marginCss)) { if (str_contains($marginCss, 'mm')) { $marginVal = (float) str_replace(['mm',' '],'', $marginCss) / 10.0; } else { $marginVal = (float) str_replace(['cm',' '],'', $marginCss); } } $sizeAdjustMm = (float) data_get($printSettings ?? [], 'size_adjust_mm', 0); $offsetTopMm = (float) data_get($printSettings ?? [], 'offset_top_mm', 0); $offsetLeftMm = (float) data_get($printSettings ?? [], 'offset_left_mm', 0); $sizeAdjustCm = $sizeAdjustMm / 10.0; $offsetTopCm = $offsetTopMm / 10.0; $offsetLeftCm = $offsetLeftMm / 10.0; $availableWidthCm = max(0.0, $paperDims[0] - 2*$marginVal); $availableHeightCm = max(0.0, $paperDims[1] - 2*$marginVal); $fitEpsilonCm = 0.1; if ((($cols ?? 1) == 1) && (($rows ?? 1) == 1)) { $availW = max(0.0, ($availableWidthCm - $fitEpsilonCm) + $sizeAdjustCm); $availH = max(0.0, ($availableHeightCm - $fitEpsilonCm) + $sizeAdjustCm); // Use Stretch to Fit (match print_certificates_html.blade.php) // This ensures that if the design was done on a stretched canvas, the preview matches it. $widthCm = $availW; $heightCm = $availH; } else { $widthCm = min(max(0.0, $widthCm + $sizeAdjustCm), $availableWidthCm - $fitEpsilonCm); $heightCm = min(max(0.0, $heightCm + $sizeAdjustCm), $availableHeightCm - $fitEpsilonCm); } // MATCH PRINTING LOGIC: Scale to fit paper, then scale elements $savedBaseW = (float) (data_get($certificateSetting, 'card.base_width_px') ?? data_get($certificateSetting, 'card.width_px') ?? 0); $savedBaseH = (float) (data_get($certificateSetting, 'card.base_height_px') ?? data_get($certificateSetting, 'card.height_px') ?? 0); $pxW = $widthCm * 37.8; $pxH = $heightCm * 37.8; $baseW = ($savedBaseW > 0) ? $savedBaseW : $pxW; $baseH = ($savedBaseH > 0) ? $savedBaseH : $pxH; $scaleX = ($savedBaseW > 0) ? ($pxW / $savedBaseW) : 1.0; $scaleY = ($savedBaseH > 0) ? ($pxH / $savedBaseH) : 1.0; $fontScale = 1.0; // Fonts are not scaled in print_certificates_html.blade.php $certificateType = data_get($certificateSetting, 'certificate_type', 'single'); $isDoubleSided = (strtolower(trim((string)$certificateType)) === 'double'); $bgFilename = $bgFilenameGlobal; $bgPathCandidate = $bgFilename ? public_path('assets/images/certificate/' . $bgFilename) : null; $bgFileExists = $bgPathCandidate && file_exists($bgPathCandidate); $bgPath = $bgFileExists ? $bgPathCandidate : null; if (!$bgPath) { $defaultDir = public_path('assets/images/certificate/background/default'); $files = glob($defaultDir.'/*.{png,jpg,jpeg,gif,webp}', GLOB_BRACE); $bgPath = ($files && count($files) > 0) ? ($defaultDir.'/'.basename($files[0])) : null; } $bgBase64 = image_to_base64_data_uri($bgPath); $titleStyle = data_get($certificateSetting, 'title', []); $photoStyle = data_get($certificateSetting, 'photo', []); $qrStyle = data_get($certificateSetting, 'qr', []); $photoSizeSetting = (float) data_get($photoStyle, 'size', 90) * $scaleX; $photoShape = data_get($photoStyle, 'shape', 'square'); $photoFilename = optional($profileParticipant)->foto; $photoPath = $photoFilename ? public_path('assets/images/profilefoto/' . $photoFilename) : public_path('assets/images/profilefoto/default-profile.png'); $photoBase64 = image_to_base64_data_uri($photoPath); $imgAspectRatio = 1.0; if (file_exists($photoPath)) { $imgInfo = @getimagesize($photoPath); if ($imgInfo && $imgInfo[1] > 0) { $imgAspectRatio = $imgInfo[0] / $imgInfo[1]; } } $overlayColor = data_get($photoStyle, 'overlay_color', '#000000'); $overlayOpacity = (int) data_get($photoStyle, 'overlay_opacity', 0); $overlayAlpha = max(0, min(100, $overlayOpacity)) / 100.0; $qrTop = (int) round((float) data_get($qrStyle, 'top', 320) * $scaleY); $qrLeft = (int) round((float) data_get($qrStyle, 'left', 90) * $scaleX); $qrSizeInput = (float) data_get($qrStyle, 'size', 80); $qrSize = max(0, (int) round($qrSizeInput * $scaleX)); @endphp
@php $bgUrl = $bgFileExists ? asset('assets/images/certificate/' . $bgFilename) : (function(){ $d=public_path('assets/images/certificate/background/default'); $fs=glob($d.'/*.{png,jpg,jpeg,gif,webp}', GLOB_BRACE); return ($fs && count($fs)>0) ? asset('assets/images/certificate/background/default/'.basename($fs[0])) : null; })(); @endphp @if($bgBase64) Certificate Background @else Certificate Background @endif
@php $titleFont = data_get($titleStyle, 'font'); if (empty($titleFont) || $titleFont === 'undefined') { $titleFont = 'DejaVu Sans'; } @endphp @if(data_get($titleStyle, 'visible', true)) @php $titleTop = (int) round((float) data_get($titleStyle, 'top', 20) * $scaleY); $titleLeft = (int) round((float) data_get($titleStyle, 'marginLeft', data_get($titleStyle, 'left', 0)) * $scaleX); @endphp
{{ str_replace(["\r\n","\n"], ' ', ($activity->name ?? 'Sertifikat PESERTA')) }}
@endif @if(data_get($certificateSetting, 'name.visible', true)) @php $nameFont = data_get($certificateSetting, 'name.font'); if (empty($nameFont) || $nameFont === 'undefined') { $nameFont = 'DejaVu Sans'; } $nameAlignRaw = strtolower((string) data_get($certificateSetting, 'name.align', 'center')); $nameAlignCss = in_array($nameAlignRaw, ['kiri','left']) ? 'left' : (in_array($nameAlignRaw, ['kanan','right']) ? 'right' : 'center'); // Use exact values from database - match the exact precision shown in debug $nameTopRaw = (float) data_get($certificateSetting, 'name.top', 190); $nameLeftRaw = (float) data_get($certificateSetting, 'name.left', 30); $nameWidthRaw = (float) data_get($certificateSetting, 'name.width', 180); // Apply scaling exactly like print_certificates_html.blade.php // Calculate with full precision first, then round for display $nameTopCalculated = $nameTopRaw * $scaleY; $nameLeftCalculated = $nameLeftRaw * $scaleX; $nameWidthCalculated = $nameWidthRaw * $scaleX; // Round to match print behavior (print uses int round) $nameTop = (int) round($nameTopCalculated); $nameLeft = (int) round($nameLeftCalculated); $nameWidth = (int) round($nameWidthCalculated); @endphp
{{ optional(optional($peserta)->user)->name ?? '-' }}
@endif @if(data_get($certificateSetting, 'email.visible', true)) @php $emailFont = data_get($certificateSetting, 'email.font'); if (empty($emailFont) || $emailFont === 'undefined') { $emailFont = 'DejaVu Sans'; } @endphp @php $emailTop = (int) round((float) data_get($certificateSetting, 'email.top', 220) * $scaleY); $emailLeft = (int) round((float) data_get($certificateSetting, 'email.left', 30) * $scaleX); $emailWidth = (int) round((float) data_get($certificateSetting, 'email.width', 180) * $scaleX); @endphp
{{ optional(optional($peserta)->user)->email ?? '-' }}
@endif @if(data_get($certificateSetting, 'no_hp.visible', false))
{{ optional($profileParticipant)->no_hp ?? '-' }}
@endif @if(data_get($certificateSetting, 'jenis_kelamin.visible', false)) @php $genderTop = (int) round((float) data_get($certificateSetting, 'jenis_kelamin.top', 260) * $scaleY); $genderLeft = (int) round((float) data_get($certificateSetting, 'jenis_kelamin.left', 30) * $scaleX); $genderWidth = (int) round((float) data_get($certificateSetting, 'jenis_kelamin.width', 180) * $scaleX); @endphp
{{ optional($profileParticipant)->jenis_kelamin ?? '-' }}
@endif @if(data_get($certificateSetting, 'pekerjaan.visible', false)) @php $jobTop = (int) round((float) data_get($certificateSetting, 'pekerjaan.top', 280) * $scaleY); $jobLeft = (int) round((float) data_get($certificateSetting, 'pekerjaan.left', 30) * $scaleX); $jobWidth = (int) round((float) data_get($certificateSetting, 'pekerjaan.width', 180) * $scaleX); @endphp
{{ optional($profileParticipant)->pekerjaan ?? '-' }}
@endif @if(data_get($certificateSetting, 'instansi.visible', false)) @php $instTop = (int) round((float) data_get($certificateSetting, 'instansi.top', 290) * $scaleY); $instLeft = (int) round((float) data_get($certificateSetting, 'instansi.left', 30) * $scaleX); $instWidth = (int) round((float) data_get($certificateSetting, 'instansi.width', 180) * $scaleX); @endphp
{{ optional($profileParticipant)->instansi ?? '-' }}
@endif @if(data_get($certificateSetting, 'jabatan.visible', false)) @php $roleTop = (int) round((float) data_get($certificateSetting, 'jabatan.top', 300) * $scaleY); $roleLeft = (int) round((float) data_get($certificateSetting, 'jabatan.left', 30) * $scaleX); $roleWidth = (int) round((float) data_get($certificateSetting, 'jabatan.width', 180) * $scaleX); @endphp
{{ optional($profileParticipant)->jabatan ?? '-' }}
@endif @if(data_get($certificateSetting, 'alamat.visible', false)) @php $addrTop = (int) round((float) data_get($certificateSetting, 'alamat.top', 320) * $scaleY); $addrLeft = (int) round((float) data_get($certificateSetting, 'alamat.left', 30) * $scaleX); $addrWidth = (int) round((float) data_get($certificateSetting, 'alamat.width', 180) * $scaleX); @endphp
{{ optional($profileParticipant)->alamat ?? '-' }}
@endif @if(data_get($certificateSetting, 'province.visible', false)) @php $provFont = data_get($certificateSetting, 'province.font'); if (empty($provFont) || $provFont === 'undefined') { $provFont = 'DejaVu Sans'; } $provTop = (int) round((float) data_get($certificateSetting, 'province.top', 340) * $scaleY); $provLeft = (int) round((float) data_get($certificateSetting, 'province.left', 30) * $scaleX); $provWidth = (int) round((float) data_get($certificateSetting, 'province.width', 180) * $scaleX); @endphp
{{ $provinceParticipant ?? '-' }}
@endif @if(data_get($certificateSetting, 'regency.visible', false)) @php $regFont = data_get($certificateSetting, 'regency.font'); if (empty($regFont) || $regFont === 'undefined') { $regFont = 'DejaVu Sans'; } $regTop = (int) round((float) data_get($certificateSetting, 'regency.top', 360) * $scaleY); $regLeft = (int) round((float) data_get($certificateSetting, 'regency.left', 30) * $scaleX); $regWidth = (int) round((float) data_get($certificateSetting, 'regency.width', 180) * $scaleX); @endphp
{{ $regencyParticipant ?? '-' }}
@endif @if(data_get($certificateSetting, 'district.visible', false)) @php $distFont = data_get($certificateSetting, 'district.font'); if (empty($distFont) || $distFont === 'undefined') { $distFont = 'DejaVu Sans'; } $distTop = (int) round((float) data_get($certificateSetting, 'district.top', 380) * $scaleY); $distLeft = (int) round((float) data_get($certificateSetting, 'district.left', 30) * $scaleX); $distWidth = (int) round((float) data_get($certificateSetting, 'district.width', 180) * $scaleX); @endphp
{{ $districtParticipant ?? '-' }}
@endif @if(data_get($certificateSetting, 'certificate_id.visible', false)) @php $certIdFont = data_get($certificateSetting, 'certificate_id.font'); if (empty($certIdFont) || $certIdFont === 'undefined') { $certIdFont = 'DejaVu Sans'; } $certTop = (int) round((float) data_get($certificateSetting, 'certificate_id.top', 360) * $scaleY); $certLeft = (int) round((float) data_get($certificateSetting, 'certificate_id.left', 30) * $scaleX); $certWidth = (int) round((float) data_get($certificateSetting, 'certificate_id.width', 180) * $scaleX); @endphp
{{ $peserta->certificate_id ?? '-' }}
@endif @if(data_get($photoStyle, 'visible', true))
@if($photoBase64) Foto
@endif
@endif @if(data_get($qrStyle, 'visible', true))
@php $qrVal = route('activity.verify-certificate', ['id' => $activity->id]) . '?certificate_id=' . urlencode((string) ($peserta->certificate_id ?? '')); $qrSizeInt = (int) round($qrSize); try { $qrBinary = \SimpleSoftwareIO\QrCode\Facades\QrCode::format('png')->size(max($qrSizeInt,40))->generate((string) $qrVal); $qrSrc = 'data:image/png;base64,'.base64_encode($qrBinary); } catch (\Throwable $e) { $qrSrc = 'https://api.qrserver.com/v1/create-qr-code/?size='.max($qrSizeInt,40).'x'.max($qrSizeInt,40).'&data='.urlencode((string) $qrVal); } @endphp QR Code
@endif
@if($isDoubleSided) @php $backBgFilename = data_get($certificateSetting, 'card.background_back'); $backBgPathCandidate = $backBgFilename ? public_path('assets/images/certificate/' . $backBgFilename) : null; $backBgExists = $backBgPathCandidate && file_exists($backBgPathCandidate); $backBgPath = $backBgExists ? $backBgPathCandidate : null; $backBgBase64 = $backBgPath ? image_to_base64_data_uri($backBgPath) : $bgBase64; if (!$backBgBase64) { $backBgBase64 = $bgBase64; } $backScaleX = $scaleX; $backScaleY = $scaleY; $backOffsetLeftCm = $offsetLeftCm; @endphp
@if($backBgBase64) Certificate Background @endif
@php $backTitleVisible = (bool) data_get($certificateSetting, 'back_title.visible', true); $backSubtitleVisible = (bool) data_get($certificateSetting, 'back_subtitle.visible', false); $backContentVisible = (bool) data_get($certificateSetting, 'back_content.visible', false); $backCertIdVisible = (bool) data_get($certificateSetting, 'back_certid.visible', true); @endphp @if($backTitleVisible) @php $backTitleFont = data_get($certificateSetting, 'back_title.font'); if (empty($backTitleFont) || $backTitleFont === 'undefined') { $backTitleFont = 'DejaVu Sans'; } @endphp
{{ $activity->name ?? 'Sertifikat Peserta' }}
@endif @if($backSubtitleVisible) @php $backSubtitleFont = data_get($certificateSetting, 'back_subtitle.font'); if (empty($backSubtitleFont) || $backSubtitleFont === 'undefined') { $backSubtitleFont = 'DejaVu Sans'; } @endphp
Informasi Tambahan
@endif @if($backContentVisible) @php $backContentFont = data_get($certificateSetting, 'back_content.font'); if (empty($backContentFont) || $backContentFont === 'undefined') { $backContentFont = 'DejaVu Sans'; } @endphp
{{ data_get($certificateSetting, 'back_content.text', 'Sertifikat ini diterbitkan sebagai bukti keikutsertaan dalam kegiatan.') }}
@endif @if($backCertIdVisible) @php $backCertIdFont = data_get($certificateSetting, 'back_certid.font'); if (empty($backCertIdFont) || $backCertIdFont === 'undefined') { $backCertIdFont = 'DejaVu Sans'; } @endphp
{{ $peserta->certificate_id ?? '-' }}
@endif
@endif @endforeach @if(count($row) < $cols) @php $pxWDefault = $widthCmDefault * 37.8; $pxHDefault = $heightCmDefault * 37.8; @endphp @for($i = 0; $i < $cols - count($row); $i++)
@endfor @endif
@endforeach @endforeach