blob: 4d5cf01508fdef6b0854f49f1184c0ae719f1e50 [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
3namespace RobThree\Auth\Providers\Qr;
4
5use BaconQrCode\Writer;
6use BaconQrCode\Renderer\ImageRenderer;
7use BaconQrCode\Renderer\RendererStyle\RendererStyle;
8use BaconQrCode\Renderer\RendererStyle\Fill;
9use BaconQrCode\Renderer\Color\Rgb;
10use BaconQrCode\Renderer\RendererStyle\EyeFill;
11
12use BaconQrCode\Renderer\Image\EpsImageBackEnd;
13use BaconQrCode\Renderer\Image\ImageBackEndInterface;
14use BaconQrCode\Renderer\Image\ImagickImageBackEnd;
15use BaconQrCode\Renderer\Image\SvgImageBackEnd;
16
17class BaconQrCodeProvider implements IQRCodeProvider
18{
19 private $borderWidth = 4; // default from Bacon QR Code
20 private $backgroundColour;
21 private $foregroundColour;
22 private $format;
23
24 /**
25 * Ensure we using the latest Bacon QR Code and specify default options
26 *
27 * @param int $borderWidth space around the QR code, 4 is the default from Bacon QR Code
28 * @param string $backgroundColour hex reference for the background colour
29 * @param string $foregroundColour hex reference for the foreground colour
30 * @param string $format the desired output, png or svg
31 */
32 public function __construct($borderWidth = 4, $backgroundColour = '#ffffff', $foregroundColour = '#000000', $format = 'png')
33 {
34 if (! class_exists(ImagickImageBackEnd::class)) {
35 throw new \RuntimeException('Make sure you are using version 2 of Bacon QR Code');
36 }
37
38 $this->borderWidth = $borderWidth;
39 $this->backgroundColour = $this->handleColour($backgroundColour);
40 $this->foregroundColour = $this->handleColour($foregroundColour);
41 $this->format = strtolower($format);
42 }
43
44 /**
45 * Standard functions from IQRCodeProvider
46 */
47
48 public function getMimeType()
49 {
50 switch ($this->format) {
51 case 'png':
52 return 'image/png';
53 case 'gif':
54 return 'image/gif';
55 case 'jpg':
56 case 'jpeg':
57 return 'image/jpeg';
58 case 'svg':
59 return 'image/svg+xml';
60 case 'eps':
61 return 'application/postscript';
62 }
63
64 throw new \RuntimeException(sprintf('Unknown MIME-type: %s', $this->format));
65 }
66
67 public function getQRCodeImage($qrText, $size)
68 {
69 switch ($this->format) {
70 case 'svg':
71 $backend = new SvgImageBackEnd;
72 break;
73 case 'eps':
74 $backend = new EpsImageBackEnd;
75 break;
76 default:
77 $backend = new ImagickImageBackEnd($this->format);
78 }
79
80 $output = $this->getQRCodeByBackend($qrText, $size, $backend);
81
82 if ($this->format == 'svg') {
83 $svg = explode("\n", $output);
84 return $svg[1];
85 }
86
87 return $output;
88 }
89
90 /**
91 * Abstract QR code generation function
92 * providing colour changing support
93 */
94 private function getQRCodeByBackend($qrText, $size, ImageBackEndInterface $backend)
95 {
96 $rendererStyleArgs = array($size, $this->borderWidth);
97
98 if (is_array($this->foregroundColour) && is_array($this->backgroundColour)) {
99 $rendererStyleArgs = array_merge($rendererStyleArgs, array(
100 null,
101 null,
102 Fill::withForegroundColor(
103 new Rgb(...$this->backgroundColour),
104 new Rgb(...$this->foregroundColour),
105 new EyeFill(null, null),
106 new EyeFill(null, null),
107 new EyeFill(null, null)
108 )
109 ));
110 }
111
112 $writer = new Writer(new ImageRenderer(
113 new RendererStyle(...$rendererStyleArgs),
114 $backend
115 ));
116
117 return $writer->writeString($qrText);
118 }
119
120 /**
121 * Ensure colour is an array of three values but also
122 * accept a string and assume its a 3 or 6 character hex
123 */
124 private function handleColour($colour)
125 {
126 if (is_string($colour) && $colour[0] == '#') {
127 $hexToRGB = function ($input) {
128 // split the array into three chunks
129 $split = str_split(trim($input, '#'), strlen($input) / 3);
130
131 // cope with three character hex reference
132 // three characters plus a # = 4
133 if (strlen($input) == 4) {
134 array_walk($split, function (&$character) {
135 $character = str_repeat($character, 2);
136 });
137 }
138
139 // convert hex to rgb
140 return array_map('hexdec', $split);
141 };
142
143 return $hexToRGB($colour);
144 }
145
146 if (is_array($colour) && count($colour) == 3) {
147 return $colour;
148 }
149
150 throw new \RuntimeException('Invalid colour value');
151 }
152}