ng { return '"' . esc_attr($family) . '",Tahoma,Arial,sans-serif'; } private static function font_display(): string { $display = (string) prk_option('font_display_strategy', 'swap'); return in_array($display, ['swap', 'fallback', 'optional', 'block'], true) ? $display : 'swap'; } private static function normalize_number_mode(string $mode): string { return $mode === 'munen' ? 'munen' : 'numfa'; } private static function normalize_font_key(string $key): string { $normalized = strtolower(trim($key)); $aliases = [ 'inherit' => 'inherit', 'custom' => 'custom', 'iransans' => 'IRANSans', 'iran_sans' => 'IRANSans', 'iranyekan' => 'IRANyekan', 'iranyekanx' => 'IRANyekan', 'iranyekan_bakh' => 'IRANyekan_bakh', 'iranyekan-bakh' => 'IRANyekan_bakh', 'yekanbakh' => 'IRANyekan_bakh', 'dana' => 'dana', 'kalemeh' => 'kalemeh', 'kalameh' => 'kalemeh', 'peyda' => 'peyda', ]; return isset($aliases[$normalized]) ? $aliases[$normalized] : 'IRANyekan_bakh'; } /** * @return int[] */ private static function selected_weights(string $option_key, string $font_key, bool $admin): array { $raw = prk_option($option_key, null); $weights = self::normalize_weights($raw); if (!empty($weights)) { return $weights; } if ($font_key === 'custom') { return $admin ? [400, 500, 600, 700] : [400, 700]; } $registry = self::registry(); if (isset($registry[$font_key]['default_weights']) && is_array($registry[$font_key]['default_weights'])) { return array_map('intval', $registry[$font_key]['default_weights']); } return [400, 500, 600, 700]; } /** * @param mixed $raw * @return int[] */ private static function normalize_weights($raw): array { if (is_string($raw) && $raw !== '') { $raw = preg_split('/[\s,|]+/', $raw); } if (!is_array($raw)) { return []; } $weights = []; foreach ($raw as $key => $value) { if (is_numeric($value)) { $candidate = (int) $value; } elseif (!is_numeric($key) && self::truthy($value)) { $candidate = (int) $key; } else { continue; } if ($candidate >= 100 && $candidate <= 900 && $candidate % 100 === 0) { $weights[] = $candidate; } } $weights = array_values(array_unique($weights)); sort($weights, SORT_NUMERIC); return $weights; } private static function sanitize_weight(int $weight): int { $weight = max(100, min(900, $weight)); return (int) (round($weight / 100) * 100); } private static function truthy($value): bool { return in_array($value, [true, 1, '1', 'true', 'yes', 'on'], true); } private static function option_enabled(string $key, bool $default): bool { if (function_exists('prk_option_enabled')) { return prk_option_enabled($key, $default); } $value = prk_option($key, null); if ($value === null) { return $default; } return self::truthy($value); } /** * @param mixed $value */ private static function media_url($value): string { if (is_array($value)) { $value = isset($value['url']) ? $value['url'] : ''; } $url = esc_url_raw((string) $value); return $url ?: ''; } private static function theme_asset_url_if_exists(string $relative): string { $relative = ltrim(str_replace(['..', '\\'], '', $relative), '/'); if ($relative === '') { return ''; } $path = get_template_directory() . '/' . $relative; if (!is_file($path)) { return ''; } return get_template_directory_uri() . '/' . $relative; } /** * @param array> $faces * @return array> */ private static function deduplicate_faces(array $faces): array { $unique = []; foreach ($faces as $face) { $key = (string) ($face['weight'] ?? 400) . '|' . (string) ($face['style'] ?? 'normal'); $unique[$key] = $face; } return array_values($unique); } private static function font_has_any_file(string $font_key): bool { $registry = self::registry(); if (!isset($registry[$font_key]['modes'])) { return false; } foreach ((array) $registry[$font_key]['modes'] as $mode) { foreach ((array) $mode as $files) { foreach ((array) $files as $relative) { if ($relative && is_file(get_template_directory() . '/' . ltrim((string) $relative, '/'))) { return true; } } } } return false; } /** * @return array */ private static function registry(): array { if (self::$registry !== null) { return self::$registry; } self::$registry = [ 'IRANSans' => [ 'default_weights' => [400, 700], 'modes' => [ 'numfa' => [ '400' => self::pair('fonts/iransans/woff2/IRANSansWeb(FaNum).woff2', 'fonts/iransans/woff/IRANSansWeb(FaNum).woff'), '700' => self::pair('fonts/iransans/woff2/IRANSansWeb(FaNum)_Bold.woff2', 'fonts/iransans/woff/IRANSansWeb(FaNum)_Bold.woff'), ], 'munen' => [ '400' => self::pair('fonts/iransans/woff2/IRANSansWeb.woff2', 'fonts/iransans/woff/IRANSansWeb.woff'), '700' => self::pair('fonts/iransans/woff2/IRANSansWeb_Bold.woff2', 'fonts/iransans/woff/IRANSansWeb_Bold.woff'), ], ], ], 'IRANyekan' => [ 'default_weights' => [400, 500, 600, 700], 'modes' => [ 'numfa' => self::iranyekan_map(), 'munen' => self::iranyekan_map(), ], ], 'IRANyekan_bakh' => [ 'default_weights' => [400, 500, 600, 700], 'modes' => [ 'numfa' => self::yekan_bakh_map(true), 'munen' => self::yekan_bakh_map(false), ], ], 'dana' => [ 'default_weights' => [400, 500, 600, 700], 'modes' => [ 'numfa' => self::dana_map(), 'munen' => self::dana_map(), ], ], 'kalemeh' => [ 'default_weights' => [400, 500, 600, 700], 'modes' => [ 'numfa' => self::kalemeh_map(), 'munen' => self::kalemeh_map(), ], ], 'peyda' => [ 'default_weights' => [400, 500, 600, 700], 'modes' => [ 'numfa' => self::peyda_map(true), 'munen' => self::peyda_map(false), ], ], ]; return self::$registry; } /** @return array */ private static function pair(string $woff2, string $woff): array { return ['woff2' => $woff2, 'woff' => $woff]; } /** @return array> */ private static function iranyekan_map(): array { $names = [ 100 => 'Thin', 200 => 'UltraLight', 300 => 'Light', 400 => 'Regular', 500 => 'Medium', 600 => 'DemiBold', 700 => 'Bold', 800 => 'ExtraBold', 900 => 'Black', ]; $map = []; foreach ($names as $weight => $name) { $map[(string) $weight] = self::pair( 'fonts/iranyekan/woff2/IRANYekanX-' . $name . '.woff2', 'fonts/iranyekan/woff/IRANYekanX-' . $name . '.woff' ); } return $map; } /** @return array> */ private static function yekan_bakh_map(bool $fa): array { $prefix = $fa ? 'YekanBakhFaNum-' : 'YekanBakh-'; $names = [ 100 => 'Thin', 300 => 'Light', 400 => 'Regular', 600 => 'SemiBold', 700 => 'Bold', 800 => 'ExtraBold', 900 => 'Black', ]; $map = []; foreach ($names as $weight => $name) { $map[(string) $weight] = self::pair( 'fonts/iranyekan-bakh/woff2/' . $prefix . $name . '.woff2', 'fonts/iranyekan-bakh/woff/' . $prefix . $name . '.woff' ); } return $map; } /** @return array> */ private static function dana_map(): array { $names = [ 100 => 'Thin', 200 => 'UltraLight', 300 => 'Light', 400 => 'Regular', 500 => 'Medium', 600 => 'DemiBold', 700 => 'Bold', 800 => 'ExtraBold', 900 => 'Black', ]; $map = []; foreach ($names as $weight => $name) { $map[(string) $weight] = self::pair( 'fonts/dana/woff2/Dana-' . $name . '.woff2', 'fonts/dana/woff/Dana-' . $name . '.woff' ); } return $map; } /** @return array> */ private static function kalemeh_map(): array { $names = [ 300 => 'Light', 500 => 'Medium', 600 => 'SemiBold', 700 => 'Bold', ]; $map = []; foreach ($names as $weight => $name) { $map[(string) $weight] = self::pair( 'fonts/kalemeh/woff2/KalamehWebFaNum-' . $name . '.woff2', 'fonts/kalemeh/woff/KalamehWebFaNum-' . $name . '.woff' ); } return $map; } /** @return array> */ private static function peyda_map(bool $fa): array { $base = $fa ? 'PeydaFaNumWeb-' : 'PeydaWeb-'; $folder = $fa ? 'fanum' : 'family'; $names = [ 100 => 'Thin', 200 => 'ExtraLight', 300 => 'Light', 400 => 'Regular', 500 => 'Medium', 600 => 'SemiBold', 700 => 'Bold', 800 => 'ExtraBold', 900 => 'Black', ]; $map = []; foreach ($names as $weight => $name) { $map[(string) $weight] = self::pair( 'fonts/peyda/' . $folder . '/woff2/' . $base . $name . '.woff2', 'fonts/peyda/' . $folder . '/woff/' . $base . $name . '.woff' ); } return $map; } }