瀏覽代碼

Add a BSD licensed QR code library

markjcrane 7 年之前
父節點
當前提交
6289ba805d

二進制
resources/qr_code/.phpqrcode.php.swp


+ 81 - 0
resources/qr_code/QR8bitByte.php

@@ -0,0 +1,81 @@
+<?php
+/**
+ * This file is part of the phpQr package
+ * 
+ * See @see QRCode class for description of package and license.
+ */
+
+/**
+ * Import necessary dependencies
+ */
+require_once 'QRByte.php';
+require_once 'QRMode.php';
+
+/**
+ * This class provides the 8bit Byte implementaton of a QRByte
+ * 
+ * @author Maik Greubel <[email protected]>
+ * @package phpQr
+ */
+class QR8bitByte implements QRByte
+{
+  /**
+   * The data
+   * @var array
+   */
+  private $data;
+  
+  /**
+   * The mode
+   * @var unknown
+   */
+  private $mode;
+  
+  /**
+   * Retrieve the mode
+   * 
+   * @return int The mode
+   * @see QRByte::getMode()
+   */
+  public function getMode()
+  {
+    return $this->mode;
+  }
+  
+  /**
+   * Retrieve the length
+   * 
+   * @return int The length
+   * @see QRByte::getLength()
+   */
+  public function getLength()
+  {
+    return strlen($this->data);    
+  }
+  
+  /**
+   * Write data to byte
+   * 
+   * @param QRBitBuffer $buffer The data to write into byte
+   * 
+   * @see QRByte::write()
+   */
+  public function write(QRBitBuffer $buffer)
+  {
+    for($i = 0; $i < strlen($this->data); $i++)
+    {
+      $buffer->put(ord($this->data[$i]), 8);
+    }
+  }
+  
+  /**
+   * Create a new instance of a QR8bitByte
+   * 
+   * @param array $data The data for the Byte
+   */
+  public function __construct($data)
+  {
+    $this->data = $data;
+    $this->mode = QRMode::MODE_8BIT_BYTE;
+  }
+}

+ 107 - 0
resources/qr_code/QRBitBuffer.php

@@ -0,0 +1,107 @@
+<?php
+/**
+ * This file is part of the phpQr package
+ *
+ * See @see QRCode class for description of package and license.
+ */
+
+/**
+ * QRBitBuffer class
+ * 
+ * The purpose of this class is to act as data holder for QRCode.
+ * 
+ * @author Maik Greubel <[email protected]>
+ * @package phpQr
+ */
+class QRBitBuffer
+{
+  /**
+   * Array based buffer access
+   * 
+   * @var array
+   */
+  private $buffer;
+  
+  /**
+   * Length of array
+   * 
+   * @var int
+   */
+  private $length;
+  
+  /**
+   * Create a new instance of QRBitBuffer
+   */
+  public function __construct()
+  {
+    $this->buffer = array();
+    $this->length = 0;
+  }
+
+  /**
+   * Get particular bit given by index
+   * 
+   * @param int $index The index of bit
+   * @return boolean
+   */
+  public function get($index)
+  {
+    $bufIndex = floor($index / 8);
+    return ( ($this->buffer[$bufIndex] >> (7 - $index % 8) ) & 1) == 1;
+  }
+  
+  /**
+   * Get the byte at particular index
+   * 
+   * @param int $index The index of the byte
+   * @return string
+   */
+  public function getAt($index)
+  {
+    return $this->buffer[$index];
+  }
+  
+  /**
+   * Put amount of bits
+   * @param int $num The data to put
+   * @param int $length The length of data
+   */
+  public function put($num, $length)
+  {
+    for($i = 0; $i < $length; $i++)
+    {
+      $this->putBit((($num >> ($length - $i - 1)) & 1) == 1);
+    }
+  }
+  
+  /**
+   * Get current length in bits
+   * 
+   * @return int The amount of bits
+   */
+  public function getLengthInBits()
+  {
+    return $this->length;
+  }
+  
+  /**
+   * Put particular bit
+   * 
+   * @param int $bit The bit to put
+   */
+  public function putBit($bit)
+  {
+    $bufIndex = floor($this->length / 8);
+    if(sizeof($this->buffer) <= $bufIndex)
+    {
+      array_push($this->buffer, 0);
+    }
+    
+    if($bit)
+    {
+      $this->buffer[$bufIndex] |= (0x80 >> ($this->length % 8));
+    }
+    
+    $this->length++;
+  }
+}

+ 41 - 0
resources/qr_code/QRByte.php

@@ -0,0 +1,41 @@
+<?php
+/**
+ * This file is part of the phpQr package
+ *
+ * See @see QRCode class for description of package and license.
+ */
+
+/**
+ * Import necessary dependencies
+ */
+require_once 'QRBitBuffer.php';
+
+/**
+ * This interface describes a QRByte implementation
+ * 
+ * @author Maik Greubel <[email protected]>
+ * @package phpQr
+ */
+interface QRByte
+{
+  /**
+   * Retrieve the mode
+   * 
+   * @return  int The mode
+   */
+  public function getMode();
+  
+  /**
+   * Retrieve the length
+   * 
+   * @return int The length
+   */
+  public function getLength();
+  
+  /**
+   * Write data to byte
+   * 
+   * @param QRBitBuffer $buffer The data to write into byte
+   */
+  public function write(QRBitBuffer $buffer);
+}

+ 656 - 0
resources/qr_code/QRCode.php

@@ -0,0 +1,656 @@
+<?php
+/**
+ * This file provides the main QRCode class.
+ * 
+ * The project is a rewrite from jQuery extension available at
+ * @link https://github.com/jeromeetienne/jquery-qrcode
+ * 
+ * For a detailed description of QRCode and its features please check
+ * @link http://www.qrcode.com/
+ * 
+ * QR Code is registered trademark of
+ *  DENSO WAVE INCORPORATED
+ *  http://www.denso-wave.com/qrcode/faqpatent-e.html
+ *  
+ * All files in the package have the same license:
+ * http://opensource.org/licenses/BSD-2-Clause
+ * 
+ * @copyright BSD2
+ * @author Maik Greubel <[email protected]>
+ */
+
+/**
+ * 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 <[email protected]>
+ */
+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;
+  }
+}

+ 14 - 0
resources/qr_code/QRCodeException.php

@@ -0,0 +1,14 @@
+<?php
+/**
+ * This file is part of the phpQr package
+ *
+ * See @see QRCode class for description of package and license.
+ */
+
+/**
+ * A derived exception
+ *
+ * @author Maik Greubel <[email protected]>
+ * @package phpQr
+ */
+class QRCodeException extends ErrorException {};

+ 187 - 0
resources/qr_code/QRCodeImage.php

@@ -0,0 +1,187 @@
+<?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';
+
+/**
+ * Derived exception class
+ *  
+ * @author Maik Greubel <[email protected]>
+ * @package phpQr
+ */
+class QRCodeImageException extends QRCodeException{};
+
+/**
+ * This class provides all needed functionality to create an image out of an QRCode bitmap
+ * 
+ * @author Maik Greubel <[email protected]>
+ * @package phpQr
+ */
+class QRCodeImage
+{
+  /**
+   * The previously created QRCode
+   * 
+   * @var QRCode
+   */
+  private $qrcode;
+  
+  /**
+   * The desired width of the destination image
+   * 
+   * @var int
+   */
+  private $width;
+
+  /**
+   * The desired height of the destination image
+   * 
+   * @var int
+   */
+  private $height;
+  
+  /**
+   * Quality of the destination image
+   * 
+   * @var int
+   */
+  private $quality;
+  
+  /**
+   * The image buffer provided by GD function imagecreate()
+   * 
+   * @var resource
+   */
+  private $img;
+  
+  /**
+   * Create a new QRCodeImage instance
+   * 
+   * @param QRCode $qrcode The previously created QRCode
+   * @param int $width The desired width of the destination image
+   * @param int $height The desired height of the destination image
+   * @param int $quality The desired quality of the destination image
+   */
+  public function __construct(QRCode $qrcode, $width, $height, $quality = 90)
+  {
+    $this->qrcode = $qrcode;
+    $this->width  = $width;
+    $this->height = $height;
+    $this->quality = $quality;
+    $this->img    = null;
+  }
+  
+  /**
+   * Draw the image
+   */
+  public function draw()
+  {
+    $moduleCount = $this->qrcode->getModuleCount();
+    $tileWidth  = $this->width / $moduleCount;
+    $tileHeight = $this->height / $moduleCount;
+    
+    $this->img = imagecreatetruecolor($this->width, $this->height);
+    
+    if($this->img)
+    {
+      $fg = imagecolorallocate($this->img, 0, 0, 0);
+      if($fg === false)
+      {
+        $this->finish();
+        throw new QRCodeImageException('Could not allocate foreground color!');
+      }
+      $bg = imagecolorallocate($this->img, 255, 255, 255);
+      if($bg === false)
+      {
+        $this->finish();
+        throw new QRCodeImageException('Could not allocate background color!');
+      }
+      
+      for($row = 0; $row < $moduleCount; $row++)
+      {
+        for($col = 0; $col < $moduleCount; $col++)
+        {
+          $fillStyle = $this->qrcode->isDark($row, $col) ? $fg : $bg;
+          
+          $x = round($col * $tileWidth);
+          $y = round($row * $tileHeight);
+          $w = (ceil(($col + 1) * $tileWidth) - floor($col * $tileWidth));
+          if($x + $w > $this->width)
+          {
+            $w = $this->width - $x;
+          }
+          $h = (ceil(($row + 1) * $tileWidth) - floor($row * $tileWidth));
+          if($y + $h > $this->height)
+          {
+            $h = $this->height - $y;
+          }
+          
+          if(!imagefilledrectangle($this->img, $x, $y, $x + $w, $y + $h, $fillStyle))
+          {
+            $this->finish();
+            throw new QRCodeImageException(sprintf('Could not fill the rectangle using desired coordinates (x = %d, y = %d, w = %d, h = %d, c = %d)',
+                $x, $y, $w, $h, $fillStyle));
+          }
+        }
+      }
+    }
+    else
+    {
+      throw new QRCodeImageException('Could not create true color image buffer!');
+    }
+  }
+  
+  /**
+   * Store the image
+   * 
+   * @param string $filename
+   */
+  public function store($filename)
+  {
+    if($this->img)
+    {
+      if(!imagejpeg($this->img, $filename, $this->quality))
+      {
+        throw new QRCodeImageException(sprintf('Could not save image to file %s', $filename));
+      }
+    }
+  }
+  
+  /**
+   * Return the image as string
+   */
+  public function getImage()
+  {
+    if($this->img)
+    {
+      ob_start();
+      if(!imagejpeg($this->img, null, $this->quality))
+      {
+        ob_end_flush();
+        throw new QRCodeImageException('Could not create a jpeg out of the image buffer!');
+      }
+      $out = ob_get_clean();
+      return $out;
+    }
+    throw new QRCodeImageException('No image data available!');
+  }
+  
+  /**
+   * Clean the image buffer
+   */
+  public function finish()
+  {
+    if($this->img)
+    {
+      imagedestroy($this->img);
+      $this->img = null;
+    }
+  }
+}

+ 20 - 0
resources/qr_code/QRErrorCorrectLevel.php

@@ -0,0 +1,20 @@
+<?php
+/**
+ * This file is part of the phpQr package
+ *
+ * See @see QRCode class for description of package and license.
+ */
+
+/**
+ * Error correct level enumeration
+ *
+ * @author Maik Greubel <[email protected]>
+ * @package phpQr
+ */
+abstract class QRErrorCorrectLevel
+{
+  const L = 1;
+  const M = 0;
+  const Q = 3;
+  const H = 2;
+}

+ 145 - 0
resources/qr_code/QRMath.php

@@ -0,0 +1,145 @@
+<?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';
+
+/**
+ * Derived exception
+ * 
+ * @author Maik Greubel <[email protected]>
+ * @package phpQr
+ */
+class QRMathException extends QRCodeException
+{
+}
+
+/**
+ * QR Code math helper class
+ *
+ * @author Maik Greubel <[email protected]>
+ * @package phpQr
+ */
+final class QRMath
+{
+  /**
+   * Exponent table
+   *
+   * @var array
+   */
+  private $EXP_TABLE = null;
+  
+  /**
+   * Logarithm table
+   * 
+   * @var array
+   */
+  private $LOG_TABLE = null;
+  
+  /**
+   * Singleton pattern
+   * 
+   * @var QRMath
+   */
+  private static $instance = null;
+  
+  /**
+   * Singleton pattern
+   *
+   * @return QRMath Singleton
+   */
+  public static function getInstance()
+  {
+    if (! self::$instance)
+    {
+      self::$instance = new self ();
+    }
+    
+    return self::$instance;
+  }
+  
+  /**
+   * Create a new instance of QRMath
+   */
+  private function __construct()
+  {
+    $this->init ();
+  }
+  
+  /**
+   * Initialize the tables
+   */
+  private function init()
+  {
+    $this->EXP_TABLE = array ();
+    for($i = 0; $i < 8; $i ++)
+    {
+      $this->EXP_TABLE [$i] = 1 << $i;
+    }
+    
+    for($i = 8; $i < 256; $i ++)
+    {
+      $this->EXP_TABLE [$i] = $this->EXP_TABLE [$i - 4] ^ $this->EXP_TABLE [$i - 5] ^ $this->EXP_TABLE [$i - 6] ^ $this->EXP_TABLE [$i - 8];
+    }
+    
+    $this->LOG_TABLE = array ();
+    for($i = 0; $i < 255; $i ++)
+    {
+      $this->LOG_TABLE [$this->EXP_TABLE [$i]] = $i;
+    }
+  }
+  
+  /**
+   * Get logarithm of n
+   *
+   * @param int $n          
+   * @throws QRMathException
+   * @return int
+   */
+  public function glog($n)
+  {
+    if ($n < 1)
+    {
+      throw new QRMathException ( "glog(" . $n . ")" );
+    }
+    
+    foreach ( $this->LOG_TABLE as $key => $value )
+    {
+      if ($key == $n)
+        return $value;
+    }
+    
+    throw new QRMathException ( "glog($n)" );
+  }
+  
+  /**
+   * Get the exponent of n
+   *
+   * @param int $n          
+   * @return int
+   */
+  public function gexp($n)
+  {
+    while ( $n < 0 )
+    {
+      $n += 255;
+    }
+    while ( $n >= 256 )
+    {
+      $n -= 255;
+    }
+    foreach ( $this->EXP_TABLE as $key => $value )
+    {
+      if ($key == $n)
+        return $value;
+    }
+    
+    throw new QRMathException ( "gexp($n)" );
+  }
+} 

+ 20 - 0
resources/qr_code/QRMode.php

@@ -0,0 +1,20 @@
+<?php
+/**
+ * This file is part of the phpQr package
+ *
+ * See @see QRCode class for description of package and license.
+ */
+
+/**
+ * QRCode mode enumeration
+ * 
+ * @author Maik Greubel <[email protected]>
+ * @package phpQr
+ */
+abstract class QRMode
+{
+  const MODE_NUMBER = 1;
+  const MODE_ALPHA_NUM = 2;
+  const MODE_8BIT_BYTE = 4;
+  const MODE_KANJI = 8;
+}

+ 145 - 0
resources/qr_code/QRPolynominal.php

@@ -0,0 +1,145 @@
+<?php
+/**
+ * This file is part of the phpQr package
+ *
+ * See @see QRCode class for description of package and license.
+ */
+
+/**
+ * Import necessary dependencies
+ */
+require_once 'QRUtil.php';
+require_once 'QRMath.php';
+require_once 'QRCodeException.php';
+
+/**
+ * Derived exception
+ *
+ * @author Maik Greubel <[email protected]>
+ * @package phpQr
+ */
+class QRPolynominalException extends QRCodeException{};
+
+/**
+ * The purpose of this class is to provide a polynominal implementation for the QRCode package
+ * 
+ * @author Maik Greubel <[email protected]>
+ * @package phpQr
+ */
+class QRPolynominal
+{
+  /**
+   * Bitmap
+   * 
+   * @var array
+   */
+  private $num;
+  
+  /**
+   * Create a new QRPolynominal instance
+   * 
+   * @param array $num
+   * @param int $shift
+   * 
+   * @throws QRPolynominalException
+   */
+  public function __construct($num, $shift)
+  {
+    if(sizeof($num) == 0)
+    {
+      throw new QRPolynominalException("Invalid num size");
+    }
+    
+    $offset = 0;
+    while($offset < sizeof($num) && $num[$offset] == 0)
+    {
+      $offset++;
+    }
+    
+    $this->num = QRUtil::getInstance()->createEmptyArray(sizeof($num) - $offset + $shift);
+    for($i = 0; $i < sizeof($num) - $offset; $i++)
+    {
+      $this->num[$i] = $num[$i + $offset];
+    }
+  }
+  
+  /**
+   * Get a particular bitmap index
+   * 
+   * @param int $index
+   * @return multitype:
+   */
+  public function get($index)
+  {
+    return $this->num[$index];
+  }
+  
+  /**
+   * Get the length of bitmap
+   */
+  public function getLength()
+  {
+    return sizeof($this->num);
+  }
+  
+  /**
+   * Multiply another polynom against this
+   * 
+   * @param QRPolynominal $e The other
+   * @return QRPolynominal The multiplied result
+   */
+  public function multiply(QRPolynominal $e)
+  {
+    $num = QRUtil::getInstance()->createEmptyArray($this->getLength() + $e->getLength() - 1);
+    
+    for($i = 0; $i < $this->getLength(); $i++)
+    {
+      for($j = 0; $j < $e->getLength(); $j++)
+      {
+        $a = QRMath::getInstance()->glog($this->get($i));
+        $b = QRMath::getInstance()->glog($e->get($j));
+        
+        $base = 0;
+        if(isset($num[$i + $j]))
+          $base = $num[$i + $j];
+        $num[$i + $j] = $base ^ QRMath::getInstance()->gexp( $a + $b );
+      }
+    }
+    
+    return new QRPolynominal($num, 0);
+  }
+  
+  /**
+   * Perform modulus against another polynom
+   * 
+   * @param QRPolynominal $e
+   * 
+   * @return QRPolynominal
+   */
+  public function mod(QRPolynominal $e)
+  {
+    if($this->getLength() - $e->getLength() < 0)
+    {
+      return $this;
+    }
+    
+    $ratio = QRMath::getInstance()->glog($this->get(0)) - QRMath::getInstance()->glog($e->get(0));
+    
+    $num = QRUtil::getInstance()->createEmptyArray($this->getLength());
+    
+    for($i = 0; $i < $this->getLength(); $i++)
+    {
+      $num[$i] = $this->get($i);
+    }
+    
+    for($i = 0; $i < $e->getLength(); $i++)
+    {
+      $num[$i] ^= QRMath::getInstance()->gexp(QRMath::getInstance()->glog($e->get($i)) + $ratio);
+    }
+    
+    $result = new QRPolynominal($num, 0);
+    $result = $result->mod($e);
+    
+    return $result;
+  }
+}

+ 503 - 0
resources/qr_code/QRRSBlock.php

@@ -0,0 +1,503 @@
+<?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 'QRErrorCorrectLevel.php';
+
+/**
+ * Derived exception class
+ * 
+ * @author Maik Greubel <[email protected]>
+ * @package phpQr
+ */
+class QRRSBlockException extends QRCodeException
+{ 
+}
+
+/**
+ * This class is a Reed-Solomon implementation for the QRCode.
+ * The purpose is to provide error correction and block information.
+ *
+ * Inspired by qrcode.js from https://github.com/jeromeetienne/jquery-qrcode
+ * 
+ * @author Maik Greubel <[email protected]>
+ * @package phpQr
+ * @link http://www.thonky.com/qr-code-tutorial/error-correction-table/
+ */
+class QRRSBlock
+{
+  /**
+   * The total count of blocks
+   * 
+   * @var int The total count of blocks
+   */
+  private $totalCount;
+  
+  /**
+   * The data count of blocks
+   * 
+   * @var int The data count of blocks
+   */
+  private $dataCount;
+  
+  /**
+   * The block table
+   * @var array The block table
+   */
+  private $RS_BLOCK_TABLE;
+  
+  /**
+   * Singleton pattern
+   * 
+   * @var QRRSBlock Singleton
+   */
+  private static $instance;
+  
+  /**
+   * The serialized block data for faster initialization
+   * 
+   * @var string
+   */
+  private $blockFileName = 'rsblock.dat';
+  
+  /**
+   * Singleton pattern
+   * 
+   * @return QRRSBlock
+   */
+  public static function getInstance()
+  {
+    if(!self::$instance)
+    {
+      self::$instance = new self(0, 0);
+    }
+    
+    return self::$instance;
+  }
+  
+  /**
+   * Retrieve the data count
+   * 
+   * @return int The data count
+   */
+  public function getDataCount()
+  {
+    return $this->dataCount;
+  }
+  
+  /**
+   * Retrieve the total count
+   * 
+   * @return int The total count
+   */
+  public function getTotalCount()
+  {
+    return $this->totalCount;
+  }
+  
+  /**
+   * Create a new QR Reed-Solomon block instance
+   * 
+   * @param int $totalCount The total count of blocks
+   * @param int $dataCount The data count of blocks
+   */
+  private function __construct($totalCount, $dataCount)
+  {
+    $this->initRsBlock();
+    
+    $this->totalCount = $totalCount;
+    $this->dataCount  = $dataCount;
+  }
+  
+  /**
+   * Get rs blocks of particular type and error correction level
+   * 
+   * @param int $typeNumber
+   * @param int $errorCorrectLevel
+   * @throws QRRSBlockException
+   * @return QRRSBlock
+   */
+  public function getRSBlocks($typeNumber, $errorCorrectLevel)
+  {
+    $rsBlock = $this->getRsBlockTable($typeNumber, $errorCorrectLevel);
+    
+    if(!$rsBlock)
+    {
+      throw new QRRSBlockException("Bad RS Block at type number " . $typeNumber . " / error correct level " . $errorCorrectLevel);
+    }
+    
+    $length = sizeof($rsBlock) / 3;
+    
+    $list = array();
+    
+    for($i = 0; $i < $length; $i++)
+    {
+      $count = $rsBlock[$i * 3 + 0];
+      $totalCount = $rsBlock[$i * 3 + 1];
+      $dataCount = $rsBlock[$i * 3 + 2];
+      
+      for($j = 0; $j < $count; $j++)
+      {
+        array_push($list, new QRRSBlock($totalCount, $dataCount));
+      }
+    }
+    
+    return $list;
+  }
+  
+  /**
+   * Get the reed-solomon block table
+   * 
+   * @param int $typeNumber
+   * @param int $errorCorrectLevel
+   * @return int|NULL
+   */
+  public function getRsBlockTable($typeNumber, $errorCorrectLevel)
+  {
+    switch ($errorCorrectLevel)
+    {
+      case QRErrorCorrectLevel::L:
+        return $this->RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 0];
+      case QRErrorCorrectLevel::M:
+        return $this->RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 1];
+      case QRErrorCorrectLevel::Q:
+        return $this->RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 2];
+      case QRErrorCorrectLevel::H:
+        return $this->RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 3];
+      default:
+        return null;
+    }
+  }
+  
+  /**
+   * This method initialize the RS block
+   */
+  private function initRsBlock()
+  {
+    if($this->loadBlockFile())
+    {
+      return;
+    }
+    
+    $this->RS_BLOCK_TABLE = array();
+    
+    // L
+    // M
+    // Q
+    // H
+    
+    // 1
+    $this->addRsBlock(array(1, 26, 19));
+    $this->addRsBlock(array(1, 26, 16));
+    $this->addRsBlock(array(1, 26, 13));
+    $this->addRsBlock(array(1, 26, 9));
+    
+    // 2
+    $this->addRsBlock(array(1, 44, 34));
+    $this->addRsBlock(array(1, 44, 28));
+    $this->addRsBlock(array(1, 44, 22));
+    $this->addRsBlock(array(1, 44, 16));
+    
+    // 3
+    $this->addRsBlock(array(1, 70, 55));
+    $this->addRsBlock(array(1, 70, 44));
+    $this->addRsBlock(array(2, 35, 17));
+    $this->addRsBlock(array(2, 35, 13));
+    
+    // 4
+    $this->addRsBlock(array(1, 100, 80));
+    $this->addRsBlock(array(2, 50, 32));
+    $this->addRsBlock(array(2, 50, 24));
+    $this->addRsBlock(array(4, 25, 9));
+    
+    // 5
+    $this->addRsBlock(array(1, 134, 108));
+    $this->addRsBlock(array(2, 67, 43));
+    $this->addRsBlock(array(2, 33, 15, 2, 34, 16));
+    $this->addRsBlock(array(2, 33, 11, 2, 34, 12));
+    
+    // 6
+    $this->addRsBlock(array(2, 86, 68));
+    $this->addRsBlock(array(4, 43, 27));
+    $this->addRsBlock(array(4, 43, 19));
+    $this->addRsBlock(array(4, 43, 15));
+    
+    // 7
+    $this->addRsBlock(array(2, 98, 78));
+    $this->addRsBlock(array(4, 49, 31));
+    $this->addRsBlock(array(2, 32, 14, 4, 33, 15));
+    $this->addRsBlock(array(4, 39, 13, 1, 40, 14));
+    
+    // 8
+    $this->addRsBlock(array(2, 121, 97));
+    $this->addRsBlock(array(2, 60, 38, 2, 61, 39));
+    $this->addRsBlock(array(4, 40, 18, 2, 41, 19));
+    $this->addRsBlock(array(4, 40, 14, 2, 41, 15));
+    
+    // 9
+    $this->addRsBlock(array(2, 146, 116));
+    $this->addRsBlock(array(3, 58, 36, 2, 59, 37));
+    $this->addRsBlock(array(4, 36, 16, 4, 37, 17));
+    $this->addRsBlock(array(4, 36, 12, 4, 37, 13));
+    
+    // 10
+    $this->addRsBlock(array(2, 86, 68, 2, 87, 69));
+    $this->addRsBlock(array(4, 69, 43, 1, 70, 44));
+    $this->addRsBlock(array(6, 43, 19, 2, 44, 20));
+    $this->addRsBlock(array(6, 43, 15, 2, 44, 16));
+    
+    // 11
+    $this->addRsBlock(array(4, 101, 81));
+    $this->addRsBlock(array(1, 80, 50, 4, 81, 51));
+    $this->addRsBlock(array(4, 50, 22, 4, 51, 23));
+    $this->addRsBlock(array(3, 36, 12, 8, 37, 13));
+    
+    // 12
+    $this->addRsBlock(array(2, 116, 92, 2, 117, 93));
+    $this->addRsBlock(array(6, 58, 36, 2, 59, 37));
+    $this->addRsBlock(array(4, 46, 20, 6, 47, 21));
+    $this->addRsBlock(array(7, 42, 14, 4, 43, 15));
+    
+    // 13
+    $this->addRsBlock(array(4, 133, 107));
+    $this->addRsBlock(array(8, 59, 37, 1, 60, 38));
+    $this->addRsBlock(array(8, 44, 20, 4, 45, 21));
+    $this->addRsBlock(array(12, 33, 11, 4, 34, 12));
+    
+    // 14
+    $this->addRsBlock(array(3, 145, 115, 1, 146, 116));
+    $this->addRsBlock(array(4, 64, 40, 5, 65, 41));
+    $this->addRsBlock(array(11, 36, 16, 5, 37, 17));
+    $this->addRsBlock(array(11, 36, 12, 5, 37, 13));
+    
+    // 15
+    $this->addRsBlock(array(5, 109, 87, 1, 110, 88));
+    $this->addRsBlock(array(5, 65, 41, 5, 66, 42));
+    $this->addRsBlock(array(5, 54, 24, 7, 55, 25));
+    $this->addRsBlock(array(11, 36, 12));
+    
+    // 16
+    $this->addRsBlock(array(5, 122, 98, 1, 123, 99));
+    $this->addRsBlock(array(7, 73, 45, 3, 74, 46));
+    $this->addRsBlock(array(15, 43, 19, 2, 44, 20));
+    $this->addRsBlock(array(3, 45, 15, 13, 46, 16));
+    
+    // 17
+    $this->addRsBlock(array(1, 135, 107, 5, 136, 108));
+    $this->addRsBlock(array(10, 74, 46, 1, 75, 47));
+    $this->addRsBlock(array(1, 50, 22, 15, 51, 23));
+    $this->addRsBlock(array(2, 42, 14, 17, 43, 15));
+    
+    // 18
+    $this->addRsBlock(array(5, 150, 120, 1, 151, 121));
+    $this->addRsBlock(array(9, 69, 43, 4, 70, 44));
+    $this->addRsBlock(array(17, 50, 22, 1, 51, 23));
+    $this->addRsBlock(array(2, 42, 14, 19, 43, 15));
+    
+    // 19
+    $this->addRsBlock(array(3, 141, 113, 4, 142, 114));
+    $this->addRsBlock(array(3, 70, 44, 11, 71, 45));
+    $this->addRsBlock(array(17, 47, 21, 4, 48, 22));
+    $this->addRsBlock(array(9, 39, 13, 16, 40, 14));
+    
+    // 20
+    $this->addRsBlock(array(3, 135, 107, 5, 136, 108));
+    $this->addRsBlock(array(3, 67, 41, 13, 68, 42));
+    $this->addRsBlock(array(15, 54, 24, 5, 55, 25));
+    $this->addRsBlock(array(15, 43, 15, 10, 44, 16));
+    
+    // 21
+    $this->addRsBlock(array(4, 144, 116, 4, 145, 117));
+    $this->addRsBlock(array(17, 68, 42));
+    $this->addRsBlock(array(17, 50, 22, 6, 51, 23));
+    $this->addRsBlock(array(19, 46, 16, 6, 47, 17));
+    
+    // 22
+    $this->addRsBlock(array(2, 139, 111, 7, 140, 112));
+    $this->addRsBlock(array(17, 74, 46));
+    $this->addRsBlock(array(7, 54, 24, 16, 55, 25));
+    $this->addRsBlock(array(34, 37, 13));
+    
+    // 23
+    $this->addRsBlock(array(4, 151, 121, 5, 152, 122));
+    $this->addRsBlock(array(4, 75, 47, 14, 76, 48));
+    $this->addRsBlock(array(11, 54, 24, 14, 55, 25));
+    $this->addRsBlock(array(16, 45, 15, 14, 46, 16));
+    
+    // 24
+    $this->addRsBlock(array(6, 147, 117, 4, 148, 118));
+    $this->addRsBlock(array(6, 73, 45, 14, 74, 46));
+    $this->addRsBlock(array(11, 54, 24, 16, 55, 25));
+    $this->addRsBlock(array(30, 46, 16, 2, 47, 17));
+    
+    // 25
+    $this->addRsBlock(array(8, 132, 106, 4, 133, 107));
+    $this->addRsBlock(array(8, 75, 47, 13, 76, 48));
+    $this->addRsBlock(array(7, 54, 24, 22, 55, 25));
+    $this->addRsBlock(array(22, 45, 15, 13, 46, 16));
+    
+    // 26
+    $this->addRsBlock(array(10, 142, 114, 2, 143, 115));
+    $this->addRsBlock(array(19, 74, 46, 4, 75, 47));
+    $this->addRsBlock(array(28, 50, 22, 6, 51, 23));
+    $this->addRsBlock(array(33, 46, 16, 4, 47, 17));
+    
+    // 27
+    $this->addRsBlock(array(8, 152, 122, 4, 153, 123));
+    $this->addRsBlock(array(22, 73, 45, 3, 74, 46));
+    $this->addRsBlock(array(8, 53, 23, 26, 54, 24));
+    $this->addRsBlock(array(12, 45, 15, 28, 46, 16));
+    
+    // 28
+    $this->addRsBlock(array(3, 147, 117, 10, 148, 118));
+    $this->addRsBlock(array(3, 73, 45, 23, 74, 46));
+    $this->addRsBlock(array(4, 54, 24, 31, 55, 25));
+    $this->addRsBlock(array(11, 45, 15, 31, 46, 16));
+    
+    // 29
+    $this->addRsBlock(array(7, 146, 116, 7, 147, 117));
+    $this->addRsBlock(array(21, 73, 45, 7, 74, 46));
+    $this->addRsBlock(array(1, 53, 23, 37, 54, 24));
+    $this->addRsBlock(array(19, 45, 15, 26, 46, 16));
+    
+    // 30
+    $this->addRsBlock(array(5, 145, 115, 10, 146, 116));
+    $this->addRsBlock(array(19, 75, 47, 10, 76, 48));
+    $this->addRsBlock(array(15, 54, 24, 25, 55, 25));
+    $this->addRsBlock(array(23, 45, 15, 25, 46, 16));
+    
+    // 31
+    $this->addRsBlock(array(13, 145, 115, 3, 146, 116));
+    $this->addRsBlock(array(2, 74, 46, 29, 75, 47));
+    $this->addRsBlock(array(42, 54, 24, 1, 55, 25));
+    $this->addRsBlock(array(23, 45, 15, 28, 46, 16));
+    
+    // 32
+    $this->addRsBlock(array(17, 145, 115));
+    $this->addRsBlock(array(10, 74, 46, 23, 75, 47));
+    $this->addRsBlock(array(42, 54, 24, 1, 55, 25));
+    $this->addRsBlock(array(23, 45, 15, 28, 46, 16));
+    
+    // 33
+    $this->addRsBlock(array(17, 145, 115, 1, 146, 116));
+    $this->addRsBlock(array(14, 74, 46, 21, 75, 47));
+    $this->addRsBlock(array(29, 54, 24, 19, 55, 25));
+    $this->addRsBlock(array(11, 45, 15, 46, 46, 16));
+    
+    // 34
+    $this->addRsBlock(array(13, 145, 115, 6, 146, 116));
+    $this->addRsBlock(array(14, 74, 46, 21, 75, 47));
+    $this->addRsBlock(array(44, 54, 24, 7, 55, 25));
+    $this->addRsBlock(array(59, 46, 16, 1, 47, 17));
+    
+    // 35
+    $this->addRsBlock(array(12, 151, 121, 7, 152, 122));
+    $this->addRsBlock(array(12, 75, 47, 26, 76, 48));
+    $this->addRsBlock(array(39, 54, 24, 14, 55, 25));
+    $this->addRsBlock(array(22, 45, 15, 41, 46, 16));
+    
+    // 36
+    $this->addRsBlock(array(6, 151, 121, 14, 152, 122));
+    $this->addRsBlock(array(6, 75, 47, 34, 76, 48));
+    $this->addRsBlock(array(46, 54, 24, 10, 55, 25));
+    $this->addRsBlock(array(2, 45, 15, 64, 46, 16));
+    
+    // 37
+    $this->addRsBlock(array(17, 152, 122, 4, 153, 123));
+    $this->addRsBlock(array(29, 74, 46, 14, 75, 47));
+    $this->addRsBlock(array(49, 54, 24, 10, 55, 25));
+    $this->addRsBlock(array(24, 45, 15, 46, 46, 16));
+    
+    // 38
+    $this->addRsBlock(array(4, 152, 122, 18, 153, 123));
+    $this->addRsBlock(array(13, 74, 46, 32, 75, 47));
+    $this->addRsBlock(array(48, 54, 24, 14, 55, 25));
+    $this->addRsBlock(array(42, 45, 15, 32, 46, 16));
+    
+    // 39
+    $this->addRsBlock(array(20, 147, 117, 4, 148, 118));
+    $this->addRsBlock(array(40, 75, 47, 7, 76, 48));
+    $this->addRsBlock(array(43, 54, 24, 22, 55, 25));
+    $this->addRsBlock(array(10, 45, 15, 67, 46, 16));
+    
+    // 40
+    $this->addRsBlock(array(19, 148, 118, 6, 149, 119));
+    $this->addRsBlock(array(18, 75, 47, 31, 76, 48));
+    $this->addRsBlock(array(34, 54, 24, 34, 55, 25));
+    $this->addRsBlock(array(20, 45, 15, 61, 46, 16));
+    
+    $this->saveBlockFile();
+  }
+  
+  /**
+   * Add a new block information to the block
+   * 
+   * @param array $block
+   */
+  private function addRsBlock($block)
+  {
+    array_push($this->RS_BLOCK_TABLE, $block);
+  }
+  
+  /**
+   * Return the absolute path to the block file
+   * @return string
+   */
+  private function getBlockFileAbsolute()
+  {
+    return sprintf("%s%s%s", dirname(__FILE__), DIRECTORY_SEPARATOR, $this->blockFileName);
+  }
+  
+  /**
+   * Try to load the block file
+   * 
+   * @return boolean
+   */
+  private function loadBlockFile()
+  {
+    $file = $this->getBlockFileAbsolute();
+    
+    if(!file_exists($file))
+    {
+      return false;
+    }
+    
+    $serialized = file_get_contents($file);
+    
+    if(!$serialized)
+    {
+      return false;
+    }
+    
+    $this->RS_BLOCK_TABLE = unserialize($serialized);
+    
+    if(!$this->RS_BLOCK_TABLE)
+    {
+      return false;
+    }
+    
+    return true;
+  }
+  
+  /**
+   * Try to save the block file
+   */
+  private function saveBlockFile()
+  {
+    $file = $this->getBlockFileAbsolute();
+    
+    if(file_exists($file))
+    {
+      unlink($file);
+    }
+    
+    file_put_contents($file, serialize($this->RS_BLOCK_TABLE));
+  }
+}

+ 464 - 0
resources/qr_code/QRUtil.php

@@ -0,0 +1,464 @@
+<?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);
+  }
+}

+ 30 - 0
resources/qr_code/README.md

@@ -0,0 +1,30 @@
+phpQr - A QRCode encoder class
+===
+
+by Maik Greubel ([email protected])
+
+The intention to create a basic qrcode encoder for PHP was a request on phpclasses.org.
+-
+
+The author studied multiple sources to get an idea how qrcode implementation works under the hood. 
+The best source was the JavaScript library jquery which provides a HTML Table based implementation.
+See source and documentation at  <https://github.com/jeromeetienne/jquery-qrcode>
+
+So the hard work was already done by Jerome Etienne. My part was only to port it to PHP code.
+
+Currently this package does not have the capability to read QRCodes.
+
+Further readings
+-
+For a detailed description of QRCode and its features please check  <http://www.qrcode.com/>
+
+Trademark information
+-
+
+QR Code is registered trademark of  
+DENSO WAVE INCORPORATED  <http://www.denso-wave.com/qrcode/faqpatent-e.html>
+
+License
+-
+All files in the package are published under the terms of the BSD 2 License.  <http://opensource.org/licenses/BSD-2-Clause>
+ 

文件差異過大導致無法顯示
+ 0 - 0
resources/qr_code/rsblock.dat


部分文件因文件數量過多而無法顯示