BaconQrCode
AbstractRenderer | Image renderer, supporting multiple backends. |
<?php
/**
* BaconQrCode
*
* @link http://github.com/Bacon/BaconQrCode For the canonical source repository
* @copyright 2013 Ben 'DASPRiD' Scholzen
* @license http://opensource.org/licenses/BSD-2-Clause Simplified BSD License
*/
namespace BaconQrCode\Renderer\Image;
use BaconQrCode\Encoder\QrCode;
use BaconQrCode\Renderer\Color;
use BaconQrCode\Renderer\Image\Decorator\DecoratorInterface;
use BaconQrCode\Exception;
/**
* Image renderer, supporting multiple backends.
*/
abstract class AbstractRenderer implements RendererInterface
{
/**
* Margin around the QR code, also known as quiet zone.
*
* @var integer
*/
protected $margin = 4;
/**
* Requested width of the rendered image.
*
* @var integer
*/
protected $width = 0;
/**
* Requested height of the rendered image.
*
* @var integer
*/
protected $height = 0;
/**
* Whether dimensions should be rounded down.
*
* @var boolean
*/
protected $roundDimensions = true;
/**
* Final width of the image.
*
* @var integer
*/
protected $finalWidth;
/**
* Final height of the image.
*
* @var integer
*/
protected $finalHeight;
/**
* Size of each individual block.
*
* @var integer
*/
protected $blockSize;
/**
* Background color.
*
* @var Color\ColorInterface
*/
protected $backgroundColor;
/**
* Whether dimensions should be rounded down
*
* @var boolean
*/
protected $floorToClosestDimension;
/**
* Foreground color.
*
* @var Color\ColorInterface
*/
protected $foregroundColor;
/**
* Decorators used on QR codes.
*
* @var array
*/
protected $decorators = array();
/**
* Sets the margin around the QR code.
*
* @param integer $margin
* @return AbstractRenderer
* @throws Exception\InvalidArgumentException
*/
public function setMargin($margin)
{
if ($margin < 0) {
throw new Exception\InvalidArgumentException('Margin must be equal to greater than 0');
}
$this->margin = (int) $margin;
return $this;
}
/**
* Gets the margin around the QR code.
*
* @return integer
*/
public function getMargin()
{
return $this->margin;
}
/**
* Sets the height around the renderd image.
*
* If the width is smaller than the matrix width plus padding, the renderer
* will automatically use that as the width instead of the specified one.
*
* @param integer $width
* @return AbstractRenderer
*/
public function setWidth($width)
{
$this->width = (int) $width;
return $this;
}
/**
* Gets the width of the rendered image.
*
* @return integer
*/
public function getWidth()
{
return $this->width;
}
/**
* Sets the height around the renderd image.
*
* If the height is smaller than the matrix height plus padding, the
* renderer will automatically use that as the height instead of the
* specified one.
*
* @param integer $height
* @return AbstractRenderer
*/
public function setHeight($height)
{
$this->height = (int) $height;
return $this;
}
/**
* Gets the height around the rendered image.
*
* @return integer
*/
public function getHeight()
{
return $this->height;
}
/**
* Sets whether dimensions should be rounded down.
*
* @param boolean $flag
* @return AbstractRenderer
*/
public function setRoundDimensions($flag)
{
$this->floorToClosestDimension = $flag;
return $this;
}
/**
* Gets whether dimensions should be rounded down.
*
* @return boolean
*/
public function shouldRoundDimensions()
{
return $this->floorToClosestDimension;
}
/**
* Sets background color.
*
* @param Color\ColorInterface $color
* @return AbstractRenderer
*/
public function setBackgroundColor(Color\ColorInterface $color)
{
$this->backgroundColor = $color;
return $this;
}
/**
* Gets background color.
*
* @return Color\ColorInterface
*/
public function getBackgroundColor()
{
if ($this->backgroundColor === null) {
$this->backgroundColor = new Color\Gray(100);
}
return $this->backgroundColor;
}
/**
* Sets foreground color.
*
* @param Color\ColorInterface $color
* @return AbstractRenderer
*/
public function setForegroundColor(Color\ColorInterface $color)
{
$this->foregroundColor = $color;
return $this;
}
/**
* Gets foreground color.
*
* @return Color\ColorInterface
*/
public function getForegroundColor()
{
if ($this->foregroundColor === null) {
$this->foregroundColor = new Color\Gray(0);
}
return $this->foregroundColor;
}
/**
* Adds a decorator to the renderer.
*
* @param DecoratorInterface $decorator
* @return AbstractRenderer
*/
public function addDecorator(DecoratorInterface $decorator)
{
$this->decorators[] = $decorator;
return $this;
}
/**
* render(): defined by RendererInterface.
*
* @see RendererInterface::render()
* @param QrCode $qrCode
* @return string
*/
public function render(QrCode $qrCode)
{
$input = $qrCode->getMatrix();
$inputWidth = $input->getWidth();
$inputHeight = $input->getHeight();
$qrWidth = $inputWidth + ($this->getMargin() << 1);
$qrHeight = $inputHeight + ($this->getMargin() << 1);
$outputWidth = max($this->getWidth(), $qrWidth);
$outputHeight = max($this->getHeight(), $qrHeight);
$multiple = (int) min($outputWidth / $qrWidth, $outputHeight / $qrHeight);
if ($this->shouldRoundDimensions()) {
$outputWidth -= $outputWidth % $multiple;
$outputHeight -= $outputHeight % $multiple;
}
// Padding includes both the quiet zone and the extra white pixels to
// accommodate the requested dimensions. For example, if input is 25x25
// the QR will be 33x33 including the quiet zone. If the requested size
// is 200x160, the multiple will be 4, for a QR of 132x132. These will
// handle all the padding from 100x100 (the actual QR) up to 200x160.
$leftPadding = (int) (($outputWidth - ($inputWidth * $multiple)) / 2);
$topPadding = (int) (($outputHeight - ($inputHeight * $multiple)) / 2);
// Store calculated parameters
$this->finalWidth = $outputWidth;
$this->finalHeight = $outputHeight;
$this->blockSize = $multiple;
$this->init();
$this->addColor('background', $this->getBackgroundColor());
$this->addColor('foreground', $this->getForegroundColor());
$this->drawBackground('background');
foreach ($this->decorators as $decorator) {
$decorator->preProcess(
$qrCode,
$this,
$outputWidth,
$outputHeight,
$leftPadding,
$topPadding,
$multiple
);
}
for ($inputY = 0, $outputY = $topPadding; $inputY < $inputHeight; $inputY++, $outputY += $multiple) {
for ($inputX = 0, $outputX = $leftPadding; $inputX < $inputWidth; $inputX++, $outputX += $multiple) {
if ($input->get($inputX, $inputY) === 1) {
$this->drawBlock($outputX, $outputY, 'foreground');
}
}
}
foreach ($this->decorators as $decorator) {
$decorator->postProcess(
$qrCode,
$this,
$outputWidth,
$outputHeight,
$leftPadding,
$topPadding,
$multiple
);
}
return $this->getByteStream();
}
}