From 33b1083770a65b76a3dcd6d8d3f32c234323b457 Mon Sep 17 00:00:00 2001 From: Benjamin Nussbaum Date: Thu, 15 Jan 2026 22:19:03 +0100 Subject: [PATCH] refactor: qr-code filter --- app/Facades/QrCode.php | 24 +++++ app/Liquid/Filters/StringMarkup.php | 23 +---- app/Providers/AppServiceProvider.php | 2 +- app/Services/QrCodeService.php | 147 +++++++++++++++++++++++++++ 4 files changed, 174 insertions(+), 22 deletions(-) create mode 100644 app/Facades/QrCode.php create mode 100644 app/Services/QrCodeService.php diff --git a/app/Facades/QrCode.php b/app/Facades/QrCode.php new file mode 100644 index 0000000..00de934 --- /dev/null +++ b/app/Facades/QrCode.php @@ -0,0 +1,24 @@ +errorCorrection($errorCorrection); } - $svg = (string) $qrCode->generate($text); - - // Add class="qr-code" to the SVG element - // The SVG may start with and then - if (preg_match('/]*)>/', $svg, $matches)) { - $attributes = $matches[1]; - // Check if class already exists - if (mb_strpos($attributes, 'class=') === false) { - $svg = preg_replace('/]*)>/', '', $svg, 1); - } else { - // If class exists, add qr-code to it - $svg = preg_replace('/(]*class=["\'])([^"\']*)(["\'][^>]*>)/', '$1$2 qr-code$3', $svg, 1); - } - } else { - // Fallback: simple replacement if no attributes - $svg = preg_replace('//', '', $svg, 1); - } - - return $svg; + return $qrCode->generate($text); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 78adbde..48178e8 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -16,7 +16,7 @@ class AppServiceProvider extends ServiceProvider */ public function register(): void { - $this->app->bind('qr-code', fn (): QrCodeService => new QrCodeService); + $this->app->bind('qr-code', fn () => new QrCodeService); } /** diff --git a/app/Services/QrCodeService.php b/app/Services/QrCodeService.php new file mode 100644 index 0000000..812415b --- /dev/null +++ b/app/Services/QrCodeService.php @@ -0,0 +1,147 @@ +format = $format; + + return $this; + } + + /** + * Set the size of the QR code + * + * @param int $size The size in pixels + * @return $this + */ + public function size(int $size): self + { + $this->size = $size; + + return $this; + } + + /** + * Set the error correction level + * + * @param string $level Error correction level: 'l', 'm', 'q', 'h' + * @return $this + */ + public function errorCorrection(string $level): self + { + $this->errorCorrection = $level; + + return $this; + } + + /** + * Generate the QR code + * + * @param string $text The text to encode + * @return string The generated QR code (SVG string) + */ + public function generate(string $text): string + { + // Ensure format is set (default to SVG) + $format = $this->format ?? 'svg'; + + if ($format !== 'svg') { + throw new InvalidArgumentException("Format '{$format}' is not supported. Only 'svg' is currently supported."); + } + + // Calculate size and margin + // If size is not set, calculate from module size (default module size is 11) + if ($this->size === null) { + $moduleSize = 11; + $this->size = 29 * $moduleSize; + } + + // Calculate margin: 4 modules on each side + // Module size = size / 29, so margin = (size / 29) * 4 + $moduleSize = $this->size / 29; + $margin = (int) ($moduleSize * 4); + + // Map error correction level + $errorCorrectionLevel = ErrorCorrectionLevel::valueOf('M'); // default + if ($this->errorCorrection !== null) { + $errorCorrectionLevel = match (mb_strtoupper($this->errorCorrection)) { + 'L' => ErrorCorrectionLevel::valueOf('L'), + 'M' => ErrorCorrectionLevel::valueOf('M'), + 'Q' => ErrorCorrectionLevel::valueOf('Q'), + 'H' => ErrorCorrectionLevel::valueOf('H'), + default => ErrorCorrectionLevel::valueOf('M'), + }; + } + + // Create renderer style with size and margin + $rendererStyle = new RendererStyle($this->size, $margin); + + // Create SVG renderer + $renderer = new ImageRenderer( + $rendererStyle, + new SvgImageBackEnd() + ); + + // Create writer + $writer = new Writer($renderer); + + // Generate SVG + $svg = $writer->writeString($text, 'ISO-8859-1', $errorCorrectionLevel); + + // Add class="qr-code" to the SVG element + $svg = $this->addQrCodeClass($svg); + + return $svg; + } + + /** + * Add the 'qr-code' class to the SVG element + * + * @param string $svg The SVG string + * @return string The SVG string with the class added + */ + protected function addQrCodeClass(string $svg): string + { + // Match + if (preg_match('/]*)>/', $svg, $matches)) { + $attributes = $matches[1]; + // Check if class already exists + if (mb_strpos($attributes, 'class=') === false) { + $svg = preg_replace('/]*)>/', '', $svg, 1); + } else { + // If class exists, add qr-code to it + $svg = preg_replace('/(]*class=["\'])([^"\']*)(["\'][^>]*>)/', '$1$2 qr-code$3', $svg, 1); + } + } else { + // Fallback: simple replacement if no attributes + $svg = preg_replace('//', '', $svg, 1); + } + + return $svg; + } +}