+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/admin/addwxqrcode.html b/public/admin/addwxqrcode.html
new file mode 100644
index 0000000..5418377
--- /dev/null
+++ b/public/admin/addwxqrcode.html
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/admin/addzfbqrcode.html b/public/admin/addzfbqrcode.html
new file mode 100644
index 0000000..c411b4d
--- /dev/null
+++ b/public/admin/addzfbqrcode.html
@@ -0,0 +1,153 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/admin/jk.html b/public/admin/jk.html
new file mode 100644
index 0000000..8218cb7
--- /dev/null
+++ b/public/admin/jk.html
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/admin/orderlist.html b/public/admin/orderlist.html
new file mode 100644
index 0000000..8746a28
--- /dev/null
+++ b/public/admin/orderlist.html
@@ -0,0 +1,274 @@
+
+
+
\ No newline at end of file
diff --git a/public/admin/wxqrcodelist.html b/public/admin/wxqrcodelist.html
new file mode 100644
index 0000000..1289aa4
--- /dev/null
+++ b/public/admin/wxqrcodelist.html
@@ -0,0 +1,83 @@
+
+
+
+ *
+ * @param $x; The horizontal component (i.e. which column)
+ * @param $y; The vertical component (i.e. which row)
+ */
+ public function flip($x, $y) {
+ $offset = $y * $this->rowSize + intval($x / 32);
+
+ $this->bits[$offset] = overflow32($this->bits[$offset]^(1 << ($x & 0x1f)));
+ }
+
+ /**
+ * Exclusive-or (XOR): Flip the bit in this {@code BitMatrix} if the corresponding
+ * mask bit is set.
+ *
+ * @param $mask; XOR mask
+ */
+ public function _xor($mask) {//было xor, php не позволяет использовать xor
+ if ($this->width != $mask->getWidth() || $this->height != $mask->getHeight()
+ || $this->rowSize != $mask->getRowSize()) {
+ throw new InvalidArgumentException("input matrix dimensions do not match");
+ }
+ $rowArray = new BitArray($this->width / 32 + 1);
+ for ($y = 0; $y < $this->height; $y++) {
+ $offset = $y * $this->rowSize;
+ $row = $mask->getRow($y, $rowArray)->getBitArray();
+ for ($x = 0; $x < $this->rowSize; $x++) {
+ $this->bits[$offset + $x] ^= $row[$x];
+ }
+ }
+ }
+
+ /**
+ * Clears all bits (sets to false).
+ */
+ 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 $left; The horizontal position to begin at (inclusive)
+ * @param $top; The vertical position to begin at (inclusive)
+ * @param $width; The width of the region
+ * @param $height; The height of the region
+ */
+ public function setRegion($left, $top, $width, $height) {
+ if ($top < 0 || $left < 0) {
+ throw new InvalidArgumentException("Left and top must be nonnegative");
+ }
+ if ($height < 1 || $width < 1) {
+ throw new InvalidArgumentException("Height and width must be at least 1");
+ }
+ $right = $left + $width;
+ $bottom = $top + $height;
+ if ($bottom > $this->height || $right > $this->width) { //> this.height || right > this.width
+ throw new InvalidArgumentException("The region must fit inside the matrix");
+ }
+ for ($y = $top; $y < $bottom; $y++) {
+ $offset = $y * $this->rowSize;
+ for ($x = $left; $x < $right; $x++) {
+ $this->bits[$offset + intval($x / 32)] = overflow32($this->bits[$offset + intval($x / 32)]|= 1 << ($x & 0x1f));
+ }
+ }
+ }
+
+ /**
+ * A fast method to retrieve one row of data from the matrix as a BitArray.
+ *
+ * @param $y; The row to retrieve
+ * @param $row; An optional caller-allocated BitArray, will be allocated if null or too small
+ * @return The resulting BitArray - this reference should always be used even when passing
+ * your own row
+ */
+ public function getRow($y, $row) {
+ if ($row == null || $row->getSize() < $this->width) {
+ $row = new BitArray($this->width);
+ } else {
+ $row->clear();
+ }
+ $offset = $y * $this->rowSize;
+ for ($x = 0; $x < $this->rowSize; $x++) {
+ $row->setBulk($x * 32, $this->bits[$offset + $x]);
+ }
+ return $row;
+ }
+
+ /**
+ * @param $y; row to set
+ * @param $row; {@link BitArray} to copy from
+ */
+ public function setRow($y, $row) {
+ $this->bits = arraycopy($row->getBitArray(), 0, $this->bits, $y * $this->rowSize, $this->rowSize);
+ }
+
+ /**
+ * Modifies this {@code BitMatrix} to represent the same but rotated 180 degrees
+ */
+ public function rotate180() {
+ $width = $this->getWidth();
+ $height = $this-getHeight();
+ $topRow = new BitArray($width);
+ $bottomRow = new BitArray($width);
+ for ($i = 0; $i < ($height+1) / 2; $i++) {
+ $topRow = $this->getRow($i, $topRow);
+ $bottomRow = $this->getRow($height - 1 - $i, $bottomRow);
+ $topRow->reverse();
+ $bottomRow->reverse();
+ $this->setRow($i, $bottomRow);
+ $this->setRow($height - 1 - $i, $topRow);
+ }
+ }
+
+ /**
+ * This is useful in detecting the enclosing rectangle of a 'pure' barcode.
+ *
+ * @return {@code left,top,width,height} enclosing rectangle of all 1 bits, or null if it is all white
+ */
+ 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++) {
+ $theBits = $this->bits[$y * $this->rowSize + $x32];
+ if ($theBits != 0) {
+ if ($y < $top) {
+ $top = $y;
+ }
+ if ($y > $bottom) {
+ $bottom = $y;
+ }
+ if ($x32 * 32 < $left) {
+ $bit = 0;
+ while (($theBits << (31 - $bit)) == 0) {
+ $bit++;
+ }
+ if (($x32 * 32 + $bit) < $left) {
+ $left = $x32 * 32 + $bit;
+ }
+ }
+ if ($x32 * 32 + 31 > $right) {
+ $bit = 31;
+ while ((sdvig3($theBits, $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 array($left, $top, $width, $height);
+ }
+
+ /**
+ * This is useful in detecting a corner of a 'pure' barcode.
+ *
+ * @return {@code x,y} coordinate of top-left-most 1 bit, or null if it is all white
+ */
+ public function getTopLeftOnBit() {
+ $bitsOffset = 0;
+ while ($bitsOffset < count($this->bits) && $this->bits[$bitsOffset] == 0) {
+ $bitsOffset++;
+ }
+ if ($bitsOffset == count($this->bits)) {
+ return null;
+ }
+ $y = $bitsOffset / $this->rowSize;
+ $x = ($bitsOffset % $this->rowSize) * 32;
+
+ $theBits = $this->bits[$bitsOffset];
+ $bit = 0;
+ while (($theBits << (31-$bit)) == 0) {
+ $bit++;
+ }
+ $x += $bit;
+ return array($x, $y);
+ }
+
+ public function getBottomRightOnBit() {
+ $bitsOffset = count($this->bits) - 1;
+ while ($bitsOffset >= 0 && $this->bits[$bitsOffset] == 0) {
+ $bitsOffset--;
+ }
+ if ($bitsOffset < 0) {
+ return null;
+ }
+
+ $y = $bitsOffset / $this->rowSize;
+ $x = ($bitsOffset % $this->rowSize) * 32;
+
+ $theBits = $this->bits[$bitsOffset];
+ $bit = 31;
+ while ((sdvig3($theBits, $bit)) == 0) {//>>>
+ $bit--;
+ }
+ $x += $bit;
+
+ return array($x, $y);
+ }
+
+ /**
+ * @return The width of the matrix
+ */
+ public function getWidth() {
+ return $this->width;
+ }
+
+ /**
+ * @return The height of the matrix
+ */
+ public function getHeight() {
+ return $this->height;
+ }
+
+ /**
+ * @return The row size of the matrix
+ */
+ public function getRowSize() {
+ return $this->rowSize;
+ }
+
+ //@Override
+ public function equals($o) {
+ if (!($o instanceof BitMatrix)) {
+ return false;
+ }
+ $other = $o;
+ return $this->width == $other->width && $this->height == $other->height && $this->rowSize == $other->rowSize &&
+ $this->bits===$other->bits;
+ }
+
+ //@Override
+ public function hashCode() {
+ $hash = $this->width;
+ $hash = 31 * $hash + $this->width;
+ $hash = 31 * $hash + $this->height;
+ $hash = 31 * $hash + $this->rowSize;
+ $hash = 31 * $hash + hashCode($this->bits);
+ return $hash;
+ }
+
+ //@Override
+ public function toString($setString='', $unsetString='',$lineSeparator='') {
+ if(!$setString||!$unsetString){
+ return (string)"X "." ";
+ }
+ if($lineSeparator&&$lineSeparator!=="\n"){
+ return $this->toString_($setString, $unsetString, $lineSeparator);
+ }
+ return (string)($setString. $unsetString. "\n");
+ }
+
+ /**
+ * @deprecated call {@link #toString(String,String)} only, which uses \n line separator always
+ */
+ // @Deprecated
+ public function toString_($setString, $unsetString, $lineSeparator) {
+ //$result = new StringBuilder(height * (width + 1));
+ $result = '';
+ for ($y = 0; $y < $this->height; $y++) {
+ for ($x = 0; $x < $this->width; $x++) {
+ $result .= ($this->get($x, $y) ? $setString : $unsetString);
+ }
+ $result .= ($lineSeparator);
+ }
+ return (string)$result;
+ }
+
+// @Override
+ public function _clone() {//clone()
+ return new BitMatrix($this->width, $this->height, $this->rowSize, $this->bits);
+ }
+
+}
\ No newline at end of file
diff --git a/public/qr-code/lib/common/BitSource.php b/public/qr-code/lib/common/BitSource.php
new file mode 100644
index 0000000..0585506
--- /dev/null
+++ b/public/qr-code/lib/common/BitSource.php
@@ -0,0 +1,112 @@
+This provides an easy abstraction to read bits at a time from a sequence of bytes, where the
+ * number of bits read is not often a multiple of 8.
+ *
+ *
This class is thread-safe but not reentrant -- unless the caller modifies the bytes array
+ * it passed in, in which case all bets are off.
+ *
+ * @author Sean Owen
+ */
+final class BitSource {
+
+ private $bytes;
+ private $byteOffset = 0;
+ private $bitOffset = 0;
+
+ /**
+ * @param bytes bytes from which this will read bits. Bits will be read from the first byte first.
+ * Bits are read within a byte from most-significant to least-significant bit.
+ */
+ public function __construct($bytes) {
+ $this->bytes = $bytes;
+ }
+
+ /**
+ * @return index of next bit in current byte which would be read by the next call to {@link #readBits(int)}.
+ */
+ public function getBitOffset() {
+ return $this->bitOffset;
+ }
+
+ /**
+ * @return index of next byte in input byte array which would be read by the next call to {@link #readBits(int)}.
+ */
+ public function getByteOffset() {
+ return $this->byteOffset;
+ }
+
+ /**
+ * @param numBits number of bits to read
+ * @return int representing the bits read. The bits will appear as the least-significant
+ * bits of the int
+ * @throws IllegalArgumentException if numBits isn't in [1,32] or more than is available
+ */
+ public function readBits($numBits) {
+ if ($numBits < 1 || $numBits > 32 || $numBits > $this->available()) {
+ throw new \InvalidArgumentException(strval($numBits));
+ }
+
+ $result = 0;
+
+ // First, read remainder from current byte
+ if ($this->bitOffset > 0) {
+ $bitsLeft = 8 - $this->bitOffset;
+ $toRead = $numBits < $bitsLeft ? $numBits : $bitsLeft;
+ $bitsToNotRead = $bitsLeft - $toRead;
+ $mask = (0xFF >> (8 - $toRead)) << $bitsToNotRead;
+ $result = ($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead;
+ $numBits -= $toRead;
+ $this->bitOffset += $toRead;
+ if ($this->bitOffset == 8) {
+ $this->bitOffset = 0;
+ $this->byteOffset++;
+ }
+ }
+
+ // Next read whole bytes
+ if ($numBits > 0) {
+ while ($numBits >= 8) {
+ $result = ($result << 8) | ($this->bytes[$this->byteOffset] & 0xFF);
+ $this->byteOffset++;
+ $numBits -= 8;
+ }
+
+ // Finally read a partial byte
+ if ($numBits > 0) {
+ $bitsToNotRead = 8 - $numBits;
+ $mask = (0xFF >> $bitsToNotRead) << $bitsToNotRead;
+ $result = ($result << $numBits) | (($this->bytes[$this->byteOffset] & $mask) >> $bitsToNotRead);
+ $this->bitOffset += $numBits;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @return number of bits that can be read successfully
+ */
+ public function available() {
+ return 8 * (count($this->bytes) - $this->byteOffset) - $this->bitOffset;
+ }
+
+}
diff --git a/public/qr-code/lib/common/CharacterSetEci.php b/public/qr-code/lib/common/CharacterSetEci.php
new file mode 100644
index 0000000..b440747
--- /dev/null
+++ b/public/qr-code/lib/common/CharacterSetEci.php
@@ -0,0 +1,154 @@
+ 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 static $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;
+ }
+ array_search($value, self::$nameToEci);
+ try
+ {
+ self::setName($value);
+ return new self($value);
+ } catch (Exception\UnexpectedValueException $e) {
+ return null;
+ }
+ }
+
+ private static function setName($value)
+ {
+ foreach (self::$nameToEci as $name => $key) {
+ if($key == $value)
+ {
+ self::$name = $name;
+ return true;
+ }
+ }
+ if(self::$name == null)
+ {
+ foreach (self::$additionalValues as $name => $key) {
+ if($key == $value)
+ {
+ self::$name = $name;
+ return true;
+ }
+ }
+ }
+ }
+ /**
+ * Gets character set ECI name.
+ *
+ * @return character set ECI name|null
+ */
+ public static function name()
+ {
+ return self::$name;
+ }
+ /**
+ * 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;
+ }
+}
\ No newline at end of file
diff --git a/public/qr-code/lib/common/DecoderResult.php b/public/qr-code/lib/common/DecoderResult.php
new file mode 100644
index 0000000..5568f90
--- /dev/null
+++ b/public/qr-code/lib/common/DecoderResult.php
@@ -0,0 +1,109 @@
+Encapsulates the result of decoding a matrix of bits. This typically
+ * applies to 2D barcode formats. For now it contains the raw bytes obtained,
+ * as well as a String interpretation of those bytes, if applicable.
+ *
+ * @author Sean Owen
+ */
+final class DecoderResult {
+
+ private $rawBytes;
+ private $text;
+ private $byteSegments;
+ private $ecLevel;
+ private $errorsCorrected;
+ private $erasures;
+ private $other;
+ private $structuredAppendParity;
+ private $structuredAppendSequenceNumber;
+
+
+
+ public function __construct($rawBytes,
+ $text,
+ $byteSegments,
+ $ecLevel,
+ $saSequence = -1,
+ $saParity = -1) {
+ $this->rawBytes = $rawBytes;
+ $this->text = $text;
+ $this->byteSegments = $byteSegments;
+ $this->ecLevel = $ecLevel;
+ $this->structuredAppendParity = $saParity;
+ $this->structuredAppendSequenceNumber = $saSequence;
+ }
+
+ public function getRawBytes() {
+ return $this->rawBytes;
+ }
+
+ public function getText() {
+ return $this->text;
+ }
+
+ public function getByteSegments() {
+ return $this->byteSegments;
+ }
+
+ public function getECLevel() {
+ return $this->ecLevel;
+ }
+
+ public function getErrorsCorrected() {
+ return $this->errorsCorrected;
+ }
+
+ public function setErrorsCorrected($errorsCorrected) {
+ $this->errorsCorrected = $errorsCorrected;
+ }
+
+ public function getErasures() {
+ return $this->erasures;
+ }
+
+ public function setErasures($erasures) {
+ $this->erasures = $erasures;
+ }
+
+ public function getOther() {
+ return $this->other;
+ }
+
+ public function setOther($other) {
+ $this->other = $other;
+ }
+
+ public function hasStructuredAppend() {
+ return $this->structuredAppendParity >= 0 && $this->structuredAppendSequenceNumber >= 0;
+ }
+
+ public function getStructuredAppendParity() {
+ return $this->structuredAppendParity;
+ }
+
+ public function getStructuredAppendSequenceNumber() {
+ return $this->structuredAppendSequenceNumber;
+ }
+
+}
\ No newline at end of file
diff --git a/public/qr-code/lib/common/DefaultGridSampler.php b/public/qr-code/lib/common/DefaultGridSampler.php
new file mode 100644
index 0000000..1e790a8
--- /dev/null
+++ b/public/qr-code/lib/common/DefaultGridSampler.php
@@ -0,0 +1,89 @@
+sampleGrid_($image, $dimensionX, $dimensionY, $transform);
+ }
+
+//@Override
+ public function sampleGrid_($image,
+ $dimensionX,
+ $dimensionY,
+ $transform) {
+ if ($dimensionX <= 0 || $dimensionY <= 0) {
+ throw NotFoundException::getNotFoundInstance();
+ }
+ $bits = new BitMatrix($dimensionX, $dimensionY);
+ $points = fill_array(0,2 * $dimensionX,0.0);
+ for ($y = 0; $y < $dimensionY; $y++) {
+ $max = count($points);
+ $iValue = (float) $y + 0.5;
+ for ($x = 0; $x < $max; $x += 2) {
+ $points[$x] = (float) ($x / 2) + 0.5;
+ $points[$x + 1] = $iValue;
+ }
+ $transform->transformPoints($points);
+// Quick check to see if points transformed to something inside the image;
+// sufficient to check the endpoints
+ $this->checkAndNudgePoints($image, $points);
+ try {
+ for ($x = 0; $x < $max; $x += 2) {
+ if ($image->get((int) $points[$x], (int) $points[$x + 1])) {
+// Black(-ish) pixel
+ $bits->set($x / 2, $y);
+ }
+ }
+ } catch (\Exception $aioobe) {//ArrayIndexOutOfBoundsException
+// This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting
+// transform gets "twisted" such that it maps a straight line of points to a set of points
+// whose endpoints are in bounds, but others are not. There is probably some mathematical
+// way to detect this about the transformation that I don't know yet.
+// This results in an ugly runtime exception despite our clever checks above -- can't have
+// that. We could check each point's coordinates but that feels duplicative. We settle for
+// catching and wrapping ArrayIndexOutOfBoundsException.
+ throw NotFoundException::getNotFoundInstance();
+ }
+ }
+ return $bits;
+ }
+
+}
diff --git a/public/qr-code/lib/common/DetectorResult.php b/public/qr-code/lib/common/DetectorResult.php
new file mode 100644
index 0000000..97f0567
--- /dev/null
+++ b/public/qr-code/lib/common/DetectorResult.php
@@ -0,0 +1,47 @@
+Encapsulates the result of detecting a barcode in an image. This includes the raw
+ * matrix of black/white pixels corresponding to the barcode, and possibly points of interest
+ * in the image, like the location of finder patterns or corners of the barcode in the image.
+ *
+ * @author Sean Owen
+ */
+class DetectorResult {
+
+ private $bits;
+ private $points;
+
+ public function __construct($bits, $points) {
+ $this->bits = $bits;
+ $this->points = $points;
+ }
+
+ public final function getBits() {
+ return $this->bits;
+ }
+
+ public final function getPoints() {
+ return $this->points;
+ }
+
+}
\ No newline at end of file
diff --git a/public/qr-code/lib/common/GlobalHistogramBinarizer.php b/public/qr-code/lib/common/GlobalHistogramBinarizer.php
new file mode 100644
index 0000000..2df580f
--- /dev/null
+++ b/public/qr-code/lib/common/GlobalHistogramBinarizer.php
@@ -0,0 +1,206 @@
+luminances = self::$EMPTY;
+ $this->buckets = fill_array(0, self::$LUMINANCE_BUCKETS,0);
+ $this->source = $source;
+ }
+
+// Applies simple sharpening to the row data to improve performance of the 1D Readers.
+//@Override
+ public function getBlackRow($y, $row=null) {
+ $this->source = $this->getLuminanceSource();
+ $width = $this->source->getWidth();
+ if ($row == null || $row->getSize() < $width) {
+ $row = new BitArray($width);
+ } else {
+ $row->clear();
+ }
+
+ $this->initArrays($width);
+ $localLuminances = $this->source->getRow($y, $this->luminances);
+ $localBuckets = $this->buckets;
+ for ($x = 0; $x < $width; $x++) {
+ $pixel = $localLuminances[$x] & 0xff;
+ $localBuckets[$pixel >> self::$LUMINANCE_SHIFT]++;
+ }
+ $blackPoint = $this->estimateBlackPoint($localBuckets);
+
+ $left = $localLuminances[0] & 0xff;
+ $center = $localLuminances[1] & 0xff;
+ for ($x = 1; $x < $width - 1; $x++) {
+ $right = $localLuminances[$x + 1] & 0xff;
+// A simple -1 4 -1 box filter with a weight of 2.
+ $luminance = (($center * 4) - $left - $right) / 2;
+ if ($luminance < $blackPoint) {
+ $row->set($x);
+ }
+ $left = $center;
+ $center = $right;
+ }
+ return $row;
+ }
+
+// Does not sharpen the data, as this call is intended to only be used by 2D Readers.
+//@Override
+ public function getBlackMatrix(){
+ $source = $this->getLuminanceSource();
+ $width = $source->getWidth();
+ $height = $source->getHeight();
+ $matrix = new BitMatrix($width, $height);
+
+// Quickly calculates the histogram by sampling four rows from the image. This proved to be
+// more robust on the blackbox tests than sampling a diagonal as we used to do.
+ $this->initArrays($width);
+ $localBuckets = $this->buckets;
+ for ($y = 1; $y < 5; $y++) {
+ $row = intval($height * $y / 5);
+ $localLuminances = $source->getRow($row, $this->luminances);
+ $right = intval(($width * 4) / 5);
+ for ($x = intval($width / 5); $x < $right; $x++) {
+ $pixel = intval32bits($localLuminances[intval($x)] & 0xff);
+ $localBuckets[intval32bits($pixel >> self::$LUMINANCE_SHIFT)]++;
+ }
+ }
+ $blackPoint = $this->estimateBlackPoint($localBuckets);
+
+// We delay reading the entire image luminance until the black point estimation succeeds.
+// Although we end up reading four rows twice, it is consistent with our motto of
+// "fail quickly" which is necessary for continuous scanning.
+ $localLuminances = $source->getMatrix();
+ for ($y = 0; $y < $height; $y++) {
+ $offset = $y * $width;
+ for ($x = 0; $x< $width; $x++) {
+ $pixel = intval($localLuminances[$offset + $x] & 0xff);
+ if ($pixel < $blackPoint) {
+ $matrix->set($x, $y);
+ }
+ }
+ }
+
+ return $matrix;
+ }
+
+//@Override
+ public function createBinarizer($source) {
+ return new GlobalHistogramBinarizer($source);
+ }
+
+ private function initArrays($luminanceSize) {
+ if (count($this->luminances) < $luminanceSize) {
+ $this->luminances = array();
+ }
+ for ($x = 0; $x < self::$LUMINANCE_BUCKETS; $x++) {
+ $this->buckets[$x] = 0;
+ }
+ }
+
+ private static function estimateBlackPoint($buckets){
+// Find the tallest peak in the histogram.
+ $numBuckets = count($buckets);
+ $maxBucketCount = 0;
+ $firstPeak = 0;
+ $firstPeakSize = 0;
+ for ($x = 0; $x < $numBuckets; $x++) {
+ if ($buckets[$x] > $firstPeakSize) {
+ $firstPeak = $x;
+ $firstPeakSize = $buckets[$x];
+ }
+ if ($buckets[$x] > $maxBucketCount) {
+ $maxBucketCount = $buckets[$x];
+ }
+ }
+
+// Find the second-tallest peak which is somewhat far from the tallest peak.
+ $secondPeak = 0;
+ $secondPeakScore = 0;
+ for ($x = 0; $x < $numBuckets; $x++) {
+ $distanceToBiggest = $x - $firstPeak;
+// Encourage more distant second peaks by multiplying by square of distance.
+ $score = $buckets[$x] * $distanceToBiggest * $distanceToBiggest;
+ if ($score > $secondPeakScore) {
+ $secondPeak = $x;
+ $secondPeakScore = $score;
+ }
+ }
+
+// Make sure firstPeak corresponds to the black peak.
+ if ($firstPeak > $secondPeak) {
+ $temp = $firstPeak;
+ $firstPeak = $secondPeak;
+ $secondPeak = $temp;
+ }
+
+// If there is too little contrast in the image to pick a meaningful black point, throw rather
+// than waste time trying to decode the image, and risk false positives.
+ if ($secondPeak - $firstPeak <= $numBuckets / 16) {
+ throw NotFoundException::getNotFoundInstance();
+ }
+
+// Find a valley between them that is low and closer to the white peak.
+ $bestValley = $secondPeak - 1;
+ $bestValleyScore = -1;
+ for ($x = $secondPeak - 1; $x > $firstPeak; $x--) {
+ $fromFirst = $x - $firstPeak;
+ $score = $fromFirst * $fromFirst * ($secondPeak - $x) * ($maxBucketCount - $buckets[$x]);
+ if ($score > $bestValleyScore) {
+ $bestValley = $x;
+ $bestValleyScore = $score;
+ }
+ }
+
+ return intval32bits($bestValley << self::$LUMINANCE_SHIFT);
+ }
+
+}
diff --git a/public/qr-code/lib/common/GridSampler.php b/public/qr-code/lib/common/GridSampler.php
new file mode 100644
index 0000000..30c3aee
--- /dev/null
+++ b/public/qr-code/lib/common/GridSampler.php
@@ -0,0 +1,177 @@
+Checks a set of points that have been transformed to sample points on an image against
+ * the image's dimensions to see if the point are even within the image.
+ *
+ *
This method will actually "nudge" the endpoints back onto the image if they are found to be
+ * barely (less than 1 pixel) off the image. This accounts for imperfect detection of finder
+ * patterns in an image where the QR Code runs all the way to the image border.
+ *
+ *
For efficiency, the method will check points from either end of the line until one is found
+ * to be within the image. Because the set of points are assumed to be linear, this is valid.
+ *
+ * @param image image into which the points should map
+ * @param points actual points in x1,y1,...,xn,yn form
+ * @throws NotFoundException if an endpoint is lies outside the image boundaries
+ */
+ protected static function checkAndNudgePoints($image,
+ $points) {
+ $width = $image->getWidth();
+ $height = $image->getHeight();
+// Check and nudge points from start until we see some that are OK:
+ $nudged = true;
+ for ($offset = 0; $offset < count($points) && $nudged; $offset += 2) {
+ $x = (int) $points[$offset];
+ $y = (int) $points[$offset + 1];
+ if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
+ throw NotFoundException::getNotFoundInstance();
+ }
+ $nudged = false;
+ if ($x == -1) {
+ $points[$offset] = 0.0;
+ $nudged = true;
+ } else if ($x == $width) {
+ $points[$offset] = $width - 1;
+ $nudged = true;
+ }
+ if ($y == -1) {
+ $points[$offset + 1] = 0.0;
+ $nudged = true;
+ } else if ($y == $height) {
+ $points[$offset + 1] = $height - 1;
+ $nudged = true;
+ }
+ }
+// Check and nudge points from end:
+ $nudged = true;
+ for ($offset = count($points) - 2; $offset >= 0 && $nudged; $offset -= 2) {
+ $x = (int) $points[$offset];
+ $y = (int) $points[$offset + 1];
+ if ($x < -1 || $x > $width || $y < -1 || $y > $height) {
+ throw NotFoundException::getNotFoundInstance();
+ }
+ $nudged = false;
+ if ($x == -1) {
+ $points[$offset] = 0.0;
+ $nudged = true;
+ } else if ($x == $width) {
+ $points[$offset] = $width - 1;
+ $nudged = true;
+ }
+ if ($y == -1) {
+ $points[$offset + 1] = 0.0;
+ $nudged = true;
+ } else if ($y == $height) {
+ $points[$offset + 1] = $height - 1;
+ $nudged = true;
+ }
+ }
+ }
+
+}
diff --git a/public/qr-code/lib/common/HybridBinarizer.php b/public/qr-code/lib/common/HybridBinarizer.php
new file mode 100644
index 0000000..670562c
--- /dev/null
+++ b/public/qr-code/lib/common/HybridBinarizer.php
@@ -0,0 +1,259 @@
+matrix != null) {
+ return $this->matrix;
+ }
+ $source = $this->getLuminanceSource();
+ $width = $source->getWidth();
+ $height = $source->getHeight();
+ if ($width >= self::$MINIMUM_DIMENSION && $height >= self::$MINIMUM_DIMENSION) {
+ $luminances = $source->getMatrix();
+ $subWidth = $width >> self::$BLOCK_SIZE_POWER;
+ if (($width & self::$BLOCK_SIZE_MASK) != 0) {
+ $subWidth++;
+ }
+ $subHeight = $height >> self::$BLOCK_SIZE_POWER;
+ if (($height & self::$BLOCK_SIZE_MASK) != 0) {
+ $subHeight++;
+ }
+ $blackPoints = $this->calculateBlackPoints($luminances, $subWidth, $subHeight, $width, $height);
+
+ $newMatrix = new BitMatrix($width, $height);
+ $this->calculateThresholdForBlock($luminances, $subWidth, $subHeight, $width, $height, $blackPoints, $newMatrix);
+ $this->matrix = $newMatrix;
+ } else {
+// If the image is too small, fall back to the global histogram approach.
+ $this->matrix = parent::getBlackMatrix();
+ }
+ return $this->matrix;
+ }
+
+//@Override
+ public function createBinarizer($source) {
+ return new HybridBinarizer($source);
+ }
+
+ /**
+ * For each block in the image, calculate the average black point using a 5x5 grid
+ * of the blocks around it. Also handles the corner cases (fractional blocks are computed based
+ * on the last pixels in the row/column which are also used in the previous block).
+ */
+ private static function calculateThresholdForBlock($luminances,
+ $subWidth,
+ $subHeight,
+ $width,
+ $height,
+ $blackPoints,
+ $matrix) {
+ for ($y = 0; $y < $subHeight; $y++) {
+ $yoffset = intval32bits($y << self::$BLOCK_SIZE_POWER);
+ $maxYOffset = $height - self::$BLOCK_SIZE;
+ if ($yoffset > $maxYOffset) {
+ $yoffset = $maxYOffset;
+ }
+ for ($x = 0; $x < $subWidth; $x++) {
+ $xoffset = intval32bits($x << self::$BLOCK_SIZE_POWER);
+ $maxXOffset = $width - self::$BLOCK_SIZE;
+ if ($xoffset > $maxXOffset) {
+ $xoffset = $maxXOffset;
+ }
+ $left = self::cap($x, 2, $subWidth - 3);
+ $top = self::cap($y, 2, $subHeight - 3);
+ $sum = 0;
+ for ($z = -2; $z <= 2; $z++) {
+ $blackRow = $blackPoints[$top + $z];
+ $sum += $blackRow[$left - 2] + $blackRow[$left - 1] + $blackRow[$left] + $blackRow[$left + 1] + $blackRow[$left + 2];
+ }
+ $average = intval($sum / 25);
+
+ self::thresholdBlock($luminances, $xoffset, $yoffset, $average, $width, $matrix);
+ }
+ }
+ }
+
+ private static function cap($value, $min, $max) {
+ if($value<$min){
+ return $min;
+ }elseif($value>$max){
+ return $max;
+ }else{
+ return $value;
+ }
+
+
+
+ }
+
+ /**
+ * Applies a single threshold to a block of pixels.
+ */
+ private static function thresholdBlock($luminances,
+ $xoffset,
+ $yoffset,
+ $threshold,
+ $stride,
+ $matrix) {
+
+ for ($y = 0, $offset = $yoffset * $stride + $xoffset; $y < self::$BLOCK_SIZE; $y++, $offset += $stride) {
+ for ($x = 0; $x < self::$BLOCK_SIZE; $x++) {
+// Comparison needs to be <= so that black == 0 pixels are black even if the threshold is 0.
+ if (($luminances[$offset + $x] & 0xFF) <= $threshold) {
+ $matrix->set($xoffset + $x, $yoffset + $y);
+ }
+ }
+ }
+ }
+
+ /**
+ * Calculates a single black point for each block of pixels and saves it away.
+ * See the following thread for a discussion of this algorithm:
+ * http://groups.google.com/group/zxing/browse_thread/thread/d06efa2c35a7ddc0
+ */
+ private static function calculateBlackPoints($luminances,
+ $subWidth,
+ $subHeight,
+ $width,
+ $height) {
+ $blackPoints = fill_array(0,$subHeight,0);
+ foreach($blackPoints as $key=>$point){
+ $blackPoints[$key] = fill_array(0,$subWidth,0);
+ }
+ for ($y = 0; $y < $subHeight; $y++) {
+ $yoffset = intval32bits($y << self::$BLOCK_SIZE_POWER);
+ $maxYOffset = $height - self::$BLOCK_SIZE;
+ if ($yoffset > $maxYOffset) {
+ $yoffset = $maxYOffset;
+ }
+ for ($x = 0; $x < $subWidth; $x++) {
+ $xoffset = intval32bits($x << self::$BLOCK_SIZE_POWER);
+ $maxXOffset = $width - self::$BLOCK_SIZE;
+ if ($xoffset > $maxXOffset) {
+ $xoffset = $maxXOffset;
+ }
+ $sum = 0;
+ $min = 0xFF;
+ $max = 0;
+ for ($yy = 0, $offset = $yoffset * $width + $xoffset; $yy < self::$BLOCK_SIZE; $yy++, $offset += $width) {
+ for ($xx = 0; $xx < self::$BLOCK_SIZE; $xx++) {
+ $pixel = intval32bits(intval($luminances[intval($offset +$xx)]) & 0xFF);
+ $sum += $pixel;
+// still looking for good contrast
+ if ($pixel < $min) {
+ $min = $pixel;
+ }
+ if ($pixel > $max) {
+ $max = $pixel;
+ }
+ }
+// short-circuit min/max tests once dynamic range is met
+ if ($max - $min > self::$MIN_DYNAMIC_RANGE) {
+// finish the rest of the rows quickly
+ for ($yy++, $offset += $width; $yy < self::$BLOCK_SIZE; $yy++, $offset += $width) {
+ for ($xx = 0; $xx < self::$BLOCK_SIZE; $xx++) {
+ $sum += intval32bits($luminances[$offset +$xx] & 0xFF);
+ }
+ }
+ }
+ }
+
+// The default estimate is the average of the values in the block.
+ $average = intval32bits($sum >> (self::$BLOCK_SIZE_POWER * 2));
+ if ($max - $min <= self::$MIN_DYNAMIC_RANGE) {
+// If variation within the block is low, assume this is a block with only light or only
+// dark pixels. In that case we do not want to use the average, as it would divide this
+// low contrast area into black and white pixels, essentially creating data out of noise.
+//
+// The default assumption is that the block is light/background. Since no estimate for
+// the level of dark pixels exists locally, use half the min for the block.
+ $average = intval($min / 2);
+
+ if ($y > 0 && $x > 0) {
+// Correct the "white background" assumption for blocks that have neighbors by comparing
+// the pixels in this block to the previously calculated black points. This is based on
+// the fact that dark barcode symbology is always surrounded by some amount of light
+// background for which reasonable black point estimates were made. The bp estimated at
+// the boundaries is used for the interior.
+
+// The (min < bp) is arbitrary but works better than other heuristics that were tried.
+ $averageNeighborBlackPoint =
+ intval(($blackPoints[$y - 1][$x] + (2 * $blackPoints[$y][$x - 1]) + $blackPoints[$y - 1][$x - 1]) / 4);
+ if ($min < $averageNeighborBlackPoint) {
+ $average = $averageNeighborBlackPoint;
+ }
+ }
+ }
+ $blackPoints[$y][$x] = intval($average);
+ }
+ }
+ return $blackPoints;
+ }
+
+}
diff --git a/public/qr-code/lib/common/PerspectiveTransform.php b/public/qr-code/lib/common/PerspectiveTransform.php
new file mode 100644
index 0000000..7c046a8
--- /dev/null
+++ b/public/qr-code/lib/common/PerspectiveTransform.php
@@ -0,0 +1,161 @@
+This class implements a perspective transform in two dimensions. Given four source and four
+ * destination points, it will compute the transformation implied between them. The code is based
+ * directly upon section 3.4.2 of George Wolberg's "Digital Image Warping"; see pages 54-56.
+ *
+ * @author Sean Owen
+ */
+final class PerspectiveTransform {
+
+ private $a11;
+ private $a12;
+ private $a13;
+ private $a21;
+ private $a22;
+ private $a23;
+ private $a31;
+ private $a32;
+ private $a33;
+
+ private function __construct($a11, $a21, $a31,
+ $a12, $a22, $a32,
+ $a13, $a23, $a33) {
+ $this->a11 = $a11;
+ $this->a12 = $a12;
+ $this->a13 = $a13;
+ $this->a21 = $a21;
+ $this->a22 = $a22;
+ $this->a23 = $a23;
+ $this->a31 = $a31;
+ $this->a32 = $a32;
+ $this->a33 = $a33;
+ }
+
+ public static function quadrilateralToQuadrilateral($x0, $y0,
+ $x1, $y1,
+ $x2, $y2,
+ $x3, $y3,
+ $x0p, $y0p,
+ $x1p, $y1p,
+ $x2p, $y2p,
+ $x3p, $y3p) {
+
+ $qToS = self::quadrilateralToSquare($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3);
+ $sToQ = self::squareToQuadrilateral($x0p, $y0p, $x1p, $y1p, $x2p, $y2p, $x3p, $y3p);
+ return $sToQ->times($qToS);
+ }
+
+ public function transformPoints(&$points,&$yValues=0) {
+ if($yValues) {
+ $this->transformPoints_($points,$yValues);
+ return;
+ }
+ $max =count($points);
+ $a11 = $this->a11;
+ $a12 = $this->a12;
+ $a13 = $this->a13;
+ $a21 = $this->a21;
+ $a22 = $this->a22;
+ $a23 = $this->a23;
+ $a31 = $this->a31;
+ $a32 = $this->a32;
+ $a33 = $this->a33;
+ for ($i = 0; $i < $max; $i += 2) {
+ $x = $points[$i];
+ $y = $points[$i + 1];
+ $denominator = $a13 * $x + $a23 * $y + $a33;
+ $points[$i] = ($a11 * $x + $a21 * $y + $a31) / $denominator;
+ $points[$i + 1] = ($a12 * $x + $a22 * $y +$a32) / $denominator;
+ }
+ }
+
+ public function transformPoints_(&$xValues, &$yValues) {
+ $n = count($xValues);
+ for ($i = 0; $i < $n; $i ++) {
+ $x = $xValues[$i];
+ $y = $yValues[$i];
+ $denominator = $this->a13 * $x + $this->a23 * $y + $this->a33;
+ $xValues[$i] = ($this->a11 * $x + $this->a21 * $y + $this->a31) / $denominator;
+ $yValues[$i] = ($this->a12 * $x + $this->a22 *$y + $this->a32) / $denominator;
+ }
+ }
+
+ public static function squareToQuadrilateral($x0, $y0,
+ $x1, $y1,
+ $x2, $y2,
+ $x3, $y3) {
+ $dx3 = $x0 - $x1 + $x2 - $x3;
+ $dy3 = $y0 - $y1 + $y2 - $y3;
+ if ($dx3 == 0.0 && $dy3 == 0.0) {
+// Affine
+ return new PerspectiveTransform($x1 - $x0, $x2 - $x1, $x0,
+ $y1 - $y0, $y2 - $y1, $y0,
+ 0.0, 0.0, 1.0);
+ } else {
+ $dx1 = $x1 - $x2;
+ $dx2 = $x3 - $x2;
+ $dy1 = $y1 - $y2;
+ $dy2 = $y3 - $y2;
+ $denominator = $dx1 * $dy2 - $dx2 * $dy1;
+ $a13 = ($dx3 * $dy2 - $dx2 * $dy3) / $denominator;
+ $a23 = ($dx1 * $dy3 - $dx3 * $dy1) / $denominator;
+ return new PerspectiveTransform($x1 - $x0 + $a13 * $x1, $x3 - $x0 + $a23 * $x3, $x0,
+ $y1 - $y0 + $a13 * $y1, $y3 - $y0 + $a23 * $y3, $y0,
+ $a13, $a23, 1.0);
+ }
+ }
+
+ public static function quadrilateralToSquare($x0, $y0,
+ $x1, $y1,
+ $x2, $y2,
+ $x3, $y3) {
+// Here, the adjoint serves as the inverse:
+ return self::squareToQuadrilateral($x0, $y0, $x1, $y1, $x2, $y2, $x3, $y3)->buildAdjoint();
+ }
+
+ function buildAdjoint() {
+// Adjoint is the transpose of the cofactor matrix:
+ return new PerspectiveTransform($this->a22 * $this->a33 - $this->a23 * $this->a32,
+ $this->a23 * $this->a31 - $this->a21 * $this->a33,
+ $this->a21 * $this->a32 - $this->a22 * $this->a31,
+ $this->a13 * $this->a32 - $this->a12 * $this->a33,
+ $this->a11 * $this->a33 - $this->a13 * $this->a31,
+ $this->a12 * $this->a31 - $this->a11 * $this->a32,
+ $this->a12 * $this->a23 - $this->a13 * $this->a22,
+ $this->a13 * $this->a21 - $this->a11 * $this->a23,
+ $this->a11 * $this->a22 - $this->a12 * $this->a21);
+ }
+
+ function times($other) {
+ return new PerspectiveTransform($this->a11 * $other->a11 + $this->a21 * $other->a12 + $this->a31 * $other->a13,
+ $this->a11 * $other->a21 + $this->a21 * $other->a22 + $this->a31 * $other->a23,
+ $this->a11 * $other->a31 + $this->a21 * $other->a32 + $this->a31 * $other->a33,
+ $this->a12 * $other->a11 + $this->a22 * $other->a12 + $this->a32 * $other->a13,
+ $this->a12 * $other->a21 + $this->a22 * $other->a22 + $this->a32 * $other->a23,
+ $this->a12 * $other->a31 + $this->a22 * $other->a32 + $this->a32 * $other->a33,
+ $this->a13 * $other->a11 + $this->a23 * $other->a12 + $this->a33 * $other->a13,
+ $this->a13 * $other->a21 + $this->a23 * $other->a22 + $this->a33 * $other->a23,
+ $this->a13 * $other->a31 + $this->a23 * $other->a32 + $this->a33 * $other->a33);
+
+ }
+
+}
diff --git a/public/qr-code/lib/common/customFunctions.php b/public/qr-code/lib/common/customFunctions.php
new file mode 100644
index 0000000..6ae71cc
--- /dev/null
+++ b/public/qr-code/lib/common/customFunctions.php
@@ -0,0 +1,93 @@
+>= 1;
+ $num++;
+ }
+ return $num;
+}
+function intval32bits($value)
+{
+ $value = ($value & 0xFFFFFFFF);
+
+ if ($value & 0x80000000)
+ $value = -((~$value & 0xFFFFFFFF) + 1);
+
+ return $value;
+}
+
+function uRShift($a, $b)
+{
+
+ if($b == 0) return $a;
+ return ($a >> $b) & ~(1<<(8*PHP_INT_SIZE-1)>>($b-1));
+}
+/*
+function sdvig3($num,$count=1){//>>> 32 bit
+ $s = decbin($num);
+
+ $sarray = str_split($s,1);
+ $sarray = array_slice($sarray,-32);//32bit
+
+ for($i=0;$i<=1;$i++) {
+ array_pop($sarray);
+ array_unshift($sarray, '0');
+ }
+ return bindec(implode($sarray));
+}
+*/
+
+function sdvig3($a,$b) {
+
+ if ($a >= 0) {
+ return bindec(decbin($a>>$b)); //simply right shift for positive number
+ }
+
+ $bin = decbin($a>>$b);
+
+ $bin = substr($bin, $b); // zero fill on the left side
+
+ $o = bindec($bin);
+ return $o;
+}
+
+function floatToIntBits($float_val)
+{
+ $int = unpack('i', pack('f', $float_val));
+ return $int[1];
+}
+
+function fill_array($index,$count,$value){
+ if($count<=0){
+ return array(0);
+ }else {
+ return array_fill($index, $count, $value);
+ }
+}
\ No newline at end of file
diff --git a/public/qr-code/lib/common/detector/MathUtils.php b/public/qr-code/lib/common/detector/MathUtils.php
new file mode 100644
index 0000000..1d3de23
--- /dev/null
+++ b/public/qr-code/lib/common/detector/MathUtils.php
@@ -0,0 +1,46 @@
+A somewhat generic detector that looks for a barcode-like rectangular region within an image.
+ * It looks within a mostly white region of an image for a region of black and white, but mostly
+ * black. It returns the four corners of the region, as best it can determine.
+ *
+ * @author Sean Owen
+ * @port Ashot Khanamiryan
+ */
+class MonochromeRectangleDetector {
+ private static $MAX_MODULES = 32;
+ private $image;
+ function __construct($image){
+ $this->image = $image;
+
+ }
+
+ /**
+ *
Detects a rectangular region of black and white -- mostly black -- with a region of mostly
+ * white, in an image.
+ *
+ * @return {@link ResultPoint}[] describing the corners of the rectangular region. The first and
+ * last points are opposed on the diagonal, as are the second and third. The first point will be
+ * the topmost point and the last, the bottommost. The second point will be leftmost and the
+ * third, the rightmost
+ * @throws NotFoundException if no Data Matrix Code can be found
+ */
+ public function detect(){
+
+ $height = $this->image->getHeight();
+ $width = $this->image->getWidth();
+ $halfHeight = $height / 2;
+ $halfWidth = $width / 2;
+
+ $deltaY = max(1, $height / (self::$MAX_MODULES * 8));
+ $deltaX = max(1, $width / (self::$MAX_MODULES * 8));
+
+
+ $top = 0;
+ $bottom = $height;
+ $left = 0;
+ $right = $width;
+ $pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
+ $halfHeight, -$deltaY, $top, $bottom, $halfWidth / 2);
+ $top = (int) $pointA->getY() - 1;
+ $pointB = $this->findCornerFromCenter($halfWidth, -$deltaX, $left,$right,
+ $halfHeight, 0, $top, $bottom, $halfHeight / 2);
+ $left = (int) $pointB->getX() - 1;
+ $pointC = $this->findCornerFromCenter($halfWidth, $deltaX, $left, $right,
+ $halfHeight, 0, $top, $bottom, $halfHeight / 2);
+ $right = (int) $pointC->getX() + 1;
+ $pointD = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
+ $halfHeight, $deltaY, $top, $bottom, $halfWidth / 2);
+ $bottom = (int) $pointD->getY() + 1;
+
+ // Go try to find po$A again with better information -- might have been off at first.
+ $pointA = $this->findCornerFromCenter($halfWidth, 0, $left, $right,
+ $halfHeight, -$deltaY, $top, $bottom, $halfWidth / 4);
+
+ return new ResultPoint( $pointA, $pointB, $pointC, $pointD );
+
+ }
+
+
+
+ /**
+ * Attempts to locate a corner of the barcode by scanning up, down, left or right from a center
+ * point which should be within the barcode.
+ *
+ * @param centerX center's x component (horizontal)
+ * @param deltaX same as deltaY but change in x per step instead
+ * @param left minimum value of x
+ * @param right maximum value of x
+ * @param centerY center's y component (vertical)
+ * @param deltaY change in y per step. If scanning up this is negative; down, positive;
+ * left or right, 0
+ * @param top minimum value of y to search through (meaningless when di == 0)
+ * @param bottom maximum value of y
+ * @param maxWhiteRun maximum run of white pixels that can still be considered to be within
+ * the barcode
+ * @return a {@link com.google.zxing.ResultPoint} encapsulating the corner that was found
+ * @throws NotFoundException if such a point cannot be found
+ */
+ private function findCornerFromCenter($centerX,
+ $deltaX,
+ $left,
+ $right,
+ $centerY,
+ $deltaY,
+ $top,
+ $bottom,
+ $maxWhiteRun){
+ $lastRange = null;
+ for ($y = $centerY, $x = $centerX;
+ $y < $bottom && $y >= $top && $x < $right && $x >= $left;
+ $y += $deltaY, $x += $deltaX) {
+ $range = 0;
+ if ($deltaX == 0) {
+ // horizontal slices, up and down
+ $range = $this->blackWhiteRange($y, $maxWhiteRun, $left, $right, true);
+ } else {
+ // vertical slices, left and right
+ $range = $this->blackWhiteRange($x, $maxWhiteRun, $top, $bottom, false);
+ }
+ if ($range == null) {
+ if ($lastRange == null) {
+ throw NotFoundException::getNotFoundInstance();
+ }
+ // lastRange was found
+ if ($deltaX == 0) {
+ $lastY = $y - $deltaY;
+ if ($lastRange[0] < $centerX) {
+ if ($lastRange[1] > $centerX) {
+ // straddle, choose one or the other based on direction
+ return new ResultPoint($deltaY > 0 ? $lastRange[0] : $lastRange[1], $lastY);
+ }
+ return new ResultPoint($lastRange[0], $lastY);
+ } else {
+ return new ResultPoint($lastRange[1], $lastY);
+ }
+ } else {
+ $lastX = $x - $deltaX;
+ if ($lastRange[0] < $centerY) {
+ if ($lastRange[1] > $centerY) {
+ return new ResultPoint($lastX, $deltaX < 0 ? $lastRange[0] : $lastRange[1]);
+ }
+ return new ResultPoint($lastX, $lastRange[0]);
+ } else {
+ return new ResultPoint($lastX, $lastRange[1]);
+ }
+ }
+ }
+ $lastRange = $range;
+ }
+ throw NotFoundException::getNotFoundInstance();
+ }
+
+
+
+ /**
+ * Computes the start and end of a region of pixels, either horizontally or vertically, that could
+ * be part of a Data Matrix barcode.
+ *
+ * @param fixedDimension if scanning horizontally, this is the row (the fixed vertical location)
+ * where we are scanning. If scanning vertically it's the column, the fixed horizontal location
+ * @param maxWhiteRun largest run of white pixels that can still be considered part of the
+ * barcode region
+ * @param minDim minimum pixel location, horizontally or vertically, to consider
+ * @param maxDim maximum pixel location, horizontally or vertically, to consider
+ * @param horizontal if true, we're scanning left-right, instead of up-down
+ * @return int[] with start and end of found range, or null if no such range is found
+ * (e.g. only white was found)
+ */
+
+ private function blackWhiteRange($fixedDimension, $maxWhiteRun, $minDim, $maxDim, $horizontal){
+ $center = ($minDim + $maxDim) / 2;
+
+ // Scan left/up first
+ $start = $center;
+ while ($start >= $minDim) {
+ if ($horizontal ? $this->image->get($start, $fixedDimension) : $this->image->get($fixedDimension, $start)) {
+ $start--;
+ } else {
+ $whiteRunStart = $start;
+ do {
+ $start--;
+ } while ($start >= $minDim && !($horizontal ? $this->image->get($start, $fixedDimension) :
+ $this->image->get($fixedDimension, $start)));
+ $whiteRunSize = $whiteRunStart - $start;
+ if ($start < $minDim || $whiteRunSize > $maxWhiteRun) {
+ $start = $whiteRunStart;
+ break;
+ }
+ }
+ }
+ $start++;
+
+ // Then try right/down
+ $end = $center;
+ while ($end < $maxDim) {
+ if ($horizontal ? $this->image->get($end, $fixedDimension) : $this->image->get($fixedDimension, $end)) {
+ $end++;
+ } else {
+ $whiteRunStart = $end;
+ do {
+ $end++;
+ } while ($end < $maxDim && !($horizontal ? $this->image->get($end, $fixedDimension) :
+ $this->image->get($fixedDimension, $end)));
+ $whiteRunSize = $end - $whiteRunStart;
+ if ($end >= $maxDim || $whiteRunSize > $maxWhiteRun) {
+ $end = $whiteRunStart;
+ break;
+ }
+ }
+ }
+ $end--;
+
+ return $end > $start ? array($start, $end) : null;
+ }
+}
\ No newline at end of file
diff --git a/public/qr-code/lib/common/reedsolomon/GenericGF.php b/public/qr-code/lib/common/reedsolomon/GenericGF.php
new file mode 100644
index 0000000..d004654
--- /dev/null
+++ b/public/qr-code/lib/common/reedsolomon/GenericGF.php
@@ -0,0 +1,181 @@
+This class contains utility methods for performing mathematical operations over
+ * the Galois Fields. Operations use a given primitive polynomial in calculations.
+ *
+ *
Throughout this package, elements of the GF are represented as an {@code int}
+ * for convenience and speed (but at the cost of memory).
+ *
+ *
+ * @author Sean Owen
+ * @author David Olivier
+ */
+final class GenericGF {
+
+ public static $AZTEC_DATA_12;
+ public static $AZTEC_DATA_10;
+ public static $AZTEC_DATA_6;
+ public static $AZTEC_PARAM;
+ public static $QR_CODE_FIELD_256;
+ public static $DATA_MATRIX_FIELD_256;
+ public static $AZTEC_DATA_8;
+ public static $MAXICODE_FIELD_64;
+
+ private $expTable;
+ private $logTable;
+ private $zero;
+ private $one;
+ private $size;
+ private $primitive;
+ private $generatorBase;
+
+
+ public static function Init(){
+ self::$AZTEC_DATA_12 = new GenericGF(0x1069, 4096, 1); // x^12 + x^6 + x^5 + x^3 + 1
+ self::$AZTEC_DATA_10 = new GenericGF(0x409, 1024, 1); // x^10 + x^3 + 1
+ self::$AZTEC_DATA_6 = new GenericGF(0x43, 64, 1); // x^6 + x + 1
+ self::$AZTEC_PARAM = new GenericGF(0x13, 16, 1); // x^4 + x + 1
+ self::$QR_CODE_FIELD_256 = new GenericGF(0x011D, 256, 0); // x^8 + x^4 + x^3 + x^2 + 1
+ self::$DATA_MATRIX_FIELD_256 = new GenericGF(0x012D, 256, 1); // x^8 + x^5 + x^3 + x^2 + 1
+ self::$AZTEC_DATA_8 = self::$DATA_MATRIX_FIELD_256;
+ self::$MAXICODE_FIELD_64 = self::$AZTEC_DATA_6;
+ }
+
+
+ /**
+ * Create a representation of GF(size) using the given primitive polynomial.
+ *
+ * @param primitive irreducible polynomial whose coefficients are represented by
+ * the bits of an int, where the least-significant bit represents the constant
+ * coefficient
+ * @param size the size of the field
+ * @param b the factor b in the generator polynomial can be 0- or 1-based
+ * (g(x) = (x+a^b)(x+a^(b+1))...(x+a^(b+2t-1))).
+ * In most cases it should be 1, but for QR code it is 0.
+ */
+ public function __construct($primitive, $size, $b) {
+ $this->primitive = $primitive;
+ $this->size = $size;
+ $this->generatorBase = $b;
+
+ $this->expTable = array();
+ $this->logTable =array();
+ $x = 1;
+ for ($i = 0; $i < $size; $i++) {
+ $this->expTable[$i] = $x;
+ $x *= 2; // we're assuming the generator alpha is 2
+ if ($x >= $size) {
+ $x ^= $primitive;
+ $x &= $size-1;
+ }
+ }
+ for ($i = 0; $i < $size-1; $i++) {
+ $this->logTable[$this->expTable[$i]] = $i;
+ }
+ // logTable[0] == 0 but this should never be used
+ $this->zero = new GenericGFPoly($this, array(0));
+ $this->one = new GenericGFPoly($this, array(1));
+ }
+
+ function getZero() {
+ return $this->zero;
+ }
+
+ function getOne() {
+ return $this->one;
+ }
+
+ /**
+ * @return the monomial representing coefficient * x^degree
+ */
+ function buildMonomial($degree, $coefficient) {
+ if ($degree < 0) {
+ throw new InvalidArgumentException();
+ }
+ if ($coefficient == 0) {
+ return $this->zero;
+ }
+ $coefficients = fill_array(0,$degree+1,0);//new int[degree + 1];
+ $coefficients[0] = $coefficient;
+ return new GenericGFPoly($this, $coefficients);
+ }
+
+ /**
+ * Implements both addition and subtraction -- they are the same in GF(size).
+ *
+ * @return sum/difference of a and b
+ */
+ static function addOrSubtract($a, $b) {
+ return $a ^ $b;
+ }
+
+ /**
+ * @return 2 to the power of a in GF(size)
+ */
+ function exp($a) {
+ return $this->expTable[$a];
+ }
+
+ /**
+ * @return base 2 log of a in GF(size)
+ */
+ function log($a) {
+ if ($a == 0) {
+ throw new InvalidArgumentException();
+ }
+ return $this->logTable[$a];
+ }
+
+ /**
+ * @return multiplicative inverse of a
+ */
+ function inverse($a) {
+ if ($a == 0) {
+ throw new Exception();
+ }
+ return $this->expTable[$this->size - $this->logTable[$a] - 1];
+ }
+
+ /**
+ * @return product of a and b in GF(size)
+ */
+ function multiply($a, $b) {
+ if ($a == 0 || $b == 0) {
+ return 0;
+ }
+ return $this->expTable[($this->logTable[$a] + $this->logTable[$b]) % ($this->size - 1)];
+ }
+
+ public function getSize() {
+ return $this->size;
+ }
+
+ public function getGeneratorBase() {
+ return $this->generatorBase;
+ }
+
+ // @Override
+ public function toString() {
+ return "GF(0x" . dechex(intval($this->primitive)) . ',' . $this->size . ')';
+ }
+
+}
+GenericGF::Init();
\ No newline at end of file
diff --git a/public/qr-code/lib/common/reedsolomon/GenericGFPoly.php b/public/qr-code/lib/common/reedsolomon/GenericGFPoly.php
new file mode 100644
index 0000000..9e9aa33
--- /dev/null
+++ b/public/qr-code/lib/common/reedsolomon/GenericGFPoly.php
@@ -0,0 +1,268 @@
+Represents a polynomial whose coefficients are elements of a GF.
+ * Instances of this class are immutable.
+ *
+ *
Much credit is due to William Rucklidge since portions of this code are an indirect
+ * port of his C++ Reed-Solomon implementation.
+ *
+ * @author Sean Owen
+ */
+final class GenericGFPoly {
+
+ private $field;
+ private $coefficients;
+
+ /**
+ * @param field the {@link GenericGF} instance representing the field to use
+ * to perform computations
+ * @param coefficients array coefficients as ints representing elements of GF(size), arranged
+ * from most significant (highest-power term) coefficient to least significant
+ * @throws IllegalArgumentException if argument is null or empty,
+ * or if leading coefficient is 0 and this is not a
+ * constant polynomial (that is, it is not the monomial "0")
+ */
+ function __construct($field, $coefficients) {
+ if (count($coefficients) == 0) {
+ throw new \InvalidArgumentException();
+ }
+ $this->field = $field;
+ $coefficientsLength = count($coefficients);
+ if ($coefficientsLength > 1 && $coefficients[0] == 0) {
+ // Leading term must be non-zero for anything except the constant polynomial "0"
+ $firstNonZero = 1;
+ while ($firstNonZero < $coefficientsLength && $coefficients[$firstNonZero] == 0) {
+ $firstNonZero++;
+ }
+ if ($firstNonZero == $coefficientsLength) {
+ $this->coefficients = array(0);
+ } else {
+ $this->coefficients = fill_array(0,$coefficientsLength - $firstNonZero,0);
+ $this->coefficients = arraycopy($coefficients,
+ $firstNonZero,
+ $this->coefficients,
+ 0,
+ count($this->coefficients));
+ }
+ } else {
+ $this->coefficients = $coefficients;
+ }
+ }
+
+ function getCoefficients() {
+ return $this->coefficients;
+ }
+
+ /**
+ * @return degree of this polynomial
+ */
+ function getDegree() {
+ return count($this->coefficients) - 1;
+ }
+
+ /**
+ * @return true iff this polynomial is the monomial "0"
+ */
+ function isZero() {
+ return $this->coefficients[0] == 0;
+ }
+
+ /**
+ * @return coefficient of x^degree term in this polynomial
+ */
+ function getCoefficient($degree) {
+ return $this->coefficients[count($this->coefficients) - 1 - $degree];
+ }
+
+ /**
+ * @return evaluation of this polynomial at a given point
+ */
+ function evaluateAt($a) {
+ if ($a == 0) {
+ // Just return the x^0 coefficient
+ return $this->getCoefficient(0);
+ }
+ $size = count($this->coefficients);
+ if ($a == 1) {
+ // Just the sum of the coefficients
+ $result = 0;
+ foreach ($this->coefficients as $coefficient ) {
+ $result = GenericGF::addOrSubtract($result, $coefficient);
+ }
+ return $result;
+ }
+ $result = $this->coefficients[0];
+ for ($i = 1; $i < $size; $i++) {
+ $result = GenericGF::addOrSubtract($this->field->multiply($a, $result), $this->coefficients[$i]);
+ }
+ return $result;
+ }
+
+ function addOrSubtract($other) {
+ if ($this->field !== $other->field) {
+ throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
+ }
+ if ($this->isZero()) {
+ return $other;
+ }
+ if ($other->isZero()) {
+ return $this;
+ }
+
+ $smallerCoefficients = $this->coefficients;
+ $largerCoefficients = $other->coefficients;
+ if (count($smallerCoefficients) > count($largerCoefficients)) {
+ $temp = $smallerCoefficients;
+ $smallerCoefficients = $largerCoefficients;
+ $largerCoefficients = $temp;
+ }
+ $sumDiff = fill_array(0,count($largerCoefficients),0);
+ $lengthDiff = count($largerCoefficients) - count($smallerCoefficients);
+ // Copy high-order terms only found in higher-degree polynomial's coefficients
+ $sumDiff = arraycopy($largerCoefficients, 0, $sumDiff, 0, $lengthDiff);
+
+ for ($i = $lengthDiff; $i < count($largerCoefficients); $i++) {
+ $sumDiff[$i] = GenericGF::addOrSubtract($smallerCoefficients[$i - $lengthDiff], $largerCoefficients[$i]);
+ }
+
+ return new GenericGFPoly($this->field, $sumDiff);
+ }
+
+ function multiply($other) {
+ if(is_int($other)){
+ return $this->multiply_($other);
+ }
+ if ($this->field !== $other->field) {
+ throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
+ }
+ if ($this->isZero() || $other->isZero()) {
+ return $this->field->getZero();
+ }
+ $aCoefficients = $this->coefficients;
+ $aLength = count($aCoefficients);
+ $bCoefficients = $other->coefficients;
+ $bLength = count($bCoefficients);
+ $product = fill_array(0,$aLength + $bLength - 1,0);
+ for ($i = 0; $i < $aLength; $i++) {
+ $aCoeff = $aCoefficients[$i];
+ for ($j = 0; $j < $bLength; $j++) {
+ $product[$i + $j] = GenericGF::addOrSubtract($product[$i + $j],
+ $this->field->multiply($aCoeff, $bCoefficients[$j]));
+ }
+ }
+ return new GenericGFPoly($this->field, $product);
+ }
+
+ function multiply_($scalar) {
+ if ($scalar == 0) {
+ return $this->field->getZero();
+ }
+ if ($scalar == 1) {
+ return $this;
+ }
+ $size = count($this->coefficients);
+ $product = fill_array(0,$size,0);
+ for ($i = 0; $i < $size; $i++) {
+ $product[$i] = $this->field->multiply($this->coefficients[$i], $scalar);
+ }
+ return new GenericGFPoly($this->field, $product);
+ }
+
+ function multiplyByMonomial($degree, $coefficient) {
+ if ($degree < 0) {
+ throw new \InvalidArgumentException();
+ }
+ if ($coefficient == 0) {
+ return $this->field->getZero();
+ }
+ $size = count($this->coefficients);
+ $product = fill_array(0,$size + $degree,0);
+ for ($i = 0; $i < $size; $i++) {
+ $product[$i] = $this->field->multiply($this->coefficients[$i], $coefficient);
+ }
+ return new GenericGFPoly($this->field, $product);
+ }
+
+ function divide($other) {
+ if ($this->field !==$other->field) {
+ throw new \InvalidArgumentException("GenericGFPolys do not have same GenericGF field");
+ }
+ if ($other->isZero()) {
+ throw new \InvalidArgumentException("Divide by 0");
+ }
+
+ $quotient = $this->field->getZero();
+ $remainder = $this;
+
+ $denominatorLeadingTerm = $other->getCoefficient($other->getDegree());
+ $inverseDenominatorLeadingTerm = $this->field->inverse($denominatorLeadingTerm);
+
+ while ($remainder->getDegree() >= $other->getDegree() && !$remainder->isZero()) {
+ $degreeDifference = $remainder->getDegree() - $other->getDegree();
+ $scale = $this->field->multiply($remainder->getCoefficient($remainder->getDegree()), $inverseDenominatorLeadingTerm);
+ $term = $other->multiplyByMonomial($degreeDifference, $scale);
+ $iterationQuotient = $this->field->buildMonomial($degreeDifference, $scale);
+ $quotient = $quotient->addOrSubtract($iterationQuotient);
+ $remainder = $remainder->addOrSubtract($term);
+ }
+
+ return array($quotient, $remainder );
+ }
+
+ //@Override
+ public function toString() {
+ $result = '';
+ for ($degree = $this->getDegree(); $degree >= 0; $degree--) {
+ $coefficient = $this->getCoefficient($degree);
+ if ($coefficient != 0) {
+ if ($coefficient < 0) {
+ $result.=" - ";
+ $coefficient = -$coefficient;
+ } else {
+ if (strlen($result) > 0) {
+ $result .= " + ";
+ }
+ }
+ if ($degree == 0 || $coefficient != 1) {
+ $alphaPower = $this->field->log($coefficient);
+ if ($alphaPower == 0) {
+ $result.='1';
+ } else if ($alphaPower == 1) {
+ $result.='a';
+ } else {
+ $result.="a^";
+ $result.=($alphaPower);
+ }
+ }
+ if ($degree != 0) {
+ if ($degree == 1) {
+ $result.='x';
+ } else {
+ $result.="x^";
+ $result.= $degree;
+ }
+ }
+ }
+ }
+ return $result;
+ }
+
+}
diff --git a/public/qr-code/lib/common/reedsolomon/ReedSolomonDecoder.php b/public/qr-code/lib/common/reedsolomon/ReedSolomonDecoder.php
new file mode 100644
index 0000000..3fb1c2e
--- /dev/null
+++ b/public/qr-code/lib/common/reedsolomon/ReedSolomonDecoder.php
@@ -0,0 +1,192 @@
+Implements Reed-Solomon decoding, as the name implies.
+ *
+ *
The algorithm will not be explained here, but the following references were helpful
+ * in creating this implementation:
Much credit is due to William Rucklidge since portions of this code are an indirect
+ * port of his C++ Reed-Solomon implementation.
+ *
+ * @author Sean Owen
+ * @author William Rucklidge
+ * @author sanfordsquires
+ */
+final class ReedSolomonDecoder {
+
+ private $field;
+
+ public function __construct($field) {
+ $this->field = $field;
+ }
+
+ /**
+ *
Decodes given set of received codewords, which include both data and error-correction
+ * codewords. Really, this means it uses Reed-Solomon to detect and correct errors, in-place,
+ * in the input.
+ *
+ * @param received data and error-correction codewords
+ * @param twoS number of error-correction codewords available
+ * @throws ReedSolomonException if decoding fails for any reason
+ */
+ public function decode(&$received, $twoS) {
+ $poly = new GenericGFPoly($this->field, $received);
+ $syndromeCoefficients = fill_array(0,$twoS,0);
+ $noError = true;
+ for ($i = 0; $i < $twoS; $i++) {
+ $eval = $poly->evaluateAt($this->field->exp($i + $this->field->getGeneratorBase()));
+ $syndromeCoefficients[count($syndromeCoefficients) - 1 - $i] = $eval;
+ if ($eval != 0) {
+ $noError = false;
+ }
+ }
+ if ($noError) {
+ return;
+ }
+ $syndrome = new GenericGFPoly($this->field, $syndromeCoefficients);
+ $sigmaOmega =
+ $this->runEuclideanAlgorithm($this->field->buildMonomial($twoS, 1), $syndrome, $twoS);
+ $sigma = $sigmaOmega[0];
+ $omega = $sigmaOmega[1];
+ $errorLocations = $this->findErrorLocations($sigma);
+ $errorMagnitudes = $this->findErrorMagnitudes($omega, $errorLocations);
+ for ($i = 0; $i < count($errorLocations); $i++) {
+ $position = count($received) - 1 - $this->field->log($errorLocations[$i]);
+ if ($position < 0) {
+ throw new ReedSolomonException("Bad error location");
+ }
+ $received[$position] = GenericGF::addOrSubtract($received[$position], $errorMagnitudes[$i]);
+ }
+
+ }
+
+ private function runEuclideanAlgorithm($a, $b, $R)
+ {
+ // Assume a's degree is >= b's
+ if ($a->getDegree() < $b->getDegree()) {
+ $temp = $a;
+ $a = $b;
+ $b = $temp;
+ }
+
+ $rLast = $a;
+ $r = $b;
+ $tLast = $this->field->getZero();
+ $t = $this->field->getOne();
+
+ // Run Euclidean algorithm until r's degree is less than R/2
+ while ($r->getDegree() >= $R / 2) {
+ $rLastLast = $rLast;
+ $tLastLast = $tLast;
+ $rLast = $r;
+ $tLast = $t;
+
+ // Divide rLastLast by rLast, with quotient in q and remainder in r
+ if ($rLast->isZero()) {
+ // Oops, Euclidean algorithm already terminated?
+ throw new ReedSolomonException("r_{i-1} was zero");
+ }
+ $r = $rLastLast;
+ $q = $this->field->getZero();
+ $denominatorLeadingTerm = $rLast->getCoefficient($rLast->getDegree());
+ $dltInverse = $this->field->inverse($denominatorLeadingTerm);
+ while ($r->getDegree() >= $rLast->getDegree() && !$r->isZero()) {
+ $degreeDiff = $r->getDegree() - $rLast->getDegree();
+ $scale = $this->field->multiply($r->getCoefficient($r->getDegree()), $dltInverse);
+ $q = $q->addOrSubtract($this->field->buildMonomial($degreeDiff, $scale));
+ $r = $r->addOrSubtract($rLast->multiplyByMonomial($degreeDiff, $scale));
+ }
+
+ $t = $q->multiply($tLast)->addOrSubtract($tLastLast);
+
+ if ($r->getDegree() >= $rLast->getDegree()) {
+ throw new IllegalStateException("Division algorithm failed to reduce polynomial?");
+ }
+ }
+
+ $sigmaTildeAtZero = $t->getCoefficient(0);
+ if ($sigmaTildeAtZero == 0) {
+ throw new ReedSolomonException("sigmaTilde(0) was zero");
+ }
+
+ $inverse = $this->field->inverse($sigmaTildeAtZero);
+ $sigma = $t->multiply($inverse);
+ $omega = $r->multiply($inverse);
+ return array($sigma, $omega);
+ }
+
+ private function findErrorLocations($errorLocator) {
+ // This is a direct application of Chien's search
+ $numErrors = $errorLocator->getDegree();
+ if ($numErrors == 1) { // shortcut
+ return array($errorLocator->getCoefficient(1) );
+ }
+ $result = fill_array(0,$numErrors,0);
+ $e = 0;
+ for ($i = 1; $i < $this->field->getSize() && $e < $numErrors; $i++) {
+ if ($errorLocator->evaluateAt($i) == 0) {
+ $result[$e] = $this->field->inverse($i);
+ $e++;
+ }
+ }
+ if ($e != $numErrors) {
+ throw new ReedSolomonException("Error locator degree does not match number of roots");
+ }
+ return $result;
+ }
+
+ private function findErrorMagnitudes($errorEvaluator, $errorLocations) {
+ // This is directly applying Forney's Formula
+ $s = count($errorLocations);
+ $result = fill_array(0,$s,0);
+ for ($i = 0; $i < $s; $i++) {
+ $xiInverse = $this->field->inverse($errorLocations[$i]);
+ $denominator = 1;
+ for ($j = 0; $j < $s; $j++) {
+ if ($i != $j) {
+ //denominator = field.multiply(denominator,
+ // GenericGF.addOrSubtract(1, field.multiply(errorLocations[j], xiInverse)));
+ // Above should work but fails on some Apple and Linux JDKs due to a Hotspot bug.
+ // Below is a funny-looking workaround from Steven Parkes
+ $term = $this->field->multiply($errorLocations[$j], $xiInverse);
+ $termPlus1 = ($term & 0x1) == 0 ? $term | 1 : $term & ~1;
+ $denominator = $this->field->multiply($denominator, $termPlus1);
+ }
+ }
+ $result[$i] = $this->field->multiply($errorEvaluator->evaluateAt($xiInverse),
+ $this->field->inverse($denominator));
+ if ($this->field->getGeneratorBase() != 0) {
+ $result[$i] = $this->field->multiply($result[$i], $xiInverse);
+ }
+ }
+ return $result;
+ }
+
+}
diff --git a/public/qr-code/lib/common/reedsolomon/ReedSolomonException.php b/public/qr-code/lib/common/reedsolomon/ReedSolomonException.php
new file mode 100644
index 0000000..10ac55e
--- /dev/null
+++ b/public/qr-code/lib/common/reedsolomon/ReedSolomonException.php
@@ -0,0 +1,31 @@
+Thrown when an exception occurs during Reed-Solomon decoding, such as when
+ * there are too many errors to correct.
+*
+* @author Sean Owen
+*/
+final class ReedSolomonException extends \Exception {
+
+
+}
+
+?>
\ No newline at end of file
diff --git a/public/qr-code/lib/qrcode/QRCodeReader.php b/public/qr-code/lib/qrcode/QRCodeReader.php
new file mode 100644
index 0000000..a354dca
--- /dev/null
+++ b/public/qr-code/lib/qrcode/QRCodeReader.php
@@ -0,0 +1,224 @@
+decoder = new Decoder();
+
+ }
+
+ protected final function getDecoder() {
+ return $this->decoder;
+ }
+
+ /**
+ * Locates and decodes a QR code in an image.
+ *
+ * @return a String representing the content encoded by the QR code
+ * @throws NotFoundException if a QR code cannot be found
+ * @throws FormatException if a QR code cannot be decoded
+ * @throws ChecksumException if error correction fails
+ */
+ //@Override
+
+
+ // @Override
+ public function decode($image, $hints=null){/* Map hints*/
+ $decoderResult = null;
+ $points = array();
+ if ($hints != null && $hints['PURE_BARCODE']) {//hints.containsKey(DecodeHintType.PURE_BARCODE)) {
+ $bits = $this->extractPureBits($image->getBlackMatrix());
+ $decoderResult = $this->decoder->decode($bits, $hints);
+ $points = self::$NO_POINTS;
+ } else {
+ $detector = new Detector($image->getBlackMatrix());
+ $detectorResult = $detector->detect($hints);
+
+ $decoderResult = $this->decoder->decode($detectorResult->getBits(), $hints);
+ $points = $detectorResult->getPoints();
+ }
+
+ // If the code was mirrored: swap the bottom-left and the top-right points.
+ if ($decoderResult->getOther() instanceof QRCodeDecoderMetaData) {
+ $decoderResult->getOther()->applyMirroredCorrection($points);
+ }
+
+ $result = new Result($decoderResult->getText(), $decoderResult->getRawBytes(), $points, 'QR_CODE');//BarcodeFormat.QR_CODE
+ $byteSegments = $decoderResult->getByteSegments();
+ if ($byteSegments != null) {
+ $result->putMetadata('BYTE_SEGMENTS', $byteSegments);//ResultMetadataType.BYTE_SEGMENTS
+ }
+ $ecLevel = $decoderResult->getECLevel();
+ if ($ecLevel != null) {
+ $result->putMetadata('ERROR_CORRECTION_LEVEL', $ecLevel);//ResultMetadataType.ERROR_CORRECTION_LEVEL
+ }
+ if ($decoderResult->hasStructuredAppend()) {
+ $result->putMetadata('STRUCTURED_APPEND_SEQUENCE',//ResultMetadataType.STRUCTURED_APPEND_SEQUENCE
+ $decoderResult->getStructuredAppendSequenceNumber());
+ $result->putMetadata('STRUCTURED_APPEND_PARITY',//ResultMetadataType.STRUCTURED_APPEND_PARITY
+ $decoderResult->getStructuredAppendParity());
+ }
+ return $result;
+ }
+
+ //@Override
+ public function reset() {
+ // do nothing
+ }
+
+ /**
+ * This method detects a code in a "pure" image -- that is, pure monochrome image
+ * which contains only an unrotated, unskewed, image of a code, with some white border
+ * around it. This is a specialized method that works exceptionally fast in this special
+ * case.
+ *
+ * @see com.google.zxing.datamatrix.DataMatrixReader#extractPureBits(BitMatrix)
+ */
+ private static function extractPureBits($image) {
+
+ $leftTopBlack = $image->getTopLeftOnBit();
+ $rightBottomBlack = $image->getBottomRightOnBit();
+ if ($leftTopBlack == null || $rightBottomBlack == null) {
+ throw NotFoundException::getNotFoundInstance();
+ }
+
+ $moduleSize = self::moduleSize($leftTopBlack, $image);
+
+ $top = $leftTopBlack[1];
+ $bottom = $rightBottomBlack[1];
+ $left = $leftTopBlack[0];
+ $right = $rightBottomBlack[0];
+
+ // Sanity check!
+ if ($left >= $right || $top >= $bottom) {
+ throw NotFoundException::getNotFoundInstance();
+ }
+
+ if ($bottom - $top != $right - $left) {
+ // Special case, where bottom-right module wasn't black so we found something else in the last row
+ // Assume it's a square, so use height as the width
+ $right = $left + ($bottom - $top);
+ }
+
+ $matrixWidth = round(($right - $left + 1) / $moduleSize);
+ $matrixHeight = round(($bottom - $top + 1) / $moduleSize);
+ if ($matrixWidth <= 0 || $matrixHeight <= 0) {
+ throw NotFoundException::getNotFoundInstance();
+ }
+ if ($matrixHeight != $matrixWidth) {
+ // Only possibly decode square regions
+ throw NotFoundException::getNotFoundInstance();
+ }
+
+ // Push in the "border" by half the module width so that we start
+ // sampling in the middle of the module. Just in case the image is a
+ // little off, this will help recover.
+ $nudge = (int) ($moduleSize / 2.0);// $nudge = (int) ($moduleSize / 2.0f);
+ $top += $nudge;
+ $left += $nudge;
+
+ // But careful that this does not sample off the edge
+ // "right" is the farthest-right valid pixel location -- right+1 is not necessarily
+ // This is positive by how much the inner x loop below would be too large
+ $nudgedTooFarRight = $left + (int) (($matrixWidth - 1) * $moduleSize) - $right;
+ if ($nudgedTooFarRight > 0) {
+ if ($nudgedTooFarRight > $nudge) {
+ // Neither way fits; abort
+ throw NotFoundException::getNotFoundInstance();
+ }
+ $left -= $nudgedTooFarRight;
+ }
+ // See logic above
+ $nudgedTooFarDown = $top + (int) (($matrixHeight - 1) * $moduleSize) - $bottom;
+ if ($nudgedTooFarDown > 0) {
+ if ($nudgedTooFarDown > $nudge) {
+ // Neither way fits; abort
+ throw NotFoundException::getNotFoundInstance();
+ }
+ $top -= $nudgedTooFarDown;
+ }
+
+ // Now just read off the bits
+ $bits = new BitMatrix($matrixWidth, $matrixHeight);
+ for ($y = 0; $y < $matrixHeight; $y++) {
+ $iOffset = $top + (int) ($y * $moduleSize);
+ for ($x = 0; $x < $matrixWidth; $x++) {
+ if ($image->get($left + (int) ($x * $moduleSize), $iOffset)) {
+ $bits->set($x, $y);
+ }
+ }
+ }
+ return $bits;
+ }
+
+ private static function moduleSize($leftTopBlack, $image) {
+ $height = $image->getHeight();
+ $width = $image->getWidth();
+ $x = $leftTopBlack[0];
+ $y = $leftTopBlack[1];
+ $inBlack = true;
+ $transitions = 0;
+ while ($x < $width && $y < $height) {
+ if ($inBlack != $image->get($x, $y)) {
+ if (++$transitions == 5) {
+ break;
+ }
+ $inBlack = !$inBlack;
+ }
+ $x++;
+ $y++;
+ }
+ if ($x == $width || $y == $height) {
+ throw NotFoundException::getNotFoundInstance();
+ }
+ return ($x - $leftTopBlack[0]) / 7.0; //return ($x - $leftTopBlack[0]) / 7.0f;
+ }
+
+}
diff --git a/public/qr-code/lib/qrcode/decoder/BitMatrixParser.php b/public/qr-code/lib/qrcode/decoder/BitMatrixParser.php
new file mode 100644
index 0000000..510cfa8
--- /dev/null
+++ b/public/qr-code/lib/qrcode/decoder/BitMatrixParser.php
@@ -0,0 +1,250 @@
+= 21 and 1 mod 4
+ */
+ function __construct($bitMatrix) {
+ $dimension = $bitMatrix->getHeight();
+ if ($dimension < 21 || ($dimension & 0x03) != 1) {
+ throw FormatException::getFormatInstance();
+ }
+ $this->bitMatrix = $bitMatrix;
+ }
+
+ /**
+ *
Reads format information from one of its two locations within the QR Code.
+ *
+ * @return {@link FormatInformation} encapsulating the QR Code's format info
+ * @throws FormatException if both format information locations cannot be parsed as
+ * the valid encoding of format information
+ */
+ function readFormatInformation() {
+
+ if ($this->parsedFormatInfo != null) {
+ return $this->parsedFormatInfo;
+ }
+
+ // Read top-left format info bits
+ $formatInfoBits1 = 0;
+ for ($i = 0; $i < 6; $i++) {
+ $formatInfoBits1 = $this->copyBit($i, 8, $formatInfoBits1);
+ }
+ // .. and skip a bit in the timing pattern ...
+ $formatInfoBits1 = $this->copyBit(7, 8, $formatInfoBits1);
+ $formatInfoBits1 = $this->copyBit(8, 8, $formatInfoBits1);
+ $formatInfoBits1 = $this->copyBit(8, 7, $formatInfoBits1);
+ // .. and skip a bit in the timing pattern ...
+ for ($j = 5; $j >= 0; $j--) {
+ $formatInfoBits1 = $this->copyBit(8, $j, $formatInfoBits1);
+ }
+
+ // Read the top-right/bottom-left pattern too
+ $dimension = $this->bitMatrix->getHeight();
+ $formatInfoBits2 = 0;
+ $jMin = $dimension - 7;
+ for ($j = $dimension - 1; $j >= $jMin; $j--) {
+ $formatInfoBits2 = $this->copyBit(8, $j, $formatInfoBits2);
+ }
+ for ($i = $dimension - 8; $i < $dimension; $i++) {
+ $formatInfoBits2 = $this->copyBit($i, 8, $formatInfoBits2);
+ }
+
+ $parsedFormatInfo = FormatInformation::decodeFormatInformation($formatInfoBits1, $formatInfoBits2);
+ if ($parsedFormatInfo != null) {
+ return $parsedFormatInfo;
+ }
+ throw FormatException::getFormatInstance();
+ }
+
+ /**
+ *
Reads version information from one of its two locations within the QR Code.
Reads the bits in the {@link BitMatrix} representing the finder pattern in the
+ * correct order in order to reconstruct the codewords bytes contained within the
+ * QR Code.
+ *
+ * @return bytes encoded within the QR Code
+ * @throws FormatException if the exact number of bytes expected is not read
+ */
+ function readCodewords(){
+
+ $formatInfo = $this->readFormatInformation();
+ $version = $this->readVersion();
+
+ // Get the data mask for the format used in this QR Code. This will exclude
+ // some bits from reading as we wind through the bit matrix.
+ $dataMask = DataMask::forReference($formatInfo->getDataMask());
+ $dimension = $this->bitMatrix->getHeight();
+ $dataMask->unmaskBitMatrix($this->bitMatrix, $dimension);
+
+ $functionPattern = $version->buildFunctionPattern();
+
+ $readingUp = true;
+ if($version->getTotalCodewords()) {
+ $result = fill_array(0, $version->getTotalCodewords(), 0);
+ }else{
+ $result = array();
+ }
+ $resultOffset = 0;
+ $currentByte = 0;
+ $bitsRead = 0;
+ // Read columns in pairs, from right to left
+ for ($j = $dimension - 1; $j > 0; $j -= 2) {
+ if ($j == 6) {
+ // Skip whole column with vertical alignment pattern;
+ // saves time and makes the other code proceed more cleanly
+ $j--;
+ }
+ // Read alternatingly from bottom to top then top to bottom
+ for ($count = 0; $count < $dimension; $count++) {
+ $i = $readingUp ? $dimension - 1 - $count : $count;
+ for ($col = 0; $col < 2; $col++) {
+ // Ignore bits covered by the function pattern
+ if (!$functionPattern->get($j - $col, $i)) {
+ // Read a bit
+ $bitsRead++;
+ $currentByte <<= 1;
+ if ($this->bitMatrix->get($j - $col, $i)) {
+ $currentByte |= 1;
+ }
+ // If we've made a whole byte, save it off
+ if ($bitsRead == 8) {
+ $result[$resultOffset++] = $currentByte; //(byte)
+ $bitsRead = 0;
+ $currentByte = 0;
+ }
+ }
+ }
+ }
+ $readingUp ^= true; // readingUp = !readingUp; // switch directions
+ }
+ if ($resultOffset != $version->getTotalCodewords()) {
+ throw FormatException::getFormatInstance();
+ }
+ return $result;
+ }
+
+ /**
+ * Revert the mask removal done while reading the code words. The bit matrix should revert to its original state.
+ */
+ function remask() {
+ if ($this->parsedFormatInfo == null) {
+ return; // We have no format information, and have no data mask
+ }
+ $dataMask = DataMask::forReference($this->parsedFormatInfo->getDataMask());
+ $dimension = $this->bitMatrix->getHeight();
+ $dataMask->unmaskBitMatrix($this->bitMatrix, $dimension);
+ }
+
+ /**
+ * Prepare the parser for a mirrored operation.
+ * This flag has effect only on the {@link #readFormatInformation()} and the
+ * {@link #readVersion()}. Before proceeding with {@link #readCodewords()} the
+ * {@link #mirror()} method should be called.
+ *
+ * @param mirror Whether to read version and format information mirrored.
+ */
+ function setMirror($mirror) {
+ $parsedVersion = null;
+ $parsedFormatInfo = null;
+ $this->mirror = $mirror;
+ }
+
+ /** Mirror the bit matrix in order to attempt a second reading. */
+ function mirror() {
+ for ($x = 0; $x < $this->bitMatrix->getWidth(); $x++) {
+ for ($y = $x + 1; $y < $this->bitMatrix->getHeight(); $y++) {
+ if ($this->bitMatrix->get($x, $y) != $this->bitMatrix->get($y, $x)) {
+ $this->bitMatrix->flip($y, $x);
+ $this->bitMatrix->flip($x, $y);
+ }
+ }
+ }
+ }
+
+}
diff --git a/public/qr-code/lib/qrcode/decoder/DataBlock.php b/public/qr-code/lib/qrcode/decoder/DataBlock.php
new file mode 100644
index 0000000..803c14f
--- /dev/null
+++ b/public/qr-code/lib/qrcode/decoder/DataBlock.php
@@ -0,0 +1,123 @@
+Encapsulates a block of data within a QR Code. QR Codes may split their data into
+ * multiple blocks, each of which is a unit of data and error-correction codewords. Each
+ * is represented by an instance of this class.
+ *
+ * @author Sean Owen
+ */
+final class DataBlock {
+
+ private $numDataCodewords;
+ private $codewords; //byte[]
+
+ private function __construct($numDataCodewords, $codewords) {
+ $this->numDataCodewords = $numDataCodewords;
+ $this->codewords = $codewords;
+ }
+
+ /**
+ *
When QR Codes use multiple data blocks, they are actually interleaved.
+ * That is, the first byte of data block 1 to n is written, then the second bytes, and so on. This
+ * method will separate the data into original blocks.
+ *
+ * @param rawCodewords bytes as read directly from the QR Code
+ * @param version version of the QR Code
+ * @param ecLevel error-correction level of the QR Code
+ * @return DataBlocks containing original bytes, "de-interleaved" from representation in the
+ * QR Code
+ */
+ static function getDataBlocks($rawCodewords,
+ $version,
+ $ecLevel) {
+
+ if (count($rawCodewords) != $version->getTotalCodewords()) {
+ throw new \InvalidArgumentException();
+ }
+
+ // Figure out the number and size of data blocks used by this version and
+ // error correction level
+ $ecBlocks = $version->getECBlocksForLevel($ecLevel);
+
+ // First count the total number of data blocks
+ $totalBlocks = 0;
+ $ecBlockArray = $ecBlocks->getECBlocks();
+ foreach ($ecBlockArray as $ecBlock) {
+ $totalBlocks += $ecBlock->getCount();
+ }
+
+ // Now establish DataBlocks of the appropriate size and number of data codewords
+ $result = array();//new DataBlock[$totalBlocks];
+ $numResultBlocks = 0;
+ foreach ($ecBlockArray as $ecBlock) {
+ for ($i = 0; $i < $ecBlock->getCount(); $i++) {
+ $numDataCodewords = $ecBlock->getDataCodewords();
+ $numBlockCodewords = $ecBlocks->getECCodewordsPerBlock() + $numDataCodewords;
+ $result[$numResultBlocks++] = new DataBlock($numDataCodewords, fill_array(0,$numBlockCodewords,0));
+ }
+ }
+
+ // All blocks have the same amount of data, except that the last n
+ // (where n may be 0) have 1 more byte. Figure out where these start.
+ $shorterBlocksTotalCodewords = count($result[0]->codewords);
+ $longerBlocksStartAt = count($result) - 1;
+ while ($longerBlocksStartAt >= 0) {
+ $numCodewords = count($result[$longerBlocksStartAt]->codewords);
+ if ($numCodewords == $shorterBlocksTotalCodewords) {
+ break;
+ }
+ $longerBlocksStartAt--;
+ }
+ $longerBlocksStartAt++;
+
+ $shorterBlocksNumDataCodewords = $shorterBlocksTotalCodewords - $ecBlocks->getECCodewordsPerBlock();
+ // The last elements of result may be 1 element longer;
+ // first fill out as many elements as all of them have
+ $rawCodewordsOffset = 0;
+ for ($i = 0; $i < $shorterBlocksNumDataCodewords; $i++) {
+ for ($j = 0; $j < $numResultBlocks; $j++) {
+ $result[$j]->codewords[$i] = $rawCodewords[$rawCodewordsOffset++];
+ }
+ }
+ // Fill out the last data block in the longer ones
+ for ($j = $longerBlocksStartAt; $j < $numResultBlocks; $j++) {
+ $result[$j]->codewords[$shorterBlocksNumDataCodewords] = $rawCodewords[$rawCodewordsOffset++];
+ }
+ // Now add in error correction blocks
+ $max = count($result[0]->codewords);
+ for ($i = $shorterBlocksNumDataCodewords; $i < $max; $i++) {
+ for ($j = 0; $j < $numResultBlocks; $j++) {
+ $iOffset = $j < $longerBlocksStartAt ? $i : $i + 1;
+ $result[$j]->codewords[$iOffset] = $rawCodewords[$rawCodewordsOffset++];
+ }
+ }
+ return $result;
+ }
+
+ function getNumDataCodewords() {
+ return $this->numDataCodewords;
+ }
+
+ function getCodewords() {
+ return $this->codewords;
+ }
+
+}
diff --git a/public/qr-code/lib/qrcode/decoder/DataMask.php b/public/qr-code/lib/qrcode/decoder/DataMask.php
new file mode 100644
index 0000000..7e5f2bb
--- /dev/null
+++ b/public/qr-code/lib/qrcode/decoder/DataMask.php
@@ -0,0 +1,175 @@
+Encapsulates data masks for the data bits in a QR code, per ISO 18004:2006 6.8. Implementations
+ * of this class can un-mask a raw BitMatrix. For simplicity, they will unmask the entire BitMatrix,
+ * including areas used for finder patterns, timing patterns, etc. These areas should be unused
+ * after the point they are unmasked anyway.
+ *
+ *
Note that the diagram in section 6.8.1 is misleading since it indicates that i is column position
+ * and j is row position. In fact, as the text says, i is row position and j is column position.
+ *
+ * @author Sean Owen
+ */
+abstract class DataMask
+{
+
+ /**
+ * See ISO 18004:2006 6.8.1
+ */
+ private static $DATA_MASKS = array();
+
+ static function Init()
+ {
+ self::$DATA_MASKS = array(
+ new DataMask000(),
+ new DataMask001(),
+ new DataMask010(),
+ new DataMask011(),
+ new DataMask100(),
+ new DataMask101(),
+ new DataMask110(),
+ new DataMask111(),
+ );
+ }
+
+ function __construct()
+ {
+
+ }
+
+ /**
+ *
Implementations of this method reverse the data masking process applied to a QR Code and
+ * make its bits ready to read.
+ *
+ * @param bits representation of QR Code bits
+ * @param dimension dimension of QR Code, represented by bits, being unmasked
+ */
+ final function unmaskBitMatrix($bits, $dimension)
+ {
+ for ($i = 0; $i < $dimension; $i++) {
+ for ($j = 0; $j < $dimension; $j++) {
+ if ($this->isMasked($i, $j)) {
+ $bits->flip($j, $i);
+ }
+ }
+ }
+ }
+
+ abstract function isMasked($i, $j);
+
+ /**
+ * @param reference a value between 0 and 7 indicating one of the eight possible
+ * data mask patterns a QR Code may use
+ * @return DataMask encapsulating the data mask pattern
+ */
+ static function forReference($reference)
+ {
+ if ($reference < 0 || $reference > 7) {
+ throw new InvalidArgumentException();
+ }
+ return self::$DATA_MASKS[$reference];
+ }
+}
+DataMask::Init();
+/**
+ * 000: mask bits for which (x + y) mod 2 == 0
+ */
+final class DataMask000 extends DataMask {
+ // @Override
+ function isMasked($i, $j) {
+ return (($i + $j) & 0x01) == 0;
+ }
+}
+
+/**
+ * 001: mask bits for which x mod 2 == 0
+ */
+final class DataMask001 extends DataMask {
+ //@Override
+ function isMasked($i, $j) {
+ return ($i & 0x01) == 0;
+ }
+}
+
+/**
+ * 010: mask bits for which y mod 3 == 0
+ */
+final class DataMask010 extends DataMask {
+ //@Override
+ function isMasked($i, $j) {
+ return $j % 3 == 0;
+ }
+}
+
+/**
+ * 011: mask bits for which (x + y) mod 3 == 0
+ */
+final class DataMask011 extends DataMask {
+ //@Override
+ function isMasked($i, $j) {
+ return ($i + $j) % 3 == 0;
+ }
+}
+
+/**
+ * 100: mask bits for which (x/2 + y/3) mod 2 == 0
+ */
+final class DataMask100 extends DataMask {
+ //@Override
+ function isMasked($i, $j) {
+ return intval((intval($i / 2) + intval($j /3)) & 0x01) == 0;
+ }
+}
+
+/**
+ * 101: mask bits for which xy mod 2 + xy mod 3 == 0
+ */
+final class DataMask101 extends DataMask {
+ //@Override
+ function isMasked($i, $j) {
+ $temp = $i * $j;
+ return ($temp & 0x01) + ($temp % 3) == 0;
+ }
+}
+
+/**
+ * 110: mask bits for which (xy mod 2 + xy mod 3) mod 2 == 0
+ */
+final class DataMask110 extends DataMask {
+ //@Override
+ function isMasked($i, $j) {
+ $temp = $i * $j;
+ return ((($temp & 0x01) + ($temp % 3)) & 0x01) == 0;
+ }
+}
+
+/**
+ * 111: mask bits for which ((x+y)mod 2 + xy mod 3) mod 2 == 0
+ */
+final class DataMask111 extends DataMask {
+ //@Override
+ function isMasked($i, $j) {
+ return (((($i + $j) & 0x01) + (($i * $j) % 3)) & 0x01) == 0;
+ }
+}
+
diff --git a/public/qr-code/lib/qrcode/decoder/DecodedBitStreamParser.php b/public/qr-code/lib/qrcode/decoder/DecodedBitStreamParser.php
new file mode 100644
index 0000000..ec4e667
--- /dev/null
+++ b/public/qr-code/lib/qrcode/decoder/DecodedBitStreamParser.php
@@ -0,0 +1,359 @@
+QR Codes can encode text as bits in one of several modes, and can use multiple modes
+ * in one QR Code. This class decodes the bits back into text.
+ *
+ *
See ISO 18004:2006, 6.4.3 - 6.4.7
+ *
+ * @author Sean Owen
+ */
+final class DecodedBitStreamParser {
+
+ /**
+ * See ISO 18004:2006, 6.4.4 Table 5
+ */
+ private static $ALPHANUMERIC_CHARS = array(
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
+ 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+ 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ ' ', '$', '%', '*', '+', '-', '.', '/', ':'
+ );
+ private static $GB2312_SUBSET = 1;
+
+
+ private function DecodedBitStreamParser() {
+
+
+ }
+
+ static function decode($bytes,
+ $version,
+ $ecLevel,
+ $hints) {
+ $bits = new BitSource($bytes);
+ $result = '';//new StringBuilder(50);
+ $byteSegments = array();
+ $symbolSequence = -1;
+ $parityData = -1;
+
+ try {
+ $currentCharacterSetECI = null;
+ $fc1InEffect = false;
+ $mode='';
+ do {
+ // While still another segment to read...
+ if ($bits->available() < 4) {
+ // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
+ $mode = Mode::$TERMINATOR;
+ } else {
+ $mode = Mode::forBits($bits->readBits(4)); // mode is encoded by 4 bits
+ }
+ if ($mode != Mode::$TERMINATOR) {
+ if ($mode == Mode::$FNC1_FIRST_POSITION || $mode == Mode::$FNC1_SECOND_POSITION) {
+ // We do little with FNC1 except alter the parsed result a bit according to the spec
+ $fc1InEffect = true;
+ } else if ($mode == Mode::$STRUCTURED_APPEND) {
+ if ($bits->available() < 16) {
+ throw FormatException::getFormatInstance();
+ }
+ // sequence number and parity is added later to the result metadata
+ // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
+ $symbolSequence = $bits->readBits(8);
+ $parityData = $bits->readBits(8);
+ } else if ($mode == Mode::$ECI) {
+ // Count doesn't apply to ECI
+ $value = self::parseECIValue($bits);
+ $currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue($value);
+ if ($currentCharacterSetECI == null) {
+ throw FormatException::getFormatInstance();
+ }
+ } else {
+ // First handle Hanzi mode which does not start with character count
+ if ($mode == Mode::$HANZI) {
+ //chinese mode contains a sub set indicator right after mode indicator
+ $subset = $bits->readBits(4);
+ $countHanzi = $bits->readBits($mode->getCharacterCountBits($version));
+ if ($subset == self::$GB2312_SUBSET) {
+ self::decodeHanziSegment($bits, $result, $countHanzi);
+ }
+ } else {
+ // "Normal" QR code modes:
+ // How many characters will follow, encoded in this mode?
+ $count = $bits->readBits($mode->getCharacterCountBits($version));
+ if ($mode == Mode::$NUMERIC) {
+ self::decodeNumericSegment($bits, $result, $count);
+ } else if ($mode == Mode::$ALPHANUMERIC) {
+ self::decodeAlphanumericSegment($bits, $result, $count, $fc1InEffect);
+ } else if ($mode == Mode::$BYTE) {
+ self::decodeByteSegment($bits, $result, $count, $currentCharacterSetECI, $byteSegments, $hints);
+ } else if ($mode == Mode::$KANJI) {
+ self::decodeKanjiSegment($bits, $result, $count);
+ } else {
+ throw FormatException::getFormatInstance();
+ }
+ }
+ }
+ }
+ } while ($mode != Mode::$TERMINATOR);
+ } catch (IllegalArgumentException $iae) {
+ // from readBits() calls
+ throw FormatException::getFormatInstance();
+ }
+
+ return new DecoderResult($bytes,
+ $result,
+ empty($byteSegments) ? null : $byteSegments,
+ $ecLevel == null ? null : 'L',//ErrorCorrectionLevel::toString($ecLevel),
+ $symbolSequence,
+ $parityData);
+ }
+
+ /**
+ * See specification GBT 18284-2000
+ */
+ private static function decodeHanziSegment($bits,
+ &$result,
+ $count) {
+ // Don't crash trying to read more bits than we have available.
+ if ($count * 13 > $bits->available()) {
+ throw FormatException::getFormatInstance();
+ }
+
+ // Each character will require 2 bytes. Read the characters as 2-byte pairs
+ // and decode as GB2312 afterwards
+ $buffer = fill_array(0,2 * $count,0);
+ $offset = 0;
+ while ($count > 0) {
+ // Each 13 bits encodes a 2-byte character
+ $twoBytes = $bits->readBits(13);
+ $assembledTwoBytes = (($twoBytes / 0x060) << 8) | ($twoBytes % 0x060);
+ if ($assembledTwoBytes < 0x003BF) {
+ // In the 0xA1A1 to 0xAAFE range
+ $assembledTwoBytes += 0x0A1A1;
+ } else {
+ // In the 0xB0A1 to 0xFAFE range
+ $assembledTwoBytes += 0x0A6A1;
+ }
+ $buffer[$offset] = (($assembledTwoBytes >> 8) & 0xFF);//(byte)
+ $buffer[$offset + 1] = ($assembledTwoBytes & 0xFF);//(byte)
+ $offset += 2;
+ $count--;
+ }
+
+ try {
+ $result .= iconv('GB2312', 'UTF-8', implode($buffer));
+ } catch (UnsupportedEncodingException $ignored) {
+ throw FormatException::getFormatInstance();
+ }
+ }
+
+ private static function decodeKanjiSegment($bits,
+ &$result,
+ $count) {
+ // Don't crash trying to read more bits than we have available.
+ if ($count * 13 > $bits->available()) {
+ throw FormatException::getFormatInstance();
+ }
+
+ // Each character will require 2 bytes. Read the characters as 2-byte pairs
+ // and decode as Shift_JIS afterwards
+ $buffer = array(0,2 * $count,0);
+ $offset = 0;
+ while ($count > 0) {
+ // Each 13 bits encodes a 2-byte character
+ $twoBytes = $bits->readBits(13);
+ $assembledTwoBytes = (($twoBytes / 0x0C0) << 8) | ($twoBytes % 0x0C0);
+ if ($assembledTwoBytes < 0x01F00) {
+ // In the 0x8140 to 0x9FFC range
+ $assembledTwoBytes += 0x08140;
+ } else {
+ // In the 0xE040 to 0xEBBF range
+ $assembledTwoBytes += 0x0C140;
+ }
+ $buffer[$offset] = ($assembledTwoBytes >> 8);//(byte)
+ $buffer[$offset + 1] = $assembledTwoBytes; //(byte)
+ $offset += 2;
+ $count--;
+ }
+ // Shift_JIS may not be supported in some environments:
+ try {
+ $result .= iconv('shift-jis','utf-8',implode($buffer));
+
+
+ } catch (UnsupportedEncodingException $ignored) {
+ throw FormatException::getFormatInstance();
+ }
+ }
+
+ private static function decodeByteSegment($bits,
+ &$result,
+ $count,
+ $currentCharacterSetECI,
+ &$byteSegments,
+ $hints) {
+ // Don't crash trying to read more bits than we have available.
+ if (8 * $count > $bits->available()) {
+ throw FormatException::getFormatInstance();
+ }
+
+ $readBytes = fill_array(0,$count,0);
+ for ($i = 0; $i < $count; $i++) {
+ $readBytes[$i] = $bits->readBits(8);//(byte)
+ }
+ $text = implode(array_map('chr',$readBytes));
+ $encoding = '';
+ if ($currentCharacterSetECI == null) {
+ // The spec isn't clear on this mode; see
+ // section 6.4.5: t does not say which encoding to assuming
+ // upon decoding. I have seen ISO-8859-1 used as well as
+ // Shift_JIS -- without anything like an ECI designator to
+ // give a hint.
+
+ $encoding = mb_detect_encoding($text, $hints);
+ } else {
+ $encoding = $currentCharacterSetECI->name();
+ }
+ try {
+ // $result.= mb_convert_encoding($text ,$encoding);//(new String(readBytes, encoding));
+ $result.= $text;//(new String(readBytes, encoding));
+ } catch (UnsupportedEncodingException $ignored) {
+ throw FormatException::getFormatInstance();
+ }
+ $byteSegments = array_merge($byteSegments, $readBytes);
+ }
+
+ private static function toAlphaNumericChar($value) {
+ if ($value >= count(self::$ALPHANUMERIC_CHARS)) {
+ throw FormatException::getFormatInstance();
+ }
+ return self::$ALPHANUMERIC_CHARS[$value];
+ }
+
+ private static function decodeAlphanumericSegment($bits,
+ &$result,
+ $count,
+ $fc1InEffect) {
+ // Read two characters at a time
+ $start = strlen($result);
+ while ($count > 1) {
+ if ($bits->available() < 11) {
+ throw FormatException::getFormatInstance();
+ }
+ $nextTwoCharsBits = $bits->readBits(11);
+ $result.=(self::toAlphaNumericChar($nextTwoCharsBits / 45));
+ $result.=(self::toAlphaNumericChar($nextTwoCharsBits % 45));
+ $count -= 2;
+ }
+ if ($count == 1) {
+ // special case: one character left
+ if ($bits->available() < 6) {
+ throw FormatException::getFormatInstance();
+ }
+ $result.=self::toAlphaNumericChar($bits->readBits(6));
+ }
+ // See section 6.4.8.1, 6.4.8.2
+ if ($fc1InEffect) {
+ // We need to massage the result a bit if in an FNC1 mode:
+ for ($i = $start; $i < strlen($result); $i++) {
+ if ($result{$i} == '%') {
+ if ($i < strlen($result) - 1 && $result{$i + 1} == '%') {
+ // %% is rendered as %
+ $result = substr_replace($result,'',$i + 1,1);//deleteCharAt(i + 1);
+ } else {
+ // In alpha mode, % should be converted to FNC1 separator 0x1D
+ $result.setCharAt($i, chr(0x1D));
+ }
+ }
+ }
+ }
+ }
+
+ private static function decodeNumericSegment($bits,
+ &$result,
+ $count) {
+ // Read three digits at a time
+ while ($count >= 3) {
+ // Each 10 bits encodes three digits
+ if ($bits->available() < 10) {
+ throw FormatException::getFormatInstance();
+ }
+ $threeDigitsBits = $bits->readBits(10);
+ if ($threeDigitsBits >= 1000) {
+ throw FormatException::getFormatInstance();
+ }
+ $result.=(self::toAlphaNumericChar($threeDigitsBits / 100));
+ $result.=(self::toAlphaNumericChar(($threeDigitsBits / 10) % 10));
+ $result.=(self::toAlphaNumericChar($threeDigitsBits % 10));
+ $count -= 3;
+ }
+ if ($count == 2) {
+ // Two digits left over to read, encoded in 7 bits
+ if ($bits->available() < 7) {
+ throw FormatException::getFormatInstance();
+ }
+ $twoDigitsBits = $bits->readBits(7);
+ if ($twoDigitsBits >= 100) {
+ throw FormatException::getFormatInstance();
+ }
+ $result.=(self::toAlphaNumericChar($twoDigitsBits / 10));
+ $result.=(self::toAlphaNumericChar($twoDigitsBits % 10));
+ } else if ($count == 1) {
+ // One digit left over to read
+ if ($bits->available() < 4) {
+ throw FormatException::getFormatInstance();
+ }
+ $digitBits = $bits->readBits(4);
+ if ($digitBits >= 10) {
+ throw FormatException::getFormatInstance();
+ }
+ $result.=(self::toAlphaNumericChar($digitBits));
+ }
+ }
+
+ private static function parseECIValue($bits) {
+ $firstByte = $bits->readBits(8);
+ if (($firstByte & 0x80) == 0) {
+ // just one byte
+ return $firstByte & 0x7F;
+ }
+ if (($firstByte & 0xC0) == 0x80) {
+ // two bytes
+ $secondByte = $bits->readBits(8);
+ return (($firstByte & 0x3F) << 8) | $secondByte;
+ }
+ if (($firstByte & 0xE0) == 0xC0) {
+ // three bytes
+ $secondThirdBytes = $bits->readBits(16);
+ return (($firstByte & 0x1F) << 16) | $secondThirdBytes;
+ }
+ throw FormatException::getFormatInstance();
+ }
+
+}
diff --git a/public/qr-code/lib/qrcode/decoder/Decoder.php b/public/qr-code/lib/qrcode/decoder/Decoder.php
new file mode 100644
index 0000000..1859fc7
--- /dev/null
+++ b/public/qr-code/lib/qrcode/decoder/Decoder.php
@@ -0,0 +1,214 @@
+The main class which implements QR Code decoding -- as opposed to locating and extracting
+ * the QR Code from an image.
+ *
+ * @author Sean Owen
+ */
+final class Decoder {
+
+ private $rsDecoder;
+
+ public function __construct() {
+ $this->rsDecoder = new ReedSolomonDecoder(GenericGF::$QR_CODE_FIELD_256);
+ }
+
+
+ function decode($variable, $hints=null){
+ if(is_array($variable)){
+ return $this->decodeImage($variable,$hints);
+ }elseif(is_object($variable)&&$variable instanceof BitMatrix){
+ return $this->decodeBits($variable,$hints);
+ }elseif(is_object($variable)&&$variable instanceof BitMatrixParser){
+ return $this->decodeParser($variable,$hints);
+ }else{
+ die('decode error Decoder.php');
+ }
+
+
+ }
+
+ /**
+ *
Convenience method that can decode a QR Code represented as a 2D array of booleans.
+ * "true" is taken to mean a black module.
+ *
+ * @param image booleans representing white/black QR Code modules
+ * @param hints decoding hints that should be used to influence decoding
+ * @return text and bytes encoded within the QR Code
+ * @throws FormatException if the QR Code cannot be decoded
+ * @throws ChecksumException if error correction fails
+ */
+ public function decodeImage($image, $hints=null)
+ {
+ $dimension = count($image);
+ $bits = new BitMatrix($dimension);
+ for ($i = 0; $i < $dimension; $i++) {
+ for ($j = 0; $j < $dimension; $j++) {
+ if ($image[$i][$j]) {
+ $bits->set($j, $i);
+ }
+ }
+ }
+ return $this->decode($bits, $hints);
+ }
+
+
+
+ /**
+ *
Decodes a QR Code represented as a {@link BitMatrix}. A 1 or "true" is taken to mean a black module.
+ *
+ * @param bits booleans representing white/black QR Code modules
+ * @param hints decoding hints that should be used to influence decoding
+ * @return text and bytes encoded within the QR Code
+ * @throws FormatException if the QR Code cannot be decoded
+ * @throws ChecksumException if error correction fails
+ */
+ public function decodeBits($bits, $hints=null)
+ {
+
+// Construct a parser and read version, error-correction level
+ $parser = new BitMatrixParser($bits);
+ $fe = null;
+ $ce = null;
+ try {
+ return $this->decode($parser, $hints);
+ } catch (FormatException $e) {
+ $fe = $e;
+ } catch (ChecksumException $e) {
+ $ce = $e;
+ }
+
+ try {
+
+// Revert the bit matrix
+ $parser->remask();
+
+// Will be attempting a mirrored reading of the version and format info.
+ $parser->setMirror(true);
+
+// Preemptively read the version.
+ $parser->readVersion();
+
+// Preemptively read the format information.
+ $parser->readFormatInformation();
+
+ /*
+ * Since we're here, this means we have successfully detected some kind
+ * of version and format information when mirrored. This is a good sign,
+ * that the QR code may be mirrored, and we should try once more with a
+ * mirrored content.
+ */
+// Prepare for a mirrored reading.
+ $parser->mirror();
+
+ $result = $this->decode($parser, $hints);
+
+// Success! Notify the caller that the code was mirrored.
+ $result->setOther(new QRCodeDecoderMetaData(true));
+
+ return $result;
+
+ } catch (FormatException $e ) {// catch (FormatException | ChecksumException e) {
+// Throw the exception from the original reading
+ if ($fe != null) {
+ throw $fe;
+ }
+ if ($ce != null) {
+ throw $ce;
+ }
+ throw $e;
+
+ }
+ }
+
+ private function decodeParser($parser,$hints=null)
+ {
+ $version = $parser->readVersion();
+ $ecLevel = $parser->readFormatInformation()->getErrorCorrectionLevel();
+
+// Read codewords
+ $codewords = $parser->readCodewords();
+// Separate into data blocks
+ $dataBlocks = DataBlock::getDataBlocks($codewords, $version, $ecLevel);
+
+// Count total number of data bytes
+ $totalBytes = 0;
+ foreach ($dataBlocks as $dataBlock) {
+ $totalBytes += $dataBlock->getNumDataCodewords();
+ }
+ $resultBytes = fill_array(0,$totalBytes,0);
+ $resultOffset = 0;
+
+// Error-correct and copy data blocks together into a stream of bytes
+ foreach ($dataBlocks as $dataBlock) {
+ $codewordBytes = $dataBlock->getCodewords();
+ $numDataCodewords = $dataBlock->getNumDataCodewords();
+ $this->correctErrors($codewordBytes, $numDataCodewords);
+ for ($i = 0; $i < $numDataCodewords; $i++) {
+ $resultBytes[$resultOffset++] = $codewordBytes[$i];
+ }
+ }
+
+// Decode the contents of that stream of bytes
+ return DecodedBitStreamParser::decode($resultBytes, $version, $ecLevel, $hints);
+ }
+
+ /**
+ *
Given data and error-correction codewords received, possibly corrupted by errors, attempts to
+ * correct the errors in-place using Reed-Solomon error correction.
+ *
+ * @param codewordBytes data and error correction codewords
+ * @param numDataCodewords number of codewords that are data bytes
+ * @throws ChecksumException if error correction fails
+ */
+ private function correctErrors(&$codewordBytes, $numDataCodewords){
+ $numCodewords = count($codewordBytes);
+// First read into an array of ints
+ $codewordsInts =fill_array(0,$numCodewords,0);
+ for ($i = 0; $i < $numCodewords; $i++) {
+ $codewordsInts[$i] = $codewordBytes[$i] & 0xFF;
+ }
+ $numECCodewords = count($codewordBytes)- $numDataCodewords;
+ try {
+ $this->rsDecoder->decode($codewordsInts, $numECCodewords);
+ } catch (ReedSolomonException $ignored) {
+ throw ChecksumException::getChecksumInstance();
+ }
+// Copy back into array of bytes -- only need to worry about the bytes that were data
+// We don't care about errors in the error-correction codewords
+ for ($i = 0; $i < $numDataCodewords; $i++) {
+ $codewordBytes[$i] = $codewordsInts[$i];
+ }
+
+ }
+
+}
diff --git a/public/qr-code/lib/qrcode/decoder/ErrorCorrectionLevel.php b/public/qr-code/lib/qrcode/decoder/ErrorCorrectionLevel.php
new file mode 100644
index 0000000..55ca97e
--- /dev/null
+++ b/public/qr-code/lib/qrcode/decoder/ErrorCorrectionLevel.php
@@ -0,0 +1,86 @@
+See ISO 18004:2006, 6.5.1. This enum encapsulates the four error correction levels
+ * defined by the QR code standard.
+ *
+ * @author Sean Owen
+ */
+class ErrorCorrectionLevel {
+
+
+ private static $FOR_BITS;
+
+
+ private $bits;
+ private $ordinal;
+
+ function __construct($bits,$ordinal=0) {
+ $this->bits = $bits;
+ $this->ordinal = $ordinal;
+ }
+
+ public static function Init(){
+ self::$FOR_BITS = array(
+
+
+ new ErrorCorrectionLevel(0x00,1), //M
+ new ErrorCorrectionLevel(0x01,0), //L
+ new ErrorCorrectionLevel(0x02,3), //H
+ new ErrorCorrectionLevel(0x03,2), //Q
+
+ );
+ }
+ /** L = ~7% correction */
+ // self::$L = new ErrorCorrectionLevel(0x01);
+ /** M = ~15% correction */
+ //self::$M = new ErrorCorrectionLevel(0x00);
+ /** Q = ~25% correction */
+ //self::$Q = new ErrorCorrectionLevel(0x03);
+ /** H = ~30% correction */
+ //self::$H = new ErrorCorrectionLevel(0x02);
+
+
+ public function getBits() {
+ return $this->bits;
+ }
+ public function toString() {
+ return $this->bits;
+ }
+ public function getOrdinal() {
+ return $this->ordinal;
+ }
+
+ /**
+ * @param bits int containing the two bits encoding a QR Code's error correction level
+ * @return ErrorCorrectionLevel representing the encoded error correction level
+ */
+ public static function forBits($bits) {
+ if ($bits < 0 || $bits >= count(self::$FOR_BITS)) {
+ throw new InvalidArgumentException();
+ }
+ $level = self::$FOR_BITS[$bits];
+ // $lev = self::$$bit;
+ return $level;
+ }
+
+
+}
+ErrorCorrectionLevel::Init();
diff --git a/public/qr-code/lib/qrcode/decoder/FormatInformation.php b/public/qr-code/lib/qrcode/decoder/FormatInformation.php
new file mode 100644
index 0000000..ac802fd
--- /dev/null
+++ b/public/qr-code/lib/qrcode/decoder/FormatInformation.php
@@ -0,0 +1,179 @@
+Encapsulates a QR Code's format information, including the data mask used and
+ * error correction level.
+ *
+ * @author Sean Owen
+ * @see DataMask
+ * @see ErrorCorrectionLevel
+ */
+final class FormatInformation {
+
+ public static $FORMAT_INFO_MASK_QR;
+
+ /**
+ * See ISO 18004:2006, Annex C, Table C.1
+ */
+ public static $FORMAT_INFO_DECODE_LOOKUP;
+ /**
+ * Offset i holds the number of 1 bits in the binary representation of i
+ */
+ private static $BITS_SET_IN_HALF_BYTE;
+
+ private $errorCorrectionLevel;
+ private $dataMask;
+
+ public static function Init(){
+
+ self::$FORMAT_INFO_MASK_QR= 0x5412;
+ self::$BITS_SET_IN_HALF_BYTE = array(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);
+ self::$FORMAT_INFO_DECODE_LOOKUP = array(
+ array(0x5412, 0x00),
+ array (0x5125, 0x01),
+ array(0x5E7C, 0x02),
+ array(0x5B4B, 0x03),
+ array(0x45F9, 0x04),
+ array(0x40CE, 0x05),
+ array(0x4F97, 0x06),
+ array(0x4AA0, 0x07),
+ array(0x77C4, 0x08),
+ array(0x72F3, 0x09),
+ array(0x7DAA, 0x0A),
+ array(0x789D, 0x0B),
+ array(0x662F, 0x0C),
+ array(0x6318, 0x0D),
+ array(0x6C41, 0x0E),
+ array(0x6976, 0x0F),
+ array(0x1689, 0x10),
+ array(0x13BE, 0x11),
+ array(0x1CE7, 0x12),
+ array(0x19D0, 0x13),
+ array(0x0762, 0x14),
+ array(0x0255, 0x15),
+ array(0x0D0C, 0x16),
+ array(0x083B, 0x17),
+ array(0x355F, 0x18),
+ array(0x3068, 0x19),
+ array(0x3F31, 0x1A),
+ array(0x3A06, 0x1B),
+ array(0x24B4, 0x1C),
+ array(0x2183, 0x1D),
+ array(0x2EDA, 0x1E),
+ array(0x2BED, 0x1F),
+ );
+
+ }
+ private function __construct($formatInfo) {
+ // Bits 3,4
+ $this->errorCorrectionLevel = ErrorCorrectionLevel::forBits(($formatInfo >> 3) & 0x03);
+ // Bottom 3 bits
+ $this->dataMask = ($formatInfo & 0x07);//(byte)
+ }
+
+ static function numBitsDiffering($a, $b) {
+ $a ^= $b; // a now has a 1 bit exactly where its bit differs with b's
+ // Count bits set quickly with a series of lookups:
+ return self::$BITS_SET_IN_HALF_BYTE[$a & 0x0F] +
+ self::$BITS_SET_IN_HALF_BYTE[intval(uRShift($a, 4) & 0x0F)] +
+ self::$BITS_SET_IN_HALF_BYTE[(uRShift($a ,8) & 0x0F)] +
+ self::$BITS_SET_IN_HALF_BYTE[(uRShift($a , 12) & 0x0F)] +
+ self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 16) & 0x0F)] +
+ self::$BITS_SET_IN_HALF_BYTE[(uRShift($a , 20) & 0x0F)] +
+ self::$BITS_SET_IN_HALF_BYTE[(uRShift($a, 24) & 0x0F)] +
+ self::$BITS_SET_IN_HALF_BYTE[(uRShift($a ,28) & 0x0F)];
+ }
+
+ /**
+ * @param maskedFormatInfo1; format info indicator, with mask still applied
+ * @param maskedFormatInfo2; second copy of same info; both are checked at the same time
+ * to establish best match
+ * @return information about the format it specifies, or {@code null}
+ * if doesn't seem to match any known pattern
+ */
+ 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);
+ }
+
+ private static function doDecodeFormatInformation($maskedFormatInfo1, $maskedFormatInfo2) {
+ // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing
+ $bestDifference = PHP_INT_MAX;
+ $bestFormatInfo = 0;
+ foreach (self::$FORMAT_INFO_DECODE_LOOKUP as $decodeInfo ) {
+ $targetInfo = $decodeInfo[0];
+ if ($targetInfo == $maskedFormatInfo1 || $targetInfo == $maskedFormatInfo2) {
+ // Found an exact match
+ return new FormatInformation($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 FormatInformation($bestFormatInfo);
+ }
+ return null;
+ }
+
+ function getErrorCorrectionLevel() {
+ return $this->errorCorrectionLevel;
+ }
+
+ function getDataMask() {
+ return $this->dataMask;
+ }
+
+ //@Override
+ public function hashCode() {
+ return ($this->errorCorrectionLevel->ordinal() << 3) | intval($this->dataMask);
+ }
+
+ //@Override
+ public function equals($o) {
+ if (!($o instanceof FormatInformation)) {
+ return false;
+ }
+ $other =$o;
+ return $this->errorCorrectionLevel == $other->errorCorrectionLevel &&
+ $this->dataMask == $other->dataMask;
+ }
+
+}
+FormatInformation::Init();
diff --git a/public/qr-code/lib/qrcode/decoder/Mode.php b/public/qr-code/lib/qrcode/decoder/Mode.php
new file mode 100644
index 0000000..30421ad
--- /dev/null
+++ b/public/qr-code/lib/qrcode/decoder/Mode.php
@@ -0,0 +1,118 @@
+See ISO 18004:2006, 6.4.1, Tables 2 and 3. This enum encapsulates the various modes in which
+ * data can be encoded to bits in the QR code standard.
+ *
+ * @author Sean Owen
+ */
+class Mode {
+ static $TERMINATOR;
+ static $NUMERIC;
+ static $ALPHANUMERIC;
+ static $STRUCTURED_APPEND;
+ static $BYTE;
+ static $ECI;
+ static $KANJI;
+ static $FNC1_FIRST_POSITION;
+ static $FNC1_SECOND_POSITION;
+ static $HANZI;
+
+
+ private $characterCountBitsForVersions;
+ private $bits;
+
+ function __construct($characterCountBitsForVersions, $bits) {
+ $this->characterCountBitsForVersions = $characterCountBitsForVersions;
+ $this->bits = $bits;
+ }
+ static function Init()
+ {
+
+
+ self::$TERMINATOR = new Mode(array(0, 0, 0), 0x00); // Not really a mode...
+ self::$NUMERIC = new Mode(array(10, 12, 14), 0x01);
+ self::$ALPHANUMERIC = new Mode(array(9, 11, 13), 0x02);
+ self::$STRUCTURED_APPEND = new Mode(array(0, 0, 0), 0x03); // Not supported
+ self::$BYTE = new Mode(array(8, 16, 16), 0x04);
+ self::$ECI = new Mode(array(0, 0, 0), 0x07); // character counts don't apply
+ self::$KANJI = new Mode(array(8, 10, 12), 0x08);
+ self::$FNC1_FIRST_POSITION = new Mode(array(0, 0, 0), 0x05);
+ self::$FNC1_SECOND_POSITION =new Mode(array(0, 0, 0), 0x09);
+ /** See GBT 18284-2000; "Hanzi" is a transliteration of this mode name. */
+ self::$HANZI = new Mode(array(8, 10, 12), 0x0D);
+ }
+ /**
+ * @param bits four bits encoding a QR Code data mode
+ * @return Mode encoded by these bits
+ * @throws IllegalArgumentException if bits do not correspond to a known mode
+ */
+ public static function forBits($bits) {
+ switch ($bits) {
+ case 0x0:
+ return self::$TERMINATOR;
+ case 0x1:
+ return self::$NUMERIC;
+ case 0x2:
+ return self::$ALPHANUMERIC;
+ case 0x3:
+ return self::$STRUCTURED_APPEND;
+ case 0x4:
+ return self::$BYTE;
+ case 0x5:
+ return self::$FNC1_FIRST_POSITION;
+ case 0x7:
+ return self::$ECI;
+ case 0x8:
+ return self::$KANJI;
+ case 0x9:
+ return self::$FNC1_SECOND_POSITION;
+ case 0xD:
+ // 0xD is defined in GBT 18284-2000, may not be supported in foreign country
+ return self::$HANZI;
+ default:
+ throw new \InvalidArgumentException();
+ }
+ }
+
+ /**
+ * @param version version in question
+ * @return number of bits used, in this QR Code symbol {@link Version}, to encode the
+ * count of characters that will follow encoded in this Mode
+ */
+ public function getCharacterCountBits($version) {
+ $number = $version->getVersionNumber();
+ $offset = 0;
+ if ($number <= 9) {
+ $offset = 0;
+ } else if ($number <= 26) {
+ $offset = 1;
+ } else {
+ $offset = 2;
+ }
+ return $this->characterCountBitsForVersions[$offset];
+ }
+
+ public function getBits() {
+ return $this->bits;
+ }
+
+}
+Mode::Init();
\ No newline at end of file
diff --git a/public/qr-code/lib/qrcode/decoder/Version.php b/public/qr-code/lib/qrcode/decoder/Version.php
new file mode 100644
index 0000000..045ba64
--- /dev/null
+++ b/public/qr-code/lib/qrcode/decoder/Version.php
@@ -0,0 +1,619 @@
+versionNumber = $versionNumber;
+ $this->alignmentPatternCenters = $alignmentPatternCenters;
+ $this->ecBlocks = $ecBlocks;
+ $total = 0;
+ if(is_array($ecBlocks)) {
+ $ecCodewords = $ecBlocks[0]->getECCodewordsPerBlock();
+ $ecbArray = $ecBlocks[0]->getECBlocks();
+ }else{
+ $ecCodewords = $ecBlocks->getECCodewordsPerBlock();
+ $ecbArray = $ecBlocks->getECBlocks();
+ }
+ foreach ($ecbArray as $ecBlock) {
+ $total += $ecBlock->getCount() * ($ecBlock->getDataCodewords() + $ecCodewords);
+ }
+ $this->totalCodewords = $total;
+ }
+ public function getVersionNumber()
+ {
+ return $this->versionNumber;
+ }
+
+ public function getAlignmentPatternCenters()
+ {
+ return $this->alignmentPatternCenters;
+ }
+
+ public function getTotalCodewords()
+ {
+ return $this->totalCodewords;
+ }
+
+ public function getDimensionForVersion()
+ {
+ return 17 + 4 * $this->versionNumber;
+ }
+
+ public function getECBlocksForLevel($ecLevel)
+ {
+ return $this->ecBlocks[$ecLevel->getOrdinal()];
+ }
+
+ /**
+ *
Deduces version information purely from QR Code dimensions.
+ *
+ * @param dimension dimension in modules
+ * @return Version for a QR Code of that dimension
+ * @throws FormatException if dimension is not 1 mod 4
+ */
+ public static function getProvisionalVersionForDimension($dimension)
+ {
+ if ($dimension % 4 != 1) {
+ throw FormatException::getFormatInstance();
+ }
+ try {
+ return self::getVersionForNumber(($dimension - 17) / 4);
+ } catch (InvalidArgumentException $ignored) {
+ throw FormatException::getFormatInstance();
+ }
+ }
+
+ public static function getVersionForNumber($versionNumber)
+ {
+ if ($versionNumber < 1 || $versionNumber > 40) {
+ throw new InvalidArgumentException();
+ }
+ if(!self::$VERSIONS){
+
+ self::$VERSIONS = self::buildVersions();
+
+ }
+ return self::$VERSIONS[$versionNumber - 1];
+ }
+
+ static function decodeVersionInformation($versionBits)
+ {
+ $bestDifference = PHP_INT_MAX;
+ $bestVersion = 0;
+ for ($i = 0; $i < count(self::$VERSION_DECODE_INFO); $i++) {
+ $targetVersion = self::$VERSION_DECODE_INFO[$i];
+// Do the version info bits match exactly? done.
+ if ($targetVersion == $versionBits) {
+ return self::getVersionForNumber($i + 7);
+ }
+// Otherwise see if this is the closest to a real version info bit string
+// we have seen so far
+ $bitsDifference = FormatInformation::numBitsDiffering($versionBits, $targetVersion);
+ if ($bitsDifference < $bestDifference) {
+ $bestVersion = $i + 7;
+ $bestDifference = $bitsDifference;
+ }
+ }
+// We can tolerate up to 3 bits of error since no two version info codewords will
+// differ in less than 8 bits.
+ if ($bestDifference <= 3) {
+ return self::getVersionForNumber($bestVersion);
+ }
+// If we didn't find a close enough match, fail
+ return null;
+ }
+
+ /**
+ * See ISO 18004:2006 Annex E
+ */
+ function buildFunctionPattern()
+ {
+ $dimension = self::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;
+ }
+ /**
+ * See ISO 18004:2006 6.5.1 Table 9
+ */
+ private static function buildVersions()
+ {
+
+
+ return array(
+ new Version(1, array(),
+ array(new ECBlocks(7, array(new ECB(1, 19))),
+ new ECBlocks(10, array(new ECB(1, 16))),
+ new ECBlocks(13, array(new ECB(1, 13))),
+ new ECBlocks(17, array(new ECB(1, 9))))),
+ new Version(2, array(6, 18),
+ array(new ECBlocks(10, array(new ECB(1, 34))),
+ new ECBlocks(16, array(new ECB(1, 28))),
+ new ECBlocks(22, array(new ECB(1, 22))),
+ new ECBlocks(28, array(new ECB(1, 16))))),
+ new Version(3, array(6, 22),
+ array( new ECBlocks(15, array(new ECB(1, 55))),
+ new ECBlocks(26, array(new ECB(1, 44))),
+ new ECBlocks(18, array(new ECB(2, 17))),
+ new ECBlocks(22, array(new ECB(2, 13))))),
+ new Version(4, array(6, 26),
+ array(new ECBlocks(20, array(new ECB(1, 80))),
+ new ECBlocks(18, array(new ECB(2, 32))),
+ new ECBlocks(26, array(new ECB(2, 24))),
+ new ECBlocks(16, array(new ECB(4, 9))))),
+ new Version(5, array(6, 30),
+ array(new ECBlocks(26, array(new ECB(1, 108))),
+ new ECBlocks(24, array(new ECB(2, 43))),
+ new ECBlocks(18, array(new ECB(2, 15),
+ new ECB(2, 16))),
+ new ECBlocks(22, array(new ECB(2, 11),
+ new ECB(2, 12))))),
+ new Version(6, array(6, 34),
+ array(new ECBlocks(18, array(new ECB(2, 68))),
+ new ECBlocks(16, array(new ECB(4, 27))),
+ new ECBlocks(24, array(new ECB(4, 19))),
+ new ECBlocks(28, array(new ECB(4, 15))))),
+ new Version(7, array(6, 22, 38),
+ array(new ECBlocks(20, array(new ECB(2, 78))),
+ new ECBlocks(18, array(new ECB(4, 31))),
+ new ECBlocks(18, array(new ECB(2, 14),
+ new ECB(4, 15))),
+ new ECBlocks(26, array(new ECB(4, 13),
+ new ECB(1, 14))))),
+ new Version(8, array(6, 24, 42),
+ array(new ECBlocks(24, array(new ECB(2, 97))),
+ new ECBlocks(22, array(new ECB(2, 38),
+ new ECB(2, 39))),
+ new ECBlocks(22, array(new ECB(4, 18),
+ new ECB(2, 19))),
+ new ECBlocks(26, array(new ECB(4, 14),
+ new ECB(2, 15))))),
+ new Version(9, array(6, 26, 46),
+ array(new ECBlocks(30, array(new ECB(2, 116))),
+ new ECBlocks(22, array(new ECB(3, 36),
+ new ECB(2, 37))),
+ new ECBlocks(20, array(new ECB(4, 16),
+ new ECB(4, 17))),
+ new ECBlocks(24, array(new ECB(4, 12),
+ new ECB(4, 13))))),
+ new Version(10, array(6, 28, 50),
+ array(new ECBlocks(18, array(new ECB(2, 68),
+ new ECB(2, 69))),
+ new ECBlocks(26, array(new ECB(4, 43),
+ new ECB(1, 44))),
+ new ECBlocks(24, array(new ECB(6, 19),
+ new ECB(2, 20))),
+ new ECBlocks(28, array(new ECB(6, 15),
+ new ECB(2, 16))))),
+ new Version(11, array(6, 30, 54),
+ array(new ECBlocks(20, array(new ECB(4, 81))),
+ new ECBlocks(30, array(new ECB(1, 50),
+ new ECB(4, 51))),
+ new ECBlocks(28, array(new ECB(4, 22),
+ new ECB(4, 23))),
+ new ECBlocks(24, array(new ECB(3, 12),
+ new ECB(8, 13))))),
+ new Version(12, array(6, 32, 58),
+ array(new ECBlocks(24, array(new ECB(2, 92),
+ new ECB(2, 93))),
+ new ECBlocks(22, array(new ECB(6, 36),
+ new ECB(2, 37))),
+ new ECBlocks(26, array(new ECB(4, 20),
+ new ECB(6, 21))),
+ new ECBlocks(28, array(new ECB(7, 14),
+ new ECB(4, 15))))),
+ new Version(13, array(6, 34, 62),
+ array(new ECBlocks(26, array(new ECB(4, 107))),
+ new ECBlocks(22, array(new ECB(8, 37),
+ new ECB(1, 38))),
+ new ECBlocks(24, array(new ECB(8, 20),
+ new ECB(4, 21))),
+ new ECBlocks(22, array(new ECB(12, 11),
+ new ECB(4, 12))))),
+ new Version(14, array(6, 26, 46, 66),
+ array(new ECBlocks(30, array(new ECB(3, 115),
+ new ECB(1, 116))),
+ new ECBlocks(24, array(new ECB(4, 40),
+ new ECB(5, 41))),
+ new ECBlocks(20, array(new ECB(11, 16),
+ new ECB(5, 17))),
+ new ECBlocks(24, array(new ECB(11, 12),
+ new ECB(5, 13))))),
+ new Version(15, array(6, 26, 48, 70),
+ array(new ECBlocks(22, array(new ECB(5, 87),
+ new ECB(1, 88))),
+ new ECBlocks(24, array(new ECB(5, 41),
+ new ECB(5, 42))),
+ new ECBlocks(30, array(new ECB(5, 24),
+ new ECB(7, 25))),
+ new ECBlocks(24, array(new ECB(11, 12),
+ new ECB(7, 13))))),
+ new Version(16, array(6, 26, 50, 74),
+ array(new ECBlocks(24, array(new ECB(5, 98),
+ new ECB(1, 99))),
+ new ECBlocks(28, array(new ECB(7, 45),
+ new ECB(3, 46))),
+ new ECBlocks(24, array(new ECB(15, 19),
+ new ECB(2, 20))),
+ new ECBlocks(30, array(new ECB(3, 15),
+ new ECB(13, 16))))),
+ new Version(17, array(6, 30, 54, 78),
+ array(new ECBlocks(28, array(new ECB(1, 107),
+ new ECB(5, 108))),
+ new ECBlocks(28, array(new ECB(10, 46),
+ new ECB(1, 47))),
+ new ECBlocks(28, array(new ECB(1, 22),
+ new ECB(15, 23))),
+ new ECBlocks(28, array(new ECB(2, 14),
+ new ECB(17, 15))))),
+ new Version(18, array(6, 30, 56, 82),
+ array(new ECBlocks(30, array(new ECB(5, 120),
+ new ECB(1, 121))),
+ new ECBlocks(26, array(new ECB(9, 43),
+ new ECB(4, 44))),
+ new ECBlocks(28, array(new ECB(17, 22),
+ new ECB(1, 23))),
+ new ECBlocks(28, array(new ECB(2, 14),
+ new ECB(19, 15))))),
+ new Version(19, array(6, 30, 58, 86),
+ array(new ECBlocks(28, array(new ECB(3, 113),
+ new ECB(4, 114))),
+ new ECBlocks(26, array(new ECB(3, 44),
+ new ECB(11, 45))),
+ new ECBlocks(26, array(new ECB(17, 21),
+ new ECB(4, 22))),
+ new ECBlocks(26, array(new ECB(9, 13),
+ new ECB(16, 14))))),
+ new Version(20, array(6, 34, 62, 90),
+ array(new ECBlocks(28, array(new ECB(3, 107),
+ new ECB(5, 108))),
+ new ECBlocks(26, array(new ECB(3, 41),
+ new ECB(13, 42))),
+ new ECBlocks(30, array(new ECB(15, 24),
+ new ECB(5, 25))),
+ new ECBlocks(28, array(new ECB(15, 15),
+ new ECB(10, 16))))),
+ new Version(21, array(6, 28, 50, 72, 94),
+ array( new ECBlocks(28, array(new ECB(4, 116),
+ new ECB(4, 117))),
+ new ECBlocks(26, array(new ECB(17, 42))),
+ new ECBlocks(28, array(new ECB(17, 22),
+ new ECB(6, 23))),
+ new ECBlocks(30, array(new ECB(19, 16),
+ new ECB(6, 17))))),
+ new Version(22, array(6, 26, 50, 74, 98),
+ array(new ECBlocks(28, array(new ECB(2, 111),
+ new ECB(7, 112))),
+ new ECBlocks(28, array(new ECB(17, 46))),
+ new ECBlocks(30, array(new ECB(7, 24),
+ new ECB(16, 25))),
+ new ECBlocks(24, array(new ECB(34, 13))))),
+ new Version(23, array(6, 30, 54, 78, 102),
+ new ECBlocks(30, array(new ECB(4, 121),
+ new ECB(5, 122))),
+ new ECBlocks(28, array(new ECB(4, 47),
+ new ECB(14, 48))),
+ new ECBlocks(30, array(new ECB(11, 24),
+ new ECB(14, 25))),
+ new ECBlocks(30, array(new ECB(16, 15),
+ new ECB(14, 16)))),
+ new Version(24, array(6, 28, 54, 80, 106),
+ array(new ECBlocks(30, array(new ECB(6, 117),
+ new ECB(4, 118))),
+ new ECBlocks(28, array(new ECB(6, 45),
+ new ECB(14, 46))),
+ new ECBlocks(30, array(new ECB(11, 24),
+ new ECB(16, 25))),
+ new ECBlocks(30, array(new ECB(30, 16),
+ new ECB(2, 17))))),
+ new Version(25, array(6, 32, 58, 84, 110),
+ array(new ECBlocks(26, array(new ECB(8, 106),
+ new ECB(4, 107))),
+ new ECBlocks(28, array(new ECB(8, 47),
+ new ECB(13, 48))),
+ new ECBlocks(30, array(new ECB(7, 24),
+ new ECB(22, 25))),
+ new ECBlocks(30, array(new ECB(22, 15),
+ new ECB(13, 16))))),
+ new Version(26, array(6, 30, 58, 86, 114),
+ array(new ECBlocks(28, array(new ECB(10, 114),
+ new ECB(2, 115))),
+ new ECBlocks(28, array(new ECB(19, 46),
+ new ECB(4, 47))),
+ new ECBlocks(28, array(new ECB(28, 22),
+ new ECB(6, 23))),
+ new ECBlocks(30, array(new ECB(33, 16),
+ new ECB(4, 17))))),
+ new Version(27, array(6, 34, 62, 90, 118),
+ array(new ECBlocks(30, array(new ECB(8, 122),
+ new ECB(4, 123))),
+ new ECBlocks(28, array(new ECB(22, 45),
+ new ECB(3, 46))),
+ new ECBlocks(30, array(new ECB(8, 23),
+ new ECB(26, 24))),
+ new ECBlocks(30, array(new ECB(12, 15),
+ new ECB(28, 16))))),
+ new Version(28, array(6, 26, 50, 74, 98, 122),
+ array(new ECBlocks(30, array(new ECB(3, 117),
+ new ECB(10, 118))),
+ new ECBlocks(28, array(new ECB(3, 45),
+ new ECB(23, 46))),
+ new ECBlocks(30, array(new ECB(4, 24),
+ new ECB(31, 25))),
+ new ECBlocks(30, array(new ECB(11, 15),
+ new ECB(31, 16))))),
+ new Version(29, array(6, 30, 54, 78, 102, 126),
+ array(new ECBlocks(30, array(new ECB(7, 116),
+ new ECB(7, 117))),
+ new ECBlocks(28, array(new ECB(21, 45),
+ new ECB(7, 46))),
+ new ECBlocks(30, array(new ECB(1, 23),
+ new ECB(37, 24))),
+ new ECBlocks(30, array(new ECB(19, 15),
+ new ECB(26, 16))))),
+ new Version(30, array(6, 26, 52, 78, 104, 130),
+ array(new ECBlocks(30, array(new ECB(5, 115),
+ new ECB(10, 116))),
+ new ECBlocks(28, array(new ECB(19, 47),
+ new ECB(10, 48))),
+ new ECBlocks(30, array(new ECB(15, 24),
+ new ECB(25, 25))),
+ new ECBlocks(30, array(new ECB(23, 15),
+ new ECB(25, 16))))),
+ new Version(31, array(6, 30, 56, 82, 108, 134),
+ array(new ECBlocks(30, array(new ECB(13, 115),
+ new ECB(3, 116))),
+ new ECBlocks(28, array(new ECB(2, 46),
+ new ECB(29, 47))),
+ new ECBlocks(30, array(new ECB(42, 24),
+ new ECB(1, 25))),
+ new ECBlocks(30, array(new ECB(23, 15),
+ new ECB(28, 16))))),
+ new Version(32, array(6, 34, 60, 86, 112, 138),
+ array(new ECBlocks(30, array(new ECB(17, 115))),
+ new ECBlocks(28, array(new ECB(10, 46),
+ new ECB(23, 47))),
+ new ECBlocks(30, array(new ECB(10, 24),
+ new ECB(35, 25))),
+ new ECBlocks(30, array(new ECB(19, 15),
+ new ECB(35, 16))))),
+ new Version(33, array(6, 30, 58, 86, 114, 142),
+ array(new ECBlocks(30, array(new ECB(17, 115),
+ new ECB(1, 116))),
+ new ECBlocks(28, array(new ECB(14, 46),
+ new ECB(21, 47))),
+ new ECBlocks(30, array(new ECB(29, 24),
+ new ECB(19, 25))),
+ new ECBlocks(30, array(new ECB(11, 15),
+ new ECB(46, 16))))),
+ new Version(34, array(6, 34, 62, 90, 118, 146),
+ array(new ECBlocks(30, array(new ECB(13, 115),
+ new ECB(6, 116))),
+ new ECBlocks(28, array(new ECB(14, 46),
+ new ECB(23, 47))),
+ new ECBlocks(30, array(new ECB(44, 24),
+ new ECB(7, 25))),
+ new ECBlocks(30, array(new ECB(59, 16),
+ new ECB(1, 17))))),
+ new Version(35, array(6, 30, 54, 78, 102, 126, 150),
+ array(new ECBlocks(30, array(new ECB(12, 121),
+ new ECB(7, 122))),
+ new ECBlocks(28, array(new ECB(12, 47),
+ new ECB(26, 48))),
+ new ECBlocks(30, array(new ECB(39, 24),
+ new ECB(14, 25))),
+ new ECBlocks(30, array(new ECB(22, 15),
+ new ECB(41, 16))))),
+ new Version(36, array(6, 24, 50, 76, 102, 128, 154),
+ array(new ECBlocks(30, array(new ECB(6, 121),
+ new ECB(14, 122))),
+ new ECBlocks(28, array(new ECB(6, 47),
+ new ECB(34, 48))),
+ new ECBlocks(30, array(new ECB(46, 24),
+ new ECB(10, 25))),
+ new ECBlocks(30, array(new ECB(2, 15),
+ new ECB(64, 16))))),
+ new Version(37, array(6, 28, 54, 80, 106, 132, 158),
+ array(new ECBlocks(30, array(new ECB(17, 122),
+ new ECB(4, 123))),
+ new ECBlocks(28, array(new ECB(29, 46),
+ new ECB(14, 47))),
+ new ECBlocks(30, array(new ECB(49, 24),
+ new ECB(10, 25))),
+ new ECBlocks(30, array(new ECB(24, 15),
+ new ECB(46, 16))))),
+ new Version(38, array(6, 32, 58, 84, 110, 136, 162),
+ array(new ECBlocks(30, array(new ECB(4, 122),
+ new ECB(18, 123))),
+ new ECBlocks(28, array(new ECB(13, 46),
+ new ECB(32, 47))),
+ new ECBlocks(30, array(new ECB(48, 24),
+ new ECB(14, 25))),
+ new ECBlocks(30, array(new ECB(42, 15),
+ new ECB(32, 16))))),
+ new Version(39, array(6, 26, 54, 82, 110, 138, 166),
+ array(new ECBlocks(30, array(new ECB(20, 117),
+ new ECB(4, 118))),
+ new ECBlocks(28, array(new ECB(40, 47),
+ new ECB(7, 48))),
+ new ECBlocks(30, array(new ECB(43, 24),
+ new ECB(22, 25))),
+ new ECBlocks(30, array(new ECB(10, 15),
+ new ECB(67, 16))))),
+ new Version(40, array(6, 30, 58, 86, 114, 142, 170),
+ array(new ECBlocks(30, array(new ECB(19, 118),
+ new ECB(6, 119))),
+ new ECBlocks(28, array(new ECB(18, 47),
+ new ECB(31, 48))),
+ new ECBlocks(30, array(new ECB(34, 24),
+ new ECB(34, 25))),
+ new ECBlocks(30, array(new ECB(20, 15),
+ new ECB(61, 16)))))
+ );
+ }
+}
+
+/**
+ *
Encapsulates a set of error-correction blocks in one symbol version. Most versions will
+ * use blocks of differing sizes within one version, so, this encapsulates the parameters for
+ * each set of blocks. It also holds the number of error-correction codewords per block since it
+ * will be the same across all blocks within one version.
+ */
+final class ECBlocks
+{
+ private $ecCodewordsPerBlock;
+ private $ecBlocks;
+
+ function __construct($ecCodewordsPerBlock, $ecBlocks)
+ {
+ $this->ecCodewordsPerBlock = $ecCodewordsPerBlock;
+ $this->ecBlocks = $ecBlocks;
+ }
+
+ public function getECCodewordsPerBlock()
+ {
+ return $this->ecCodewordsPerBlock;
+ }
+
+ public function getNumBlocks()
+ {
+ $total = 0;
+ foreach ($this->ecBlocks as $ecBlock) {
+ $total += $ecBlock->getCount();
+ }
+ return $total;
+ }
+
+ public function getTotalECCodewords()
+ {
+ return $this->ecCodewordsPerBlock * $this->getNumBlocks();
+ }
+
+ public function getECBlocks()
+ {
+ return $this->ecBlocks;
+ }
+}
+
+/**
+ *
Encapsualtes the parameters for one error-correction block in one symbol version.
+ * This includes the number of data codewords, and the number of times a block with these
+ * parameters is used consecutively in the QR code version's format.
+ */
+final class ECB
+{
+ private $count;
+ private $dataCodewords;
+
+ function __construct($count, $dataCodewords)
+ {
+ $this->count = $count;
+ $this->dataCodewords = $dataCodewords;
+ }
+
+ public function getCount()
+ {
+ return $this->count;
+ }
+
+ public function getDataCodewords()
+ {
+ return $this->dataCodewords;
+ }
+
+
+//@Override
+ public function toString()
+ {
+ die('Version ECB toString()');
+ // return parent::$versionNumber;
+ }
+
+
+}
+
+
diff --git a/public/qr-code/lib/qrcode/detector/AlignmentPattern.php b/public/qr-code/lib/qrcode/detector/AlignmentPattern.php
new file mode 100644
index 0000000..b6d4130
--- /dev/null
+++ b/public/qr-code/lib/qrcode/detector/AlignmentPattern.php
@@ -0,0 +1,60 @@
+Encapsulates an alignment pattern, which are the smaller square patterns found in
+ * all but the simplest QR Codes.
+ *
+ * @author Sean Owen
+ */
+final class AlignmentPattern extends ResultPoint {
+
+private $estimatedModuleSize;
+
+function __construct($posX, $posY, $estimatedModuleSize) {
+parent::__construct($posX, $posY);
+$this->estimatedModuleSize = $estimatedModuleSize;
+}
+
+ /**
+ *
Determines if this alignment pattern "about equals" an alignment pattern at the stated
+ * position and size -- meaning, it is at nearly the same center with nearly the same size.
+ */
+ function aboutEquals($moduleSize, $i, $j) {
+ if (abs($i - $this->getY()) <= $moduleSize && abs($j - $this->getX()) <= $moduleSize) {
+ $moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize);
+ return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize;
+ }
+ return false;
+}
+
+ /**
+ * Combines this object's current estimate of a finder pattern position and module size
+ * with a new estimate. It returns a new {@code FinderPattern} containing an average of the two.
+ */
+ function combineEstimate($i, $j, $newModuleSize) {
+ $combinedX = ($this->getX() + $j) / 2.0;
+ $combinedY = ($this->getY() + $i) / 2.0;
+ $combinedModuleSize = ($this->estimatedModuleSize + $newModuleSize) / 2.0;
+ return new AlignmentPattern($combinedX, $combinedY, $combinedModuleSize);
+ }
+
+}
\ No newline at end of file
diff --git a/public/qr-code/lib/qrcode/detector/AlignmentPatternFinder.php b/public/qr-code/lib/qrcode/detector/AlignmentPatternFinder.php
new file mode 100644
index 0000000..0484ffa
--- /dev/null
+++ b/public/qr-code/lib/qrcode/detector/AlignmentPatternFinder.php
@@ -0,0 +1,277 @@
+This class attempts to find alignment patterns in a QR Code. Alignment patterns look like finder
+ * patterns but are smaller and appear at regular intervals throughout the image.
+ *
+ *
At the moment this only looks for the bottom-right alignment pattern.
+ *
+ *
This is mostly a simplified copy of {@link FinderPatternFinder}. It is copied,
+ * pasted and stripped down here for maximum performance but does unfortunately duplicate
+ * some code.
+ *
+ *
This class is thread-safe but not reentrant. Each thread must allocate its own object.
Creates a finder that will look in a portion of the whole image.
+ *
+ * @param image image to search
+ * @param startX left column from which to start searching
+ * @param startY top row from which to start searching
+ * @param width width of region to search
+ * @param height height of region to search
+ * @param moduleSize estimated module size so far
+ */
+ function __construct($image,
+ $startX,
+ $startY,
+ $width,
+ $height,
+ $moduleSize,
+ $resultPointCallback) {
+ $this->image = $image;
+ $this->possibleCenters = array();
+ $this->startX = $startX;
+ $this->startY = $startY;
+ $this->width = $width;
+ $this->height = $height;
+ $this->moduleSize = $moduleSize;
+ $this->crossCheckStateCount = array();
+ $this->resultPointCallback = $resultPointCallback;
+ }
+
+ /**
+ *
This method attempts to find the bottom-right alignment pattern in the image. It is a bit messy since
+ * it's pretty performance-critical and so is written to be fast foremost.
+ *
+ * @return {@link AlignmentPattern} if found
+ * @throws NotFoundException if not found
+ */
+ function find() {
+ $startX = $this->startX;
+ $height = $this->height;
+ $maxJ = $startX + $this->width;
+ $middleI = $this->startY + ($height / 2);
+ // We are looking for black/white/black modules in 1:1:1 ratio;
+ // this tracks the number of black/white/black modules seen so far
+ $stateCount = array();
+ for ($iGen = 0; $iGen < $height; $iGen++) {
+ // Search from middle outwards
+ $i = $middleI + (($iGen & 0x01) == 0 ? ($iGen + 1) / 2 : -(($iGen + 1) / 2));
+ $i = intval($i);
+ $stateCount[0] = 0;
+ $stateCount[1] = 0;
+ $stateCount[2] = 0;
+ $j = $startX;
+ // Burn off leading white pixels before anything else; if we start in the middle of
+ // a white run, it doesn't make sense to count its length, since we don't know if the
+ // white run continued to the left of the start point
+ while ($j < $maxJ && !$this->image->get($j, $i)) {
+ $j++;
+ }
+ $currentState = 0;
+ while ($j < $maxJ) {
+ if ($this->image->get($j, $i)) {
+ // Black pixel
+ if ($currentState == 1) { // Counting black pixels
+ $stateCount[$currentState]++;
+ } else { // Counting white pixels
+ if ($currentState == 2) { // A winner?
+ if ($this->foundPatternCross($stateCount)) { // Yes
+ $confirmed = $this->handlePossibleCenter($stateCount, $i, $j);
+ if ($confirmed != null) {
+ return $confirmed;
+ }
+ }
+ $stateCount[0] = $stateCount[2];
+ $stateCount[1] = 1;
+ $stateCount[2] = 0;
+ $currentState = 1;
+ } else {
+ $stateCount[++$currentState]++;
+ }
+ }
+ } else { // White pixel
+ if ($currentState == 1) { // Counting black pixels
+ $currentState++;
+ }
+ $stateCount[$currentState]++;
+ }
+ $j++;
+ }
+ if ($this->foundPatternCross($stateCount)) {
+ $confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ);
+ if ($confirmed != null) {
+ return $confirmed;
+ }
+ }
+
+ }
+
+ // Hmm, nothing we saw was observed and confirmed twice. If we had
+ // any guess at all, return it.
+ if (count($this->possibleCenters)) {
+ return $this->possibleCenters[0];
+ }
+
+ throw NotFoundException::getNotFoundInstance();
+ }
+
+ /**
+ * Given a count of black/white/black pixels just seen and an end position,
+ * figures the location of the center of this black/white/black run.
+ */
+ private static function centerFromEnd($stateCount, $end) {
+ return (float) ($end - $stateCount[2]) - $stateCount[1] / 2.0;
+ }
+
+ /**
+ * @param stateCount count of black/white/black pixels just read
+ * @return true iff the proportions of the counts is close enough to the 1/1/1 ratios
+ * used by alignment patterns to be considered a match
+ */
+ private function foundPatternCross($stateCount) {
+ $moduleSize = $this->moduleSize;
+ $maxVariance = $moduleSize / 2.0;
+ for ($i = 0; $i < 3; $i++) {
+ if (abs($moduleSize - $stateCount[$i]) >= $maxVariance) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ *
After a horizontal scan finds a potential alignment pattern, this method
+ * "cross-checks" by scanning down vertically through the center of the possible
+ * alignment pattern to see if the same proportion is detected.
+ *
+ * @param startI row where an alignment pattern was detected
+ * @param centerJ center of the section that appears to cross an alignment pattern
+ * @param maxCount maximum reasonable number of modules that should be
+ * observed in any reading state, based on the results of the horizontal scan
+ * @return vertical center of alignment pattern, or {@link Float#NaN} if not found
+ */
+ private function crossCheckVertical($startI, $centerJ, $maxCount,
+ $originalStateCountTotal) {
+ $image = $this->image;
+
+ $maxI = $image->getHeight();
+ $stateCount = $this->crossCheckStateCount;
+ $stateCount[0] = 0;
+ $stateCount[1] = 0;
+ $stateCount[2] = 0;
+
+ // Start counting up from center
+ $i = $startI;
+ while ($i >= 0 && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) {
+ $stateCount[1]++;
+ $i--;
+ }
+ // If already too many modules in this state or ran off the edge:
+ if ($i < 0 || $stateCount[1] > $maxCount) {
+ return NAN;
+ }
+ while ($i >= 0 && !$image->get($centerJ, $i) && $stateCount[0] <= $maxCount) {
+ $stateCount[0]++;
+ $i--;
+ }
+ if ($stateCount[0] > $maxCount) {
+ return NAN;
+ }
+
+ // Now also count down from center
+ $i = $startI + 1;
+ while ($i < $maxI && $image->get($centerJ, $i) && $stateCount[1] <= $maxCount) {
+ $stateCount[1]++;
+ $i++;
+ }
+ if ($i == $maxI || $stateCount[1] > $maxCount) {
+ return NAN;
+ }
+ while ($i < $maxI && !$image->get($centerJ, $i) && $stateCount[2] <= $maxCount) {
+ $stateCount[2]++;
+ $i++;
+ }
+ if ($stateCount[2] > $maxCount) {
+ return NAN;
+ }
+
+ $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2];
+ if (5 * abs($stateCountTotal - $originalStateCountTotal) >= 2 * $originalStateCountTotal) {
+ return NAN;
+ }
+
+ return $this->foundPatternCross($stateCount) ? $this->centerFromEnd($stateCount, $i) : NAN;
+ }
+
+ /**
+ *
This is called when a horizontal scan finds a possible alignment pattern. It will
+ * cross check with a vertical scan, and if successful, will see if this pattern had been
+ * found on a previous horizontal scan. If so, we consider it confirmed and conclude we have
+ * found the alignment pattern.
+ *
+ * @param stateCount reading state module counts from horizontal scan
+ * @param i row where alignment pattern may be found
+ * @param j end of possible alignment pattern in row
+ * @return {@link AlignmentPattern} if we have found the same pattern twice, or null if not
+ */
+ private function handlePossibleCenter($stateCount, $i, $j) {
+ $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2];
+ $centerJ = $this->centerFromEnd($stateCount, $j);
+ $centerI = $this->crossCheckVertical($i, (int) $centerJ, 2 * $stateCount[1], $stateCountTotal);
+ if (!is_nan($centerI)) {
+ $estimatedModuleSize = (float) ($stateCount[0] + $stateCount[1] + $stateCount[2]) / 3.0;
+ foreach ($this->possibleCenters as $center) {
+ // Look for about the same center and module size:
+ if ($center->aboutEquals($estimatedModuleSize, $centerI, $centerJ)) {
+ return $center->combineEstimate($centerI, $centerJ, $estimatedModuleSize);
+ }
+ }
+ // Hadn't found this before; save it
+ $point = new AlignmentPattern($centerJ, $centerI, $estimatedModuleSize);
+ $this->possibleCenters[] = $point;
+ if ($this->resultPointCallback != null) {
+ $this->resultPointCallback->foundPossibleResultPoint($point);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/public/qr-code/lib/qrcode/detector/Detector.php b/public/qr-code/lib/qrcode/detector/Detector.php
new file mode 100644
index 0000000..2043a49
--- /dev/null
+++ b/public/qr-code/lib/qrcode/detector/Detector.php
@@ -0,0 +1,405 @@
+Encapsulates logic that can detect a QR Code in an image, even if the QR Code
+ * is rotated or skewed, or partially obscured.
+ *
+ * @author Sean Owen
+ */
+?>
+image = $image;
+ }
+
+ protected final function getImage() {
+ return $this->image;
+ }
+
+ protected final function getResultPointCallback() {
+ return $this->resultPointCallback;
+ }
+
+ /**
+ *
Detects a QR Code in an image.
+ *
+ * @return {@link DetectorResult} encapsulating results of detecting a QR Code
+ * @throws NotFoundException if QR Code cannot be found
+ * @throws FormatException if a QR Code cannot be decoded
+ */
+
+
+ /**
+ *
Detects a QR Code in an image.
+ *
+ * @param hints optional hints to detector
+ * @return {@link DetectorResult} encapsulating results of detecting a QR Code
+ * @throws NotFoundException if QR Code cannot be found
+ * @throws FormatException if a QR Code cannot be decoded
+ */
+ public final function detect($hints=null){/*Map*/
+
+ $resultPointCallback = $hints == null ? null :
+ $hints->get('NEED_RESULT_POINT_CALLBACK');
+ /* resultPointCallback = hints == null ? null :
+ (ResultPointCallback) hints.get(DecodeHintType.NEED_RESULT_POINT_CALLBACK);*/
+ $finder = new FinderPatternFinder($this->image, $resultPointCallback);
+ $info = $finder->find($hints);
+
+ return $this->processFinderPatternInfo($info);
+ }
+
+ protected final function processFinderPatternInfo($info){
+
+ $topLeft = $info->getTopLeft();
+ $topRight = $info->getTopRight();
+ $bottomLeft = $info->getBottomLeft();
+
+ $moduleSize = (float) $this->calculateModuleSize($topLeft, $topRight, $bottomLeft);
+ if ($moduleSize < 1.0) {
+ throw NotFoundException::getNotFoundInstance();
+ }
+ $dimension =(int) $this->computeDimension($topLeft, $topRight, $bottomLeft, $moduleSize);
+ $provisionalVersion = Version::getProvisionalVersionForDimension($dimension);
+ $modulesBetweenFPCenters = $provisionalVersion->getDimensionForVersion() - 7;
+
+ $alignmentPattern = null;
+// Anything above version 1 has an alignment pattern
+ if (count($provisionalVersion->getAlignmentPatternCenters())> 0) {
+
+// Guess where a "bottom right" finder pattern would have been
+ $bottomRightX = $topRight->getX() - $topLeft->getX() + $bottomLeft->getX();
+ $bottomRightY = $topRight->getY() - $topLeft->getY() + $bottomLeft->getY();
+
+// Estimate that alignment pattern is closer by 3 modules
+// from "bottom right" to known top left location
+ $correctionToTopLeft = 1.0 - 3.0 / (float) $modulesBetweenFPCenters;
+ $estAlignmentX = (int) ($topLeft->getX() + $correctionToTopLeft * ($bottomRightX - $topLeft->getX()));
+ $estAlignmentY = (int) ($topLeft->getY() + $correctionToTopLeft * ($bottomRightY - $topLeft->getY()));
+
+// Kind of arbitrary -- expand search radius before giving up
+ for ($i = 4; $i <= 16; $i <<= 1) {//??????????
+ try {
+ $alignmentPattern = $this->findAlignmentInRegion($moduleSize,
+ $estAlignmentX,
+ $estAlignmentY,
+ (float) $i);
+ break;
+ } catch (NotFoundException $re) {
+// try next round
+ }
+ }
+// If we didn't find alignment pattern... well try anyway without it
+ }
+
+ $transform =
+ $this->createTransform($topLeft, $topRight, $bottomLeft, $alignmentPattern, $dimension);
+
+ $bits = $this->sampleGrid($this->image, $transform, $dimension);
+
+ $points = array();
+ if ($alignmentPattern == null) {
+ $points = array($bottomLeft, $topLeft, $topRight);
+ } else {
+ // die('$points = new ResultPoint[]{bottomLeft, topLeft, topRight, alignmentPattern};');
+$points = array($bottomLeft, $topLeft, $topRight, $alignmentPattern);
+ }
+ return new DetectorResult($bits, $points);
+ }
+
+ private static function createTransform($topLeft,
+ $topRight,
+ $bottomLeft,
+ $alignmentPattern,
+ $dimension) {
+ $dimMinusThree = (float) $dimension - 3.5;
+ $bottomRightX = 0.0;
+ $bottomRightY = 0.0;
+ $sourceBottomRightX = 0.0;
+ $sourceBottomRightY = 0.0;
+ if ($alignmentPattern != null) {
+ $bottomRightX = $alignmentPattern->getX();
+ $bottomRightY = $alignmentPattern->getY();
+ $sourceBottomRightX = $dimMinusThree - 3.0;
+ $sourceBottomRightY = $sourceBottomRightX;
+ } else {
+// Don't have an alignment pattern, just make up the bottom-right point
+ $bottomRightX = ($topRight->getX() - $topLeft->getX()) + $bottomLeft->getX();
+ $bottomRightY = ($topRight->getY() - $topLeft->getY()) + $bottomLeft->getY();
+ $sourceBottomRightX = $dimMinusThree;
+ $sourceBottomRightY = $dimMinusThree;
+ }
+
+ return PerspectiveTransform::quadrilateralToQuadrilateral(
+ 3.5,
+ 3.5,
+ $dimMinusThree,
+ 3.5,
+ $sourceBottomRightX,
+ $sourceBottomRightY,
+ 3.5,
+ $dimMinusThree,
+ $topLeft->getX(),
+ $topLeft->getY(),
+ $topRight->getX(),
+ $topRight->getY(),
+ $bottomRightX,
+ $bottomRightY,
+ $bottomLeft->getX(),
+ $bottomLeft->getY());
+ }
+
+ private static function sampleGrid($image, $transform,
+ $dimension) {
+
+ $sampler = GridSampler::getInstance();
+ return $sampler->sampleGrid_($image, $dimension, $dimension, $transform);
+ }
+
+ /**
+ *
Computes the dimension (number of modules on a size) of the QR Code based on the position
+ * of the finder patterns and estimated module size.
Computes an average estimated module size based on estimated derived from the positions
+ * of the three finder patterns.
+ *
+ * @param topLeft detected top-left finder pattern center
+ * @param topRight detected top-right finder pattern center
+ * @param bottomLeft detected bottom-left finder pattern center
+ * @return estimated module size
+ */
+ protected final function calculateModuleSize($topLeft,
+ $topRight,
+ $bottomLeft) {
+// Take the average
+ return ($this->calculateModuleSizeOneWay($topLeft, $topRight) +
+ $this->calculateModuleSizeOneWay($topLeft, $bottomLeft)) / 2.0;
+ }
+
+ /**
+ *
Estimates module size based on two finder patterns -- it uses
+ * {@link #sizeOfBlackWhiteBlackRunBothWays(int, int, int, int)} to figure the
+ * width of each, measuring along the axis between their centers.
+ */
+ private function calculateModuleSizeOneWay($pattern, $otherPattern) {
+ $moduleSizeEst1 = $this->sizeOfBlackWhiteBlackRunBothWays($pattern->getX(),
+ (int) $pattern->getY(),
+ (int) $otherPattern->getX(),
+ (int) $otherPattern->getY());
+ $moduleSizeEst2 = $this->sizeOfBlackWhiteBlackRunBothWays((int) $otherPattern->getX(),
+ (int) $otherPattern->getY(),
+ (int) $pattern->getX(),
+ (int) $pattern->getY());
+ if (is_nan($moduleSizeEst1)) {
+ return $moduleSizeEst2 / 7.0;
+ }
+ if (is_nan($moduleSizeEst2)) {
+ return $moduleSizeEst1 / 7.0;
+ }
+// Average them, and divide by 7 since we've counted the width of 3 black modules,
+// and 1 white and 1 black module on either side. Ergo, divide sum by 14.
+ return ($moduleSizeEst1 + $moduleSizeEst2) / 14.0;
+ }
+
+ /**
+ * See {@link #sizeOfBlackWhiteBlackRun(int, int, int, int)}; computes the total width of
+ * a finder pattern by looking for a black-white-black run from the center in the direction
+ * of another po$(another finder pattern center), and in the opposite direction too.
+ */
+ private function sizeOfBlackWhiteBlackRunBothWays($fromX, $fromY, $toX, $toY) {
+
+ $result = $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY);
+
+// Now count other way -- don't run off image though of course
+ $scale = 1.0;
+ $otherToX = $fromX - ($toX - $fromX);
+ if ($otherToX < 0) {
+ $scale = (float) $fromX / (float) ($fromX - $otherToX);
+ $otherToX = 0;
+ } else if ($otherToX >= $this->image->getWidth()) {
+ $scale = (float) ($this->image->getWidth() - 1 - $fromX) / (float) ($otherToX - $fromX);
+ $otherToX = $this->image->getWidth() - 1;
+ }
+ $otherToY = (int) ($fromY - ($toY - $fromY) * $scale);
+
+ $scale = 1.0;
+ if ($otherToY < 0) {
+ $scale = (float) $fromY / (float) ($fromY - $otherToY);
+ $otherToY = 0;
+ } else if ($otherToY >= $this->image->getHeight()) {
+ $scale = (float) ($this->image->getHeight() - 1 - $fromY) / (float) ($otherToY - $fromY);
+ $otherToY = $this->image->getHeight() - 1;
+ }
+ $otherToX = (int) ($fromX + ($otherToX - $fromX) * $scale);
+
+ $result += $this->sizeOfBlackWhiteBlackRun($fromX, $fromY, $otherToX, $otherToY);
+
+// Middle pixel is double-counted this way; subtract 1
+ return $result - 1.0;
+ }
+
+ /**
+ *
This method traces a line from a po$in the image, in the direction towards another point.
+ * It begins in a black region, and keeps going until it finds white, then black, then white again.
+ * It reports the distance from the start to this point.
+ *
+ *
This is used when figuring out how wide a finder pattern is, when the finder pattern
+ * may be skewed or rotated.
+ */
+ private function sizeOfBlackWhiteBlackRun($fromX, $fromY, $toX, $toY) {
+// Mild variant of Bresenham's algorithm;
+// see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
+ $steep = abs($toY - $fromY) > abs($toX - $fromX);
+ if ($steep) {
+ $temp = $fromX;
+ $fromX = $fromY;
+ $fromY = $temp;
+ $temp = $toX;
+ $toX = $toY;
+ $toY = $temp;
+ }
+
+ $dx = abs($toX - $fromX);
+ $dy = abs($toY - $fromY);
+ $error = -$dx / 2;
+ $xstep = $fromX < $toX ? 1 : -1;
+ $ystep = $fromY < $toY ? 1 : -1;
+
+// In black pixels, looking for white, first or second time.
+ $state = 0;
+// Loop up until x == toX, but not beyond
+ $xLimit = $toX + $xstep;
+ for ($x = $fromX, $y = $fromY; $x != $xLimit; $x += $xstep) {
+ $realX = $steep ? $y : $x;
+ $realY = $steep ? $x : $y;
+
+// Does current pixel mean we have moved white to black or vice versa?
+// Scanning black in state 0,2 and white in state 1, so if we find the wrong
+// color, advance to next state or end if we are in state 2 already
+ if (($state == 1) == $this->image->get($realX, $realY)) {
+ if ($state == 2) {
+ return MathUtils::distance($x, $y, $fromX, $fromY);
+ }
+ $state++;
+ }
+
+ $error += $dy;
+ if ($error > 0) {
+ if ($y == $toY) {
+ break;
+ }
+ $y += $ystep;
+ $error -= $dx;
+ }
+ }
+// Found black-white-black; give the benefit of the doubt that the next pixel outside the image
+// is "white" so this last po$at (toX+xStep,toY) is the right ending. This is really a
+// small approximation; (toX+xStep,toY+yStep) might be really correct. Ignore this.
+ if ($state == 2) {
+ return MathUtils::distance($toX + $xstep, $toY, $fromX, $fromY);
+ }
+// else we didn't find even black-white-black; no estimate is really possible
+ return NAN;
+ }
+
+ /**
+ *
Attempts to locate an alignment pattern in a limited region of the image, which is
+ * guessed to contain it. This method uses {@link AlignmentPattern}.
+ *
+ * @param overallEstModuleSize estimated module size so far
+ * @param estAlignmentX x coordinate of center of area probably containing alignment pattern
+ * @param estAlignmentY y coordinate of above
+ * @param allowanceFactor number of pixels in all directions to search from the center
+ * @return {@link AlignmentPattern} if found, or null otherwise
+ * @throws NotFoundException if an unexpected error occurs during detection
+ */
+ protected final function findAlignmentInRegion($overallEstModuleSize,
+ $estAlignmentX,
+ $estAlignmentY,
+ $allowanceFactor)
+ {
+// Look for an alignment pattern (3 modules in size) around where it
+// should be
+ $allowance = (int) ($allowanceFactor * $overallEstModuleSize);
+ $alignmentAreaLeftX = max(0, $estAlignmentX - $allowance);
+ $alignmentAreaRightX = min($this->image->getWidth() - 1, $estAlignmentX + $allowance);
+ if ($alignmentAreaRightX - $alignmentAreaLeftX < $overallEstModuleSize * 3) {
+ throw NotFoundException::getNotFoundInstance();
+ }
+
+ $alignmentAreaTopY = max(0, $estAlignmentY - $allowance);
+ $alignmentAreaBottomY = min($this->image->getHeight() - 1, $estAlignmentY + $allowance);
+ if ($alignmentAreaBottomY - $alignmentAreaTopY < $overallEstModuleSize * 3) {
+ throw NotFoundException::getNotFoundInstance();
+ }
+
+ $alignmentFinder =
+ new AlignmentPatternFinder(
+ $this->image,
+ $alignmentAreaLeftX,
+ $alignmentAreaTopY,
+ $alignmentAreaRightX - $alignmentAreaLeftX,
+ $alignmentAreaBottomY - $alignmentAreaTopY,
+ $overallEstModuleSize,
+ $this->resultPointCallback);
+ return $alignmentFinder->find();
+ }
+
+}
diff --git a/public/qr-code/lib/qrcode/detector/FinderPattern.php b/public/qr-code/lib/qrcode/detector/FinderPattern.php
new file mode 100644
index 0000000..1900a78
--- /dev/null
+++ b/public/qr-code/lib/qrcode/detector/FinderPattern.php
@@ -0,0 +1,81 @@
+Encapsulates a finder pattern, which are the three square patterns found in
+ * the corners of QR Codes. It also encapsulates a count of similar finder patterns,
+ * as a convenience to the finder's bookkeeping.
+ *
+ * @author Sean Owen
+ */
+final class FinderPattern extends ResultPoint {
+
+private $estimatedModuleSize;
+private $count;
+
+
+
+ function __construct($posX, $posY, $estimatedModuleSize, $count=1) {
+ parent::__construct($posX, $posY);
+ $this->estimatedModuleSize = $estimatedModuleSize;
+ $this->count = $count;
+}
+
+ public function getEstimatedModuleSize() {
+ return $this->estimatedModuleSize;
+ }
+
+ function getCount() {
+ return $this->count;
+ }
+
+ /*
+ void incrementCount() {
+ this.count++;
+ }
+ */
+
+ /**
+ *
Determines if this finder pattern "about equals" a finder pattern at the stated
+ * position and size -- meaning, it is at nearly the same center with nearly the same size.
+ */
+ function aboutEquals($moduleSize, $i, $j) {
+ if (abs($i - $this->getY()) <= $moduleSize && abs($j - $this->getX()) <= $moduleSize) {
+ $moduleSizeDiff = abs($moduleSize - $this->estimatedModuleSize);
+ return $moduleSizeDiff <= 1.0 || $moduleSizeDiff <= $this->estimatedModuleSize;
+ }
+ return false;
+}
+
+ /**
+ * Combines this object's current estimate of a finder pattern position and module size
+ * with a new estimate. It returns a new {@code FinderPattern} containing a weighted average
+ * based on count.
+ */
+ function combineEstimate($i, $j, $newModuleSize) {
+ $combinedCount = $this->count + 1;
+ $combinedX = ($this->count * $this->getX() + $j) / $combinedCount;
+ $combinedY = ($this->count * $this->getY() + $i) / $combinedCount;
+ $combinedModuleSize = ($this->count * $this->estimatedModuleSize + $newModuleSize) / $combinedCount;
+ return new FinderPattern($combinedX, $combinedY, $combinedModuleSize, $combinedCount);
+ }
+
+}
diff --git a/public/qr-code/lib/qrcode/detector/FinderPatternFinder.php b/public/qr-code/lib/qrcode/detector/FinderPatternFinder.php
new file mode 100644
index 0000000..d2fe473
--- /dev/null
+++ b/public/qr-code/lib/qrcode/detector/FinderPatternFinder.php
@@ -0,0 +1,705 @@
+This class attempts to find finder patterns in a QR Code. Finder patterns are the square
+ * markers at three corners of a QR Code.
+ *
+ *
This class is thread-safe but not reentrant. Each thread must allocate its own object.
+ *
+ * @author Sean Owen
+ */
+class FinderPatternFinder
+{
+
+ private static $CENTER_QUORUM = 2;
+ protected static $MIN_SKIP = 3; // 1 pixel/module times 3 modules/center
+ protected static $MAX_MODULES = 57; // support up to version 10 for mobile clients
+
+ private $image;
+ private $average;
+ private $possibleCenters; //private final List possibleCenters;
+ private $hasSkipped = false;
+ private $crossCheckStateCount;
+ private $resultPointCallback;
+
+ /**
+ *
Creates a finder that will search the image for three finder patterns.
+ *
+ * @param image image to search
+ */
+ public function __construct($image, $resultPointCallback = null)
+ {
+ $this->image = $image;
+
+
+ $this->possibleCenters = array();//new ArrayList<>();
+ $this->crossCheckStateCount = fill_array(0,5,0);
+ $this->resultPointCallback = $resultPointCallback;
+ }
+
+ protected final function getImage()
+ {
+ return $this->image;
+ }
+
+ protected final function getPossibleCenters()
+ { //List getPossibleCenters()
+ return $this->possibleCenters;
+ }
+
+ final function find($hints)
+ {/*final FinderPatternInfo find(Map hints) throws NotFoundException {*/
+ $tryHarder = $hints != null && $hints['TRY_HARDER'];
+ $pureBarcode = $hints != null && $hints['PURE_BARCODE'];
+ $maxI = $this->image->getHeight();
+ $maxJ = $this->image->getWidth();
+ // We are looking for black/white/black/white/black modules in
+ // 1:1:3:1:1 ratio; this tracks the number of such modules seen so far
+
+ // Let's assume that the maximum version QR Code we support takes up 1/4 the height of the
+ // image, and then account for the center being 3 modules in size. This gives the smallest
+ // number of pixels the center could be, so skip this often. When trying harder, look for all
+ // QR versions regardless of how dense they are.
+ $iSkip = intval((3 * $maxI) / (4 * self::$MAX_MODULES));
+ if ($iSkip < self::$MIN_SKIP || $tryHarder) {
+ $iSkip = self::$MIN_SKIP;
+ }
+
+ $done = false;
+ $stateCount = array();
+ for ($i = $iSkip - 1; $i < $maxI && !$done; $i += $iSkip) {
+ // Get a row of black/white values
+ $stateCount[0] = 0;
+ $stateCount[1] = 0;
+ $stateCount[2] = 0;
+ $stateCount[3] = 0;
+ $stateCount[4] = 0;
+ $currentState = 0;
+ for ($j = 0; $j < $maxJ; $j++) {
+ if ($this->image->get($j, $i)) {
+ // Black pixel
+ if (($currentState & 1) == 1) { // Counting white pixels
+ $currentState++;
+ }
+ $stateCount[$currentState]++;
+ } else { // White pixel
+ if (($currentState & 1) == 0) { // Counting black pixels
+ if ($currentState == 4) { // A winner?
+ if ($this->foundPatternCross($stateCount)) { // Yes
+ $confirmed = $this->handlePossibleCenter($stateCount, $i, $j, $pureBarcode);
+ if ($confirmed) {
+ // Start examining every other line. Checking each line turned out to be too
+ // expensive and didn't improve performance.
+ $iSkip = 2;
+ if ($this->hasSkipped) {
+ $done = $this->haveMultiplyConfirmedCenters();
+ } else {
+ $rowSkip = $this->findRowSkip();
+ if ($rowSkip > $stateCount[2]) {
+ // Skip rows between row of lower confirmed center
+ // and top of presumed third confirmed center
+ // but back up a bit to get a full chance of detecting
+ // it, entire width of center of finder pattern
+
+ // Skip by rowSkip, but back off by $stateCount[2] (size of last center
+ // of pattern we saw) to be conservative, and also back off by iSkip which
+ // is about to be re-added
+ $i += $rowSkip - $stateCount[2] - $iSkip;
+ $j = $maxJ - 1;
+ }
+ }
+ } else {
+
+
+
+ $stateCount[0] = $stateCount[2];
+ $stateCount[1] = $stateCount[3];
+ $stateCount[2] = $stateCount[4];
+ $stateCount[3] = 1;
+ $stateCount[4] = 0;
+ $currentState = 3;
+ continue;
+ }
+ // Clear state to start looking again
+ $currentState = 0;
+ $stateCount[0] = 0;
+ $stateCount[1] = 0;
+ $stateCount[2] = 0;
+ $stateCount[3] = 0;
+ $stateCount[4] = 0;
+ } else { // No, shift counts back by two
+ $stateCount[0] = $stateCount[2];
+ $stateCount[1] = $stateCount[3];
+ $stateCount[2] = $stateCount[4];
+ $stateCount[3] = 1;
+ $stateCount[4] = 0;
+ $currentState = 3;
+ }
+ } else {
+ $stateCount[++$currentState]++;
+ }
+ } else { // Counting white pixels
+ $stateCount[$currentState]++;
+ }
+ }
+ }
+ if ($this->foundPatternCross($stateCount)) {
+ $confirmed = $this->handlePossibleCenter($stateCount, $i, $maxJ, $pureBarcode);
+ if ($confirmed) {
+ $iSkip = $stateCount[0];
+ if ($this->hasSkipped) {
+ // Found a third one
+ $done = $this->haveMultiplyConfirmedCenters();
+ }
+ }
+ }
+ }
+
+ $patternInfo = $this->selectBestPatterns();
+ $patternInfo = ResultPoint::orderBestPatterns($patternInfo);
+
+ return new FinderPatternInfo($patternInfo);
+ }
+
+ /**
+ * Given a count of black/white/black/white/black pixels just seen and an end position,
+ * figures the location of the center of this run.
+ */
+ private static function centerFromEnd($stateCount, $end)
+ {
+ return (float)($end - $stateCount[4] - $stateCount[3]) - $stateCount[2] / 2.0;
+ }
+
+ /**
+ * @param $stateCount ; count of black/white/black/white/black pixels just read
+ * @return true iff the proportions of the counts is close enough to the 1/1/3/1/1 ratios
+ * used by finder patterns to be considered a match
+ */
+ protected static function foundPatternCross($stateCount)
+ {
+ $totalModuleSize = 0;
+ for ($i = 0; $i < 5; $i++) {
+ $count = $stateCount[$i];
+ if ($count == 0) {
+ return false;
+ }
+ $totalModuleSize += $count;
+ }
+ if ($totalModuleSize < 7) {
+ return false;
+ }
+ $moduleSize = $totalModuleSize / 7.0;
+ $maxVariance = $moduleSize / 2.0;
+ // Allow less than 50% variance from 1-1-3-1-1 proportions
+ return
+ abs($moduleSize - $stateCount[0]) < $maxVariance &&
+ abs($moduleSize - $stateCount[1]) < $maxVariance &&
+ abs(3.0 * $moduleSize - $stateCount[2]) < 3 * $maxVariance &&
+ abs($moduleSize - $stateCount[3]) < $maxVariance &&
+ abs($moduleSize - $stateCount[4]) < $maxVariance;
+ }
+
+ private function getCrossCheckStateCount()
+ {
+ $this->crossCheckStateCount[0] = 0;
+ $this->crossCheckStateCount[1] = 0;
+ $this->crossCheckStateCount[2] = 0;
+ $this->crossCheckStateCount[3] = 0;
+ $this->crossCheckStateCount[4] = 0;
+ return $this->crossCheckStateCount;
+ }
+
+ /**
+ * After a vertical and horizontal scan finds a potential finder pattern, this method
+ * "cross-cross-cross-checks" by scanning down diagonally through the center of the possible
+ * finder pattern to see if the same proportion is detected.
+ *
+ * @param $startI ; row where a finder pattern was detected
+ * @param centerJ ; center of the section that appears to cross a finder pattern
+ * @param $maxCount ; maximum reasonable number of modules that should be
+ * observed in any reading state, based on the results of the horizontal scan
+ * @param originalStateCountTotal ; The original state count total.
+ * @return true if proportions are withing expected limits
+ */
+ private function crossCheckDiagonal($startI, $centerJ, $maxCount, $originalStateCountTotal)
+ {
+ $stateCount = $this->getCrossCheckStateCount();
+
+ // Start counting up, left from center finding black center mass
+ $i = 0;
+ $startI = intval($startI);
+ $centerJ = intval($centerJ);
+ while ($startI >= $i && $centerJ >= $i && $this->image->get($centerJ - $i, $startI - $i)) {
+ $stateCount[2]++;
+ $i++;
+ }
+
+ if ($startI < $i || $centerJ < $i) {
+ return false;
+ }
+
+ // Continue up, left finding white space
+ while ($startI >= $i && $centerJ >= $i && !$this->image->get($centerJ - $i, $startI - $i) &&
+ $stateCount[1] <= $maxCount) {
+ $stateCount[1]++;
+ $i++;
+ }
+
+ // If already too many modules in this state or ran off the edge:
+ if ($startI < $i || $centerJ < $i || $stateCount[1] > $maxCount) {
+ return false;
+ }
+
+ // Continue up, left finding black border
+ while ($startI >= $i && $centerJ >= $i && $this->image->get($centerJ - $i, $startI - $i) &&
+ $stateCount[0] <= $maxCount) {
+ $stateCount[0]++;
+ $i++;
+ }
+ if ($stateCount[0] > $maxCount) {
+ return false;
+ }
+
+ $maxI = $this->image->getHeight();
+ $maxJ = $this->image->getWidth();
+
+ // Now also count down, right from center
+ $i = 1;
+ while ($startI + $i < $maxI && $centerJ + $i < $maxJ && $this->image->get($centerJ + $i, $startI + $i)) {
+ $stateCount[2]++;
+ $i++;
+ }
+
+ // Ran off the edge?
+ if ($startI + $i >= $maxI || $centerJ + $i >= $maxJ) {
+ return false;
+ }
+
+ while ($startI + $i < $maxI && $centerJ + $i < $maxJ && !$this->image->get($centerJ + $i, $startI + $i) &&
+ $stateCount[3] < $maxCount) {
+ $stateCount[3]++;
+ $i++;
+ }
+
+ if ($startI + $i >= $maxI || $centerJ + $i >= $maxJ || $stateCount[3] >= $maxCount) {
+ return false;
+ }
+
+ while ($startI + $i < $maxI && $centerJ + $i < $maxJ && $this->image->get($centerJ + $i, $startI + $i) &&
+ $stateCount[4] < $maxCount) {
+ $stateCount[4]++;
+ $i++;
+ }
+
+ if ($stateCount[4] >= $maxCount) {
+ return false;
+ }
+
+ // If we found a finder-pattern-like section, but its size is more than 100% different than
+ // the original, assume it's a false positive
+ $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] + $stateCount[4];
+ return
+ abs($stateCountTotal - $originalStateCountTotal) < 2 * $originalStateCountTotal &&
+ $this->foundPatternCross($stateCount);
+ }
+
+ /**
+ *
After a horizontal scan finds a potential finder pattern, this method
+ * "cross-checks" by scanning down vertically through the center of the possible
+ * finder pattern to see if the same proportion is detected.
+ *
+ * @param $startI ; row where a finder pattern was detected
+ * @param centerJ ; center of the section that appears to cross a finder pattern
+ * @param $maxCount ; maximum reasonable number of modules that should be
+ * observed in any reading state, based on the results of the horizontal scan
+ * @return vertical center of finder pattern, or {@link Float#NaN} if not found
+ */
+ private function crossCheckVertical($startI, $centerJ, $maxCount,
+ $originalStateCountTotal)
+ {
+ $image = $this->image;
+
+ $maxI = $image->getHeight();
+ $stateCount = $this->getCrossCheckStateCount();
+
+ // Start counting up from center
+ $i = $startI;
+ while ($i >= 0 && $image->get($centerJ, $i)) {
+ $stateCount[2]++;
+ $i--;
+ }
+ if ($i < 0) {
+ return NAN;
+ }
+ while ($i >= 0 && !$image->get($centerJ, $i) && $stateCount[1] <= $maxCount) {
+ $stateCount[1]++;
+ $i--;
+ }
+ // If already too many modules in this state or ran off the edge:
+ if ($i < 0 || $stateCount[1] > $maxCount) {
+ return NAN;
+ }
+ while ($i >= 0 && $image->get($centerJ, $i) && $stateCount[0] <= $maxCount) {
+ $stateCount[0]++;
+ $i--;
+ }
+ if ($stateCount[0] > $maxCount) {
+ return NAN;
+ }
+
+ // Now also count down from center
+ $i = $startI + 1;
+ while ($i < $maxI && $image->get($centerJ, $i)) {
+ $stateCount[2]++;
+ $i++;
+ }
+ if ($i == $maxI) {
+ return NAN;
+ }
+ while ($i < $maxI && !$image->get($centerJ, $i) && $stateCount[3] < $maxCount) {
+ $stateCount[3]++;
+ $i++;
+ }
+ if ($i == $maxI || $stateCount[3] >= $maxCount) {
+ return NAN;
+ }
+ while ($i < $maxI && $image->get($centerJ, $i) && $stateCount[4] < $maxCount) {
+ $stateCount[4]++;
+ $i++;
+ }
+ if ($stateCount[4] >= $maxCount) {
+ return NAN;
+ }
+
+ // If we found a finder-pattern-like section, but its size is more than 40% different than
+ // the original, assume it's a false positive
+ $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] +
+ $stateCount[4];
+ if (5 * abs($stateCountTotal - $originalStateCountTotal) >= 2 * $originalStateCountTotal) {
+ return NAN;
+ }
+
+ return $this->foundPatternCross($stateCount) ? $this->centerFromEnd($stateCount, $i) : NAN;
+ }
+
+ /**
+ *
Like {@link #crossCheckVertical(int, int, int, int)}, and in fact is basically identical,
+ * except it reads horizontally instead of vertically. This is used to cross-cross
+ * check a vertical cross check and locate the real center of the alignment pattern.
This is called when a horizontal scan finds a possible alignment pattern. It will
+ * cross check with a vertical scan, and if successful, will, ah, cross-cross-check
+ * with another horizontal scan. This is needed primarily to locate the real horizontal
+ * center of the pattern in cases of extreme skew.
+ * And then we cross-cross-cross check with another diagonal scan.
+ *
+ *
If that succeeds the finder pattern location is added to a list that tracks
+ * the number of times each location has been nearly-matched as a finder pattern.
+ * Each additional find is more evidence that the location is in fact a finder
+ * pattern center
+ *
+ * @param $stateCount reading state module counts from horizontal scan
+ * @param i row where finder pattern may be found
+ * @param j end of possible finder pattern in row
+ * @param pureBarcode true if in "pure barcode" mode
+ * @return true if a finder pattern candidate was found this time
+ */
+ protected final function handlePossibleCenter($stateCount, $i, $j, $pureBarcode)
+ {
+ $stateCountTotal = $stateCount[0] + $stateCount[1] + $stateCount[2] + $stateCount[3] +
+ $stateCount[4];
+ $centerJ = $this->centerFromEnd($stateCount, $j);
+ $centerI = $this->crossCheckVertical($i, intval($centerJ), $stateCount[2], $stateCountTotal);
+ if (!is_nan($centerI)) {
+ // Re-cross check
+ $centerJ = $this->crossCheckHorizontal(intval($centerJ), intval($centerI), $stateCount[2], $stateCountTotal);
+ if (!is_nan($centerJ) &&
+ (!$pureBarcode || $this->crossCheckDiagonal(intval($centerI), intval($centerJ), $stateCount[2], $stateCountTotal))
+ ) {
+ $estimatedModuleSize = (float)$stateCountTotal / 7.0;
+ $found = false;
+ for ($index = 0; $index < count($this->possibleCenters); $index++) {
+ $center = $this->possibleCenters[$index];
+ // Look for about the same center and module size:
+ if ($center->aboutEquals($estimatedModuleSize, $centerI, $centerJ)) {
+ $this->possibleCenters[$index] = $center->combineEstimate($centerI, $centerJ, $estimatedModuleSize);
+ $found = true;
+ break;
+ }
+ }
+ if (!$found) {
+ $point = new FinderPattern($centerJ, $centerI, $estimatedModuleSize);
+ $this->possibleCenters[] = $point;
+ if ($this->resultPointCallback != null) {
+ $this->resultPointCallback->foundPossibleResultPoint($point);
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return number of rows we could safely skip during scanning, based on the first
+ * two finder patterns that have been located. In some cases their position will
+ * allow us to infer that the third pattern must lie below a certain point farther
+ * down in the image.
+ */
+ private function findRowSkip()
+ {
+ $max = count($this->possibleCenters);
+ if ($max <= 1) {
+ return 0;
+ }
+ $firstConfirmedCenter = null;
+ foreach ($this->possibleCenters as $center) {
+
+
+ if ($center->getCount() >= self::$CENTER_QUORUM) {
+ if ($firstConfirmedCenter == null) {
+ $firstConfirmedCenter = $center;
+ } else {
+ // We have two confirmed centers
+ // How far down can we skip before resuming looking for the next
+ // pattern? In the worst case, only the difference between the
+ // difference in the x / y coordinates of the two centers.
+ // This is the case where you find top left last.
+ $this->hasSkipped = true;
+ return intval((abs($firstConfirmedCenter->getX() - $center->getX()) -
+ abs($firstConfirmedCenter->getY() - $center->getY())) / 2);
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * @return true iff we have found at least 3 finder patterns that have been detected
+ * at least {@link #CENTER_QUORUM} times each, and, the estimated module size of the
+ * candidates is "pretty similar"
+ */
+ private function haveMultiplyConfirmedCenters()
+ {
+ $confirmedCount = 0;
+ $totalModuleSize = 0.0;
+ $max = count($this->possibleCenters);
+ foreach ($this->possibleCenters as $pattern) {
+ if ($pattern->getCount() >= self::$CENTER_QUORUM) {
+ $confirmedCount++;
+ $totalModuleSize += $pattern->getEstimatedModuleSize();
+ }
+ }
+ if ($confirmedCount < 3) {
+ return false;
+ }
+ // OK, we have at least 3 confirmed centers, but, it's possible that one is a "false positive"
+ // and that we need to keep looking. We detect this by asking if the estimated module sizes
+ // vary too much. We arbitrarily say that when the total deviation from average exceeds
+ // 5% of the total module size estimates, it's too much.
+ $average = $totalModuleSize / (float)$max;
+ $totalDeviation = 0.0;
+ foreach ($this->possibleCenters as $pattern) {
+ $totalDeviation += abs($pattern->getEstimatedModuleSize() - $average);
+ }
+ return $totalDeviation <= 0.05 * $totalModuleSize;
+ }
+
+ /**
+ * @return the 3 best {@link FinderPattern}s from our list of candidates. The "best" are
+ * those that have been detected at least {@link #CENTER_QUORUM} times, and whose module
+ * size differs from the average among those patterns the least
+ * @throws NotFoundException if 3 such finder patterns do not exist
+ */
+ private function selectBestPatterns()
+ {
+ $startSize = count($this->possibleCenters);
+ if ($startSize < 3) {
+ // Couldn't find enough finder patterns
+ throw new NotFoundException;
+ }
+
+ // Filter outlier possibilities whose module size is too different
+ if ($startSize > 3) {
+ // But we can only afford to do so if we have at least 4 possibilities to choose from
+ $totalModuleSize = 0.0;
+ $square = 0.0;
+ foreach ($this->possibleCenters as $center) {
+ $size = $center->getEstimatedModuleSize();
+ $totalModuleSize += $size;
+ $square += $size * $size;
+ }
+ $this->average = $totalModuleSize / (float)$startSize;
+ $stdDev = (float)sqrt($square / $startSize - $this->average * $this->average);
+
+ usort($this->possibleCenters, array($this,'FurthestFromAverageComparator'));
+
+ $limit = max(0.2 * $this->average, $stdDev);
+
+ for ($i = 0; $i < count($this->possibleCenters) && count($this->possibleCenters) > 3; $i++) {
+ $pattern = $this->possibleCenters[$i];
+ if (abs($pattern->getEstimatedModuleSize() - $this->average) > $limit) {
+ unset($this->possibleCenters[$i]);//возможно что ключи меняются в java при вызове .remove(i) ???
+ $this->possibleCenters = array_values($this->possibleCenters);
+ $i--;
+ }
+ }
+ }
+
+ if (count($this->possibleCenters) > 3) {
+ // Throw away all but those first size candidate points we found.
+
+ $totalModuleSize = 0.0;
+ foreach ($this->possibleCenters as $possibleCenter) {
+ $totalModuleSize += $possibleCenter->getEstimatedModuleSize();
+ }
+
+ $this->average = $totalModuleSize / (float)count($this->possibleCenters);
+
+ usort($this->possibleCenters, array($this,'CenterComparator'));
+
+ array_slice($this->possibleCenters, 3, count($this->possibleCenters) - 3);
+
+
+ }
+
+ return array($this->possibleCenters[0], $this->possibleCenters[1], $this->possibleCenters[2]);
+
+
+ }
+ /**
+ *