From 03d5d92fbe74aa48ea5df6e9f03f5c606e21fab4 Mon Sep 17 00:00:00 2001 From: smiley Date: Sat, 17 Dec 2016 22:22:20 +0100 Subject: [PATCH] added QRMarkup & cleaned up the output --- src/Output/QRImage.php | 97 +++--------------- src/Output/QRImageOptions.php | 8 +- src/Output/QRMarkup.php | 133 +++++++++++++++++++++++++ src/Output/QRMarkupOptions.php | 38 +++++++ src/Output/QROutputAbstract.php | 27 +++++ src/Output/QROutputOptionsAbstract.php | 26 +++++ src/Output/QRString.php | 58 ++--------- src/Output/QRStringOptions.php | 11 +- src/QRCode.php | 10 +- 9 files changed, 258 insertions(+), 150 deletions(-) create mode 100644 src/Output/QRMarkup.php create mode 100644 src/Output/QRMarkupOptions.php create mode 100644 src/Output/QROutputOptionsAbstract.php diff --git a/src/Output/QRImage.php b/src/Output/QRImage.php index 0691d124e..9f639f1f5 100644 --- a/src/Output/QRImage.php +++ b/src/Output/QRImage.php @@ -20,37 +20,29 @@ use chillerlan\QRCode\QRCode; class QRImage extends QROutputAbstract{ /** - * @var \chillerlan\QRCode\Output\QRImageOptions $outputOptions + * @var string */ - protected $options; + protected $optionsInterface = QRImageOptions::class; /** - * @var \chillerlan\QRCode\Output\QRImageOptions $outputOptions + * @var array */ - public function __construct(QRImageOptions $outputOptions = null){ - $this->options = $outputOptions; - - if(!$this->options instanceof QRImageOptions){ - $this->options = new QRImageOptions; - } - - // clamp input (determine sane values!) - $this->options->pixelSize = max(1, min(25, (int)$this->options->pixelSize)); - $this->options->marginSize = max(0, min(25, (int)$this->options->marginSize)); - - foreach(['fgRed', 'fgGreen', 'fgBlue', 'bgRed', 'bgGreen', 'bgBlue'] as $val){ - $this->options->{$val} = max(0, min(255, (int)$this->options->{$val})); - } - - } + protected $types = [ + QRCode::OUTPUT_IMAGE_GIF, + QRCode::OUTPUT_IMAGE_JPG, + QRCode::OUTPUT_IMAGE_PNG, + ]; /** * @return string */ public function dump(){ - // svg doesn't need all this GD business - if($this->options->type === QRCode::OUTPUT_IMAGE_SVG){ - return $this->toSVG(); + // clamp input (@todo: determine sane values!) + $this->options->pixelSize = max(1, min(25, (int)$this->options->pixelSize)); + $this->options->marginSize = max(0, min(25, (int)$this->options->marginSize)); + + foreach(['fgRed', 'fgGreen', 'fgBlue', 'bgRed', 'bgGreen', 'bgBlue'] as $val){ + $this->options->{$val} = max(0, min(255, (int)$this->options->{$val})); } $length = $this->pixelCount * $this->options->pixelSize + $this->options->marginSize * 2; @@ -117,65 +109,4 @@ class QRImage extends QROutputAbstract{ return $imageData; } - /** - * @return string - */ - protected function toSVG(){ - $length = $this->pixelCount * $this->options->pixelSize + $this->options->marginSize * 2; - $class = 'f' . hash('crc32', microtime(true)); - $foreground = 'rgb(' . $this->options->fgRed . ',' . $this->options->fgGreen . ',' . $this->options->fgBlue . ')'; - $background = (bool)$this->options->transparent - ? 'transparent' - : 'rgb(' . $this->options->bgRed . ',' . $this->options->bgGreen . ',' . $this->options->bgBlue . ')'; - - ob_start(); - - // svg header - echo ''; - - // svg body - foreach($this->matrix AS $r=>$row){ - //we'll combine active blocks within a single row as a lightweight compression technique - $from = -1; - $count = 0; - - foreach($row AS $c=>$pixel){ - if($pixel){ - $count++; - if($from < 0) - $from = $c; - } - else if($from >= 0){ - echo ''; - - // reset count - $from = -1; - $count = 0; - } - } - - // close off the row, if applicable - if($from >= 0){ - echo ''; - } - } - - // close svg - echo ''; - $imageData = ob_get_clean(); - - // if saving to file, append the correct headers - if($this->options->cachefile){ - if(false === @file_put_contents($this->options->cachefile, '' . "\n" . $imageData)){ - throw new QRCodeOutputException('Could not write to cache file.'); - } - } - - if((bool)$this->options->base64){ - $imageData = 'data:image/svg+xml;base64,'.base64_encode($imageData); - } - - return $imageData; - } - } diff --git a/src/Output/QRImageOptions.php b/src/Output/QRImageOptions.php index 265a44ab0..fc86adbea 100644 --- a/src/Output/QRImageOptions.php +++ b/src/Output/QRImageOptions.php @@ -1,5 +1,6 @@ + * @copyright 2016 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\QRCode; + +/** + * + */ +class QRMarkup extends QROutputAbstract{ + + /** + * @var string + */ + protected $optionsInterface = QRMarkupOptions::class; + + /** + * @var array + */ + protected $types = [ + QRCode::OUTPUT_MARKUP_HTML, + QRCode::OUTPUT_MARKUP_SVG, + ]; + + /** + * @return string + */ + public function dump(){ + switch($this->options->type){ + case QRCode::OUTPUT_MARKUP_SVG : return $this->toSVG(); + case QRCode::OUTPUT_MARKUP_HTML: + default: + return $this->toHTML(); + } + } + + /** + * @return string + */ + protected function toHTML(){ + $html = ''; + + foreach($this->matrix as $row){ + // in order to not bloat the output too much, we use the shortest possible valid HTML tags + $html .= '<'.$this->options->htmlRowTag.'>'; + + foreach($row as $col){ + $tag = $col + ? 'b' // dark + : 'i'; // light + + $html .= '<'.$tag.'>'; + } + + if(!(bool)$this->options->htmlOmitEndTag){ + $html .= 'options->htmlRowTag.'>'; + } + + $html .= $this->options->eol; + } + + return $html; + } + + /** + * @return string + * @throws \chillerlan\QRCode\Output\QRCodeOutputException + */ + protected function toSVG(){ + $length = $this->pixelCount * $this->options->pixelSize + $this->options->marginSize * 2; + $class = !empty($this->options->cssClass) ? $this->options->cssClass : hash('crc32', microtime(true)); + + // svg header + $svg = ''.$this->options->eol. + ''.$this->options->eol; + + // svg body + foreach($this->matrix as $r => $row){ + //we'll combine active blocks within a single row as a lightweight compression technique + $from = -1; + $count = 0; + + foreach($row as $c => $pixel){ + if($pixel){ + $count++; + + if($from < 0){ + $from = $c; + } + } + elseif($from >= 0){ + $svg .= ''.$this->options->eol; + + // reset count + $from = -1; + $count = 0; + } + } + + // close off the row, if applicable + if($from >= 0){ + $svg .= ''.$this->options->eol; + } + } + + // close svg + $svg .= ''.$this->options->eol; + + // if saving to file, append the correct headers + if($this->options->cachefile){ + $svg = ''.$this->options->eol.$svg; + + if(@file_put_contents($this->options->cachefile, $svg) === false){ + throw new QRCodeOutputException('Could not write to cache file.'); // @codeCoverageIgnore + } + } + + return $svg; + } + +} diff --git a/src/Output/QRMarkupOptions.php b/src/Output/QRMarkupOptions.php new file mode 100644 index 000000000..31c4072c3 --- /dev/null +++ b/src/Output/QRMarkupOptions.php @@ -0,0 +1,38 @@ + + * @copyright 2015 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +use chillerlan\QRCode\QRCode; + +/** + * + */ +class QRMarkupOptions extends QROutputOptionsAbstract{ + + public $type = QRCode::OUTPUT_MARKUP_SVG; + + public $htmlRowTag = 'p'; + + public $htmlOmitEndTag = true; + + public $fgColor = '#000'; + + public $bgColor = '#fff'; + + public $pixelSize = 5; + + public $marginSize = 5; + + public $cssClass = ''; + +} diff --git a/src/Output/QROutputAbstract.php b/src/Output/QROutputAbstract.php index 2ffa34cbb..2ade9e5c3 100644 --- a/src/Output/QROutputAbstract.php +++ b/src/Output/QROutputAbstract.php @@ -17,6 +17,16 @@ namespace chillerlan\QRCode\Output; */ abstract class QROutputAbstract implements QROutputInterface{ + /** + * @var string + */ + protected $optionsInterface; + + /** + * @var array + */ + protected $types; + /** * @var array */ @@ -32,6 +42,23 @@ abstract class QROutputAbstract implements QROutputInterface{ */ protected $options; + /** + * @var \chillerlan\QRCode\Output\QROutputOptionsAbstract $outputOptions + * @throws \chillerlan\QRCode\Output\QRCodeOutputException + */ + public function __construct(QROutputOptionsAbstract $outputOptions = null){ + $this->options = $outputOptions; + + if($this->optionsInterface && !$this->options instanceof $this->optionsInterface){ + $this->options = new $this->optionsInterface; + } + + if(is_array($this->types) && !in_array($this->options->type, $this->types , true)){ + throw new QRCodeOutputException('Invalid output type!'); + } + + } + /** * @param array $matrix * diff --git a/src/Output/QROutputOptionsAbstract.php b/src/Output/QROutputOptionsAbstract.php new file mode 100644 index 000000000..88779db40 --- /dev/null +++ b/src/Output/QROutputOptionsAbstract.php @@ -0,0 +1,26 @@ + + * @copyright 2016 Smiley + * @license MIT + */ + +namespace chillerlan\QRCode\Output; + +/** + * + */ +abstract class QROutputOptionsAbstract{ + + public $type; + + public $eol = PHP_EOL; + + public $cachefile = null; + +} diff --git a/src/Output/QRString.php b/src/Output/QRString.php index 0509a826e..3b274f3c0 100644 --- a/src/Output/QRString.php +++ b/src/Output/QRString.php @@ -19,27 +19,12 @@ use chillerlan\QRCode\QRCode; */ class QRString extends QROutputAbstract{ - /** - * @var \chillerlan\QRCode\Output\QRStringOptions - */ - protected $options; + protected $optionsInterface = QRStringOptions::class; - /** - * @var \chillerlan\QRCode\Output\QRStringOptions $outputOptions - * @throws \chillerlan\QRCode\Output\QRCodeOutputException - */ - public function __construct(QRStringOptions $outputOptions = null){ - $this->options = $outputOptions; - - if(!$this->options instanceof QRStringOptions){ - $this->options = new QRStringOptions; - } - - if(!in_array($this->options->type, [QRCode::OUTPUT_STRING_TEXT, QRCode::OUTPUT_STRING_JSON, QRCode::OUTPUT_STRING_HTML], true)){ - throw new QRCodeOutputException('Invalid string output type!'); - } - - } + protected $types = [ + QRCode::OUTPUT_STRING_TEXT, + QRCode::OUTPUT_STRING_JSON, + ]; /** * @return string @@ -47,11 +32,10 @@ class QRString extends QROutputAbstract{ public function dump(){ switch($this->options->type){ - case QRCode::OUTPUT_STRING_JSON: return json_encode($this->matrix); case QRCode::OUTPUT_STRING_TEXT: return $this->toString(); - case QRCode::OUTPUT_STRING_HTML: + case QRCode::OUTPUT_STRING_JSON: default: - return $this->toHTML(); + return json_encode($this->matrix); } } @@ -75,32 +59,4 @@ class QRString extends QROutputAbstract{ return $str; } - /** - * @return string - */ - protected function toHTML(){ - $html = ''; - - foreach($this->matrix as $row){ - // in order to not bloat the output too much, we use the shortest possible valid HTML tags - $html .= '<'.$this->options->htmlRowTag.'>'; - - foreach($row as $col){ - $tag = $col - ? 'b' // dark - : 'i'; // light - - $html .= '<'.$tag.'>'; - } - - if(!(bool)$this->options->htmlOmitEndTag){ - $html .= 'options->htmlRowTag.'>'; - } - - $html .= $this->options->eol; - } - - return $html; - } - } diff --git a/src/Output/QRStringOptions.php b/src/Output/QRStringOptions.php index 4038bdbd3..6f428786a 100644 --- a/src/Output/QRStringOptions.php +++ b/src/Output/QRStringOptions.php @@ -11,23 +11,18 @@ */ namespace chillerlan\QRCode\Output; + use chillerlan\QRCode\QRCode; /** * */ -class QRStringOptions{ +class QRStringOptions extends QROutputOptionsAbstract{ - public $type = QRCode::OUTPUT_STRING_HTML; + public $type = QRCode::OUTPUT_STRING_JSON; public $textDark = '#'; public $textLight = ' '; - public $eol = PHP_EOL; - - public $htmlRowTag = 'p'; - - public $htmlOmitEndTag = true; - } diff --git a/src/QRCode.php b/src/QRCode.php index 8ca3cfb69..0a7aa9a0d 100644 --- a/src/QRCode.php +++ b/src/QRCode.php @@ -27,14 +27,16 @@ class QRCode{ /** * API constants */ - const OUTPUT_STRING_TEXT = 0; - const OUTPUT_STRING_JSON = 1; - const OUTPUT_STRING_HTML = 2; + const OUTPUT_STRING_TEXT = 'txt'; + const OUTPUT_STRING_JSON = 'json'; + + const OUTPUT_MARKUP_HTML = 'html'; + const OUTPUT_MARKUP_SVG = 'svg'; +# const OUTPUT_MARKUP_XML = 'xml'; // anyone? const OUTPUT_IMAGE_PNG = 'png'; const OUTPUT_IMAGE_JPG = 'jpg'; const OUTPUT_IMAGE_GIF = 'gif'; - const OUTPUT_IMAGE_SVG = 'svg'; const ERROR_CORRECT_LEVEL_L = 1; // 7%. const ERROR_CORRECT_LEVEL_M = 0; // 15%.