SproutCMS

This is the code documentation for the SproutCMS project

source of /sprout/Helpers/phpqrcode/qrinput.php

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