diff --git a/DEPLOY.md b/DEPLOY.md index 52d9173..ecd2c1c 100644 --- a/DEPLOY.md +++ b/DEPLOY.md @@ -23,7 +23,7 @@ rsync -av backend/url-shortener/ alpine-be:/var/www/localhost/htdocs/apps/s/ cd /var/www/.../androidcast_project/android_cast php81 examples/crash_reporter/backend/scripts/ensure_url_shortener_prod_config.php -apk add libqrencode-tools # QR PNG (/api/v1/qr/…) +apk add libqrencode-tools php83-gd # QR PNG (/api/v1/qr/…) + logo overlay rc-service php-fpm81 restart ``` diff --git a/assets/ac_qr_logo.png b/assets/ac_qr_logo.png index d95f9cb..c8c2e3a 100644 Binary files a/assets/ac_qr_logo.png and b/assets/ac_qr_logo.png differ diff --git a/src/QrBrandedRenderer.php b/src/QrBrandedRenderer.php index 1ca612f..5d11126 100644 --- a/src/QrBrandedRenderer.php +++ b/src/QrBrandedRenderer.php @@ -3,8 +3,8 @@ declare(strict_types=1); /** Branded QR PNG: high error correction + centered app logo with white border. */ final class QrBrandedRenderer { - private const LOGO_FRACTION = 0.22; - private const BORDER_FRACTION = 0.045; + private const LOGO_FRACTION = 0.24; + private const BORDER_FRACTION = 0.05; private function __construct() { } @@ -22,12 +22,36 @@ final class QrBrandedRenderer { return $branded ?? $raw; } + public static function dataUri(string $text, int $size = 256): ?string { + $png = self::renderPng($text, $size); + if ($png === null) { + return null; + } + return 'data:image/png;base64,' . base64_encode($png); + } + /** @return list */ private static function logoCandidates(): array { - return [ - dirname(__DIR__) . '/assets/ac_qr_logo.png', - __DIR__ . '/../assets/ac_qr_logo.png', - ]; + $paths = []; + $env = getenv('AC_QR_LOGO_PATH'); + if (is_string($env) && $env !== '') { + $paths[] = $env; + } + $paths[] = dirname(__DIR__) . '/assets/ac_qr_logo.png'; + $paths[] = __DIR__ . '/../assets/ac_qr_logo.png'; + $paths[] = dirname(__DIR__, 2) . '/ac-platform-php/assets/ac_qr_logo.png'; + $paths[] = dirname(__DIR__, 2) . '/ac-platform-php/public/assets/ac_qr_logo.png'; + return $paths; + } + + /** @return \GdImage|false */ + private static function loadLogoImage(string $path) { + $bytes = @file_get_contents($path); + if (!is_string($bytes) || $bytes === '') { + return false; + } + $img = @imagecreatefromstring($bytes); + return $img !== false ? $img : false; } private static function logoPath(): ?string { @@ -71,7 +95,7 @@ final class QrBrandedRenderer { if ($qr === false) { return null; } - $logo = @imagecreatefrompng($logoPath); + $logo = self::loadLogoImage($logoPath); if ($logo === false) { imagedestroy($qr); return null;