added QRMarkup & cleaned up the output

This commit is contained in:
smiley
2016-12-17 22:22:20 +01:00
parent 8209f30fb4
commit 03d5d92fbe
9 changed files with 258 additions and 150 deletions
+14 -83
View File
@@ -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 xmlns="http://www.w3.org/2000/svg" version="1.1" width="' . $length . '" height="' . $length . '" viewBox="0 0 ' . $length . ' ' . $length . '" style="background-color:' . $background . '"><defs><style>.' . $class . '{fill:' . $foreground . '} rect{shape-rendering:crispEdges}</style></defs>';
// 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 '<rect x="' . ($from * $this->options->pixelSize + $this->options->marginSize) . '" y="' . ($r * $this->options->pixelSize + $this->options->marginSize) . '" width="' . ($this->options->pixelSize * $count) . '" height="' . $this->options->pixelSize . '" class="' . $class . '" />';
// reset count
$from = -1;
$count = 0;
}
}
// close off the row, if applicable
if($from >= 0){
echo '<rect x="' . ($from * $this->options->pixelSize + $this->options->marginSize) . '" y="' . ($r * $this->options->pixelSize + $this->options->marginSize) . '" width="' . ($this->options->pixelSize * $count) . '" height="' . $this->options->pixelSize . '" class="' . $class . '" />';
}
}
// close svg
echo '</svg>';
$imageData = ob_get_clean();
// if saving to file, append the correct headers
if($this->options->cachefile){
if(false === @file_put_contents($this->options->cachefile, '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' . "\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;
}
}
+4 -4
View File
@@ -1,5 +1,6 @@
<?php
/**
* Class QRImageOptions
*
* @filesource QRImageOptions.php
* @created 08.12.2015
@@ -10,19 +11,18 @@
*/
namespace chillerlan\QRCode\Output;
use chillerlan\QRCode\QRCode;
/**
* Class QRImageOptions
*
*/
class QRImageOptions{
class QRImageOptions extends QROutputOptionsAbstract{
public $type = QRCode::OUTPUT_IMAGE_PNG;
public $base64 = true;
public $cachefile = null;
public $pixelSize = 5;
public $marginSize = 5;
+133
View File
@@ -0,0 +1,133 @@
<?php
/**
* Class QRMarkup
*
* @filesource QRMarkup.php
* @created 17.12.2016
* @package chillerlan\QRCode\Output
* @author Smiley <smiley@chillerlan.net>
* @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.'></'.$tag.'>';
}
if(!(bool)$this->options->htmlOmitEndTag){
$html .= '</'.$this->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 = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="'.$length.'" height="'.$length.'" viewBox="0 0 '.$length.' '.$length.'" style="background-color:'.$this->options->bgColor.'">'.$this->options->eol.
'<defs><style>.'.$class.'{fill:'.$this->options->fgColor.'} rect{shape-rendering:crispEdges}</style></defs>'.$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 .= '<rect x="'.($from * $this->options->pixelSize + $this->options->marginSize).'" y="'.($r * $this->options->pixelSize + $this->options->marginSize)
.'" width="'.($this->options->pixelSize * $count).'" height="'.$this->options->pixelSize.'" class="'.$class.'" />'.$this->options->eol;
// reset count
$from = -1;
$count = 0;
}
}
// close off the row, if applicable
if($from >= 0){
$svg .= '<rect x="'.($from * $this->options->pixelSize + $this->options->marginSize).'" y="'.($r * $this->options->pixelSize + $this->options->marginSize)
.'" width="'.($this->options->pixelSize * $count).'" height="'.$this->options->pixelSize.'" class="'.$class.'" />'.$this->options->eol;
}
}
// close svg
$svg .= '</svg>'.$this->options->eol;
// if saving to file, append the correct headers
if($this->options->cachefile){
$svg = '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'.$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;
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
/**
* Class QRMarkupOptions
*
* @filesource QRMarkupOptions.php
* @created 08.12.2015
* @package chillerlan\QRCode\Output
* @author Smiley <smiley@chillerlan.net>
* @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 = '';
}
+27
View File
@@ -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
*
+26
View File
@@ -0,0 +1,26 @@
<?php
/**
* Class QROutputOptionsAbstract
*
* @filesource QROutputOptionsAbstract.php
* @created 17.12.2016
* @package chillerlan\QRCode\Output
* @author Smiley <smiley@chillerlan.net>
* @copyright 2016 Smiley
* @license MIT
*/
namespace chillerlan\QRCode\Output;
/**
*
*/
abstract class QROutputOptionsAbstract{
public $type;
public $eol = PHP_EOL;
public $cachefile = null;
}
+7 -51
View File
@@ -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.'></'.$tag.'>';
}
if(!(bool)$this->options->htmlOmitEndTag){
$html .= '</'.$this->options->htmlRowTag.'>';
}
$html .= $this->options->eol;
}
return $html;
}
}
+3 -8
View File
@@ -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;
}
+6 -4
View File
@@ -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%.