47 lines
1.7 KiB
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,
|
|
);
|
|
}
|
|
}
|