*/ public static function defaults(): array { return [ 'foreground' => '#000000', 'background' => '#ffffff', 'error_correction' => 'M', 'margin' => 4, 'module_style' => 'square', 'finder_outer' => 'square', 'finder_inner' => 'square', 'frame_style' => 'none', 'frame_text' => '', 'frame_color' => '#000000', 'scale' => 8, 'logo_path' => null, 'gradient_type' => 'none', 'gradient_color1' => '#000000', 'gradient_color2' => '#7c3aed', 'gradient_rotation' => 45, 'logo_size' => 0.3, 'logo_margin' => 5, 'logo_white_bg' => false, 'logo_shape' => 'none', ]; } /** @param array|null $style */ public static function merge(?array $style): array { $merged = array_merge(self::defaults(), $style ?? []); if (! in_array($merged['error_correction'], ['L', 'M', 'Q', 'H'], true)) { $merged['error_correction'] = 'M'; } if (! in_array($merged['module_style'], QrModuleStyleCatalog::keys(), true)) { $merged['module_style'] = 'square'; } if (! QrCornerStyleCatalog::isValidOuter((string) ($merged['finder_outer'] ?? 'square'))) { $merged['finder_outer'] = 'square'; } if (! QrCornerStyleCatalog::isValidInner((string) ($merged['finder_inner'] ?? 'square'))) { $merged['finder_inner'] = 'square'; } if (! QrFrameStyleCatalog::isValid((string) ($merged['frame_style'] ?? 'none'))) { $merged['frame_style'] = 'none'; } $merged['frame_text'] = substr(trim((string) ($merged['frame_text'] ?? '')), 0, 100); $frameColor = trim((string) ($merged['frame_color'] ?? '#000000')); $merged['frame_color'] = preg_match('/^#[0-9a-fA-F]{6}$/', $frameColor) ? $frameColor : '#000000'; if (! in_array($merged['gradient_type'], ['none', 'linear', 'radial'], true)) { $merged['gradient_type'] = 'none'; } $merged['logo_size'] = max(0.1, min(0.4, (float) ($merged['logo_size'] ?? 0.3))); $merged['logo_margin'] = max(0, min(15, (int) ($merged['logo_margin'] ?? 5))); if (! in_array($merged['logo_shape'], ['none', 'rounded', 'circle'], true)) { $merged['logo_shape'] = 'none'; } $merged['logo_white_bg'] = (bool) ($merged['logo_white_bg'] ?? false); if ($merged['module_style'] === 'bold') { $merged['scale'] = min(16, (int) $merged['scale'] + 2); } $merged['margin'] = max(0, min(10, (int) $merged['margin'])); $merged['scale'] = max(4, min(16, (int) $merged['scale'])); return $merged; } /** @param array|null $style */ public static function mergeForRender(?array $style): array { return QrStyleNormalizer::normalize(self::merge($style)); } /** @param array $style */ public static function recommendedEcc(array $style): string { $ecc = (string) ($style['error_correction'] ?? 'M'); $order = ['L' => 0, 'M' => 1, 'Q' => 2, 'H' => 3]; $minimum = 'M'; $moduleStyle = (string) ($style['module_style'] ?? 'square'); if (in_array($moduleStyle, ['bubble', 'fluid'], true)) { $minimum = 'H'; } elseif (QrModuleStyleCatalog::isDecorative($moduleStyle)) { $minimum = 'Q'; } if (! empty($style['logo_path'])) { $minimum = 'H'; } if (QrScanReliability::contrastRatio($style) < 4.5) { $minimum = 'H'; } return ($order[$minimum] ?? 1) > ($order[$ecc] ?? 1) ? $minimum : $ecc; } }