qrinput.php 24 KB

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