CluPilotCloud/app/Services/Wireguard/QrCode.php

47 lines
1.7 KiB
PHP

<?php
namespace App\Services\Wireguard;
use BaconQrCode\Common\ErrorCorrectionLevel;
use BaconQrCode\Encoder\Encoder;
/**
* Renders a WireGuard config as a QR code the mobile apps can scan.
*
* Hand-rolled SVG rather than Bacon's renderer: that one needs the imagick or
* GD backend for images, and its SVG writer pulls in a dependency chain we do
* not otherwise need. The matrix is the only interesting part, and Bacon gives
* us that directly.
*/
final class QrCode
{
public static function svg(string $text, int $size = 220): string
{
// Level L: a WireGuard config is long, and the phone is held still
// against a screen — more redundancy would only make the code denser.
$matrix = Encoder::encode($text, ErrorCorrectionLevel::L())->getMatrix();
$width = $matrix->getWidth();
// Four modules is the specified minimum quiet zone. Less than that and
// some readers fail to find the symbol at all against a bordered card.
$quiet = 4;
$total = $width + 2 * $quiet;
$cells = '';
for ($y = 0; $y < $width; $y++) {
for ($x = 0; $x < $width; $x++) {
if ($matrix->get($x, $y) === 1) {
$cells .= sprintf('<rect x="%d" y="%d" width="1" height="1"/>', $x + $quiet, $y + $quiet);
}
}
}
return sprintf(
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 %1$d %1$d" width="%2$d" height="%2$d" shape-rendering="crispEdges" role="img">'
.'<rect width="%1$d" height="%1$d" fill="#ffffff"/><g fill="#000000">%3$s</g></svg>',
$total,
$size,
$cells,
);
}
}