LICENSE 0000666 00000002434 13436753160 0005570 0 ustar 00 Copyright (c) 2013, Ben 'DASPRiD' Scholzen All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. README.md 0000666 00000000761 13436753160 0006043 0 ustar 00 QR Code generator ================= Master: [![Build Status](https://api.travis-ci.org/Bacon/BaconQrCode.png?branch=master)](http://travis-ci.org/Bacon/BaconQrCode) Introduction ------------ BaconQrCode is a port of QR code portion of the ZXing library. It currently only features the encoder part, but could later receive the decoder part as well. As the Reed Solomon codec implementation of the ZXing library performs quite slow in PHP, it was exchanged with the implementation by Phil Karn. autoload_register.php 0000666 00000000111 13436753160 0010776 0 ustar 00 array( __DIR__ . '/autoload_classmap.php', ), 'Zend\Loader\StandardAutoloader' => array( 'namespaces' => array( __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__, ), ), ); } } autoload_classmap.php 0000666 00000011317 13436753160 0010767 0 ustar 00 __DIR__ . '/src/BaconQrCode/Common/AbstractEnum.php', 'BaconQrCode\Common\BitArray' => __DIR__ . '/src/BaconQrCode/Common/BitArray.php', 'BaconQrCode\Common\BitMatrix' => __DIR__ . '/src/BaconQrCode/Common/BitMatrix.php', 'BaconQrCode\Common\BitUtils' => __DIR__ . '/src/BaconQrCode/Common/BitUtils.php', 'BaconQrCode\Common\CharacterSetEci' => __DIR__ . '/src/BaconQrCode/Common/CharacterSetEci.php', 'BaconQrCode\Common\EcBlock' => __DIR__ . '/src/BaconQrCode/Common/EcBlock.php', 'BaconQrCode\Common\EcBlocks' => __DIR__ . '/src/BaconQrCode/Common/EcBlocks.php', 'BaconQrCode\Common\ErrorCorrectionLevel' => __DIR__ . '/src/BaconQrCode/Common/ErrorCorrectionLevel.php', 'BaconQrCode\Common\FormatInformation' => __DIR__ . '/src/BaconQrCode/Common/FormatInformation.php', 'BaconQrCode\Common\Mode' => __DIR__ . '/src/BaconQrCode/Common/Mode.php', 'BaconQrCode\Common\ReedSolomonCodec' => __DIR__ . '/src/BaconQrCode/Common/ReedSolomonCodec.php', 'BaconQrCode\Common\Version' => __DIR__ . '/src/BaconQrCode/Common/Version.php', 'BaconQrCode\Encoder\BlockPair' => __DIR__ . '/src/BaconQrCode/Encoder/BlockPair.php', 'BaconQrCode\Encoder\ByteMatrix' => __DIR__ . '/src/BaconQrCode/Encoder/ByteMatrix.php', 'BaconQrCode\Encoder\Encoder' => __DIR__ . '/src/BaconQrCode/Encoder/Encoder.php', 'BaconQrCode\Encoder\MaskUtil' => __DIR__ . '/src/BaconQrCode/Encoder/MaskUtil.php', 'BaconQrCode\Encoder\MatrixUtil' => __DIR__ . '/src/BaconQrCode/Encoder/MatrixUtil.php', 'BaconQrCode\Encoder\QrCode' => __DIR__ . '/src/BaconQrCode/Encoder/QrCode.php', 'BaconQrCode\Exception\ExceptionInterface' => __DIR__ . '/src/BaconQrCode/Exception/ExceptionInterface.php', 'BaconQrCode\Exception\InvalidArgumentException' => __DIR__ . '/src/BaconQrCode/Exception/InvalidArgumentException.php', 'BaconQrCode\Exception\OutOfBoundsException' => __DIR__ . '/src/BaconQrCode/Exception/OutOfBoundsException.php', 'BaconQrCode\Exception\RuntimeException' => __DIR__ . '/src/BaconQrCode/Exception/RuntimeException.php', 'BaconQrCode\Exception\UnexpectedValueException' => __DIR__ . '/src/BaconQrCode/Exception/UnexpectedValueException.php', 'BaconQrCode\Exception\WriterException' => __DIR__ . '/src/BaconQrCode/Exception/WriterException.php', 'BaconQrCode\Renderer\Color\Cmyk' => __DIR__ . '/src/BaconQrCode/Renderer/Color/Cmyk.php', 'BaconQrCode\Renderer\Color\ColorInterface' => __DIR__ . '/src/BaconQrCode/Renderer/Color/ColorInterface.php', 'BaconQrCode\Renderer\Color\Gray' => __DIR__ . '/src/BaconQrCode/Renderer/Color/Gray.php', 'BaconQrCode\Renderer\Color\Rgb' => __DIR__ . '/src/BaconQrCode/Renderer/Color/Rgb.php', 'BaconQrCode\Renderer\Image\AbstractRenderer' => __DIR__ . '/src/BaconQrCode/Renderer/Image/AbstractRenderer.php', 'BaconQrCode\Renderer\Image\Decorator\DecoratorInterface' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Decorator/DecoratorInterface.php', 'BaconQrCode\Renderer\Image\Decorator\FinderPattern' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Decorator/FinderPattern.php', 'BaconQrCode\Renderer\Image\Eps' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Eps.php', 'BaconQrCode\Renderer\Image\Png' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Png.php', 'BaconQrCode\Renderer\Image\RendererInterface' => __DIR__ . '/src/BaconQrCode/Renderer/Image/RendererInterface.php', 'BaconQrCode\Renderer\Image\Svg' => __DIR__ . '/src/BaconQrCode/Renderer/Image/Svg.php', 'BaconQrCode\Renderer\RendererInterface' => __DIR__ . '/src/BaconQrCode/Renderer/RendererInterface.php', 'BaconQrCode\Renderer\Text\Plain' => __DIR__ . '/src/BaconQrCode/Renderer/Text/Plain.php', 'BaconQrCode\Renderer\Text\Html' => __DIR__ . '/src/BaconQrCode/Renderer/Text/Html.php', 'BaconQrCode\Writer' => __DIR__ . '/src/BaconQrCode/Writer.php', ); src/BaconQrCode/Renderer/Image/Decorator/DecoratorInterface.php 0000666 00000003125 13436753160 0020456 0 ustar 00 outerColor = $color; return $this; } /** * Gets outer color. * * @return Color\ColorInterface */ public function getOuterColor() { if ($this->outerColor === null) { $this->outerColor = new Color\Gray(100); } return $this->outerColor; } /** * Sets inner color. * * @param Color\ColorInterface $color * @return FinderPattern */ public function setInnerColor(Color\ColorInterface $color) { $this->innerColor = $color; return $this; } /** * Gets inner color. * * @return Color\ColorInterface */ public function getInnerColor() { if ($this->innerColor === null) { $this->innerColor = new Color\Gray(0); } return $this->innerColor; } /** * preProcess(): defined by DecoratorInterface. * * @see DecoratorInterface::preProcess() * @param QrCode $qrCode * @param RendererInterface $renderer * @param integer $outputWidth * @param integer $outputHeight * @param integer $leftPadding * @param integer $topPadding * @param integer $multiple * @return void */ public function preProcess( QrCode $qrCode, RendererInterface $renderer, $outputWidth, $outputHeight, $leftPadding, $topPadding, $multiple ) { $matrix = $qrCode->getMatrix(); $positions = array( array(0, 0), array($matrix->getWidth() - 7, 0), array(0, $matrix->getHeight() - 7), ); foreach (self::$outerPositionDetectionPattern as $y => $row) { foreach ($row as $x => $isSet) { foreach ($positions as $position) { $matrix->set($x + $position[0], $y + $position[1], 0); } } } } /** * postProcess(): defined by DecoratorInterface. * * @see DecoratorInterface::postProcess() * * @param QrCode $qrCode * @param RendererInterface $renderer * @param integer $outputWidth * @param integer $outputHeight * @param integer $leftPadding * @param integer $topPadding * @param integer $multiple * @return void */ public function postProcess( QrCode $qrCode, RendererInterface $renderer, $outputWidth, $outputHeight, $leftPadding, $topPadding, $multiple ) { $matrix = $qrCode->getMatrix(); $positions = array( array(0, 0), array($matrix->getWidth() - 7, 0), array(0, $matrix->getHeight() - 7), ); $renderer->addColor('finder-outer', $this->getOuterColor()); $renderer->addColor('finder-inner', $this->getInnerColor()); foreach (self::$outerPositionDetectionPattern as $y => $row) { foreach ($row as $x => $isOuterSet) { $isInnerSet = self::$innerPositionDetectionPattern[$y][$x]; if ($isOuterSet) { foreach ($positions as $position) { $renderer->drawBlock( $leftPadding + $x * $multiple + $position[0] * $multiple, $topPadding + $y * $multiple + $position[1] * $multiple, 'finder-outer' ); } } if ($isInnerSet) { foreach ($positions as $position) { $renderer->drawBlock( $leftPadding + $x * $multiple + $position[0] * $multiple, $topPadding + $y * $multiple + $position[1] * $multiple, 'finder-inner' ); } } } } } } src/BaconQrCode/Renderer/Image/Eps.php 0000666 00000007354 13436753160 0013530 0 ustar 00 eps = "%!PS-Adobe-3.0 EPSF-3.0\n" . "%%BoundingBox: 0 0 " . $this->finalWidth . " " . $this->finalHeight . "\n" . "/F { rectfill } def\n"; } /** * addColor(): defined by RendererInterface. * * @see ImageRendererInterface::addColor() * @param string $id * @param ColorInterface $color * @return void */ public function addColor($id, ColorInterface $color) { if ( !$color instanceof Rgb && !$color instanceof Cmyk && !$color instanceof Gray ) { $color = $color->toCmyk(); } $this->colors[$id] = $color; } /** * drawBackground(): defined by RendererInterface. * * @see ImageRendererInterface::drawBackground() * @param string $colorId * @return void */ public function drawBackground($colorId) { $this->setColor($colorId); $this->eps .= "0 0 " . $this->finalWidth . " " . $this->finalHeight . " F\n"; } /** * drawBlock(): defined by RendererInterface. * * @see ImageRendererInterface::drawBlock() * @param integer $x * @param integer $y * @param string $colorId * @return void */ public function drawBlock($x, $y, $colorId) { $this->setColor($colorId); $this->eps .= $x . " " . ($this->finalHeight - $y) . " " . $this->blockSize . " " . $this->blockSize . " F\n"; } /** * getByteStream(): defined by RendererInterface. * * @see ImageRendererInterface::getByteStream() * @return string */ public function getByteStream() { return $this->eps; } /** * Sets color to use. * * @param string $colorId * @return void */ protected function setColor($colorId) { if ($colorId !== $this->currentColor) { $color = $this->colors[$colorId]; if ($color instanceof Rgb) { $this->eps .= sprintf( "%F %F %F setrgbcolor\n", $color->getRed() / 100, $color->getGreen() / 100, $color->getBlue() / 100 ); } elseif ($color instanceof Cmyk) { $this->eps .= sprintf( "%F %F %F %F setcmykcolor\n", $color->getCyan() / 100, $color->getMagenta() / 100, $color->getYellow() / 100, $color->getBlack() / 100 ); } elseif ($color instanceof Gray) { $this->eps .= sprintf( "%F setgray\n", $color->getGray() / 100 ); } $this->currentColor = $colorId; } } } src/BaconQrCode/Renderer/Image/Svg.php 0000666 00000007325 13436753160 0013536 0 ustar 00 svg = new SimpleXMLElement( '' . '' ); $this->svg->addAttribute('version', '1.1'); $this->svg->addAttribute('width', $this->finalWidth . 'px'); $this->svg->addAttribute('height', $this->finalHeight . 'px'); $this->svg->addChild('defs'); } /** * addColor(): defined by RendererInterface. * * @see ImageRendererInterface::addColor() * @param string $id * @param ColorInterface $color * @return void * @throws Exception\InvalidArgumentException */ public function addColor($id, ColorInterface $color) { $this->colors[$id] = (string) $color->toRgb(); } /** * drawBackground(): defined by RendererInterface. * * @see ImageRendererInterface::drawBackground() * @param string $colorId * @return void */ public function drawBackground($colorId) { $rect = $this->svg->addChild('rect'); $rect->addAttribute('x', 0); $rect->addAttribute('y', 0); $rect->addAttribute('width', $this->finalWidth); $rect->addAttribute('height', $this->finalHeight); $rect->addAttribute('fill', '#' . $this->colors[$colorId]); } /** * drawBlock(): defined by RendererInterface. * * @see ImageRendererInterface::drawBlock() * @param integer $x * @param integer $y * @param string $colorId * @return void */ public function drawBlock($x, $y, $colorId) { $use = $this->svg->addChild('use'); $use->addAttribute('x', $x); $use->addAttribute('y', $y); $use->addAttribute( 'xlink:href', $this->getRectPrototypeId($colorId), 'http://www.w3.org/1999/xlink' ); } /** * getByteStream(): defined by RendererInterface. * * @see ImageRendererInterface::getByteStream() * @return string */ public function getByteStream() { return $this->svg->asXML(); } /** * Get the prototype ID for a color. * * @param integer $colorId * @return string */ protected function getRectPrototypeId($colorId) { if (!isset($this->prototypeIds[$colorId])) { $id = 'r' . dechex(count($this->prototypeIds)); $rect = $this->svg->defs->addChild('rect'); $rect->addAttribute('id', $id); $rect->addAttribute('width', $this->blockSize); $rect->addAttribute('height', $this->blockSize); $rect->addAttribute('fill', '#' . $this->colors[$colorId]); $this->prototypeIds[$colorId] = '#' . $id; } return $this->prototypeIds[$colorId]; } } src/BaconQrCode/Renderer/Image/AbstractRenderer.php 0000666 00000020227 13436753160 0016225 0 ustar 00 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(); } } src/BaconQrCode/Renderer/Image/RendererInterface.php 0000666 00000002463 13436753160 0016364 0 ustar 00 image = imagecreatetruecolor($this->finalWidth, $this->finalHeight); } /** * addColor(): defined by RendererInterface. * * @see ImageRendererInterface::addColor() * @param string $id * @param ColorInterface $color * @return void * @throws Exception\RuntimeException */ public function addColor($id, ColorInterface $color) { if ($this->image === null) { throw new Exception\RuntimeException('Colors can only be added after init'); } $color = $color->toRgb(); $this->colors[$id] = imagecolorallocate( $this->image, $color->getRed(), $color->getGreen(), $color->getBlue() ); } /** * drawBackground(): defined by RendererInterface. * * @see ImageRendererInterface::drawBackground() * @param string $colorId * @return void */ public function drawBackground($colorId) { imagefill($this->image, 0, 0, $this->colors[$colorId]); } /** * drawBlock(): defined by RendererInterface. * * @see ImageRendererInterface::drawBlock() * @param integer $x * @param integer $y * @param string $colorId * @return void */ public function drawBlock($x, $y, $colorId) { imagefilledrectangle( $this->image, $x, $y, $x + $this->blockSize - 1, $y + $this->blockSize - 1, $this->colors[$colorId] ); } /** * getByteStream(): defined by RendererInterface. * * @see ImageRendererInterface::getByteStream() * @return string */ public function getByteStream() { ob_start(); imagepng($this->image); return ob_get_clean(); } } src/BaconQrCode/Renderer/Color/ColorInterface.php 0000666 00000001224 13436753160 0015722 0 ustar 00 255) { throw new Exception\InvalidArgumentException('Red must be between 0 and 255'); } if ($green < 0 || $green > 255) { throw new Exception\InvalidArgumentException('Green must be between 0 and 255'); } if ($blue < 0 || $blue > 255) { throw new Exception\InvalidArgumentException('Blue must be between 0 and 255'); } $this->red = (int) $red; $this->green = (int) $green; $this->blue = (int) $blue; } /** * Returns the red value. * * @return integer */ public function getRed() { return $this->red; } /** * Returns the green value. * * @return integer */ public function getGreen() { return $this->green; } /** * Returns the blue value. * * @return integer */ public function getBlue() { return $this->blue; } /** * Returns a hexadecimal string representation of the RGB value. * * @return string */ public function __toString() { return sprintf('%02x%02x%02x', $this->red, $this->green, $this->blue); } /** * toRgb(): defined by ColorInterface. * * @see ColorInterface::toRgb() * @return Rgb */ public function toRgb() { return $this; } /** * toCmyk(): defined by ColorInterface. * * @see ColorInterface::toCmyk() * @return Cmyk */ public function toCmyk() { $c = 1 - ($this->red / 255); $m = 1 - ($this->green / 255); $y = 1 - ($this->blue / 255); $k = min($c, $m, $y); return new Cmyk( 100 * ($c - $k) / (1 - $k), 100 * ($m - $k) / (1 - $k), 100 * ($y - $k) / (1 - $k), 100 * $k ); } /** * toGray(): defined by ColorInterface. * * @see ColorInterface::toGray() * @return Gray */ public function toGray() { return new Gray(($this->red * 0.21 + $this->green * 0.71 + $this->blue * 0.07) / 2.55); } } src/BaconQrCode/Renderer/Color/Cmyk.php 0000666 00000006270 13436753160 0013734 0 ustar 00 100) { throw new Exception\InvalidArgumentException('Cyan must be between 0 and 100'); } if ($magenta < 0 || $magenta > 100) { throw new Exception\InvalidArgumentException('Magenta must be between 0 and 100'); } if ($yellow < 0 || $yellow > 100) { throw new Exception\InvalidArgumentException('Yellow must be between 0 and 100'); } if ($black < 0 || $black > 100) { throw new Exception\InvalidArgumentException('Black must be between 0 and 100'); } $this->cyan = (int) $cyan; $this->magenta = (int) $magenta; $this->yellow = (int) $yellow; $this->black = (int) $black; } /** * Returns the cyan value. * * @return integer */ public function getCyan() { return $this->cyan; } /** * Returns the magenta value. * * @return integer */ public function getMagenta() { return $this->magenta; } /** * Returns the yellow value. * * @return integer */ public function getYellow() { return $this->yellow; } /** * Returns the black value. * * @return integer */ public function getBlack() { return $this->black; } /** * toRgb(): defined by ColorInterface. * * @see ColorInterface::toRgb() * @return Rgb */ public function toRgb() { $k = $this->black / 100; $c = (-$k * $this->cyan + $k * 100 + $this->cyan) / 100; $m = (-$k * $this->magenta + $k * 100 + $this->magenta) / 100; $y = (-$k * $this->yellow + $k * 100 + $this->yellow) / 100; return new Rgb( -$c * 255 + 255, -$m * 255 + 255, -$y * 255 + 255 ); } /** * toCmyk(): defined by ColorInterface. * * @see ColorInterface::toCmyk() * @return Cmyk */ public function toCmyk() { return $this; } /** * toGray(): defined by ColorInterface. * * @see ColorInterface::toGray() * @return Gray */ public function toGray() { return $this->toRgb()->toGray(); } } src/BaconQrCode/Renderer/Color/Gray.php 0000666 00000003167 13436753160 0013735 0 ustar 00 100) { throw new Exception\InvalidArgumentException('Gray must be between 0 and 100'); } $this->gray = (int) $gray; } /** * Returns the gray value. * * @return integer */ public function getGray() { return $this->gray; } /** * toRgb(): defined by ColorInterface. * * @see ColorInterface::toRgb() * @return Rgb */ public function toRgb() { return new Rgb($this->gray * 2.55, $this->gray * 2.55, $this->gray * 2.55); } /** * toCmyk(): defined by ColorInterface. * * @see ColorInterface::toCmyk() * @return Cmyk */ public function toCmyk() { return new Cmyk(0, 0, 0, 100 - $this->gray); } /** * toGray(): defined by ColorInterface. * * @see ColorInterface::toGray() * @return Gray */ public function toGray() { return $this; } } src/BaconQrCode/Renderer/Text/Html.php 0000666 00000003431 13436753160 0013577 0 ustar 00 class = $class; } /** * Get CSS class name. * * @return string */ public function getClass() { return $this->class; } /** * Set CSS style value. * * @param string $style */ public function setStyle($style) { $this->style = $style; } /** * Get CSS style value. * * @return string */ public function getStyle() { return $this->style; } /** * render(): defined by RendererInterface. * * @see RendererInterface::render() * @param QrCode $qrCode * @return string */ public function render(QrCode $qrCode) { $textCode = parent::render($qrCode); $result = '
' . $textCode . ''; return $result; } } src/BaconQrCode/Renderer/Text/Plain.php 0000666 00000006566 13436753160 0013752 0 ustar 00 fullBlock = $fullBlock; } /** * Get char used as full block (occupied space, "black"). * * @return string */ public function getFullBlock() { return $this->fullBlock; } /** * Set char used as empty block (empty space, "white"). * * @param string $emptyBlock */ public function setEmptyBlock($emptyBlock) { $this->emptyBlock = $emptyBlock; } /** * Get char used as empty block (empty space, "white"). * * @return string */ public function getEmptyBlock() { return $this->emptyBlock; } /** * 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; } /** * render(): defined by RendererInterface. * * @see RendererInterface::render() * @param QrCode $qrCode * @return string */ public function render(QrCode $qrCode) { $result = ''; $matrix = $qrCode->getMatrix(); $width = $matrix->getWidth(); // Top margin for ($x = 0; $x < $this->margin; $x++) { $result .= str_repeat($this->emptyBlock, $width + 2 * $this->margin)."\n"; } // Body $array = $matrix->getArray(); foreach ($array as $row) { $result .= str_repeat($this->emptyBlock, $this->margin); // left margin foreach ($row as $byte) { $result .= $byte ? $this->fullBlock : $this->emptyBlock; } $result .= str_repeat($this->emptyBlock, $this->margin); // right margin $result .= "\n"; } // Bottom margin for ($x = 0; $x < $this->margin; $x++) { $result .= str_repeat($this->emptyBlock, $width + 2 * $this->margin)."\n"; } return $result; } } src/BaconQrCode/Renderer/RendererInterface.php 0000666 00000001004 13436753160 0015330 0 ustar 00 ecLevel = new ErrorCorrectionLevel(($formatInfo >> 3) & 0x3); $this->dataMask = $formatInfo & 0x7; } /** * Checks how many bits are different between two integers. * * @param integer $a * @param integer $b * @return integer */ public static function numBitsDiffering($a, $b) { $a ^= $b; return ( self::$bitsSetInHalfByte[$a & 0xf] + self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 4) & 0xf)] + self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 8) & 0xf)] + self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 12) & 0xf)] + self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 16) & 0xf)] + self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 20) & 0xf)] + self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 24) & 0xf)] + self::$bitsSetInHalfByte[(BitUtils::unsignedRightShift($a, 28) & 0xf)] ); } /** * Decodes format information. * * @param integer $maskedFormatInfo1 * @param integer $maskedFormatInfo2 * @return FormatInformation|null */ public static function decodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) { $formatInfo = self::doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2); if ($formatInfo !== null) { return $formatInfo; } // Should return null, but, some QR codes apparently do not mask this // info. Try again by actually masking the pattern first. return self::doDecodeFormatInformation( $maskedFormatInfo1 ^ self::FORMAT_INFO_MASK_QR, $maskedFormatInfo2 ^ self::FORMAT_INFO_MASK_QR ); } /** * Internal method for decoding format information. * * @param integer $maskedFormatInfo1 * @param integer $maskedFormatInfo2 * @return FormatInformation|null */ protected static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) { $bestDifference = PHP_INT_MAX; $bestFormatInfo = 0; foreach (self::$formatInfoDecodeLookup as $decodeInfo) { $targetInfo = $decodeInfo[0]; if ($targetInfo === $maskedFormatInfo1 || $targetInfo === $maskedFormatInfo2) { // Found an exact match return new self($decodeInfo[1]); } $bitsDifference = self::numBitsDiffering($maskedFormatInfo1, $targetInfo); if ($bitsDifference < $bestDifference) { $bestFormatInfo = $decodeInfo[1]; $bestDifference = $bitsDifference; } if ($maskedFormatInfo1 !== $maskedFormatInfo2) { // Also try the other option $bitsDifference = self::numBitsDiffering($maskedFormatInfo2, $targetInfo); if ($bitsDifference < $bestDifference) { $bestFormatInfo = $decodeInfo[1]; $bestDifference = $bitsDifference; } } } // Hamming distance of the 32 masked codes is 7, by construction, so // <= 3 bits differing means we found a match. if ($bestDifference <= 3) { return new self($bestFormatInfo); } return null; } /** * Gets the error correction level. * * @return ErrorCorrectionLevel */ public function getErrorCorrectionLevel() { return $this->ecLevel; } /** * Gets the data mask. * * @return integer */ public function getDataMask() { return $this->dataMask; } /** * Hashes the code of the EC level. * * @return integer */ public function hashCode() { return ($this->ecLevel->get() << 3) | $this->dataMask; } /** * Verifies if this instance equals another one. * * @param mixed $other * @return boolean */ public function equals($other) { if (!$other instanceof self) { return false; } return ( $this->ecLevel->get() === $other->getErrorCorrectionLevel()->get() && $this->dataMask === $other->getDataMask() ); } } src/BaconQrCode/Common/AbstractEnum.php 0000666 00000004423 13436753160 0014023 0 ustar 00 strict = $strict; $this->change($initialValue); } /** * Changes the value of the enum. * * @param mixed $value * @return void */ public function change($value) { if (!in_array($value, $this->getConstList(), $this->strict)) { throw new Exception\UnexpectedValueException('Value not a const in enum ' . get_class($this)); } $this->value = $value; } /** * Gets current value. * * @return mixed */ public function get() { return $this->value; } /** * Gets all constants (possible values) as an array. * * @param boolean $includeDefault * @return array */ public function getConstList($includeDefault = true) { if ($this->constants === null) { $reflection = new ReflectionClass($this); $this->constants = $reflection->getConstants(); } if ($includeDefault) { return $this->constants; } $constants = $this->constants; unset($constants['__default']); return $constants; } /** * Gets the name of the enum. * * @return string */ public function __toString() { return array_search($this->value, $this->getConstList()); } } src/BaconQrCode/Common/EcBlock.php 0000666 00000002546 13436753160 0012741 0 ustar 00 count = $count; $this->dataCodewords = $dataCodewords; } /** * Returns how many times the block is used. * * @return integer */ public function getCount() { return $this->count; } /** * Returns the number of data codewords. * * @return integer */ public function getDataCodewords() { return $this->dataCodewords; } } src/BaconQrCode/Common/BitUtils.php 0000666 00000002226 13436753160 0013171 0 ustar 00 >>" in other * languages. * * @param integer $a * @param integer $b * @return integer */ public static function unsignedRightShift($a, $b) { return ( $a >= 0 ? $a >> $b : (($a & 0x7fffffff) >> $b) | (0x40000000 >> ($b - 1)) ); } /** * Gets the number of trailing zeros. * * @param integer $i * @return integer */ public static function numberOfTrailingZeros($i) { $lastPos = strrpos(str_pad(decbin($i), 32, '0', STR_PAD_LEFT), '1'); return $lastPos === false ? 32 : 31 - $lastPos; } } src/BaconQrCode/Common/BitMatrix.php 0000666 00000020333 13436753160 0013334 0 ustar 00 width = $width; $this->height = $height; $this->rowSize = ($width + 31) >> 5; $this->bits = new SplFixedArray($this->rowSize * $height); } /** * Gets the requested bit, where true means black. * * @param integer $x * @param integer $y * @return boolean */ public function get($x, $y) { $offset = $y * $this->rowSize + ($x >> 5); return (BitUtils::unsignedRightShift($this->bits[$offset], ($x & 0x1f)) & 1) !== 0; } /** * Sets the given bit to true. * * @param integer $x * @param integer $y * @return void */ public function set($x, $y) { $offset = $y * $this->rowSize + ($x >> 5); $this->bits[$offset] = $this->bits[$offset] | (1 << ($x & 0x1f)); } /** * Flips the given bit. * * @param integer $x * @param integer $y * @return void */ public function flip($x, $y) { $offset = $y * $this->rowSize + ($x >> 5); $this->bits[$offset] = $this->bits[$offset] ^ (1 << ($x & 0x1f)); } /** * Clears all bits (set to false). * * @return void */ public function clear() { $max = count($this->bits); for ($i = 0; $i < $max; $i++) { $this->bits[$i] = 0; } } /** * Sets a square region of the bit matrix to true. * * @param integer $left * @param integer $top * @param integer $width * @param integer $height * @return void */ public function setRegion($left, $top, $width, $height) { if ($top < 0 || $left < 0) { throw new Exception\InvalidArgumentException('Left and top must be non-negative'); } if ($height < 1 || $width < 1) { throw new Exception\InvalidArgumentException('Width and height must be at least 1'); } $right = $left + $width; $bottom = $top + $height; if ($bottom > $this->height || $right > $this->width) { throw new Exception\InvalidArgumentException('The region must fit inside the matrix'); } for ($y = $top; $y < $bottom; $y++) { $offset = $y * $this->rowSize; for ($x = $left; $x < $right; $x++) { $index = $offset + ($x >> 5); $this->bits[$index] = $this->bits[$index] | (1 << ($x & 0x1f)); } } } /** * A fast method to retrieve one row of data from the matrix as a BitArray. * * @param integer $y * @param BitArray $row * @return BitArray */ public function getRow($y, BitArray $row = null) { if ($row === null || $row->getSize() < $this->width) { $row = new BitArray($this->width); } $offset = $y * $this->rowSize; for ($x = 0; $x < $this->rowSize; $x++) { $row->setBulk($x << 5, $this->bits[$offset + $x]); } return $row; } /** * Sets a row of data from a BitArray. * * @param integer $y * @param BitArray $row * @return void */ public function setRow($y, BitArray $row) { $bits = $row->getBitArray(); for ($i = 0; $i < $this->rowSize; $i++) { $this->bits[$y * $this->rowSize + $i] = $bits[$i]; } } /** * This is useful in detecting the enclosing rectangle of a 'pure' barcode. * * @return SplFixedArray */ public function getEnclosingRectangle() { $left = $this->width; $top = $this->height; $right = -1; $bottom = -1; for ($y = 0; $y < $this->height; $y++) { for ($x32 = 0; $x32 < $this->rowSize; $x32++) { $bits = $this->bits[$y * $this->rowSize + $x32]; if ($bits !== 0) { if ($y < $top) { $top = $y; } if ($y > $bottom) { $bottom = $y; } if ($x32 * 32 < $left) { $bit = 0; while (($bits << (31 - $bit)) === 0) { $bit++; } if (($x32 * 32 + $bit) < $left) { $left = $x32 * 32 + $bit; } } } if ($x32 * 32 + 31 > $right) { $bit = 31; while (BitUtils::unsignedRightShift($bits, $bit) === 0) { $bit--; } if (($x32 * 32 + $bit) > $right) { $right = $x32 * 32 + $bit; } } } } $width = $right - $left; $height = $bottom - $top; if ($width < 0 || $height < 0) { return null; } return SplFixedArray::fromArray(array($left, $top, $width, $height)); } /** * Gets the most top left set bit. * * This is useful in detecting a corner of a 'pure' barcode. * * @return SplFixedArray */ public function getTopLeftOnBit() { $bitsOffset = 0; while ($bitsOffset < count($this->bits) && $this->bits[$bitsOffset] === 0) { $bitsOffset++; } if ($bitsOffset === count($this->bits)) { return null; } $x = intval($bitsOffset / $this->rowSize); $y = ($bitsOffset % $this->rowSize) << 5; $bits = $this->bits[$bitsOffset]; $bit = 0; while (($bits << (31 - $bit)) === 0) { $bit++; } $x += $bit; return SplFixedArray::fromArray(array($x, $y)); } /** * Gets the most bottom right set bit. * * This is useful in detecting a corner of a 'pure' barcode. * * @return SplFixedArray */ public function getBottomRightOnBit() { $bitsOffset = count($this->bits) - 1; while ($bitsOffset >= 0 && $this->bits[$bitsOffset] === 0) { $bitsOffset--; } if ($bitsOffset < 0) { return null; } $x = intval($bitsOffset / $this->rowSize); $y = ($bitsOffset % $this->rowSize) << 5; $bits = $this->bits[$bitsOffset]; $bit = 0; while (BitUtils::unsignedRightShift($bits, $bit) === 0) { $bit--; } $x += $bit; return SplFixedArray::fromArray(array($x, $y)); } /** * Gets the width of the matrix, * * @return integer */ public function getWidth() { return $this->width; } /** * Gets the height of the matrix. * * @return integer */ public function getHeight() { return $this->height; } } src/BaconQrCode/Common/CharacterSetEci.php 0000666 00000007564 13436753160 0014435 0 ustar 00 self::ISO8859_1, 'ISO-8859-2' => self::ISO8859_2, 'ISO-8859-3' => self::ISO8859_3, 'ISO-8859-4' => self::ISO8859_4, 'ISO-8859-5' => self::ISO8859_5, 'ISO-8859-6' => self::ISO8859_6, 'ISO-8859-7' => self::ISO8859_7, 'ISO-8859-8' => self::ISO8859_8, 'ISO-8859-9' => self::ISO8859_9, 'ISO-8859-10' => self::ISO8859_10, 'ISO-8859-11' => self::ISO8859_11, 'ISO-8859-12' => self::ISO8859_12, 'ISO-8859-13' => self::ISO8859_13, 'ISO-8859-14' => self::ISO8859_14, 'ISO-8859-15' => self::ISO8859_15, 'ISO-8859-16' => self::ISO8859_16, 'SHIFT-JIS' => self::SJIS, 'WINDOWS-1250' => self::CP1250, 'WINDOWS-1251' => self::CP1251, 'WINDOWS-1252' => self::CP1252, 'WINDOWS-1256' => self::CP1256, 'UTF-16BE' => self::UNICODE_BIG_UNMARKED, 'UTF-8' => self::UTF8, 'ASCII' => self::ASCII, 'GBK' => self::GB18030, 'EUC-KR' => self::EUC_KR, ); /** * Additional possible values for character sets. * * @var array */ protected $additionalValues = array( self::CP437 => 2, self::ASCII => 170, ); /** * Gets character set ECI by value. * * @param string $name * @return CharacterSetEci|null */ public static function getCharacterSetECIByValue($value) { if ($value < 0 || $value >= 900) { throw new Exception\InvalidArgumentException('Value must be between 0 and 900'); } if (false !== ($key = array_search($value, self::$additionalValues))) { $value = $key; } try { return new self($value); } catch (Exception\UnexpectedValueException $e) { return null; } } /** * Gets character set ECI by name. * * @param string $name * @return CharacterSetEci|null */ public static function getCharacterSetECIByName($name) { $name = strtoupper($name); if (isset(self::$nameToEci[$name])) { return new self(self::$nameToEci[$name]); } return null; } } src/BaconQrCode/Common/Version.php 0000666 00000062054 13436753160 0013064 0 ustar 00 versionNumber = $versionNumber; $this->alignmentPatternCenters = $alignmentPatternCenters; $this->errorCorrectionBlocks = $ecBlocks; $totalCodewords = 0; $ecCodewords = $ecBlocks[0]->getEcCodewordsPerBlock(); foreach ($ecBlocks[0]->getEcBlocks() as $ecBlock) { $totalCodewords += $ecBlock->getCount() * ($ecBlock->getDataCodewords() + $ecCodewords); } $this->totalCodewords = $totalCodewords; } /** * Gets the version number. * * @return integer */ public function getVersionNumber() { return $this->versionNumber; } /** * Gets the alignment pattern centers. * * @return SplFixedArray */ public function getAlignmentPatternCenters() { return $this->alignmentPatternCenters; } /** * Gets the total number of codewords. * * @return integer */ public function getTotalCodewords() { return $this->totalCodewords; } /** * Gets the dimension for the current version. * * @return integer */ public function getDimensionForVersion() { return 17 + 4 * $this->versionNumber; } /** * Gets the number of EC blocks for a specific EC level. * * @param ErrorCorrectionLevel $ecLevel * @return integer */ public function getEcBlocksForLevel(ErrorCorrectionLevel $ecLevel) { return $this->errorCorrectionBlocks[$ecLevel->getOrdinal()]; } /** * Gets a provisional version number for a specific dimension. * * @param integer $dimension * @return Version * @throws Exception\InvalidArgumentException */ public static function getProvisionalVersionForDimension($dimension) { if ($dimension % 4 !== 1) { throw new Exception\InvalidArgumentException('Dimension is not 1 mod 4'); } return self::getVersionForNumber(($dimension - 17) >> 2); } /** * Gets a version instance for a specific version number. * * @param integer $versionNumber * @return Version * @throws Exception\InvalidArgumentException */ public static function getVersionForNumber($versionNumber) { if ($versionNumber < 1 || $versionNumber > 40) { throw new Exception\InvalidArgumentException('Version number must be between 1 and 40'); } if (!isset(self::$versions[$versionNumber])) { self::buildVersion($versionNumber); } return self::$versions[$versionNumber - 1]; } /** * Decodes version information from an integer and returns the version. * * @param integer $versionBits * @return Version|null */ public static function decodeVersionInformation($versionBits) { $bestDifference = PHP_INT_MAX; $bestVersion = 0; foreach (self::$versionDecodeInfo as $i => $targetVersion) { if ($targetVersion === $versionBits) { return self::getVersionForNumber($i + 7); } $bitsDifference = FormatInformation::numBitsDiffering($versionBits, $targetVersion); if ($bitsDifference < $bestDifference) { $bestVersion = $i + 7; $bestDifference = $bitsDifference; } } if ($bestDifference <= 3) { return self::getVersionForNumber($bestVersion); } return null; } /** * Builds the function pattern for the current version. * * @return BitMatrix */ public function buildFunctionPattern() { $dimension = $this->getDimensionForVersion(); $bitMatrix = new BitMatrix($dimension); // Top left finder pattern + separator + format $bitMatrix->setRegion(0, 0, 9, 9); // Top right finder pattern + separator + format $bitMatrix->setRegion($dimension - 8, 0, 8, 9); // Bottom left finder pattern + separator + format $bitMatrix->setRegion(0, $dimension - 8, 9, 8); // Alignment patterns $max = count($this->alignmentPatternCenters); for ($x = 0; $x < $max; $x++) { $i = $this->alignmentPatternCenters[$x] - 2; for ($y = 0; $y < $max; $y++) { if (($x === 0 && ($y === 0 || $y === $max - 1)) || ($x === $max - 1 && $y === 0)) { // No alignment patterns near the three finder paterns continue; } $bitMatrix->setRegion($this->alignmentPatternCenters[$y] - 2, $i, 5, 5); } } // Vertical timing pattern $bitMatrix->setRegion(6, 9, 1, $dimension - 17); // Horizontal timing pattern $bitMatrix->setRegion(9, 6, $dimension - 17, 1); if ($this->versionNumber > 6) { // Version info, top right $bitMatrix->setRegion($dimension - 11, 0, 3, 6); // Version info, bottom left $bitMatrix->setRegion(0, $dimension - 11, 6, 3); } return $bitMatrix; } /** * Returns a string representation for the version. * * @return string */ public function __toString() { return (string) $this->versionNumber; } /** * Build and cache a specific version. * * See ISO 18004:2006 6.5.1 Table 9. * * @param integer $versionNumber * @return void */ protected static function buildVersion($versionNumber) { switch ($versionNumber) { case 1: $patterns = array(); $ecBlocks = array( new EcBlocks(7, new EcBlock(1, 19)), new EcBlocks(10, new EcBlock(1, 16)), new EcBlocks(13, new EcBlock(1, 13)), new EcBlocks(17, new EcBlock(1, 9)), ); break; case 2: $patterns = array(6, 18); $ecBlocks = array( new EcBlocks(10, new EcBlock(1, 34)), new EcBlocks(16, new EcBlock(1, 28)), new EcBlocks(22, new EcBlock(1, 22)), new EcBlocks(28, new EcBlock(1, 16)), ); break; case 3: $patterns = array(6, 22); $ecBlocks = array( new EcBlocks(15, new EcBlock(1, 55)), new EcBlocks(26, new EcBlock(1, 44)), new EcBlocks(18, new EcBlock(2, 17)), new EcBlocks(22, new EcBlock(2, 13)), ); break; case 4: $patterns = array(6, 26); $ecBlocks = array( new EcBlocks(20, new EcBlock(1, 80)), new EcBlocks(18, new EcBlock(2, 32)), new EcBlocks(26, new EcBlock(3, 24)), new EcBlocks(16, new EcBlock(4, 9)), ); break; case 5: $patterns = array(6, 30); $ecBlocks = array( new EcBlocks(26, new EcBlock(1, 108)), new EcBlocks(24, new EcBlock(2, 43)), new EcBlocks(18, new EcBlock(2, 15), new EcBlock(2, 16)), new EcBlocks(22, new EcBlock(2, 11), new EcBlock(2, 12)), ); break; case 6: $patterns = array(6, 34); $ecBlocks = array( new EcBlocks(18, new EcBlock(2, 68)), new EcBlocks(16, new EcBlock(4, 27)), new EcBlocks(24, new EcBlock(4, 19)), new EcBlocks(28, new EcBlock(4, 15)), ); break; case 7: $patterns = array(6, 22, 38); $ecBlocks = array( new EcBlocks(20, new EcBlock(2, 78)), new EcBlocks(18, new EcBlock(4, 31)), new EcBlocks(18, new EcBlock(2, 14), new EcBlock(4, 15)), new EcBlocks(26, new EcBlock(4, 13)), ); break; case 8: $patterns = array(6, 24, 42); $ecBlocks = array( new EcBlocks(24, new EcBlock(2, 97)), new EcBlocks(22, new EcBlock(2, 38), new EcBlock(2, 39)), new EcBlocks(22, new EcBlock(4, 18), new EcBlock(2, 19)), new EcBlocks(26, new EcBlock(4, 14), new EcBlock(2, 15)), ); break; case 9: $patterns = array(6, 26, 46); $ecBlocks = array( new EcBlocks(30, new EcBlock(2, 116)), new EcBlocks(22, new EcBlock(3, 36), new EcBlock(2, 37)), new EcBlocks(20, new EcBlock(4, 16), new EcBlock(4, 17)), new EcBlocks(24, new EcBlock(4, 12), new EcBlock(4, 13)), ); break; case 10: $patterns = array(6, 28, 50); $ecBlocks = array( new EcBlocks(18, new EcBlock(2, 68), new EcBlock(2, 69)), new EcBlocks(26, new EcBlock(4, 43), new EcBlock(1, 44)), new EcBlocks(24, new EcBlock(6, 19), new EcBlock(2, 20)), new EcBlocks(28, new EcBlock(6, 15), new EcBlock(2, 16)), ); break; case 11: $patterns = array(6, 30, 54); $ecBlocks = array( new EcBlocks(20, new EcBlock(4, 81)), new EcBlocks(30, new EcBlock(1, 50), new EcBlock(4, 51)), new EcBlocks(28, new EcBlock(4, 22), new EcBlock(4, 23)), new EcBlocks(24, new EcBlock(3, 12), new EcBlock(8, 13)), ); break; case 12: $patterns = array(6, 32, 58); $ecBlocks = array( new EcBlocks(24, new EcBlock(2, 92), new EcBlock(2, 93)), new EcBlocks(22, new EcBlock(6, 36), new EcBlock(2, 37)), new EcBlocks(26, new EcBlock(4, 20), new EcBlock(6, 21)), new EcBlocks(28, new EcBlock(7, 14), new EcBlock(4, 15)), ); break; case 13: $patterns = array(6, 34, 62); $ecBlocks = array( new EcBlocks(26, new EcBlock(4, 107)), new EcBlocks(22, new EcBlock(8, 37), new EcBlock(1, 38)), new EcBlocks(24, new EcBlock(8, 20), new EcBlock(4, 21)), new EcBlocks(22, new EcBlock(12, 11), new EcBlock(4, 12)), ); break; case 14: $patterns = array(6, 26, 46, 66); $ecBlocks = array( new EcBlocks(30, new EcBlock(3, 115), new EcBlock(1, 116)), new EcBlocks(24, new EcBlock(4, 40), new EcBlock(5, 41)), new EcBlocks(20, new EcBlock(11, 16), new EcBlock(5, 17)), new EcBlocks(24, new EcBlock(11, 12), new EcBlock(5, 13)), ); break; case 15: $patterns = array(6, 26, 48, 70); $ecBlocks = array( new EcBlocks(22, new EcBlock(5, 87), new EcBlock(1, 88)), new EcBlocks(24, new EcBlock(5, 41), new EcBlock(5, 42)), new EcBlocks(30, new EcBlock(5, 24), new EcBlock(7, 25)), new EcBlocks(24, new EcBlock(11, 12), new EcBlock(7, 13)), ); break; case 16: $patterns = array(6, 26, 50, 74); $ecBlocks = array( new EcBlocks(24, new EcBlock(5, 98), new EcBlock(1, 99)), new EcBlocks(28, new EcBlock(7, 45), new EcBlock(3, 46)), new EcBlocks(24, new EcBlock(15, 19), new EcBlock(2, 20)), new EcBlocks(30, new EcBlock(3, 15), new EcBlock(13, 16)), ); break; case 17: $patterns = array(6, 30, 54, 78); $ecBlocks = array( new EcBlocks(28, new EcBlock(1, 107), new EcBlock(5, 108)), new EcBlocks(28, new EcBlock(10, 46), new EcBlock(1, 47)), new EcBlocks(28, new EcBlock(1, 22), new EcBlock(15, 23)), new EcBlocks(28, new EcBlock(2, 14), new EcBlock(17, 15)), ); break; case 18: $patterns = array(6, 30, 56, 82); $ecBlocks = array( new EcBlocks(30, new EcBlock(5, 120), new EcBlock(1, 121)), new EcBlocks(26, new EcBlock(9, 43), new EcBlock(4, 44)), new EcBlocks(28, new EcBlock(17, 22), new EcBlock(1, 23)), new EcBlocks(28, new EcBlock(2, 14), new EcBlock(19, 15)), ); break; case 19: $patterns = array(6, 30, 58, 86); $ecBlocks = array( new EcBlocks(28, new EcBlock(3, 113), new EcBlock(4, 114)), new EcBlocks(26, new EcBlock(3, 44), new EcBlock(11, 45)), new EcBlocks(26, new EcBlock(17, 21), new EcBlock(4, 22)), new EcBlocks(26, new EcBlock(9, 13), new EcBlock(16, 14)), ); break; case 20: $patterns = array(6, 34, 62, 90); $ecBlocks = array( new EcBlocks(28, new EcBlock(3, 107), new EcBlock(5, 108)), new EcBlocks(26, new EcBlock(3, 41), new EcBlock(13, 42)), new EcBlocks(30, new EcBlock(15, 24), new EcBlock(5, 25)), new EcBlocks(28, new EcBlock(15, 15), new EcBlock(10, 16)), ); break; case 21: $patterns = array(6, 28, 50, 72, 94); $ecBlocks = array( new EcBlocks(28, new EcBlock(4, 116), new EcBlock(4, 117)), new EcBlocks(26, new EcBlock(17, 42)), new EcBlocks(28, new EcBlock(17, 22), new EcBlock(6, 23)), new EcBlocks(30, new EcBlock(19, 16), new EcBlock(6, 17)), ); break; case 22: $patterns = array(6, 26, 50, 74, 98); $ecBlocks = array( new EcBlocks(28, new EcBlock(2, 111), new EcBlock(7, 112)), new EcBlocks(28, new EcBlock(17, 46)), new EcBlocks(30, new EcBlock(7, 24), new EcBlock(16, 25)), new EcBlocks(24, new EcBlock(34, 13)), ); break; case 23: $patterns = array(6, 30, 54, 78, 102); $ecBlocks = array( new EcBlocks(30, new EcBlock(4, 121), new EcBlock(5, 122)), new EcBlocks(28, new EcBlock(4, 47), new EcBlock(14, 48)), new EcBlocks(30, new EcBlock(11, 24), new EcBlock(14, 25)), new EcBlocks(30, new EcBlock(16, 15), new EcBlock(14, 16)), ); break; case 24: $patterns = array(6, 28, 54, 80, 106); $ecBlocks = array( new EcBlocks(30, new EcBlock(6, 117), new EcBlock(4, 118)), new EcBlocks(28, new EcBlock(6, 45), new EcBlock(14, 46)), new EcBlocks(30, new EcBlock(11, 24), new EcBlock(16, 25)), new EcBlocks(30, new EcBlock(30, 16), new EcBlock(2, 17)), ); break; case 25: $patterns = array(6, 32, 58, 84, 110); $ecBlocks = array( new EcBlocks(26, new EcBlock(8, 106), new EcBlock(4, 107)), new EcBlocks(28, new EcBlock(8, 47), new EcBlock(13, 48)), new EcBlocks(30, new EcBlock(7, 24), new EcBlock(22, 25)), new EcBlocks(30, new EcBlock(22, 15), new EcBlock(13, 16)), ); break; case 26: $patterns = array(6, 30, 58, 86, 114); $ecBlocks = array( new EcBlocks(28, new EcBlock(10, 114), new EcBlock(2, 115)), new EcBlocks(28, new EcBlock(19, 46), new EcBlock(4, 47)), new EcBlocks(28, new EcBlock(28, 22), new EcBlock(6, 23)), new EcBlocks(30, new EcBlock(33, 16), new EcBlock(4, 17)), ); break; case 27: $patterns = array(6, 34, 62, 90, 118); $ecBlocks = array( new EcBlocks(30, new EcBlock(8, 122), new EcBlock(4, 123)), new EcBlocks(28, new EcBlock(22, 45), new EcBlock(3, 46)), new EcBlocks(30, new EcBlock(8, 23), new EcBlock(26, 24)), new EcBlocks(30, new EcBlock(12, 15), new EcBlock(28, 16)), ); break; case 28: $patterns = array(6, 26, 50, 74, 98, 122); $ecBlocks = array( new EcBlocks(30, new EcBlock(3, 117), new EcBlock(10, 118)), new EcBlocks(28, new EcBlock(3, 45), new EcBlock(23, 46)), new EcBlocks(30, new EcBlock(4, 24), new EcBlock(31, 25)), new EcBlocks(30, new EcBlock(11, 15), new EcBlock(31, 16)), ); break; case 29: $patterns = array(6, 30, 54, 78, 102, 126); $ecBlocks = array( new EcBlocks(30, new EcBlock(7, 116), new EcBlock(7, 117)), new EcBlocks(28, new EcBlock(21, 45), new EcBlock(7, 46)), new EcBlocks(30, new EcBlock(1, 23), new EcBlock(37, 24)), new EcBlocks(30, new EcBlock(19, 15), new EcBlock(26, 16)), ); break; case 30: $patterns = array(6, 26, 52, 78, 104, 130); $ecBlocks = array( new EcBlocks(30, new EcBlock(5, 115), new EcBlock(10, 116)), new EcBlocks(28, new EcBlock(19, 47), new EcBlock(10, 48)), new EcBlocks(30, new EcBlock(15, 24), new EcBlock(25, 25)), new EcBlocks(30, new EcBlock(23, 15), new EcBlock(25, 16)), ); break; case 31: $patterns = array(6, 30, 56, 82, 108, 134); $ecBlocks = array( new EcBlocks(30, new EcBlock(13, 115), new EcBlock(3, 116)), new EcBlocks(28, new EcBlock(2, 46), new EcBlock(29, 47)), new EcBlocks(30, new EcBlock(42, 24), new EcBlock(1, 25)), new EcBlocks(30, new EcBlock(23, 15), new EcBlock(28, 16)), ); break; case 32: $patterns = array(6, 34, 60, 86, 112, 138); $ecBlocks = array( new EcBlocks(30, new EcBlock(17, 115)), new EcBlocks(28, new EcBlock(10, 46), new EcBlock(23, 47)), new EcBlocks(30, new EcBlock(10, 24), new EcBlock(35, 25)), new EcBlocks(30, new EcBlock(19, 15), new EcBlock(35, 16)), ); break; case 33: $patterns = array(6, 30, 58, 86, 114, 142); $ecBlocks = array( new EcBlocks(30, new EcBlock(17, 115), new EcBlock(1, 116)), new EcBlocks(28, new EcBlock(14, 46), new EcBlock(21, 47)), new EcBlocks(30, new EcBlock(29, 24), new EcBlock(19, 25)), new EcBlocks(30, new EcBlock(11, 15), new EcBlock(46, 16)), ); break; case 34: $patterns = array(6, 34, 62, 90, 118, 146); $ecBlocks = array( new EcBlocks(30, new EcBlock(13, 115), new EcBlock(6, 116)), new EcBlocks(28, new EcBlock(14, 46), new EcBlock(23, 47)), new EcBlocks(30, new EcBlock(44, 24), new EcBlock(7, 25)), new EcBlocks(30, new EcBlock(59, 16), new EcBlock(1, 17)), ); break; case 35: $patterns = array(6, 30, 54, 78, 102, 126, 150); $ecBlocks = array( new EcBlocks(30, new EcBlock(12, 121), new EcBlock(7, 122)), new EcBlocks(28, new EcBlock(12, 47), new EcBlock(26, 48)), new EcBlocks(30, new EcBlock(39, 24), new EcBlock(14, 25)), new EcBlocks(30, new EcBlock(22, 15), new EcBlock(41, 16)), ); break; case 36: $patterns = array(6, 24, 50, 76, 102, 128, 154); $ecBlocks = array( new EcBlocks(30, new EcBlock(6, 121), new EcBlock(14, 122)), new EcBlocks(28, new EcBlock(6, 47), new EcBlock(34, 48)), new EcBlocks(30, new EcBlock(46, 24), new EcBlock(10, 25)), new EcBlocks(30, new EcBlock(2, 15), new EcBlock(64, 16)), ); break; case 37: $patterns = array(6, 28, 54, 80, 106, 132, 158); $ecBlocks = array( new EcBlocks(30, new EcBlock(17, 122), new EcBlock(4, 123)), new EcBlocks(28, new EcBlock(29, 46), new EcBlock(14, 47)), new EcBlocks(30, new EcBlock(49, 24), new EcBlock(10, 25)), new EcBlocks(30, new EcBlock(24, 15), new EcBlock(46, 16)), ); break; case 38: $patterns = array(6, 32, 58, 84, 110, 136, 162); $ecBlocks = array( new EcBlocks(30, new EcBlock(4, 122), new EcBlock(18, 123)), new EcBlocks(28, new EcBlock(13, 46), new EcBlock(32, 47)), new EcBlocks(30, new EcBlock(48, 24), new EcBlock(14, 25)), new EcBlocks(30, new EcBlock(42, 15), new EcBlock(32, 16)), ); break; case 39: $patterns = array(6, 26, 54, 82, 110, 138, 166); $ecBlocks = array( new EcBlocks(30, new EcBlock(20, 117), new EcBlock(4, 118)), new EcBlocks(28, new EcBlock(40, 47), new EcBlock(7, 48)), new EcBlocks(30, new EcBlock(43, 24), new EcBlock(22, 25)), new EcBlocks(30, new EcBlock(10, 15), new EcBlock(67, 16)), ); break; case 40: $patterns = array(6, 30, 58, 86, 114, 142, 170); $ecBlocks = array( new EcBlocks(30, new EcBlock(19, 118), new EcBlock(6, 119)), new EcBlocks(28, new EcBlock(18, 47), new EcBlock(31, 48)), new EcBlocks(30, new EcBlock(34, 24), new EcBlock(34, 25)), new EcBlocks(30, new EcBlock(20, 15), new EcBlock(61, 16)), ); break; } self::$versions[$versionNumber - 1] = new self( $versionNumber, SplFixedArray::fromArray($patterns), SplFixedArray::fromArray($ecBlocks) ); } } src/BaconQrCode/Common/ErrorCorrectionLevel.php 0000666 00000002201 13436753160 0015534 0 ustar 00 value) { case self::L: return 0; break; case self::M: return 1; break; case self::Q: return 2; break; case self::H: return 3; break; } } } src/BaconQrCode/Common/Mode.php 0000666 00000003771 13436753160 0012324 0 ustar 00 array(0, 0, 0), self::NUMERIC => array(10, 12, 14), self::ALPHANUMERIC => array(9, 11, 13), self::STRUCTURED_APPEND => array(0, 0, 0), self::BYTE => array(8, 16, 16), self::ECI => array(0, 0, 0), self::KANJI => array(8, 10, 12), self::FNC1_FIRST_POSITION => array(0, 0, 0), self::FNC1_SECOND_POSITION => array(0, 0, 0), self::HANZI => array(8, 10, 12), ); /** * Gets the number of bits used in a specific QR code version. * * @param Version $version * @return integer */ public function getCharacterCountBits(Version $version) { $number = $version->getVersionNumber(); if ($number <= 9) { $offset = 0; } elseif ($number <= 26) { $offset = 1; } else { $offset = 2; } return self::$characterCountBitsForVersions[$this->value][$offset]; } } src/BaconQrCode/Common/BitArray.php 0000666 00000023235 13436753160 0013152 0 ustar 00 size = $size; $this->bits = new SplFixedArray(($this->size + 31) >> 3); } /** * Gets the size in bits. * * @return integer */ public function getSize() { return $this->size; } /** * Gets the size in bytes. * * @return integer */ public function getSizeInBytes() { return ($this->size + 7) >> 3; } /** * Ensures that the array has a minimum capacity. * * @param integer $size * @return void */ public function ensureCapacity($size) { if ($size > count($this->bits) << 5) { $this->bits->setSize(($size + 31) >> 5); } } /** * Gets a specific bit. * * @param integer $i * @return boolean */ public function get($i) { return ($this->bits[$i >> 5] & (1 << ($i & 0x1f))) !== 0; } /** * Sets a specific bit. * * @param integer $i * @return void */ public function set($i) { $this->bits[$i >> 5] = $this->bits[$i >> 5] | 1 << ($i & 0x1f); } /** * Flips a specific bit. * * @param integer $i * @return void */ public function flip($i) { $this->bits[$i >> 5] ^= 1 << ($i & 0x1f); } /** * Gets the next set bit position from a given position. * * @param integer $from * @return integer */ public function getNextSet($from) { if ($from >= $this->size) { return $this->size; } $bitsOffset = $from >> 5; $currentBits = $this->bits[$bitsOffset]; $bitsLength = count($this->bits); $currentBits &= ~((1 << ($from & 0x1f)) - 1); while ($currentBits === 0) { if (++$bitsOffset === $bitsLength) { return $this->size; } $currentBits = $this->bits[$bitsOffset]; } $result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits); return $result > $this->size ? $this->size : $result; } /** * Gets the next unset bit position from a given position. * * @param integer $from * @return integer */ public function getNextUnset($from) { if ($from >= $this->size) { return $this->size; } $bitsOffset = $from >> 5; $currentBits = ~$this->bits[$bitsOffset]; $bitsLength = count($this->bits); $currentBits &= ~((1 << ($from & 0x1f)) - 1); while ($currentBits === 0) { if (++$bitsOffset === $bitsLength) { return $this->size; } $currentBits = ~$this->bits[$bitsOffset]; } $result = ($bitsOffset << 5) + BitUtils::numberOfTrailingZeros($currentBits); return $result > $this->size ? $this->size : $result; } /** * Sets a bulk of bits. * * @param integer $i * @param integer $newBits * @return void */ public function setBulk($i, $newBits) { $this->bits[$i >> 5] = $newBits; } /** * Sets a range of bits. * * @param integer $start * @param integer $end * @return void * @throws Exception\InvalidArgumentException */ public function setRange($start, $end) { if ($end < $start) { throw new Exception\InvalidArgumentException('End must be greater or equal to start'); } if ($end === $start) { return; } $end--; $firstInt = $start >> 5; $lastInt = $end >> 5; for ($i = $firstInt; $i <= $lastInt; $i++) { $firstBit = $i > $firstInt ? 0 : $start & 0x1f; $lastBit = $i < $lastInt ? 31 : $end & 0x1f; if ($firstBit === 0 && $lastBit === 31) { $mask = 0x7fffffff; } else { $mask = 0; for ($j = $firstBit; $j < $lastBit; $j++) { $mask |= 1 << $j; } } $this->bits[$i] = $this->bits[$i] | $mask; } } /** * Clears the bit array, unsetting every bit. * * @return void */ public function clear() { $bitsLength = count($this->bits); for ($i = 0; $i < $bitsLength; $i++) { $this->bits[$i] = 0; } } /** * Checks if a range of bits is set or not set. * * @param integer $start * @param integer $end * @param integer $value * @return boolean * @throws Exception\InvalidArgumentException */ public function isRange($start, $end, $value) { if ($end < $start) { throw new Exception\InvalidArgumentException('End must be greater or equal to start'); } if ($end === $start) { return; } $end--; $firstInt = $start >> 5; $lastInt = $end >> 5; for ($i = $firstInt; $i <= $lastInt; $i++) { $firstBit = $i > $firstInt ? 0 : $start & 0x1f; $lastBit = $i < $lastInt ? 31 : $end & 0x1f; if ($firstBit === 0 && $lastBit === 31) { $mask = 0x7fffffff; } else { $mask = 0; for ($j = $firstBit; $j <= $lastBit; $j++) { $mask |= 1 << $j; } } if (($this->bits[$i] & $mask) !== ($value ? $mask : 0)) { return false; } } return true; } /** * Appends a bit to the array. * * @param boolean $bit * @return void */ public function appendBit($bit) { $this->ensureCapacity($this->size + 1); if ($bit) { $this->bits[$this->size >> 5] = $this->bits[$this->size >> 5] | (1 << ($this->size & 0x1f)); } $this->size++; } /** * Appends a number of bits (up to 32) to the array. * * @param integer $value * @param integer $numBits * @return void * @throws Exception\InvalidArgumentException */ public function appendBits($value, $numBits) { if ($numBits < 0 || $numBits > 32) { throw new Exception\InvalidArgumentException('Num bits must be between 0 and 32'); } $this->ensureCapacity($this->size + $numBits); for ($numBitsLeft = $numBits; $numBitsLeft > 0; $numBitsLeft--) { $this->appendBit((($value >> ($numBitsLeft - 1)) & 0x01) === 1); } } /** * Appends another bit array to this array. * * @param BitArray $other * @return void */ public function appendBitArray(self $other) { $otherSize = $other->getSize(); $this->ensureCapacity($this->size + $other->getSize()); for ($i = 0; $i < $otherSize; $i++) { $this->appendBit($other->get($i)); } } /** * Makes an exclusive-or comparision on the current bit array. * * @param BitArray $other * @return void * @throws Exception\InvalidArgumentException */ public function xorBits(self $other) { $bitsLength = count($this->bits); $otherBits = $other->getBitArray(); if ($bitsLength !== count($otherBits)) { throw new Exception\InvalidArgumentException('Sizes don\'t match'); } for ($i = 0; $i < $bitsLength; $i++) { $this->bits[$i] = $this->bits[$i] ^ $otherBits[$i]; } } /** * Converts the bit array to a byte array. * * @param integer $bitOffset * @param integer $numBytes * @return SplFixedArray */ public function toBytes($bitOffset, $numBytes) { $bytes = new SplFixedArray($numBytes); for ($i = 0; $i < $numBytes; $i++) { $byte = 0; for ($j = 0; $j < 8; $j++) { if ($this->get($bitOffset)) { $byte |= 1 << (7 - $j); } $bitOffset++; } $bytes[$i] = $byte; } return $bytes; } /** * Gets the internal bit array. * * @return SplFixedArray */ public function getBitArray() { return $this->bits; } /** * Reverses the array. * * @return void */ public function reverse() { $newBits = new SplFixedArray(count($this->bits)); for ($i = 0; $i < $this->size; $i++) { if ($this->get($this->size - $i - 1)) { $newBits[$i >> 5] = $newBits[$i >> 5] | (1 << ($i & 0x1f)); } } $this->bits = newBits; } /** * Returns a string representation of the bit array. * * @return string */ public function __toString() { $result = ''; for ($i = 0; $i < $this->size; $i++) { if (($i & 0x07) === 0) { $result .= ' '; } $result .= $this->get($i) ? 'X' : '.'; } return $result; } } src/BaconQrCode/Common/ReedSolomonCodec.php 0000666 00000035712 13436753160 0014624 0 ustar 00 8) { throw new Exception\InvalidArgumentException('Symbol size must be between 0 and 8'); } if ($firstRoot < 0 || $firstRoot >= (1 << $symbolSize)) { throw new Exception\InvalidArgumentException('First root must be between 0 and ' . (1 << $symbolSize)); } if ($numRoots < 0 || $numRoots >= (1 << $symbolSize)) { throw new Exception\InvalidArgumentException('Num roots must be between 0 and ' . (1 << $symbolSize)); } if ($padding < 0 || $padding >= ((1 << $symbolSize) - 1 - $numRoots)) { throw new Exception\InvalidArgumentException('Padding must be between 0 and ' . ((1 << $symbolSize) - 1 - $numRoots)); } $this->symbolSize = $symbolSize; $this->blockSize = (1 << $symbolSize) - 1; $this->padding = $padding; $this->alphaTo = SplFixedArray::fromArray(array_fill(0, $this->blockSize + 1, 0)); $this->indexOf = SplFixedArray::fromArray(array_fill(0, $this->blockSize + 1, 0)); // Generate galous field lookup table $this->indexOf[0] = $this->blockSize; $this->alphaTo[$this->blockSize] = 0; $sr = 1; for ($i = 0; $i < $this->blockSize; $i++) { $this->indexOf[$sr] = $i; $this->alphaTo[$i] = $sr; $sr <<= 1; if ($sr & (1 << $symbolSize)) { $sr ^= $gfPoly; } $sr &= $this->blockSize; } if ($sr !== 1) { throw new Exception\RuntimeException('Field generator polynomial is not primitive'); } // Form RS code generator polynomial from its roots $this->generatorPoly = SplFixedArray::fromArray(array_fill(0, $numRoots + 1, 0)); $this->firstRoot = $firstRoot; $this->primitive = $primitive; $this->numRoots = $numRoots; // Find prim-th root of 1, used in decoding for ($iPrimitive = 1; ($iPrimitive % $primitive) !== 0; $iPrimitive += $this->blockSize); $this->iPrimitive = intval($iPrimitive / $primitive); $this->generatorPoly[0] = 1; for ($i = 0, $root = $firstRoot * $primitive; $i < $numRoots; $i++, $root += $primitive) { $this->generatorPoly[$i + 1] = 1; for ($j = $i; $j > 0; $j--) { if ($this->generatorPoly[$j] !== 0) { $this->generatorPoly[$j] = $this->generatorPoly[$j - 1] ^ $this->alphaTo[$this->modNn($this->indexOf[$this->generatorPoly[$j]] + $root)]; } else { $this->generatorPoly[$j] = $this->generatorPoly[$j - 1]; } } $this->generatorPoly[$j] = $this->alphaTo[$this->modNn($this->indexOf[$this->generatorPoly[0]] + $root)]; } // Convert generator poly to index form for quicker encoding for ($i = 0; $i <= $numRoots; $i++) { $this->generatorPoly[$i] = $this->indexOf[$this->generatorPoly[$i]]; } } /** * Encodes data and writes result back into parity array. * * @param SplFixedArray $data * @param SplFixedArray $parity * @return void */ public function encode(SplFixedArray $data, SplFixedArray $parity) { for ($i = 0; $i < $this->numRoots; $i++) { $parity[$i] = 0; } $iterations = $this->blockSize - $this->numRoots - $this->padding; for ($i = 0; $i < $iterations; $i++) { $feedback = $this->indexOf[$data[$i] ^ $parity[0]]; if ($feedback !== $this->blockSize) { // Feedback term is non-zero $feedback = $this->modNn($this->blockSize - $this->generatorPoly[$this->numRoots] + $feedback); for ($j = 1; $j < $this->numRoots; $j++) { $parity[$j] = $parity[$j] ^ $this->alphaTo[$this->modNn($feedback + $this->generatorPoly[$this->numRoots - $j])]; } } for ($j = 0; $j < $this->numRoots - 1; $j++) { $parity[$j] = $parity[$j + 1]; } if ($feedback !== $this->blockSize) { $parity[$this->numRoots - 1] = $this->alphaTo[$this->modNn($feedback + $this->generatorPoly[0])]; } else { $parity[$this->numRoots - 1] = 0; } } } /** * Decodes received data. * * @param SplFixedArray $data * @param SplFixedArray|null $erasures * @return null|integer */ public function decode(SplFixedArray $data, SplFixedArray $erasures = null) { // This speeds up the initialization a bit. $numRootsPlusOne = SplFixedArray::fromArray(array_fill(0, $this->numRoots + 1, 0)); $numRoots = SplFixedArray::fromArray(array_fill(0, $this->numRoots, 0)); $lambda = clone $numRootsPlusOne; $b = clone $numRootsPlusOne; $t = clone $numRootsPlusOne; $omega = clone $numRootsPlusOne; $root = clone $numRoots; $loc = clone $numRoots; $numErasures = ($erasures !== null ? count($erasures) : 0); // Form the Syndromes; i.e., evaluate data(x) at roots of g(x) $syndromes = SplFixedArray::fromArray(array_fill(0, $this->numRoots, $data[0])); for ($i = 1; $i < $this->blockSize - $this->padding; $i++) { for ($j = 0; $j < $this->numRoots; $j++) { if ($syndromes[$j] === 0) { $syndromes[$j] = $data[$i]; } else { $syndromes[$j] = $data[$i] ^ $this->alphaTo[ $this->modNn($this->indexOf[$syndromes[$j]] + ($this->firstRoot + $j) * $this->primitive) ]; } } } // Convert syndromes to index form, checking for nonzero conditions $syndromeError = 0; for ($i = 0; $i < $this->numRoots; $i++) { $syndromeError |= $syndromes[$i]; $syndromes[$i] = $this->indexOf[$syndromes[$i]]; } if (!$syndromeError) { // If syndrome is zero, data[] is a codeword and there are no errors // to correct, so return data[] unmodified. return 0; } $lambda[0] = 1; if ($numErasures > 0) { // Init lambda to be the erasure locator polynomial $lambda[1] = $this->alphaTo[$this->modNn($this->primitive * ($this->blockSize - 1 - $erasures[0]))]; for ($i = 1; $i < $numErasures; $i++) { $u = $this->modNn($this->primitive * ($this->blockSize - 1 - $erasures[$i])); for ($j = $i + 1; $j > 0; $j--) { $tmp = $this->indexOf[$lambda[$j - 1]]; if ($tmp !== $this->blockSize) { $lambda[$j] = $lambda[$j] ^ $this->alphaTo[$this->modNn($u + $tmp)]; } } } } for ($i = 0; $i <= $this->numRoots; $i++) { $b[$i] = $this->indexOf[$lambda[$i]]; } // Begin Berlekamp-Massey algorithm to determine error+erasure locator // polynomial $r = $numErasures; $el = $numErasures; while (++$r <= $this->numRoots) { // Compute discrepancy at the r-th step in poly form $discrepancyR = 0; for ($i = 0; $i < $r; $i++) { if ($lambda[$i] !== 0 && $syndromes[$r - $i - 1] !== $this->blockSize) { $discrepancyR ^= $this->alphaTo[$this->modNn($this->indexOf[$lambda[$i]] + $syndromes[$r - $i - 1])]; } } $discrepancyR = $this->indexOf[$discrepancyR]; if ($discrepancyR === $this->blockSize) { $tmp = $b->toArray(); array_unshift($tmp, $this->blockSize); array_pop($tmp); $b = SplFixedArray::fromArray($tmp); } else { $t[0] = $lambda[0]; for ($i = 0; $i < $this->numRoots; $i++) { if ($b[$i] !== $this->blockSize) { $t[$i + 1] = $lambda[$i + 1] ^ $this->alphaTo[$this->modNn($discrepancyR + $b[$i])]; } else { $t[$i + 1] = $lambda[$i + 1]; } } if (2 * $el <= $r + $numErasures - 1) { $el = $r + $numErasures - $el; for ($i = 0; $i <= $this->numRoots; $i++) { $b[$i] = ( $lambda[$i] === 0 ? $this->blockSize : $this->modNn($this->indexOf[$lambda[$i]] - $discrepancyR + $this->blockSize) ); } } else { $tmp = $b->toArray(); array_unshift($tmp, $this->blockSize); array_pop($tmp); $b = SplFixedArray::fromArray($tmp); } $lambda = clone $t; } } // Convert lambda to index form and compute deg(lambda(x)) $degLambda = 0; for ($i = 0; $i <= $this->numRoots; $i++) { $lambda[$i] = $this->indexOf[$lambda[$i]]; if ($lambda[$i] !== $this->blockSize) { $degLambda = $i; } } // Find roots of the error+erasure locator polynomial by Chien search. $reg = clone $lambda; $reg[0] = 0; $count = 0; for ($i = 1, $k = $this->iPrimitive - 1; $i <= $this->blockSize; $i++, $k = $this->modNn($k + $this->iPrimitive)) { $q = 1; for ($j = $degLambda; $j > 0; $j--) { if ($reg[$j] !== $this->blockSize) { $reg[$j] = $this->modNn($reg[$j] + $j); $q ^= $this->alphaTo[$reg[$j]]; } } if ($q !== 0) { // Not a root continue; } // Store root (index-form) and error location number $root[$count] = $i; $loc[$count] = $k; if (++$count === $degLambda) { break; } } if ($degLambda !== $count) { // deg(lambda) unequal to number of roots: uncorreactable error // detected return null; } // Compute err+eras evaluate poly omega(x) = s(x)*lambda(x) (modulo // x**numRoots). In index form. Also find deg(omega). $degOmega = $degLambda - 1; for ($i = 0; $i <= $degOmega; $i++) { $tmp = 0; for ($j = $i; $j >= 0; $j--) { if ($syndromes[$i - $j] !== $this->blockSize && $lambda[$j] !== $this->blockSize) { $tmp ^= $this->alphaTo[$this->modNn($syndromes[$i - $j] + $lambda[$j])]; } } $omega[$i] = $this->indexOf[$tmp]; } // Compute error values in poly-form. num1 = omega(inv(X(l))), num2 = // inv(X(l))**(firstRoot-1) and den = lambda_pr(inv(X(l))) all in poly // form. for ($j = $count - 1; $j >= 0; $j--) { $num1 = 0; for ($i = $degOmega; $i >= 0; $i--) { if ($omega[$i] !== $this->blockSize) { $num1 ^= $this->alphaTo[$this->modNn($omega[$i] + $i * $root[$j])]; } } $num2 = $this->alphaTo[$this->modNn($root[$j] * ($this->firstRoot - 1) + $this->blockSize)]; $den = 0; // lambda[i+1] for i even is the formal derivativelambda_pr of // lambda[i] for ($i = min($degLambda, $this->numRoots - 1) & ~1; $i >= 0; $i -= 2) { if ($lambda[$i + 1] !== $this->blockSize) { $den ^= $this->alphaTo[$this->modNn($lambda[$i + 1] + $i * $root[$j])]; } } // Apply error to data if ($num1 !== 0 && $loc[$j] >= $this->padding) { $data[$loc[$j] - $this->padding] = $data[$loc[$j] - $this->padding] ^ ( $this->alphaTo[ $this->modNn( $this->indexOf[$num1] + $this->indexOf[$num2] + $this->blockSize - $this->indexOf[$den] ) ] ); } } if ($erasures !== null) { if (count($erasures) < $count) { $erasures->setSize($count); } for ($i = 0; $i < $count; $i++) { $erasures[$i] = $loc[$i]; } } return $count; } /** * Computes $x % GF_SIZE, where GF_SIZE is 2**GF_BITS - 1, without a slow * divide. * * @param itneger $x * @return integer */ protected function modNn($x) { while ($x >= $this->blockSize) { $x -= $this->blockSize; $x = ($x >> $this->symbolSize) + ($x & $this->blockSize); } return $x; } } src/BaconQrCode/Common/EcBlocks.php 0000666 00000004377 13436753160 0013130 0 ustar 00 ecCodewordsPerBlock = $ecCodewordsPerBlock; $this->ecBlocks = new SplFixedArray($ecb2 === null ? 1 : 2); $this->ecBlocks[0] = $ecb1; if ($ecb2 !== null) { $this->ecBlocks[1] = $ecb2; } } /** * Gets the number of EC codewords per block. * * @return integer */ public function getEcCodewordsPerBlock() { return $this->ecCodewordsPerBlock; } /** * Gets the total number of EC block appearances. * * @return integer */ public function getNumBlocks() { $total = 0; foreach ($this->ecBlocks as $ecBlock) { $total += $ecBlock->getCount(); } return $total; } /** * Gets the total count of EC codewords. * * @return integer */ public function getTotalEcCodewords() { return $this->ecCodewordsPerBlock * $this->getNumBlocks(); } /** * Gets the EC blocks included in this collection. * * @return SplFixedArray */ public function getEcBlocks() { return $this->ecBlocks; } } src/BaconQrCode/Writer.php 0000666 00000005026 13436753160 0011457 0 ustar 00 renderer = $renderer; } /** * Sets the renderer used to create a byte stream. * * @param RendererInterface $renderer * @return Writer */ public function setRenderer(RendererInterface $renderer) { $this->renderer = $renderer; return $this; } /** * Gets the renderer used to create a byte stream. * * @return RendererInterface */ public function getRenderer() { return $this->renderer; } /** * Writes QR code and returns it as string. * * Content is a string which *should* be encoded in UTF-8, in case there are * non ASCII-characters present. * * @param string $content * @param string $encoding * @param integer $ecLevel * @return string * @throws Exception\InvalidArgumentException */ public function writeString( $content, $encoding = Encoder::DEFAULT_BYTE_MODE_ECODING, $ecLevel = ErrorCorrectionLevel::L ) { if (strlen($content) === 0) { throw new Exception\InvalidArgumentException('Found empty contents'); } $qrCode = Encoder::encode($content, new ErrorCorrectionLevel($ecLevel), $encoding); return $this->getRenderer()->render($qrCode); } /** * Writes QR code to a file. * * @see Writer::writeString() * @param string $content * @param string $filename * @param string $encoding * @param integer $ecLevel * @return void */ public function writeFile( $content, $filename, $encoding = Encoder::DEFAULT_BYTE_MODE_ECODING, $ecLevel = ErrorCorrectionLevel::L ) { file_put_contents($filename, $this->writeString($content, $encoding, $ecLevel)); } } src/BaconQrCode/Exception/UnexpectedValueException.php 0000666 00000000566 13436753160 0017125 0 ustar 00 getArray(); $width = $matrix->getWidth(); $height = $matrix->getHeight(); for ($y = 0; $y < $height - 1; $y++) { for ($x = 0; $x < $width - 1; $x++) { $value = $array[$y][$x]; if ($value === $array[$y][$x + 1] && $value === $array[$y + 1][$x] && $value === $array[$y + 1][$x + 1]) { $penalty++; } } } return self::N2 * $penalty; } /** * Applies mask penalty rule 3 and returns the penalty. * * Finds consecutive cells of 00001011101 or 10111010000, and gives penalty * to them. If we find patterns like 000010111010000, we give penalties * twice (i.e. 40 * 2). * * @param ByteMatrix $matrix * @return integer */ public static function applyMaskPenaltyRule3(ByteMatrix $matrix) { $penalty = 0; $array = $matrix->getArray(); $width = $matrix->getWidth(); $height = $matrix->getHeight(); for ($y = 0; $y < $height; $y++) { for ($x = 0; $x < $width; $x++) { if ( $x + 6 < $width && $array[$y][$x] === 1 && $array[$y][$x + 1] === 0 && $array[$y][$x + 2] === 1 && $array[$y][$x + 3] === 1 && $array[$y][$x + 4] === 1 && $array[$y][$x + 5] === 0 && $array[$y][$x + 6] === 1 && ( ( $x + 10 < $width && $array[$y][$x + 7] === 0 && $array[$y][$x + 8] === 0 && $array[$y][$x + 9] === 0 && $array[$y][$x + 10] === 0 ) || ( $x - 4 >= 0 && $array[$y][$x - 1] === 0 && $array[$y][$x - 2] === 0 && $array[$y][$x - 3] === 0 && $array[$y][$x - 4] === 0 ) ) ) { $penalty += self::N3; } if ( $y + 6 < $height && $array[$y][$x] === 1 && $array[$y + 1][$x] === 0 && $array[$y + 2][$x] === 1 && $array[$y + 3][$x] === 1 && $array[$y + 4][$x] === 1 && $array[$y + 5][$x] === 0 && $array[$y + 6][$x] === 1 && ( ( $y + 10 < $height && $array[$y + 7][$x] === 0 && $array[$y + 8][$x] === 0 && $array[$y + 9][$x] === 0 && $array[$y + 10][$x] === 0 ) || ( $y - 4 >= 0 && $array[$y - 1][$x] === 0 && $array[$y - 2][$x] === 0 && $array[$y - 3][$x] === 0 && $array[$y - 4][$x] === 0 ) ) ) { $penalty += self::N3; } } } return $penalty; } /** * Applies mask penalty rule 4 and returns the penalty. * * Calculates the ratio of dark cells and gives penalty if the ratio is far * from 50%. It gives 10 penalty for 5% distance. * * @param ByteMatrix $matrix * @return integer */ public static function applyMaskPenaltyRule4(ByteMatrix $matrix) { $numDarkCells = 0; $array = $matrix->getArray(); $width = $matrix->getWidth(); $height = $matrix->getHeight(); for ($y = 0; $y < $height; $y++) { $arrayY = $array[$y]; for ($x = 0; $x < $width; $x++) { if ($arrayY[$x] === 1) { $numDarkCells++; } } } $numTotalCells = $height * $width; $darkRatio = $numDarkCells / $numTotalCells; $fixedPercentVariances = (int) (abs($darkRatio - 0.5) * 20); return $fixedPercentVariances * self::N4; } /** * Returns the mask bit for "getMaskPattern" at "x" and "y". * * See 8.8 of JISX0510:2004 for mask pattern conditions. * * @param integer $maskPattern * @param integer $x * @param integer $y * @return integer * @throws Exception\InvalidArgumentException */ public static function getDataMaskBit($maskPattern, $x, $y) { switch ($maskPattern) { case 0: $intermediate = ($y + $x) & 0x1; break; case 1: $intermediate = $y & 0x1; break; case 2: $intermediate = $x % 3; break; case 3: $intermediate = ($y + $x) % 3; break; case 4: $intermediate = (BitUtils::unsignedRightShift($y, 1) + ($x / 3)) & 0x1; break; case 5: $temp = $y * $x; $intermediate = ($temp & 0x1) + ($temp % 3); break; case 6: $temp = $y * $x; $intermediate = (($temp & 0x1) + ($temp % 3)) & 0x1; break; case 7: $temp = $y * $x; $intermediate = (($temp % 3) + (($y + $x) & 0x1)) & 0x1; break; default: throw new Exception\InvalidArgumentException('Invalid mask pattern: ' . $maskPattern); } return $intermediate === 0; } /** * Helper function for applyMaskPenaltyRule1. * * We need this for doing this calculation in both vertical and horizontal * orders respectively. * * @param ByteMatrix $matrix * @param boolean $isHorizontal * @return integer */ protected static function applyMaskPenaltyRule1Internal(ByteMatrix $matrix, $isHorizontal) { $penalty = 0; $iLimit = $isHorizontal ? $matrix->getHeight() : $matrix->getWidth(); $jLimit = $isHorizontal ? $matrix->getWidth() : $matrix->getHeight(); $array = $matrix->getArray(); for ($i = 0; $i < $iLimit; $i++) { $numSameBitCells = 0; $prevBit = -1; for ($j = 0; $j < $jLimit; $j++) { $bit = $isHorizontal ? $array[$i][$j] : $array[$j][$i]; if ($bit === $prevBit) { $numSameBitCells++; } else { if ($numSameBitCells >= 5) { $penalty += self::N1 + ($numSameBitCells - 5); } $numSameBitCells = 1; $prevBit = $bit; } } if ($numSameBitCells >= 5) { $penalty += self::N1 + ($numSameBitCells - 5); } } return $penalty; } } src/BaconQrCode/Encoder/MatrixUtil.php 0000666 00000043740 13436753160 0013671 0 ustar 00 clear(-1); } /** * Builds a complete matrix. * * @param BitArray $dataBits * @param ErrorCorrectionLevel $level * @param Version $version * @param integer $maskPattern * @param ByteMatrix $matrix * @return void */ public static function buildMatrix( BitArray $dataBits, ErrorCorrectionLevel $level, Version $version, $maskPattern, ByteMatrix $matrix ) { self::clearMatrix($matrix); self::embedBasicPatterns($version, $matrix); self::embedTypeInfo($level, $maskPattern, $matrix); self::maybeEmbedVersionInfo($version, $matrix); self::embedDataBits($dataBits, $maskPattern, $matrix); } /** * Embeds type information into a matrix. * * @param ErrorCorrectionLevel $level * @param integer $maskPattern * @param ByteMatrix $matrix * @return void */ protected static function embedTypeInfo(ErrorCorrectionLevel $level, $maskPattern, ByteMatrix $matrix) { $typeInfoBits = new BitArray(); self::makeTypeInfoBits($level, $maskPattern, $typeInfoBits); $typeInfoBitsSize = $typeInfoBits->getSize(); for ($i = 0; $i < $typeInfoBitsSize; $i++) { $bit = $typeInfoBits->get($typeInfoBitsSize - 1 - $i); $x1 = self::$typeInfoCoordinates[$i][0]; $y1 = self::$typeInfoCoordinates[$i][1]; $matrix->set($x1, $y1, $bit); if ($i < 8) { $x2 = $matrix->getWidth() - $i - 1; $y2 = 8; } else { $x2 = 8; $y2 = $matrix->getHeight() - 7 + ($i - 8); } $matrix->set($x2, $y2, $bit); } } /** * Generates type information bits and appends them to a bit array. * * @param ErrorCorrectionLevel $level * @param integer $maskPattern * @param BitArray $bits * @return void * @throws Exception\RuntimeException */ protected static function makeTypeInfoBits(ErrorCorrectionLevel $level, $maskPattern, BitArray $bits) { $typeInfo = ($level->get() << 3) | $maskPattern; $bits->appendBits($typeInfo, 5); $bchCode = self::calculateBchCode($typeInfo, self::$typeInfoPoly); $bits->appendBits($bchCode, 10); $maskBits = new BitArray(); $maskBits->appendBits(self::$typeInfoMaskPattern, 15); $bits->xorBits($maskBits); if ($bits->getSize() !== 15) { throw new Exception\RuntimeException('Bit array resulted in invalid size: ' . $bits->getSize()); } } /** * Embeds version information if required. * * @param Version $version * @param ByteMatrix $matrix * @return void */ protected static function maybeEmbedVersionInfo(Version $version, ByteMatrix $matrix) { if ($version->getVersionNumber() < 7) { return; } $versionInfoBits = new BitArray(); self::makeVersionInfoBits($version, $versionInfoBits); $bitIndex = 6 * 3 - 1; for ($i = 0; $i < 6; $i++) { for ($j = 0; $j < 3; $j++) { $bit = $versionInfoBits->get($bitIndex); $bitIndex--; $matrix->set($i, $matrix->getHeight() - 11 + $j, $bit); $matrix->set($matrix->getHeight() - 11 + $j, $i, $bit); } } } /** * Generates version information bits and appends them to a bit array. * * @param Version $version * @param BitArray $bits * @return void * @throws Exception\RuntimeException */ protected static function makeVersionInfoBits(Version $version, BitArray $bits) { $bits->appendBits($version->getVersionNumber(), 6); $bchCode = self::calculateBchCode($version->getVersionNumber(), self::$versionInfoPoly); $bits->appendBits($bchCode, 12); if ($bits->getSize() !== 18) { throw new Exception\RuntimeException('Bit array resulted in invalid size: ' . $bits->getSize()); } } /** * Calculates the BHC code for a value and a polynomial. * * @param integer $value * @param integer $poly * @return integer */ protected static function calculateBchCode($value, $poly) { $msbSetInPoly = self::findMsbSet($poly); $value <<= $msbSetInPoly - 1; while (self::findMsbSet($value) >= $msbSetInPoly) { $value ^= $poly << (self::findMsbSet($value) - $msbSetInPoly); } return $value; } /** * Finds and MSB set. * * @param integer $value * @return integer */ protected static function findMsbSet($value) { $numDigits = 0; while ($value !== 0) { $value >>= 1; $numDigits++; } return $numDigits; } /** * Embeds basic patterns into a matrix. * * @param Version $version * @param ByteMatrix $matrix * @return void */ protected static function embedBasicPatterns(Version $version, ByteMatrix $matrix) { self::embedPositionDetectionPatternsAndSeparators($matrix); self::embedDarkDotAtLeftBottomCorner($matrix); self::maybeEmbedPositionAdjustmentPatterns($version, $matrix); self::embedTimingPatterns($matrix); } /** * Embeds position detection patterns and separators into a byte matrix. * * @param ByteMatrix $matrix * @return void */ protected static function embedPositionDetectionPatternsAndSeparators(ByteMatrix $matrix) { $pdpWidth = count(self::$positionDetectionPattern[0]); self::embedPositionDetectionPattern(0, 0, $matrix); self::embedPositionDetectionPattern($matrix->getWidth() - $pdpWidth, 0, $matrix); self::embedPositionDetectionPattern(0, $matrix->getWidth() - $pdpWidth, $matrix); $hspWidth = 8; self::embedHorizontalSeparationPattern(0, $hspWidth - 1, $matrix); self::embedHorizontalSeparationPattern($matrix->getWidth() - $hspWidth, $hspWidth - 1, $matrix); self::embedHorizontalSeparationPattern(0, $matrix->getWidth() - $hspWidth, $matrix); $vspSize = 7; self::embedVerticalSeparationPattern($vspSize, 0, $matrix); self::embedVerticalSeparationPattern($matrix->getHeight() - $vspSize - 1, 0, $matrix); self::embedVerticalSeparationPattern($vspSize, $matrix->getHeight() - $vspSize, $matrix); } /** * Embeds a single position detection pattern into a byte matrix. * * @param integer $xStart * @param integer $yStart * @param ByteMatrix $matrix * @return void */ protected static function embedPositionDetectionPattern($xStart, $yStart, ByteMatrix $matrix) { for ($y = 0; $y < 7; $y++) { for ($x = 0; $x < 7; $x++) { $matrix->set($xStart + $x, $yStart + $y, self::$positionDetectionPattern[$y][$x]); } } } /** * Embeds a single horizontal separation pattern. * * @param integer $xStart * @param integer $yStart * @param ByteMatrix $matrix * @return void * @throws Exception\RuntimeException */ protected static function embedHorizontalSeparationPattern($xStart, $yStart, ByteMatrix $matrix) { for ($x = 0; $x < 8; $x++) { if ($matrix->get($xStart + $x, $yStart) !== -1) { throw new Exception\RuntimeException('Byte already set'); } $matrix->set($xStart + $x, $yStart, 0); } } /** * Embeds a single vertical separation pattern. * * @param integer $xStart * @param integer $yStart * @param ByteMatrix $matrix * @return void * @throws Exception\RuntimeException */ protected static function embedVerticalSeparationPattern($xStart, $yStart, ByteMatrix $matrix) { for ($y = 0; $y < 7; $y++) { if ($matrix->get($xStart, $yStart + $y) !== -1) { throw new Exception\RuntimeException('Byte already set'); } $matrix->set($xStart, $yStart + $y, 0); } } /** * Embeds a dot at the left bottom corner. * * @param ByteMatrix $matrix * @return void * @throws Exception\RuntimeException */ protected static function embedDarkDotAtLeftBottomCorner(ByteMatrix $matrix) { if ($matrix->get(8, $matrix->getHeight() - 8) === 0) { throw new Exception\RuntimeException('Byte already set to 0'); } $matrix->set(8, $matrix->getHeight() - 8, 1); } /** * Embeds position adjustment patterns if required. * * @param Version $version * @param ByteMatrix $matrix * @return void */ protected static function maybeEmbedPositionAdjustmentPatterns(Version $version, ByteMatrix $matrix) { if ($version->getVersionNumber() < 2) { return; } $index = $version->getVersionNumber() - 1; $coordinates = self::$positionAdjustmentPatternCoordinateTable[$index]; $numCoordinates = count($coordinates); for ($i = 0; $i < $numCoordinates; $i++) { for ($j = 0; $j < $numCoordinates; $j++) { $y = $coordinates[$i]; $x = $coordinates[$j]; if ($x === null || $y === null) { continue; } if ($matrix->get($x, $y) === -1) { self::embedPositionAdjustmentPattern($x - 2, $y - 2, $matrix); } } } } /** * Embeds a single position adjustment pattern. * * @param integer $xStart * @param intger $yStart * @param ByteMatrix $matrix * @return void */ protected static function embedPositionAdjustmentPattern($xStart, $yStart, ByteMatrix $matrix) { for ($y = 0; $y < 5; $y++) { for ($x = 0; $x < 5; $x++) { $matrix->set($xStart + $x, $yStart + $y, self::$positionAdjustmentPattern[$y][$x]); } } } /** * Embeds timing patterns into a matrix. * * @param ByteMatrix $matrix * @return void */ protected static function embedTimingPatterns(ByteMatrix $matrix) { $matrixWidth = $matrix->getWidth(); for ($i = 8; $i < $matrixWidth - 8; $i++) { $bit = ($i + 1) % 2; if ($matrix->get($i, 6) === -1) { $matrix->set($i, 6, $bit); } if ($matrix->get(6, $i) === -1) { $matrix->set(6, $i, $bit); } } } /** * Embeds "dataBits" using "getMaskPattern". * * For debugging purposes, it skips masking process if "getMaskPattern" is * -1. See 8.7 of JISX0510:2004 (p.38) for how to embed data bits. * * @param BitArray $dataBits * @param integer $maskPattern * @param ByteMatrix $matrix * @return void * @throws Exception\WriterException */ protected static function embedDataBits(BitArray $dataBits, $maskPattern, ByteMatrix $matrix) { $bitIndex = 0; $direction = -1; // Start from the right bottom cell. $x = $matrix->getWidth() - 1; $y = $matrix->getHeight() - 1; while ($x > 0) { // Skip vertical timing pattern. if ($x === 6) { $x--; } while ($y >= 0 && $y < $matrix->getHeight()) { for ($i = 0; $i < 2; $i++) { $xx = $x - $i; // Skip the cell if it's not empty. if ($matrix->get($xx, $y) !== -1) { continue; } if ($bitIndex < $dataBits->getSize()) { $bit = $dataBits->get($bitIndex); $bitIndex++; } else { // Padding bit. If there is no bit left, we'll fill the // left cells with 0, as described in 8.4.9 of // JISX0510:2004 (p. 24). $bit = false; } // Skip masking if maskPattern is -1. if ($maskPattern !== -1 && MaskUtil::getDataMaskBit($maskPattern, $xx, $y)) { $bit = !$bit; } $matrix->set($xx, $y, $bit); } $y += $direction; } $direction = -$direction; $y += $direction; $x -= 2; } // All bits should be consumed if ($bitIndex !== $dataBits->getSize()) { throw new Exception\WriterException('Not all bits consumed (' . $bitIndex . ' out of ' . $dataBits->getSize() .')'); } } } src/BaconQrCode/Encoder/QrCode.php 0000666 00000007342 13436753160 0012742 0 ustar 00 mode; } /** * Sets the mode. * * @param Mode $mode * @return void */ public function setMode(Mode $mode) { $this->mode = $mode; } /** * Gets the EC level. * * @return ErrorCorrectionLevel */ public function getErrorCorrectionLevel() { return $this->errorCorrectionLevel; } /** * Sets the EC level. * * @param ErrorCorrectionLevel $errorCorrectionLevel * @return void */ public function setErrorCorrectionLevel(ErrorCorrectionLevel $errorCorrectionLevel) { $this->errorCorrectionLevel = $errorCorrectionLevel; } /** * Gets the version. * * @return Version */ public function getVersion() { return $this->version; } /** * Sets the version. * * @param Version $version * @return void */ public function setVersion(Version $version) { $this->version = $version; } /** * Gets the mask pattern. * * @return integer */ public function getMaskPattern() { return $this->maskPattern; } /** * Sets the mask pattern. * * @param integer $maskPattern * @return void */ public function setMaskPattern($maskPattern) { $this->maskPattern = $maskPattern; } /** * Gets the matrix. * * @return ByteMatrix */ public function getMatrix() { return $this->matrix; } /** * Sets the matrix. * * @param ByteMatrix $matrix * @return void */ public function setMatrix(ByteMatrix $matrix) { $this->matrix = $matrix; } /** * Validates whether a mask pattern is valid. * * @param integer $maskPattern * @return boolean */ public static function isValidMaskPattern($maskPattern) { return $maskPattern > 0 && $maskPattern < self::NUM_MASK_PATTERNS; } /** * Returns a string representation of the QR code. * * @return string */ public function __toString() { $result = "<<\n" . " mode: " . $this->mode . "\n" . " ecLevel: " . $this->errorCorrectionLevel . "\n" . " version: " . $this->version . "\n" . " maskPattern: " . $this->maskPattern . "\n"; if ($this->matrix === null) { $result .= " matrix: null\n"; } else { $result .= " matrix:\n"; $result .= $this->matrix; } $result .= ">>\n"; return $result; } } src/BaconQrCode/Encoder/ByteMatrix.php 0000666 00000006042 13436753160 0013651 0 ustar 00 height = $height; $this->width = $width; $this->bytes = new SplFixedArray($height); for ($y = 0; $y < $height; $y++) { $this->bytes[$y] = new SplFixedArray($width); } } /** * Gets the width of the matrix. * * @return integer */ public function getWidth() { return $this->width; } /** * Gets the height of the matrix. * * @return integer */ public function getHeight() { return $this->height; } /** * Gets the internal representation of the matrix. * * @return SplFixedArray */ public function getArray() { return $this->bytes; } /** * Gets the byte for a specific position. * * @param integer $x * @param integer $y * @return integer */ public function get($x, $y) { return $this->bytes[$y][$x]; } /** * Sets the byte for a specific position. * * @param integer $x * @param integer $y * @param integer $value * @return void */ public function set($x, $y, $value) { $this->bytes[$y][$x] = (int) $value; } /** * Clears the matrix with a specific value. * * @param integer $value * @return void */ public function clear($value) { for ($y = 0; $y < $this->height; $y++) { for ($x = 0; $x < $this->width; $x++) { $this->bytes[$y][$x] = $value; } } } /** * Returns a string representation of the matrix. * * @return string */ public function __toString() { $result = ''; for ($y = 0; $y < $this->height; $y++) { for ($x = 0; $x < $this->width; $x++) { switch ($this->bytes[$y][$x]) { case 0: $result .= ' 0'; break; case 1: $result .= ' 1'; break; default: $result .= ' '; break; } } $result .= "\n"; } return $result; } } src/BaconQrCode/Encoder/BlockPair.php 0000666 00000002361 13436753160 0013427 0 ustar 00 dataBytes = $data; $this->errorCorrectionBytes = $errorCorrection; } /** * Gets the data bytes. * * @return SplFixedArray */ public function getDataBytes() { return $this->dataBytes; } /** * Gets the error correction bytes. * * @return SplFixedArray */ public function getErrorCorrectionBytes() { return $this->errorCorrectionBytes; } } src/BaconQrCode/Encoder/Encoder.php 0000666 00000053576 13436753160 0013156 0 ustar 00 get() === Mode::BYTE && $encoding !== self::DEFAULT_BYTE_MODE_ECODING) { $eci = CharacterSetEci::getCharacterSetEciByName($encoding); if ($eci !== null) { self::appendEci($eci, $headerBits); } } // (With ECI in place,) Write the mode marker self::appendModeInfo($mode, $headerBits); // Collect data within the main segment, separately, to count its size // if needed. Don't add it to main payload yet. $dataBits = new BitArray(); self::appendBytes($content, $mode, $dataBits, $encoding); // Hard part: need to know version to know how many bits length takes. // But need to know how many bits it takes to know version. First we // take a guess at version by assuming version will be the minimum, 1: $provisionalBitsNeeded = $headerBits->getSize() + $mode->getCharacterCountBits(Version::getVersionForNumber(1)) + $dataBits->getSize(); $provisionalVersion = self::chooseVersion($provisionalBitsNeeded, $ecLevel); // Use that guess to calculate the right version. I am still not sure // this works in 100% of cases. $bitsNeeded = $headerBits->getSize() + $mode->getCharacterCountBits($provisionalVersion) + $dataBits->getSize(); $version = self::chooseVersion($bitsNeeded, $ecLevel); $headerAndDataBits = new BitArray(); $headerAndDataBits->appendBitArray($headerBits); // Find "length" of main segment and write it. $numLetters = ($mode->get() === Mode::BYTE ? $dataBits->getSizeInBytes() : strlen($content)); self::appendLengthInfo($numLetters, $version, $mode, $headerAndDataBits); // Put data together into the overall payload. $headerAndDataBits->appendBitArray($dataBits); $ecBlocks = $version->getEcBlocksForLevel($ecLevel); $numDataBytes = $version->getTotalCodewords() - $ecBlocks->getTotalEcCodewords(); // Terminate the bits properly. self::terminateBits($numDataBytes, $headerAndDataBits); // Interleave data bits with error correction code. $finalBits = self::interleaveWithEcBytes( $headerAndDataBits, $version->getTotalCodewords(), $numDataBytes, $ecBlocks->getNumBlocks() ); $qrCode = new QrCode(); $qrCode->setErrorCorrectionLevel($ecLevel); $qrCode->setMode($mode); $qrCode->setVersion($version); // Choose the mask pattern and set to "qrCode". $dimension = $version->getDimensionForVersion(); $matrix = new ByteMatrix($dimension, $dimension); $maskPattern = self::chooseMaskPattern($finalBits, $ecLevel, $version, $matrix); $qrCode->setMaskPattern($maskPattern); // Build the matrix and set it to "qrCode". MatrixUtil::buildMatrix($finalBits, $ecLevel, $version, $maskPattern, $matrix); $qrCode->setMatrix($matrix); return $qrCode; } /** * Gets the alphanumeric code for a byte. * * @param string|integer $code * @return integer */ protected static function getAlphanumericCode($code) { $code = (is_string($code) ? ord($code) : $code); if (isset(self::$alphanumericTable[$code])) { return self::$alphanumericTable[$code]; } return -1; } /** * Chooses the best mode for a given content. * * @param string $content * @param string $encoding * @return Mode */ protected static function chooseMode($content, $encoding = null) { if (strcasecmp($encoding, 'SHIFT-JIS') === 0) { return self::isOnlyDoubleByteKanji($content) ? new Mode(Mode::KANJI) : new Mode(Mode::BYTE); } $hasNumeric = false; $hasAlphanumeric = false; $contentLength = strlen($content); for ($i = 0; $i < $contentLength; $i++) { $char = $content[$i]; if (ctype_digit($char)) { $hasNumeric = true; } elseif (self::getAlphanumericCode($char) !== -1) { $hasAlphanumeric = true; } else { return new Mode(Mode::BYTE); } } if ($hasAlphanumeric) { return new Mode(Mode::ALPHANUMERIC); } elseif ($hasNumeric) { return new Mode(Mode::NUMERIC); } return new Mode(Mode::BYTE); } /** * Calculates the mask penalty for a matrix. * * @param ByteMatrix $matrix * @return integer */ protected static function calculateMaskPenalty(ByteMatrix $matrix) { return ( MaskUtil::applyMaskPenaltyRule1($matrix) + MaskUtil::applyMaskPenaltyRule2($matrix) + MaskUtil::applyMaskPenaltyRule3($matrix) + MaskUtil::applyMaskPenaltyRule4($matrix) ); } /** * Chooses the best mask pattern for a matrix. * * @param BitArray $bits * @param ErrorCorrectionLevel $ecLevel * @param Version $version * @param ByteMatrix $matrix * @return integer */ protected static function chooseMaskPattern( BitArray $bits, ErrorCorrectionLevel $ecLevel, Version $version, ByteMatrix $matrix ) { $minPenality = PHP_INT_MAX; $bestMaskPattern = -1; for ($maskPattern = 0; $maskPattern < QrCode::NUM_MASK_PATTERNS; $maskPattern++) { MatrixUtil::buildMatrix($bits, $ecLevel, $version, $maskPattern, $matrix); $penalty = self::calculateMaskPenalty($matrix); if ($penalty < $minPenality) { $minPenality = $penalty; $bestMaskPattern = $maskPattern; } } return $bestMaskPattern; } /** * Chooses the best version for the input. * * @param integer $numInputBits * @param ErrorCorrectionLevel $ecLevel * @return Version * @throws Exception\WriterException */ protected static function chooseVersion($numInputBits, ErrorCorrectionLevel $ecLevel) { for ($versionNum = 1; $versionNum <= 40; $versionNum++) { $version = Version::getVersionForNumber($versionNum); $numBytes = $version->getTotalCodewords(); $ecBlocks = $version->getEcBlocksForLevel($ecLevel); $numEcBytes = $ecBlocks->getTotalEcCodewords(); $numDataBytes = $numBytes - $numEcBytes; $totalInputBytes = intval(($numInputBits + 8) / 8); if ($numDataBytes >= $totalInputBytes) { return $version; } } throw new Exception\WriterException('Data too big'); } /** * Terminates the bits in a bit array. * * @param integer $numDataBytes * @param BitArray $bits * @throws Exception\WriterException */ protected static function terminateBits($numDataBytes, BitArray $bits) { $capacity = $numDataBytes << 3; if ($bits->getSize() > $capacity) { throw new Exception\WriterException('Data bits cannot fit in the QR code'); } for ($i = 0; $i < 4 && $bits->getSize() < $capacity; $i++) { $bits->appendBit(false); } $numBitsInLastByte = $bits->getSize() & 0x7; if ($numBitsInLastByte > 0) { for ($i = $numBitsInLastByte; $i < 8; $i++) { $bits->appendBit(false); } } $numPaddingBytes = $numDataBytes - $bits->getSizeInBytes(); for ($i = 0; $i < $numPaddingBytes; $i++) { $bits->appendBits(($i & 0x1) === 0 ? 0xec : 0x11, 8); } if ($bits->getSize() !== $capacity) { throw new Exception\WriterException('Bits size does not equal capacity'); } } /** * Gets number of data- and EC bytes for a block ID. * * @param integer $numTotalBytes * @param integer $numDataBytes * @param integer $numRsBlocks * @param integer $blockId * @return array * @throws Exception\WriterException */ protected static function getNumDataBytesAndNumEcBytesForBlockId( $numTotalBytes, $numDataBytes, $numRsBlocks, $blockId ) { if ($blockId >= $numRsBlocks) { throw new Exception\WriterException('Block ID too large'); } $numRsBlocksInGroup2 = $numTotalBytes % $numRsBlocks; $numRsBlocksInGroup1 = $numRsBlocks - $numRsBlocksInGroup2; $numTotalBytesInGroup1 = intval($numTotalBytes / $numRsBlocks); $numTotalBytesInGroup2 = $numTotalBytesInGroup1 + 1; $numDataBytesInGroup1 = intval($numDataBytes / $numRsBlocks); $numDataBytesInGroup2 = $numDataBytesInGroup1 + 1; $numEcBytesInGroup1 = $numTotalBytesInGroup1 - $numDataBytesInGroup1; $numEcBytesInGroup2 = $numTotalBytesInGroup2 - $numDataBytesInGroup2; if ($numEcBytesInGroup1 !== $numEcBytesInGroup2) { throw new Exception\WriterException('EC bytes mismatch'); } if ($numRsBlocks !== $numRsBlocksInGroup1 + $numRsBlocksInGroup2) { throw new Exception\WriterException('RS blocks mismatch'); } if ($numTotalBytes !== (($numDataBytesInGroup1 + $numEcBytesInGroup1) * $numRsBlocksInGroup1) + (($numDataBytesInGroup2 + $numEcBytesInGroup2) * $numRsBlocksInGroup2) ) { throw new Exception\WriterException('Total bytes mismatch'); } if ($blockId < $numRsBlocksInGroup1) { return array($numDataBytesInGroup1, $numEcBytesInGroup1); } else { return array($numDataBytesInGroup2, $numEcBytesInGroup2); } } /** * Interleaves data with EC bytes. * * @param BitArray $bits * @param integer $numTotalBytes * @param integer $numDataBytes * @param integer $numRsBlocks * @return BitArray * @throws Exception\WriterException */ protected static function interleaveWithEcBytes(BitArray $bits, $numTotalBytes, $numDataBytes, $numRsBlocks) { if ($bits->getSizeInBytes() !== $numDataBytes) { throw new Exception\WriterException('Number of bits and data bytes does not match'); } $dataBytesOffset = 0; $maxNumDataBytes = 0; $maxNumEcBytes = 0; $blocks = new SplFixedArray($numRsBlocks); for ($i = 0; $i < $numRsBlocks; $i++) { list($numDataBytesInBlock, $numEcBytesInBlock) = self::getNumDataBytesAndNumEcBytesForBlockId( $numTotalBytes, $numDataBytes, $numRsBlocks, $i ); $size = $numDataBytesInBlock; $dataBytes = $bits->toBytes(8 * $dataBytesOffset, $size); $ecBytes = self::generateEcBytes($dataBytes, $numEcBytesInBlock); $blocks[$i] = new BlockPair($dataBytes, $ecBytes); $maxNumDataBytes = max($maxNumDataBytes, $size); $maxNumEcBytes = max($maxNumEcBytes, count($ecBytes)); $dataBytesOffset += $numDataBytesInBlock; } if ($numDataBytes !== $dataBytesOffset) { throw new Exception\WriterException('Data bytes does not match offset'); } $result = new BitArray(); for ($i = 0; $i < $maxNumDataBytes; $i++) { foreach ($blocks as $block) { $dataBytes = $block->getDataBytes(); if ($i < count($dataBytes)) { $result->appendBits($dataBytes[$i], 8); } } } for ($i = 0; $i < $maxNumEcBytes; $i++) { foreach ($blocks as $block) { $ecBytes = $block->getErrorCorrectionBytes(); if ($i < count($ecBytes)) { $result->appendBits($ecBytes[$i], 8); } } } if ($numTotalBytes !== $result->getSizeInBytes()) { throw new Exception\WriterException('Interleaving error: ' . $numTotalBytes . ' and ' . $result->getSizeInBytes() . ' differ'); } return $result; } /** * Generates EC bytes for given data. * * @param SplFixedArray $dataBytes * @param integer $numEcBytesInBlock * @return SplFixedArray */ protected static function generateEcBytes(SplFixedArray $dataBytes, $numEcBytesInBlock) { $numDataBytes = count($dataBytes); $toEncode = new SplFixedArray($numDataBytes + $numEcBytesInBlock); for ($i = 0; $i < $numDataBytes; $i++) { $toEncode[$i] = $dataBytes[$i] & 0xff; } $ecBytes = new SplFixedArray($numEcBytesInBlock); $codec = self::getCodec($numDataBytes, $numEcBytesInBlock); $codec->encode($toEncode, $ecBytes); return $ecBytes; } /** * Gets an RS codec and caches it. * * @param integer $numDataBytes * @param integer $numEcBytesInBlock * @return ReedSolomonCodec */ protected static function getCodec($numDataBytes, $numEcBytesInBlock) { $cacheId = $numDataBytes . '-' . $numEcBytesInBlock; if (!isset(self::$codecs[$cacheId])) { self::$codecs[$cacheId] = new ReedSolomonCodec( 8, 0x11d, 0, 1, $numEcBytesInBlock, 255 - $numDataBytes - $numEcBytesInBlock ); } return self::$codecs[$cacheId]; } /** * Appends mode information to a bit array. * * @param Mode $mode * @param BitArray $bits * @return void */ protected static function appendModeInfo(Mode $mode, BitArray $bits) { $bits->appendBits($mode->get(), 4); } /** * Appends length information to a bit array. * * @param integer $numLetters * @param Version $version * @param Mode $mode * @param BitArray $bits * @return void * @throws Exception\WriterException */ protected static function appendLengthInfo($numLetters, Version $version, Mode $mode, BitArray $bits) { $numBits = $mode->getCharacterCountBits($version); if ($numLetters >= (1 << $numBits)) { throw new Exception\WriterException($numLetters . ' is bigger than ' . ((1 << $numBits) - 1)); } $bits->appendBits($numLetters, $numBits); } /** * Appends bytes to a bit array in a specific mode. * * @param stirng $content * @param Mode $mode * @param BitArray $bits * @param string $encoding * @return void * @throws Exception\WriterException */ protected static function appendBytes($content, Mode $mode, BitArray $bits, $encoding) { switch ($mode->get()) { case Mode::NUMERIC: self::appendNumericBytes($content, $bits); break; case Mode::ALPHANUMERIC: self::appendAlphanumericBytes($content, $bits); break; case Mode::BYTE: self::append8BitBytes($content, $bits, $encoding); break; case Mode::KANJI: self::appendKanjiBytes($content, $bits); break; default: throw new Exception\WriterException('Invalid mode: ' . $mode->get()); } } /** * Appends numeric bytes to a bit array. * * @param string $content * @param BitArray $bits * @return void */ protected static function appendNumericBytes($content, BitArray $bits) { $length = strlen($content); $i = 0; while ($i < $length) { $num1 = (int) $content[$i]; if ($i + 2 < $length) { // Encode three numeric letters in ten bits. $num2 = (int) $content[$i + 1]; $num3 = (int) $content[$i + 2]; $bits->appendBits($num1 * 100 + $num2 * 10 + $num3, 10); $i += 3; } elseif ($i + 1 < $length) { // Encode two numeric letters in seven bits. $num2 = (int) $content[$i + 1]; $bits->appendBits($num1 * 10 + $num2, 7); $i += 2; } else { // Encode one numeric letter in four bits. $bits->appendBits($num1, 4); $i++; } } } /** * Appends alpha-numeric bytes to a bit array. * * @param string $content * @param BitArray $bits * @return void */ protected static function appendAlphanumericBytes($content, BitArray $bits) { $length = strlen($content); $i = 0; while ($i < $length) { if (-1 === ($code1 = self::getAlphanumericCode($content[$i]))) { throw new Exception\WriterException('Invalid alphanumeric code'); } if ($i + 1 < $length) { if (-1 === ($code2 = self::getAlphanumericCode($content[$i + 1]))) { throw new Exception\WriterException('Invalid alphanumeric code'); } // Encode two alphanumeric letters in 11 bits. $bits->appendBits($code1 * 45 + $code2, 11); $i += 2; } else { // Encode one alphanumeric letter in six bits. $bits->appendBits($code1, 6); $i++; } } } /** * Appends regular 8-bit bytes to a bit array. * * @param string $content * @param BitArray $bits * @return void */ protected static function append8BitBytes($content, BitArray $bits, $encoding) { if (false === ($bytes = @iconv('utf-8', $encoding, $content))) { throw new Exception\WriterException('Could not encode content to ' . $encoding); } $length = strlen($bytes); for ($i = 0; $i < $length; $i++) { $bits->appendBits(ord($bytes[$i]), 8); } } /** * Appends KANJI bytes to a bit array. * * @param string $content * @param BitArray $bits * @return void */ protected static function appendKanjiBytes($content, BitArray $bits) { if (strlen($content) % 2 > 0) { // We just do a simple length check here. The for loop will check // individual characters. throw new Exception\WriterException('Content does not seem to be encoded in SHIFT-JIS'); } $length = strlen($content); for ($i = 0; $i < $length; $i += 2) { $byte1 = ord($content[$i]) & 0xff; $byte2 = ord($content[$i + 1]) & 0xff; $code = ($byte1 << 8) | $byte2; if ($code >= 0x8140 && $code <= 0x9ffc) { $subtracted = $code - 0x8140; } elseif ($code >= 0xe040 && $code <= 0xebbf) { $subtracted = $code - 0xc140; } else { throw new Exception\WriterException('Invalid byte sequence'); } $encoded = (($subtracted >> 8) * 0xc0) + ($subtracted & 0xff); $bits->appendBits($encoded, 13); } } /** * Appends ECI information to a bit array. * * @param CharacterSetEci $eci * @param BitArray $bits * @return void */ protected static function appendEci(CharacterSetEci $eci, BitArray $bits) { $mode = new Mode(Mode::ECI); $bits->appendBits($mode->get(), 4); $bits->appendBits($eci->get(), 8); } } composer.json 0000666 00000001145 13436753160 0007303 0 ustar 00 { "name": "bacon/bacon-qr-code", "description": "BaconQrCode is a QR code generator for PHP.", "license" : "BSD-2-Clause", "homepage": "https://github.com/Bacon/BaconQrCode", "require": { "php": ">=5.3.3" }, "suggest": { "ext-gd": "to generate QR code images" }, "authors": [ { "name": "Ben Scholzen 'DASPRiD'", "email": "mail@dasprids.de", "homepage": "http://www.dasprids.de", "role": "Developer" } ], "autoload": { "psr-0": { "BaconQrCode": "src/" } } } autoload_function.php 0000666 00000000344 13436753160 0011007 0 ustar 00
' . " \n" . " ███████ █████ ███████ \n" . " █ █ █ █ █ █ \n" . " █ ███ █ ██ █ ███ █ \n" . " █ ███ █ ███ █ ███ █ \n" . " █ ███ █ █ █ █ ███ █ \n" . " █ █ ██ █ █ \n" . " ███████ █ █ █ ███████ \n" . " █████ \n" . " ██ ██ █ ██ █ █ █ \n" . " ██ ██ █ █ ██ \n" . " ████████ █ ██ █ ██ \n" . " ██ █ █ \n" . " ██ ███ █ █ █ █ \n" . " █ ███ █ █ \n" . " ███████ ██ ██████ \n" . " █ █ ████ ██ \n" . " █ ███ █ ██ ██ ██ █ ██ \n" . " █ ███ █ ██ ██ █ ██ \n" . " █ ███ █ █ █ ██ ██ \n" . " █ █ ███ ███ ████ \n" . " ███████ ████ ██ \n" . " \n" . '' ; $qrCode = Encoder::encode( $content, new ErrorCorrectionLevel(ErrorCorrectionLevel::L), Encoder::DEFAULT_BYTE_MODE_ECODING ); $this->assertEquals($expected, $this->renderer->render($qrCode)); } public function testSetStyle() { $content = 'foobar'; $qrCode = Encoder::encode( $content, new ErrorCorrectionLevel(ErrorCorrectionLevel::L), Encoder::DEFAULT_BYTE_MODE_ECODING ); $this->renderer->setStyle('bar'); $this->assertEquals('bar', $this->renderer->getStyle()); $this->assertStringMatchesFormat('%astyle="bar"%a', $this->renderer->render($qrCode)); } public function testSetClass() { $content = 'foobar'; $qrCode = Encoder::encode( $content, new ErrorCorrectionLevel(ErrorCorrectionLevel::L), Encoder::DEFAULT_BYTE_MODE_ECODING ); $this->renderer->setClass('bar'); $this->assertEquals('bar', $this->renderer->getClass()); $this->assertStringMatchesFormat('%aclass="bar"%a', $this->renderer->render($qrCode)); } } tests/BaconQrCode/Renderer/Text/TextTest.php 0000666 00000013270 13436753160 0015034 0 ustar 00 renderer = new Plain(); $this->writer = new Writer($this->renderer); } public function testBasicRender() { $content = 'foobar'; $expected = " \n" . " ███████ █████ ███████ \n" . " █ █ █ █ █ █ \n" . " █ ███ █ ██ █ ███ █ \n" . " █ ███ █ ███ █ ███ █ \n" . " █ ███ █ █ █ █ ███ █ \n" . " █ █ ██ █ █ \n" . " ███████ █ █ █ ███████ \n" . " █████ \n" . " ██ ██ █ ██ █ █ █ \n" . " ██ ██ █ █ ██ \n" . " ████████ █ ██ █ ██ \n" . " ██ █ █ \n" . " ██ ███ █ █ █ █ \n" . " █ ███ █ █ \n" . " ███████ ██ ██████ \n" . " █ █ ████ ██ \n" . " █ ███ █ ██ ██ ██ █ ██ \n" . " █ ███ █ ██ ██ █ ██ \n" . " █ ███ █ █ █ ██ ██ \n" . " █ █ ███ ███ ████ \n" . " ███████ ████ ██ \n" . " \n" ; $qrCode = Encoder::encode( $content, new ErrorCorrectionLevel(ErrorCorrectionLevel::L), Encoder::DEFAULT_BYTE_MODE_ECODING ); $this->assertEquals($expected, $this->renderer->render($qrCode)); } public function testBasicRenderNoMargins() { $content = 'foobar'; $expected = "███████ █████ ███████\n" . "█ █ █ █ █ █\n" . "█ ███ █ ██ █ ███ █\n" . "█ ███ █ ███ █ ███ █\n" . "█ ███ █ █ █ █ ███ █\n" . "█ █ ██ █ █\n" . "███████ █ █ █ ███████\n" . " █████ \n" . "██ ██ █ ██ █ █ █\n" . " ██ ██ █ █ ██ \n" . " ████████ █ ██ █ ██\n" . " ██ █ █\n" . " ██ ███ █ █ █ █\n" . " █ ███ █ █ \n" . "███████ ██ ██████ \n" . "█ █ ████ ██ \n" . "█ ███ █ ██ ██ ██ █ ██\n" . "█ ███ █ ██ ██ █ ██ \n" . "█ ███ █ █ █ ██ ██\n" . "█ █ ███ ███ ████\n" . "███████ ████ ██ \n" ; $qrCode = Encoder::encode( $content, new ErrorCorrectionLevel(ErrorCorrectionLevel::L), Encoder::DEFAULT_BYTE_MODE_ECODING ); $this->renderer->setMargin(0); $this->assertEquals(0, $this->renderer->getMargin()); $this->assertEquals($expected, $this->renderer->render($qrCode)); } public function testBasicRenderCustomChar() { $content = 'foobar'; $expected = "-----------------------\n" . "-#######-#####-#######-\n" . "-#-----#--#-#--#-----#-\n" . "-#-###-#--##---#-###-#-\n" . "-#-###-#--###--#-###-#-\n" . "-#-###-#---#-#-#-###-#-\n" . "-#-----#----##-#-----#-\n" . "-#######-#-#-#-#######-\n" . "---------#####---------\n" . "-##-##-#--##-#-#-----#-\n" . "----##----##-#-#-##----\n" . "--########-#--##-#--##-\n" . "-----------##------#-#-\n" . "--##--###--#---#--#--#-\n" . "---------#-###----#-#--\n" . "-#######--##-######----\n" . "-#-----#---####---##---\n" . "-#-###-#-##-##-##-#-##-\n" . "-#-###-#-##-##--#-##---\n" . "-#-###-#---#---#-##-##-\n" . "-#-----#-###--###-####-\n" . "-#######-####---##-----\n" . "-----------------------\n" ; $qrCode = Encoder::encode( $content, new ErrorCorrectionLevel(ErrorCorrectionLevel::L), Encoder::DEFAULT_BYTE_MODE_ECODING ); $this->renderer->setFullBlock('#'); $this->renderer->setEmptyBlock('-'); $this->assertEquals('#', $this->renderer->getFullBlock()); $this->assertEquals('-', $this->renderer->getEmptyBlock()); $this->assertEquals($expected, $this->renderer->render($qrCode)); } } tests/BaconQrCode/Common/ModeTest.php 0000666 00000002201 13436753160 0013522 0 ustar 00 assertEquals(0x0, Mode::TERMINATOR); $this->assertEquals(0x1, Mode::NUMERIC); $this->assertEquals(0x2, Mode::ALPHANUMERIC); $this->assertEquals(0x4, Mode::BYTE); $this->assertEquals(0x8, Mode::KANJI); } public function testInvalidModeThrowsException() { $this->setExpectedException( 'BaconQrCode\Exception\UnexpectedValueException', 'Value not a const in enum BaconQrCode\Common\Mode' ); new Mode(10); } } tests/BaconQrCode/Common/VersionTest.php 0000666 00000005427 13436753160 0014300 0 ustar 00 assertNotNull($version); $this->assertEquals($versionNumber, $version->getVersionNumber()); $this->assertNotNull($version->getAlignmentPatternCenters()); if ($versionNumber > 1) { $this->assertTrue(count($version->getAlignmentPatternCenters()) > 0); } $this->assertEquals($dimension, $version->getDimensionForVersion()); $this->assertNotNull($version->getEcBlocksForLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::H))); $this->assertNotNull($version->getEcBlocksForLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::L))); $this->assertNotNull($version->getEcBlocksForLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::M))); $this->assertNotNull($version->getEcBlocksForLevel(new ErrorCorrectionLevel(ErrorCorrectionLevel::Q))); $this->assertNotNull($version->buildFunctionPattern()); } /** * @dataProvider versionProvider * @param integer $versionNumber * @param integer $dimension */ public function testGetProvisionalVersionForDimension($versionNumber, $dimension) { $this->assertEquals( $versionNumber, Version::getProvisionalVersionForDimension($dimension)->getVersionNumber() ); } /** * @dataProvider decodeInformationProvider * @param integer $expectedVersion * @param integer $mask */ public function testDecodeVersionInformation($expectedVersion, $mask) { $version = Version::decodeVersionInformation($mask); $this->assertNotNull($version); $this->assertEquals($expectedVersion, $version->getVersionNumber()); } } tests/BaconQrCode/Common/BitUtilsTest.php 0000666 00000001706 13436753160 0014406 0 ustar 00 assertEquals(1, BitUtils::unsignedRightShift(1, 0)); $this->assertEquals(1, BitUtils::unsignedRightShift(10, 3)); $this->assertEquals(536870910, BitUtils::unsignedRightShift(-10, 3)); } public function testNumberOfTrailingZeros() { $this->assertEquals(32, BitUtils::numberOfTrailingZeros(0)); $this->assertEquals(1, BitUtils::numberOfTrailingZeros(10)); $this->assertEquals(0, BitUtils::numberOfTrailingZeros(15)); $this->assertEquals(2, BitUtils::numberOfTrailingZeros(20)); } } tests/BaconQrCode/Common/BitArrayTest.php 0000666 00000011465 13436753160 0014367 0 ustar 00 assertFalse($array->get($i)); $array->set($i); $this->assertTrue($array->get($i)); } } public function testGetNextSet1() { $array = new BitArray(32); for ($i = 0; $i < $array->getSize(); $i++) { $this->assertEquals($i, 32, '', $array->getNextSet($i)); } $array = new BitArray(33); for ($i = 0; $i < $array->getSize(); $i++) { $this->assertEquals($i, 33, '', $array->getNextSet($i)); } } public function testGetNextSet2() { $array = new BitArray(33); for ($i = 0; $i < $array->getSize(); $i++) { $this->assertEquals($i, $i <= 31 ? 31 : 33, '', $array->getNextSet($i)); } $array = new BitArray(33); for ($i = 0; $i < $array->getSize(); $i++) { $this->assertEquals($i, 32, '', $array->getNextSet($i)); } } public function testGetNextSet3() { $array = new BitArray(63); $array->set(31); $array->set(32); for ($i = 0; $i < $array->getSize(); $i++) { if ($i <= 31) { $expected = 31; } elseif ($i <= 32) { $expected = 32; } else { $expected = 63; } $this->assertEquals($i, $expected, '', $array->getNextSet($i)); } } public function testGetNextSet4() { $array = new BitArray(63); $array->set(33); $array->set(40); for ($i = 0; $i < $array->getSize(); $i++) { if ($i <= 33) { $expected = 33; } elseif ($i <= 40) { $expected = 40; } else { $expected = 63; } $this->assertEquals($i, $expected, '', $array->getNextSet($i)); } } public function testGetNextSet5() { mt_srand(hexdec('deadbeef')); for ($i = 0; $i < 10; $i++) { $array = new BitArray(mt_rand(1, 100)); $numSet = mt_rand(0, 19); for ($j = 0; $j < $numSet; $j++) { $array->set(mt_rand(0, $array->getSize() - 1)); } $numQueries = mt_rand(0, 19); for ($j = 0; $j < $numQueries; $j++) { $query = mt_rand(0, $array->getSize() - 1); $expected = $query; while ($expected < $array->getSize() && !$array->get($expected)) { $expected++; } $actual = $array->getNextSet($query); if ($actual !== $expected) { $array->getNextSet($query); } $this->assertEquals($expected, $actual); } } } public function testSetBulk() { $array = new BitArray(64); $array->setBulk(32, 0xFFFF0000); for ($i = 0; $i < 48; $i++) { $this->assertFalse($array->get($i)); } for ($i = 48; $i < 64; $i++) { $this->assertTrue($array->get($i)); } } public function testClear() { $array = new BitArray(32); for ($i = 0; $i < 32; $i++) { $array->set($i); } $array->clear(); for ($i = 0; $i < 32; $i++) { $this->assertFalse($array->get($i)); } } public function testGetArray() { $array = new BitArray(64); $array->set(0); $array->set(63); $ints = $array->getBitArray(); $this->assertEquals(1, $ints[0]); $this->assertEquals(0x80000000, $ints[1]); } public function testIsRange() { $array = new BitArray(64); $this->assertTrue($array->isRange(0, 64, false)); $this->assertFalse($array->isRange(0, 64, true)); $array->set(32); $this->assertTrue($array->isRange(32, 33, true)); $array->set(31); $this->assertTrue($array->isRange(31, 33, true)); $array->set(34); $this->assertFalse($array->isRange(31, 35, true)); for ($i = 0; $i < 31; $i++) { $array->set($i); } $this->assertTrue($array->isRange(0, 33, true)); for ($i = 33; $i < 64; $i++) { $array->set($i); } $this->assertTrue($array->isRange(0, 64, true)); $this->assertFalse($array->isRange(0, 64, false)); } } tests/BaconQrCode/Common/FormatInformationTest.php 0000666 00000006073 13436753160 0016307 0 ustar 00 unmaskedTestFormatInfo = $this->maskedTestFormatInfo ^ 0x5412; } public function testBitsDiffering() { $this->assertEquals(0, FormatInformation::numBitsDiffering(1, 1)); $this->assertEquals(1, FormatInformation::numBitsDiffering(0, 2)); $this->assertEquals(2, FormatInformation::numBitsDiffering(1, 2)); $this->assertEquals(32, FormatInformation::numBitsDiffering(-1, 0)); } public function testDecode() { $expected = FormatInformation::decodeFormatInformation( $this->maskedTestFormatInfo, $this->maskedTestFormatInfo ); $this->assertNotNull($expected); $this->assertEquals(7, $expected->getDataMask()); $this->assertEquals(ErrorCorrectionLevel::Q, $expected->getErrorCorrectionLevel()->get()); $this->assertEquals( $expected, FormatInformation::decodeFormatInformation( $this->unmaskedTestFormatInfo, $this->maskedTestFormatInfo ) ); } public function testDecodeWithBitDifference() { $expected = FormatInformation::decodeFormatInformation( $this->maskedTestFormatInfo, $this->maskedTestFormatInfo ); $this->assertEquals( $expected, FormatInformation::decodeFormatInformation( $this->maskedTestFormatInfo ^ 0x1, $this->maskedTestFormatInfo ^ 0x1 ) ); $this->assertEquals( $expected, FormatInformation::decodeFormatInformation( $this->maskedTestFormatInfo ^ 0x3, $this->maskedTestFormatInfo ^ 0x3 ) ); $this->assertEquals( $expected, FormatInformation::decodeFormatInformation( $this->maskedTestFormatInfo ^ 0x7, $this->maskedTestFormatInfo ^ 0x7 ) ); $this->assertNull( FormatInformation::decodeFormatInformation( $this->maskedTestFormatInfo ^ 0xf, $this->maskedTestFormatInfo ^ 0xf ) ); } public function testDecodeWithMisRead() { $expected = FormatInformation::decodeFormatInformation( $this->maskedTestFormatInfo, $this->maskedTestFormatInfo ); $this->assertEquals( $expected, FormatInformation::decodeFormatInformation( $this->maskedTestFormatInfo ^ 0x3, $this->maskedTestFormatInfo ^ 0xf ) ); } } tests/BaconQrCode/Common/BitMatrixTest.php 0000666 00000006445 13436753160 0014557 0 ustar 00 assertEquals(33, $matrix->getHeight()); for ($y = 0; $y < 33; $y++) { for ($x = 0; $x < 33; $x++) { if ($y * $x % 3 === 0) { $matrix->set($x, $y); } } } for ($y = 0; $y < 33; $y++) { for ($x = 0; $x < 33; $x++) { $this->assertEquals($x * $y % 3 === 0, $matrix->get($x, $y)); } } } public function testSetRegion() { $matrix = new BitMatrix(5); $matrix->setRegion(1, 1, 3, 3); for ($y = 0; $y < 5; $y++) { for ($x = 0; $x < 5; $x++) { $this->assertEquals($y >= 1 && $y <= 3 && $x >= 1 && $x <= 3, $matrix->get($x, $y)); } } } public function testRectangularMatrix() { $matrix = new BitMatrix(75, 20); $this->assertEquals(75, $matrix->getWidth()); $this->assertEquals(20, $matrix->getHeight()); $matrix->set(10, 0); $matrix->set(11, 1); $matrix->set(50, 2); $matrix->set(51, 3); $matrix->flip(74, 4); $matrix->flip(0, 5); $this->assertTrue($matrix->get(10, 0)); $this->assertTrue($matrix->get(11, 1)); $this->assertTrue($matrix->get(50, 2)); $this->assertTrue($matrix->get(51, 3)); $this->assertTrue($matrix->get(74, 4)); $this->assertTrue($matrix->get(0, 5)); $matrix->flip(50, 2); $matrix->flip(51, 3); $this->assertFalse($matrix->get(50, 2)); $this->assertFalse($matrix->get(51, 3)); } public function testRectangularSetRegion() { $matrix = new BitMatrix(320, 240); $this->assertEquals(320, $matrix->getWidth()); $this->assertEquals(240, $matrix->getHeight()); $matrix->setRegion(105, 22, 80, 12); for ($y = 0; $y < 240; $y++) { for ($x = 0; $x < 320; $x++) { $this->assertEquals($y >= 22 && $y < 34 && $x >= 105 && $x < 185, $matrix->get($x, $y)); } } } public function testGetRow() { $matrix = new BitMatrix(102, 5); for ($x = 0; $x < 102; $x++) { if ($x & 3 === 0) { $matrix->set($x, 2); } } $array1 = $matrix->getRow(2, null); $this->assertEquals(102, $array1->getSize()); $array2 = new BitArray(60); $array2 = $matrix->getRow(2, $array2); $this->assertEquals(102, $array2->getSize()); $array3 = new BitArray(200); $array3 = $matrix->getRow(2, $array3); $this->assertEquals(200, $array3->getSize()); for ($x = 0; $x < 102; $x++) { $on = ($x & 3 === 0); $this->assertEquals($on, $array1->get($x)); $this->assertEquals($on, $array2->get($x)); $this->assertEquals($on, $array3->get($x)); } } } tests/BaconQrCode/Common/ReedSolomonCodecTest.php 0000666 00000006725 13436753160 0016041 0 ustar 00 encode($block, $parity); // Copy parity into test blocks for ($i = 0; $i < $numRoots; $i++) { $block[$i + $dataSize] = $parity[$i]; $tBlock[$i + $dataSize] = $parity[$i]; } // Seed with errors for ($i = 0; $i < $errors; $i++) { $errorValue = mt_rand(1, $blockSize); do { $errorLocation = mt_rand(0, $blockSize); } while ($errorLocations[$errorLocation] !== 0); $errorLocations[$errorLocation] = 1; if (mt_rand(0, 1)) { $erasures[] = $errorLocation; } $tBlock[$errorLocation] ^= $errorValue; } $erasures = SplFixedArray::fromArray($erasures); // Decode the errored block $foundErrors = $codec->decode($tBlock, $erasures); if ($errors > 0 && $foundErrors === null) { $this->assertEquals($block, $tBlock, 'Decoder failed to correct errors'); } $this->assertEquals($errors, $foundErrors, 'Found errors do not equal expected errors'); for ($i = 0; $i < $foundErrors; $i++) { if ($errorLocations[$erasures[$i]] === 0) { $this->fail(sprintf('Decoder indicates error in location %d without error', $erasures[$i])); } } $this->assertEquals($block, $tBlock, 'Decoder did not correct errors'); } } } tests/BaconQrCode/Common/ErrorCorrectionLevelTest.php 0000666 00000002370 13436753160 0016756 0 ustar 00 assertEquals(0x0, ErrorCorrectionLevel::M); $this->assertEquals(0x1, ErrorCorrectionLevel::L); $this->assertEquals(0x2, ErrorCorrectionLevel::H); $this->assertEquals(0x3, ErrorCorrectionLevel::Q); } public function testInvalidErrorCorrectionLevelThrowsException() { $this->setExpectedException( 'BaconQrCode\Exception\UnexpectedValueException', 'Value not a const in enum BaconQrCode\Common\ErrorCorrectionLevel' ); new ErrorCorrectionLevel(4); } } tests/BaconQrCode/Encoder/EncoderTest.php 0000666 00000046624 13436753160 0014365 0 ustar 00 getMethods(ReflectionMethod::IS_STATIC) as $method) { $method->setAccessible(true); $this->methods[$method->getName()] = $method; } } public function testGetAlphanumericCode() { // The first ten code points are numbers. for ($i = 0; $i < 10; $i++) { $this->assertEquals($i, $this->methods['getAlphanumericCode']->invoke(null, ord('0') + $i)); } // The next 26 code points are capital alphabet letters. for ($i = 10; $i < 36; $i++) { // The first ten code points are numbers $this->assertEquals($i, $this->methods['getAlphanumericCode']->invoke(null, ord('A') + $i - 10)); } // Others are symbol letters. $this->assertEquals(36, $this->methods['getAlphanumericCode']->invoke(null, ' ')); $this->assertEquals(37, $this->methods['getAlphanumericCode']->invoke(null, '$')); $this->assertEquals(38, $this->methods['getAlphanumericCode']->invoke(null, '%')); $this->assertEquals(39, $this->methods['getAlphanumericCode']->invoke(null, '*')); $this->assertEquals(40, $this->methods['getAlphanumericCode']->invoke(null, '+')); $this->assertEquals(41, $this->methods['getAlphanumericCode']->invoke(null, '-')); $this->assertEquals(42, $this->methods['getAlphanumericCode']->invoke(null, '.')); $this->assertEquals(43, $this->methods['getAlphanumericCode']->invoke(null, '/')); $this->assertEquals(44, $this->methods['getAlphanumericCode']->invoke(null, ':')); // Should return -1 for other letters. $this->assertEquals(-1, $this->methods['getAlphanumericCode']->invoke(null, 'a')); $this->assertEquals(-1, $this->methods['getAlphanumericCode']->invoke(null, '#')); $this->assertEquals(-1, $this->methods['getAlphanumericCode']->invoke(null, "\0")); } public function testChooseMode() { // Numeric mode $this->assertSame(Mode::NUMERIC, $this->methods['chooseMode']->invoke(null, '0')->get()); $this->assertSame(Mode::NUMERIC, $this->methods['chooseMode']->invoke(null, '0123456789')->get()); // Alphanumeric mode $this->assertSame(Mode::ALPHANUMERIC, $this->methods['chooseMode']->invoke(null, 'A')->get()); $this->assertSame(Mode::ALPHANUMERIC, $this->methods['chooseMode']->invoke(null, '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:')->get()); // 8-bit byte mode $this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, 'a')->get()); $this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, '#')->get()); $this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, '')->get()); // AIUE in Hiragana in SHIFT-JIS $this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, "\x8\xa\x8\xa\x8\xa\x8\xa6")->get()); // Nihon in Kanji in SHIFT-JIS $this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, "\x9\xf\x9\x7b")->get()); // Sou-Utso-Byou in Kanji in SHIFT-JIS $this->assertSame(Mode::BYTE, $this->methods['chooseMode']->invoke(null, "\xe\x4\x9\x5\x9\x61")->get()); } public function testEncode() { $qrCode = Encoder::encode('ABCDEF', new ErrorCorrectionLevel(ErrorCorrectionLevel::H)); $expected = "<<\n" . " mode: ALPHANUMERIC\n" . " ecLevel: H\n" . " version: 1\n" . " maskPattern: 0\n" . " matrix:\n" . " 1 1 1 1 1 1 1 0 1 1 1 1 0 0 1 1 1 1 1 1 1\n" . " 1 0 0 0 0 0 1 0 0 1 1 1 0 0 1 0 0 0 0 0 1\n" . " 1 0 1 1 1 0 1 0 0 1 0 1 1 0 1 0 1 1 1 0 1\n" . " 1 0 1 1 1 0 1 0 1 1 1 0 1 0 1 0 1 1 1 0 1\n" . " 1 0 1 1 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1\n" . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 1\n" . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" . " 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0\n" . " 0 0 1 0 1 1 1 0 1 1 0 0 1 1 0 0 0 1 0 0 1\n" . " 1 0 1 1 1 0 0 1 0 0 0 1 0 1 0 0 0 0 0 0 0\n" . " 0 0 1 1 0 0 1 0 1 0 0 0 1 0 1 0 1 0 1 1 0\n" . " 1 1 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0\n" . " 0 0 1 1 0 1 1 1 1 0 0 0 1 0 1 0 1 1 1 1 0\n" . " 0 0 0 0 0 0 0 0 1 0 0 1 1 1 0 1 0 1 0 0 0\n" . " 1 1 1 1 1 1 1 0 0 0 1 0 1 0 1 1 0 0 0 0 1\n" . " 1 0 0 0 0 0 1 0 1 1 1 1 0 1 0 1 1 1 1 0 1\n" . " 1 0 1 1 1 0 1 0 1 0 1 1 0 1 0 1 0 0 0 0 1\n" . " 1 0 1 1 1 0 1 0 0 1 1 0 1 1 1 1 0 1 0 1 0\n" . " 1 0 1 1 1 0 1 0 1 0 0 0 1 0 1 0 1 1 1 0 1\n" . " 1 0 0 0 0 0 1 0 0 1 1 0 1 1 0 1 0 0 0 1 1\n" . " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 0 1 0 1\n" . ">>\n"; $this->assertEquals($expected, $qrCode->__toString()); } public function testSimpleUtf8Eci() { $qrCode = Encoder::encode('hello', new ErrorCorrectionLevel(ErrorCorrectionLevel::H), 'utf-8'); $expected = "<<\n" . " mode: BYTE\n" . " ecLevel: H\n" . " version: 1\n" . " maskPattern: 3\n" . " matrix:\n" . " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1\n" . " 1 0 0 0 0 0 1 0 0 0 1 0 1 0 1 0 0 0 0 0 1\n" . " 1 0 1 1 1 0 1 0 0 1 0 1 0 0 1 0 1 1 1 0 1\n" . " 1 0 1 1 1 0 1 0 0 1 1 0 1 0 1 0 1 1 1 0 1\n" . " 1 0 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 0 1\n" . " 1 0 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1\n" . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" . " 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0\n" . " 0 0 1 1 0 0 1 1 1 1 0 0 0 1 1 0 1 0 0 0 0\n" . " 0 0 1 1 1 0 0 0 0 0 1 1 0 0 0 1 0 1 1 1 0\n" . " 0 1 0 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 1 1 1\n" . " 1 1 0 0 1 0 0 1 1 0 0 1 1 1 1 0 1 0 1 1 0\n" . " 0 0 0 0 1 0 1 1 1 1 0 0 0 0 0 1 0 0 1 0 0\n" . " 0 0 0 0 0 0 0 0 1 1 1 1 0 0 1 1 1 0 0 0 1\n" . " 1 1 1 1 1 1 1 0 1 1 1 0 1 0 1 1 0 0 1 0 0\n" . " 1 0 0 0 0 0 1 0 0 0 1 0 0 1 1 1 1 1 1 0 1\n" . " 1 0 1 1 1 0 1 0 0 1 0 0 0 0 1 1 0 0 0 0 0\n" . " 1 0 1 1 1 0 1 0 1 1 1 0 1 0 0 0 1 1 0 0 0\n" . " 1 0 1 1 1 0 1 0 1 1 0 0 0 1 0 0 1 0 0 0 0\n" . " 1 0 0 0 0 0 1 0 0 0 0 1 1 0 1 0 1 0 1 1 0\n" . " 1 1 1 1 1 1 1 0 0 1 0 1 1 1 0 1 1 0 0 0 0\n" . ">>\n"; $this->assertEquals($expected, $qrCode->__toString()); } public function testAppendModeInfo() { $bits = new BitArray(); $this->methods['appendModeInfo']->invoke(null, new Mode(Mode::NUMERIC), $bits); $this->assertEquals(' ...X', $bits->__toString()); } public function testAppendLengthInfo() { // 1 letter (1/1), 10 bits. $bits = new BitArray(); $this->methods['appendLengthInfo']->invoke( null, 1, Version::getVersionForNumber(1), new Mode(Mode::NUMERIC), $bits ); $this->assertEquals(' ........ .X', $bits->__toString()); // 2 letters (2/1), 11 bits. $bits = new BitArray(); $this->methods['appendLengthInfo']->invoke( null, 2, Version::getVersionForNumber(10), new Mode(Mode::ALPHANUMERIC), $bits ); $this->assertEquals(' ........ .X.', $bits->__toString()); // 255 letters (255/1), 16 bits. $bits = new BitArray(); $this->methods['appendLengthInfo']->invoke( null, 255, Version::getVersionForNumber(27), new Mode(Mode::BYTE), $bits ); $this->assertEquals(' ........ XXXXXXXX', $bits->__toString()); // 512 letters (1024/2), 12 bits. $bits = new BitArray(); $this->methods['appendLengthInfo']->invoke( null, 512, Version::getVersionForNumber(40), new Mode(Mode::KANJI), $bits ); $this->assertEquals(' ..X..... ....', $bits->__toString()); } public function testAppendBytes() { // Should use appendNumericBytes. // 1 = 01 = 0001 in 4 bits. $bits = new BitArray(); $this->methods['appendBytes']->invoke( null, '1', new Mode(Mode::NUMERIC), $bits, Encoder::DEFAULT_BYTE_MODE_ECODING ); $this->assertEquals(' ...X', $bits->__toString()); // Should use appendAlphaNumericBytes. // A = 10 = 0xa = 001010 in 6 bits. $bits = new BitArray(); $this->methods['appendBytes']->invoke( null, 'A', new Mode(Mode::ALPHANUMERIC), $bits, Encoder::DEFAULT_BYTE_MODE_ECODING ); $this->assertEquals(' ..X.X.', $bits->__toString()); // Should use append8BitBytes. // 0x61, 0x62, 0x63 $bits = new BitArray(); $this->methods['appendBytes']->invoke( null, 'abc', new Mode(Mode::BYTE), $bits, Encoder::DEFAULT_BYTE_MODE_ECODING ); $this->assertEquals(' .XX....X .XX...X. .XX...XX', $bits->__toString()); // Should use appendKanjiBytes. // 0x93, 0x5f $bits = new BitArray(); $this->methods['appendBytes']->invoke( null, "\x93\x5f", new Mode(Mode::KANJI), $bits, Encoder::DEFAULT_BYTE_MODE_ECODING ); $this->assertEquals(' .XX.XX.. XXXXX', $bits->__toString()); // Lower letters such as 'a' cannot be encoded in alphanumeric mode. $this->setExpectedException( 'BaconQrCode\Exception\WriterException', 'Invalid alphanumeric code' ); $this->methods['appendBytes']->invoke( null, "a", new Mode(Mode::ALPHANUMERIC), $bits, Encoder::DEFAULT_BYTE_MODE_ECODING ); } public function testTerminateBits() { $bits = new BitArray(); $this->methods['terminateBits']->invoke(null, 0, $bits); $this->assertEquals('', $bits->__toString()); $bits = new BitArray(); $this->methods['terminateBits']->invoke(null, 1, $bits); $this->assertEquals(' ........', $bits->__toString()); $bits = new BitArray(); $bits->appendBits(0, 3); $this->methods['terminateBits']->invoke(null, 1, $bits); $this->assertEquals(' ........', $bits->__toString()); $bits = new BitArray(); $bits->appendBits(0, 5); $this->methods['terminateBits']->invoke(null, 1, $bits); $this->assertEquals(' ........', $bits->__toString()); $bits = new BitArray(); $bits->appendBits(0, 8); $this->methods['terminateBits']->invoke(null, 1, $bits); $this->assertEquals(' ........', $bits->__toString()); $bits = new BitArray(); $this->methods['terminateBits']->invoke(null, 2, $bits); $this->assertEquals(' ........ XXX.XX..', $bits->__toString()); $bits = new BitArray(); $bits->appendBits(0, 1); $this->methods['terminateBits']->invoke(null, 3, $bits); $this->assertEquals(' ........ XXX.XX.. ...X...X', $bits->__toString()); } public function testGetNumDataBytesAndNumEcBytesForBlockId() { // Version 1-H. list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 26, 9, 1, 0); $this->assertEquals(9, $numDataBytes); $this->assertEquals(17, $numEcBytes); // Version 3-H. 2 blocks. list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 70, 26, 2, 0); $this->assertEquals(13, $numDataBytes); $this->assertEquals(22, $numEcBytes); list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 70, 26, 2, 1); $this->assertEquals(13, $numDataBytes); $this->assertEquals(22, $numEcBytes); // Version 7-H. (4 + 1) blocks. list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 196, 66, 5, 0); $this->assertEquals(13, $numDataBytes); $this->assertEquals(26, $numEcBytes); list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 196, 66, 5, 4); $this->assertEquals(14, $numDataBytes); $this->assertEquals(26, $numEcBytes); // Version 40-H. (20 + 61) blocks. list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 3706, 1276, 81, 0); $this->assertEquals(15, $numDataBytes); $this->assertEquals(30, $numEcBytes); list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 3706, 1276, 81, 20); $this->assertEquals(16, $numDataBytes); $this->assertEquals(30, $numEcBytes); list($numDataBytes, $numEcBytes) = $this->methods['getNumDataBytesAndNumEcBytesForBlockId']->invoke(null, 3706, 1276, 81, 80); $this->assertEquals(16, $numDataBytes); $this->assertEquals(30, $numEcBytes); } public function testInterleaveWithEcBytes() { $dataBytes = SplFixedArray::fromArray(array(32, 65, 205, 69, 41, 220, 46, 128, 236)); $in = new BitArray(); foreach ($dataBytes as $dataByte) { $in->appendBits($dataByte, 8); } $outBits = $this->methods['interleaveWithEcBytes']->invoke(null, $in, 26, 9, 1); $expected = SplFixedArray::fromArray(array( // Data bytes. 32, 65, 205, 69, 41, 220, 46, 128, 236, // Error correction bytes. 42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61, )); $out = $outBits->toBytes(0, count($expected)); $this->assertEquals($expected, $out); } public function testAppendNumericBytes() { // 1 = 01 = 0001 in 4 bits. $bits = new BitArray(); $this->methods['appendNumericBytes']->invoke(null, '1', $bits); $this->assertEquals(' ...X', $bits->__toString()); // 12 = 0xc = 0001100 in 7 bits. $bits = new BitArray(); $this->methods['appendNumericBytes']->invoke(null, '12', $bits); $this->assertEquals(' ...XX..', $bits->__toString()); // 123 = 0x7b = 0001111011 in 10 bits. $bits = new BitArray(); $this->methods['appendNumericBytes']->invoke(null, '123', $bits); $this->assertEquals(' ...XXXX. XX', $bits->__toString()); // 1234 = "123" + "4" = 0001111011 + 0100 in 14 bits. $bits = new BitArray(); $this->methods['appendNumericBytes']->invoke(null, '1234', $bits); $this->assertEquals(' ...XXXX. XX.X..', $bits->__toString()); // Empty $bits = new BitArray(); $this->methods['appendNumericBytes']->invoke(null, '', $bits); $this->assertEquals('', $bits->__toString()); } public function testAppendAlphanumericBytes() { $bits = new BitArray(); $this->methods['appendAlphanumericBytes']->invoke(null, 'A', $bits); $this->assertEquals(' ..X.X.', $bits->__toString()); $bits = new BitArray(); $this->methods['appendAlphanumericBytes']->invoke(null, 'AB', $bits); $this->assertEquals(' ..XXX..X X.X', $bits->__toString()); $bits = new BitArray(); $this->methods['appendAlphanumericBytes']->invoke(null, 'ABC', $bits); $this->assertEquals(' ..XXX..X X.X..XX. .', $bits->__toString()); // Empty $bits = new BitArray(); $this->methods['appendAlphanumericBytes']->invoke(null, '', $bits); $this->assertEquals('', $bits->__toString()); // Invalid data $this->setExpectedException('BaconQrCode\Exception\WriterException', 'Invalid alphanumeric code'); $bits = new BitArray(); $this->methods['appendAlphanumericBytes']->invoke(null, 'abc', $bits); } public function testAppend8BitBytes() { // 0x61, 0x62, 0x63 $bits = new BitArray(); $this->methods['append8BitBytes']->invoke(null, 'abc', $bits, Encoder::DEFAULT_BYTE_MODE_ECODING); $this->assertEquals(' .XX....X .XX...X. .XX...XX', $bits->__toString()); // Empty $bits = new BitArray(); $this->methods['append8BitBytes']->invoke(null, '', $bits, Encoder::DEFAULT_BYTE_MODE_ECODING); $this->assertEquals('', $bits->__toString()); } public function testAppendKanjiBytes() { // Numbers are from page 21 of JISX0510:2004 $bits = new BitArray(); $this->methods['appendKanjiBytes']->invoke(null, "\x93\x5f", $bits); $this->assertEquals(' .XX.XX.. XXXXX', $bits->__toString()); $this->methods['appendKanjiBytes']->invoke(null, "\xe4\xaa", $bits); $this->assertEquals(' .XX.XX.. XXXXXXX. X.X.X.X. X.', $bits->__toString()); } public function testGenerateEcBytes() { // Numbers are from http://www.swetake.com/qr/qr3.html and // http://www.swetake.com/qr/qr9.html $dataBytes = SplFixedArray::fromArray(array(32, 65, 205, 69, 41, 220, 46, 128, 236)); $ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 17); $expected = SplFixedArray::fromArray(array(42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61)); $this->assertEquals($expected, $ecBytes); $dataBytes = SplFixedArray::fromArray(array(67, 70, 22, 38, 54, 70, 86, 102, 118, 134, 150, 166, 182, 198, 214)); $ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 18); $expected = SplFixedArray::fromArray(array(175, 80, 155, 64, 178, 45, 214, 233, 65, 209, 12, 155, 117, 31, 140, 214, 27, 187)); $this->assertEquals($expected, $ecBytes); // High-order zero coefficient case. $dataBytes = SplFixedArray::fromArray(array(32, 49, 205, 69, 42, 20, 0, 236, 17)); $ecBytes = $this->methods['generateEcBytes']->invoke(null, $dataBytes, 17); $expected = SplFixedArray::fromArray(array(0, 3, 130, 179, 194, 0, 55, 211, 110, 79, 98, 72, 170, 96, 211, 137, 213)); $this->assertEquals($expected, $ecBytes); } } tests/BaconQrCode/Encoder/MatrixUtilTest.php 0000666 00000036411 13436753160 0015101 0 ustar 00 getMethods(ReflectionMethod::IS_STATIC) as $method) { $method->setAccessible(true); $this->methods[$method->getName()] = $method; } } public function testToString() { $matrix= new ByteMatrix(3, 3); $matrix->set(0, 0, 0); $matrix->set(1, 0, 1); $matrix->set(2, 0, 0); $matrix->set(0, 1, 1); $matrix->set(1, 1, 0); $matrix->set(2, 1, 1); $matrix->set(0, 2, -1); $matrix->set(1, 2, -1); $matrix->set(2, 2, -1); $expected = " 0 1 0\n 1 0 1\n \n"; $this->assertEquals($expected, $matrix->__toString()); } public function testClearMatrix() { $matrix = new ByteMatrix(2, 2); MatrixUtil::clearMatrix($matrix); $this->assertEquals(-1, $matrix->get(0, 0)); $this->assertEquals(-1, $matrix->get(1, 0)); $this->assertEquals(-1, $matrix->get(0, 1)); $this->assertEquals(-1, $matrix->get(1, 1)); } public function testEmbedBasicPatterns1() { $matrix = new ByteMatrix(21, 21); MatrixUtil::clearMatrix($matrix); $this->methods['embedBasicPatterns']->invoke( null, Version::getVersionForNumber(1), $matrix ); $expected = " 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1\n" . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n" . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n" . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 1 \n" . " 0 \n" . " 1 \n" . " 0 \n" . " 1 \n" . " 0 0 0 0 0 0 0 0 1 \n" . " 1 1 1 1 1 1 1 0 \n" . " 1 0 0 0 0 0 1 0 \n" . " 1 0 1 1 1 0 1 0 \n" . " 1 0 1 1 1 0 1 0 \n" . " 1 0 1 1 1 0 1 0 \n" . " 1 0 0 0 0 0 1 0 \n" . " 1 1 1 1 1 1 1 0 \n"; $this->assertEquals($expected, $matrix->__toString()); } public function testEmbedBasicPatterns2() { $matrix = new ByteMatrix(25, 25); MatrixUtil::clearMatrix($matrix); $this->methods['embedBasicPatterns']->invoke( null, Version::getVersionForNumber(2), $matrix ); $expected = " 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1\n" . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n" . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" . " 1 0 1 1 1 0 1 0 0 1 0 1 1 1 0 1\n" . " 1 0 0 0 0 0 1 0 0 1 0 0 0 0 0 1\n" . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 1 \n" . " 0 \n" . " 1 \n" . " 0 \n" . " 1 \n" . " 0 \n" . " 1 \n" . " 0 \n" . " 1 1 1 1 1 1 \n" . " 0 0 0 0 0 0 0 0 1 1 0 0 0 1 \n" . " 1 1 1 1 1 1 1 0 1 0 1 0 1 \n" . " 1 0 0 0 0 0 1 0 1 0 0 0 1 \n" . " 1 0 1 1 1 0 1 0 1 1 1 1 1 \n" . " 1 0 1 1 1 0 1 0 \n" . " 1 0 1 1 1 0 1 0 \n" . " 1 0 0 0 0 0 1 0 \n" . " 1 1 1 1 1 1 1 0 \n"; $this->assertEquals($expected, $matrix->__toString()); } public function testEmbedTypeInfo() { $matrix = new ByteMatrix(21, 21); MatrixUtil::clearMatrix($matrix); $this->methods['embedTypeInfo']->invoke( null, new ErrorCorrectionLevel(ErrorCorrectionLevel::M), 5, $matrix ); $expected = " 0 \n" . " 1 \n" . " 1 \n" . " 1 \n" . " 0 \n" . " 0 \n" . " \n" . " 1 \n" . " 1 0 0 0 0 0 0 1 1 1 0 0 1 1 1 0\n" . " \n" . " \n" . " \n" . " \n" . " \n" . " 0 \n" . " 0 \n" . " 0 \n" . " 0 \n" . " 0 \n" . " 0 \n" . " 1 \n"; $this->assertEquals($expected, $matrix->__toString()); } public function testEmbedVersionInfo() { $matrix = new ByteMatrix(21, 21); MatrixUtil::clearMatrix($matrix); $this->methods['maybeEmbedVersionInfo']->invoke( null, Version::getVersionForNumber(7), $matrix ); $expected = " 0 0 1 \n" . " 0 1 0 \n" . " 0 1 0 \n" . " 0 1 1 \n" . " 1 1 1 \n" . " 0 0 0 \n" . " \n" . " \n" . " \n" . " \n" . " 0 0 0 0 1 0 \n" . " 0 1 1 1 1 0 \n" . " 1 0 0 1 1 0 \n" . " \n" . " \n" . " \n" . " \n" . " \n" . " \n" . " \n" . " \n"; $this->assertEquals($expected, $matrix->__toString()); } public function testEmbedDataBits() { $matrix = new ByteMatrix(21, 21); MatrixUtil::clearMatrix($matrix); $this->methods['embedBasicPatterns']->invoke( null, Version::getVersionForNumber(1), $matrix ); $bits = new BitArray(); $this->methods['embedDataBits']->invoke( null, $bits, -1, $matrix ); $expected = " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1\n" . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n" . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n" . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n" . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 1 0 1 1 1 0 1\n" . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n" . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 1 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" . " 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"; $this->assertEquals($expected, $matrix->__toString()); } public function testBuildMatrix() { $bytes = array( 32, 65, 205, 69, 41, 220, 46, 128, 236, 42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219 , 61 ); $bits = new BitArray(); foreach ($bytes as $byte) { $bits->appendBits($byte, 8); } $matrix = new ByteMatrix(21, 21); MatrixUtil::buildMatrix( $bits, new ErrorCorrectionLevel(ErrorCorrectionLevel::H), Version::getVersionForNumber(1), 3, $matrix ); $expected = " 1 1 1 1 1 1 1 0 0 1 1 0 0 0 1 1 1 1 1 1 1\n" . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 1\n" . " 1 0 1 1 1 0 1 0 0 0 0 1 0 0 1 0 1 1 1 0 1\n" . " 1 0 1 1 1 0 1 0 0 1 1 0 0 0 1 0 1 1 1 0 1\n" . " 1 0 1 1 1 0 1 0 1 1 0 0 1 0 1 0 1 1 1 0 1\n" . " 1 0 0 0 0 0 1 0 0 0 1 1 1 0 1 0 0 0 0 0 1\n" . " 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1\n" . " 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 0 0 0\n" . " 0 0 1 1 0 0 1 1 1 0 0 1 1 1 1 0 1 0 0 0 0\n" . " 1 0 1 0 1 0 0 0 0 0 1 1 1 0 0 1 0 1 1 1 0\n" . " 1 1 1 1 0 1 1 0 1 0 1 1 1 0 0 1 1 1 0 1 0\n" . " 1 0 1 0 1 1 0 1 1 1 0 0 1 1 1 0 0 1 0 1 0\n" . " 0 0 1 0 0 1 1 1 0 0 0 0 0 0 1 0 1 1 1 1 1\n" . " 0 0 0 0 0 0 0 0 1 1 0 1 0 0 0 0 0 1 0 1 1\n" . " 1 1 1 1 1 1 1 0 1 1 1 1 0 0 0 0 1 0 1 1 0\n" . " 1 0 0 0 0 0 1 0 0 0 0 1 0 1 1 1 0 0 0 0 0\n" . " 1 0 1 1 1 0 1 0 0 1 0 0 1 1 0 0 1 0 0 1 1\n" . " 1 0 1 1 1 0 1 0 1 1 0 1 0 0 0 0 0 1 1 1 0\n" . " 1 0 1 1 1 0 1 0 1 1 1 1 0 0 0 0 1 1 1 0 0\n" . " 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0\n" . " 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 0 1 0 0 1 0\n"; $this->assertEquals($expected, $matrix->__toString()); } public function testFindMsbSet() { $this->assertEquals(0, $this->methods['findMsbSet']->invoke(null, 0)); $this->assertEquals(1, $this->methods['findMsbSet']->invoke(null, 1)); $this->assertEquals(8, $this->methods['findMsbSet']->invoke(null, 0x80)); $this->assertEquals(32, $this->methods['findMsbSet']->invoke(null, 0x80000000)); } public function testCalculateBchCode() { // Encoding of type information. // From Appendix C in JISX0510:2004 (p 65) $this->assertEquals(0xdc, $this->methods['calculateBchCode']->invoke(null, 5, 0x537)); // From http://www.swetake.com/qr/qr6.html $this->assertEquals(0x1c2, $this->methods['calculateBchCode']->invoke(null, 0x13, 0x537)); // From http://www.swetake.com/qr/qr11.html $this->assertEquals(0x214, $this->methods['calculateBchCode']->invoke(null, 0x1b, 0x537)); // Encoding of version information. // From Appendix D in JISX0510:2004 (p 68) $this->assertEquals(0xc94, $this->methods['calculateBchCode']->invoke(null, 7, 0x1f25)); $this->assertEquals(0x5bc, $this->methods['calculateBchCode']->invoke(null, 8, 0x1f25)); $this->assertEquals(0xa99, $this->methods['calculateBchCode']->invoke(null, 9, 0x1f25)); $this->assertEquals(0x4d3, $this->methods['calculateBchCode']->invoke(null, 10, 0x1f25)); $this->assertEquals(0x9a6, $this->methods['calculateBchCode']->invoke(null, 20, 0x1f25)); $this->assertEquals(0xd75, $this->methods['calculateBchCode']->invoke(null, 30, 0x1f25)); $this->assertEquals(0xc69, $this->methods['calculateBchCode']->invoke(null, 40, 0x1f25)); } public function testMakeVersionInfoBits() { // From Appendix D in JISX0510:2004 (p 68) $bits = new BitArray(); $this->methods['makeVersionInfoBits']->invoke(null, Version::getVersionForNumber(7), $bits); $this->assertEquals(' ...XXXXX ..X..X.X ..', $bits->__toString()); } public function testMakeTypeInfoBits() { // From Appendix D in JISX0510:2004 (p 68) $bits = new BitArray(); $this->methods['makeTypeInfoBits']->invoke(null, new ErrorCorrectionLevel(ErrorCorrectionLevel::M), 5, $bits); $this->assertEquals(' X......X X..XXX.', $bits->__toString()); } } tests/BaconQrCode/Encoder/MaskUtilTest.php 0000666 00000021355 13436753160 0014531 0 ustar 00 fail('Data mask bit did not match'); } } } } public function testApplyMaskPenaltyRule1() { $matrix = new ByteMatrix(4, 1); $matrix->set(0, 0, 0); $matrix->set(1, 0, 0); $matrix->set(2, 0, 0); $matrix->set(3, 0, 0); $this->assertEquals(0, MaskUtil::applyMaskPenaltyRule1($matrix)); // Horizontal $matrix = new ByteMatrix(6, 1); $matrix->set(0, 0, 0); $matrix->set(1, 0, 0); $matrix->set(2, 0, 0); $matrix->set(3, 0, 0); $matrix->set(4, 0, 0); $matrix->set(5, 0, 1); $this->assertEquals(3, MaskUtil::applyMaskPenaltyRule1($matrix)); $matrix->set(5, 0, 0); $this->assertEquals(4, MaskUtil::applyMaskPenaltyRule1($matrix)); // Vertical $matrix = new ByteMatrix(1, 6); $matrix->set(0, 0, 0); $matrix->set(0, 1, 0); $matrix->set(0, 2, 0); $matrix->set(0, 3, 0); $matrix->set(0, 4, 0); $matrix->set(0, 5, 1); $this->assertEquals(3, MaskUtil::applyMaskPenaltyRule1($matrix)); $matrix->set(0, 5, 0); $this->assertEquals(4, MaskUtil::applyMaskPenaltyRule1($matrix)); } public function testApplyMaskPenaltyRule2() { $matrix = new ByteMatrix(1, 1); $matrix->set(0, 0, 0); $this->assertEquals(0, MaskUtil::applyMaskPenaltyRule2($matrix)); $matrix = new ByteMatrix(2, 2); $matrix->set(0, 0, 0); $matrix->set(1, 0, 0); $matrix->set(0, 1, 0); $matrix->set(1, 1, 1); $this->assertEquals(0, MaskUtil::applyMaskPenaltyRule2($matrix)); $matrix = new ByteMatrix(2, 2); $matrix->set(0, 0, 0); $matrix->set(1, 0, 0); $matrix->set(0, 1, 0); $matrix->set(1, 1, 0); $this->assertEquals(3, MaskUtil::applyMaskPenaltyRule2($matrix)); $matrix = new ByteMatrix(3, 3); $matrix->set(0, 0, 0); $matrix->set(1, 0, 0); $matrix->set(2, 0, 0); $matrix->set(0, 1, 0); $matrix->set(1, 1, 0); $matrix->set(2, 1, 0); $matrix->set(0, 2, 0); $matrix->set(1, 2, 0); $matrix->set(2, 2, 0); $this->assertEquals(3 * 4, MaskUtil::applyMaskPenaltyRule2($matrix)); } public function testApplyMaskPenalty3() { // Horizontal 00001011101 $matrix = new ByteMatrix(11, 1); $matrix->set(0, 0, 0); $matrix->set(1, 0, 0); $matrix->set(2, 0, 0); $matrix->set(3, 0, 0); $matrix->set(4, 0, 1); $matrix->set(5, 0, 0); $matrix->set(6, 0, 1); $matrix->set(7, 0, 1); $matrix->set(8, 0, 1); $matrix->set(9, 0, 0); $matrix->set(10, 0, 1); $this->assertEquals(40, MaskUtil::applyMaskPenaltyRule3($matrix)); // Horizontal 10111010000 $matrix = new ByteMatrix(11, 1); $matrix->set(0, 0, 1); $matrix->set(1, 0, 0); $matrix->set(2, 0, 1); $matrix->set(3, 0, 1); $matrix->set(4, 0, 1); $matrix->set(5, 0, 0); $matrix->set(6, 0, 1); $matrix->set(7, 0, 0); $matrix->set(8, 0, 0); $matrix->set(9, 0, 0); $matrix->set(10, 0, 0); $this->assertEquals(40, MaskUtil::applyMaskPenaltyRule3($matrix)); // Vertical 00001011101 $matrix = new ByteMatrix(1, 11); $matrix->set(0, 0, 0); $matrix->set(0, 1, 0); $matrix->set(0, 2, 0); $matrix->set(0, 3, 0); $matrix->set(0, 4, 1); $matrix->set(0, 5, 0); $matrix->set(0, 6, 1); $matrix->set(0, 7, 1); $matrix->set(0, 8, 1); $matrix->set(0, 9, 0); $matrix->set(0, 10, 1); $this->assertEquals(40, MaskUtil::applyMaskPenaltyRule3($matrix)); // Vertical 10111010000 $matrix = new ByteMatrix(1, 11); $matrix->set(0, 0, 1); $matrix->set(0, 1, 0); $matrix->set(0, 2, 1); $matrix->set(0, 3, 1); $matrix->set(0, 4, 1); $matrix->set(0, 5, 0); $matrix->set(0, 6, 1); $matrix->set(0, 7, 0); $matrix->set(0, 8, 0); $matrix->set(0, 9, 0); $matrix->set(0, 10, 0); $this->assertEquals(40, MaskUtil::applyMaskPenaltyRule3($matrix)); } public function testApplyMaskPenaltyRule4() { // Dark cell ratio = 0% $matrix = new ByteMatrix(1, 1); $matrix->set(0, 0, 0); $this->assertEquals(100, MaskUtil::applyMaskPenaltyRule4($matrix)); // Dark cell ratio = 5% $matrix = new ByteMatrix(2, 1); $matrix->set(0, 0, 0); $matrix->set(0, 0, 1); $this->assertEquals(0, MaskUtil::applyMaskPenaltyRule4($matrix)); // Dark cell ratio = 66.67% $matrix = new ByteMatrix(6, 1); $matrix->set(0, 0, 0); $matrix->set(1, 0, 1); $matrix->set(2, 0, 1); $matrix->set(3, 0, 1); $matrix->set(4, 0, 1); $matrix->set(5, 0, 0); $this->assertEquals(30, MaskUtil::applyMaskPenaltyRule4($matrix)); } } tests/bootstrap.php 0000666 00000000446 13436753160 0010454 0 ustar 00