*/ /** * Import necessary dependencies */ require_once 'QR8bitByte.php'; require_once 'QRBitBuffer.php'; require_once 'QRRSBlock.php'; require_once 'QRUtil.php'; require_once 'QRCodeException.php'; /** * This is the main class * * It provides the functionality to generate a QRCode bitmap * out of appended data elements. * * @package phpQr * @author Maik Greubel */ class QRCode { /** * Needed for padding * * @final * */ const PAD0 = 0xec; /** * Needed for padding * * @final * */ const PAD1 = 0x11; /** * The type number of qrcode * * @var int */ private $typeNumber; /** * Level of error correction * * @see QRErrorCorrectLevel * * @var int */ private $errorCorrectLevel; /** * Bitmap * * @var array */ private $modules; /** * Amount of modules in bitmap * * @var int */ private $moduleCount; /** * The data as array * * @var array */ private $dataCache; /** * All append data elements * * @var array */ private $dataList; /** * Create a new instance of QRCode * * @param int $typeNumber * The type of QRCode * @param int $errorCorrectLevel * The error correction level */ public function __construct($typeNumber, $errorCorrectLevel) { $this->typeNumber = $typeNumber; $this->errorCorrectLevel = $errorCorrectLevel; $this->modules = null; $this->moduleCount = 0; $this->dataCache = null; $this->dataList = array (); } /** * This function is only needed for debugging purposes and returns the bitmap * DONT USE THIS TO MANIPULATE THE BITMAP! * * @return array */ public function getModules() { return $this->modules; } /** * Add a new data element to the QRCode * * @param string $data */ public function addData($data) { $newData = new QR8bitByte ( $data ); array_push ( $this->dataList, $newData ); $this->dataCache = null; } /** * Returns whether a given bitmap entry is dark or not * * @param int $row * The row in bitmap * @param int $col * The column in bitmap * * @throws QRCodeException * @return true in case of its a dark bit, false otherwise */ public function isDark($row, $col) { if ($row < 0 || $this->moduleCount <= $row || $col < 0 || $this->moduleCount <= $col) { throw new QRCodeException ( "$row,$col" ); } return $this->modules [$row] [$col]; } /** * Get the amount of modules in bitmap * * @return int */ public function getModuleCount() { return $this->moduleCount; } /** * Generate the QRCode bitmap */ public function make() { if ($this->typeNumber < 1) { $typeNumber = 1; for($typeNumber = 1; $typeNumber < 40; $typeNumber ++) { $rsBlocks = QRRSBlock::getInstance ()->getRSBlocks ( $typeNumber, $this->errorCorrectLevel ); $buffer = new QRBitBuffer (); $totalDataCount = 0; for($i = 0; $i < sizeof ( $rsBlocks ); $i ++) { $totalDataCount += $rsBlocks [$i]->getDataCount (); } for($i = 0; $i < sizeof ( $this->dataList ); $i ++) { $data = $this->dataList [$i]; assert ( $data instanceof QRByte ); $buffer->put ( $data->getMode (), 4 ); $buffer->put ( $data->getLength (), QRUtil::getInstance ()->getLengthInBits ( $data->getMode (), $typeNumber ) ); $data->write ( $buffer ); } if ($buffer->getLengthInBits () <= $totalDataCount * 8) break; } $this->typeNumber = $typeNumber; } $this->makeImpl ( false, $this->getBestMaskPattern () ); } /** * Generates the bitmap (really) * * @param boolean $test * @param int $maskPattern */ private function makeImpl($test, $maskPattern) { $this->moduleCount = $this->typeNumber * 4 + 17; $this->modules = QRUtil::getInstance ()->createEmptyArray ( $this->moduleCount ); for($row = 0; $row < $this->moduleCount; $row ++) { $this->modules [$row] = QRUtil::getInstance ()->createEmptyArray ( $this->moduleCount ); for($col = 0; $col < $this->moduleCount; $col ++) { $this->modules [$row] [$col] = null; } } $this->setupPositionProbePattern ( 0, 0 ); $this->setupPositionProbePattern ( $this->moduleCount - 7, 0 ); $this->setupPositionProbePattern ( 0, $this->moduleCount - 7 ); $this->setupPositionAdjustPattern (); $this->setupTimingPattern (); $this->setupTypeInfo ( $test, $maskPattern ); if ($this->typeNumber >= 7) { $this->setTypeNumber ( $test ); } if ($this->dataCache == null) { $this->dataCache = self::createData ( $this->typeNumber, $this->errorCorrectLevel, $this->dataList ); } $this->mapData ( $this->dataCache, $maskPattern ); } /** * Add the position probes to the bitmap * * @param int $row * @param int $col */ private function setupPositionProbePattern($row, $col) { for($r = - 1; $r <= 7; $r ++) { if ($row + $r <= - 1 || $this->moduleCount <= $row + $r) continue; for($c = - 1; $c <= 7; $c ++) { if ($col + $c <= - 1 || $this->moduleCount <= $col + $c) continue; if ((0 <= $r && $r <= 6 && ($c == 0 || $c == 6)) || (0 <= $c && $c <= 6 && ($r == 0 || $r == 6)) || (2 <= $r && $r <= 4 && 2 <= $c && $c <= 4)) { $this->modules [$row + $r] [$col + $c] = true; } else { $this->modules [$row + $r] [$col + $c] = false; } } } } /** * Get the best mask pattern for this QRCode * * @return int */ private function getBestMaskPattern() { $minLostPoint = 0; $pattern = 0; for($i = 0; $i < 8; $i ++) { $this->makeImpl ( true, $i ); $lostPoint = QRUtil::getInstance ()->getLostPoint ( $this ); if ($i == 0 || $minLostPoint > $lostPoint) { $minLostPoint = $lostPoint; $pattern = $i; } } return $pattern; } /** * Add the timing pattern to bitmap */ private function setupTimingPattern() { for($r = 8; $r < $this->moduleCount - 8; $r ++) { if ($this->modules [$r] [6] != null) { continue; } $this->modules [$r] [6] = ($r % 2 == 0); } for($c = 8; $c < $this->moduleCount - 8; $c ++) { if ($this->modules [6] [$c] != null) { continue; } $this->modules [6] [$c] = ($c % 2 == 0); } } /** * Add the position adjust pattern to bitmap */ private function setupPositionAdjustPattern() { $pos = QRUtil::getInstance ()->getPatternPosition ( $this->typeNumber ); for($i = 0; $i < sizeof ( $pos ); $i ++) { for($j = 0; $j < sizeof ( $pos ); $j ++) { $row = $pos [$i]; $col = $pos [$j]; if ($this->modules [$row] [$col] != null) { continue; } for($r = - 2; $r <= 2; $r ++) { for($c = - 2; $c <= 2; $c ++) { if ($r == - 2 || $r == 2 || $c == - 2 || $c == 2 || ($r == 0 && $c == 0)) { $this->modules [$row + $r] [$col + $c] = true; } else { $this->modules [$row + $r] [$col + $c] = false; } } } } } } /** * Add the type number to bitmap * * @param boolean $test */ private function setTypeNumber($test) { $bits = QRUtil::getInstance ()->getBCHTypeNumber ( $this->typeNumber ); for($i = 0; $i < 18; $i ++) { $mod = (! $test && (($bits >> $i) & 1) == 1); $this->modules [floor ( $i / 3 )] [$i % 3 + $this->moduleCount - 8 - 3] = $mod; } for($i = 0; $i < 18; $i ++) { $mod = (! $test && (($bits >> $i) & 1) == 1); $this->modules [$i % 3 + $this->moduleCount - 8 - 3] [floor ( $i / 3 )] = $mod; } } /** * Add the type info to bitmap * * @param boolean $test * @param int $maskPattern */ private function setupTypeInfo($test, $maskPattern) { $data = ($this->errorCorrectLevel << 3) | $maskPattern; $bits = QRUtil::getInstance ()->getBCHTypeInfo ( $data ); // vertical for($i = 0; $i < 15; $i ++) { $mod = (! $test && (($bits >> $i) & 1) == 1); if ($i < 6) { $this->modules [$i] [8] = $mod; } else if ($i < 8) { $this->modules [$i + 1] [8] = $mod; } else { $this->modules [$this->moduleCount - 15 + $i] [8] = $mod; } } // horizontal for($i = 0; $i < 15; $i ++) { $mod = (! $test && (($bits >> $i) & 1) == 1); if ($i < 8) { $this->modules [8] [$this->moduleCount - $i - 1] = $mod; } else if ($i < 9) { $this->modules [8] [15 - $i - 1 + 1] = $mod; } else { $this->modules [8] [15 - $i - 1] = $mod; } } // fixed module $this->modules [$this->moduleCount - 8] [8] = (! $test); } /** * Add the data to bitmap * * @param array $data * @param int $maskPattern */ private function mapData($data, $maskPattern) { $inc = - 1; $row = $this->moduleCount - 1; $bitIndex = 7; $byteIndex = 0; for($col = $this->moduleCount - 1; $col > 0; $col -= 2) { if ($col == 6) $col --; while ( true ) { for($c = 0; $c < 2; $c ++) { if ($this->modules [$row] [$col - $c] === null) { $dark = false; if ($byteIndex < sizeof ( $data )) { $dark = ((($data [$byteIndex] >> $bitIndex) & 1) == 1); } $mask = QRUtil::getInstance ()->getMask ( $maskPattern, $row, $col - $c ); if ($mask) $dark = ! $dark; $this->modules [$row] [$col - $c] = $dark; $bitIndex --; if ($bitIndex == - 1) { $byteIndex ++; $bitIndex = 7; } } } $row += $inc; if ($row < 0 || $this->moduleCount <= $row) { $row -= $inc; $inc = - $inc; break; } } } } /** * Create a bitmap out of all append data elements * * @param int $typeNumber * @param int $errorCorrectLevel * @param array $dataList * * @throws QRCodeException * * @return array */ private function createData($typeNumber, $errorCorrectLevel, $dataList) { $rsBlocks = QRRSBlock::getInstance ()->getRSBlocks ( $typeNumber, $errorCorrectLevel ); $buffer = new QRBitBuffer (); for($i = 0; $i < sizeof ( $dataList ); $i ++) { $data = $dataList [$i]; assert ( $data instanceof QRByte ); $buffer->put ( $data->getMode (), 4 ); $buffer->put ( $data->getLength (), QRUtil::getInstance ()->getLengthInBits ( $data->getMode (), $typeNumber ) ); $data->write ( $buffer ); } // calc num max data $totalDataCount = 0; for($i = 0; $i < sizeof ( $rsBlocks ); $i ++) { $totalDataCount += $rsBlocks [$i]->getDataCount (); } if ($buffer->getLengthInBits () > $totalDataCount * 8) { throw new QRCodeException ( "code length overflow (" . $buffer->getLengthInBits () . " > " . ($totalDataCount * 8) . ")" ); } // end code if ($buffer->getLengthInBits () + 4 <= $totalDataCount * 8) { $buffer->put ( 0, 4 ); } // padding while ( $buffer->getLengthInBits () % 8 != 0 ) { $buffer->putBit ( false ); } // padding while ( true ) { if ($buffer->getLengthInBits () >= $totalDataCount * 8) { break; } $buffer->put ( QRCode::PAD0, 8 ); if ($buffer->getLengthInBits () >= $totalDataCount * 8) { break; } $buffer->put ( QRCode::PAD1, 8 ); } return $this->createBytes ( $buffer, $rsBlocks ); } /** * Create bitmap out of the bit buffer using reed solomon blocks * * @param QRBitBuffer $buffer * @param array $rsBlocks * @return array */ public function createBytes(QRBitBuffer $buffer, $rsBlocks) { $offset = 0; $maxDcCount = 0; $maxEcCount = 0; $dcdata = QRUtil::getInstance ()->createEmptyArray ( sizeof ( $rsBlocks ) ); $ecdata = QRUtil::getInstance ()->createEmptyArray ( sizeof ( $rsBlocks ) ); for($r = 0; $r < sizeof ( $rsBlocks ); $r ++) { $dcCount = $rsBlocks [$r]->getDataCount (); $ecCount = $rsBlocks [$r]->getTotalCount () - $dcCount; $maxDcCount = max ( array ( $maxDcCount, $dcCount ) ); $maxEcCount = max ( array ( $maxEcCount, $ecCount ) ); $dcdata [$r] = QRUtil::getInstance ()->createEmptyArray ( $dcCount ); for($i = 0; $i < sizeof ( $dcdata [$r] ); $i ++) { $dcdata [$r] [$i] = 0xff & $buffer->getAt ( $i + $offset ); } $offset += $dcCount; $rsPoly = QRUtil::getInstance ()->getErrorCorrectPolynominal ( $ecCount ); $rawPoly = new QRPolynominal ( $dcdata [$r], $rsPoly->getLength () - 1 ); $modPoly = $rawPoly->mod ( $rsPoly ); $ecdata [$r] = QRUtil::getInstance ()->createEmptyArray ( $rsPoly->getLength () - 1 ); for($i = 0; $i < sizeof ( $ecdata [$r] ); $i ++) { $modIndex = $i + $modPoly->getLength () - sizeof ( $ecdata [$r] ); $ecdata [$r] [$i] = ($modIndex >= 0) ? $modPoly->get ( $modIndex ) : 0; } } $totalCodeCount = 0; for($i = 0; $i < sizeof ( $rsBlocks ); $i ++) { $totalCodeCount += $rsBlocks [$i]->getTotalCount (); } $data = QRUtil::getInstance ()->createEmptyArray ( $totalCodeCount ); $index = 0; for($i = 0; $i < $maxDcCount; $i ++) { for($r = 0; $r < sizeof ( $rsBlocks ); $r ++) { if ($i < sizeof ( $dcdata [$r] )) { $data [$index ++] = $dcdata [$r] [$i]; } } } for($i = 0; $i < $maxEcCount; $i ++) { for($r = 0; $r < sizeof ( $rsBlocks ); $r ++) { if ($i < sizeof ( $ecdata [$r] )) { $data [$index ++] = $ecdata [$r] [$i]; } } } return $data; } } //yea will need to inspect this for the ldap qr code