QRRSBlock.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. <?php
  2. /**
  3. * This file is part of the phpQr package
  4. *
  5. * See @see QRCode class for description of package and license.
  6. */
  7. /**
  8. * Import necessary dependencies
  9. */
  10. require_once 'QRCodeException.php';
  11. require_once 'QRErrorCorrectLevel.php';
  12. /**
  13. * Derived exception class
  14. *
  15. * @author Maik Greubel <[email protected]>
  16. * @package phpQr
  17. */
  18. class QRRSBlockException extends QRCodeException
  19. {
  20. }
  21. /**
  22. * This class is a Reed-Solomon implementation for the QRCode.
  23. * The purpose is to provide error correction and block information.
  24. *
  25. * Inspired by qrcode.js from https://github.com/jeromeetienne/jquery-qrcode
  26. *
  27. * @author Maik Greubel <[email protected]>
  28. * @package phpQr
  29. * @link http://www.thonky.com/qr-code-tutorial/error-correction-table/
  30. */
  31. class QRRSBlock
  32. {
  33. /**
  34. * The total count of blocks
  35. *
  36. * @var int The total count of blocks
  37. */
  38. private $totalCount;
  39. /**
  40. * The data count of blocks
  41. *
  42. * @var int The data count of blocks
  43. */
  44. private $dataCount;
  45. /**
  46. * The block table
  47. * @var array The block table
  48. */
  49. private $RS_BLOCK_TABLE;
  50. /**
  51. * Singleton pattern
  52. *
  53. * @var QRRSBlock Singleton
  54. */
  55. private static $instance;
  56. /**
  57. * The serialized block data for faster initialization
  58. *
  59. * @var string
  60. */
  61. private $blockFileName = 'rsblock.dat';
  62. /**
  63. * Singleton pattern
  64. *
  65. * @return QRRSBlock
  66. */
  67. public static function getInstance()
  68. {
  69. if(!self::$instance)
  70. {
  71. self::$instance = new self(0, 0);
  72. }
  73. return self::$instance;
  74. }
  75. /**
  76. * Retrieve the data count
  77. *
  78. * @return int The data count
  79. */
  80. public function getDataCount()
  81. {
  82. return $this->dataCount;
  83. }
  84. /**
  85. * Retrieve the total count
  86. *
  87. * @return int The total count
  88. */
  89. public function getTotalCount()
  90. {
  91. return $this->totalCount;
  92. }
  93. /**
  94. * Create a new QR Reed-Solomon block instance
  95. *
  96. * @param int $totalCount The total count of blocks
  97. * @param int $dataCount The data count of blocks
  98. */
  99. private function __construct($totalCount, $dataCount)
  100. {
  101. $this->initRsBlock();
  102. $this->totalCount = $totalCount;
  103. $this->dataCount = $dataCount;
  104. }
  105. /**
  106. * Get rs blocks of particular type and error correction level
  107. *
  108. * @param int $typeNumber
  109. * @param int $errorCorrectLevel
  110. * @throws QRRSBlockException
  111. * @return QRRSBlock
  112. */
  113. public function getRSBlocks($typeNumber, $errorCorrectLevel)
  114. {
  115. $rsBlock = $this->getRsBlockTable($typeNumber, $errorCorrectLevel);
  116. if(!$rsBlock)
  117. {
  118. throw new QRRSBlockException("Bad RS Block at type number " . $typeNumber . " / error correct level " . $errorCorrectLevel);
  119. }
  120. $length = sizeof($rsBlock) / 3;
  121. $list = array();
  122. for($i = 0; $i < $length; $i++)
  123. {
  124. $count = $rsBlock[$i * 3 + 0];
  125. $totalCount = $rsBlock[$i * 3 + 1];
  126. $dataCount = $rsBlock[$i * 3 + 2];
  127. for($j = 0; $j < $count; $j++)
  128. {
  129. array_push($list, new QRRSBlock($totalCount, $dataCount));
  130. }
  131. }
  132. return $list;
  133. }
  134. /**
  135. * Get the reed-solomon block table
  136. *
  137. * @param int $typeNumber
  138. * @param int $errorCorrectLevel
  139. * @return int|NULL
  140. */
  141. public function getRsBlockTable($typeNumber, $errorCorrectLevel)
  142. {
  143. switch ($errorCorrectLevel)
  144. {
  145. case QRErrorCorrectLevel::L:
  146. return $this->RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 0];
  147. case QRErrorCorrectLevel::M:
  148. return $this->RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 1];
  149. case QRErrorCorrectLevel::Q:
  150. return $this->RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 2];
  151. case QRErrorCorrectLevel::H:
  152. return $this->RS_BLOCK_TABLE[($typeNumber - 1) * 4 + 3];
  153. default:
  154. return null;
  155. }
  156. }
  157. /**
  158. * This method initialize the RS block
  159. */
  160. private function initRsBlock()
  161. {
  162. if($this->loadBlockFile())
  163. {
  164. return;
  165. }
  166. $this->RS_BLOCK_TABLE = array();
  167. // L
  168. // M
  169. // Q
  170. // H
  171. // 1
  172. $this->addRsBlock(array(1, 26, 19));
  173. $this->addRsBlock(array(1, 26, 16));
  174. $this->addRsBlock(array(1, 26, 13));
  175. $this->addRsBlock(array(1, 26, 9));
  176. // 2
  177. $this->addRsBlock(array(1, 44, 34));
  178. $this->addRsBlock(array(1, 44, 28));
  179. $this->addRsBlock(array(1, 44, 22));
  180. $this->addRsBlock(array(1, 44, 16));
  181. // 3
  182. $this->addRsBlock(array(1, 70, 55));
  183. $this->addRsBlock(array(1, 70, 44));
  184. $this->addRsBlock(array(2, 35, 17));
  185. $this->addRsBlock(array(2, 35, 13));
  186. // 4
  187. $this->addRsBlock(array(1, 100, 80));
  188. $this->addRsBlock(array(2, 50, 32));
  189. $this->addRsBlock(array(2, 50, 24));
  190. $this->addRsBlock(array(4, 25, 9));
  191. // 5
  192. $this->addRsBlock(array(1, 134, 108));
  193. $this->addRsBlock(array(2, 67, 43));
  194. $this->addRsBlock(array(2, 33, 15, 2, 34, 16));
  195. $this->addRsBlock(array(2, 33, 11, 2, 34, 12));
  196. // 6
  197. $this->addRsBlock(array(2, 86, 68));
  198. $this->addRsBlock(array(4, 43, 27));
  199. $this->addRsBlock(array(4, 43, 19));
  200. $this->addRsBlock(array(4, 43, 15));
  201. // 7
  202. $this->addRsBlock(array(2, 98, 78));
  203. $this->addRsBlock(array(4, 49, 31));
  204. $this->addRsBlock(array(2, 32, 14, 4, 33, 15));
  205. $this->addRsBlock(array(4, 39, 13, 1, 40, 14));
  206. // 8
  207. $this->addRsBlock(array(2, 121, 97));
  208. $this->addRsBlock(array(2, 60, 38, 2, 61, 39));
  209. $this->addRsBlock(array(4, 40, 18, 2, 41, 19));
  210. $this->addRsBlock(array(4, 40, 14, 2, 41, 15));
  211. // 9
  212. $this->addRsBlock(array(2, 146, 116));
  213. $this->addRsBlock(array(3, 58, 36, 2, 59, 37));
  214. $this->addRsBlock(array(4, 36, 16, 4, 37, 17));
  215. $this->addRsBlock(array(4, 36, 12, 4, 37, 13));
  216. // 10
  217. $this->addRsBlock(array(2, 86, 68, 2, 87, 69));
  218. $this->addRsBlock(array(4, 69, 43, 1, 70, 44));
  219. $this->addRsBlock(array(6, 43, 19, 2, 44, 20));
  220. $this->addRsBlock(array(6, 43, 15, 2, 44, 16));
  221. // 11
  222. $this->addRsBlock(array(4, 101, 81));
  223. $this->addRsBlock(array(1, 80, 50, 4, 81, 51));
  224. $this->addRsBlock(array(4, 50, 22, 4, 51, 23));
  225. $this->addRsBlock(array(3, 36, 12, 8, 37, 13));
  226. // 12
  227. $this->addRsBlock(array(2, 116, 92, 2, 117, 93));
  228. $this->addRsBlock(array(6, 58, 36, 2, 59, 37));
  229. $this->addRsBlock(array(4, 46, 20, 6, 47, 21));
  230. $this->addRsBlock(array(7, 42, 14, 4, 43, 15));
  231. // 13
  232. $this->addRsBlock(array(4, 133, 107));
  233. $this->addRsBlock(array(8, 59, 37, 1, 60, 38));
  234. $this->addRsBlock(array(8, 44, 20, 4, 45, 21));
  235. $this->addRsBlock(array(12, 33, 11, 4, 34, 12));
  236. // 14
  237. $this->addRsBlock(array(3, 145, 115, 1, 146, 116));
  238. $this->addRsBlock(array(4, 64, 40, 5, 65, 41));
  239. $this->addRsBlock(array(11, 36, 16, 5, 37, 17));
  240. $this->addRsBlock(array(11, 36, 12, 5, 37, 13));
  241. // 15
  242. $this->addRsBlock(array(5, 109, 87, 1, 110, 88));
  243. $this->addRsBlock(array(5, 65, 41, 5, 66, 42));
  244. $this->addRsBlock(array(5, 54, 24, 7, 55, 25));
  245. $this->addRsBlock(array(11, 36, 12));
  246. // 16
  247. $this->addRsBlock(array(5, 122, 98, 1, 123, 99));
  248. $this->addRsBlock(array(7, 73, 45, 3, 74, 46));
  249. $this->addRsBlock(array(15, 43, 19, 2, 44, 20));
  250. $this->addRsBlock(array(3, 45, 15, 13, 46, 16));
  251. // 17
  252. $this->addRsBlock(array(1, 135, 107, 5, 136, 108));
  253. $this->addRsBlock(array(10, 74, 46, 1, 75, 47));
  254. $this->addRsBlock(array(1, 50, 22, 15, 51, 23));
  255. $this->addRsBlock(array(2, 42, 14, 17, 43, 15));
  256. // 18
  257. $this->addRsBlock(array(5, 150, 120, 1, 151, 121));
  258. $this->addRsBlock(array(9, 69, 43, 4, 70, 44));
  259. $this->addRsBlock(array(17, 50, 22, 1, 51, 23));
  260. $this->addRsBlock(array(2, 42, 14, 19, 43, 15));
  261. // 19
  262. $this->addRsBlock(array(3, 141, 113, 4, 142, 114));
  263. $this->addRsBlock(array(3, 70, 44, 11, 71, 45));
  264. $this->addRsBlock(array(17, 47, 21, 4, 48, 22));
  265. $this->addRsBlock(array(9, 39, 13, 16, 40, 14));
  266. // 20
  267. $this->addRsBlock(array(3, 135, 107, 5, 136, 108));
  268. $this->addRsBlock(array(3, 67, 41, 13, 68, 42));
  269. $this->addRsBlock(array(15, 54, 24, 5, 55, 25));
  270. $this->addRsBlock(array(15, 43, 15, 10, 44, 16));
  271. // 21
  272. $this->addRsBlock(array(4, 144, 116, 4, 145, 117));
  273. $this->addRsBlock(array(17, 68, 42));
  274. $this->addRsBlock(array(17, 50, 22, 6, 51, 23));
  275. $this->addRsBlock(array(19, 46, 16, 6, 47, 17));
  276. // 22
  277. $this->addRsBlock(array(2, 139, 111, 7, 140, 112));
  278. $this->addRsBlock(array(17, 74, 46));
  279. $this->addRsBlock(array(7, 54, 24, 16, 55, 25));
  280. $this->addRsBlock(array(34, 37, 13));
  281. // 23
  282. $this->addRsBlock(array(4, 151, 121, 5, 152, 122));
  283. $this->addRsBlock(array(4, 75, 47, 14, 76, 48));
  284. $this->addRsBlock(array(11, 54, 24, 14, 55, 25));
  285. $this->addRsBlock(array(16, 45, 15, 14, 46, 16));
  286. // 24
  287. $this->addRsBlock(array(6, 147, 117, 4, 148, 118));
  288. $this->addRsBlock(array(6, 73, 45, 14, 74, 46));
  289. $this->addRsBlock(array(11, 54, 24, 16, 55, 25));
  290. $this->addRsBlock(array(30, 46, 16, 2, 47, 17));
  291. // 25
  292. $this->addRsBlock(array(8, 132, 106, 4, 133, 107));
  293. $this->addRsBlock(array(8, 75, 47, 13, 76, 48));
  294. $this->addRsBlock(array(7, 54, 24, 22, 55, 25));
  295. $this->addRsBlock(array(22, 45, 15, 13, 46, 16));
  296. // 26
  297. $this->addRsBlock(array(10, 142, 114, 2, 143, 115));
  298. $this->addRsBlock(array(19, 74, 46, 4, 75, 47));
  299. $this->addRsBlock(array(28, 50, 22, 6, 51, 23));
  300. $this->addRsBlock(array(33, 46, 16, 4, 47, 17));
  301. // 27
  302. $this->addRsBlock(array(8, 152, 122, 4, 153, 123));
  303. $this->addRsBlock(array(22, 73, 45, 3, 74, 46));
  304. $this->addRsBlock(array(8, 53, 23, 26, 54, 24));
  305. $this->addRsBlock(array(12, 45, 15, 28, 46, 16));
  306. // 28
  307. $this->addRsBlock(array(3, 147, 117, 10, 148, 118));
  308. $this->addRsBlock(array(3, 73, 45, 23, 74, 46));
  309. $this->addRsBlock(array(4, 54, 24, 31, 55, 25));
  310. $this->addRsBlock(array(11, 45, 15, 31, 46, 16));
  311. // 29
  312. $this->addRsBlock(array(7, 146, 116, 7, 147, 117));
  313. $this->addRsBlock(array(21, 73, 45, 7, 74, 46));
  314. $this->addRsBlock(array(1, 53, 23, 37, 54, 24));
  315. $this->addRsBlock(array(19, 45, 15, 26, 46, 16));
  316. // 30
  317. $this->addRsBlock(array(5, 145, 115, 10, 146, 116));
  318. $this->addRsBlock(array(19, 75, 47, 10, 76, 48));
  319. $this->addRsBlock(array(15, 54, 24, 25, 55, 25));
  320. $this->addRsBlock(array(23, 45, 15, 25, 46, 16));
  321. // 31
  322. $this->addRsBlock(array(13, 145, 115, 3, 146, 116));
  323. $this->addRsBlock(array(2, 74, 46, 29, 75, 47));
  324. $this->addRsBlock(array(42, 54, 24, 1, 55, 25));
  325. $this->addRsBlock(array(23, 45, 15, 28, 46, 16));
  326. // 32
  327. $this->addRsBlock(array(17, 145, 115));
  328. $this->addRsBlock(array(10, 74, 46, 23, 75, 47));
  329. $this->addRsBlock(array(42, 54, 24, 1, 55, 25));
  330. $this->addRsBlock(array(23, 45, 15, 28, 46, 16));
  331. // 33
  332. $this->addRsBlock(array(17, 145, 115, 1, 146, 116));
  333. $this->addRsBlock(array(14, 74, 46, 21, 75, 47));
  334. $this->addRsBlock(array(29, 54, 24, 19, 55, 25));
  335. $this->addRsBlock(array(11, 45, 15, 46, 46, 16));
  336. // 34
  337. $this->addRsBlock(array(13, 145, 115, 6, 146, 116));
  338. $this->addRsBlock(array(14, 74, 46, 21, 75, 47));
  339. $this->addRsBlock(array(44, 54, 24, 7, 55, 25));
  340. $this->addRsBlock(array(59, 46, 16, 1, 47, 17));
  341. // 35
  342. $this->addRsBlock(array(12, 151, 121, 7, 152, 122));
  343. $this->addRsBlock(array(12, 75, 47, 26, 76, 48));
  344. $this->addRsBlock(array(39, 54, 24, 14, 55, 25));
  345. $this->addRsBlock(array(22, 45, 15, 41, 46, 16));
  346. // 36
  347. $this->addRsBlock(array(6, 151, 121, 14, 152, 122));
  348. $this->addRsBlock(array(6, 75, 47, 34, 76, 48));
  349. $this->addRsBlock(array(46, 54, 24, 10, 55, 25));
  350. $this->addRsBlock(array(2, 45, 15, 64, 46, 16));
  351. // 37
  352. $this->addRsBlock(array(17, 152, 122, 4, 153, 123));
  353. $this->addRsBlock(array(29, 74, 46, 14, 75, 47));
  354. $this->addRsBlock(array(49, 54, 24, 10, 55, 25));
  355. $this->addRsBlock(array(24, 45, 15, 46, 46, 16));
  356. // 38
  357. $this->addRsBlock(array(4, 152, 122, 18, 153, 123));
  358. $this->addRsBlock(array(13, 74, 46, 32, 75, 47));
  359. $this->addRsBlock(array(48, 54, 24, 14, 55, 25));
  360. $this->addRsBlock(array(42, 45, 15, 32, 46, 16));
  361. // 39
  362. $this->addRsBlock(array(20, 147, 117, 4, 148, 118));
  363. $this->addRsBlock(array(40, 75, 47, 7, 76, 48));
  364. $this->addRsBlock(array(43, 54, 24, 22, 55, 25));
  365. $this->addRsBlock(array(10, 45, 15, 67, 46, 16));
  366. // 40
  367. $this->addRsBlock(array(19, 148, 118, 6, 149, 119));
  368. $this->addRsBlock(array(18, 75, 47, 31, 76, 48));
  369. $this->addRsBlock(array(34, 54, 24, 34, 55, 25));
  370. $this->addRsBlock(array(20, 45, 15, 61, 46, 16));
  371. $this->saveBlockFile();
  372. }
  373. /**
  374. * Add a new block information to the block
  375. *
  376. * @param array $block
  377. */
  378. private function addRsBlock($block)
  379. {
  380. array_push($this->RS_BLOCK_TABLE, $block);
  381. }
  382. /**
  383. * Return the absolute path to the block file
  384. * @return string
  385. */
  386. private function getBlockFileAbsolute()
  387. {
  388. return sprintf("%s%s%s", dirname(__FILE__), DIRECTORY_SEPARATOR, $this->blockFileName);
  389. }
  390. /**
  391. * Try to load the block file
  392. *
  393. * @return boolean
  394. */
  395. private function loadBlockFile()
  396. {
  397. $file = $this->getBlockFileAbsolute();
  398. if(!file_exists($file))
  399. {
  400. return false;
  401. }
  402. $serialized = file_get_contents($file);
  403. if(!$serialized)
  404. {
  405. return false;
  406. }
  407. $this->RS_BLOCK_TABLE = unserialize($serialized);
  408. if(!$this->RS_BLOCK_TABLE)
  409. {
  410. return false;
  411. }
  412. return true;
  413. }
  414. /**
  415. * Try to save the block file
  416. */
  417. private function saveBlockFile()
  418. {
  419. $file = $this->getBlockFileAbsolute();
  420. if(file_exists($file))
  421. {
  422. unlink($file);
  423. }
  424. file_put_contents($file, serialize($this->RS_BLOCK_TABLE));
  425. }
  426. }