qrinput.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726
  1. <?php
  2. /*
  3. * PHP QR Code encoder
  4. *
  5. * Input encoding class
  6. *
  7. * Based on libqrencode C library distributed under LGPL 2.1
  8. * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net>
  9. *
  10. * PHP QR Code is distributed under LGPL 3
  11. * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm>
  12. *
  13. * This library is free software; you can redistribute it and/or
  14. * modify it under the terms of the GNU Lesser General Public
  15. * License as published by the Free Software Foundation; either
  16. * version 3 of the License, or any later version.
  17. *
  18. * This library is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  21. * Lesser General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU Lesser General Public
  24. * License along with this library; if not, write to the Free Software
  25. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  26. */
  27. define('STRUCTURE_HEADER_BITS', 20);
  28. define('MAX_STRUCTURED_SYMBOLS', 16);
  29. class QRinputItem {
  30. public $mode;
  31. public $size;
  32. public $data;
  33. public $bstream;
  34. public function __construct($mode, $size, $data, $bstream = null)
  35. {
  36. $setData = array_slice($data, 0, $size);
  37. if (count($setData) < $size) {
  38. $setData = array_merge($setData, array_fill(0,$size-count($setData),0));
  39. }
  40. if(!QRinput::check($mode, $size, $setData)) {
  41. throw new Exception('Error m:'.$mode.',s:'.$size.',d:'.join(',',$setData));
  42. }
  43. $this->mode = $mode;
  44. $this->size = $size;
  45. $this->data = $setData;
  46. $this->bstream = $bstream;
  47. }
  48. //----------------------------------------------------------------------
  49. public function encodeModeNum($version)
  50. {
  51. try {
  52. $words = (int)($this->size / 3);
  53. $bs = new QRbitstream();
  54. $val = 0x1;
  55. $bs->appendNum(4, $val);
  56. $bs->appendNum(QRspec::lengthIndicator(QR_MODE_NUM, $version), $this->size);
  57. for($i=0; $i<$words; $i++) {
  58. $val = (ord($this->data[$i*3 ]) - ord('0')) * 100;
  59. $val += (ord($this->data[$i*3+1]) - ord('0')) * 10;
  60. $val += (ord($this->data[$i*3+2]) - ord('0'));
  61. $bs->appendNum(10, $val);
  62. }
  63. if($this->size - $words * 3 == 1) {
  64. $val = ord($this->data[$words*3]) - ord('0');
  65. $bs->appendNum(4, $val);
  66. } else if($this->size - $words * 3 == 2) {
  67. $val = (ord($this->data[$words*3 ]) - ord('0')) * 10;
  68. $val += (ord($this->data[$words*3+1]) - ord('0'));
  69. $bs->appendNum(7, $val);
  70. }
  71. $this->bstream = $bs;
  72. return 0;
  73. } catch (Exception $e) {
  74. return -1;
  75. }
  76. }
  77. //----------------------------------------------------------------------
  78. public function encodeModeAn($version)
  79. {
  80. try {
  81. $words = (int)($this->size / 2);
  82. $bs = new QRbitstream();
  83. $bs->appendNum(4, 0x02);
  84. $bs->appendNum(QRspec::lengthIndicator(QR_MODE_AN, $version), $this->size);
  85. for($i=0; $i<$words; $i++) {
  86. $val = (int)QRinput::lookAnTable(ord($this->data[$i*2 ])) * 45;
  87. $val += (int)QRinput::lookAnTable(ord($this->data[$i*2+1]));
  88. $bs->appendNum(11, $val);
  89. }
  90. if($this->size & 1) {
  91. $val = QRinput::lookAnTable(ord($this->data[$words * 2]));
  92. $bs->appendNum(6, $val);
  93. }
  94. $this->bstream = $bs;
  95. return 0;
  96. } catch (Exception $e) {
  97. return -1;
  98. }
  99. }
  100. //----------------------------------------------------------------------
  101. public function encodeMode8($version)
  102. {
  103. try {
  104. $bs = new QRbitstream();
  105. $bs->appendNum(4, 0x4);
  106. $bs->appendNum(QRspec::lengthIndicator(QR_MODE_8, $version), $this->size);
  107. for($i=0; $i<$this->size; $i++) {
  108. $bs->appendNum(8, ord($this->data[$i]));
  109. }
  110. $this->bstream = $bs;
  111. return 0;
  112. } catch (Exception $e) {
  113. return -1;
  114. }
  115. }
  116. //----------------------------------------------------------------------
  117. public function encodeModeKanji($version)
  118. {
  119. try {
  120. $bs = new QRbitrtream();
  121. $bs->appendNum(4, 0x8);
  122. $bs->appendNum(QRspec::lengthIndicator(QR_MODE_KANJI, $version), (int)($this->size / 2));
  123. for($i=0; $i<$this->size; $i+=2) {
  124. $val = (ord($this->data[$i]) << 8) | ord($this->data[$i+1]);
  125. if($val <= 0x9ffc) {
  126. $val -= 0x8140;
  127. } else {
  128. $val -= 0xc140;
  129. }
  130. $h = ($val >> 8) * 0xc0;
  131. $val = ($val & 0xff) + $h;
  132. $bs->appendNum(13, $val);
  133. }
  134. $this->bstream = $bs;
  135. return 0;
  136. } catch (Exception $e) {
  137. return -1;
  138. }
  139. }
  140. //----------------------------------------------------------------------
  141. public function encodeModeStructure()
  142. {
  143. try {
  144. $bs = new QRbitstream();
  145. $bs->appendNum(4, 0x03);
  146. $bs->appendNum(4, ord($this->data[1]) - 1);
  147. $bs->appendNum(4, ord($this->data[0]) - 1);
  148. $bs->appendNum(8, ord($this->data[2]));
  149. $this->bstream = $bs;
  150. return 0;
  151. } catch (Exception $e) {
  152. return -1;
  153. }
  154. }
  155. //----------------------------------------------------------------------
  156. public function estimateBitStreamSizeOfEntry($version)
  157. {
  158. $bits = 0;
  159. if($version == 0)
  160. $version = 1;
  161. switch($this->mode) {
  162. case QR_MODE_NUM: $bits = QRinput::estimateBitsModeNum($this->size); break;
  163. case QR_MODE_AN: $bits = QRinput::estimateBitsModeAn($this->size); break;
  164. case QR_MODE_8: $bits = QRinput::estimateBitsMode8($this->size); break;
  165. case QR_MODE_KANJI: $bits = QRinput::estimateBitsModeKanji($this->size);break;
  166. case QR_MODE_STRUCTURE: return STRUCTURE_HEADER_BITS;
  167. default:
  168. return 0;
  169. }
  170. $l = QRspec::lengthIndicator($this->mode, $version);
  171. $m = 1 << $l;
  172. $num = (int)(($this->size + $m - 1) / $m);
  173. $bits += $num * (4 + $l);
  174. return $bits;
  175. }
  176. //----------------------------------------------------------------------
  177. public function encodeBitStream($version)
  178. {
  179. try {
  180. unset($this->bstream);
  181. $words = QRspec::maximumWords($this->mode, $version);
  182. if($this->size > $words) {
  183. $st1 = new QRinputItem($this->mode, $words, $this->data);
  184. $st2 = new QRinputItem($this->mode, $this->size - $words, array_slice($this->data, $words));
  185. $st1->encodeBitStream($version);
  186. $st2->encodeBitStream($version);
  187. $this->bstream = new QRbitstream();
  188. $this->bstream->append($st1->bstream);
  189. $this->bstream->append($st2->bstream);
  190. unset($st1);
  191. unset($st2);
  192. } else {
  193. $ret = 0;
  194. switch($this->mode) {
  195. case QR_MODE_NUM: $ret = $this->encodeModeNum($version); break;
  196. case QR_MODE_AN: $ret = $this->encodeModeAn($version); break;
  197. case QR_MODE_8: $ret = $this->encodeMode8($version); break;
  198. case QR_MODE_KANJI: $ret = $this->encodeModeKanji($version);break;
  199. case QR_MODE_STRUCTURE: $ret = $this->encodeModeStructure(); break;
  200. default:
  201. break;
  202. }
  203. if($ret < 0)
  204. return -1;
  205. }
  206. return $this->bstream->size();
  207. } catch (Exception $e) {
  208. return -1;
  209. }
  210. }
  211. };
  212. //##########################################################################
  213. class QRinput {
  214. public $items;
  215. private $version;
  216. private $level;
  217. //----------------------------------------------------------------------
  218. public function __construct($version = 0, $level = QR_ECLEVEL_L)
  219. {
  220. if ($version < 0 || $version > QRSPEC_VERSION_MAX || $level > QR_ECLEVEL_H) {
  221. throw new Exception('Invalid version no');
  222. }
  223. $this->version = $version;
  224. $this->level = $level;
  225. }
  226. //----------------------------------------------------------------------
  227. public function getVersion()
  228. {
  229. return $this->version;
  230. }
  231. //----------------------------------------------------------------------
  232. public function setVersion($version)
  233. {
  234. if($version < 0 || $version > QRSPEC_VERSION_MAX) {
  235. throw new Exception('Invalid version no');
  236. return -1;
  237. }
  238. $this->version = $version;
  239. return 0;
  240. }
  241. //----------------------------------------------------------------------
  242. public function getErrorCorrectionLevel()
  243. {
  244. return $this->level;
  245. }
  246. //----------------------------------------------------------------------
  247. public function setErrorCorrectionLevel($level)
  248. {
  249. if($level > QR_ECLEVEL_H) {
  250. throw new Exception('Invalid ECLEVEL');
  251. return -1;
  252. }
  253. $this->level = $level;
  254. return 0;
  255. }
  256. //----------------------------------------------------------------------
  257. public function appendEntry(QRinputItem $entry)
  258. {
  259. $this->items[] = $entry;
  260. }
  261. //----------------------------------------------------------------------
  262. public function append($mode, $size, $data)
  263. {
  264. try {
  265. $entry = new QRinputItem($mode, $size, $data);
  266. $this->items[] = $entry;
  267. return 0;
  268. } catch (Exception $e) {
  269. return -1;
  270. }
  271. }
  272. //----------------------------------------------------------------------
  273. public function insertStructuredAppendHeader($size, $index, $parity)
  274. {
  275. if( $size > MAX_STRUCTURED_SYMBOLS ) {
  276. throw new Exception('insertStructuredAppendHeader wrong size');
  277. }
  278. if( $index <= 0 || $index > MAX_STRUCTURED_SYMBOLS ) {
  279. throw new Exception('insertStructuredAppendHeader wrong index');
  280. }
  281. $buf = array($size, $index, $parity);
  282. try {
  283. $entry = new QRinputItem(QR_MODE_STRUCTURE, 3, buf);
  284. array_unshift($this->items, $entry);
  285. return 0;
  286. } catch (Exception $e) {
  287. return -1;
  288. }
  289. }
  290. //----------------------------------------------------------------------
  291. public function calcParity()
  292. {
  293. $parity = 0;
  294. foreach($this->items as $item) {
  295. if($item->mode != QR_MODE_STRUCTURE) {
  296. for($i=$item->size-1; $i>=0; $i--) {
  297. $parity ^= $item->data[$i];
  298. }
  299. }
  300. }
  301. return $parity;
  302. }
  303. //----------------------------------------------------------------------
  304. public static function checkModeNum($size, $data)
  305. {
  306. for($i=0; $i<$size; $i++) {
  307. if((ord($data[$i]) < ord('0')) || (ord($data[$i]) > ord('9'))){
  308. return false;
  309. }
  310. }
  311. return true;
  312. }
  313. //----------------------------------------------------------------------
  314. public static function estimateBitsModeNum($size)
  315. {
  316. $w = (int)$size / 3;
  317. $bits = $w * 10;
  318. switch($size - $w * 3) {
  319. case 1:
  320. $bits += 4;
  321. break;
  322. case 2:
  323. $bits += 7;
  324. break;
  325. default:
  326. break;
  327. }
  328. return $bits;
  329. }
  330. //----------------------------------------------------------------------
  331. public static $anTable = array(
  332. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  333. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  334. 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43,
  335. 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1,
  336. -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
  337. 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
  338. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  339. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
  340. );
  341. //----------------------------------------------------------------------
  342. public static function lookAnTable($c)
  343. {
  344. return (($c > 127)?-1:self::$anTable[$c]);
  345. }
  346. //----------------------------------------------------------------------
  347. public static function checkModeAn($size, $data)
  348. {
  349. for($i=0; $i<$size; $i++) {
  350. if (self::lookAnTable(ord($data[$i])) == -1) {
  351. return false;
  352. }
  353. }
  354. return true;
  355. }
  356. //----------------------------------------------------------------------
  357. public static function estimateBitsModeAn($size)
  358. {
  359. $w = (int)($size / 2);
  360. $bits = $w * 11;
  361. if($size & 1) {
  362. $bits += 6;
  363. }
  364. return $bits;
  365. }
  366. //----------------------------------------------------------------------
  367. public static function estimateBitsMode8($size)
  368. {
  369. return $size * 8;
  370. }
  371. //----------------------------------------------------------------------
  372. public function estimateBitsModeKanji($size)
  373. {
  374. return (int)(($size / 2) * 13);
  375. }
  376. //----------------------------------------------------------------------
  377. public static function checkModeKanji($size, $data)
  378. {
  379. if($size & 1)
  380. return false;
  381. for($i=0; $i<$size; $i+=2) {
  382. $val = (ord($data[$i]) << 8) | ord($data[$i+1]);
  383. if( $val < 0x8140
  384. || ($val > 0x9ffc && $val < 0xe040)
  385. || $val > 0xebbf) {
  386. return false;
  387. }
  388. }
  389. return true;
  390. }
  391. /***********************************************************************
  392. * Validation
  393. **********************************************************************/
  394. public static function check($mode, $size, $data)
  395. {
  396. if($size <= 0)
  397. return false;
  398. switch($mode) {
  399. case QR_MODE_NUM: return self::checkModeNum($size, $data); break;
  400. case QR_MODE_AN: return self::checkModeAn($size, $data); break;
  401. case QR_MODE_KANJI: return self::checkModeKanji($size, $data); break;
  402. case QR_MODE_8: return true; break;
  403. case QR_MODE_STRUCTURE: return true; break;
  404. default:
  405. break;
  406. }
  407. return false;
  408. }
  409. //----------------------------------------------------------------------
  410. public function estimateBitStreamSize($version)
  411. {
  412. $bits = 0;
  413. foreach($this->items as $item) {
  414. $bits += $item->estimateBitStreamSizeOfEntry($version);
  415. }
  416. return $bits;
  417. }
  418. //----------------------------------------------------------------------
  419. public function estimateVersion()
  420. {
  421. $version = 0;
  422. $prev = 0;
  423. do {
  424. $prev = $version;
  425. $bits = $this->estimateBitStreamSize($prev);
  426. $version = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
  427. if ($version < 0) {
  428. return -1;
  429. }
  430. } while ($version > $prev);
  431. return $version;
  432. }
  433. //----------------------------------------------------------------------
  434. public static function lengthOfCode($mode, $version, $bits)
  435. {
  436. $payload = $bits - 4 - QRspec::lengthIndicator($mode, $version);
  437. switch($mode) {
  438. case QR_MODE_NUM:
  439. $chunks = (int)($payload / 10);
  440. $remain = $payload - $chunks * 10;
  441. $size = $chunks * 3;
  442. if($remain >= 7) {
  443. $size += 2;
  444. } else if($remain >= 4) {
  445. $size += 1;
  446. }
  447. break;
  448. case QR_MODE_AN:
  449. $chunks = (int)($payload / 11);
  450. $remain = $payload - $chunks * 11;
  451. $size = $chunks * 2;
  452. if($remain >= 6)
  453. $size++;
  454. break;
  455. case QR_MODE_8:
  456. $size = (int)($payload / 8);
  457. break;
  458. case QR_MODE_KANJI:
  459. $size = (int)(($payload / 13) * 2);
  460. break;
  461. case QR_MODE_STRUCTURE:
  462. $size = (int)($payload / 8);
  463. break;
  464. default:
  465. $size = 0;
  466. break;
  467. }
  468. $maxsize = QRspec::maximumWords($mode, $version);
  469. if($size < 0) $size = 0;
  470. if($size > $maxsize) $size = $maxsize;
  471. return $size;
  472. }
  473. //----------------------------------------------------------------------
  474. public function createBitStream()
  475. {
  476. $total = 0;
  477. foreach($this->items as $item) {
  478. $bits = $item->encodeBitStream($this->version);
  479. if($bits < 0)
  480. return -1;
  481. $total += $bits;
  482. }
  483. return $total;
  484. }
  485. //----------------------------------------------------------------------
  486. public function convertData()
  487. {
  488. $ver = $this->estimateVersion();
  489. if($ver > $this->getVersion()) {
  490. $this->setVersion($ver);
  491. }
  492. for(;;) {
  493. $bits = $this->createBitStream();
  494. if($bits < 0)
  495. return -1;
  496. $ver = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level);
  497. if($ver < 0) {
  498. throw new Exception('WRONG VERSION');
  499. } else if($ver > $this->getVersion()) {
  500. $this->setVersion($ver);
  501. } else {
  502. break;
  503. }
  504. }
  505. return 0;
  506. }
  507. //----------------------------------------------------------------------
  508. public function appendPaddingBit(&$bstream)
  509. {
  510. $bits = $bstream->size();
  511. $maxwords = QRspec::getDataLength($this->version, $this->level);
  512. $maxbits = $maxwords * 8;
  513. if ($maxbits == $bits) {
  514. return 0;
  515. }
  516. if ($maxbits - $bits < 5) {
  517. return $bstream->appendNum($maxbits - $bits, 0);
  518. }
  519. $bits += 4;
  520. $words = (int)(($bits + 7) / 8);
  521. $padding = new QRbitstream();
  522. $ret = $padding->appendNum($words * 8 - $bits + 4, 0);
  523. if($ret < 0)
  524. return $ret;
  525. $padlen = $maxwords - $words;
  526. if($padlen > 0) {
  527. $padbuf = array();
  528. for($i=0; $i<$padlen; $i++) {
  529. $padbuf[$i] = ($i&1)?0x11:0xec;
  530. }
  531. $ret = $padding->appendBytes($padlen, $padbuf);
  532. if($ret < 0)
  533. return $ret;
  534. }
  535. $ret = $bstream->append($padding);
  536. return $ret;
  537. }
  538. //----------------------------------------------------------------------
  539. public function mergeBitStream()
  540. {
  541. if($this->convertData() < 0) {
  542. return null;
  543. }
  544. $bstream = new QRbitstream();
  545. foreach($this->items as $item) {
  546. $ret = $bstream->append($item->bstream);
  547. if($ret < 0) {
  548. return null;
  549. }
  550. }
  551. return $bstream;
  552. }
  553. //----------------------------------------------------------------------
  554. public function getBitStream()
  555. {
  556. $bstream = $this->mergeBitStream();
  557. if($bstream == null) {
  558. return null;
  559. }
  560. $ret = $this->appendPaddingBit($bstream);
  561. if($ret < 0) {
  562. return null;
  563. }
  564. return $bstream;
  565. }
  566. //----------------------------------------------------------------------
  567. public function getByteStream()
  568. {
  569. $bstream = $this->getBitStream();
  570. if($bstream == null) {
  571. return null;
  572. }
  573. return $bstream->toByte();
  574. }
  575. }