|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 //============================================================+ 00003 // File name : qrcode.php 00004 // Version : 1.0.009 00005 // Begin : 2010-03-22 00006 // Last Update : 2010-12-16 00007 // Author : Nicola Asuni - Tecnick.com S.r.l - Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com 00008 // License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html) 00009 // ------------------------------------------------------------------- 00010 // Copyright (C) 2010-2010 Nicola Asuni - Tecnick.com S.r.l. 00011 // 00012 // This file is part of TCPDF software library. 00013 // 00014 // TCPDF is free software: you can redistribute it and/or modify it 00015 // under the terms of the GNU Lesser General Public License as 00016 // published by the Free Software Foundation, either version 3 of the 00017 // License, or (at your option) any later version. 00018 // 00019 // TCPDF is distributed in the hope that it will be useful, but 00020 // WITHOUT ANY WARRANTY; without even the implied warranty of 00021 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 00022 // See the GNU Lesser General Public License for more details. 00023 // 00024 // You should have received a copy of the GNU Lesser General Public License 00025 // along with TCPDF. If not, see <http://www.gnu.org/licenses/>. 00026 // 00027 // See LICENSE.TXT file for more information. 00028 // ------------------------------------------------------------------- 00029 // 00030 // DESCRIPTION : 00031 // 00032 // Class to create QR-code arrays for TCPDF class. 00033 // QR Code symbol is a 2D barcode that can be scanned by 00034 // handy terminals such as a mobile phone with CCD. 00035 // The capacity of QR Code is up to 7000 digits or 4000 00036 // characters, and has high robustness. 00037 // This class supports QR Code model 2, described in 00038 // JIS (Japanese Industrial Standards) X0510:2004 00039 // or ISO/IEC 18004. 00040 // Currently the following features are not supported: 00041 // ECI and FNC1 mode, Micro QR Code, QR Code model 1, 00042 // Structured mode. 00043 // 00044 // This class is derived from the following projects: 00045 // --------------------------------------------------------- 00046 // "PHP QR Code encoder" 00047 // License: GNU-LGPLv3 00048 // Copyright (C) 2010 by Dominik Dzienia <deltalab at poczta dot fm> 00049 // http://phpqrcode.sourceforge.net/ 00050 // https://sourceforge.net/projects/phpqrcode/ 00051 // 00052 // The "PHP QR Code encoder" is based on 00053 // "C libqrencode library" (ver. 3.1.1) 00054 // License: GNU-LGPL 2.1 00055 // Copyright (C) 2006-2010 by Kentaro Fukuchi 00056 // http://megaui.net/fukuchi/works/qrencode/index.en.html 00057 // 00058 // Reed-Solomon code encoder is written by Phil Karn, KA9Q. 00059 // Copyright (C) 2002-2006 Phil Karn, KA9Q 00060 // 00061 // QR Code is registered trademark of DENSO WAVE INCORPORATED 00062 // http://www.denso-wave.com/qrcode/index-e.html 00063 // --------------------------------------------------------- 00064 //============================================================+ 00065 00082 // definitions 00083 if (!defined('QRCODEDEFS')) { 00084 00088 define('QRCODEDEFS', true); 00089 00090 // ----------------------------------------------------- 00091 00092 // Encoding modes (characters which can be encoded in QRcode) 00093 00097 define('QR_MODE_NL', -1); 00098 00102 define('QR_MODE_NM', 0); 00103 00107 define('QR_MODE_AN', 1); 00108 00112 define('QR_MODE_8B', 2); 00113 00117 define('QR_MODE_KJ', 3); 00118 00122 define('QR_MODE_ST', 4); 00123 00124 // ----------------------------------------------------- 00125 00126 // Levels of error correction. 00127 // QRcode has a function of an error correcting for miss reading that white is black. 00128 // Error correcting is defined in 4 level as below. 00129 00133 define('QR_ECLEVEL_L', 0); 00134 00138 define('QR_ECLEVEL_M', 1); 00139 00143 define('QR_ECLEVEL_Q', 2); 00144 00148 define('QR_ECLEVEL_H', 3); 00149 00150 // ----------------------------------------------------- 00151 00152 // Version. Size of QRcode is defined as version. 00153 // Version is from 1 to 40. 00154 // Version 1 is 21*21 matrix. And 4 modules increases whenever 1 version increases. 00155 // So version 40 is 177*177 matrix. 00156 00160 define('QRSPEC_VERSION_MAX', 40); 00161 00165 define('QRSPEC_WIDTH_MAX', 177); 00166 00167 // ----------------------------------------------------- 00168 00172 define('QRCAP_WIDTH', 0); 00173 00177 define('QRCAP_WORDS', 1); 00178 00182 define('QRCAP_REMINDER', 2); 00183 00187 define('QRCAP_EC', 3); 00188 00189 // ----------------------------------------------------- 00190 00191 // Structure (currently usupported) 00192 00196 define('STRUCTURE_HEADER_BITS', 20); 00197 00201 define('MAX_STRUCTURED_SYMBOLS', 16); 00202 00203 // ----------------------------------------------------- 00204 00205 // Masks 00206 00210 define('N1', 3); 00211 00215 define('N2', 3); 00216 00220 define('N3', 40); 00221 00225 define('N4', 10); 00226 00227 // ----------------------------------------------------- 00228 00229 // Optimization settings 00230 00234 define('QR_FIND_BEST_MASK', true); 00235 00239 define('QR_FIND_FROM_RANDOM', 2); 00240 00244 define('QR_DEFAULT_MASK', 2); 00245 00246 // ----------------------------------------------------- 00247 00248 } // end of definitions 00249 00250 // #*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*# 00251 00252 // for compatibility with PHP4 00253 if (!function_exists('str_split')) { 00260 function str_split($string, $split_length=1) { 00261 if ((strlen($string) > $split_length) OR (!$split_length)) { 00262 do { 00263 $c = strlen($string); 00264 $parts[] = substr($string, 0, $split_length); 00265 $string = substr($string, $split_length); 00266 } while ($string !== false); 00267 } else { 00268 $parts = array($string); 00269 } 00270 return $parts; 00271 } 00272 } 00273 00274 // ##################################################### 00275 00291 class QRcode { 00292 00297 protected $barcode_array = array(); 00298 00303 protected $version = 0; 00304 00309 protected $level = QR_ECLEVEL_L; 00310 00315 protected $hint = QR_MODE_8B; 00316 00321 protected $casesensitive = true; 00322 00327 protected $structured = 0; 00328 00333 protected $data; 00334 00335 // FrameFiller 00336 00341 protected $width; 00342 00347 protected $frame; 00348 00353 protected $x; 00354 00359 protected $y; 00360 00365 protected $dir; 00366 00371 protected $bit; 00372 00373 // ---- QRrawcode ---- 00374 00379 protected $datacode = array(); 00380 00385 protected $ecccode = array(); 00386 00391 protected $blocks; 00392 00397 protected $rsblocks = array(); //of RSblock 00398 00403 protected $count; 00404 00409 protected $dataLength; 00410 00415 protected $eccLength; 00416 00421 protected $b1; 00422 00423 // ---- QRmask ---- 00424 00429 protected $runLength = array(); 00430 00431 // ---- QRsplit ---- 00432 00437 protected $dataStr = ''; 00438 00443 protected $items; 00444 00445 // Reed-Solomon items 00446 00451 protected $rsitems = array(); 00452 00457 protected $frames = array(); 00458 00463 protected $anTable = array( 00464 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00465 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00466 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 00467 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 00468 -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 00469 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 00470 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00471 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 00472 ); 00473 00479 protected $capacity = array( 00480 array( 0, 0, 0, array( 0, 0, 0, 0)), // 00481 array( 21, 26, 0, array( 7, 10, 13, 17)), // 1 00482 array( 25, 44, 7, array( 10, 16, 22, 28)), // 00483 array( 29, 70, 7, array( 15, 26, 36, 44)), // 00484 array( 33, 100, 7, array( 20, 36, 52, 64)), // 00485 array( 37, 134, 7, array( 26, 48, 72, 88)), // 5 00486 array( 41, 172, 7, array( 36, 64, 96, 112)), // 00487 array( 45, 196, 0, array( 40, 72, 108, 130)), // 00488 array( 49, 242, 0, array( 48, 88, 132, 156)), // 00489 array( 53, 292, 0, array( 60, 110, 160, 192)), // 00490 array( 57, 346, 0, array( 72, 130, 192, 224)), // 10 00491 array( 61, 404, 0, array( 80, 150, 224, 264)), // 00492 array( 65, 466, 0, array( 96, 176, 260, 308)), // 00493 array( 69, 532, 0, array( 104, 198, 288, 352)), // 00494 array( 73, 581, 3, array( 120, 216, 320, 384)), // 00495 array( 77, 655, 3, array( 132, 240, 360, 432)), // 15 00496 array( 81, 733, 3, array( 144, 280, 408, 480)), // 00497 array( 85, 815, 3, array( 168, 308, 448, 532)), // 00498 array( 89, 901, 3, array( 180, 338, 504, 588)), // 00499 array( 93, 991, 3, array( 196, 364, 546, 650)), // 00500 array( 97, 1085, 3, array( 224, 416, 600, 700)), // 20 00501 array(101, 1156, 4, array( 224, 442, 644, 750)), // 00502 array(105, 1258, 4, array( 252, 476, 690, 816)), // 00503 array(109, 1364, 4, array( 270, 504, 750, 900)), // 00504 array(113, 1474, 4, array( 300, 560, 810, 960)), // 00505 array(117, 1588, 4, array( 312, 588, 870, 1050)), // 25 00506 array(121, 1706, 4, array( 336, 644, 952, 1110)), // 00507 array(125, 1828, 4, array( 360, 700, 1020, 1200)), // 00508 array(129, 1921, 3, array( 390, 728, 1050, 1260)), // 00509 array(133, 2051, 3, array( 420, 784, 1140, 1350)), // 00510 array(137, 2185, 3, array( 450, 812, 1200, 1440)), // 30 00511 array(141, 2323, 3, array( 480, 868, 1290, 1530)), // 00512 array(145, 2465, 3, array( 510, 924, 1350, 1620)), // 00513 array(149, 2611, 3, array( 540, 980, 1440, 1710)), // 00514 array(153, 2761, 3, array( 570, 1036, 1530, 1800)), // 00515 array(157, 2876, 0, array( 570, 1064, 1590, 1890)), // 35 00516 array(161, 3034, 0, array( 600, 1120, 1680, 1980)), // 00517 array(165, 3196, 0, array( 630, 1204, 1770, 2100)), // 00518 array(169, 3362, 0, array( 660, 1260, 1860, 2220)), // 00519 array(173, 3532, 0, array( 720, 1316, 1950, 2310)), // 00520 array(177, 3706, 0, array( 750, 1372, 2040, 2430)) // 40 00521 ); 00522 00527 protected $lengthTableBits = array( 00528 array(10, 12, 14), 00529 array( 9, 11, 13), 00530 array( 8, 16, 16), 00531 array( 8, 10, 12) 00532 ); 00533 00539 protected $eccTable = array( 00540 array(array( 0, 0), array( 0, 0), array( 0, 0), array( 0, 0)), // 00541 array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)), // 1 00542 array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)), // 00543 array(array( 1, 0), array( 1, 0), array( 2, 0), array( 2, 0)), // 00544 array(array( 1, 0), array( 2, 0), array( 2, 0), array( 4, 0)), // 00545 array(array( 1, 0), array( 2, 0), array( 2, 2), array( 2, 2)), // 5 00546 array(array( 2, 0), array( 4, 0), array( 4, 0), array( 4, 0)), // 00547 array(array( 2, 0), array( 4, 0), array( 2, 4), array( 4, 1)), // 00548 array(array( 2, 0), array( 2, 2), array( 4, 2), array( 4, 2)), // 00549 array(array( 2, 0), array( 3, 2), array( 4, 4), array( 4, 4)), // 00550 array(array( 2, 2), array( 4, 1), array( 6, 2), array( 6, 2)), // 10 00551 array(array( 4, 0), array( 1, 4), array( 4, 4), array( 3, 8)), // 00552 array(array( 2, 2), array( 6, 2), array( 4, 6), array( 7, 4)), // 00553 array(array( 4, 0), array( 8, 1), array( 8, 4), array(12, 4)), // 00554 array(array( 3, 1), array( 4, 5), array(11, 5), array(11, 5)), // 00555 array(array( 5, 1), array( 5, 5), array( 5, 7), array(11, 7)), // 15 00556 array(array( 5, 1), array( 7, 3), array(15, 2), array( 3, 13)), // 00557 array(array( 1, 5), array(10, 1), array( 1, 15), array( 2, 17)), // 00558 array(array( 5, 1), array( 9, 4), array(17, 1), array( 2, 19)), // 00559 array(array( 3, 4), array( 3, 11), array(17, 4), array( 9, 16)), // 00560 array(array( 3, 5), array( 3, 13), array(15, 5), array(15, 10)), // 20 00561 array(array( 4, 4), array(17, 0), array(17, 6), array(19, 6)), // 00562 array(array( 2, 7), array(17, 0), array( 7, 16), array(34, 0)), // 00563 array(array( 4, 5), array( 4, 14), array(11, 14), array(16, 14)), // 00564 array(array( 6, 4), array( 6, 14), array(11, 16), array(30, 2)), // 00565 array(array( 8, 4), array( 8, 13), array( 7, 22), array(22, 13)), // 25 00566 array(array(10, 2), array(19, 4), array(28, 6), array(33, 4)), // 00567 array(array( 8, 4), array(22, 3), array( 8, 26), array(12, 28)), // 00568 array(array( 3, 10), array( 3, 23), array( 4, 31), array(11, 31)), // 00569 array(array( 7, 7), array(21, 7), array( 1, 37), array(19, 26)), // 00570 array(array( 5, 10), array(19, 10), array(15, 25), array(23, 25)), // 30 00571 array(array(13, 3), array( 2, 29), array(42, 1), array(23, 28)), // 00572 array(array(17, 0), array(10, 23), array(10, 35), array(19, 35)), // 00573 array(array(17, 1), array(14, 21), array(29, 19), array(11, 46)), // 00574 array(array(13, 6), array(14, 23), array(44, 7), array(59, 1)), // 00575 array(array(12, 7), array(12, 26), array(39, 14), array(22, 41)), // 35 00576 array(array( 6, 14), array( 6, 34), array(46, 10), array( 2, 64)), // 00577 array(array(17, 4), array(29, 14), array(49, 10), array(24, 46)), // 00578 array(array( 4, 18), array(13, 32), array(48, 14), array(42, 32)), // 00579 array(array(20, 4), array(40, 7), array(43, 22), array(10, 67)), // 00580 array(array(19, 6), array(18, 31), array(34, 34), array(20, 61)) // 40 00581 ); 00582 00589 protected $alignmentPattern = array( 00590 array( 0, 0), 00591 array( 0, 0), array(18, 0), array(22, 0), array(26, 0), array(30, 0), // 1- 5 00592 array(34, 0), array(22, 38), array(24, 42), array(26, 46), array(28, 50), // 6-10 00593 array(30, 54), array(32, 58), array(34, 62), array(26, 46), array(26, 48), // 11-15 00594 array(26, 50), array(30, 54), array(30, 56), array(30, 58), array(34, 62), // 16-20 00595 array(28, 50), array(26, 50), array(30, 54), array(28, 54), array(32, 58), // 21-25 00596 array(30, 58), array(34, 62), array(26, 50), array(30, 54), array(26, 52), // 26-30 00597 array(30, 56), array(34, 60), array(30, 58), array(34, 62), array(30, 54), // 31-35 00598 array(24, 50), array(28, 54), array(32, 58), array(26, 54), array(30, 58) // 35-40 00599 ); 00600 00607 protected $versionPattern = array( 00608 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, // 00609 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, // 00610 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, // 00611 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, // 00612 0x27541, 0x28c69 00613 ); 00614 00619 protected $formatInfo = array( 00620 array(0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976), // 00621 array(0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0), // 00622 array(0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed), // 00623 array(0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b) // 00624 ); 00625 00626 00627 // ------------------------------------------------- 00628 // ------------------------------------------------- 00629 00630 00639 public function __construct($code, $eclevel = 'L') { 00640 $barcode_array = array(); 00641 if ((is_null($code)) OR ($code == '\0') OR ($code == '')) { 00642 return false; 00643 } 00644 // set error correction level 00645 $this->level = array_search($eclevel, array('L', 'M', 'Q', 'H')); 00646 if ($this->level === false) { 00647 $this->level = QR_ECLEVEL_L; 00648 } 00649 if (($this->hint != QR_MODE_8B) AND ($this->hint != QR_MODE_KJ)) { 00650 return false; 00651 } 00652 if (($this->version < 0) OR ($this->version > QRSPEC_VERSION_MAX)) { 00653 return false; 00654 } 00655 $this->items = array(); 00656 $this->encodeString($code); 00657 if (is_null($this->data)) { 00658 return false; 00659 } 00660 $qrTab = $this->binarize($this->data); 00661 $size = count($qrTab); 00662 $barcode_array['num_rows'] = $size; 00663 $barcode_array['num_cols'] = $size; 00664 $barcode_array['bcode'] = array(); 00665 foreach ($qrTab as $line) { 00666 $arrAdd = array(); 00667 foreach (str_split($line) as $char) { 00668 $arrAdd[] = ($char=='1')?1:0; 00669 } 00670 $barcode_array['bcode'][] = $arrAdd; 00671 } 00672 $this->barcode_array = $barcode_array; 00673 } 00674 00680 public function getBarcodeArray() { 00681 return $this->barcode_array; 00682 } 00683 00689 protected function binarize($frame) { 00690 $len = count($frame); 00691 // the frame is square (width = height) 00692 foreach ($frame as &$frameLine) { 00693 for ($i=0; $i<$len; $i++) { 00694 $frameLine[$i] = (ord($frameLine[$i])&1)?'1':'0'; 00695 } 00696 } 00697 return $frame; 00698 } 00699 00704 protected function encodeString($string) { 00705 $this->dataStr = $string; 00706 if (!$this->casesensitive) { 00707 $this->toUpper(); 00708 } 00709 $ret = $this->splitString(); 00710 if ($ret < 0) { 00711 return NULL; 00712 } 00713 $this->encodeMask(-1); 00714 } 00715 00720 protected function encodeMask($mask) { 00721 $spec = array(0, 0, 0, 0, 0); 00722 $this->datacode = $this->getByteStream($this->items); 00723 if (is_null($this->datacode)) { 00724 return NULL; 00725 } 00726 $spec = $this->getEccSpec($this->version, $this->level, $spec); 00727 $this->b1 = $this->rsBlockNum1($spec); 00728 $this->dataLength = $this->rsDataLength($spec); 00729 $this->eccLength = $this->rsEccLength($spec); 00730 $this->ecccode = array_fill(0, $this->eccLength, 0); 00731 $this->blocks = $this->rsBlockNum($spec); 00732 $ret = $this->init($spec); 00733 if ($ret < 0) { 00734 return NULL; 00735 } 00736 $this->count = 0; 00737 $this->width = $this->getWidth($this->version); 00738 $this->frame = $this->newFrame($this->version); 00739 $this->x = $this->width - 1; 00740 $this->y = $this->width - 1; 00741 $this->dir = -1; 00742 $this->bit = -1; 00743 // inteleaved data and ecc codes 00744 for ($i=0; $i < ($this->dataLength + $this->eccLength); $i++) { 00745 $code = $this->getCode(); 00746 $bit = 0x80; 00747 for ($j=0; $j<8; $j++) { 00748 $addr = $this->getNextPosition(); 00749 $this->setFrameAt($addr, 0x02 | (($bit & $code) != 0)); 00750 $bit = $bit >> 1; 00751 } 00752 } 00753 // remainder bits 00754 $j = $this->getRemainder($this->version); 00755 for ($i=0; $i<$j; $i++) { 00756 $addr = $this->getNextPosition(); 00757 $this->setFrameAt($addr, 0x02); 00758 } 00759 // masking 00760 $this->runLength = array_fill(0, QRSPEC_WIDTH_MAX + 1, 0); 00761 if ($mask < 0) { 00762 if (QR_FIND_BEST_MASK) { 00763 $masked = $this->mask($this->width, $this->frame, $this->level); 00764 } else { 00765 $masked = $this->makeMask($this->width, $this->frame, (intval(QR_DEFAULT_MASK) % 8), $this->level); 00766 } 00767 } else { 00768 $masked = $this->makeMask($this->width, $this->frame, $mask, $this->level); 00769 } 00770 if ($masked == NULL) { 00771 return NULL; 00772 } 00773 $this->data = $masked; 00774 } 00775 00776 // - - - - - - - - - - - - - - - - - - - - - - - - - 00777 00778 // FrameFiller 00779 00785 protected function setFrameAt($at, $val) { 00786 $this->frame[$at['y']][$at['x']] = chr($val); 00787 } 00788 00794 protected function getFrameAt($at) { 00795 return ord($this->frame[$at['y']][$at['x']]); 00796 } 00797 00802 protected function getNextPosition() { 00803 do { 00804 if ($this->bit == -1) { 00805 $this->bit = 0; 00806 return array('x'=>$this->x, 'y'=>$this->y); 00807 } 00808 $x = $this->x; 00809 $y = $this->y; 00810 $w = $this->width; 00811 if ($this->bit == 0) { 00812 $x--; 00813 $this->bit++; 00814 } else { 00815 $x++; 00816 $y += $this->dir; 00817 $this->bit--; 00818 } 00819 if ($this->dir < 0) { 00820 if ($y < 0) { 00821 $y = 0; 00822 $x -= 2; 00823 $this->dir = 1; 00824 if ($x == 6) { 00825 $x--; 00826 $y = 9; 00827 } 00828 } 00829 } else { 00830 if ($y == $w) { 00831 $y = $w - 1; 00832 $x -= 2; 00833 $this->dir = -1; 00834 if ($x == 6) { 00835 $x--; 00836 $y -= 8; 00837 } 00838 } 00839 } 00840 if (($x < 0) OR ($y < 0)) { 00841 return NULL; 00842 } 00843 $this->x = $x; 00844 $this->y = $y; 00845 } while(ord($this->frame[$y][$x]) & 0x80); 00846 return array('x'=>$x, 'y'=>$y); 00847 } 00848 00849 // - - - - - - - - - - - - - - - - - - - - - - - - - 00850 00851 // QRrawcode 00852 00858 protected function init($spec) { 00859 $dl = $this->rsDataCodes1($spec); 00860 $el = $this->rsEccCodes1($spec); 00861 $rs = $this->init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el); 00862 $blockNo = 0; 00863 $dataPos = 0; 00864 $eccPos = 0; 00865 $endfor = $this->rsBlockNum1($spec); 00866 for ($i=0; $i < $endfor; ++$i) { 00867 $ecc = array_slice($this->ecccode, $eccPos); 00868 $this->rsblocks[$blockNo] = array(); 00869 $this->rsblocks[$blockNo]['dataLength'] = $dl; 00870 $this->rsblocks[$blockNo]['data'] = array_slice($this->datacode, $dataPos); 00871 $this->rsblocks[$blockNo]['eccLength'] = $el; 00872 $ecc = $this->encode_rs_char($rs, $this->rsblocks[$blockNo]['data'], $ecc); 00873 $this->rsblocks[$blockNo]['ecc'] = $ecc; 00874 $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc); 00875 $dataPos += $dl; 00876 $eccPos += $el; 00877 $blockNo++; 00878 } 00879 if ($this->rsBlockNum2($spec) == 0) { 00880 return 0; 00881 } 00882 $dl = $this->rsDataCodes2($spec); 00883 $el = $this->rsEccCodes2($spec); 00884 $rs = $this->init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el); 00885 if ($rs == NULL) { 00886 return -1; 00887 } 00888 $endfor = $this->rsBlockNum2($spec); 00889 for ($i=0; $i < $endfor; ++$i) { 00890 $ecc = array_slice($this->ecccode, $eccPos); 00891 $this->rsblocks[$blockNo] = array(); 00892 $this->rsblocks[$blockNo]['dataLength'] = $dl; 00893 $this->rsblocks[$blockNo]['data'] = array_slice($this->datacode, $dataPos); 00894 $this->rsblocks[$blockNo]['eccLength'] = $el; 00895 $ecc = $this->encode_rs_char($rs, $this->rsblocks[$blockNo]['data'], $ecc); 00896 $this->rsblocks[$blockNo]['ecc'] = $ecc; 00897 $this->ecccode = array_merge(array_slice($this->ecccode, 0, $eccPos), $ecc); 00898 $dataPos += $dl; 00899 $eccPos += $el; 00900 $blockNo++; 00901 } 00902 return 0; 00903 } 00904 00909 protected function getCode() { 00910 if ($this->count < $this->dataLength) { 00911 $row = $this->count % $this->blocks; 00912 $col = $this->count / $this->blocks; 00913 if ($col >= $this->rsblocks[0]['dataLength']) { 00914 $row += $this->b1; 00915 } 00916 $ret = $this->rsblocks[$row]['data'][$col]; 00917 } elseif ($this->count < $this->dataLength + $this->eccLength) { 00918 $row = ($this->count - $this->dataLength) % $this->blocks; 00919 $col = ($this->count - $this->dataLength) / $this->blocks; 00920 $ret = $this->rsblocks[$row]['ecc'][$col]; 00921 } else { 00922 return 0; 00923 } 00924 $this->count++; 00925 return $ret; 00926 } 00927 00928 // - - - - - - - - - - - - - - - - - - - - - - - - - 00929 00930 // QRmask 00931 00940 protected function writeFormatInformation($width, &$frame, $mask, $level) { 00941 $blacks = 0; 00942 $format = $this->getFormatInfo($mask, $level); 00943 for ($i=0; $i<8; ++$i) { 00944 if ($format & 1) { 00945 $blacks += 2; 00946 $v = 0x85; 00947 } else { 00948 $v = 0x84; 00949 } 00950 $frame[8][$width - 1 - $i] = chr($v); 00951 if ($i < 6) { 00952 $frame[$i][8] = chr($v); 00953 } else { 00954 $frame[$i + 1][8] = chr($v); 00955 } 00956 $format = $format >> 1; 00957 } 00958 for ($i=0; $i<7; ++$i) { 00959 if ($format & 1) { 00960 $blacks += 2; 00961 $v = 0x85; 00962 } else { 00963 $v = 0x84; 00964 } 00965 $frame[$width - 7 + $i][8] = chr($v); 00966 if ($i == 0) { 00967 $frame[8][7] = chr($v); 00968 } else { 00969 $frame[8][6 - $i] = chr($v); 00970 } 00971 $format = $format >> 1; 00972 } 00973 return $blacks; 00974 } 00975 00982 protected function mask0($x, $y) { 00983 return ($x + $y) & 1; 00984 } 00985 00992 protected function mask1($x, $y) { 00993 return ($y & 1); 00994 } 00995 01002 protected function mask2($x, $y) { 01003 return ($x % 3); 01004 } 01005 01012 protected function mask3($x, $y) { 01013 return ($x + $y) % 3; 01014 } 01015 01022 protected function mask4($x, $y) { 01023 return (((int)($y / 2)) + ((int)($x / 3))) & 1; 01024 } 01025 01032 protected function mask5($x, $y) { 01033 return (($x * $y) & 1) + ($x * $y) % 3; 01034 } 01035 01042 protected function mask6($x, $y) { 01043 return ((($x * $y) & 1) + ($x * $y) % 3) & 1; 01044 } 01045 01052 protected function mask7($x, $y) { 01053 return ((($x * $y) % 3) + (($x + $y) & 1)) & 1; 01054 } 01055 01063 protected function generateMaskNo($maskNo, $width, $frame) { 01064 $bitMask = array_fill(0, $width, array_fill(0, $width, 0)); 01065 for ($y=0; $y<$width; ++$y) { 01066 for ($x=0; $x<$width; ++$x) { 01067 if (ord($frame[$y][$x]) & 0x80) { 01068 $bitMask[$y][$x] = 0; 01069 } else { 01070 $maskFunc = call_user_func(array($this, 'mask'.$maskNo), $x, $y); 01071 $bitMask[$y][$x] = ($maskFunc == 0)?1:0; 01072 } 01073 } 01074 } 01075 return $bitMask; 01076 } 01077 01087 protected function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly=false) { 01088 $b = 0; 01089 $bitMask = array(); 01090 $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d); 01091 if ($maskGenOnly) { 01092 return; 01093 } 01094 $d = $s; 01095 for ($y=0; $y<$width; ++$y) { 01096 for ($x=0; $x<$width; ++$x) { 01097 if ($bitMask[$y][$x] == 1) { 01098 $d[$y][$x] = chr(ord($s[$y][$x]) ^ ((int)($bitMask[$y][$x]))); 01099 } 01100 $b += (int)(ord($d[$y][$x]) & 1); 01101 } 01102 } 01103 return $b; 01104 } 01105 01114 protected function makeMask($width, $frame, $maskNo, $level) { 01115 $masked = array_fill(0, $width, str_repeat("\0", $width)); 01116 $this->makeMaskNo($maskNo, $width, $frame, $masked); 01117 $this->writeFormatInformation($width, $masked, $maskNo, $level); 01118 return $masked; 01119 } 01120 01126 protected function calcN1N3($length) { 01127 $demerit = 0; 01128 for ($i=0; $i<$length; ++$i) { 01129 if ($this->runLength[$i] >= 5) { 01130 $demerit += (N1 + ($this->runLength[$i] - 5)); 01131 } 01132 if ($i & 1) { 01133 if (($i >= 3) AND ($i < ($length-2)) AND ($this->runLength[$i] % 3 == 0)) { 01134 $fact = (int)($this->runLength[$i] / 3); 01135 if (($this->runLength[$i-2] == $fact) 01136 AND ($this->runLength[$i-1] == $fact) 01137 AND ($this->runLength[$i+1] == $fact) 01138 AND ($this->runLength[$i+2] == $fact)) { 01139 if (($this->runLength[$i-3] < 0) OR ($this->runLength[$i-3] >= (4 * $fact))) { 01140 $demerit += N3; 01141 } elseif ((($i+3) >= $length) OR ($this->runLength[$i+3] >= (4 * $fact))) { 01142 $demerit += N3; 01143 } 01144 } 01145 } 01146 } 01147 } 01148 return $demerit; 01149 } 01150 01157 protected function evaluateSymbol($width, $frame) { 01158 $head = 0; 01159 $demerit = 0; 01160 for ($y=0; $y<$width; ++$y) { 01161 $head = 0; 01162 $this->runLength[0] = 1; 01163 $frameY = $frame[$y]; 01164 if ($y > 0) { 01165 $frameYM = $frame[$y-1]; 01166 } 01167 for ($x=0; $x<$width; ++$x) { 01168 if (($x > 0) AND ($y > 0)) { 01169 $b22 = ord($frameY[$x]) & ord($frameY[$x-1]) & ord($frameYM[$x]) & ord($frameYM[$x-1]); 01170 $w22 = ord($frameY[$x]) | ord($frameY[$x-1]) | ord($frameYM[$x]) | ord($frameYM[$x-1]); 01171 if (($b22 | ($w22 ^ 1)) & 1) { 01172 $demerit += N2; 01173 } 01174 } 01175 if (($x == 0) AND (ord($frameY[$x]) & 1)) { 01176 $this->runLength[0] = -1; 01177 $head = 1; 01178 $this->runLength[$head] = 1; 01179 } elseif ($x > 0) { 01180 if ((ord($frameY[$x]) ^ ord($frameY[$x-1])) & 1) { 01181 $head++; 01182 $this->runLength[$head] = 1; 01183 } else { 01184 $this->runLength[$head]++; 01185 } 01186 } 01187 } 01188 $demerit += $this->calcN1N3($head+1); 01189 } 01190 for ($x=0; $x<$width; ++$x) { 01191 $head = 0; 01192 $this->runLength[0] = 1; 01193 for ($y=0; $y<$width; ++$y) { 01194 if (($y == 0) AND (ord($frame[$y][$x]) & 1)) { 01195 $this->runLength[0] = -1; 01196 $head = 1; 01197 $this->runLength[$head] = 1; 01198 } elseif ($y > 0) { 01199 if ((ord($frame[$y][$x]) ^ ord($frame[$y-1][$x])) & 1) { 01200 $head++; 01201 $this->runLength[$head] = 1; 01202 } else { 01203 $this->runLength[$head]++; 01204 } 01205 } 01206 } 01207 $demerit += $this->calcN1N3($head+1); 01208 } 01209 return $demerit; 01210 } 01211 01219 protected function mask($width, $frame, $level) { 01220 $minDemerit = PHP_INT_MAX; 01221 $bestMaskNum = 0; 01222 $bestMask = array(); 01223 $checked_masks = array(0, 1, 2, 3, 4, 5, 6, 7); 01224 if (QR_FIND_FROM_RANDOM !== false) { 01225 $howManuOut = 8 - (QR_FIND_FROM_RANDOM % 9); 01226 for ($i = 0; $i < $howManuOut; ++$i) { 01227 $remPos = rand (0, count($checked_masks)-1); 01228 unset($checked_masks[$remPos]); 01229 $checked_masks = array_values($checked_masks); 01230 } 01231 } 01232 $bestMask = $frame; 01233 foreach ($checked_masks as $i) { 01234 $mask = array_fill(0, $width, str_repeat("\0", $width)); 01235 $demerit = 0; 01236 $blacks = 0; 01237 $blacks = $this->makeMaskNo($i, $width, $frame, $mask); 01238 $blacks += $this->writeFormatInformation($width, $mask, $i, $level); 01239 $blacks = (int)(100 * $blacks / ($width * $width)); 01240 $demerit = (int)((int)(abs($blacks - 50) / 5) * N4); 01241 $demerit += $this->evaluateSymbol($width, $mask); 01242 if ($demerit < $minDemerit) { 01243 $minDemerit = $demerit; 01244 $bestMask = $mask; 01245 $bestMaskNum = $i; 01246 } 01247 } 01248 return $bestMask; 01249 } 01250 01251 // - - - - - - - - - - - - - - - - - - - - - - - - - 01252 01253 // QRsplit 01254 01261 protected function isdigitat($str, $pos) { 01262 if ($pos >= strlen($str)) { 01263 return false; 01264 } 01265 return ((ord($str[$pos]) >= ord('0'))&&(ord($str[$pos]) <= ord('9'))); 01266 } 01267 01274 protected function isalnumat($str, $pos) { 01275 if ($pos >= strlen($str)) { 01276 return false; 01277 } 01278 return ($this->lookAnTable(ord($str[$pos])) >= 0); 01279 } 01280 01286 protected function identifyMode($pos) { 01287 if ($pos >= strlen($this->dataStr)) { 01288 return QR_MODE_NL; 01289 } 01290 $c = $this->dataStr[$pos]; 01291 if ($this->isdigitat($this->dataStr, $pos)) { 01292 return QR_MODE_NM; 01293 } elseif ($this->isalnumat($this->dataStr, $pos)) { 01294 return QR_MODE_AN; 01295 } elseif ($this->hint == QR_MODE_KJ) { 01296 if ($pos+1 < strlen($this->dataStr)) { 01297 $d = $this->dataStr[$pos+1]; 01298 $word = (ord($c) << 8) | ord($d); 01299 if (($word >= 0x8140 && $word <= 0x9ffc) OR ($word >= 0xe040 && $word <= 0xebbf)) { 01300 return QR_MODE_KJ; 01301 } 01302 } 01303 } 01304 return QR_MODE_8B; 01305 } 01306 01311 protected function eatNum() { 01312 $ln = $this->lengthIndicator(QR_MODE_NM, $this->version); 01313 $p = 0; 01314 while($this->isdigitat($this->dataStr, $p)) { 01315 $p++; 01316 } 01317 $run = $p; 01318 $mode = $this->identifyMode($p); 01319 if ($mode == QR_MODE_8B) { 01320 $dif = $this->estimateBitsModeNum($run) + 4 + $ln 01321 + $this->estimateBitsMode8(1) // + 4 + l8 01322 - $this->estimateBitsMode8($run + 1); // - 4 - l8 01323 if ($dif > 0) { 01324 return $this->eat8(); 01325 } 01326 } 01327 if ($mode == QR_MODE_AN) { 01328 $dif = $this->estimateBitsModeNum($run) + 4 + $ln 01329 + $this->estimateBitsModeAn(1) // + 4 + la 01330 - $this->estimateBitsModeAn($run + 1);// - 4 - la 01331 if ($dif > 0) { 01332 return $this->eatAn(); 01333 } 01334 } 01335 $this->items = $this->appendNewInputItem($this->items, QR_MODE_NM, $run, str_split($this->dataStr)); 01336 return $run; 01337 } 01338 01343 protected function eatAn() { 01344 $la = $this->lengthIndicator(QR_MODE_AN, $this->version); 01345 $ln = $this->lengthIndicator(QR_MODE_NM, $this->version); 01346 $p =1 ; 01347 while($this->isalnumat($this->dataStr, $p)) { 01348 if ($this->isdigitat($this->dataStr, $p)) { 01349 $q = $p; 01350 while($this->isdigitat($this->dataStr, $q)) { 01351 $q++; 01352 } 01353 $dif = $this->estimateBitsModeAn($p) // + 4 + la 01354 + $this->estimateBitsModeNum($q - $p) + 4 + $ln 01355 - $this->estimateBitsModeAn($q); // - 4 - la 01356 if ($dif < 0) { 01357 break; 01358 } else { 01359 $p = $q; 01360 } 01361 } else { 01362 $p++; 01363 } 01364 } 01365 $run = $p; 01366 if (!$this->isalnumat($this->dataStr, $p)) { 01367 $dif = $this->estimateBitsModeAn($run) + 4 + $la 01368 + $this->estimateBitsMode8(1) // + 4 + l8 01369 - $this->estimateBitsMode8($run + 1); // - 4 - l8 01370 if ($dif > 0) { 01371 return $this->eat8(); 01372 } 01373 } 01374 $this->items = $this->appendNewInputItem($this->items, QR_MODE_AN, $run, str_split($this->dataStr)); 01375 return $run; 01376 } 01377 01382 protected function eatKanji() { 01383 $p = 0; 01384 while($this->identifyMode($p) == QR_MODE_KJ) { 01385 $p += 2; 01386 } 01387 $this->items = $this->appendNewInputItem($this->items, QR_MODE_KJ, $p, str_split($this->dataStr)); 01388 return $run; 01389 } 01390 01395 protected function eat8() { 01396 $la = $this->lengthIndicator(QR_MODE_AN, $this->version); 01397 $ln = $this->lengthIndicator(QR_MODE_NM, $this->version); 01398 $p = 1; 01399 $dataStrLen = strlen($this->dataStr); 01400 while($p < $dataStrLen) { 01401 $mode = $this->identifyMode($p); 01402 if ($mode == QR_MODE_KJ) { 01403 break; 01404 } 01405 if ($mode == QR_MODE_NM) { 01406 $q = $p; 01407 while($this->isdigitat($this->dataStr, $q)) { 01408 $q++; 01409 } 01410 $dif = $this->estimateBitsMode8($p) // + 4 + l8 01411 + $this->estimateBitsModeNum($q - $p) + 4 + $ln 01412 - $this->estimateBitsMode8($q); // - 4 - l8 01413 if ($dif < 0) { 01414 break; 01415 } else { 01416 $p = $q; 01417 } 01418 } elseif ($mode == QR_MODE_AN) { 01419 $q = $p; 01420 while($this->isalnumat($this->dataStr, $q)) { 01421 $q++; 01422 } 01423 $dif = $this->estimateBitsMode8($p) // + 4 + l8 01424 + $this->estimateBitsModeAn($q - $p) + 4 + $la 01425 - $this->estimateBitsMode8($q); // - 4 - l8 01426 if ($dif < 0) { 01427 break; 01428 } else { 01429 $p = $q; 01430 } 01431 } else { 01432 $p++; 01433 } 01434 } 01435 $run = $p; 01436 $this->items = $this->appendNewInputItem($this->items, QR_MODE_8B, $run, str_split($this->dataStr)); 01437 return $run; 01438 } 01439 01443 protected function splitString() { 01444 while (strlen($this->dataStr) > 0) { 01445 if ($this->dataStr == '') { 01446 return 0; 01447 } 01448 $mode = $this->identifyMode(0); 01449 switch ($mode) { 01450 case QR_MODE_NM: { 01451 $length = $this->eatNum(); 01452 break; 01453 } 01454 case QR_MODE_AN: { 01455 $length = $this->eatAn(); 01456 break; 01457 } 01458 case QR_MODE_KJ: { 01459 if ($hint == QR_MODE_KJ) { 01460 $length = $this->eatKanji(); 01461 } else { 01462 $length = $this->eat8(); 01463 } 01464 break; 01465 } 01466 default: { 01467 $length = $this->eat8(); 01468 break; 01469 } 01470 } 01471 if ($length == 0) { 01472 return 0; 01473 } 01474 if ($length < 0) { 01475 return -1; 01476 } 01477 $this->dataStr = substr($this->dataStr, $length); 01478 } 01479 } 01480 01484 protected function toUpper() { 01485 $stringLen = strlen($this->dataStr); 01486 $p = 0; 01487 while ($p < $stringLen) { 01488 $mode = $this->identifyMode(substr($this->dataStr, $p), $this->hint); 01489 if ($mode == QR_MODE_KJ) { 01490 $p += 2; 01491 } else { 01492 if ((ord($this->dataStr[$p]) >= ord('a')) AND (ord($this->dataStr[$p]) <= ord('z'))) { 01493 $this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32); 01494 } 01495 $p++; 01496 } 01497 } 01498 return $this->dataStr; 01499 } 01500 01501 // - - - - - - - - - - - - - - - - - - - - - - - - - 01502 01503 // QRinputItem 01504 01513 protected function newInputItem($mode, $size, $data, $bstream=null) { 01514 $setData = array_slice($data, 0, $size); 01515 if (count($setData) < $size) { 01516 $setData = array_merge($setData, array_fill(0, ($size - count($setData)), 0)); 01517 } 01518 if (!$this->check($mode, $size, $setData)) { 01519 return NULL; 01520 } 01521 $inputitem = array(); 01522 $inputitem['mode'] = $mode; 01523 $inputitem['size'] = $size; 01524 $inputitem['data'] = $setData; 01525 $inputitem['bstream'] = $bstream; 01526 return $inputitem; 01527 } 01528 01535 protected function encodeModeNum($inputitem, $version) { 01536 $words = (int)($inputitem['size'] / 3); 01537 $inputitem['bstream'] = array(); 01538 $val = 0x1; 01539 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val); 01540 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_NM, $version), $inputitem['size']); 01541 for ($i=0; $i < $words; ++$i) { 01542 $val = (ord($inputitem['data'][$i*3 ]) - ord('0')) * 100; 01543 $val += (ord($inputitem['data'][$i*3+1]) - ord('0')) * 10; 01544 $val += (ord($inputitem['data'][$i*3+2]) - ord('0')); 01545 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 10, $val); 01546 } 01547 if ($inputitem['size'] - $words * 3 == 1) { 01548 $val = ord($inputitem['data'][$words*3]) - ord('0'); 01549 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, $val); 01550 } elseif (($inputitem['size'] - ($words * 3)) == 2) { 01551 $val = (ord($inputitem['data'][$words*3 ]) - ord('0')) * 10; 01552 $val += (ord($inputitem['data'][$words*3+1]) - ord('0')); 01553 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 7, $val); 01554 } 01555 return $inputitem; 01556 } 01557 01564 protected function encodeModeAn($inputitem, $version) { 01565 $words = (int)($inputitem['size'] / 2); 01566 $inputitem['bstream'] = array(); 01567 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x02); 01568 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_AN, $version), $inputitem['size']); 01569 for ($i=0; $i < $words; ++$i) { 01570 $val = (int)($this->lookAnTable(ord($inputitem['data'][$i*2])) * 45); 01571 $val += (int)($this->lookAnTable(ord($inputitem['data'][($i*2)+1]))); 01572 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 11, $val); 01573 } 01574 if ($inputitem['size'] & 1) { 01575 $val = $this->lookAnTable(ord($inputitem['data'][($words * 2)])); 01576 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 6, $val); 01577 } 01578 return $inputitem; 01579 } 01580 01587 protected function encodeMode8($inputitem, $version) { 01588 $inputitem['bstream'] = array(); 01589 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x4); 01590 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_8B, $version), $inputitem['size']); 01591 for ($i=0; $i < $inputitem['size']; ++$i) { 01592 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, ord($inputitem['data'][$i])); 01593 } 01594 return $inputitem; 01595 } 01596 01603 protected function encodeModeKanji($inputitem, $version) { 01604 $inputitem['bstream'] = array(); 01605 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x8); 01606 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], $this->lengthIndicator(QR_MODE_KJ, $version), (int)($inputitem['size'] / 2)); 01607 for ($i=0; $i<$inputitem['size']; $i+=2) { 01608 $val = (ord($inputitem['data'][$i]) << 8) | ord($inputitem['data'][$i+1]); 01609 if ($val <= 0x9ffc) { 01610 $val -= 0x8140; 01611 } else { 01612 $val -= 0xc140; 01613 } 01614 $h = ($val >> 8) * 0xc0; 01615 $val = ($val & 0xff) + $h; 01616 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 13, $val); 01617 } 01618 return $inputitem; 01619 } 01620 01626 protected function encodeModeStructure($inputitem) { 01627 $inputitem['bstream'] = array(); 01628 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, 0x03); 01629 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, ord($inputitem['data'][1]) - 1); 01630 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 4, ord($inputitem['data'][0]) - 1); 01631 $inputitem['bstream'] = $this->appendNum($inputitem['bstream'], 8, ord($inputitem['data'][2])); 01632 return $inputitem; 01633 } 01634 01641 protected function encodeBitStream($inputitem, $version) { 01642 $inputitem['bstream'] = array(); 01643 $words = $this->maximumWords($inputitem['mode'], $version); 01644 if ($inputitem['size'] > $words) { 01645 $st1 = $this->newInputItem($inputitem['mode'], $words, $inputitem['data']); 01646 $st2 = $this->newInputItem($inputitem['mode'], $inputitem['size'] - $words, array_slice($inputitem['data'], $words)); 01647 $st1 = $this->encodeBitStream($st1, $version); 01648 $st2 = $this->encodeBitStream($st2, $version); 01649 $inputitem['bstream'] = array(); 01650 $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st1['bstream']); 01651 $inputitem['bstream'] = $this->appendBitstream($inputitem['bstream'], $st2['bstream']); 01652 } else { 01653 switch($inputitem['mode']) { 01654 case QR_MODE_NM: { 01655 $inputitem = $this->encodeModeNum($inputitem, $version); 01656 break; 01657 } 01658 case QR_MODE_AN: { 01659 $inputitem = $this->encodeModeAn($inputitem, $version); 01660 break; 01661 } 01662 case QR_MODE_8B: { 01663 $inputitem = $this->encodeMode8($inputitem, $version); 01664 break; 01665 } 01666 case QR_MODE_KJ: { 01667 $inputitem = $this->encodeModeKanji($inputitem, $version); 01668 break; 01669 } 01670 case QR_MODE_ST: { 01671 $inputitem = $this->encodeModeStructure($inputitem); 01672 break; 01673 } 01674 default: { 01675 break; 01676 } 01677 } 01678 } 01679 return $inputitem; 01680 } 01681 01682 // - - - - - - - - - - - - - - - - - - - - - - - - - 01683 01684 // QRinput 01685 01696 protected function appendNewInputItem($items, $mode, $size, $data) { 01697 $newitem = $this->newInputItem($mode, $size, $data); 01698 if (!empty($newitem)) { 01699 $items[] = $newitem; 01700 } 01701 return $items; 01702 } 01703 01712 protected function insertStructuredAppendHeader($items, $size, $index, $parity) { 01713 if ($size > MAX_STRUCTURED_SYMBOLS) { 01714 return -1; 01715 } 01716 if (($index <= 0) OR ($index > MAX_STRUCTURED_SYMBOLS)) { 01717 return -1; 01718 } 01719 $buf = array($size, $index, $parity); 01720 $entry = $this->newInputItem(QR_MODE_ST, 3, buf); 01721 array_unshift($items, $entry); 01722 return $items; 01723 } 01724 01730 protected function calcParity($items) { 01731 $parity = 0; 01732 foreach ($items as $item) { 01733 if ($item['mode'] != QR_MODE_ST) { 01734 for ($i=$item['size']-1; $i>=0; --$i) { 01735 $parity ^= $item['data'][$i]; 01736 } 01737 } 01738 } 01739 return $parity; 01740 } 01741 01748 protected function checkModeNum($size, $data) { 01749 for ($i=0; $i<$size; ++$i) { 01750 if ((ord($data[$i]) < ord('0')) OR (ord($data[$i]) > ord('9'))){ 01751 return false; 01752 } 01753 } 01754 return true; 01755 } 01756 01762 protected function lookAnTable($c) { 01763 return (($c > 127)?-1:$this->anTable[$c]); 01764 } 01765 01772 protected function checkModeAn($size, $data) { 01773 for ($i=0; $i<$size; ++$i) { 01774 if ($this->lookAnTable(ord($data[$i])) == -1) { 01775 return false; 01776 } 01777 } 01778 return true; 01779 } 01780 01786 protected function estimateBitsModeNum($size) { 01787 $w = (int)($size / 3); 01788 $bits = ($w * 10); 01789 switch($size - ($w * 3)) { 01790 case 1: { 01791 $bits += 4; 01792 break; 01793 } 01794 case 2: { 01795 $bits += 7; 01796 break; 01797 } 01798 } 01799 return $bits; 01800 } 01801 01807 protected function estimateBitsModeAn($size) { 01808 $bits = (int)($size * 5.5); // (size / 2 ) * 11 01809 if ($size & 1) { 01810 $bits += 6; 01811 } 01812 return $bits; 01813 } 01814 01820 protected function estimateBitsMode8($size) { 01821 return (int)($size * 8); 01822 } 01823 01829 protected function estimateBitsModeKanji($size) { 01830 return (int)($size * 6.5); // (size / 2 ) * 13 01831 } 01832 01839 protected function checkModeKanji($size, $data) { 01840 if ($size & 1) { 01841 return false; 01842 } 01843 for ($i=0; $i<$size; $i+=2) { 01844 $val = (ord($data[$i]) << 8) | ord($data[$i+1]); 01845 if (($val < 0x8140) OR (($val > 0x9ffc) AND ($val < 0xe040)) OR ($val > 0xebbf)) { 01846 return false; 01847 } 01848 } 01849 return true; 01850 } 01851 01859 protected function check($mode, $size, $data) { 01860 if ($size <= 0) { 01861 return false; 01862 } 01863 switch($mode) { 01864 case QR_MODE_NM: { 01865 return $this->checkModeNum($size, $data); 01866 } 01867 case QR_MODE_AN: { 01868 return $this->checkModeAn($size, $data); 01869 } 01870 case QR_MODE_KJ: { 01871 return $this->checkModeKanji($size, $data); 01872 } 01873 case QR_MODE_8B: { 01874 return true; 01875 } 01876 case QR_MODE_ST: { 01877 return true; 01878 } 01879 default: { 01880 break; 01881 } 01882 } 01883 return false; 01884 } 01885 01892 protected function estimateBitStreamSize($items, $version) { 01893 $bits = 0; 01894 if ($version == 0) { 01895 $version = 1; 01896 } 01897 foreach ($items as $item) { 01898 switch($item['mode']) { 01899 case QR_MODE_NM: { 01900 $bits = $this->estimateBitsModeNum($item['size']); 01901 break; 01902 } 01903 case QR_MODE_AN: { 01904 $bits = $this->estimateBitsModeAn($item['size']); 01905 break; 01906 } 01907 case QR_MODE_8B: { 01908 $bits = $this->estimateBitsMode8($item['size']); 01909 break; 01910 } 01911 case QR_MODE_KJ: { 01912 $bits = $this->estimateBitsModeKanji($item['size']); 01913 break; 01914 } 01915 case QR_MODE_ST: { 01916 return STRUCTURE_HEADER_BITS; 01917 } 01918 default: { 01919 return 0; 01920 } 01921 } 01922 $l = $this->lengthIndicator($item['mode'], $version); 01923 $m = 1 << $l; 01924 $num = (int)(($item['size'] + $m - 1) / $m); 01925 $bits += $num * (4 + $l); 01926 } 01927 return $bits; 01928 } 01929 01935 protected function estimateVersion($items) { 01936 $version = 0; 01937 $prev = 0; 01938 do { 01939 $prev = $version; 01940 $bits = $this->estimateBitStreamSize($items, $prev); 01941 $version = $this->getMinimumVersion((int)(($bits + 7) / 8), $this->level); 01942 if ($version < 0) { 01943 return -1; 01944 } 01945 } while ($version > $prev); 01946 return $version; 01947 } 01948 01956 protected function lengthOfCode($mode, $version, $bits) { 01957 $payload = $bits - 4 - $this->lengthIndicator($mode, $version); 01958 switch($mode) { 01959 case QR_MODE_NM: { 01960 $chunks = (int)($payload / 10); 01961 $remain = $payload - $chunks * 10; 01962 $size = $chunks * 3; 01963 if ($remain >= 7) { 01964 $size += 2; 01965 } elseif ($remain >= 4) { 01966 $size += 1; 01967 } 01968 break; 01969 } 01970 case QR_MODE_AN: { 01971 $chunks = (int)($payload / 11); 01972 $remain = $payload - $chunks * 11; 01973 $size = $chunks * 2; 01974 if ($remain >= 6) { 01975 ++$size; 01976 } 01977 break; 01978 } 01979 case QR_MODE_8B: { 01980 $size = (int)($payload / 8); 01981 break; 01982 } 01983 case QR_MODE_KJ: { 01984 $size = (int)(($payload / 13) * 2); 01985 break; 01986 } 01987 case QR_MODE_ST: { 01988 $size = (int)($payload / 8); 01989 break; 01990 } 01991 default: { 01992 $size = 0; 01993 break; 01994 } 01995 } 01996 $maxsize = $this->maximumWords($mode, $version); 01997 if ($size < 0) { 01998 $size = 0; 01999 } 02000 if ($size > $maxsize) { 02001 $size = $maxsize; 02002 } 02003 return $size; 02004 } 02005 02011 protected function createBitStream($items) { 02012 $total = 0; 02013 foreach ($items as $key => $item) { 02014 $items[$key] = $this->encodeBitStream($item, $this->version); 02015 $bits = count($items[$key]['bstream']); 02016 $total += $bits; 02017 } 02018 return array($items, $total); 02019 } 02020 02026 protected function convertData($items) { 02027 $ver = $this->estimateVersion($items); 02028 if ($ver > $this->version) { 02029 $this->version = $ver; 02030 } 02031 for (;;) { 02032 $cbs = $this->createBitStream($items); 02033 $items = $cbs[0]; 02034 $bits = $cbs[1]; 02035 if ($bits < 0) { 02036 return -1; 02037 } 02038 $ver = $this->getMinimumVersion((int)(($bits + 7) / 8), $this->level); 02039 if ($ver < 0) { 02040 return -1; 02041 } elseif ($ver > $this->version) { 02042 $this->version = $ver; 02043 } else { 02044 break; 02045 } 02046 } 02047 return $items; 02048 } 02049 02055 protected function appendPaddingBit($bstream) { 02056 if (is_null($bstream)) { 02057 return null; 02058 } 02059 $bits = count($bstream); 02060 $maxwords = $this->getDataLength($this->version, $this->level); 02061 $maxbits = $maxwords * 8; 02062 if ($maxbits == $bits) { 02063 return $bstream; 02064 } 02065 if ($maxbits - $bits < 5) { 02066 return $this->appendNum($bstream, $maxbits - $bits, 0); 02067 } 02068 $bits += 4; 02069 $words = (int)(($bits + 7) / 8); 02070 $padding = array(); 02071 $padding = $this->appendNum($padding, $words * 8 - $bits + 4, 0); 02072 $padlen = $maxwords - $words; 02073 if ($padlen > 0) { 02074 $padbuf = array(); 02075 for ($i=0; $i<$padlen; ++$i) { 02076 $padbuf[$i] = ($i&1)?0x11:0xec; 02077 } 02078 $padding = $this->appendBytes($padding, $padlen, $padbuf); 02079 } 02080 return $this->appendBitstream($bstream, $padding); 02081 } 02082 02088 protected function mergeBitStream($items) { 02089 $items = $this->convertData($items); 02090 if (!is_array($items)) { 02091 return null; 02092 } 02093 $bstream = array(); 02094 foreach ($items as $item) { 02095 $bstream = $this->appendBitstream($bstream, $item['bstream']); 02096 } 02097 return $bstream; 02098 } 02099 02105 protected function getBitStream($items) { 02106 $bstream = $this->mergeBitStream($items); 02107 return $this->appendPaddingBit($bstream); 02108 } 02109 02115 protected function getByteStream($items) { 02116 $bstream = $this->getBitStream($items); 02117 return $this->bitstreamToByte($bstream); 02118 } 02119 02120 // - - - - - - - - - - - - - - - - - - - - - - - - - 02121 02122 // QRbitstream 02123 02129 protected function allocate($setLength) { 02130 return array_fill(0, $setLength, 0); 02131 } 02132 02139 protected function newFromNum($bits, $num) { 02140 $bstream = $this->allocate($bits); 02141 $mask = 1 << ($bits - 1); 02142 for ($i=0; $i<$bits; ++$i) { 02143 if ($num & $mask) { 02144 $bstream[$i] = 1; 02145 } else { 02146 $bstream[$i] = 0; 02147 } 02148 $mask = $mask >> 1; 02149 } 02150 return $bstream; 02151 } 02152 02159 protected function newFromBytes($size, $data) { 02160 $bstream = $this->allocate($size * 8); 02161 $p=0; 02162 for ($i=0; $i<$size; ++$i) { 02163 $mask = 0x80; 02164 for ($j=0; $j<8; ++$j) { 02165 if ($data[$i] & $mask) { 02166 $bstream[$p] = 1; 02167 } else { 02168 $bstream[$p] = 0; 02169 } 02170 $p++; 02171 $mask = $mask >> 1; 02172 } 02173 } 02174 return $bstream; 02175 } 02176 02183 protected function appendBitstream($bitstream, $append) { 02184 if ((!is_array($append)) OR (count($append) == 0)) { 02185 return $bitstream; 02186 } 02187 if (count($bitstream) == 0) { 02188 return $append; 02189 } 02190 return array_values(array_merge($bitstream, $append)); 02191 } 02192 02200 protected function appendNum($bitstream, $bits, $num) { 02201 if ($bits == 0) { 02202 return 0; 02203 } 02204 $b = $this->newFromNum($bits, $num); 02205 return $this->appendBitstream($bitstream, $b); 02206 } 02207 02215 protected function appendBytes($bitstream, $size, $data) { 02216 if ($size == 0) { 02217 return 0; 02218 } 02219 $b = $this->newFromBytes($size, $data); 02220 return $this->appendBitstream($bitstream, $b); 02221 } 02222 02228 protected function bitstreamToByte($bstream) { 02229 if (is_null($bstream)) { 02230 return null; 02231 } 02232 $size = count($bstream); 02233 if ($size == 0) { 02234 return array(); 02235 } 02236 $data = array_fill(0, (int)(($size + 7) / 8), 0); 02237 $bytes = (int)($size / 8); 02238 $p = 0; 02239 for ($i=0; $i<$bytes; $i++) { 02240 $v = 0; 02241 for ($j=0; $j<8; $j++) { 02242 $v = $v << 1; 02243 $v |= $bstream[$p]; 02244 $p++; 02245 } 02246 $data[$i] = $v; 02247 } 02248 if ($size & 7) { 02249 $v = 0; 02250 for ($j=0; $j<($size & 7); $j++) { 02251 $v = $v << 1; 02252 $v |= $bstream[$p]; 02253 $p++; 02254 } 02255 $data[$bytes] = $v; 02256 } 02257 return $data; 02258 } 02259 02260 // - - - - - - - - - - - - - - - - - - - - - - - - - 02261 02262 // QRspec 02263 02273 protected function qrstrset($srctab, $x, $y, $repl, $replLen=false) { 02274 $srctab[$y] = substr_replace($srctab[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl)); 02275 return $srctab; 02276 } 02277 02284 protected function getDataLength($version, $level) { 02285 return $this->capacity[$version][QRCAP_WORDS] - $this->capacity[$version][QRCAP_EC][$level]; 02286 } 02287 02294 protected function getECCLength($version, $level){ 02295 return $this->capacity[$version][QRCAP_EC][$level]; 02296 } 02297 02303 protected function getWidth($version) { 02304 return $this->capacity[$version][QRCAP_WIDTH]; 02305 } 02306 02312 protected function getRemainder($version) { 02313 return $this->capacity[$version][QRCAP_REMINDER]; 02314 } 02315 02322 protected function getMinimumVersion($size, $level) { 02323 for ($i=1; $i <= QRSPEC_VERSION_MAX; ++$i) { 02324 $words = $this->capacity[$i][QRCAP_WORDS] - $this->capacity[$i][QRCAP_EC][$level]; 02325 if ($words >= $size) { 02326 return $i; 02327 } 02328 } 02329 return -1; 02330 } 02331 02338 protected function lengthIndicator($mode, $version) { 02339 if ($mode == QR_MODE_ST) { 02340 return 0; 02341 } 02342 if ($version <= 9) { 02343 $l = 0; 02344 } elseif ($version <= 26) { 02345 $l = 1; 02346 } else { 02347 $l = 2; 02348 } 02349 return $this->lengthTableBits[$mode][$l]; 02350 } 02351 02358 protected function maximumWords($mode, $version) { 02359 if ($mode == QR_MODE_ST) { 02360 return 3; 02361 } 02362 if ($version <= 9) { 02363 $l = 0; 02364 } else if ($version <= 26) { 02365 $l = 1; 02366 } else { 02367 $l = 2; 02368 } 02369 $bits = $this->lengthTableBits[$mode][$l]; 02370 $words = (1 << $bits) - 1; 02371 if ($mode == QR_MODE_KJ) { 02372 $words *= 2; // the number of bytes is required 02373 } 02374 return $words; 02375 } 02376 02384 protected function getEccSpec($version, $level, $spec) { 02385 if (count($spec) < 5) { 02386 $spec = array(0, 0, 0, 0, 0); 02387 } 02388 $b1 = $this->eccTable[$version][$level][0]; 02389 $b2 = $this->eccTable[$version][$level][1]; 02390 $data = $this->getDataLength($version, $level); 02391 $ecc = $this->getECCLength($version, $level); 02392 if ($b2 == 0) { 02393 $spec[0] = $b1; 02394 $spec[1] = (int)($data / $b1); 02395 $spec[2] = (int)($ecc / $b1); 02396 $spec[3] = 0; 02397 $spec[4] = 0; 02398 } else { 02399 $spec[0] = $b1; 02400 $spec[1] = (int)($data / ($b1 + $b2)); 02401 $spec[2] = (int)($ecc / ($b1 + $b2)); 02402 $spec[3] = $b2; 02403 $spec[4] = $spec[1] + 1; 02404 } 02405 return $spec; 02406 } 02407 02415 protected function putAlignmentMarker($frame, $ox, $oy) { 02416 $finder = array( 02417 "\xa1\xa1\xa1\xa1\xa1", 02418 "\xa1\xa0\xa0\xa0\xa1", 02419 "\xa1\xa0\xa1\xa0\xa1", 02420 "\xa1\xa0\xa0\xa0\xa1", 02421 "\xa1\xa1\xa1\xa1\xa1" 02422 ); 02423 $yStart = $oy - 2; 02424 $xStart = $ox - 2; 02425 for ($y=0; $y < 5; $y++) { 02426 $frame = $this->qrstrset($frame, $xStart, $yStart+$y, $finder[$y]); 02427 } 02428 return $frame; 02429 } 02430 02438 protected function putAlignmentPattern($version, $frame, $width) { 02439 if ($version < 2) { 02440 return $frame; 02441 } 02442 $d = $this->alignmentPattern[$version][1] - $this->alignmentPattern[$version][0]; 02443 if ($d < 0) { 02444 $w = 2; 02445 } else { 02446 $w = (int)(($width - $this->alignmentPattern[$version][0]) / $d + 2); 02447 } 02448 if ($w * $w - 3 == 1) { 02449 $x = $this->alignmentPattern[$version][0]; 02450 $y = $this->alignmentPattern[$version][0]; 02451 $frame = $this->putAlignmentMarker($frame, $x, $y); 02452 return $frame; 02453 } 02454 $cx = $this->alignmentPattern[$version][0]; 02455 $wo = $w - 1; 02456 for ($x=1; $x < $wo; ++$x) { 02457 $frame = $this->putAlignmentMarker($frame, 6, $cx); 02458 $frame = $this->putAlignmentMarker($frame, $cx, 6); 02459 $cx += $d; 02460 } 02461 $cy = $this->alignmentPattern[$version][0]; 02462 for ($y=0; $y < $wo; ++$y) { 02463 $cx = $this->alignmentPattern[$version][0]; 02464 for ($x=0; $x < $wo; ++$x) { 02465 $frame = $this->putAlignmentMarker($frame, $cx, $cy); 02466 $cx += $d; 02467 } 02468 $cy += $d; 02469 } 02470 return $frame; 02471 } 02472 02478 protected function getVersionPattern($version) { 02479 if (($version < 7) OR ($version > QRSPEC_VERSION_MAX)) { 02480 return 0; 02481 } 02482 return $this->versionPattern[($version - 7)]; 02483 } 02484 02491 protected function getFormatInfo($mask, $level) { 02492 if (($mask < 0) OR ($mask > 7)) { 02493 return 0; 02494 } 02495 if (($level < 0) OR ($level > 3)) { 02496 return 0; 02497 } 02498 return $this->formatInfo[$level][$mask]; 02499 } 02500 02508 protected function putFinderPattern($frame, $ox, $oy) { 02509 $finder = array( 02510 "\xc1\xc1\xc1\xc1\xc1\xc1\xc1", 02511 "\xc1\xc0\xc0\xc0\xc0\xc0\xc1", 02512 "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", 02513 "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", 02514 "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", 02515 "\xc1\xc0\xc0\xc0\xc0\xc0\xc1", 02516 "\xc1\xc1\xc1\xc1\xc1\xc1\xc1" 02517 ); 02518 for ($y=0; $y < 7; $y++) { 02519 $frame = $this->qrstrset($frame, $ox, ($oy + $y), $finder[$y]); 02520 } 02521 return $frame; 02522 } 02523 02529 protected function createFrame($version) { 02530 $width = $this->capacity[$version][QRCAP_WIDTH]; 02531 $frameLine = str_repeat ("\0", $width); 02532 $frame = array_fill(0, $width, $frameLine); 02533 // Finder pattern 02534 $frame = $this->putFinderPattern($frame, 0, 0); 02535 $frame = $this->putFinderPattern($frame, $width - 7, 0); 02536 $frame = $this->putFinderPattern($frame, 0, $width - 7); 02537 // Separator 02538 $yOffset = $width - 7; 02539 for ($y=0; $y < 7; ++$y) { 02540 $frame[$y][7] = "\xc0"; 02541 $frame[$y][$width - 8] = "\xc0"; 02542 $frame[$yOffset][7] = "\xc0"; 02543 ++$yOffset; 02544 } 02545 $setPattern = str_repeat("\xc0", 8); 02546 $frame = $this->qrstrset($frame, 0, 7, $setPattern); 02547 $frame = $this->qrstrset($frame, $width-8, 7, $setPattern); 02548 $frame = $this->qrstrset($frame, 0, $width - 8, $setPattern); 02549 // Format info 02550 $setPattern = str_repeat("\x84", 9); 02551 $frame = $this->qrstrset($frame, 0, 8, $setPattern); 02552 $frame = $this->qrstrset($frame, $width - 8, 8, $setPattern, 8); 02553 $yOffset = $width - 8; 02554 for ($y=0; $y < 8; ++$y,++$yOffset) { 02555 $frame[$y][8] = "\x84"; 02556 $frame[$yOffset][8] = "\x84"; 02557 } 02558 // Timing pattern 02559 $wo = $width - 15; 02560 for ($i=1; $i < $wo; ++$i) { 02561 $frame[6][7+$i] = chr(0x90 | ($i & 1)); 02562 $frame[7+$i][6] = chr(0x90 | ($i & 1)); 02563 } 02564 // Alignment pattern 02565 $frame = $this->putAlignmentPattern($version, $frame, $width); 02566 // Version information 02567 if ($version >= 7) { 02568 $vinf = $this->getVersionPattern($version); 02569 $v = $vinf; 02570 for ($x=0; $x<6; ++$x) { 02571 for ($y=0; $y<3; ++$y) { 02572 $frame[($width - 11)+$y][$x] = chr(0x88 | ($v & 1)); 02573 $v = $v >> 1; 02574 } 02575 } 02576 $v = $vinf; 02577 for ($y=0; $y<6; ++$y) { 02578 for ($x=0; $x<3; ++$x) { 02579 $frame[$y][$x+($width - 11)] = chr(0x88 | ($v & 1)); 02580 $v = $v >> 1; 02581 } 02582 } 02583 } 02584 // and a little bit... 02585 $frame[$width - 8][8] = "\x81"; 02586 return $frame; 02587 } 02588 02594 protected function newFrame($version) { 02595 if (($version < 1) OR ($version > QRSPEC_VERSION_MAX)) { 02596 return NULL; 02597 } 02598 if (!isset($this->frames[$version])) { 02599 $this->frames[$version] = $this->createFrame($version); 02600 } 02601 if (is_null($this->frames[$version])) { 02602 return NULL; 02603 } 02604 return $this->frames[$version]; 02605 } 02606 02612 protected function rsBlockNum($spec) { 02613 return ($spec[0] + $spec[3]); 02614 } 02615 02621 protected function rsBlockNum1($spec) { 02622 return $spec[0]; 02623 } 02624 02630 protected function rsDataCodes1($spec) { 02631 return $spec[1]; 02632 } 02633 02639 protected function rsEccCodes1($spec) { 02640 return $spec[2]; 02641 } 02642 02648 protected function rsBlockNum2($spec) { 02649 return $spec[3]; 02650 } 02651 02657 protected function rsDataCodes2($spec) { 02658 return $spec[4]; 02659 } 02660 02666 protected function rsEccCodes2($spec) { 02667 return $spec[2]; 02668 } 02669 02675 protected function rsDataLength($spec) { 02676 return ($spec[0] * $spec[1]) + ($spec[3] * $spec[4]); 02677 } 02678 02684 protected function rsEccLength($spec) { 02685 return ($spec[0] + $spec[3]) * $spec[2]; 02686 } 02687 02688 // - - - - - - - - - - - - - - - - - - - - - - - - - 02689 02690 // QRrs 02691 02702 protected function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) { 02703 foreach ($this->rsitems as $rs) { 02704 if (($rs['pad'] != $pad) OR ($rs['nroots'] != $nroots) OR ($rs['mm'] != $symsize) 02705 OR ($rs['gfpoly'] != $gfpoly) OR ($rs['fcr'] != $fcr) OR ($rs['prim'] != $prim)) { 02706 continue; 02707 } 02708 return $rs; 02709 } 02710 $rs = $this->init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad); 02711 array_unshift($this->rsitems, $rs); 02712 return $rs; 02713 } 02714 02715 // - - - - - - - - - - - - - - - - - - - - - - - - - 02716 02717 // QRrsItem 02718 02725 protected function modnn($rs, $x) { 02726 while ($x >= $rs['nn']) { 02727 $x -= $rs['nn']; 02728 $x = ($x >> $rs['mm']) + ($x & $rs['nn']); 02729 } 02730 return $x; 02731 } 02732 02743 protected function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) { 02744 // Based on Reed solomon encoder by Phil Karn, KA9Q (GNU-LGPLv2) 02745 $rs = null; 02746 // Check parameter ranges 02747 if (($symsize < 0) OR ($symsize > 8)) { 02748 return $rs; 02749 } 02750 if (($fcr < 0) OR ($fcr >= (1<<$symsize))) { 02751 return $rs; 02752 } 02753 if (($prim <= 0) OR ($prim >= (1<<$symsize))) { 02754 return $rs; 02755 } 02756 if (($nroots < 0) OR ($nroots >= (1<<$symsize))) { 02757 return $rs; 02758 } 02759 if (($pad < 0) OR ($pad >= ((1<<$symsize) -1 - $nroots))) { 02760 return $rs; 02761 } 02762 $rs = array(); 02763 $rs['mm'] = $symsize; 02764 $rs['nn'] = (1 << $symsize) - 1; 02765 $rs['pad'] = $pad; 02766 $rs['alpha_to'] = array_fill(0, ($rs['nn'] + 1), 0); 02767 $rs['index_of'] = array_fill(0, ($rs['nn'] + 1), 0); 02768 // PHP style macro replacement ;) 02769 $NN =& $rs['nn']; 02770 $A0 =& $NN; 02771 // Generate Galois field lookup tables 02772 $rs['index_of'][0] = $A0; // log(zero) = -inf 02773 $rs['alpha_to'][$A0] = 0; // alpha**-inf = 0 02774 $sr = 1; 02775 for ($i=0; $i<$rs['nn']; ++$i) { 02776 $rs['index_of'][$sr] = $i; 02777 $rs['alpha_to'][$i] = $sr; 02778 $sr <<= 1; 02779 if ($sr & (1 << $symsize)) { 02780 $sr ^= $gfpoly; 02781 } 02782 $sr &= $rs['nn']; 02783 } 02784 if ($sr != 1) { 02785 // field generator polynomial is not primitive! 02786 return NULL; 02787 } 02788 // Form RS code generator polynomial from its roots 02789 $rs['genpoly'] = array_fill(0, ($nroots + 1), 0); 02790 $rs['fcr'] = $fcr; 02791 $rs['prim'] = $prim; 02792 $rs['nroots'] = $nroots; 02793 $rs['gfpoly'] = $gfpoly; 02794 // Find prim-th root of 1, used in decoding 02795 for ($iprim=1; ($iprim % $prim) != 0; $iprim += $rs['nn']) { 02796 ; // intentional empty-body loop! 02797 } 02798 $rs['iprim'] = (int)($iprim / $prim); 02799 $rs['genpoly'][0] = 1; 02800 for ($i = 0,$root=$fcr*$prim; $i < $nroots; $i++, $root += $prim) { 02801 $rs['genpoly'][$i+1] = 1; 02802 // Multiply rs->genpoly[] by @**(root + x) 02803 for ($j = $i; $j > 0; --$j) { 02804 if ($rs['genpoly'][$j] != 0) { 02805 $rs['genpoly'][$j] = $rs['genpoly'][$j-1] ^ $rs['alpha_to'][$this->modnn($rs, $rs['index_of'][$rs['genpoly'][$j]] + $root)]; 02806 } else { 02807 $rs['genpoly'][$j] = $rs['genpoly'][$j-1]; 02808 } 02809 } 02810 // rs->genpoly[0] can never be zero 02811 $rs['genpoly'][0] = $rs['alpha_to'][$this->modnn($rs, $rs['index_of'][$rs['genpoly'][0]] + $root)]; 02812 } 02813 // convert rs->genpoly[] to index form for quicker encoding 02814 for ($i = 0; $i <= $nroots; ++$i) { 02815 $rs['genpoly'][$i] = $rs['index_of'][$rs['genpoly'][$i]]; 02816 } 02817 return $rs; 02818 } 02819 02827 protected function encode_rs_char($rs, $data, $parity) { 02828 $MM =& $rs['mm']; // bits per symbol 02829 $NN =& $rs['nn']; // the total number of symbols in a RS block 02830 $ALPHA_TO =& $rs['alpha_to']; // the address of an array of NN elements to convert Galois field elements in index (log) form to polynomial form 02831 $INDEX_OF =& $rs['index_of']; // the address of an array of NN elements to convert Galois field elements in polynomial form to index (log) form 02832 $GENPOLY =& $rs['genpoly']; // an array of NROOTS+1 elements containing the generator polynomial in index form 02833 $NROOTS =& $rs['nroots']; // the number of roots in the RS code generator polynomial, which is the same as the number of parity symbols in a block 02834 $FCR =& $rs['fcr']; // first consecutive root, index form 02835 $PRIM =& $rs['prim']; // primitive element, index form 02836 $IPRIM =& $rs['iprim']; // prim-th root of 1, index form 02837 $PAD =& $rs['pad']; // the number of pad symbols in a block 02838 $A0 =& $NN; 02839 $parity = array_fill(0, $NROOTS, 0); 02840 for ($i=0; $i < ($NN - $NROOTS - $PAD); $i++) { 02841 $feedback = $INDEX_OF[$data[$i] ^ $parity[0]]; 02842 if ($feedback != $A0) { 02843 // feedback term is non-zero 02844 // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must 02845 // always be for the polynomials constructed by init_rs() 02846 $feedback = $this->modnn($rs, $NN - $GENPOLY[$NROOTS] + $feedback); 02847 for ($j=1; $j < $NROOTS; ++$j) { 02848 $parity[$j] ^= $ALPHA_TO[$this->modnn($rs, $feedback + $GENPOLY[($NROOTS - $j)])]; 02849 } 02850 } 02851 // Shift 02852 array_shift($parity); 02853 if ($feedback != $A0) { 02854 array_push($parity, $ALPHA_TO[$this->modnn($rs, $feedback + $GENPOLY[0])]); 02855 } else { 02856 array_push($parity, 0); 02857 } 02858 } 02859 return $parity; 02860 } 02861 02862 } // end QRcode class 02863 02864 //============================================================+ 02865 // END OF FILE 02866 //============================================================+