mode = $mode; $this->size = $size; $this->data = $setData; $this->bstream = $bstream; } public function encodeModeNum($version) { try { $words = (int) ($this->size / 3); $bs = new QRbitstream(); $val = 1; $bs->appendNum(4, $val); $bs->appendNum(QRspec::lengthIndicator(QR_MODE_NUM, $version), $this->size); for ($i = 0; $i < $words; $i++) { $val = (ord($this->data[$i * 3]) - ord('0')) * 100; $val += (ord($this->data[$i * 3 + 1]) - ord('0')) * 10; $val += ord($this->data[$i * 3 + 2]) - ord('0'); $bs->appendNum(10, $val); } if ($this->size - $words * 3 == 1) { $val = ord($this->data[$words * 3]) - ord('0'); $bs->appendNum(4, $val); } else if ($this->size - $words * 3 == 2) { $val = (ord($this->data[$words * 3]) - ord('0')) * 10; $val += ord($this->data[$words * 3 + 1]) - ord('0'); $bs->appendNum(7, $val); } $this->bstream = $bs; return 0; } catch (Exception $e) { return -1; } } public function encodeModeAn($version) { try { $words = (int) ($this->size / 2); $bs = new QRbitstream(); $bs->appendNum(4, 2); $bs->appendNum(QRspec::lengthIndicator(QR_MODE_AN, $version), $this->size); for ($i = 0; $i < $words; $i++) { $val = (int) QRinput::lookAnTable(ord($this->data[$i * 2])) * 45; $val += (int) QRinput::lookAnTable(ord($this->data[$i * 2 + 1])); $bs->appendNum(11, $val); } if ($this->size & 1) { $val = QRinput::lookAnTable(ord($this->data[$words * 2])); $bs->appendNum(6, $val); } $this->bstream = $bs; return 0; } catch (Exception $e) { return -1; } } public function encodeMode8($version) { try { $bs = new QRbitstream(); $bs->appendNum(4, 4); $bs->appendNum(QRspec::lengthIndicator(QR_MODE_8, $version), $this->size); for ($i = 0; $i < $this->size; $i++) { $bs->appendNum(8, ord($this->data[$i])); } $this->bstream = $bs; return 0; } catch (Exception $e) { return -1; } } public function encodeModeKanji($version) { try { $bs = new QRbitrtream(); $bs->appendNum(4, 8); $bs->appendNum(QRspec::lengthIndicator(QR_MODE_KANJI, $version), (int) ($this->size / 2)); for ($i = 0; $i < $this->size; $i += 2) { $val = ord($this->data[$i]) << 8 | ord($this->data[$i + 1]); if ($val <= 40956) { $val -= 33088; } else { $val -= 49472; } $h = ($val >> 8) * 192; $val = ($val & 255) + $h; $bs->appendNum(13, $val); } $this->bstream = $bs; return 0; } catch (Exception $e) { return -1; } } public function encodeModeStructure() { try { $bs = new QRbitstream(); $bs->appendNum(4, 3); $bs->appendNum(4, ord($this->data[1]) - 1); $bs->appendNum(4, ord($this->data[0]) - 1); $bs->appendNum(8, ord($this->data[2])); $this->bstream = $bs; return 0; } catch (Exception $e) { return -1; } } public function estimateBitStreamSizeOfEntry($version) { $bits = 0; if ($version == 0) { $version = 1; } switch ($this->mode) { case QR_MODE_NUM: $bits = QRinput::estimateBitsModeNum($this->size); break; case QR_MODE_AN: $bits = QRinput::estimateBitsModeAn($this->size); break; case QR_MODE_8: $bits = QRinput::estimateBitsMode8($this->size); break; case QR_MODE_KANJI: $bits = QRinput::estimateBitsModeKanji($this->size); break; case QR_MODE_STRUCTURE: return STRUCTURE_HEADER_BITS; default: return 0; } $l = QRspec::lengthIndicator($this->mode, $version); $m = 1 << $l; $num = (int) (($this->size + $m - 1) / $m); $bits += $num * (4 + $l); return $bits; } public function encodeBitStream($version) { try { unset($this->bstream); $words = QRspec::maximumWords($this->mode, $version); if ($words < $this->size) { $st1 = new QRinputItem($this->mode, $words, $this->data); $st2 = new QRinputItem($this->mode, $this->size - $words, array_slice($this->data, $words)); $st1->encodeBitStream($version); $st2->encodeBitStream($version); $this->bstream = new QRbitstream(); $this->bstream->append($st1->bstream); $this->bstream->append($st2->bstream); unset($st1); unset($st2); } else { $ret = 0; switch ($this->mode) { case QR_MODE_NUM: $ret = $this->encodeModeNum($version); break; case QR_MODE_AN: $ret = $this->encodeModeAn($version); break; case QR_MODE_8: $ret = $this->encodeMode8($version); break; case QR_MODE_KANJI: $ret = $this->encodeModeKanji($version); break; case QR_MODE_STRUCTURE: $ret = $this->encodeModeStructure(); break; default: break; } if ($ret < 0) { return -1; } } return $this->bstream->size(); } catch (Exception $e) { return -1; } } } class QRinput { public $items; private $version; private $level; static public $anTable = array(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1); public function __construct($version = 0, $level = QR_ECLEVEL_L) { if ($version < 0 || QRSPEC_VERSION_MAX < $version || QR_ECLEVEL_H < $level) { throw new Exception('Invalid version no'); return NULL; } $this->version = $version; $this->level = $level; } public function getVersion() { return $this->version; } public function setVersion($version) { if ($version < 0 || QRSPEC_VERSION_MAX < $version) { throw new Exception('Invalid version no'); return -1; } $this->version = $version; return 0; } public function getErrorCorrectionLevel() { return $this->level; } public function setErrorCorrectionLevel($level) { if (QR_ECLEVEL_H < $level) { throw new Exception('Invalid ECLEVEL'); return -1; } $this->level = $level; return 0; } public function appendEntry(QRinputItem $entry) { $this->items[] = $entry; } public function append($mode, $size, $data) { try { $entry = new QRinputItem($mode, $size, $data); $this->items[] = $entry; return 0; } catch (Exception $e) { return -1; } } public function insertStructuredAppendHeader($size, $index, $parity) { if (MAX_STRUCTURED_SYMBOLS < $size) { throw new Exception('insertStructuredAppendHeader wrong size'); } if ($index <= 0 || MAX_STRUCTURED_SYMBOLS < $index) { throw new Exception('insertStructuredAppendHeader wrong index'); } $buf = array($size, $index, $parity); try { $entry = new QRinputItem(QR_MODE_STRUCTURE, 3, buf); array_unshift($this->items, $entry); return 0; } catch (Exception $e) { return -1; } } public function calcParity() { $parity = 0; foreach ($this->items as $item) { if ($item->mode != QR_MODE_STRUCTURE) { for ($i = $item->size - 1; 0 <= $i; $i--) { $parity ^= $item->data[$i]; } } } return $parity; } static public function checkModeNum($size, $data) { for ($i = 0; $i < $size; $i++) { if (ord($data[$i]) < ord('0') || ord('9') < ord($data[$i])) { return false; } } return true; } static public function estimateBitsModeNum($size) { $w = (int) $size / 3; $bits = $w * 10; switch ($size - $w * 3) { case 1: $bits += 4; break; case 2: $bits += 7; break; default: break; } return $bits; } static public function lookAnTable($c) { return 127 < $c ? -1 : self::$anTable[$c]; } static public function checkModeAn($size, $data) { for ($i = 0; $i < $size; $i++) { if (self::lookAnTable(ord($data[$i])) == -1) { return false; } } return true; } static public function estimateBitsModeAn($size) { $w = (int) ($size / 2); $bits = $w * 11; if ($size & 1) { $bits += 6; } return $bits; } static public function estimateBitsMode8($size) { return $size * 8; } public function estimateBitsModeKanji($size) { return (int) ($size / 2 * 13); } static public function checkModeKanji($size, $data) { if ($size & 1) { return false; } for ($i = 0; $i < $size; $i += 2) { $val = ord($data[$i]) << 8 | ord($data[$i + 1]); if ($val < 33088 || 40956 < $val && $val < 57408 || 60351 < $val) { return false; } } return true; } static public function check($mode, $size, $data) { if ($size <= 0) { return false; } switch ($mode) { case QR_MODE_NUM: return self::checkModeNum($size, $data); break; case QR_MODE_AN: return self::checkModeAn($size, $data); break; case QR_MODE_KANJI: return self::checkModeKanji($size, $data); break; case QR_MODE_8: return true; break; case QR_MODE_STRUCTURE: return true; break; default: break; } return false; } public function estimateBitStreamSize($version) { $bits = 0; foreach ($this->items as $item) { $bits += $item->estimateBitStreamSizeOfEntry($version); } return $bits; } public function estimateVersion() { $version = 0; $prev = 0; do { $prev = $version; $bits = $this->estimateBitStreamSize($prev); $version = QRspec::getMinimumVersion((int) (($bits + 7) / 8), $this->level); if ($version < 0) { return -1; } } while ($prev < $version); return $version; } static public function lengthOfCode($mode, $version, $bits) { $payload = $bits - 4 - QRspec::lengthIndicator($mode, $version); switch ($mode) { case QR_MODE_NUM: $chunks = (int) ($payload / 10); $remain = $payload - $chunks * 10; $size = $chunks * 3; if (7 <= $remain) { $size += 2; } else if (4 <= $remain) { $size += 1; } break; case QR_MODE_AN: $chunks = (int) ($payload / 11); $remain = $payload - $chunks * 11; $size = $chunks * 2; if (6 <= $remain) { $size++; } break; case QR_MODE_8: $size = (int) ($payload / 8); break; case QR_MODE_KANJI: $size = (int) ($payload / 13 * 2); break; case QR_MODE_STRUCTURE: $size = (int) ($payload / 8); break; default: $size = 0; break; } $maxsize = QRspec::maximumWords($mode, $version); if ($size < 0) { $size = 0; } if ($maxsize < $size) { $size = $maxsize; } return $size; } public function createBitStream() { $total = 0; foreach ($this->items as $item) { $bits = $item->encodeBitStream($this->version); if ($bits < 0) { return -1; } $total += $bits; } return $total; } public function convertData() { $ver = $this->estimateVersion(); if ($this->getVersion() < $ver) { $this->setVersion($ver); } for (; ; ) { $bits = $this->createBitStream(); if ($bits < 0) { return -1; } $ver = QRspec::getMinimumVersion((int) (($bits + 7) / 8), $this->level); if ($ver < 0) { throw new Exception('WRONG VERSION'); return -1; } else if ($this->getVersion() < $ver) { $this->setVersion($ver); } else { break; } } return 0; } public function appendPaddingBit(&$bstream) { $bits = $bstream->size(); $maxwords = QRspec::getDataLength($this->version, $this->level); $maxbits = $maxwords * 8; if ($maxbits == $bits) { return 0; } if ($maxbits - $bits < 5) { return $bstream->appendNum($maxbits - $bits, 0); } $bits += 4; $words = (int) (($bits + 7) / 8); $padding = new QRbitstream(); $ret = $padding->appendNum($words * 8 - $bits + 4, 0); if ($ret < 0) { return $ret; } $padlen = $maxwords - $words; if (0 < $padlen) { $padbuf = array(); for ($i = 0; $i < $padlen; $i++) { $padbuf[$i] = $i & 1 ? 17 : 236; } $ret = $padding->appendBytes($padlen, $padbuf); if ($ret < 0) { return $ret; } } $ret = $bstream->append($padding); return $ret; } public function mergeBitStream() { if ($this->convertData() < 0) { return NULL; } $bstream = new QRbitstream(); foreach ($this->items as $item) { $ret = $bstream->append($item->bstream); if ($ret < 0) { return NULL; } } return $bstream; } public function getBitStream() { $bstream = $this->mergeBitStream(); if ($bstream == NULL) { return NULL; } $ret = $this->appendPaddingBit($bstream); if ($ret < 0) { return NULL; } return $bstream; } public function getByteStream() { $bstream = $this->getBitStream(); if ($bstream == NULL) { return NULL; } return $bstream->toByte(); } } define('STRUCTURE_HEADER_BITS', 20); define('MAX_STRUCTURED_SYMBOLS', 16); ?>