PoC for qr code gneration 2

This commit is contained in:
Matej Bačo
2025-03-02 13:34:19 +01:00
parent 8d47d62d7a
commit 1bc2515339
4 changed files with 202 additions and 80 deletions
+121 -7
View File
@@ -8,6 +8,8 @@ use Appwrite\SDK\MethodType;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\URL\URL as URLParse;
use Appwrite\Utopia\Response;
use chillerlan\QRCode\Common\EccLevel;
use chillerlan\QRCode\Output\QRImagick;
use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
use Utopia\App;
@@ -485,17 +487,28 @@ App::get('/v1/avatars/qr')
contentType: ContentType::IMAGE_PNG
))
->param('text', '', new Text(512), 'Plain text to be converted to QR code image.')
->param('size', 400, new Range(1, 1000), 'QR code size. Pass an integer between 1 to 1000. Defaults to 400.', true)
->param('margin', 1, new Range(0, 10), 'Margin from edge. Pass an integer between 0 to 10. Defaults to 1.', true)
->param('size', 512, new Range(32, 1024), 'QR code size. Pass an integer between 32 to 1024. Defaults to 512.', true)
->param('margin', 1, new Range(0, 128), 'Margin from edge. Pass an integer between 0 to 128. Defaults to 1.', true)
->param('download', false, new Boolean(true), 'Return resulting image with \'Content-Disposition: attachment \' headers for the browser to start downloading it. Pass 0 for no header, or 1 for otherwise. Default value is set to 0.', true)
->inject('response')
->action(function (string $text, int $size, int $margin, bool $download, Response $response) {
->action(function (string $text, int $size, int $margin, mixed $download, Response $response) {
$download = ($download === '1' || $download === 'true' || $download === 1 || $download === true);
$options = new QROptions([
'addQuietzone' => true,
'quietzoneSize' => $margin,
'outputType' => QRCode::OUTPUT_IMAGICK,
'addQuietzone' => $margin > 0,
'readerUseImagickIfAvailable' => true,
'outputInterface' => QRGdRounded::class,
'returnResource' => true,
'outputBase64' => false,
'scale' => 64,
'quality' => 75,
'imagickFormat' => 'png',
'drawCircularModules' => true,
'addLogoSpace' => true,
'eccLevel' => EccLevel::H, // Needed for addLogoSpace
'logoSpaceWidth' => 10,
'logoSpaceHeight' => 10,
]);
$qrcode = new QRCode($options);
@@ -507,10 +520,33 @@ App::get('/v1/avatars/qr')
$image = new Image($qrcode->render($text));
$image->crop((int) $size, (int) $size);
$logo = \file_get_contents('https://appwrite.io/assets/logomark/logo.png');
$logoImg = new Image($logo);
$logoImg->crop(128 + 16, 128);
$image->image->setImageColorspace($logoImg->image->getImageColorspace());
$image->image->compositeImage($logoImg->image, Imagick::COMPOSITE_OVER, 192 - 8, 192);
$markerUrl = 'https://cloud.appwrite.io/v1/storage/buckets/67c4487d0010bd67a4f4/files/marker3/view?project=67c2cb23003706429442';
$marker = \file_get_contents($markerUrl);
$markerImg = new Image($marker);
$markerImg->crop(120, 120);
$image->image->compositeImage($markerImg->image, Imagick::COMPOSITE_OVER, 16, 16);
$markerImg->image->rotateImage('transparent', 90);
$image->image->compositeImage($markerImg->image, Imagick::COMPOSITE_OVER, 376, 16);
$markerImg->image->rotateImage('transparent', 180);
$image->image->compositeImage($markerImg->image, Imagick::COMPOSITE_OVER, 16, 376);
// TODO: Overlay last small marker too
$response
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
->setContentType('image/png')
->send($image->output('png', 9));
->send($image->output('png', 75));
});
App::get('/v1/avatars/initials')
@@ -1277,3 +1313,81 @@ App::get('/v1/cards/cloud-og')
->setContentType('image/png')
->file($baseImage->getImageBlob());
});
class QRGdRounded extends QRImagick
{
protected function module(int $x, int $y, int $M_TYPE): void
{
if (!$this->matrix->isDark($M_TYPE)) {
return;
}
$this->imagickDraw->setFillColor($this->getModuleValue($M_TYPE));
/*
$this->imagickDraw->circle(
(($x + 0.5) * $this->scale),
(($y + 0.5) * $this->scale),
((($x + 0.5 + $this->circleRadius) * $this->scale) - 8),
((($y + 0.5) * $this->scale) - 8)
);
return;
*/
$neighbours = $this->matrix->checkNeighbours($x, $y);
$x1 = ($x * $this->scale);
$y1 = ($y * $this->scale);
$x2 = (($x + 1) * $this->scale);
$y2 = (($y + 1) * $this->scale);
$rectsize = (int)($this->scale / 2);
// ------------------
// Outer rounding
// ------------------
if (($neighbours & (1 << 7))) { // neighbour left
// top left
$this->imagickDraw->rectangle($x1, $y1, ($x1 + $rectsize), ($y1 + $rectsize));
// bottom left
$this->imagickDraw->rectangle($x1, ($y2 - $rectsize), ($x1 + $rectsize), $y2);
}
if (($neighbours & (1 << 3))) { // neighbour right
// top right
$this->imagickDraw->rectangle(($x2 - $rectsize), $y1, $x2, ($y1 + $rectsize));
// bottom right
$this->imagickDraw->rectangle(($x2 - $rectsize), ($y2 - $rectsize), $x2, $y2);
}
if (($neighbours & (1 << 1))) { // neighbour top
// top left
$this->imagickDraw->rectangle($x1, $y1, ($x1 + $rectsize), ($y1 + $rectsize));
// top right
$this->imagickDraw->rectangle(($x2 - $rectsize), $y1, $x2, ($y1 + $rectsize));
}
if (($neighbours & (1 << 5))) { // neighbour bottom
// bottom left
$this->imagickDraw->rectangle($x1, ($y2 - $rectsize), ($x1 + $rectsize), $y2);
// bottom right
$this->imagickDraw->rectangle(($x2 - $rectsize), ($y2 - $rectsize), $x2, $y2);
}
$this->imagickDraw->circle(
(($x + 0.5) * $this->scale),
(($y + 0.5) * $this->scale),
((($x + 0.5 + $this->circleRadius) * $this->scale) + 2),
((($y + 0.5) * $this->scale) + 2)
);
/*
$this->imagickDraw->rectangle(
$x1, $y1, $x2, $y2
);
*/
return;
}
}
+1 -1
View File
@@ -77,7 +77,7 @@
"matomo/device-detector": "6.1.*",
"dragonmantank/cron-expression": "3.3.2",
"phpmailer/phpmailer": "6.9.1",
"chillerlan/php-qrcode": "4.3.4",
"chillerlan/php-qrcode": "dev-main",
"adhocore/jwt": "1.1.2",
"spomky-labs/otphp": "^10.0",
"webonyx/graphql-php": "14.11.*",
Generated
+79 -72
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "6883b3e81cfb0c5355997def668d5df2",
"content-hash": "4e5e640d4909991707c193d0e348d677",
"packages": [
{
"name": "adhocore/jwt",
@@ -279,16 +279,16 @@
},
{
"name": "brick/math",
"version": "0.12.2",
"version": "0.12.3",
"source": {
"type": "git",
"url": "https://github.com/brick/math.git",
"reference": "901eddb1e45a8e0f689302e40af871c181ecbe40"
"reference": "866551da34e9a618e64a819ee1e01c20d8a588ba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/901eddb1e45a8e0f689302e40af871c181ecbe40",
"reference": "901eddb1e45a8e0f689302e40af871c181ecbe40",
"url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba",
"reference": "866551da34e9a618e64a819ee1e01c20d8a588ba",
"shasum": ""
},
"require": {
@@ -327,7 +327,7 @@
],
"support": {
"issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.12.2"
"source": "https://github.com/brick/math/tree/0.12.3"
},
"funding": [
{
@@ -335,50 +335,70 @@
"type": "github"
}
],
"time": "2025-02-26T10:21:45+00:00"
"time": "2025-02-28T13:11:00+00:00"
},
{
"name": "chillerlan/php-qrcode",
"version": "4.3.4",
"version": "dev-main",
"source": {
"type": "git",
"url": "https://github.com/chillerlan/php-qrcode.git",
"reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d"
"reference": "b5ff1258a9a8bf6eff15d8c12365400ba60d652c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d",
"reference": "2ca4bf5ae048af1981d1023ee42a0a2a9d51e51d",
"url": "https://api.github.com/repos/chillerlan/php-qrcode/zipball/b5ff1258a9a8bf6eff15d8c12365400ba60d652c",
"reference": "b5ff1258a9a8bf6eff15d8c12365400ba60d652c",
"shasum": ""
},
"require": {
"chillerlan/php-settings-container": "^2.1.4",
"chillerlan/php-settings-container": "^3.2.1",
"ext-mbstring": "*",
"php": "^7.4 || ^8.0"
"php": "^8.2"
},
"require-dev": {
"phan/phan": "^5.3",
"phpunit/phpunit": "^9.5",
"setasign/fpdf": "^1.8.2"
"chillerlan/php-authenticator": "^5.2.1",
"ext-fileinfo": "*",
"intervention/image": "^3.8",
"phpbench/phpbench": "^1.3",
"phpmd/phpmd": "^2.15",
"phpstan/phpstan": "^2.0",
"phpstan/phpstan-deprecation-rules": "^2.0",
"phpunit/phpunit": "^11.3",
"setasign/fpdf": "^1.8.2",
"slevomat/coding-standard": "^8.15",
"squizlabs/php_codesniffer": "^3.10"
},
"suggest": {
"chillerlan/php-authenticator": "Yet another Google authenticator! Also creates URIs for mobile apps.",
"setasign/fpdf": "Required to use the QR FPDF output."
"intervention/image": "More advanced GD and ImageMagick output.",
"setasign/fpdf": "Required to use the QR FPDF output.",
"simple-icons/simple-icons": "SVG icons that you can use to embed as logos in the QR Code"
},
"default-branch": true,
"type": "library",
"autoload": {
"psr-4": {
"chillerlan\\QRCode\\": "src/"
"chillerlan\\QRCode\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
"MIT",
"Apache-2.0"
],
"authors": [
{
"name": "Kazuhiko Arase",
"homepage": "https://github.com/kazuhikoarase"
"homepage": "https://github.com/kazuhikoarase/qrcode-generator"
},
{
"name": "ZXing Authors",
"homepage": "https://github.com/zxing/zxing"
},
{
"name": "Ashot Khanamiryan",
"homepage": "https://github.com/khanamiryan/php-qrcode-detector-decoder"
},
{
"name": "Smiley",
@@ -390,54 +410,53 @@
"homepage": "https://github.com/chillerlan/php-qrcode/graphs/contributors"
}
],
"description": "A QR code generator. PHP 7.4+",
"description": "A QR Code generator and reader with a user-friendly API. PHP 8.2+",
"homepage": "https://github.com/chillerlan/php-qrcode",
"keywords": [
"phpqrcode",
"qr",
"qr code",
"qr-reader",
"qrcode",
"qrcode-generator"
"qrcode-generator",
"qrcode-reader"
],
"support": {
"docs": "https://php-qrcode.readthedocs.io",
"issues": "https://github.com/chillerlan/php-qrcode/issues",
"source": "https://github.com/chillerlan/php-qrcode/tree/4.3.4"
"source": "https://github.com/chillerlan/php-qrcode"
},
"funding": [
{
"url": "https://www.paypal.com/donate?hosted_button_id=WLYUNAT9ZTJZ4",
"type": "custom"
},
{
"url": "https://ko-fi.com/codemasher",
"type": "ko_fi"
"type": "Ko-Fi"
}
],
"time": "2022-07-25T09:12:45+00:00"
"time": "2024-11-27T19:19:56+00:00"
},
{
"name": "chillerlan/php-settings-container",
"version": "2.1.6",
"version": "3.2.1",
"source": {
"type": "git",
"url": "https://github.com/chillerlan/php-settings-container.git",
"reference": "5553558bd381fce5108c6d0343c12e488cfec6bb"
"reference": "95ed3e9676a1d47cab2e3174d19b43f5dbf52681"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/5553558bd381fce5108c6d0343c12e488cfec6bb",
"reference": "5553558bd381fce5108c6d0343c12e488cfec6bb",
"url": "https://api.github.com/repos/chillerlan/php-settings-container/zipball/95ed3e9676a1d47cab2e3174d19b43f5dbf52681",
"reference": "95ed3e9676a1d47cab2e3174d19b43f5dbf52681",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^7.4 || ^8.0"
"php": "^8.1"
},
"require-dev": {
"phpmd/phpmd": "^2.15",
"phpstan/phpstan": "^1.11",
"phpstan/phpstan-deprecation-rules": "^1.2",
"phpunit/phpunit": "^9.6",
"phpunit/phpunit": "^10.5",
"squizlabs/php_codesniffer": "^3.10"
},
"type": "library",
@@ -457,10 +476,9 @@
"homepage": "https://github.com/codemasher"
}
],
"description": "A container class for immutable settings objects. Not a DI container. PHP 7.4+",
"description": "A container class for immutable settings objects. Not a DI container.",
"homepage": "https://github.com/chillerlan/php-settings-container",
"keywords": [
"PHP7",
"Settings",
"configuration",
"container",
@@ -480,7 +498,7 @@
"type": "ko_fi"
}
],
"time": "2024-07-17T01:04:28+00:00"
"time": "2024-07-16T11:13:48+00:00"
},
{
"name": "composer/semver",
@@ -2371,16 +2389,16 @@
},
{
"name": "ramsey/collection",
"version": "2.0.0",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/ramsey/collection.git",
"reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5"
"reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ramsey/collection/zipball/a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
"reference": "a4b48764bfbb8f3a6a4d1aeb1a35bb5e9ecac4a5",
"url": "https://api.github.com/repos/ramsey/collection/zipball/3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109",
"reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109",
"shasum": ""
},
"require": {
@@ -2388,25 +2406,22 @@
},
"require-dev": {
"captainhook/plugin-composer": "^5.3",
"ergebnis/composer-normalize": "^2.28.3",
"fakerphp/faker": "^1.21",
"ergebnis/composer-normalize": "^2.45",
"fakerphp/faker": "^1.24",
"hamcrest/hamcrest-php": "^2.0",
"jangregor/phpstan-prophecy": "^1.0",
"mockery/mockery": "^1.5",
"jangregor/phpstan-prophecy": "^2.1",
"mockery/mockery": "^1.6",
"php-parallel-lint/php-console-highlighter": "^1.0",
"php-parallel-lint/php-parallel-lint": "^1.3",
"phpcsstandards/phpcsutils": "^1.0.0-rc1",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/extension-installer": "^1.2",
"phpstan/phpstan": "^1.9",
"phpstan/phpstan-mockery": "^1.1",
"phpstan/phpstan-phpunit": "^1.3",
"phpunit/phpunit": "^9.5",
"psalm/plugin-mockery": "^1.1",
"psalm/plugin-phpunit": "^0.18.4",
"ramsey/coding-standard": "^2.0.3",
"ramsey/conventional-commits": "^1.3",
"vimeo/psalm": "^5.4"
"php-parallel-lint/php-parallel-lint": "^1.4",
"phpspec/prophecy-phpunit": "^2.3",
"phpstan/extension-installer": "^1.4",
"phpstan/phpstan": "^2.1",
"phpstan/phpstan-mockery": "^2.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^10.5",
"ramsey/coding-standard": "^2.3",
"ramsey/conventional-commits": "^1.6",
"roave/security-advisories": "dev-latest"
},
"type": "library",
"extra": {
@@ -2444,19 +2459,9 @@
],
"support": {
"issues": "https://github.com/ramsey/collection/issues",
"source": "https://github.com/ramsey/collection/tree/2.0.0"
"source": "https://github.com/ramsey/collection/tree/2.1.0"
},
"funding": [
{
"url": "https://github.com/ramsey",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/ramsey/collection",
"type": "tidelift"
}
],
"time": "2022-12-31T21:50:55+00:00"
"time": "2025-03-02T04:48:29+00:00"
},
{
"name": "ramsey/uuid",
@@ -8806,7 +8811,9 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {
"chillerlan/php-qrcode": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
+1
View File
@@ -85,6 +85,7 @@ services:
- ./public:/usr/src/code/public
- ./src:/usr/src/code/src
- ./dev:/usr/src/code/dev
- ./vendor/utopia-php/image:/usr/src/code/vendor/utopia-php/image # Exposed private image to do Imagick operations
depends_on:
- mariadb
- redis