Преглед изворни кода

:bath: extract QRMarkup into QRMarkupSVG and QRMarkupHTML

smiley пре 3 година
родитељ
комит
cde2af576b

+ 6 - 24
examples/QRSvgWithLogo.php

@@ -10,44 +10,26 @@
 
 
 namespace chillerlan\QRCodeExamples;
 namespace chillerlan\QRCodeExamples;
 
 
-use chillerlan\QRCode\Output\QRMarkup;
-use function file_get_contents, sprintf;
+use chillerlan\QRCode\Output\QRMarkupSVG;
+use function ceil, file_get_contents, sprintf;
 
 
 /**
 /**
  * Create SVG QR Codes with embedded logos (that are also SVG)
  * Create SVG QR Codes with embedded logos (that are also SVG)
  */
  */
-class QRSvgWithLogo extends QRMarkup{
+class QRSvgWithLogo extends QRMarkupSVG{
 
 
 	/**
 	/**
-	 * SVG output
-	 *
-	 * @see https://github.com/codemasher/php-qrcode/pull/5
-	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg
-	 * @see https://www.sarasoueidan.com/demos/interactive-svg-coordinate-system/
+	 * @inheritDoc
 	 */
 	 */
-	protected function svg(bool $saveToFile):string{
+	protected function paths():string{
 		$size = (int)ceil($this->moduleCount * $this->options->svgLogoScale);
 		$size = (int)ceil($this->moduleCount * $this->options->svgLogoScale);
 
 
 		// we're calling QRMatrix::setLogoSpace() manually, so QROptions::$addLogoSpace has no effect here
 		// we're calling QRMatrix::setLogoSpace() manually, so QROptions::$addLogoSpace has no effect here
 		$this->matrix->setLogoSpace($size, $size);
 		$this->matrix->setLogoSpace($size, $size);
 
 
-		$svg = $this->svgHeader();
-
-		if(!empty($this->options->svgDefs)){
-			$svg .= sprintf('<defs>%1$s%2$s</defs>%2$s', $this->options->svgDefs, $this->options->eol);
-		}
-
-		$svg .= $this->svgPaths();
+		$svg = parent::paths();
 		$svg .= $this->getLogo();
 		$svg .= $this->getLogo();
 
 
-		// close svg
-		$svg .= sprintf('%1$s</svg>%1$s', $this->options->eol);
-
-		// transform to data URI only when not saving to file
-		if(!$saveToFile && $this->options->imageBase64){
-			$svg = $this->base64encode($svg, 'image/svg+xml');
-		}
-
 		return $svg;
 		return $svg;
 	}
 	}
 
 

+ 6 - 152
src/Output/QRMarkup.php

@@ -10,15 +10,12 @@
 
 
 namespace chillerlan\QRCode\Output;
 namespace chillerlan\QRCode\Output;
 
 
-use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\QRCode;
-
-use function implode, is_string, sprintf, strip_tags, trim;
+use function is_string, strip_tags, trim;
 
 
 /**
 /**
- * Converts the matrix into markup types: HTML, SVG, ...
+ * Abstract for markup types: HTML, SVG, ... XML anyone?
  */
  */
-class QRMarkup extends QROutputAbstract{
+abstract class QRMarkup extends QROutputAbstract{
 
 
 	/**
 	/**
 	 * @inheritDoc
 	 * @inheritDoc
@@ -44,18 +41,11 @@ class QRMarkup extends QROutputAbstract{
 	/**
 	/**
 	 * @inheritDoc
 	 * @inheritDoc
 	 */
 	 */
-	public function dump(string $file = null){
+	public function dump(string $file = null):string{
 		$file       ??= $this->options->cachefile;
 		$file       ??= $this->options->cachefile;
 		$saveToFile   = $file !== null;
 		$saveToFile   = $file !== null;
 
 
-		switch($this->options->outputType){
-			case QRCode::OUTPUT_MARKUP_HTML:
-				$data = $this->html($saveToFile);
-				break;
-			case QRCode::OUTPUT_MARKUP_SVG:
-			default:
-				$data = $this->svg($saveToFile);
-		}
+		$data = $this->createMarkup($saveToFile);
 
 
 		if($saveToFile){
 		if($saveToFile){
 			$this->saveToFile($data, $file);
 			$this->saveToFile($data, $file);
@@ -65,143 +55,7 @@ class QRMarkup extends QROutputAbstract{
 	}
 	}
 
 
 	/**
 	/**
-	 * HTML output
-	 */
-	protected function html(bool $saveToFile):string{
-
-		$html = empty($this->options->cssClass)
-			? '<div>'
-			: sprintf('<div class="%s">', $this->options->cssClass);
-
-		$html .= $this->options->eol;
-
-		foreach($this->matrix->matrix() as $row){
-			$html .= '<div>';
-
-			foreach($row as $M_TYPE){
-				$html .= sprintf('<span style="background: %s;"></span>', $this->moduleValues[$M_TYPE]);
-			}
-
-			$html .= '</div>'.$this->options->eol;
-		}
-
-		$html .= '</div>'.$this->options->eol;
-
-		if($saveToFile){
-			return sprintf(
-				'<!DOCTYPE html><head><meta charset="UTF-8"><title>QR Code</title></head><body>%s</body>',
-				$this->options->eol.$html
-			);
-		}
-
-		return $html;
-	}
-
-	/**
-	 * SVG output
 	 *
 	 *
-	 * @see https://github.com/codemasher/php-qrcode/pull/5
-	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg
-	 * @see https://www.sarasoueidan.com/demos/interactive-svg-coordinate-system/
-	 */
-	protected function svg(bool $saveToFile):string{
-		$svg = $this->svgHeader();
-
-		if(!empty($this->options->svgDefs)){
-			$svg .= sprintf('<defs>%1$s%2$s</defs>%2$s', $this->options->svgDefs, $this->options->eol);
-		}
-
-		$svg .= $this->svgPaths();
-
-		// close svg
-		$svg .= sprintf('%1$s</svg>%1$s', $this->options->eol);
-
-		// transform to data URI only when not saving to file
-		if(!$saveToFile && $this->options->imageBase64){
-			$svg = $this->base64encode($svg, 'image/svg+xml');
-		}
-
-		return $svg;
-	}
-
-	/**
-	 * returns the <svg> header with the given options parsed
 	 */
 	 */
-	protected function svgHeader():string{
-		$width  = $this->options->svgWidth !== null ? sprintf(' width="%s"', $this->options->svgWidth) : '';
-		$height = $this->options->svgHeight !== null ? sprintf(' height="%s"', $this->options->svgHeight) : '';
-
-		/** @noinspection HtmlUnknownAttribute */
-		return sprintf(
-			'<?xml version="1.0" encoding="UTF-8"?>%6$s'.
-			'<svg xmlns="http://www.w3.org/2000/svg" class="qr-svg %1$s" viewBox="0 0 %2$s %2$s" preserveAspectRatio="%3$s"%4$s%5$s>%6$s',
-			$this->options->cssClass,
-			$this->options->svgViewBoxSize ?? $this->moduleCount,
-			$this->options->svgPreserveAspectRatio,
-			$width,
-			$height,
-			$this->options->eol
-		);
-	}
-
-	/**
-	 * returns one or more SVG <path> elements
-	 *
-	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
-	 */
-	protected function svgPaths():string{
-		$paths = $this->collectModules(fn(int $x, int $y):string => $this->svgModule($x, $y));
-		$svg   = [];
-
-		// create the path elements
-		foreach($paths as $M_TYPE => $path){
-			$path = trim(implode(' ', $path));
-
-			if(empty($path)){
-				continue;
-			}
-
-			$cssClass = implode(' ', [
-				'qr-'.$M_TYPE,
-				($M_TYPE & QRMatrix::IS_DARK) === QRMatrix::IS_DARK ? 'dark' : 'light',
-				$this->options->cssClass,
-			]);
-
-			$format = empty($this->moduleValues[$M_TYPE])
-				? '<path class="%1$s" d="%2$s"/>'
-				: '<path class="%1$s" fill="%3$s" fill-opacity="%4$s" d="%2$s"/>';
-
-			$svg[] = sprintf($format, $cssClass, $path, $this->moduleValues[$M_TYPE], $this->options->svgOpacity);
-		}
-
-		return implode($this->options->eol, $svg);
-	}
-
-	/**
-	 * returns a path segment for a single module
-	 *
-	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d
-	 */
-	protected function svgModule(int $x, int $y):string{
-
-		if($this->options->imageTransparent && !$this->matrix->check($x, $y)){
-			return '';
-		}
-
-		if($this->options->drawCircularModules && $this->matrix->checkTypeNotIn($x, $y, $this->options->keepAsSquare)){
-			$r = $this->options->circleRadius;
-
-			return sprintf(
-				'M%1$s %2$s a%3$s %3$s 0 1 0 %4$s 0 a%3$s %3$s 0 1 0 -%4$s 0Z',
-				($x + 0.5 - $r),
-				($y + 0.5),
-				$r,
-				($r * 2)
-			);
-
-		}
-
-		return sprintf('M%1$s %2$s h%3$s v1 h-%4$sZ', $x, $y, 1, 1);
-	}
-
+	abstract protected function createMarkup(bool $saveToFile):string;
 }
 }

+ 53 - 0
src/Output/QRMarkupHTML.php

@@ -0,0 +1,53 @@
+<?php
+/**
+ * Class QRMarkupHTML
+ *
+ * @created      06.06.2022
+ * @author       smiley <smiley@chillerlan.net>
+ * @copyright    2022 smiley
+ * @license      MIT
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use function sprintf;
+
+/**
+ * HTML output
+ */
+class QRMarkupHTML extends QRMarkup{
+
+	/**
+	 * @inheritDoc
+	 */
+	protected function createMarkup(bool $saveToFile):string{
+		$html = empty($this->options->cssClass)
+			? '<div>'
+			: sprintf('<div class="%s">', $this->options->cssClass);
+
+		$html .= $this->options->eol;
+
+		foreach($this->matrix->matrix() as $row){
+			$html .= '<div>';
+
+			foreach($row as $M_TYPE){
+				$html .= sprintf('<span style="background: %s;"></span>', $this->moduleValues[$M_TYPE]);
+			}
+
+			$html .= '</div>'.$this->options->eol;
+		}
+
+		$html .= '</div>'.$this->options->eol;
+
+		// wrap the snippet into a body when saving to file
+		if($saveToFile){
+			$html = sprintf(
+				'<!DOCTYPE html><head><meta charset="UTF-8"><title>QR Code</title></head><body>%s</body>',
+				$this->options->eol.$html
+			);
+		}
+
+		return $html;
+	}
+
+}

+ 137 - 0
src/Output/QRMarkupSVG.php

@@ -0,0 +1,137 @@
+<?php
+/**
+ * Class QRMarkupSVG
+ *
+ * @created      06.06.2022
+ * @author       smiley <smiley@chillerlan.net>
+ * @copyright    2022 smiley
+ * @license      MIT
+ */
+
+namespace chillerlan\QRCode\Output;
+
+use chillerlan\QRCode\Data\QRMatrix;
+use function array_chunk, implode, sprintf;
+
+/**
+ * SVG output
+ *
+ * @see https://github.com/codemasher/php-qrcode/pull/5
+ * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/svg
+ * @see https://www.sarasoueidan.com/demos/interactive-svg-coordinate-system/
+ * @see http://apex.infogridpacific.com/SVG/svg-tutorial-contents.html
+ */
+class QRMarkupSVG extends QRMarkup{
+
+	/**
+	 * @inheritDoc
+	 */
+	protected function createMarkup(bool $saveToFile):string{
+		$svg = $this->header();
+
+		if(!empty($this->options->svgDefs)){
+			$svg .= sprintf('<defs>%1$s%2$s</defs>%2$s', $this->options->svgDefs, $this->options->eol);
+		}
+
+		$svg .= $this->paths();
+
+		// close svg
+		$svg .= sprintf('%1$s</svg>%1$s', $this->options->eol);
+
+		// transform to data URI only when not saving to file
+		if(!$saveToFile && $this->options->imageBase64){
+			$svg = $this->base64encode($svg, 'image/svg+xml');
+		}
+
+		return $svg;
+	}
+
+	/**
+	 * returns the <svg> header with the given options parsed
+	 */
+	protected function header():string{
+		$width  = $this->options->svgWidth !== null ? sprintf(' width="%s"', $this->options->svgWidth) : '';
+		$height = $this->options->svgHeight !== null ? sprintf(' height="%s"', $this->options->svgHeight) : '';
+
+		/** @noinspection HtmlUnknownAttribute */
+		return sprintf(
+			'<?xml version="1.0" encoding="UTF-8"?>%6$s'.
+			'<svg xmlns="http://www.w3.org/2000/svg" class="qr-svg %1$s" viewBox="0 0 %2$s %2$s" preserveAspectRatio="%3$s"%4$s%5$s>%6$s',
+			$this->options->cssClass,
+			$this->options->svgViewBoxSize ?? $this->moduleCount,
+			$this->options->svgPreserveAspectRatio,
+			$width,
+			$height,
+			$this->options->eol
+		);
+	}
+
+	/**
+	 * returns one or more SVG <path> elements
+	 *
+	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
+	 */
+	protected function paths():string{
+		$paths = $this->collectModules(fn(int $x, int $y):string => $this->module($x, $y));
+		$svg   = [];
+
+		// create the path elements
+		foreach($paths as $M_TYPE => $modules){
+			// limit the total line length
+			$chunks = array_chunk($modules, 100);
+			$chonks = [];
+
+			foreach($chunks as $chunk){
+				$chonks[] = implode(' ', $chunk);
+			}
+
+			$path = implode($this->options->eol, $chonks);
+
+			if(empty($path)){
+				continue;
+			}
+
+			$cssClass = implode(' ', [
+				'qr-'.$M_TYPE,
+				($M_TYPE & QRMatrix::IS_DARK) === QRMatrix::IS_DARK ? 'dark' : 'light',
+				$this->options->cssClass,
+			]);
+
+			$format = empty($this->moduleValues[$M_TYPE])
+				? '<path class="%1$s" d="%2$s"/>'
+				: '<path class="%1$s" fill="%3$s" fill-opacity="%4$s" d="%2$s"/>';
+
+			$svg[] = sprintf($format, $cssClass, $path, $this->moduleValues[$M_TYPE], $this->options->svgOpacity);
+		}
+
+		return implode($this->options->eol, $svg);
+	}
+
+	/**
+	 * returns a path segment for a single module
+	 *
+	 * @see https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d
+	 */
+	protected function module(int $x, int $y):string{
+
+		if($this->options->imageTransparent && !$this->matrix->check($x, $y)){
+			return '';
+		}
+
+		if($this->options->drawCircularModules && $this->matrix->checkTypeNotIn($x, $y, $this->options->keepAsSquare)){
+			$r = $this->options->circleRadius;
+
+			return sprintf(
+				'M%1$s %2$s a%3$s %3$s 0 1 0 %4$s 0 a%3$s %3$s 0 1 0 -%4$s 0Z',
+				($x + 0.5 - $r),
+				($y + 0.5),
+				$r,
+				($r * 2)
+			);
+
+		}
+
+		return sprintf('M%1$s %2$s h1 v1 h-1Z', $x, $y);
+	}
+
+}

+ 5 - 3
src/QRCode.php

@@ -13,7 +13,9 @@ namespace chillerlan\QRCode;
 use chillerlan\QRCode\Common\{EccLevel, ECICharset, MaskPattern, Mode};
 use chillerlan\QRCode\Common\{EccLevel, ECICharset, MaskPattern, Mode};
 use chillerlan\QRCode\Data\{AlphaNum, Byte, ECI, Kanji, Number, QRCodeDataException, QRData, QRDataModeInterface, QRMatrix};
 use chillerlan\QRCode\Data\{AlphaNum, Byte, ECI, Kanji, Number, QRCodeDataException, QRData, QRDataModeInterface, QRMatrix};
 use chillerlan\QRCode\Decoder\{Decoder, DecoderResult, LuminanceSourceInterface};
 use chillerlan\QRCode\Decoder\{Decoder, DecoderResult, LuminanceSourceInterface};
-use chillerlan\QRCode\Output\{QRCodeOutputException, QRFpdf, QRGdImage, QRImagick, QRMarkup, QREps, QROutputInterface, QRString};
+use chillerlan\QRCode\Output\{
+	QRCodeOutputException, QRFpdf, QRGdImage, QRImagick, QRMarkupHTML, QRMarkupSVG, QREps, QROutputInterface, QRString
+};
 use chillerlan\Settings\SettingsContainerInterface;
 use chillerlan\Settings\SettingsContainerInterface;
 use function class_exists, class_implements, in_array, mb_convert_encoding, mb_detect_encoding;
 use function class_exists, class_implements, in_array, mb_convert_encoding, mb_detect_encoding;
 
 
@@ -90,8 +92,8 @@ class QRCode{
 	 * @var string[]
 	 * @var string[]
 	 */
 	 */
 	public const OUTPUT_MODES = [
 	public const OUTPUT_MODES = [
-		self::OUTPUT_MARKUP_SVG  => QRMarkup::class,
-		self::OUTPUT_MARKUP_HTML => QRMarkup::class,
+		self::OUTPUT_MARKUP_SVG  => QRMarkupSVG::class,
+		self::OUTPUT_MARKUP_HTML => QRMarkupHTML::class,
 		self::OUTPUT_IMAGE_PNG   => QRGdImage::class,
 		self::OUTPUT_IMAGE_PNG   => QRGdImage::class,
 		self::OUTPUT_IMAGE_GIF   => QRGdImage::class,
 		self::OUTPUT_IMAGE_GIF   => QRGdImage::class,
 		self::OUTPUT_IMAGE_JPG   => QRGdImage::class,
 		self::OUTPUT_IMAGE_JPG   => QRGdImage::class,

+ 2 - 0
tests/Output/QRMarkupHTMLTest.php

@@ -10,6 +10,7 @@
 
 
 namespace chillerlan\QRCodeTest\Output;
 namespace chillerlan\QRCodeTest\Output;
 
 
+use chillerlan\QRCode\Output\QRMarkupHTML;
 use chillerlan\QRCode\QRCode;
 use chillerlan\QRCode\QRCode;
 
 
 /**
 /**
@@ -17,6 +18,7 @@ use chillerlan\QRCode\QRCode;
  */
  */
 final class QRMarkupHTMLTest extends QRMarkupTestAbstract{
 final class QRMarkupHTMLTest extends QRMarkupTestAbstract{
 
 
+	protected string $FQN  = QRMarkupHTML::class;
 	protected string $type = QRCode::OUTPUT_MARKUP_HTML;
 	protected string $type = QRCode::OUTPUT_MARKUP_HTML;
 
 
 }
 }

+ 2 - 0
tests/Output/QRMarkupSVGTest.php

@@ -10,6 +10,7 @@
 
 
 namespace chillerlan\QRCodeTest\Output;
 namespace chillerlan\QRCodeTest\Output;
 
 
+use chillerlan\QRCode\Output\QRMarkupSVG;
 use chillerlan\QRCode\QRCode;
 use chillerlan\QRCode\QRCode;
 
 
 /**
 /**
@@ -17,6 +18,7 @@ use chillerlan\QRCode\QRCode;
  */
  */
 final class QRMarkupSVGTest extends QRMarkupTestAbstract{
 final class QRMarkupSVGTest extends QRMarkupTestAbstract{
 
 
+	protected string $FQN  = QRMarkupSVG::class;
 	protected string $type = QRCode::OUTPUT_MARKUP_SVG;
 	protected string $type = QRCode::OUTPUT_MARKUP_SVG;
 
 
 }
 }

+ 0 - 3
tests/Output/QRMarkupTestAbstract.php

@@ -11,15 +11,12 @@
 namespace chillerlan\QRCodeTest\Output;
 namespace chillerlan\QRCodeTest\Output;
 
 
 use chillerlan\QRCode\Data\QRMatrix;
 use chillerlan\QRCode\Data\QRMatrix;
-use chillerlan\QRCode\Output\QRMarkup;
 
 
 /**
 /**
  * Tests the QRMarkup output module
  * Tests the QRMarkup output module
  */
  */
 abstract class QRMarkupTestAbstract extends QROutputTestAbstract{
 abstract class QRMarkupTestAbstract extends QROutputTestAbstract{
 
 
-	protected string $FQN  = QRMarkup::class;
-
 	/**
 	/**
 	 * @inheritDoc
 	 * @inheritDoc
 	 */
 	 */