withQuietZone(2) ); } /** * Generate QR code as SVG string */ public function generateSvg( string $data, ?ErrorCorrectionLevel $errorLevel = null, ?QrCodeVersion $version = null ): string { $matrix = $this->generateMatrix($data, $errorLevel, $version); return $this->svgRenderer->render($matrix); } /** * Generate QR code as SVG data URI */ public function generateDataUri( string $data, ?ErrorCorrectionLevel $errorLevel = null, ?QrCodeVersion $version = null ): string { $matrix = $this->generateMatrix($data, $errorLevel, $version); return $this->svgRenderer->renderAsDataUri($matrix); } /** * Generate QR code matrix */ public function generateMatrix( string $data, ?ErrorCorrectionLevel $errorLevel = null, ?QrCodeVersion $version = null ): QrCodeMatrix { if (empty($data)) { throw new InvalidArgumentException('QR code data cannot be empty'); } $errorLevel = $errorLevel ?? ErrorCorrectionLevel::forTotp(); $version = $version ?? $this->determineOptimalVersion($data, $errorLevel); // Encode data into bit stream $encodedData = $this->dataEncoder->encode($data, $version, $errorLevel); // Generate matrix from encoded data return $this->matrixGenerator->generateMatrix($encodedData, $version, $errorLevel); } /** * Create generator with custom renderer */ public function withRenderer(SvgRenderer $renderer): self { return new self( dataEncoder: $this->dataEncoder, matrixGenerator: $this->matrixGenerator, svgRenderer: $renderer ); } /** * Get recommended configuration for data */ public function analyzeData(string $data): array { if (empty($data)) { throw new InvalidArgumentException('Cannot analyze empty data'); } $mode = DataMode::detectForData($data); $errorLevel = ErrorCorrectionLevel::forTotp(); $version = $this->determineOptimalVersion($data, $errorLevel); return [ 'data_length' => strlen($data), 'detected_mode' => $mode, 'recommended_error_level' => $errorLevel, 'optimal_version' => $version->getVersion(), 'matrix_size' => $version->getModuleCount(), 'data_capacity' => $version->getDataCapacity($errorLevel), 'efficiency' => $mode->getEfficiency(), 'capacity_used' => round((strlen($data) / $version->getDataCapacity($errorLevel)) * 100, 1), ]; } /** * Generate QR code for TOTP URI */ public function generateTotpQrCode(string $totpUri): string { // Validate TOTP URI format if (! str_starts_with($totpUri, 'otpauth://totp/')) { throw new InvalidArgumentException('Invalid TOTP URI format'); } // Use appropriate settings for TOTP (allow larger versions) $errorLevel = ErrorCorrectionLevel::M; // Medium error correction for TOTP return $this->generateSvg($totpUri, $errorLevel, null); // Auto-detect version } /** * Determine the optimal QR code version for given data */ private function determineOptimalVersion(string $data, ErrorCorrectionLevel $errorLevel): QrCodeVersion { $dataLength = strlen($data); // Try to find the smallest version that can fit the data return QrCodeVersion::forDataLength($dataLength, $errorLevel); } /** * Get generator configuration */ public function getConfiguration(): array { return [ 'encoder' => DataEncoder::class, 'matrix_generator' => MatrixGenerator::class, 'renderer' => $this->svgRenderer->getConfiguration(), ]; } /** * Validate data before encoding */ private function validateData(string $data): void { if (strlen($data) > 2953) { // Max capacity for version 40, error level L throw new InvalidArgumentException('Data too large for QR code: ' . strlen($data) . ' bytes'); } // Check for null bytes or other problematic characters if (strpos($data, "\0") !== false) { throw new InvalidArgumentException('Data contains null bytes'); } } }