国内流行的内容管理系统(CMS)多端全媒体解决方案 https://www.dedebiz.com
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

730 lines
24KB

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