PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.9.1
WP 2FA – Two-factor authentication for WordPress v2.9.1
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / bacon / bacon-qr-code / src / Renderer / Image / SvgImageBackEnd.php
wp-2fa / includes / classes / bacon / bacon-qr-code / src / Renderer / Image Last commit date
EpsImageBackEnd.php 11 months ago ImageBackEndInterface.php 11 months ago ImagickImageBackEnd.php 11 months ago SvgImageBackEnd.php 11 months ago TransformationMatrix.php 11 months ago
SvgImageBackEnd.php
262 lines
1 <?php
2
3 declare (strict_types=1);
4 namespace WP2FA_Vendor\BaconQrCode\Renderer\Image;
5
6 use WP2FA_Vendor\BaconQrCode\Exception\RuntimeException;
7 use WP2FA_Vendor\BaconQrCode\Renderer\Color\Alpha;
8 use WP2FA_Vendor\BaconQrCode\Renderer\Color\ColorInterface;
9 use WP2FA_Vendor\BaconQrCode\Renderer\Path\Close;
10 use WP2FA_Vendor\BaconQrCode\Renderer\Path\Curve;
11 use WP2FA_Vendor\BaconQrCode\Renderer\Path\EllipticArc;
12 use WP2FA_Vendor\BaconQrCode\Renderer\Path\Line;
13 use WP2FA_Vendor\BaconQrCode\Renderer\Path\Move;
14 use WP2FA_Vendor\BaconQrCode\Renderer\Path\Path;
15 use WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle\Gradient;
16 use WP2FA_Vendor\BaconQrCode\Renderer\RendererStyle\GradientType;
17 use XMLWriter;
18 final class SvgImageBackEnd implements ImageBackEndInterface
19 {
20 private const PRECISION = 3;
21 /**
22 * @var XMLWriter|null
23 */
24 private $xmlWriter;
25 /**
26 * @var int[]|null
27 */
28 private $stack;
29 /**
30 * @var int|null
31 */
32 private $currentStack;
33 /**
34 * @var int|null
35 */
36 private $gradientCount;
37 public function __construct()
38 {
39 if (!\class_exists(XMLWriter::class)) {
40 throw new RuntimeException('You need to install the libxml extension to use this back end');
41 }
42 }
43 public function new(int $size, ColorInterface $backgroundColor) : void
44 {
45 $this->xmlWriter = new XMLWriter();
46 $this->xmlWriter->openMemory();
47 $this->xmlWriter->startDocument('1.0', 'UTF-8');
48 $this->xmlWriter->startElement('svg');
49 $this->xmlWriter->writeAttribute('xmlns', 'http://www.w3.org/2000/svg');
50 $this->xmlWriter->writeAttribute('version', '1.1');
51 $this->xmlWriter->writeAttribute('width', (string) $size);
52 $this->xmlWriter->writeAttribute('height', (string) $size);
53 $this->xmlWriter->writeAttribute('viewBox', '0 0 ' . $size . ' ' . $size);
54 $this->gradientCount = 0;
55 $this->currentStack = 0;
56 $this->stack[0] = 0;
57 $alpha = 1;
58 if ($backgroundColor instanceof Alpha) {
59 $alpha = $backgroundColor->getAlpha() / 100;
60 }
61 if (0 === $alpha) {
62 return;
63 }
64 $this->xmlWriter->startElement('rect');
65 $this->xmlWriter->writeAttribute('x', '0');
66 $this->xmlWriter->writeAttribute('y', '0');
67 $this->xmlWriter->writeAttribute('width', (string) $size);
68 $this->xmlWriter->writeAttribute('height', (string) $size);
69 $this->xmlWriter->writeAttribute('fill', $this->getColorString($backgroundColor));
70 if ($alpha < 1) {
71 $this->xmlWriter->writeAttribute('fill-opacity', (string) $alpha);
72 }
73 $this->xmlWriter->endElement();
74 }
75 public function scale(float $size) : void
76 {
77 if (null === $this->xmlWriter) {
78 throw new RuntimeException('No image has been started');
79 }
80 $this->xmlWriter->startElement('g');
81 $this->xmlWriter->writeAttribute('transform', \sprintf('scale(%s)', \round($size, self::PRECISION)));
82 ++$this->stack[$this->currentStack];
83 }
84 public function translate(float $x, float $y) : void
85 {
86 if (null === $this->xmlWriter) {
87 throw new RuntimeException('No image has been started');
88 }
89 $this->xmlWriter->startElement('g');
90 $this->xmlWriter->writeAttribute('transform', \sprintf('translate(%s,%s)', \round($x, self::PRECISION), \round($y, self::PRECISION)));
91 ++$this->stack[$this->currentStack];
92 }
93 public function rotate(int $degrees) : void
94 {
95 if (null === $this->xmlWriter) {
96 throw new RuntimeException('No image has been started');
97 }
98 $this->xmlWriter->startElement('g');
99 $this->xmlWriter->writeAttribute('transform', \sprintf('rotate(%d)', $degrees));
100 ++$this->stack[$this->currentStack];
101 }
102 public function push() : void
103 {
104 if (null === $this->xmlWriter) {
105 throw new RuntimeException('No image has been started');
106 }
107 $this->xmlWriter->startElement('g');
108 $this->stack[] = 1;
109 ++$this->currentStack;
110 }
111 public function pop() : void
112 {
113 if (null === $this->xmlWriter) {
114 throw new RuntimeException('No image has been started');
115 }
116 for ($i = 0; $i < $this->stack[$this->currentStack]; ++$i) {
117 $this->xmlWriter->endElement();
118 }
119 \array_pop($this->stack);
120 --$this->currentStack;
121 }
122 public function drawPathWithColor(Path $path, ColorInterface $color) : void
123 {
124 if (null === $this->xmlWriter) {
125 throw new RuntimeException('No image has been started');
126 }
127 $alpha = 1;
128 if ($color instanceof Alpha) {
129 $alpha = $color->getAlpha() / 100;
130 }
131 $this->startPathElement($path);
132 $this->xmlWriter->writeAttribute('fill', $this->getColorString($color));
133 if ($alpha < 1) {
134 $this->xmlWriter->writeAttribute('fill-opacity', (string) $alpha);
135 }
136 $this->xmlWriter->endElement();
137 }
138 public function drawPathWithGradient(Path $path, Gradient $gradient, float $x, float $y, float $width, float $height) : void
139 {
140 if (null === $this->xmlWriter) {
141 throw new RuntimeException('No image has been started');
142 }
143 $gradientId = $this->createGradientFill($gradient, $x, $y, $width, $height);
144 $this->startPathElement($path);
145 $this->xmlWriter->writeAttribute('fill', 'url(#' . $gradientId . ')');
146 $this->xmlWriter->endElement();
147 }
148 public function done() : string
149 {
150 if (null === $this->xmlWriter) {
151 throw new RuntimeException('No image has been started');
152 }
153 foreach ($this->stack as $openElements) {
154 for ($i = $openElements; $i > 0; --$i) {
155 $this->xmlWriter->endElement();
156 }
157 }
158 $this->xmlWriter->endDocument();
159 $blob = $this->xmlWriter->outputMemory(\true);
160 $this->xmlWriter = null;
161 $this->stack = null;
162 $this->currentStack = null;
163 $this->gradientCount = null;
164 return $blob;
165 }
166 private function startPathElement(Path $path) : void
167 {
168 $pathData = [];
169 foreach ($path as $op) {
170 switch (\true) {
171 case $op instanceof Move:
172 $pathData[] = \sprintf('M%s %s', \round($op->getX(), self::PRECISION), \round($op->getY(), self::PRECISION));
173 break;
174 case $op instanceof Line:
175 $pathData[] = \sprintf('L%s %s', \round($op->getX(), self::PRECISION), \round($op->getY(), self::PRECISION));
176 break;
177 case $op instanceof EllipticArc:
178 $pathData[] = \sprintf('A%s %s %s %u %u %s %s', \round($op->getXRadius(), self::PRECISION), \round($op->getYRadius(), self::PRECISION), \round($op->getXAxisAngle(), self::PRECISION), $op->isLargeArc(), $op->isSweep(), \round($op->getX(), self::PRECISION), \round($op->getY(), self::PRECISION));
179 break;
180 case $op instanceof Curve:
181 $pathData[] = \sprintf('C%s %s %s %s %s %s', \round($op->getX1(), self::PRECISION), \round($op->getY1(), self::PRECISION), \round($op->getX2(), self::PRECISION), \round($op->getY2(), self::PRECISION), \round($op->getX3(), self::PRECISION), \round($op->getY3(), self::PRECISION));
182 break;
183 case $op instanceof Close:
184 $pathData[] = 'Z';
185 break;
186 default:
187 throw new RuntimeException('Unexpected draw operation: ' . \get_class($op));
188 }
189 }
190 $this->xmlWriter->startElement('path');
191 $this->xmlWriter->writeAttribute('fill-rule', 'evenodd');
192 $this->xmlWriter->writeAttribute('d', \implode('', $pathData));
193 }
194 private function createGradientFill(Gradient $gradient, float $x, float $y, float $width, float $height) : string
195 {
196 $this->xmlWriter->startElement('defs');
197 $startColor = $gradient->getStartColor();
198 $endColor = $gradient->getEndColor();
199 if ($gradient->getType() === GradientType::RADIAL()) {
200 $this->xmlWriter->startElement('radialGradient');
201 } else {
202 $this->xmlWriter->startElement('linearGradient');
203 }
204 $this->xmlWriter->writeAttribute('gradientUnits', 'userSpaceOnUse');
205 switch ($gradient->getType()) {
206 case GradientType::HORIZONTAL():
207 $this->xmlWriter->writeAttribute('x1', (string) \round($x, self::PRECISION));
208 $this->xmlWriter->writeAttribute('y1', (string) \round($y, self::PRECISION));
209 $this->xmlWriter->writeAttribute('x2', (string) \round($x + $width, self::PRECISION));
210 $this->xmlWriter->writeAttribute('y2', (string) \round($y, self::PRECISION));
211 break;
212 case GradientType::VERTICAL():
213 $this->xmlWriter->writeAttribute('x1', (string) \round($x, self::PRECISION));
214 $this->xmlWriter->writeAttribute('y1', (string) \round($y, self::PRECISION));
215 $this->xmlWriter->writeAttribute('x2', (string) \round($x, self::PRECISION));
216 $this->xmlWriter->writeAttribute('y2', (string) \round($y + $height, self::PRECISION));
217 break;
218 case GradientType::DIAGONAL():
219 $this->xmlWriter->writeAttribute('x1', (string) \round($x, self::PRECISION));
220 $this->xmlWriter->writeAttribute('y1', (string) \round($y, self::PRECISION));
221 $this->xmlWriter->writeAttribute('x2', (string) \round($x + $width, self::PRECISION));
222 $this->xmlWriter->writeAttribute('y2', (string) \round($y + $height, self::PRECISION));
223 break;
224 case GradientType::INVERSE_DIAGONAL():
225 $this->xmlWriter->writeAttribute('x1', (string) \round($x, self::PRECISION));
226 $this->xmlWriter->writeAttribute('y1', (string) \round($y + $height, self::PRECISION));
227 $this->xmlWriter->writeAttribute('x2', (string) \round($x + $width, self::PRECISION));
228 $this->xmlWriter->writeAttribute('y2', (string) \round($y, self::PRECISION));
229 break;
230 case GradientType::RADIAL():
231 $this->xmlWriter->writeAttribute('cx', (string) \round(($x + $width) / 2, self::PRECISION));
232 $this->xmlWriter->writeAttribute('cy', (string) \round(($y + $height) / 2, self::PRECISION));
233 $this->xmlWriter->writeAttribute('r', (string) \round(\max($width, $height) / 2, self::PRECISION));
234 break;
235 }
236 $id = \sprintf('g%d', ++$this->gradientCount);
237 $this->xmlWriter->writeAttribute('id', $id);
238 $this->xmlWriter->startElement('stop');
239 $this->xmlWriter->writeAttribute('offset', '0%');
240 $this->xmlWriter->writeAttribute('stop-color', $this->getColorString($startColor));
241 if ($startColor instanceof Alpha) {
242 $this->xmlWriter->writeAttribute('stop-opacity', (string) $startColor->getAlpha());
243 }
244 $this->xmlWriter->endElement();
245 $this->xmlWriter->startElement('stop');
246 $this->xmlWriter->writeAttribute('offset', '100%');
247 $this->xmlWriter->writeAttribute('stop-color', $this->getColorString($endColor));
248 if ($endColor instanceof Alpha) {
249 $this->xmlWriter->writeAttribute('stop-opacity', (string) $endColor->getAlpha());
250 }
251 $this->xmlWriter->endElement();
252 $this->xmlWriter->endElement();
253 $this->xmlWriter->endElement();
254 return $id;
255 }
256 private function getColorString(ColorInterface $color) : string
257 {
258 $color = $color->toRgb();
259 return \sprintf('#%02x%02x%02x', $color->getRed(), $color->getGreen(), $color->getBlue());
260 }
261 }
262