AbstractWriter.php
3 years ago
BinaryWriter.php
3 years ago
DebugWriter.php
3 years ago
EpsWriter.php
3 years ago
FpdfWriter.php
3 years ago
PngWriter.php
3 years ago
SvgWriter.php
3 years ago
WriterInterface.php
3 years ago
SvgWriter.php
138 lines
| 1 | <?php |
| 2 | |
| 3 | declare (strict_types=1); |
| 4 | /* |
| 5 | * (c) Jeroen van den Enden <info@endroid.nl> |
| 6 | * |
| 7 | * This source file is subject to the MIT license that is bundled |
| 8 | * with this source code in the file LICENSE. |
| 9 | */ |
| 10 | namespace WP2FA_Vendor\Endroid\QrCode\Writer; |
| 11 | |
| 12 | use WP2FA_Vendor\Endroid\QrCode\Exception\GenerateImageException; |
| 13 | use WP2FA_Vendor\Endroid\QrCode\Exception\MissingLogoHeightException; |
| 14 | use WP2FA_Vendor\Endroid\QrCode\Exception\ValidationException; |
| 15 | use WP2FA_Vendor\Endroid\QrCode\QrCodeInterface; |
| 16 | use SimpleXMLElement; |
| 17 | class SvgWriter extends AbstractWriter |
| 18 | { |
| 19 | public function writeString(QrCodeInterface $qrCode) : string |
| 20 | { |
| 21 | $options = $qrCode->getWriterOptions(); |
| 22 | if ($qrCode->getValidateResult()) { |
| 23 | throw new ValidationException('Built-in validation reader can not check SVG images: please disable via setValidateResult(false)'); |
| 24 | } |
| 25 | $data = $qrCode->getData(); |
| 26 | $svg = new SimpleXMLElement('<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"/>'); |
| 27 | $svg->addAttribute('version', '1.1'); |
| 28 | $svg->addAttribute('width', $data['outer_width'] . 'px'); |
| 29 | $svg->addAttribute('height', $data['outer_height'] . 'px'); |
| 30 | $svg->addAttribute('viewBox', '0 0 ' . $data['outer_width'] . ' ' . $data['outer_height']); |
| 31 | $svg->addChild('defs'); |
| 32 | // Block definition |
| 33 | $block_id = isset($options['rect_id']) && $options['rect_id'] ? $options['rect_id'] : 'block'; |
| 34 | $blockDefinition = $svg->defs->addChild('rect'); |
| 35 | $blockDefinition->addAttribute('id', $block_id); |
| 36 | $blockDefinition->addAttribute('width', \strval($data['block_size'])); |
| 37 | $blockDefinition->addAttribute('height', \strval($data['block_size'])); |
| 38 | $blockDefinition->addAttribute('fill', '#' . \sprintf('%02x%02x%02x', $qrCode->getForegroundColor()['r'], $qrCode->getForegroundColor()['g'], $qrCode->getForegroundColor()['b'])); |
| 39 | $blockDefinition->addAttribute('fill-opacity', \strval($this->getOpacity($qrCode->getForegroundColor()['a']))); |
| 40 | // Background |
| 41 | $background = $svg->addChild('rect'); |
| 42 | $background->addAttribute('x', '0'); |
| 43 | $background->addAttribute('y', '0'); |
| 44 | $background->addAttribute('width', \strval($data['outer_width'])); |
| 45 | $background->addAttribute('height', \strval($data['outer_height'])); |
| 46 | $background->addAttribute('fill', '#' . \sprintf('%02x%02x%02x', $qrCode->getBackgroundColor()['r'], $qrCode->getBackgroundColor()['g'], $qrCode->getBackgroundColor()['b'])); |
| 47 | $background->addAttribute('fill-opacity', \strval($this->getOpacity($qrCode->getBackgroundColor()['a']))); |
| 48 | foreach ($data['matrix'] as $row => $values) { |
| 49 | foreach ($values as $column => $value) { |
| 50 | if (1 === $value) { |
| 51 | $block = $svg->addChild('use'); |
| 52 | $block->addAttribute('x', \strval($data['margin_left'] + $data['block_size'] * $column)); |
| 53 | $block->addAttribute('y', \strval($data['margin_left'] + $data['block_size'] * $row)); |
| 54 | $block->addAttribute('xlink:href', '#' . $block_id, 'http://www.w3.org/1999/xlink'); |
| 55 | } |
| 56 | } |
| 57 | } |
| 58 | $logoPath = $qrCode->getLogoPath(); |
| 59 | if (\is_string($logoPath)) { |
| 60 | $forceXlinkHref = \false; |
| 61 | if (isset($options['force_xlink_href']) && $options['force_xlink_href']) { |
| 62 | $forceXlinkHref = \true; |
| 63 | } |
| 64 | $this->addLogo($svg, $data['outer_width'], $data['outer_height'], $logoPath, $qrCode->getLogoWidth(), $qrCode->getLogoHeight(), $forceXlinkHref); |
| 65 | } |
| 66 | $xml = $svg->asXML(); |
| 67 | if (!\is_string($xml)) { |
| 68 | throw new GenerateImageException('Unable to save SVG XML'); |
| 69 | } |
| 70 | if (isset($options['exclude_xml_declaration']) && $options['exclude_xml_declaration']) { |
| 71 | $xml = \str_replace("<?xml version=\"1.0\"?>\n", '', $xml); |
| 72 | } |
| 73 | return $xml; |
| 74 | } |
| 75 | private function addLogo(SimpleXMLElement $svg, int $imageWidth, int $imageHeight, string $logoPath, int $logoWidth = null, int $logoHeight = null, bool $forceXlinkHref = \false) : void |
| 76 | { |
| 77 | $mimeType = $this->getMimeType($logoPath); |
| 78 | $imageData = \file_get_contents($logoPath); |
| 79 | if (!\is_string($imageData)) { |
| 80 | throw new GenerateImageException('Unable to read image data: check your logo path'); |
| 81 | } |
| 82 | if ('image/svg+xml' === $mimeType && (null === $logoHeight || null === $logoWidth)) { |
| 83 | throw new MissingLogoHeightException('SVG Logos require an explicit height set via setLogoSize($width, $height)'); |
| 84 | } |
| 85 | if (null === $logoHeight || null === $logoWidth) { |
| 86 | $logoImage = \imagecreatefromstring(\strval($imageData)); |
| 87 | if (!$logoImage) { |
| 88 | throw new GenerateImageException('Unable to generate image: check your GD installation or logo path'); |
| 89 | } |
| 90 | /** @var mixed $logoImage */ |
| 91 | $logoSourceWidth = \imagesx($logoImage); |
| 92 | $logoSourceHeight = \imagesy($logoImage); |
| 93 | if (\PHP_VERSION_ID < 80000) { |
| 94 | \imagedestroy($logoImage); |
| 95 | } |
| 96 | if (null === $logoWidth) { |
| 97 | $logoWidth = $logoSourceWidth; |
| 98 | } |
| 99 | if (null === $logoHeight) { |
| 100 | $aspectRatio = $logoWidth / $logoSourceWidth; |
| 101 | $logoHeight = \intval($logoSourceHeight * $aspectRatio); |
| 102 | } |
| 103 | } |
| 104 | $logoX = $imageWidth / 2 - $logoWidth / 2; |
| 105 | $logoY = $imageHeight / 2 - $logoHeight / 2; |
| 106 | $imageDefinition = $svg->addChild('image'); |
| 107 | $imageDefinition->addAttribute('x', \strval($logoX)); |
| 108 | $imageDefinition->addAttribute('y', \strval($logoY)); |
| 109 | $imageDefinition->addAttribute('width', \strval($logoWidth)); |
| 110 | $imageDefinition->addAttribute('height', \strval($logoHeight)); |
| 111 | $imageDefinition->addAttribute('preserveAspectRatio', 'none'); |
| 112 | // xlink:href is actually deprecated, but still required when placing the qr code in a pdf. |
| 113 | // SimpleXML strips out the xlink part by using addAttribute(), so it must be set directly. |
| 114 | if ($forceXlinkHref) { |
| 115 | $imageDefinition['xlink:href'] = 'data:' . $mimeType . ';base64,' . \base64_encode($imageData); |
| 116 | } else { |
| 117 | $imageDefinition->addAttribute('href', 'data:' . $mimeType . ';base64,' . \base64_encode($imageData)); |
| 118 | } |
| 119 | } |
| 120 | private function getOpacity(int $alpha) : float |
| 121 | { |
| 122 | $opacity = 1 - $alpha / 127; |
| 123 | return $opacity; |
| 124 | } |
| 125 | public static function getContentType() : string |
| 126 | { |
| 127 | return 'image/svg+xml'; |
| 128 | } |
| 129 | public static function getSupportedExtensions() : array |
| 130 | { |
| 131 | return ['svg']; |
| 132 | } |
| 133 | public function getName() : string |
| 134 | { |
| 135 | return 'svg'; |
| 136 | } |
| 137 | } |
| 138 |