$v !== null && $v > 0))); } public function norm(?string $s): string { $s = (string) $s; $s = str_replace(["\xC2\xA0", "\xA0"], ' ', $s); $s = preg_replace('/\s+/u', ' ', $s) ?? $s; $s = preg_replace('/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/u', '', $s) ?? $s; return trim($s); } public function toPdf(string $s): string { if (function_exists('iconv')) { $o = @iconv('UTF-8', 'ISO-8859-1//TRANSLIT', $s); if ($o !== false && $o !== '') { return $o; } } if (function_exists('mb_convert_encoding')) { $o = @mb_convert_encoding($s, 'ISO-8859-1', 'UTF-8'); if ($o !== '') { return $o; } } $o = @utf8_decode($s); return $o !== '' ? $o : 'STAFF'; } public function fitText(FPDF $pdf, string $text, int $startSize, int $minSize, float $maxWidth): int { $size = $startSize; while ($size >= $minSize) { $pdf->SetFontSize($size); if ($pdf->GetStringWidth($text) <= $maxWidth) { return $size; } $size--; } return $minSize; } public function formatRole(?string $role): string { $role = (string) $role; $role = str_replace(['-', '_'], ' ', $role); $role = preg_replace('/\s+/u', ' ', trim($role)); if ($role === '') { return ''; } $special = [ 'of' => 'of', 'head' => 'Head', ]; $tokens = explode(' ', $role); $out = []; foreach ($tokens as $tok) { if ($tok === '') { continue; } $core = preg_replace('/^\P{L}+|\P{L}+$/u', '', $tok); $start = strpos($tok, $core); if ($start === false) { $out[] = ucfirst(mb_strtolower($tok, 'UTF-8')); continue; } $pre = substr($tok, 0, $start); $suf = substr($tok, $start + strlen($core)); $lw = mb_strtolower($core, 'UTF-8'); if (isset($special[$lw])) { $coreFmt = $special[$lw]; } elseif (preg_match('/^\p{L}{1,4}$/u', $core)) { $coreFmt = mb_strtoupper($core, 'UTF-8'); } else { $coreFmt = preg_replace_callback( '/\p{L}+/u', static fn ($m) => mb_strtoupper(mb_substr($m[0], 0, 1, 'UTF-8'), 'UTF-8') . mb_strtolower(mb_substr($m[0], 1, null, 'UTF-8'), 'UTF-8'), mb_strtolower($core, 'UTF-8') ); } $out[] = $pre . $coreFmt . $suf; } return implode(' ', $out); } public function formatRolesCsv(?string $csv): string { if ($csv === null) { return ''; } $parts = array_filter(array_map('trim', explode(',', (string) $csv)), 'strlen'); if (empty($parts)) { return ''; } return implode(', ', array_map(fn ($r) => $this->formatRole($r), $parts)); } public function formatClass(string $class): string { $c = trim($class); if ($c === '') { return ''; } $lc = strtolower($c); if ($lc === 'kg' || $lc === 'kindergarten') { return 'KG'; } if ($lc === 'youth') { return 'Youth'; } return $c; } public function resolveRole(array $info): string { $candidates = [ 'role', 'active_role', 'role_name', fn ($r) => !empty($r['roles']) ? (array_values(array_filter(array_map('trim', explode(',', (string) $r['roles']))))[0] ?? '') : '', 'job_title', 'title', 'position', 'staff_role', 'department_role', 'dept_role', ]; foreach ($candidates as $key) { if (is_callable($key)) { $v = $key($info); if ($this->norm($v) !== '') { return $this->norm($v); } } else { if (!empty($info[$key])) { $v = $this->norm((string) $info[$key]); if ($v !== '') { return $v; } } } } return 'STAFF'; } public function firstExisting(array $paths): ?string { foreach ($paths as $path) { if (!empty($path) && file_exists($path)) { return $path; } } return null; } }