<?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(); } }