123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- <?php
- /**
- * This file is part of the phpQr package
- *
- * See @see QRCode class for description of package and license.
- */
- /**
- * Import necessary dependencies
- */
- require_once 'QRCodeException.php';
- require_once 'QRPolynominal.php';
- /**
- * Derived exception
- *
- * @author Maik Greubel <[email protected]>
- * @package phpQr
- */
- class QRUtilException extends QRCodeException {};
- /**
- * Mask pattern enumeration
- *
- * @author Maik Greubel <[email protected]>
- * @package phpQr
- */
- abstract class QRMaskPattern
- {
- const PATTERN000 = 0;
- const PATTERN001 = 1;
- const PATTERN010 = 2;
- const PATTERN011 = 3;
- const PATTERN100 = 4;
- const PATTERN101 = 5;
- const PATTERN110 = 6;
- const PATTERN111 = 7;
- }
- /**
- * The purpose of this class is to provide some common utility
- * functionality for the QRCode class and its parts.
- *
- * @author Maik Greubel <[email protected]>
- * @package phpQr
- */
- class QRUtil
- {
- /**
- * Pattern position table
- *
- * @var array
- */
- private $PATTERN_POSITION_TABLE = null;
-
- /**
- *
- * @var int G15 pattern
- */
- private $G15;
-
- /**
- *
- * @var int G18 pattern
- */
- private $G18;
-
- /**
- *
- * @var int G15 mask pattern
- */
- private $G15_MASK;
-
- /**
- *
- * @var QRUtil Singleton
- */
- private static $instance;
-
- /**
- * Singleton pattern
- *
- * @return QRUtil
- */
- public static function getInstance()
- {
- if(!self::$instance)
- {
- self::$instance = new self;
- }
-
- return self::$instance;
- }
-
- /**
- * Create a new instance of QRUtil
- */
- private function __construct()
- {
- $this->init();
- $this->G15 = ((1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0));
- $this->G18 = ((1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0));
- $this->G15_MASK = ((1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1));
- }
-
- /**
- * Retrieve the Bose-Chaudhuri-Hocquenghem code type info
- *
- * @param array $data
- *
- * @return int
- */
- public function getBCHTypeInfo($data)
- {
- $d = $data << 10;
- while($this->getBCHDigit($d) - $this->getBCHDigit($this->G15) >= 0)
- {
- $d ^= ($this->G15 << ($this->getBCHDigit($d) - $this->getBCHDigit($this->G15)));
- }
-
- return (($data << 10) | $d) ^ $this->G15_MASK;
- }
-
- /**
- * Retrieve the Bose-Chaudhuri-Hocquenghem code type number
- *
- * @param array $data
- *
- * @return int
- */
- public function getBCHTypeNumber($data)
- {
- $d = $data << 12;
- while($this->getBCHDigit($d) - $this->getBCHDigit($this->G18) >= 0)
- {
- $d ^= ($this->G18 << ($this->getBCHDigit($d) - $this->getBCHDigit($this->G18)));
- }
- return ($data << 12) | $d;
- }
-
- /**
- * Retrieve the Bose-Chaudhuri-Hocquenghem digit
- *
- * @param array $data
- *
- * @return int
- */
- public function getBCHDigit($data)
- {
- $digit = 0;
- while($data != 0)
- {
- $digit++;
- $data = $data >> 1;
- }
-
- return $digit;
- }
-
- /**
- * Return the pattern position
- *
- * @param int $typeNumber
- * @return array
- */
- public function getPatternPosition($typeNumber)
- {
- return $this->PATTERN_POSITION_TABLE[$typeNumber - 1];
- }
-
- /**
- * Return whether to mask a bit
- *
- * @param int $maskPattern
- * @param int $i
- * @param int $j
- * @throws QRUtilException
- * @return boolean
- */
- public function getMask($maskPattern, $i, $j)
- {
- switch($maskPattern)
- {
- case QRMaskPattern::PATTERN000: return ($i + $j) % 2 == 0;
- case QRMaskPattern::PATTERN001: return ($i % 2) == 0;
- case QRMaskPattern::PATTERN010: return ($j % 3) == 0;
- case QRMaskPattern::PATTERN011: return ($i + $j) % 3 == 0;
- case QRMaskPattern::PATTERN100: return (floor($i / 2) + floor($j / 3)) % 2 == 0;
- case QRMaskPattern::PATTERN101: return ($i * $j) % 2 + ($i * $j) % 3 == 0;
- case QRMaskPattern::PATTERN110: return (($i * $j) % 2 + ($i * $j) % 3) % 2 == 0;
- case QRMaskPattern::PATTERN111: return (($i * $j) % 3 + ($i + $j) % 2) % 2 == 0;
-
- default: throw new QRUtilException("Bad mask pattern " . $maskPattern);
- }
- }
-
- /**
- * Return error correction polynom
- *
- * @param int $errorCorrectLength
- * @return QRPolynominal
- */
- public function getErrorCorrectPolynominal($errorCorrectLength)
- {
- $a = new QRPolynominal(array(1), 0);
- for($i = 0; $i < $errorCorrectLength; $i++)
- {
- $a = $a->multiply(new QRPolynominal(array(1, QRMath::getInstance()->gexp($i)), 0));
- }
-
- return $a;
- }
-
- /**
- * Get the bitmap length in bits
- *
- * @param int $mode
- * @param int $type
- * @throws QRUtilException
- * @return int
- */
- public function getLengthInBits($mode, $type)
- {
- // 1 - 9
- if(1 <= $type && $type < 10)
- {
- switch($mode)
- {
- case QRMode::MODE_NUMBER: return 10;
- case QRMode::MODE_ALPHA_NUM: return 9;
- case QRMode::MODE_8BIT_BYTE: return 8;
- case QRMode::MODE_KANJI: return 8;
- default: throw new QRUtilException("Bad mode " . $mode);
- }
- }
- // 10 - 26
- else if($type < 27)
- {
- switch($mode)
- {
- case QRMode::MODE_NUMBER: return 12;
- case QRMode::MODE_ALPHA_NUM: return 11;
- case QRMode::MODE_8BIT_BYTE: return 16;
- case QRMode::MODE_KANJI: return 10;
- default: throw new QRUtilException("Bad mode " . $mode);
- }
- }
- // 27 - 40
- else if($type < 41)
- {
- switch($mode)
- {
- case QRMode::MODE_NUMBER: return 14;
- case QRMode::MODE_ALPHA_NUM: return 13;
- case QRMode::MODE_8BIT_BYTE: return 16;
- case QRMode::MODE_KANJI: return 12;
- default: throw new QRUtilException("Bad mode " . $mode);
-
- }
- }
- else
- {
- throw new QRUtilException("Bad type " . $type);
- }
- }
-
- /**
- * Calculate the lost point
- *
- * @param QRCode $qrCode
- *
- * @return number
- */
- public function getLostPoint(QRCode $qrCode)
- {
- $moduleCount = $qrCode->getModuleCount();
-
- $lostPoint = 0;
-
- // Level 1
- for($row = 0; $row < $moduleCount; $row++)
- {
- for($col = 0; $col < $moduleCount; $col++)
- {
- $sameCount = 0;
- $dark = $qrCode->isDark($row, $col);
-
- for($r = -1; $r <= 1; $r++)
- {
- if($row + $r < 0 || $moduleCount <= $row + $r)
- {
- continue;
- }
-
- for($c = -1; $c <= 1; $c++)
- {
- if($col + $c < 0 || $moduleCount <= $col + $c)
- {
- continue;
- }
-
- if($r == 0 && $c == 0)
- {
- continue;
- }
-
- if($dark == $qrCode->isDark($row + $r, $col + $c))
- {
- $sameCount++;
- }
- }
- }
-
- if($sameCount > 5)
- {
- $lostPoint += (3 + $sameCount - 5);
- }
- }
- }
-
- // Level 2
- for($row = 0; $row < $moduleCount - 1; $row++)
- {
- for($col = 0; $col < $moduleCount - 1; $col++)
- {
- $count = 0;
- if($qrCode->isDark($row, $col )) $count++;
- if($qrCode->isDark($row + 1, $col )) $count++;
- if($qrCode->isDark($row, $col + 1)) $count++;
- if($qrCode->isDark($row + 1, $col + 1)) $count++;
- if($count == 0 || $count == 4)
- {
- $lostPoint += 3;
- }
- }
- }
-
- // Level 3
- for($row = 0; $row < $moduleCount; $row++)
- {
- for($col = 0; $col < $moduleCount - 6; $col++)
- {
- if($qrCode->isDark($row, $col)
- && !$qrCode->isDark($row, $col + 1)
- && $qrCode->isDark($row, $col + 2)
- && $qrCode->isDark($row, $col + 3)
- && $qrCode->isDark($row, $col + 4)
- && !$qrCode->isDark($row, $col + 5)
- && $qrCode->isDark($row, $col + 6))
- {
- $lostPoint += 40;
- }
- }
- }
-
- for($col = 0; $col < $moduleCount; $col++)
- {
- for($row = 0; $row < $moduleCount - 6; $row++)
- {
- if($qrCode->isDark($row, $col)
- && !$qrCode->isDark($row + 1, $col)
- && $qrCode->isDark($row + 2, $col)
- && $qrCode->isDark($row + 3, $col)
- && $qrCode->isDark($row + 4, $col)
- && !$qrCode->isDark($row + 5, $col)
- && $qrCode->isDark($row + 6, $col))
- {
- $lostPoint += 40;
- }
- }
- }
-
- // Level 4
- $darkCount = 0;
-
- for($col = 0; $col < $moduleCount; $col++)
- {
- for($row = 0; $row < $moduleCount; $row++)
- {
- if($qrCode->isDark($row, $col))
- {
- $darkCount++;
- }
- }
- }
-
- $ratio = abs(100 * $darkCount / $moduleCount / $moduleCount - 50) / 5;
- $lostPoint += $ratio * 10;
-
- return $lostPoint;
- }
-
- /**
- * Initialize the pattern position table
- */
- private function init()
- {
- $this->PATTERN_POSITION_TABLE = array();
-
- $this->addPattern(array());
- $this->addPattern(array(6, 18));
- $this->addPattern(array(6, 22));
- $this->addPattern(array(6, 26));
- $this->addPattern(array(6, 30));
- $this->addPattern(array(6, 34));
- $this->addPattern(array(6, 22, 38));
- $this->addPattern(array(6, 24, 42));
- $this->addPattern(array(6, 26, 46));
- $this->addPattern(array(6, 28, 50));
- $this->addPattern(array(6, 30, 54));
- $this->addPattern(array(6, 32, 58));
- $this->addPattern(array(6, 34, 62));
- $this->addPattern(array(6, 26, 46, 66));
- $this->addPattern(array(6, 26, 48, 70));
- $this->addPattern(array(6, 26, 50, 74));
- $this->addPattern(array(6, 30, 54, 78));
- $this->addPattern(array(6, 30, 56, 82));
- $this->addPattern(array(6, 30, 58, 86));
- $this->addPattern(array(6, 34, 62, 90));
- $this->addPattern(array(6, 28, 50, 72, 94));
- $this->addPattern(array(6, 26, 50, 74, 98));
- $this->addPattern(array(6, 30, 54, 78, 102));
- $this->addPattern(array(6, 28, 54, 80, 106));
- $this->addPattern(array(6, 32, 58, 84, 110));
- $this->addPattern(array(6, 30, 58, 86, 114));
- $this->addPattern(array(6, 34, 62, 90, 118));
- $this->addPattern(array(6, 26, 50, 74, 98, 122));
- $this->addPattern(array(6, 30, 54, 78, 102, 126));
- $this->addPattern(array(6, 26, 52, 78, 104, 130));
- $this->addPattern(array(6, 30, 56, 82, 108, 134));
- $this->addPattern(array(6, 34, 60, 86, 112, 138));
- $this->addPattern(array(6, 30, 58, 86, 114, 142));
- $this->addPattern(array(6, 34, 62, 90, 118, 146));
- $this->addPattern(array(6, 30, 54, 78, 102, 126, 150));
- $this->addPattern(array(6, 24, 50, 76, 102, 128, 154));
- $this->addPattern(array(6, 28, 54, 80, 106, 132, 158));
- $this->addPattern(array(6, 32, 58, 84, 110, 136, 162));
- $this->addPattern(array(6, 26, 54, 82, 110, 138, 166));
- $this->addPattern(array(6, 30, 58, 86, 114, 142, 170));
- }
-
- /**
- * Add a pattern to the pattern position table
- *
- * @param array $d
- */
- private function addPattern($d)
- {
- array_push($this->PATTERN_POSITION_TABLE, $d);
- }
-
- /**
- * Create an empty array of n elements
- *
- * All elements are uninitialed.
- *
- * @param int $numElements
- * @return array
- */
- public function createEmptyArray($numElements)
- {
- return array_fill(0, $numElements, null);
- }
- }
|