@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at recaptime-dev/main 5015 lines 192 kB view raw
1<?php 2 3/* 4 * PHP QR Code encoder 5 * 6 * This file contains MERGED version of PHP QR Code library. 7 * It was auto-generated from full version for your convenience. 8 * 9 * This merged version was configured to not requre any external files, 10 * with disabled cache, error loging and weker but faster mask matching. 11 * If you need tune it up please use non-merged version. 12 * 13 * For full version, documentation, examples of use please visit: 14 * 15 * http://phpqrcode.sourceforge.net/ 16 * https://sourceforge.net/projects/phpqrcode/ 17 * 18 * PHP QR Code is distributed under LGPL 3 19 * Copyright (C) 2010 Dominik Dzienia <deltalab at poczta dot fm> 20 * 21 * This library is free software; you can redistribute it and/or 22 * modify it under the terms of the GNU Lesser General Public 23 * License as published by the Free Software Foundation; either 24 * version 3 of the License, or any later version. 25 * 26 * This library is distributed in the hope that it will be useful, 27 * but WITHOUT ANY WARRANTY; without even the implied warranty of 28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 29 * Lesser General Public License for more details. 30 * 31 * You should have received a copy of the GNU Lesser General Public 32 * License along with this library; if not, write to the Free Software 33 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 34 */ 35 36 37 38/* 39 * Version: 1.9.9 40 * Build: 20130526 41 */ 42 43 44 45//---- qrconst.php ----------------------------- 46 47 48 49 50 51/* 52 * PHP QR Code encoder 53 * 54 * Common constants 55 * 56 * Based on libqrencode C library distributed under LGPL 2.1 57 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net> 58 * 59 * PHP QR Code is distributed under LGPL 3 60 * Copyright (C) 2010-2013 Dominik Dzienia <deltalab at poczta dot fm> 61 * 62 * This library is free software; you can redistribute it and/or 63 * modify it under the terms of the GNU Lesser General Public 64 * License as published by the Free Software Foundation; either 65 * version 3 of the License, or any later version. 66 * 67 * This library is distributed in the hope that it will be useful, 68 * but WITHOUT ANY WARRANTY; without even the implied warranty of 69 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 70 * Lesser General Public License for more details. 71 * 72 * You should have received a copy of the GNU Lesser General Public 73 * License along with this library; if not, write to the Free Software 74 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 75 */ 76 77 78 /** \defgroup QR_CONST Global Constants 79 Constant used globally for function arguments. 80 Make PHP calls a little bit more clear, in place of missing (in dynamicaly typed language) enum types. 81 * @{ 82 */ 83 84 /** @name QR-Code Encoding Modes */ 85 /** @{ */ 86 87 /** null encoding, used when no encoding was speciffied yet */ 88 define('QR_MODE_NUL', -1); 89 /** Numerical encoding, only numbers (0-9) */ 90 define('QR_MODE_NUM', 0); 91 /** AlphaNumerical encoding, numbers (0-9) uppercase text (A-Z) and few special characters (space, $, %, *, +, -, ., /, :) */ 92 define('QR_MODE_AN', 1); 93 /** 8-bit encoding, raw 8 bit encoding */ 94 define('QR_MODE_8', 2); 95 /** Kanji encoding */ 96 define('QR_MODE_KANJI', 3); 97 /** Structure, internal encoding for structure-related data */ 98 define('QR_MODE_STRUCTURE', 4); 99 /**@}*/ 100 101 /** @name QR-Code Levels of Error Correction 102 Constants speciffy ECC level from lowest __L__ to the highest __H__. 103 Higher levels are recomended for Outdoor-presented codes, but generates bigger codes. 104 */ 105 /** @{*/ 106 /** ~7% of codewords can be restored */ 107 define('QR_ECLEVEL_L', 0); 108 /** ~15% of codewords can be restored */ 109 define('QR_ECLEVEL_M', 1); 110 /** ~25% of codewords can be restored */ 111 define('QR_ECLEVEL_Q', 2); 112 /** ~30% of codewords can be restored */ 113 define('QR_ECLEVEL_H', 3); 114 /** @}*/ 115 116 /** @name QR-Code Supported output formats */ 117 /** @{*/ 118 define('QR_FORMAT_TEXT', 0); 119 define('QR_FORMAT_PNG', 1); 120 /** @}*/ 121 122 /** @}*/ 123 124 125 126 127//---- merged_config.php ----------------------------- 128 129 130 131 132/* 133 * PHP QR Code encoder 134 * 135 * Config file, tuned-up for merged verion 136 */ 137 138 define('QR_CACHEABLE', false); // use cache - more disk reads but less CPU power, masks and format templates are stored there 139 define('QR_CACHE_DIR', false); // used when QR_CACHEABLE === true 140 define('QR_LOG_DIR', false); // default error logs dir 141 142 define('QR_FIND_BEST_MASK', true); // if true, estimates best mask (spec. default, but extremally slow; set to false to significant performance boost but (propably) worst quality code 143 define('QR_FIND_FROM_RANDOM', 2); // if false, checks all masks available, otherwise value tells count of masks need to be checked, mask id are got randomly 144 define('QR_DEFAULT_MASK', 2); // when QR_FIND_BEST_MASK === false 145 146 define('QR_PNG_MAXIMUM_SIZE', 1024); // maximum allowed png image width (in pixels), tune to make sure GD and PHP can handle such big images 147 148 149 150 151//---- qrtools.php ----------------------------- 152 153 154 155 156/* 157 * PHP QR Code encoder 158 * 159 * Toolset, handy and debug utilites. 160 * 161 * PHP QR Code is distributed under LGPL 3 162 * Copyright (C) 2010-2013 Dominik Dzienia <deltalab at poczta dot fm> 163 * 164 * This library is free software; you can redistribute it and/or 165 * modify it under the terms of the GNU Lesser General Public 166 * License as published by the Free Software Foundation; either 167 * version 3 of the License, or any later version. 168 * 169 * This library is distributed in the hope that it will be useful, 170 * but WITHOUT ANY WARRANTY; without even the implied warranty of 171 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 172 * Lesser General Public License for more details. 173 * 174 * You should have received a copy of the GNU Lesser General Public 175 * License along with this library; if not, write to the Free Software 176 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 177 */ 178 179 /** @addtogroup CoreGroup */ 180 /** @{ */ 181 182 /** Helper class */ 183 class QRtools { 184 185 public static $timeBenchmarkStarted = false; 186 187 //---------------------------------------------------------------------- 188 public static function binarize($frame) 189 { 190 $len = count($frame); 191 foreach ($frame as &$frameLine) { 192 193 for($i=0; $i<$len; $i++) { 194 $frameLine[$i] = (ord($frameLine[$i])&1)?'1':'0'; 195 } 196 } 197 198 return $frame; 199 } 200 201 //---------------------------------------------------------------------- 202 public static function tcpdfBarcodeArray($code, $mode = 'QR,L', $tcPdfVersion = '4.5.037') 203 { 204 $barcode_array = array(); 205 206 if (!is_array($mode)) 207 $mode = explode(',', $mode); 208 209 $eccLevel = 'L'; 210 211 if (count($mode) > 1) { 212 $eccLevel = $mode[1]; 213 } 214 215 $qrTab = QRcode::text($code, false, $eccLevel); 216 $size = count($qrTab); 217 218 $barcode_array['num_rows'] = $size; 219 $barcode_array['num_cols'] = $size; 220 $barcode_array['bcode'] = array(); 221 222 foreach ($qrTab as $line) { 223 $arrAdd = array(); 224 foreach(str_split($line) as $char) 225 $arrAdd[] = ($char=='1')?1:0; 226 $barcode_array['bcode'][] = $arrAdd; 227 } 228 229 return $barcode_array; 230 } 231 232 //---------------------------------------------------------------------- 233 public static function clearCache() 234 { 235 self::$frames = array(); 236 } 237 238 //---------------------------------------------------------------------- 239 public static function buildCache() 240 { 241 QRtools::markTime('before_build_cache'); 242 243 $mask = new QRmask(); 244 for ($a=1; $a <= QRSPEC_VERSION_MAX; $a++) { 245 $frame = QRspec::newFrame($a); 246 if (QR_IMAGE) { 247 $fileName = QR_CACHE_DIR.'frame_'.$a.'.png'; 248 QRimage::png(self::binarize($frame), $fileName, 1, 0); 249 } 250 251 $width = count($frame); 252 $bitMask = array_fill(0, $width, array_fill(0, $width, 0)); 253 for ($maskNo=0; $maskNo<8; $maskNo++) 254 $mask->makeMaskNo($maskNo, $width, $frame, $bitMask, true); 255 } 256 257 QRtools::markTime('after_build_cache'); 258 } 259 260 //---------------------------------------------------------------------- 261 public static function log($outfile, $err) 262 { 263 if (QR_LOG_DIR !== false) { 264 if ($err != '') { 265 if ($outfile !== false) { 266 file_put_contents(QR_LOG_DIR.basename($outfile).'-errors.txt', date('Y-m-d H:i:s').': '.$err, FILE_APPEND); 267 } else { 268 file_put_contents(QR_LOG_DIR.'errors.txt', date('Y-m-d H:i:s').': '.$err, FILE_APPEND); 269 } 270 } 271 } 272 } 273 274 //---------------------------------------------------------------------- 275 public static function dumpMask($frame) 276 { 277 $width = count($frame); 278 for($y=0;$y<$width;$y++) { 279 for($x=0;$x<$width;$x++) { 280 echo ord($frame[$y][$x]).','; 281 } 282 } 283 } 284 285 //---------------------------------------------------------------------- 286 public static function startTimeBenchmark() 287 { 288 $GLOBALS['qr_time_bench'] = array(); 289 self::markTime('start'); 290 } 291 292 //---------------------------------------------------------------------- 293 public static function markTime($markerId) 294 { 295 list($usec, $sec) = explode(" ", microtime()); 296 $time = ((float)$usec + (float)$sec); 297 298 if (!isset($GLOBALS['qr_time_bench'])) 299 $GLOBALS['qr_time_bench'] = array(); 300 301 $GLOBALS['qr_time_bench'][$markerId] = $time; 302 303 if ((!self::$timeBenchmarkStarted)&&($markerId != 'start')) { 304 self::$timeBenchmarkStarted = true; 305 $GLOBALS['qr_time_bench']['start'] = $time; 306 } 307 } 308 309 //---------------------------------------------------------------------- 310 public static function timeBenchmark() 311 { 312 self::markTime('finish'); 313 314 $lastTime = 0; 315 $startTime = 0; 316 $p = 0; 317 318 echo '<table cellpadding="3" cellspacing="1"> 319 <thead><tr style="border-bottom:1px solid silver"><td colspan="2" style="text-align:center">BENCHMARK</td></tr></thead> 320 <tbody>'; 321 322 foreach($GLOBALS['qr_time_bench'] as $markerId=>$thisTime) { 323 if ($p > 0) { 324 echo '<tr><th style="text-align:right">till '.$markerId.': </th><td>'.number_format($thisTime-$lastTime, 6).'s</td></tr>'; 325 } else { 326 $startTime = $thisTime; 327 } 328 329 $p++; 330 $lastTime = $thisTime; 331 } 332 333 echo '</tbody><tfoot> 334 <tr style="border-top:2px solid black"><th style="text-align:right">TOTAL: </th><td>'.number_format($lastTime-$startTime, 6).'s</td></tr> 335 </tfoot> 336 </table>'; 337 } 338 339 } 340 341 /** @}*/ 342 343 //########################################################################## 344 345 346 347 348//---- qrspec.php ----------------------------- 349 350 351 352 353/* 354 * PHP QR Code encoder 355 * 356 * QR Code specifications 357 * 358 * Based on libqrencode C library distributed under LGPL 2.1 359 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net> 360 * 361 * PHP QR Code is distributed under LGPL 3 362 * Copyright (C) 2010-2013 Dominik Dzienia <deltalab at poczta dot fm> 363 * 364 * The following data / specifications are taken from 365 * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004) 366 * or 367 * "Automatic identification and data capture techniques -- 368 * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006) 369 * 370 * This library is free software; you can redistribute it and/or 371 * modify it under the terms of the GNU Lesser General Public 372 * License as published by the Free Software Foundation; either 373 * version 3 of the License, or any later version. 374 * 375 * This library is distributed in the hope that it will be useful, 376 * but WITHOUT ANY WARRANTY; without even the implied warranty of 377 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 378 * Lesser General Public License for more details. 379 * 380 * You should have received a copy of the GNU Lesser General Public 381 * License along with this library; if not, write to the Free Software 382 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 383 */ 384 385 /** Maximal Version no allowed by QR-Code spec */ 386 define('QRSPEC_VERSION_MAX', 40); 387 /** Maximal Code size in pixels allowed by QR-Code spec */ 388 define('QRSPEC_WIDTH_MAX', 177); 389 390 define('QRCAP_WIDTH', 0); 391 define('QRCAP_WORDS', 1); 392 define('QRCAP_REMINDER', 2); 393 define('QRCAP_EC', 3); 394 395 /** @addtogroup CoreGroup */ 396 /** @{ */ 397 398 /** QR-Code specification and Code Frame handling. 399 Contains code specifications, calculates base frame, code structure 400 and base properties 401 */ 402 class QRspec { 403 404 /** Array specifying properties of QR-Code "versions". 405 Each so-called version has specified code area size and capacity. 406 There are 40 versions, this table specifies for each of them four parameters: 407 408 - Integer __QRCAP_WIDTH__ - size of code in pixels 409 - Integer __QRCAP_WORDS__ - code capacity, in words 410 - Integer __QRCAP_REMINDER__ - remainder words 411 - Array of Integers __QRCAP_EC__ - RS correction code count for each of four ECC levels 412 \hideinitializer 413 */ 414 public static $capacity = array( 415 array( 0, 0, 0, array( 0, 0, 0, 0)), 416 array( 21, 26, 0, array( 7, 10, 13, 17)), // 1 417 array( 25, 44, 7, array( 10, 16, 22, 28)), 418 array( 29, 70, 7, array( 15, 26, 36, 44)), 419 array( 33, 100, 7, array( 20, 36, 52, 64)), 420 array( 37, 134, 7, array( 26, 48, 72, 88)), // 5 421 array( 41, 172, 7, array( 36, 64, 96, 112)), 422 array( 45, 196, 0, array( 40, 72, 108, 130)), 423 array( 49, 242, 0, array( 48, 88, 132, 156)), 424 array( 53, 292, 0, array( 60, 110, 160, 192)), 425 array( 57, 346, 0, array( 72, 130, 192, 224)), //10 426 array( 61, 404, 0, array( 80, 150, 224, 264)), 427 array( 65, 466, 0, array( 96, 176, 260, 308)), 428 array( 69, 532, 0, array( 104, 198, 288, 352)), 429 array( 73, 581, 3, array( 120, 216, 320, 384)), 430 array( 77, 655, 3, array( 132, 240, 360, 432)), //15 431 array( 81, 733, 3, array( 144, 280, 408, 480)), 432 array( 85, 815, 3, array( 168, 308, 448, 532)), 433 array( 89, 901, 3, array( 180, 338, 504, 588)), 434 array( 93, 991, 3, array( 196, 364, 546, 650)), 435 array( 97, 1085, 3, array( 224, 416, 600, 700)), //20 436 array(101, 1156, 4, array( 224, 442, 644, 750)), 437 array(105, 1258, 4, array( 252, 476, 690, 816)), 438 array(109, 1364, 4, array( 270, 504, 750, 900)), 439 array(113, 1474, 4, array( 300, 560, 810, 960)), 440 array(117, 1588, 4, array( 312, 588, 870, 1050)), //25 441 array(121, 1706, 4, array( 336, 644, 952, 1110)), 442 array(125, 1828, 4, array( 360, 700, 1020, 1200)), 443 array(129, 1921, 3, array( 390, 728, 1050, 1260)), 444 array(133, 2051, 3, array( 420, 784, 1140, 1350)), 445 array(137, 2185, 3, array( 450, 812, 1200, 1440)), //30 446 array(141, 2323, 3, array( 480, 868, 1290, 1530)), 447 array(145, 2465, 3, array( 510, 924, 1350, 1620)), 448 array(149, 2611, 3, array( 540, 980, 1440, 1710)), 449 array(153, 2761, 3, array( 570, 1036, 1530, 1800)), 450 array(157, 2876, 0, array( 570, 1064, 1590, 1890)), //35 451 array(161, 3034, 0, array( 600, 1120, 1680, 1980)), 452 array(165, 3196, 0, array( 630, 1204, 1770, 2100)), 453 array(169, 3362, 0, array( 660, 1260, 1860, 2220)), 454 array(173, 3532, 0, array( 720, 1316, 1950, 2310)), 455 array(177, 3706, 0, array( 750, 1372, 2040, 2430)) //40 456 ); 457 458 //---------------------------------------------------------------------- 459 /** Calculates data length for specified code configuration. 460 @param Integer $version Code version 461 @param Integer $level ECC level 462 @returns Code data capacity 463 */ 464 public static function getDataLength($version, $level) 465 { 466 return self::$capacity[$version][QRCAP_WORDS] - self::$capacity[$version][QRCAP_EC][$level]; 467 } 468 469 //---------------------------------------------------------------------- 470 /** Calculates count of Error Correction Codes for specified code configuration. 471 @param Integer $version Code version 472 @param Integer $level ECC level 473 @returns ECC code count 474 */ 475 public static function getECCLength($version, $level) 476 { 477 return self::$capacity[$version][QRCAP_EC][$level]; 478 } 479 480 //---------------------------------------------------------------------- 481 /** Gets pixel width of code. 482 @param Integer $version Code version 483 @returns Code width, in pixels 484 */ 485 public static function getWidth($version) 486 { 487 return self::$capacity[$version][QRCAP_WIDTH]; 488 } 489 490 //---------------------------------------------------------------------- 491 /** Gets reminder chars length. 492 @param Integer $version Code version 493 @returns Reminder length 494 */ 495 public static function getRemainder($version) 496 { 497 return self::$capacity[$version][QRCAP_REMINDER]; 498 } 499 500 //---------------------------------------------------------------------- 501 /** Finds minimal code version capable of hosting specified data length. 502 @param Integer $size amount of raw data 503 @param Integer $level ECC level 504 @returns code version capable of hosting specified amount of data at specified ECC level 505 */ 506 public static function getMinimumVersion($size, $level) 507 { 508 509 for($i=1; $i<= QRSPEC_VERSION_MAX; $i++) { 510 $words = self::$capacity[$i][QRCAP_WORDS] - self::$capacity[$i][QRCAP_EC][$level]; 511 if($words >= $size) 512 return $i; 513 } 514 515 return -1; 516 } 517 518 //###################################################################### 519 520 /** Length bits Table. 521 \hideinitializer 522 */ 523 public static $lengthTableBits = array( 524 array(10, 12, 14), 525 array( 9, 11, 13), 526 array( 8, 16, 16), 527 array( 8, 10, 12) 528 ); 529 530 //---------------------------------------------------------------------- 531 public static function lengthIndicator($mode, $version) 532 { 533 if ($mode == QR_MODE_STRUCTURE) 534 return 0; 535 536 if ($version <= 9) { 537 $l = 0; 538 } else if ($version <= 26) { 539 $l = 1; 540 } else { 541 $l = 2; 542 } 543 544 return self::$lengthTableBits[$mode][$l]; 545 } 546 547 //---------------------------------------------------------------------- 548 public static function maximumWords($mode, $version) 549 { 550 if($mode == QR_MODE_STRUCTURE) 551 return 3; 552 553 if($version <= 9) { 554 $l = 0; 555 } else if($version <= 26) { 556 $l = 1; 557 } else { 558 $l = 2; 559 } 560 561 $bits = self::$lengthTableBits[$mode][$l]; 562 $words = (1 << $bits) - 1; 563 564 if($mode == QR_MODE_KANJI) { 565 $words *= 2; // the number of bytes is required 566 } 567 568 return $words; 569 } 570 571 // Error correction code ----------------------------------------------- 572 /** Table of the error correction code (Reed-Solomon block). 573 @see Table 12-16 (pp.30-36), JIS X0510:2004. 574 \hideinitializer 575 */ 576 577 public static $eccTable = array( 578 array(array( 0, 0), array( 0, 0), array( 0, 0), array( 0, 0)), 579 array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)), // 1 580 array(array( 1, 0), array( 1, 0), array( 1, 0), array( 1, 0)), 581 array(array( 1, 0), array( 1, 0), array( 2, 0), array( 2, 0)), 582 array(array( 1, 0), array( 2, 0), array( 2, 0), array( 4, 0)), 583 array(array( 1, 0), array( 2, 0), array( 2, 2), array( 2, 2)), // 5 584 array(array( 2, 0), array( 4, 0), array( 4, 0), array( 4, 0)), 585 array(array( 2, 0), array( 4, 0), array( 2, 4), array( 4, 1)), 586 array(array( 2, 0), array( 2, 2), array( 4, 2), array( 4, 2)), 587 array(array( 2, 0), array( 3, 2), array( 4, 4), array( 4, 4)), 588 array(array( 2, 2), array( 4, 1), array( 6, 2), array( 6, 2)), //10 589 array(array( 4, 0), array( 1, 4), array( 4, 4), array( 3, 8)), 590 array(array( 2, 2), array( 6, 2), array( 4, 6), array( 7, 4)), 591 array(array( 4, 0), array( 8, 1), array( 8, 4), array(12, 4)), 592 array(array( 3, 1), array( 4, 5), array(11, 5), array(11, 5)), 593 array(array( 5, 1), array( 5, 5), array( 5, 7), array(11, 7)), //15 594 array(array( 5, 1), array( 7, 3), array(15, 2), array( 3, 13)), 595 array(array( 1, 5), array(10, 1), array( 1, 15), array( 2, 17)), 596 array(array( 5, 1), array( 9, 4), array(17, 1), array( 2, 19)), 597 array(array( 3, 4), array( 3, 11), array(17, 4), array( 9, 16)), 598 array(array( 3, 5), array( 3, 13), array(15, 5), array(15, 10)), //20 599 array(array( 4, 4), array(17, 0), array(17, 6), array(19, 6)), 600 array(array( 2, 7), array(17, 0), array( 7, 16), array(34, 0)), 601 array(array( 4, 5), array( 4, 14), array(11, 14), array(16, 14)), 602 array(array( 6, 4), array( 6, 14), array(11, 16), array(30, 2)), 603 array(array( 8, 4), array( 8, 13), array( 7, 22), array(22, 13)), //25 604 array(array(10, 2), array(19, 4), array(28, 6), array(33, 4)), 605 array(array( 8, 4), array(22, 3), array( 8, 26), array(12, 28)), 606 array(array( 3, 10), array( 3, 23), array( 4, 31), array(11, 31)), 607 array(array( 7, 7), array(21, 7), array( 1, 37), array(19, 26)), 608 array(array( 5, 10), array(19, 10), array(15, 25), array(23, 25)), //30 609 array(array(13, 3), array( 2, 29), array(42, 1), array(23, 28)), 610 array(array(17, 0), array(10, 23), array(10, 35), array(19, 35)), 611 array(array(17, 1), array(14, 21), array(29, 19), array(11, 46)), 612 array(array(13, 6), array(14, 23), array(44, 7), array(59, 1)), 613 array(array(12, 7), array(12, 26), array(39, 14), array(22, 41)), //35 614 array(array( 6, 14), array( 6, 34), array(46, 10), array( 2, 64)), 615 array(array(17, 4), array(29, 14), array(49, 10), array(24, 46)), 616 array(array( 4, 18), array(13, 32), array(48, 14), array(42, 32)), 617 array(array(20, 4), array(40, 7), array(43, 22), array(10, 67)), 618 array(array(19, 6), array(18, 31), array(34, 34), array(20, 61)),//40 619 ); 620 621 //---------------------------------------------------------------------- 622 // CACHEABLE!!! 623 624 public static function getEccSpec($version, $level, array &$spec) 625 { 626 if (count($spec) < 5) { 627 $spec = array(0,0,0,0,0); 628 } 629 630 $b1 = self::$eccTable[$version][$level][0]; 631 $b2 = self::$eccTable[$version][$level][1]; 632 $data = self::getDataLength($version, $level); 633 $ecc = self::getECCLength($version, $level); 634 635 if($b2 == 0) { 636 $spec[0] = $b1; 637 $spec[1] = (int)($data / $b1); 638 $spec[2] = (int)($ecc / $b1); 639 $spec[3] = 0; 640 $spec[4] = 0; 641 } else { 642 $spec[0] = $b1; 643 $spec[1] = (int)($data / ($b1 + $b2)); 644 $spec[2] = (int)($ecc / ($b1 + $b2)); 645 $spec[3] = $b2; 646 $spec[4] = $spec[1] + 1; 647 } 648 } 649 650 // Alignment pattern --------------------------------------------------- 651 652 /** Positions of alignment patterns. 653 This array includes only the second and the third position of the 654 lignment patterns. Rest of them can be calculated from the distance 655 between them. 656 657 @see Table 1 in Appendix E (pp.71) of JIS X0510:2004. 658 \hideinitializer 659 */ 660 661 public static $alignmentPattern = array( 662 array( 0, 0), 663 array( 0, 0), array(18, 0), array(22, 0), array(26, 0), array(30, 0), // 1- 5 664 array(34, 0), array(22, 38), array(24, 42), array(26, 46), array(28, 50), // 6-10 665 array(30, 54), array(32, 58), array(34, 62), array(26, 46), array(26, 48), //11-15 666 array(26, 50), array(30, 54), array(30, 56), array(30, 58), array(34, 62), //16-20 667 array(28, 50), array(26, 50), array(30, 54), array(28, 54), array(32, 58), //21-25 668 array(30, 58), array(34, 62), array(26, 50), array(30, 54), array(26, 52), //26-30 669 array(30, 56), array(34, 60), array(30, 58), array(34, 62), array(30, 54), //31-35 670 array(24, 50), array(28, 54), array(32, 58), array(26, 54), array(30, 58), //35-40 671 ); 672 673 //---------------------------------------------------------------------- 674 /** Puts an alignment marker. 675 @param frame 676 @param width 677 @param ox,oy center coordinate of the pattern 678 */ 679 public static function putAlignmentMarker(array &$frame, $ox, $oy) 680 { 681 $finder = array( 682 "\xa1\xa1\xa1\xa1\xa1", 683 "\xa1\xa0\xa0\xa0\xa1", 684 "\xa1\xa0\xa1\xa0\xa1", 685 "\xa1\xa0\xa0\xa0\xa1", 686 "\xa1\xa1\xa1\xa1\xa1" 687 ); 688 689 $yStart = $oy-2; 690 $xStart = $ox-2; 691 692 for($y=0; $y<5; $y++) { 693 self::set($frame, $xStart, $yStart+$y, $finder[$y]); 694 } 695 } 696 697 //---------------------------------------------------------------------- 698 public static function putAlignmentPattern($version, &$frame, $width) 699 { 700 if($version < 2) 701 return; 702 703 $d = self::$alignmentPattern[$version][1] - self::$alignmentPattern[$version][0]; 704 if($d < 0) { 705 $w = 2; 706 } else { 707 $w = (int)(($width - self::$alignmentPattern[$version][0]) / $d + 2); 708 } 709 710 if($w * $w - 3 == 1) { 711 $x = self::$alignmentPattern[$version][0]; 712 $y = self::$alignmentPattern[$version][0]; 713 self::putAlignmentMarker($frame, $x, $y); 714 return; 715 } 716 717 $cx = self::$alignmentPattern[$version][0]; 718 for($x=1; $x<$w - 1; $x++) { 719 self::putAlignmentMarker($frame, 6, $cx); 720 self::putAlignmentMarker($frame, $cx, 6); 721 $cx += $d; 722 } 723 724 $cy = self::$alignmentPattern[$version][0]; 725 for($y=0; $y<$w-1; $y++) { 726 $cx = self::$alignmentPattern[$version][0]; 727 for($x=0; $x<$w-1; $x++) { 728 self::putAlignmentMarker($frame, $cx, $cy); 729 $cx += $d; 730 } 731 $cy += $d; 732 } 733 } 734 735 // Version information pattern ----------------------------------------- 736 /** Version information pattern (BCH coded). 737 size: [QRSPEC_VERSION_MAX - 6] 738 @see Table 1 in Appendix D (pp.68) of JIS X0510:2004. 739 \hideinitializer 740 */ 741 742 public static $versionPattern = array( 743 0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d, 744 0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9, 745 0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75, 746 0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64, 747 0x27541, 0x28c69 748 ); 749 750 //---------------------------------------------------------------------- 751 public static function getVersionPattern($version) 752 { 753 if($version < 7 || $version > QRSPEC_VERSION_MAX) 754 return 0; 755 756 return self::$versionPattern[$version -7]; 757 } 758 759 //---------------------------------------------------------------------- 760 /** Format information. 761 @see calcFormatInfo in tests/test_qrspec.c (orginal qrencode c lib) 762 \hideinitializer 763 */ 764 765 public static $formatInfo = array( 766 array(0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976), 767 array(0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0), 768 array(0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed), 769 array(0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b) 770 ); 771 772 public static function getFormatInfo($mask, $level) 773 { 774 if($mask < 0 || $mask > 7) 775 return 0; 776 777 if($level < 0 || $level > 3) 778 return 0; 779 780 return self::$formatInfo[$level][$mask]; 781 } 782 783 // Frame --------------------------------------------------------------- 784 785 /** Cache of initial frames. */ 786 public static $frames = array(); 787 788 /** Put a finder pattern. 789 @param frame 790 @param width 791 @param ox,oy upper-left coordinate of the pattern 792 \hideinitializer 793 */ 794 public static function putFinderPattern(&$frame, $ox, $oy) 795 { 796 $finder = array( 797 "\xc1\xc1\xc1\xc1\xc1\xc1\xc1", 798 "\xc1\xc0\xc0\xc0\xc0\xc0\xc1", 799 "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", 800 "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", 801 "\xc1\xc0\xc1\xc1\xc1\xc0\xc1", 802 "\xc1\xc0\xc0\xc0\xc0\xc0\xc1", 803 "\xc1\xc1\xc1\xc1\xc1\xc1\xc1" 804 ); 805 806 for($y=0; $y<7; $y++) { 807 self::set($frame, $ox, $oy+$y, $finder[$y]); 808 } 809 } 810 811 //---------------------------------------------------------------------- 812 public static function createFrame($version) 813 { 814 $width = self::$capacity[$version][QRCAP_WIDTH]; 815 $frameLine = str_repeat ("\0", $width); 816 $frame = array_fill(0, $width, $frameLine); 817 818 // Finder pattern 819 self::putFinderPattern($frame, 0, 0); 820 self::putFinderPattern($frame, $width - 7, 0); 821 self::putFinderPattern($frame, 0, $width - 7); 822 823 // Separator 824 $yOffset = $width - 7; 825 826 for($y=0; $y<7; $y++) { 827 $frame[$y][7] = "\xc0"; 828 $frame[$y][$width - 8] = "\xc0"; 829 $frame[$yOffset][7] = "\xc0"; 830 $yOffset++; 831 } 832 833 $setPattern = str_repeat("\xc0", 8); 834 835 self::set($frame, 0, 7, $setPattern); 836 self::set($frame, $width-8, 7, $setPattern); 837 self::set($frame, 0, $width - 8, $setPattern); 838 839 // Format info 840 $setPattern = str_repeat("\x84", 9); 841 self::set($frame, 0, 8, $setPattern); 842 self::set($frame, $width - 8, 8, $setPattern, 8); 843 844 $yOffset = $width - 8; 845 846 for($y=0; $y<8; $y++,$yOffset++) { 847 $frame[$y][8] = "\x84"; 848 $frame[$yOffset][8] = "\x84"; 849 } 850 851 // Timing pattern 852 853 for($i=1; $i<$width-15; $i++) { 854 $frame[6][7+$i] = chr(0x90 | ($i & 1)); 855 $frame[7+$i][6] = chr(0x90 | ($i & 1)); 856 } 857 858 // Alignment pattern 859 self::putAlignmentPattern($version, $frame, $width); 860 861 // Version information 862 if($version >= 7) { 863 $vinf = self::getVersionPattern($version); 864 865 $v = $vinf; 866 867 for($x=0; $x<6; $x++) { 868 for($y=0; $y<3; $y++) { 869 $frame[($width - 11)+$y][$x] = chr(0x88 | ($v & 1)); 870 $v = $v >> 1; 871 } 872 } 873 874 $v = $vinf; 875 for($y=0; $y<6; $y++) { 876 for($x=0; $x<3; $x++) { 877 $frame[$y][$x+($width - 11)] = chr(0x88 | ($v & 1)); 878 $v = $v >> 1; 879 } 880 } 881 } 882 883 // and a little bit... 884 $frame[$width - 8][8] = "\x81"; 885 886 return $frame; 887 } 888 889 //---------------------------------------------------------------------- 890 /** Dumps debug HTML of frame. 891 @param Array $frame code frame 892 @param Boolean $binary_mode in binary mode only contents is dumped, without styling 893 */ 894 public static function debug($frame, $binary_mode = false) 895 { 896 if ($binary_mode) { 897 898 foreach ($frame as &$frameLine) { 899 $frameLine = join('<span class="m">&nbsp;&nbsp;</span>', explode('0', $frameLine)); 900 $frameLine = join('&#9608;&#9608;', explode('1', $frameLine)); 901 } 902 903 echo '<style> .m { background-color: white; } </style> '; 904 echo '<pre><tt><br/ ><br/ ><br/ >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'; 905 echo join("<br/ >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;", $frame); 906 echo '</tt></pre><br/ ><br/ ><br/ ><br/ ><br/ ><br/ >'; 907 908 } else { 909 910 foreach ($frame as &$frameLine) { 911 912 $frameLine = strtr($frameLine, array( 913 "\xc0" => '<span class="m">&nbsp;</span>', //marker 0 914 "\xc1" => '<span class="m">&#9618;</span>', //marker 1 915 "\xa0" => '<span class="p">&nbsp;</span>', //submarker 0 916 "\xa1" => '<span class="p">&#9618;</span>', //submarker 1 917 "\x84" => '<span class="s">F</span>', //format 0 918 "\x85" => '<span class="s">f</span>', //format 1 919 "\x81" => '<span class="x">S</span>', //special bit 920 "\x90" => '<span class="c">C</span>', //clock 0 921 "\x91" => '<span class="c">c</span>', //clock 1 922 "\x88" => '<span class="f">&nbsp;</span>', //version 0 923 "\x89" => '<span class="f">&#9618;</span>', //version 1 924 "\x03" => '1', // 1 925 "\x02" => '0', // 0 926 )); 927 } 928 929 echo '<style>'; 930 echo ' .p { background-color: yellow; }'; 931 echo ' .m { background-color: #00FF00; }'; 932 echo ' .s { background-color: #FF0000; }'; 933 echo ' .c { background-color: aqua; }'; 934 echo ' .x { background-color: pink; }'; 935 echo ' .f { background-color: gold; }'; 936 echo '</style>'; 937 938 echo "<tt>"; 939 echo join("<br/ >", $frame); 940 echo "<br/>Legend:<br/>"; 941 echo '1 - data 1<br/>'; 942 echo '0 - data 0<br/>'; 943 echo '<span class="m">&nbsp;</span> - marker bit 0<br/>'; 944 echo '<span class="m">&#9618;</span> - marker bit 1<br/>'; 945 echo '<span class="p">&nbsp;</span> - secondary marker bit 0<br/>'; 946 echo '<span class="p">&#9618;</span> - secondary marker bit 1<br/>'; 947 echo '<span class="s">F</span> - format bit 0<br/>'; 948 echo '<span class="s">f</span> - format bit 1<br/>'; 949 echo '<span class="x">S</span> - special bit<br/>'; 950 echo '<span class="c">C</span> - clock bit 0<br/>'; 951 echo '<span class="c">c</span> - clock bit 1<br/>'; 952 echo '<span class="f">&nbsp;</span> - version bit 0<br/>'; 953 echo '<span class="f">&#9618;</span> - version bit 1<br/>'; 954 echo "</tt>"; 955 956 } 957 } 958 959 //---------------------------------------------------------------------- 960 /** Serializes frame. 961 Create compressed, serialized version of frame. 962 @param Array $frame Code Frame 963 @return String binary compresed Code Frame 964 */ 965 public static function serial($frame) 966 { 967 return gzcompress(join("\n", $frame), 9); 968 } 969 970 //---------------------------------------------------------------------- 971 /** Deserializes frame. 972 Loads frame from serialized compressed binary 973 @param String $code binary, GZipped, serialized frame 974 @return Array Code Frame array 975 */ 976 public static function unserial($code) 977 { 978 return explode("\n", gzuncompress($code)); 979 } 980 981 //---------------------------------------------------------------------- 982 public static function newFrame($version) 983 { 984 if($version < 1 || $version > QRSPEC_VERSION_MAX) 985 return null; 986 987 if(!isset(self::$frames[$version])) { 988 989 $fileName = QR_CACHE_DIR.'frame_'.$version.'.dat'; 990 991 if (QR_CACHEABLE) { 992 if (file_exists($fileName)) { 993 self::$frames[$version] = self::unserial(file_get_contents($fileName)); 994 } else { 995 self::$frames[$version] = self::createFrame($version); 996 file_put_contents($fileName, self::serial(self::$frames[$version])); 997 } 998 } else { 999 self::$frames[$version] = self::createFrame($version); 1000 } 1001 } 1002 1003 if(is_null(self::$frames[$version])) 1004 return null; 1005 1006 return self::$frames[$version]; 1007 } 1008 1009 //---------------------------------------------------------------------- 1010 /** Sets code frame with speciffied code. 1011 @param Array $frame target frame (modified by reference) 1012 @param Integer $x X-axis position of replacement 1013 @param Integer $y Y-axis position of replacement 1014 @param Byte $repl replacement string 1015 @param Integer $replLen (optional) replacement string length, when __Integer__ > 1 subset of given $repl is used, when __false__ whole $repl is used 1016 */ 1017 public static function set(&$frame, $x, $y, $repl, $replLen = false) { 1018 $frame[$y] = substr_replace($frame[$y], ($replLen !== false)?substr($repl,0,$replLen):$repl, $x, ($replLen !== false)?$replLen:strlen($repl)); 1019 } 1020 1021 //---------------------------------------------------------------------- 1022 1023 /** @name Reed-Solomon related shorthand getters. 1024 Syntax-sugar to access code speciffication by getter name, not by spec array field. 1025 */ 1026 /** @{*/ 1027 public static function rsBlockNum($spec) { return $spec[0] + $spec[3]; } 1028 public static function rsBlockNum1($spec) { return $spec[0]; } 1029 public static function rsDataCodes1($spec) { return $spec[1]; } 1030 public static function rsEccCodes1($spec) { return $spec[2]; } 1031 public static function rsBlockNum2($spec) { return $spec[3]; } 1032 public static function rsDataCodes2($spec) { return $spec[4]; } 1033 public static function rsEccCodes2($spec) { return $spec[2]; } 1034 public static function rsDataLength($spec) { return ($spec[0] * $spec[1]) + ($spec[3] * $spec[4]); } 1035 public static function rsEccLength($spec) { return ($spec[0] + $spec[3]) * $spec[2]; } 1036 /** @}*/ 1037 } 1038 1039 /** @}*/ 1040 1041 1042 1043 1044 1045//---- qrimage.php ----------------------------- 1046 1047 1048 1049 1050/* 1051 * PHP QR Code encoder 1052 * 1053 * Image output of code using GD2 1054 * 1055 * PHP QR Code is distributed under LGPL 3 1056 * Copyright (C) 2010-2013 Dominik Dzienia <deltalab at poczta dot fm> 1057 * 1058 * This library is free software; you can redistribute it and/or 1059 * modify it under the terms of the GNU Lesser General Public 1060 * License as published by the Free Software Foundation; either 1061 * version 3 of the License, or any later version. 1062 * 1063 * This library is distributed in the hope that it will be useful, 1064 * but WITHOUT ANY WARRANTY; without even the implied warranty of 1065 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1066 * Lesser General Public License for more details. 1067 * 1068 * You should have received a copy of the GNU Lesser General Public 1069 * License along with this library; if not, write to the Free Software 1070 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 1071 */ 1072 1073 define('QR_IMAGE', true); 1074 1075 1076 /** @defgroup OutputGroup Standard API Output 1077 Provide simple Raster & Vector output */ 1078 1079 /** @addtogroup OutputGroup */ 1080 /** @{ */ 1081 1082 /** Image rendering helper. 1083 Uses GD2 image to render QR Code into image file */ 1084 class QRimage { 1085 1086 //---------------------------------------------------------------------- 1087 /** 1088 Creates PNG image. 1089 @param Array $frame frame containing code 1090 @param String $filename (optional) output file name, if __false__ outputs to browser with required headers 1091 @param Integer $pixelPerPoint (optional) pixel size, multiplier for each 'virtual' pixel 1092 @param Integer $outerFrame (optional) code margin (silent zone) in 'virtual' pixels 1093 @param Boolean $saveandprint (optional) if __true__ code is outputed to browser and saved to file, otherwise only saved to file. It is effective only if $outfile is specified. 1094 */ 1095 1096 public static function png($frame, $filename = false, $pixelPerPoint = 4, $outerFrame = 4,$saveandprint=FALSE) 1097 { 1098 $image = self::image($frame, $pixelPerPoint, $outerFrame); 1099 1100 if ($filename === false) { 1101 Header("Content-type: image/png"); 1102 ImagePng($image); 1103 } else { 1104 if($saveandprint===TRUE){ 1105 ImagePng($image, $filename); 1106 header("Content-type: image/png"); 1107 ImagePng($image); 1108 }else{ 1109 ImagePng($image, $filename); 1110 } 1111 } 1112 1113 ImageDestroy($image); 1114 } 1115 1116 //---------------------------------------------------------------------- 1117 /** 1118 Creates JPEG image. 1119 @param Array $frame frame containing code 1120 @param String $filename (optional) output file name, if __false__ outputs to browser with required headers 1121 @param Integer $pixelPerPoint (optional) pixel size, multiplier for each 'virtual' pixel 1122 @param Integer $outerFrame (optional) code margin (silent zone) in 'virtual' pixels 1123 @param Integer $q (optional) JPEG compression level (__0__ .. __100__) 1124 */ 1125 1126 public static function jpg($frame, $filename = false, $pixelPerPoint = 8, $outerFrame = 4, $q = 85) 1127 { 1128 $image = self::image($frame, $pixelPerPoint, $outerFrame); 1129 1130 if ($filename === false) { 1131 Header("Content-type: image/jpeg"); 1132 ImageJpeg($image, null, $q); 1133 } else { 1134 ImageJpeg($image, $filename, $q); 1135 } 1136 1137 ImageDestroy($image); 1138 } 1139 1140 //---------------------------------------------------------------------- 1141 /** 1142 Creates generic GD2 image object 1143 @param Array $frame frame containing code 1144 @param Integer $pixelPerPoint (optional) pixel size, multiplier for each 'virtual' pixel 1145 @param Integer $outerFrame (optional) code margin (silent zone) in 'virtual' pixels 1146 @return __Resource__ GD2 image resource (remember to ImageDestroy it!) 1147 */ 1148 public static function image($frame, $pixelPerPoint = 4, $outerFrame = 4) 1149 { 1150 $h = count($frame); 1151 $w = strlen($frame[0]); 1152 1153 $imgW = $w + 2*$outerFrame; 1154 $imgH = $h + 2*$outerFrame; 1155 1156 $base_image =ImageCreate($imgW, $imgH); 1157 1158 $col[0] = ImageColorAllocate($base_image,255,255,255); 1159 $col[1] = ImageColorAllocate($base_image,0,0,0); 1160 1161 imagefill($base_image, 0, 0, $col[0]); 1162 1163 for($y=0; $y<$h; $y++) { 1164 for($x=0; $x<$w; $x++) { 1165 if ($frame[$y][$x] == '1') { 1166 ImageSetPixel($base_image,$x+$outerFrame,$y+$outerFrame,$col[1]); 1167 } 1168 } 1169 } 1170 1171 $target_image =ImageCreate($imgW * $pixelPerPoint, $imgH * $pixelPerPoint); 1172 ImageCopyResized($target_image, $base_image, 0, 0, 0, 0, $imgW * $pixelPerPoint, $imgH * $pixelPerPoint, $imgW, $imgH); 1173 ImageDestroy($base_image); 1174 1175 return $target_image; 1176 } 1177 } 1178 1179 /** @} */ 1180 1181 1182 1183//---- qrinput.php ----------------------------- 1184 1185 1186 1187 1188/* 1189 * PHP QR Code encoder 1190 * 1191 * Input encoding class 1192 * 1193 * Based on libqrencode C library distributed under LGPL 2.1 1194 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net> 1195 * 1196 * PHP QR Code is distributed under LGPL 3 1197 * Copyright (C) 2010-2013 Dominik Dzienia <deltalab at poczta dot fm> 1198 * 1199 * This library is free software; you can redistribute it and/or 1200 * modify it under the terms of the GNU Lesser General Public 1201 * License as published by the Free Software Foundation; either 1202 * version 3 of the License, or any later version. 1203 * 1204 * This library is distributed in the hope that it will be useful, 1205 * but WITHOUT ANY WARRANTY; without even the implied warranty of 1206 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1207 * Lesser General Public License for more details. 1208 * 1209 * You should have received a copy of the GNU Lesser General Public 1210 * License along with this library; if not, write to the Free Software 1211 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 1212 */ 1213 1214 define('STRUCTURE_HEADER_BITS', 20); 1215 define('MAX_STRUCTURED_SYMBOLS', 16); 1216 1217 /** @addtogroup CoreGroup */ 1218 /** @{ */ 1219 1220 class QRinputItem { 1221 1222 public $mode; 1223 public $size; 1224 public $data; 1225 public $bstream; 1226 1227 public function __construct($mode, $size, $data, $bstream = null) 1228 { 1229 $setData = array_slice($data, 0, $size); 1230 1231 if (count($setData) < $size) { 1232 $setData = array_merge($setData, array_fill(0,$size-count($setData),0)); 1233 } 1234 1235 if(!QRinput::check($mode, $size, $setData)) { 1236 throw new Exception('Error m:'.$mode.',s:'.$size.',d:'.join(',',$setData)); 1237 return null; 1238 } 1239 1240 $this->mode = $mode; 1241 $this->size = $size; 1242 $this->data = $setData; 1243 $this->bstream = $bstream; 1244 } 1245 1246 //---------------------------------------------------------------------- 1247 public function encodeModeNum($version) 1248 { 1249 try { 1250 1251 $words = (int)($this->size / 3); 1252 $bs = new QRbitstream(); 1253 1254 $val = 0x1; 1255 $bs->appendNum(4, $val); 1256 $bs->appendNum(QRspec::lengthIndicator(QR_MODE_NUM, $version), $this->size); 1257 1258 for($i=0; $i<$words; $i++) { 1259 $val = (ord($this->data[$i*3 ]) - ord('0')) * 100; 1260 $val += (ord($this->data[$i*3+1]) - ord('0')) * 10; 1261 $val += (ord($this->data[$i*3+2]) - ord('0')); 1262 $bs->appendNum(10, $val); 1263 } 1264 1265 if($this->size - $words * 3 == 1) { 1266 $val = ord($this->data[$words*3]) - ord('0'); 1267 $bs->appendNum(4, $val); 1268 } else if($this->size - $words * 3 == 2) { 1269 $val = (ord($this->data[$words*3 ]) - ord('0')) * 10; 1270 $val += (ord($this->data[$words*3+1]) - ord('0')); 1271 $bs->appendNum(7, $val); 1272 } 1273 1274 $this->bstream = $bs; 1275 return 0; 1276 1277 } catch (Exception $e) { 1278 return -1; 1279 } 1280 } 1281 1282 //---------------------------------------------------------------------- 1283 public function encodeModeAn($version) 1284 { 1285 try { 1286 $words = (int)($this->size / 2); 1287 $bs = new QRbitstream(); 1288 1289 $bs->appendNum(4, 0x02); 1290 $bs->appendNum(QRspec::lengthIndicator(QR_MODE_AN, $version), $this->size); 1291 1292 for($i=0; $i<$words; $i++) { 1293 $val = (int)QRinput::lookAnTable(ord($this->data[$i*2 ])) * 45; 1294 $val += (int)QRinput::lookAnTable(ord($this->data[$i*2+1])); 1295 1296 $bs->appendNum(11, $val); 1297 } 1298 1299 if($this->size & 1) { 1300 $val = QRinput::lookAnTable(ord($this->data[$words * 2])); 1301 $bs->appendNum(6, $val); 1302 } 1303 1304 $this->bstream = $bs; 1305 return 0; 1306 1307 } catch (Exception $e) { 1308 return -1; 1309 } 1310 } 1311 1312 //---------------------------------------------------------------------- 1313 public function encodeMode8($version) 1314 { 1315 try { 1316 $bs = new QRbitstream(); 1317 1318 $bs->appendNum(4, 0x4); 1319 $bs->appendNum(QRspec::lengthIndicator(QR_MODE_8, $version), $this->size); 1320 1321 for($i=0; $i<$this->size; $i++) { 1322 $bs->appendNum(8, ord($this->data[$i])); 1323 } 1324 1325 $this->bstream = $bs; 1326 return 0; 1327 1328 } catch (Exception $e) { 1329 return -1; 1330 } 1331 } 1332 1333 //---------------------------------------------------------------------- 1334 public function encodeModeKanji($version) 1335 { 1336 try { 1337 1338 $bs = new QRbitstream(); 1339 1340 $bs->appendNum(4, 0x8); 1341 $bs->appendNum(QRspec::lengthIndicator(QR_MODE_KANJI, $version), (int)($this->size / 2)); 1342 1343 for($i=0; $i<$this->size; $i+=2) { 1344 $val = (ord($this->data[$i]) << 8) | ord($this->data[$i+1]); 1345 if($val <= 0x9ffc) { 1346 $val -= 0x8140; 1347 } else { 1348 $val -= 0xc140; 1349 } 1350 1351 $h = ($val >> 8) * 0xc0; 1352 $val = ($val & 0xff) + $h; 1353 1354 $bs->appendNum(13, $val); 1355 } 1356 1357 $this->bstream = $bs; 1358 return 0; 1359 1360 } catch (Exception $e) { 1361 return -1; 1362 } 1363 } 1364 1365 //---------------------------------------------------------------------- 1366 public function encodeModeStructure() 1367 { 1368 try { 1369 $bs = new QRbitstream(); 1370 1371 $bs->appendNum(4, 0x03); 1372 $bs->appendNum(4, ord($this->data[1]) - 1); 1373 $bs->appendNum(4, ord($this->data[0]) - 1); 1374 $bs->appendNum(8, ord($this->data[2])); 1375 1376 $this->bstream = $bs; 1377 return 0; 1378 1379 } catch (Exception $e) { 1380 return -1; 1381 } 1382 } 1383 1384 //---------------------------------------------------------------------- 1385 public function estimateBitStreamSizeOfEntry($version) 1386 { 1387 $bits = 0; 1388 1389 if($version == 0) 1390 $version = 1; 1391 1392 switch($this->mode) { 1393 case QR_MODE_NUM: $bits = QRinput::estimateBitsModeNum($this->size); break; 1394 case QR_MODE_AN: $bits = QRinput::estimateBitsModeAn($this->size); break; 1395 case QR_MODE_8: $bits = QRinput::estimateBitsMode8($this->size); break; 1396 case QR_MODE_KANJI: $bits = QRinput::estimateBitsModeKanji($this->size);break; 1397 case QR_MODE_STRUCTURE: return STRUCTURE_HEADER_BITS; 1398 default: 1399 return 0; 1400 } 1401 1402 $l = QRspec::lengthIndicator($this->mode, $version); 1403 $m = 1 << $l; 1404 $num = (int)(($this->size + $m - 1) / $m); 1405 1406 $bits += $num * (4 + $l); 1407 1408 return $bits; 1409 } 1410 1411 //---------------------------------------------------------------------- 1412 public function encodeBitStream($version) 1413 { 1414 try { 1415 1416 unset($this->bstream); 1417 $words = QRspec::maximumWords($this->mode, $version); 1418 1419 if($this->size > $words) { 1420 1421 $st1 = new QRinputItem($this->mode, $words, $this->data); 1422 $st2 = new QRinputItem($this->mode, $this->size - $words, array_slice($this->data, $words)); 1423 1424 $st1->encodeBitStream($version); 1425 $st2->encodeBitStream($version); 1426 1427 $this->bstream = new QRbitstream(); 1428 $this->bstream->append($st1->bstream); 1429 $this->bstream->append($st2->bstream); 1430 1431 unset($st1); 1432 unset($st2); 1433 1434 } else { 1435 1436 $ret = 0; 1437 1438 switch($this->mode) { 1439 case QR_MODE_NUM: $ret = $this->encodeModeNum($version); break; 1440 case QR_MODE_AN: $ret = $this->encodeModeAn($version); break; 1441 case QR_MODE_8: $ret = $this->encodeMode8($version); break; 1442 case QR_MODE_KANJI: $ret = $this->encodeModeKanji($version);break; 1443 case QR_MODE_STRUCTURE: $ret = $this->encodeModeStructure(); break; 1444 1445 default: 1446 break; 1447 } 1448 1449 if($ret < 0) 1450 return -1; 1451 } 1452 1453 return $this->bstream->size(); 1454 1455 } catch (Exception $e) { 1456 return -1; 1457 } 1458 } 1459 }; 1460 1461 //########################################################################## 1462 1463 class QRinput { 1464 1465 public $items; 1466 1467 private $version; 1468 private $level; 1469 1470 //---------------------------------------------------------------------- 1471 public function __construct($version = 0, $level = QR_ECLEVEL_L) 1472 { 1473 if ($version < 0 || $version > QRSPEC_VERSION_MAX || $level > QR_ECLEVEL_H) { 1474 throw new Exception('Invalid version no'); 1475 return NULL; 1476 } 1477 1478 $this->version = $version; 1479 $this->level = $level; 1480 } 1481 1482 //---------------------------------------------------------------------- 1483 public function getVersion() 1484 { 1485 return $this->version; 1486 } 1487 1488 //---------------------------------------------------------------------- 1489 public function setVersion($version) 1490 { 1491 if($version < 0 || $version > QRSPEC_VERSION_MAX) { 1492 throw new Exception('Invalid version no'); 1493 return -1; 1494 } 1495 1496 $this->version = $version; 1497 1498 return 0; 1499 } 1500 1501 //---------------------------------------------------------------------- 1502 public function getErrorCorrectionLevel() 1503 { 1504 return $this->level; 1505 } 1506 1507 //---------------------------------------------------------------------- 1508 public function setErrorCorrectionLevel($level) 1509 { 1510 if($level > QR_ECLEVEL_H) { 1511 throw new Exception('Invalid ECLEVEL'); 1512 return -1; 1513 } 1514 1515 $this->level = $level; 1516 1517 return 0; 1518 } 1519 1520 //---------------------------------------------------------------------- 1521 public function appendEntry(QRinputItem $entry) 1522 { 1523 $this->items[] = $entry; 1524 } 1525 1526 //---------------------------------------------------------------------- 1527 public function append($mode, $size, $data) 1528 { 1529 try { 1530 $entry = new QRinputItem($mode, $size, $data); 1531 $this->items[] = $entry; 1532 return 0; 1533 } catch (Exception $e) { 1534 return -1; 1535 } 1536 } 1537 1538 //---------------------------------------------------------------------- 1539 1540 public function insertStructuredAppendHeader($size, $index, $parity) 1541 { 1542 if( $size > MAX_STRUCTURED_SYMBOLS ) { 1543 throw new Exception('insertStructuredAppendHeader wrong size'); 1544 } 1545 1546 if( $index <= 0 || $index > MAX_STRUCTURED_SYMBOLS ) { 1547 throw new Exception('insertStructuredAppendHeader wrong index'); 1548 } 1549 1550 $buf = array($size, $index, $parity); 1551 1552 try { 1553 $entry = new QRinputItem(QR_MODE_STRUCTURE, 3, buf); 1554 array_unshift($this->items, $entry); 1555 return 0; 1556 } catch (Exception $e) { 1557 return -1; 1558 } 1559 } 1560 1561 //---------------------------------------------------------------------- 1562 public function calcParity() 1563 { 1564 $parity = 0; 1565 1566 foreach($this->items as $item) { 1567 if($item->mode != QR_MODE_STRUCTURE) { 1568 for($i=$item->size-1; $i>=0; $i--) { 1569 $parity ^= $item->data[$i]; 1570 } 1571 } 1572 } 1573 1574 return $parity; 1575 } 1576 1577 //---------------------------------------------------------------------- 1578 public static function checkModeNum($size, $data) 1579 { 1580 for($i=0; $i<$size; $i++) { 1581 if((ord($data[$i]) < ord('0')) || (ord($data[$i]) > ord('9'))){ 1582 return false; 1583 } 1584 } 1585 1586 return true; 1587 } 1588 1589 //---------------------------------------------------------------------- 1590 public static function estimateBitsModeNum($size) 1591 { 1592 $w = (int)$size / 3; 1593 $bits = $w * 10; 1594 1595 switch($size - $w * 3) { 1596 case 1: 1597 $bits += 4; 1598 break; 1599 case 2: 1600 $bits += 7; 1601 break; 1602 default: 1603 break; 1604 } 1605 1606 return $bits; 1607 } 1608 1609 //---------------------------------------------------------------------- 1610 public static $anTable = array( 1611 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1612 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1613 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 1614 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, 1615 -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 1616 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, 1617 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1618 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 1619 ); 1620 1621 //---------------------------------------------------------------------- 1622 public static function lookAnTable($c) 1623 { 1624 return (($c > 127)?-1:self::$anTable[$c]); 1625 } 1626 1627 //---------------------------------------------------------------------- 1628 public static function checkModeAn($size, $data) 1629 { 1630 for($i=0; $i<$size; $i++) { 1631 if (self::lookAnTable(ord($data[$i])) == -1) { 1632 return false; 1633 } 1634 } 1635 1636 return true; 1637 } 1638 1639 //---------------------------------------------------------------------- 1640 public static function estimateBitsModeAn($size) 1641 { 1642 $w = (int)($size / 2); 1643 $bits = $w * 11; 1644 1645 if($size & 1) { 1646 $bits += 6; 1647 } 1648 1649 return $bits; 1650 } 1651 1652 //---------------------------------------------------------------------- 1653 public static function estimateBitsMode8($size) 1654 { 1655 return $size * 8; 1656 } 1657 1658 //---------------------------------------------------------------------- 1659 public function estimateBitsModeKanji($size) 1660 { 1661 return (int)(($size / 2) * 13); 1662 } 1663 1664 //---------------------------------------------------------------------- 1665 public static function checkModeKanji($size, $data) 1666 { 1667 if($size & 1) 1668 return false; 1669 1670 for($i=0; $i<$size; $i+=2) { 1671 $val = (ord($data[$i]) << 8) | ord($data[$i+1]); 1672 if( $val < 0x8140 1673 || ($val > 0x9ffc && $val < 0xe040) 1674 || $val > 0xebbf) { 1675 return false; 1676 } 1677 } 1678 1679 return true; 1680 } 1681 1682 /*********************************************************************** 1683 * Validation 1684 **********************************************************************/ 1685 1686 public static function check($mode, $size, $data) 1687 { 1688 if($size <= 0) 1689 return false; 1690 1691 switch($mode) { 1692 case QR_MODE_NUM: return self::checkModeNum($size, $data); break; 1693 case QR_MODE_AN: return self::checkModeAn($size, $data); break; 1694 case QR_MODE_KANJI: return self::checkModeKanji($size, $data); break; 1695 case QR_MODE_8: return true; break; 1696 case QR_MODE_STRUCTURE: return true; break; 1697 1698 default: 1699 break; 1700 } 1701 1702 return false; 1703 } 1704 1705 1706 //---------------------------------------------------------------------- 1707 public function estimateBitStreamSize($version) 1708 { 1709 $bits = 0; 1710 1711 foreach($this->items as $item) { 1712 $bits += $item->estimateBitStreamSizeOfEntry($version); 1713 } 1714 1715 return $bits; 1716 } 1717 1718 //---------------------------------------------------------------------- 1719 public function estimateVersion() 1720 { 1721 $version = 0; 1722 $prev = 0; 1723 do { 1724 $prev = $version; 1725 $bits = $this->estimateBitStreamSize($prev); 1726 $version = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level); 1727 if ($version < 0) { 1728 return -1; 1729 } 1730 } while ($version > $prev); 1731 1732 return $version; 1733 } 1734 1735 //---------------------------------------------------------------------- 1736 public static function lengthOfCode($mode, $version, $bits) 1737 { 1738 $payload = $bits - 4 - QRspec::lengthIndicator($mode, $version); 1739 switch($mode) { 1740 case QR_MODE_NUM: 1741 $chunks = (int)($payload / 10); 1742 $remain = $payload - $chunks * 10; 1743 $size = $chunks * 3; 1744 if($remain >= 7) { 1745 $size += 2; 1746 } else if($remain >= 4) { 1747 $size += 1; 1748 } 1749 break; 1750 case QR_MODE_AN: 1751 $chunks = (int)($payload / 11); 1752 $remain = $payload - $chunks * 11; 1753 $size = $chunks * 2; 1754 if($remain >= 6) 1755 $size++; 1756 break; 1757 case QR_MODE_8: 1758 $size = (int)($payload / 8); 1759 break; 1760 case QR_MODE_KANJI: 1761 $size = (int)(($payload / 13) * 2); 1762 break; 1763 case QR_MODE_STRUCTURE: 1764 $size = (int)($payload / 8); 1765 break; 1766 default: 1767 $size = 0; 1768 break; 1769 } 1770 1771 $maxsize = QRspec::maximumWords($mode, $version); 1772 if($size < 0) $size = 0; 1773 if($size > $maxsize) $size = $maxsize; 1774 1775 return $size; 1776 } 1777 1778 //---------------------------------------------------------------------- 1779 public function createBitStream() 1780 { 1781 $total = 0; 1782 1783 foreach($this->items as $item) { 1784 $bits = $item->encodeBitStream($this->version); 1785 1786 if($bits < 0) 1787 return -1; 1788 1789 $total += $bits; 1790 } 1791 1792 return $total; 1793 } 1794 1795 //---------------------------------------------------------------------- 1796 public function convertData() 1797 { 1798 $ver = $this->estimateVersion(); 1799 if($ver > $this->getVersion()) { 1800 $this->setVersion($ver); 1801 } 1802 1803 for(;;) { 1804 $bits = $this->createBitStream(); 1805 1806 if($bits < 0) 1807 return -1; 1808 1809 $ver = QRspec::getMinimumVersion((int)(($bits + 7) / 8), $this->level); 1810 if($ver < 0) { 1811 throw new Exception('WRONG VERSION'); 1812 return -1; 1813 } else if($ver > $this->getVersion()) { 1814 $this->setVersion($ver); 1815 } else { 1816 break; 1817 } 1818 } 1819 1820 return 0; 1821 } 1822 1823 //---------------------------------------------------------------------- 1824 public function appendPaddingBit(&$bstream) 1825 { 1826 $bits = $bstream->size(); 1827 $maxwords = QRspec::getDataLength($this->version, $this->level); 1828 $maxbits = $maxwords * 8; 1829 1830 if ($maxbits == $bits) { 1831 return 0; 1832 } 1833 1834 if ($maxbits - $bits < 5) { 1835 return $bstream->appendNum($maxbits - $bits, 0); 1836 } 1837 1838 $bits += 4; 1839 $words = (int)(($bits + 7) / 8); 1840 1841 $padding = new QRbitstream(); 1842 $ret = $padding->appendNum($words * 8 - $bits + 4, 0); 1843 1844 if($ret < 0) 1845 return $ret; 1846 1847 $padlen = $maxwords - $words; 1848 1849 if($padlen > 0) { 1850 1851 $padbuf = array(); 1852 for($i=0; $i<$padlen; $i++) { 1853 $padbuf[$i] = ($i&1)?0x11:0xec; 1854 } 1855 1856 $ret = $padding->appendBytes($padlen, $padbuf); 1857 1858 if($ret < 0) 1859 return $ret; 1860 1861 } 1862 1863 $ret = $bstream->append($padding); 1864 1865 return $ret; 1866 } 1867 1868 //---------------------------------------------------------------------- 1869 public function mergeBitStream() 1870 { 1871 if($this->convertData() < 0) { 1872 return null; 1873 } 1874 1875 $bstream = new QRbitstream(); 1876 1877 foreach($this->items as $item) { 1878 $ret = $bstream->append($item->bstream); 1879 if($ret < 0) { 1880 return null; 1881 } 1882 } 1883 1884 return $bstream; 1885 } 1886 1887 //---------------------------------------------------------------------- 1888 public function getBitStream() 1889 { 1890 1891 $bstream = $this->mergeBitStream(); 1892 1893 if($bstream == null) { 1894 return null; 1895 } 1896 1897 $ret = $this->appendPaddingBit($bstream); 1898 if($ret < 0) { 1899 return null; 1900 } 1901 1902 return $bstream; 1903 } 1904 1905 //---------------------------------------------------------------------- 1906 public function getByteStream() 1907 { 1908 $bstream = $this->getBitStream(); 1909 if($bstream == null) { 1910 return null; 1911 } 1912 1913 return $bstream->toByte(); 1914 } 1915 } 1916 1917 /** @}*/ 1918 1919 1920 1921//---- qrbitstream.php ----------------------------- 1922 1923 1924 1925 1926/* 1927 * PHP QR Code encoder 1928 * 1929 * Bitstream class 1930 * 1931 * Based on libqrencode C library distributed under LGPL 2.1 1932 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net> 1933 * 1934 * PHP QR Code is distributed under LGPL 3 1935 * Copyright (C) 2010-2013 Dominik Dzienia <deltalab at poczta dot fm> 1936 * 1937 * This library is free software; you can redistribute it and/or 1938 * modify it under the terms of the GNU Lesser General Public 1939 * License as published by the Free Software Foundation; either 1940 * version 3 of the License, or any later version. 1941 * 1942 * This library is distributed in the hope that it will be useful, 1943 * but WITHOUT ANY WARRANTY; without even the implied warranty of 1944 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1945 * Lesser General Public License for more details. 1946 * 1947 * You should have received a copy of the GNU Lesser General Public 1948 * License along with this library; if not, write to the Free Software 1949 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 1950 */ 1951 1952 /** @addtogroup CoreGroup */ 1953 /** @{ */ 1954 1955 /** 1956 PHP bit stream. 1957 Class implementing array of bits (= 1 or 0 ints). Allows to initialize and append 1958 bits from given Integer or array of Bytes. 1959 */ 1960 class QRbitstream { 1961 1962 /** 1963 Array containing bit data stream 1964 */ 1965 public $data = array(); 1966 1967 //---------------------------------------------------------------------- 1968 /** 1969 @return Integer size of byte stream 1970 */ 1971 public function size() 1972 { 1973 return count($this->data); 1974 } 1975 1976 //---------------------------------------------------------------------- 1977 /** 1978 Allocates bit stream, fills bit data stream with 0's. 1979 This operation is __destructive__, will replace orginal stream contents! 1980 @param Integer $setLength desired target stream size 1981 @return Integer 0 on success, other on failure 1982 */ 1983 public function allocate($setLength) 1984 { 1985 $this->data = array_fill(0, $setLength, 0); 1986 return 0; 1987 } 1988 1989 //---------------------------------------------------------------------- 1990 /** 1991 Creates new bit stream from given Integer number. 1992 @param Integer $bits bit count 1993 @param Integer $num integer to convert 1994 @return QRbitstream bit stream object containing first $bits bits from $num in order from LSB to MSB 1995 */ 1996 public static function newFromNum($bits, $num) 1997 { 1998 $bstream = new QRbitstream(); 1999 $bstream->allocate($bits); 2000 2001 $mask = 1 << ($bits - 1); 2002 for($i=0; $i<$bits; $i++) { 2003 if($num & $mask) { 2004 $bstream->data[$i] = 1; 2005 } else { 2006 $bstream->data[$i] = 0; 2007 } 2008 $mask = $mask >> 1; 2009 } 2010 2011 return $bstream; 2012 } 2013 2014 //---------------------------------------------------------------------- 2015 /** 2016 Creates new bit stream from given byte array. 2017 @param Integer $size size of array 2018 @param Array $data array ob bytes 2019 @return QRbitstream bit stream object containing bit contents of given bytes array 2020 */ 2021 public static function newFromBytes($size, $data) 2022 { 2023 $bstream = new QRbitstream(); 2024 $bstream->allocate($size * 8); 2025 $p=0; 2026 2027 for($i=0; $i<$size; $i++) { 2028 $mask = 0x80; 2029 for($j=0; $j<8; $j++) { 2030 if($data[$i] & $mask) { 2031 $bstream->data[$p] = 1; 2032 } else { 2033 $bstream->data[$p] = 0; 2034 } 2035 $p++; 2036 $mask = $mask >> 1; 2037 } 2038 } 2039 2040 return $bstream; 2041 } 2042 2043 //---------------------------------------------------------------------- 2044 /** 2045 Appends given bit stream at end of this stream. 2046 @param QRbitstream $arg bit stream to be appended 2047 @return Integer status of append operation, 0 when success, -1 when $arg is null 2048 */ 2049 public function append(QRbitstream $arg) 2050 { 2051 if (is_null($arg)) { 2052 return -1; 2053 } 2054 2055 if($arg->size() == 0) { 2056 return 0; 2057 } 2058 2059 if($this->size() == 0) { 2060 $this->data = $arg->data; 2061 return 0; 2062 } 2063 2064 $this->data = array_values(array_merge($this->data, $arg->data)); 2065 2066 return 0; 2067 } 2068 2069 //---------------------------------------------------------------------- 2070 /** 2071 Appends bit stream cteated from given Integer number at end of current stream. 2072 @param Integer $bits bit count 2073 @param Integer $num integer to convert 2074 @return Integer status of append operation, status of append operation, 0 when success, -1 otherwise 2075 */ 2076 public function appendNum($bits, $num) 2077 { 2078 if ($bits == 0) 2079 return 0; 2080 2081 $b = QRbitstream::newFromNum($bits, $num); 2082 2083 if(is_null($b)) 2084 return -1; 2085 2086 $ret = $this->append($b); 2087 unset($b); 2088 2089 return $ret; 2090 } 2091 2092 //---------------------------------------------------------------------- 2093 /** 2094 Appends bit stream created from from given byte array at end of current stream. 2095 @param Integer $size size of array 2096 @param Array $data array ob bytes 2097 @return Integer status of append operation, status of append operation, 0 when success, -1 otherwise 2098 */ 2099 public function appendBytes($size, $data) 2100 { 2101 if ($size == 0) 2102 return 0; 2103 2104 $b = QRbitstream::newFromBytes($size, $data); 2105 2106 if(is_null($b)) 2107 return -1; 2108 2109 $ret = $this->append($b); 2110 unset($b); 2111 2112 return $ret; 2113 } 2114 2115 //---------------------------------------------------------------------- 2116 /** 2117 Converts current bit stream into byte array. 2118 @returns Array array of bytes 2119 */ 2120 public function toByte() 2121 { 2122 2123 $size = $this->size(); 2124 2125 if($size == 0) { 2126 return array(); 2127 } 2128 2129 $data = array_fill(0, (int)(($size + 7) / 8), 0); 2130 $bytes = (int)($size / 8); 2131 2132 $p = 0; 2133 2134 for($i=0; $i<$bytes; $i++) { 2135 $v = 0; 2136 for($j=0; $j<8; $j++) { 2137 $v = $v << 1; 2138 $v |= $this->data[$p]; 2139 $p++; 2140 } 2141 $data[$i] = $v; 2142 } 2143 2144 if($size & 7) { 2145 $v = 0; 2146 for($j=0; $j<($size & 7); $j++) { 2147 $v = $v << 1; 2148 $v |= $this->data[$p]; 2149 $p++; 2150 } 2151 $data[$bytes] = $v; 2152 } 2153 2154 return $data; 2155 } 2156 2157 } 2158 2159 /** @}*/ 2160 2161 2162 2163//---- qrsplit.php ----------------------------- 2164 2165 2166 2167 2168/* 2169 * PHP QR Code encoder 2170 * 2171 * Input splitting classes 2172 * 2173 * Based on libqrencode C library distributed under LGPL 2.1 2174 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net> 2175 * 2176 * PHP QR Code is distributed under LGPL 3 2177 * Copyright (C) 2010-2013 Dominik Dzienia <deltalab at poczta dot fm> 2178 * 2179 * The following data / specifications are taken from 2180 * "Two dimensional symbol -- QR-code -- Basic Specification" (JIS X0510:2004) 2181 * or 2182 * "Automatic identification and data capture techniques -- 2183 * QR Code 2005 bar code symbology specification" (ISO/IEC 18004:2006) 2184 * 2185 * This library is free software; you can redistribute it and/or 2186 * modify it under the terms of the GNU Lesser General Public 2187 * License as published by the Free Software Foundation; either 2188 * version 3 of the License, or any later version. 2189 * 2190 * This library is distributed in the hope that it will be useful, 2191 * but WITHOUT ANY WARRANTY; without even the implied warranty of 2192 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 2193 * Lesser General Public License for more details. 2194 * 2195 * You should have received a copy of the GNU Lesser General Public 2196 * License along with this library; if not, write to the Free Software 2197 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 2198 */ 2199 2200 /** @addtogroup CoreGroup */ 2201 /** @{ */ 2202 2203 /** Input stream splitter. */ 2204 class QRsplit { 2205 2206 public $dataStr = ''; 2207 public $input; 2208 public $modeHint; 2209 2210 //---------------------------------------------------------------------- 2211 public function __construct($dataStr, $input, $modeHint) 2212 { 2213 $this->dataStr = $dataStr; 2214 $this->input = $input; 2215 $this->modeHint = $modeHint; 2216 } 2217 2218 //---------------------------------------------------------------------- 2219 public static function isdigitat($str, $pos) 2220 { 2221 if ($pos >= strlen($str)) 2222 return false; 2223 2224 return ((ord($str[$pos]) >= ord('0'))&&(ord($str[$pos]) <= ord('9'))); 2225 } 2226 2227 //---------------------------------------------------------------------- 2228 public static function isalnumat($str, $pos) 2229 { 2230 if ($pos >= strlen($str)) 2231 return false; 2232 2233 return (QRinput::lookAnTable(ord($str[$pos])) >= 0); 2234 } 2235 2236 //---------------------------------------------------------------------- 2237 public function identifyMode($pos) 2238 { 2239 if ($pos >= strlen($this->dataStr)) 2240 return QR_MODE_NUL; 2241 2242 $c = $this->dataStr[$pos]; 2243 2244 if(self::isdigitat($this->dataStr, $pos)) { 2245 return QR_MODE_NUM; 2246 } else if(self::isalnumat($this->dataStr, $pos)) { 2247 return QR_MODE_AN; 2248 } else if($this->modeHint == QR_MODE_KANJI) { 2249 2250 if ($pos+1 < strlen($this->dataStr)) 2251 { 2252 $d = $this->dataStr[$pos+1]; 2253 $word = (ord($c) << 8) | ord($d); 2254 if(($word >= 0x8140 && $word <= 0x9ffc) || ($word >= 0xe040 && $word <= 0xebbf)) { 2255 return QR_MODE_KANJI; 2256 } 2257 } 2258 } 2259 2260 return QR_MODE_8; 2261 } 2262 2263 //---------------------------------------------------------------------- 2264 public function eatNum() 2265 { 2266 $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion()); 2267 2268 $p = 0; 2269 while(self::isdigitat($this->dataStr, $p)) { 2270 $p++; 2271 } 2272 2273 $run = $p; 2274 $mode = $this->identifyMode($p); 2275 2276 if($mode == QR_MODE_8) { 2277 $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln 2278 + QRinput::estimateBitsMode8(1) // + 4 + l8 2279 - QRinput::estimateBitsMode8($run + 1); // - 4 - l8 2280 if($dif > 0) { 2281 return $this->eat8(); 2282 } 2283 } 2284 if($mode == QR_MODE_AN) { 2285 $dif = QRinput::estimateBitsModeNum($run) + 4 + $ln 2286 + QRinput::estimateBitsModeAn(1) // + 4 + la 2287 - QRinput::estimateBitsModeAn($run + 1);// - 4 - la 2288 if($dif > 0) { 2289 return $this->eatAn(); 2290 } 2291 } 2292 2293 $ret = $this->input->append(QR_MODE_NUM, $run, str_split($this->dataStr)); 2294 if($ret < 0) 2295 return -1; 2296 2297 return $run; 2298 } 2299 2300 //---------------------------------------------------------------------- 2301 public function eatAn() 2302 { 2303 $la = QRspec::lengthIndicator(QR_MODE_AN, $this->input->getVersion()); 2304 $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion()); 2305 2306 $p = 0; 2307 2308 while(self::isalnumat($this->dataStr, $p)) { 2309 if(self::isdigitat($this->dataStr, $p)) { 2310 $q = $p; 2311 while(self::isdigitat($this->dataStr, $q)) { 2312 $q++; 2313 } 2314 2315 $dif = QRinput::estimateBitsModeAn($p) // + 4 + la 2316 + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln 2317 - QRinput::estimateBitsModeAn($q); // - 4 - la 2318 2319 if($dif < 0) { 2320 break; 2321 } else { 2322 $p = $q; 2323 } 2324 } else { 2325 $p++; 2326 } 2327 } 2328 2329 $run = $p; 2330 2331 if(!self::isalnumat($this->dataStr, $p)) { 2332 $dif = QRinput::estimateBitsModeAn($run) + 4 + $la 2333 + QRinput::estimateBitsMode8(1) // + 4 + l8 2334 - QRinput::estimateBitsMode8($run + 1); // - 4 - l8 2335 if($dif > 0) { 2336 return $this->eat8(); 2337 } 2338 } 2339 2340 $ret = $this->input->append(QR_MODE_AN, $run, str_split($this->dataStr)); 2341 if($ret < 0) 2342 return -1; 2343 2344 return $run; 2345 } 2346 2347 //---------------------------------------------------------------------- 2348 public function eatKanji() 2349 { 2350 $p = 0; 2351 2352 while($this->identifyMode($p) == QR_MODE_KANJI) { 2353 $p += 2; 2354 } 2355 2356 $ret = $this->input->append(QR_MODE_KANJI, $p, str_split($this->dataStr)); 2357 if($ret < 0) 2358 return -1; 2359 2360 return $run; 2361 } 2362 2363 //---------------------------------------------------------------------- 2364 public function eat8() 2365 { 2366 $la = QRspec::lengthIndicator(QR_MODE_AN, $this->input->getVersion()); 2367 $ln = QRspec::lengthIndicator(QR_MODE_NUM, $this->input->getVersion()); 2368 2369 $p = 1; 2370 $dataStrLen = strlen($this->dataStr); 2371 2372 while($p < $dataStrLen) { 2373 2374 $mode = $this->identifyMode($p); 2375 if($mode == QR_MODE_KANJI) { 2376 break; 2377 } 2378 if($mode == QR_MODE_NUM) { 2379 $q = $p; 2380 while(self::isdigitat($this->dataStr, $q)) { 2381 $q++; 2382 } 2383 $dif = QRinput::estimateBitsMode8($p) // + 4 + l8 2384 + QRinput::estimateBitsModeNum($q - $p) + 4 + $ln 2385 - QRinput::estimateBitsMode8($q); // - 4 - l8 2386 if($dif < 0) { 2387 break; 2388 } else { 2389 $p = $q; 2390 } 2391 } else if($mode == QR_MODE_AN) { 2392 $q = $p; 2393 while(self::isalnumat($this->dataStr, $q)) { 2394 $q++; 2395 } 2396 $dif = QRinput::estimateBitsMode8($p) // + 4 + l8 2397 + QRinput::estimateBitsModeAn($q - $p) + 4 + $la 2398 - QRinput::estimateBitsMode8($q); // - 4 - l8 2399 if($dif < 0) { 2400 break; 2401 } else { 2402 $p = $q; 2403 } 2404 } else { 2405 $p++; 2406 } 2407 } 2408 2409 $run = $p; 2410 $ret = $this->input->append(QR_MODE_8, $run, str_split($this->dataStr)); 2411 2412 if($ret < 0) 2413 return -1; 2414 2415 return $run; 2416 } 2417 2418 //---------------------------------------------------------------------- 2419 public function splitString() 2420 { 2421 while (strlen($this->dataStr) > 0) 2422 { 2423 if($this->dataStr == '') 2424 return 0; 2425 2426 $mode = $this->identifyMode(0); 2427 2428 switch ($mode) { 2429 case QR_MODE_NUM: $length = $this->eatNum(); break; 2430 case QR_MODE_AN: $length = $this->eatAn(); break; 2431 case QR_MODE_KANJI: 2432 if ($hint == QR_MODE_KANJI) 2433 $length = $this->eatKanji(); 2434 else $length = $this->eat8(); 2435 break; 2436 default: $length = $this->eat8(); break; 2437 2438 } 2439 2440 if($length == 0) return 0; 2441 if($length < 0) return -1; 2442 2443 $this->dataStr = substr($this->dataStr, $length); 2444 } 2445 } 2446 2447 //---------------------------------------------------------------------- 2448 public function toUpper() 2449 { 2450 $stringLen = strlen($this->dataStr); 2451 $p = 0; 2452 2453 while ($p<$stringLen) { 2454 $mode = self::identifyMode(substr($this->dataStr, $p), $this->modeHint); 2455 if($mode == QR_MODE_KANJI) { 2456 $p += 2; 2457 } else { 2458 if (ord($this->dataStr[$p]) >= ord('a') && ord($this->dataStr[$p]) <= ord('z')) { 2459 $this->dataStr[$p] = chr(ord($this->dataStr[$p]) - 32); 2460 } 2461 $p++; 2462 } 2463 } 2464 2465 return $this->dataStr; 2466 } 2467 2468 //---------------------------------------------------------------------- 2469 public static function splitStringToQRinput($string, QRinput $input, $modeHint, $casesensitive = true) 2470 { 2471 if(is_null($string) || $string == '\0' || $string == '') { 2472 throw new Exception('empty string!!!'); 2473 } 2474 2475 $split = new QRsplit($string, $input, $modeHint); 2476 2477 if(!$casesensitive) 2478 $split->toUpper(); 2479 2480 return $split->splitString(); 2481 } 2482 } 2483 2484 /** @} */ 2485 2486 2487 2488//---- qrrscode.php ----------------------------- 2489 2490 2491 2492 2493/* 2494 * PHP QR Code encoder 2495 * 2496 * Reed-Solomon error correction support 2497 * 2498 * Copyright (C) 2002, 2003, 2004, 2006 Phil Karn, KA9Q 2499 * (libfec is released under the GNU Lesser General Public License.) 2500 * 2501 * Based on libqrencode C library distributed under LGPL 2.1 2502 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net> 2503 * 2504 * PHP QR Code is distributed under LGPL 3 2505 * Copyright (C) 2010-2013 Dominik Dzienia <deltalab at poczta dot fm> 2506 * 2507 * This library is free software; you can redistribute it and/or 2508 * modify it under the terms of the GNU Lesser General Public 2509 * License as published by the Free Software Foundation; either 2510 * version 3 of the License, or any later version. 2511 * 2512 * This library is distributed in the hope that it will be useful, 2513 * but WITHOUT ANY WARRANTY; without even the implied warranty of 2514 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 2515 * Lesser General Public License for more details. 2516 * 2517 * You should have received a copy of the GNU Lesser General Public 2518 * License along with this library; if not, write to the Free Software 2519 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 2520 */ 2521 2522 /** @addtogroup CoreGroup */ 2523 /** @{ */ 2524 2525 /** Reed-Solomon encoder item */ 2526 class QRrsItem { 2527 2528 /** Bits per symbol */ 2529 public $mm; 2530 /** Symbols per block (= (1<<mm)-1) */ 2531 public $nn; 2532 /** Log lookup table */ 2533 public $alpha_to = array(); 2534 /** Antilog lookup table */ 2535 public $index_of = array(); 2536 /** Generator polynomial */ 2537 public $genpoly = array(); 2538 /** Number of generator roots = number of parity symbols */ 2539 public $nroots; 2540 /** First consecutive root, index form */ 2541 public $fcr; 2542 /** Primitive element, index form */ 2543 public $prim; 2544 /** Prim-th root of 1, index form */ 2545 public $iprim; 2546 /** Padding bytes in shortened block */ 2547 public $pad; 2548 /** Galois Field Polynomial */ 2549 public $gfpoly; 2550 2551 //---------------------------------------------------------------------- 2552 /** Modulo function in defined Field 2553 @param Integer $x number to be modulo-mapped 2554 */ 2555 public function modnn($x) 2556 { 2557 while ($x >= $this->nn) { 2558 $x -= $this->nn; 2559 $x = ($x >> $this->mm) + ($x & $this->nn); 2560 } 2561 2562 return $x; 2563 } 2564 2565 //---------------------------------------------------------------------- 2566 /** Encoder initialisation 2567 @param Integer $symsize symbol size, bit count (1..8) 2568 @param Integer $gfpoly Galois Field Polynomial 2569 @param Integer $fcr First consecutive root 2570 @param Integer $prim Primitive element 2571 @param Integer $nroots Number of generator roots = number of parity symbols 2572 @param Integer $pad Padding bytes in shortened block 2573 */ 2574 public static function init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) 2575 { 2576 // Common code for intializing a Reed-Solomon control block (char or int symbols) 2577 // Copyright 2004 Phil Karn, KA9Q 2578 // May be used under the terms of the GNU Lesser General Public License (LGPL) 2579 2580 $rs = null; 2581 2582 // Check parameter ranges 2583 if($symsize < 0 || $symsize > 8) return $rs; 2584 if($fcr < 0 || $fcr >= (1<<$symsize)) return $rs; 2585 if($prim <= 0 || $prim >= (1<<$symsize)) return $rs; 2586 if($nroots < 0 || $nroots >= (1<<$symsize)) return $rs; // Can't have more roots than symbol values! 2587 if($pad < 0 || $pad >= ((1<<$symsize) -1 - $nroots)) return $rs; // Too much padding 2588 2589 $rs = new QRrsItem(); 2590 $rs->mm = $symsize; 2591 $rs->nn = (1<<$symsize)-1; 2592 $rs->pad = $pad; 2593 2594 $rs->alpha_to = array_fill(0, $rs->nn+1, 0); 2595 $rs->index_of = array_fill(0, $rs->nn+1, 0); 2596 2597 // PHP style macro replacement ;) 2598 $NN =& $rs->nn; 2599 $A0 =& $NN; 2600 2601 // Generate Galois field lookup tables 2602 $rs->index_of[0] = $A0; // log(zero) = -inf 2603 $rs->alpha_to[$A0] = 0; // alpha**-inf = 0 2604 $sr = 1; 2605 2606 for($i=0; $i<$rs->nn; $i++) { 2607 $rs->index_of[$sr] = $i; 2608 $rs->alpha_to[$i] = $sr; 2609 $sr <<= 1; 2610 if($sr & (1<<$symsize)) { 2611 $sr ^= $gfpoly; 2612 } 2613 $sr &= $rs->nn; 2614 } 2615 2616 if($sr != 1){ 2617 // field generator polynomial is not primitive! 2618 $rs = NULL; 2619 return $rs; 2620 } 2621 2622 /* Form RS code generator polynomial from its roots */ 2623 $rs->genpoly = array_fill(0, $nroots+1, 0); 2624 2625 $rs->fcr = $fcr; 2626 $rs->prim = $prim; 2627 $rs->nroots = $nroots; 2628 $rs->gfpoly = $gfpoly; 2629 2630 /* Find prim-th root of 1, used in decoding */ 2631 for($iprim=1;($iprim % $prim) != 0;$iprim += $rs->nn) 2632 ; // intentional empty-body loop! 2633 2634 $rs->iprim = (int)($iprim / $prim); 2635 $rs->genpoly[0] = 1; 2636 2637 for ($i = 0,$root=$fcr*$prim; $i < $nroots; $i++, $root += $prim) { 2638 $rs->genpoly[$i+1] = 1; 2639 2640 // Multiply rs->genpoly[] by @**(root + x) 2641 for ($j = $i; $j > 0; $j--) { 2642 if ($rs->genpoly[$j] != 0) { 2643 $rs->genpoly[$j] = $rs->genpoly[$j-1] ^ $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[$j]] + $root)]; 2644 } else { 2645 $rs->genpoly[$j] = $rs->genpoly[$j-1]; 2646 } 2647 } 2648 // rs->genpoly[0] can never be zero 2649 $rs->genpoly[0] = $rs->alpha_to[$rs->modnn($rs->index_of[$rs->genpoly[0]] + $root)]; 2650 } 2651 2652 // convert rs->genpoly[] to index form for quicker encoding 2653 for ($i = 0; $i <= $nroots; $i++) 2654 $rs->genpoly[$i] = $rs->index_of[$rs->genpoly[$i]]; 2655 2656 return $rs; 2657 } 2658 2659 //---------------------------------------------------------------------- 2660 /** Appends char into encoder 2661 @param String input 2662 @param Array parity table 2663 */ 2664 public function encode_rs_char($data, &$parity) 2665 { 2666 $MM =& $this->mm; 2667 $NN =& $this->nn; 2668 $ALPHA_TO =& $this->alpha_to; 2669 $INDEX_OF =& $this->index_of; 2670 $GENPOLY =& $this->genpoly; 2671 $NROOTS =& $this->nroots; 2672 $FCR =& $this->fcr; 2673 $PRIM =& $this->prim; 2674 $IPRIM =& $this->iprim; 2675 $PAD =& $this->pad; 2676 $A0 =& $NN; 2677 2678 $parity = array_fill(0, $NROOTS, 0); 2679 2680 for($i=0; $i< ($NN-$NROOTS-$PAD); $i++) { 2681 2682 $feedback = $INDEX_OF[$data[$i] ^ $parity[0]]; 2683 if($feedback != $A0) { 2684 // feedback term is non-zero 2685 2686 // This line is unnecessary when GENPOLY[NROOTS] is unity, as it must 2687 // always be for the polynomials constructed by init_rs() 2688 $feedback = $this->modnn($NN - $GENPOLY[$NROOTS] + $feedback); 2689 2690 for($j=1;$j<$NROOTS;$j++) { 2691 $parity[$j] ^= $ALPHA_TO[$this->modnn($feedback + $GENPOLY[$NROOTS-$j])]; 2692 } 2693 } 2694 2695 // Shift 2696 array_shift($parity); 2697 if($feedback != $A0) { 2698 array_push($parity, $ALPHA_TO[$this->modnn($feedback + $GENPOLY[0])]); 2699 } else { 2700 array_push($parity, 0); 2701 } 2702 } 2703 } 2704 } 2705 2706 //########################################################################## 2707 /** Reed-Solomon encoder */ 2708 class QRrs { 2709 2710 /** Encoder items array */ 2711 public static $items = array(); 2712 2713 //---------------------------------------------------------------------- 2714 /** Encoder initialisation 2715 @param Integer $symsize symbol size, bit count (1..8) 2716 @param Integer $gfpoly Galois Field Polynomial 2717 @param Integer $fcr First consecutive root 2718 @param Integer $prim Primitive element 2719 @param Integer $nroots Number of generator roots = number of parity symbols 2720 @param Integer $pad Padding bytes in shortened block 2721 */ 2722 public static function init_rs($symsize, $gfpoly, $fcr, $prim, $nroots, $pad) 2723 { 2724 foreach(self::$items as $rs) { 2725 if($rs->pad != $pad) continue; 2726 if($rs->nroots != $nroots) continue; 2727 if($rs->mm != $symsize) continue; 2728 if($rs->gfpoly != $gfpoly) continue; 2729 if($rs->fcr != $fcr) continue; 2730 if($rs->prim != $prim) continue; 2731 2732 return $rs; 2733 } 2734 2735 $rs = QRrsItem::init_rs_char($symsize, $gfpoly, $fcr, $prim, $nroots, $pad); 2736 array_unshift(self::$items, $rs); 2737 2738 return $rs; 2739 } 2740 } 2741 2742 /** @}*/ 2743 2744 2745 2746//---- qrmask.php ----------------------------- 2747 2748 2749 2750 2751/* 2752 * PHP QR Code encoder 2753 * 2754 * Masking 2755 * 2756 * Based on libqrencode C library distributed under LGPL 2.1 2757 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net> 2758 * 2759 * PHP QR Code is distributed under LGPL 3 2760 * Copyright (C) 2010-2013 Dominik Dzienia <deltalab at poczta dot fm> 2761 * 2762 * This library is free software; you can redistribute it and/or 2763 * modify it under the terms of the GNU Lesser General Public 2764 * License as published by the Free Software Foundation; either 2765 * version 3 of the License, or any later version. 2766 * 2767 * This library is distributed in the hope that it will be useful, 2768 * but WITHOUT ANY WARRANTY; without even the implied warranty of 2769 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 2770 * Lesser General Public License for more details. 2771 * 2772 * You should have received a copy of the GNU Lesser General Public 2773 * License along with this library; if not, write to the Free Software 2774 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 2775 */ 2776 2777 2778 2779 define('N1', 3); 2780 define('N2', 3); 2781 define('N3', 40); 2782 define('N4', 10); 2783 2784 /** @addtogroup CoreGroup */ 2785 /** @{ */ 2786 2787 class QRmask { 2788 2789 public $runLength = array(); 2790 2791 //---------------------------------------------------------------------- 2792 public function __construct() 2793 { 2794 $this->runLength = array_fill(0, QRSPEC_WIDTH_MAX + 1, 0); 2795 } 2796 2797 //---------------------------------------------------------------------- 2798 public function writeFormatInformation($width, &$frame, $mask, $level) 2799 { 2800 $blacks = 0; 2801 $format = QRspec::getFormatInfo($mask, $level); 2802 2803 for($i=0; $i<8; $i++) { 2804 if($format & 1) { 2805 $blacks += 2; 2806 $v = 0x85; 2807 } else { 2808 $v = 0x84; 2809 } 2810 2811 $frame[8][$width - 1 - $i] = chr($v); 2812 if($i < 6) { 2813 $frame[$i][8] = chr($v); 2814 } else { 2815 $frame[$i + 1][8] = chr($v); 2816 } 2817 $format = $format >> 1; 2818 } 2819 2820 for($i=0; $i<7; $i++) { 2821 if($format & 1) { 2822 $blacks += 2; 2823 $v = 0x85; 2824 } else { 2825 $v = 0x84; 2826 } 2827 2828 $frame[$width - 7 + $i][8] = chr($v); 2829 if($i == 0) { 2830 $frame[8][7] = chr($v); 2831 } else { 2832 $frame[8][6 - $i] = chr($v); 2833 } 2834 2835 $format = $format >> 1; 2836 } 2837 2838 return $blacks; 2839 } 2840 2841 //---------------------------------------------------------------------- 2842 public function mask0($x, $y) { return ($x+$y)&1; } 2843 public function mask1($x, $y) { return ($y&1); } 2844 public function mask2($x, $y) { return ($x%3); } 2845 public function mask3($x, $y) { return ($x+$y)%3; } 2846 public function mask4($x, $y) { return (((int)($y/2))+((int)($x/3)))&1; } 2847 public function mask5($x, $y) { return (($x*$y)&1)+($x*$y)%3; } 2848 public function mask6($x, $y) { return ((($x*$y)&1)+($x*$y)%3)&1; } 2849 public function mask7($x, $y) { return ((($x*$y)%3)+(($x+$y)&1))&1; } 2850 2851 //---------------------------------------------------------------------- 2852 private function generateMaskNo($maskNo, $width, $frame) 2853 { 2854 $bitMask = array_fill(0, $width, array_fill(0, $width, 0)); 2855 2856 for($y=0; $y<$width; $y++) { 2857 for($x=0; $x<$width; $x++) { 2858 if(ord($frame[$y][$x]) & 0x80) { 2859 $bitMask[$y][$x] = 0; 2860 } else { 2861 $maskFunc = call_user_func(array($this, 'mask'.$maskNo), $x, $y); 2862 $bitMask[$y][$x] = ($maskFunc == 0)?1:0; 2863 } 2864 2865 } 2866 } 2867 2868 return $bitMask; 2869 } 2870 2871 //---------------------------------------------------------------------- 2872 public static function serial($bitFrame) 2873 { 2874 $codeArr = array(); 2875 2876 foreach ($bitFrame as $line) 2877 $codeArr[] = join('', $line); 2878 2879 return gzcompress(join("\n", $codeArr), 9); 2880 } 2881 2882 //---------------------------------------------------------------------- 2883 public static function unserial($code) 2884 { 2885 $codeArr = array(); 2886 2887 $codeLines = explode("\n", gzuncompress($code)); 2888 foreach ($codeLines as $line) 2889 $codeArr[] = str_split($line); 2890 2891 return $codeArr; 2892 } 2893 2894 //---------------------------------------------------------------------- 2895 public function makeMaskNo($maskNo, $width, $s, &$d, $maskGenOnly = false) 2896 { 2897 $b = 0; 2898 $bitMask = array(); 2899 2900 $fileName = QR_CACHE_DIR.'mask_'.$maskNo.DIRECTORY_SEPARATOR.'mask_'.$width.'_'.$maskNo.'.dat'; 2901 2902 if (QR_CACHEABLE) { 2903 if (file_exists($fileName)) { 2904 $bitMask = self::unserial(file_get_contents($fileName)); 2905 } else { 2906 $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d); 2907 if (!file_exists(QR_CACHE_DIR.'mask_'.$maskNo)) 2908 mkdir(QR_CACHE_DIR.'mask_'.$maskNo); 2909 file_put_contents($fileName, self::serial($bitMask)); 2910 } 2911 } else { 2912 $bitMask = $this->generateMaskNo($maskNo, $width, $s, $d); 2913 } 2914 2915 if ($maskGenOnly) 2916 return; 2917 2918 $d = $s; 2919 2920 for($y=0; $y<$width; $y++) { 2921 for($x=0; $x<$width; $x++) { 2922 if($bitMask[$y][$x] == 1) { 2923 $d[$y][$x] = chr(ord($s[$y][$x]) ^ (int)$bitMask[$y][$x]); 2924 } 2925 $b += (int)(ord($d[$y][$x]) & 1); 2926 } 2927 } 2928 2929 return $b; 2930 } 2931 2932 //---------------------------------------------------------------------- 2933 public function makeMask($width, $frame, $maskNo, $level) 2934 { 2935 $masked = array_fill(0, $width, str_repeat("\0", $width)); 2936 $this->makeMaskNo($maskNo, $width, $frame, $masked); 2937 $this->writeFormatInformation($width, $masked, $maskNo, $level); 2938 2939 return $masked; 2940 } 2941 2942 //---------------------------------------------------------------------- 2943 public function calcN1N3($length) 2944 { 2945 $demerit = 0; 2946 2947 for($i=0; $i<$length; $i++) { 2948 2949 if($this->runLength[$i] >= 5) { 2950 $demerit += (N1 + ($this->runLength[$i] - 5)); 2951 } 2952 if($i & 1) { 2953 if(($i >= 3) && ($i < ($length-2)) && ($this->runLength[$i] % 3 == 0)) { 2954 $fact = (int)($this->runLength[$i] / 3); 2955 if(($this->runLength[$i-2] == $fact) && 2956 ($this->runLength[$i-1] == $fact) && 2957 ($this->runLength[$i+1] == $fact) && 2958 ($this->runLength[$i+2] == $fact)) { 2959 if(($this->runLength[$i-3] < 0) || ($this->runLength[$i-3] >= (4 * $fact))) { 2960 $demerit += N3; 2961 } else if((($i+3) >= $length) || ($this->runLength[$i+3] >= (4 * $fact))) { 2962 $demerit += N3; 2963 } 2964 } 2965 } 2966 } 2967 } 2968 return $demerit; 2969 } 2970 2971 //---------------------------------------------------------------------- 2972 public function evaluateSymbol($width, $frame) 2973 { 2974 $head = 0; 2975 $demerit = 0; 2976 2977 for($y=0; $y<$width; $y++) { 2978 $head = 0; 2979 $this->runLength[0] = 1; 2980 2981 $frameY = $frame[$y]; 2982 2983 if ($y>0) 2984 $frameYM = $frame[$y-1]; 2985 2986 for($x=0; $x<$width; $x++) { 2987 if(($x > 0) && ($y > 0)) { 2988 $b22 = ord($frameY[$x]) & ord($frameY[$x-1]) & ord($frameYM[$x]) & ord($frameYM[$x-1]); 2989 $w22 = ord($frameY[$x]) | ord($frameY[$x-1]) | ord($frameYM[$x]) | ord($frameYM[$x-1]); 2990 2991 if(($b22 | ($w22 ^ 1))&1) { 2992 $demerit += N2; 2993 } 2994 } 2995 if(($x == 0) && (ord($frameY[$x]) & 1)) { 2996 $this->runLength[0] = -1; 2997 $head = 1; 2998 $this->runLength[$head] = 1; 2999 } else if($x > 0) { 3000 if((ord($frameY[$x]) ^ ord($frameY[$x-1])) & 1) { 3001 $head++; 3002 $this->runLength[$head] = 1; 3003 } else { 3004 $this->runLength[$head]++; 3005 } 3006 } 3007 } 3008 3009 $demerit += $this->calcN1N3($head+1); 3010 } 3011 3012 for($x=0; $x<$width; $x++) { 3013 $head = 0; 3014 $this->runLength[0] = 1; 3015 3016 for($y=0; $y<$width; $y++) { 3017 if($y == 0 && (ord($frame[$y][$x]) & 1)) { 3018 $this->runLength[0] = -1; 3019 $head = 1; 3020 $this->runLength[$head] = 1; 3021 } else if($y > 0) { 3022 if((ord($frame[$y][$x]) ^ ord($frame[$y-1][$x])) & 1) { 3023 $head++; 3024 $this->runLength[$head] = 1; 3025 } else { 3026 $this->runLength[$head]++; 3027 } 3028 } 3029 } 3030 3031 $demerit += $this->calcN1N3($head+1); 3032 } 3033 3034 return $demerit; 3035 } 3036 3037 3038 //---------------------------------------------------------------------- 3039 public function mask($width, $frame, $level) 3040 { 3041 $minDemerit = PHP_INT_MAX; 3042 $bestMaskNum = 0; 3043 $bestMask = array(); 3044 3045 $checked_masks = array(0,1,2,3,4,5,6,7); 3046 3047 if (QR_FIND_FROM_RANDOM !== false) { 3048 3049 $howManuOut = 8-(QR_FIND_FROM_RANDOM % 9); 3050 for ($i = 0; $i < $howManuOut; $i++) { 3051 $remPos = rand (0, count($checked_masks)-1); 3052 unset($checked_masks[$remPos]); 3053 $checked_masks = array_values($checked_masks); 3054 } 3055 3056 } 3057 3058 $bestMask = $frame; 3059 3060 foreach($checked_masks as $i) { 3061 $mask = array_fill(0, $width, str_repeat("\0", $width)); 3062 3063 $demerit = 0; 3064 $blacks = 0; 3065 $blacks = $this->makeMaskNo($i, $width, $frame, $mask); 3066 $blacks += $this->writeFormatInformation($width, $mask, $i, $level); 3067 $blacks = (int)(100 * $blacks / ($width * $width)); 3068 $demerit = (int)((int)(abs($blacks - 50) / 5) * N4); 3069 $demerit += $this->evaluateSymbol($width, $mask); 3070 3071 if($demerit < $minDemerit) { 3072 $minDemerit = $demerit; 3073 $bestMask = $mask; 3074 $bestMaskNum = $i; 3075 } 3076 } 3077 3078 return $bestMask; 3079 } 3080 3081 //---------------------------------------------------------------------- 3082 } 3083 3084 /** @}*/ 3085 3086 3087 3088//---- qrarea.php ----------------------------- 3089 3090 3091 3092 3093/* 3094 * PHP QR Code encoder 3095 * 3096 * Area finding for SVG and CANVAS output 3097 * 3098 * Based on libqrencode C library distributed under LGPL 2.1 3099 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net> 3100 * 3101 * PHP QR Code is distributed under LGPL 3 3102 * Copyright (C) 2010-2013 Dominik Dzienia <deltalab at poczta dot fm> 3103 * 3104 * This library is free software; you can redistribute it and/or 3105 * modify it under the terms of the GNU Lesser General Public 3106 * License as published by the Free Software Foundation; either 3107 * version 3 of the License, or any later version. 3108 * 3109 * This library is distributed in the hope that it will be useful, 3110 * but WITHOUT ANY WARRANTY; without even the implied warranty of 3111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 3112 * Lesser General Public License for more details. 3113 * 3114 * You should have received a copy of the GNU Lesser General Public 3115 * License along with this library; if not, write to the Free Software 3116 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 3117 */ 3118 3119 // N 3120 // W E 3121 // S 3122 3123 define('QR_AREA_N', 0); 3124 define('QR_AREA_E', 1); 3125 define('QR_AREA_S', 2); 3126 define('QR_AREA_W', 3); 3127 3128 define('QR_AREA_X', 0); 3129 define('QR_AREA_Y', 1); 3130 3131 define('QR_AREA_TRACKER', 0); 3132 define('QR_AREA_PATH', 1); 3133 define('QR_AREA_POINT', 2); 3134 define('QR_AREA_RECT', 3); 3135 define('QR_AREA_LSHAPE', 4); 3136 3137 /** @addtogroup OutputGroup */ 3138 /** @{ */ 3139 3140 class QRareaGroup { 3141 public $total = 0; 3142 public $vertical = false; 3143 public $horizontal = false; 3144 public $points = array(); 3145 public $id = 0; 3146 public $paths = array(); 3147 3148 //---------------------------------------------------------------------- 3149 public function __construct($selfId, $sx, $sy) 3150 { 3151 $this->total = 1; 3152 $this->points = array(array($sx,$sy,false)); 3153 $this->id = $selfId; 3154 } 3155 3156 } 3157 3158 //########################################################################## 3159 3160 class QRarea { 3161 3162 public $width = 0; 3163 private $tab = array(); 3164 private $tab_edges = array(); 3165 private $groups = array(); 3166 private $curr_group = 0; 3167 public $paths = array(); 3168 3169 3170 //---------------------------------------------------------------------- 3171 public function __construct($source_tab) 3172 { 3173 $py = 0; 3174 $this->width = count($source_tab); 3175 $this->tab = array(); 3176 $this->tab_edges = array(); 3177 $this->paths = array(); 3178 3179 foreach ($source_tab as $line) { 3180 $arr = array(); 3181 $arr_edge = array(); 3182 $px=0; 3183 3184 foreach (str_split($line) as $item) { 3185 3186 if ($py<7 && $px<7) 3187 $item = 0; 3188 3189 if ($py<7 && $px>=($this->width-7)) 3190 $item = 0; 3191 3192 if ($py>=($this->width-7) && $px<7) 3193 $item = 0; 3194 3195 $arr[] = (int)$item; 3196 $arr_edge[] = array(false, false, false, false); 3197 3198 $px++; 3199 } 3200 3201 $this->tab[] = $arr; 3202 $this->tab_edges[] = $arr_edge; 3203 $py++; 3204 } 3205 3206 $this->paths[] = array(QR_AREA_TRACKER, 0,0); 3207 $this->paths[] = array(QR_AREA_TRACKER, 0,($this->width-7)); 3208 $this->paths[] = array(QR_AREA_TRACKER, ($this->width-7),0); 3209 3210 $this->groups = array(); 3211 $this->curr_group = 1; 3212 } 3213 3214 //---------------------------------------------------------------------- 3215 public function getGroups() 3216 { 3217 return $this->groups; 3218 } 3219 3220 //---------------------------------------------------------------------- 3221 public function getPaths() 3222 { 3223 return $this->paths; 3224 } 3225 3226 //---------------------------------------------------------------------- 3227 public function getWidth() 3228 { 3229 return $this->width; 3230 } 3231 3232 //---------------------------------------------------------------------- 3233 public function dumpTab() 3234 { 3235 echo "<style>"; 3236 echo "td { height: 2.5em; color: black; font-size: 8px; 3237 border-top: 1px solid silver; border-left: 1px solid silver }"; 3238 echo "table { border-bottom: 1px solid silver; border-right: 1px solid silver }"; 3239 echo "</style>"; 3240 echo "<table border=0 cellpadding=0 cellspacing=0>"; 3241 3242 $colorTab = array(); 3243 3244 foreach($this->tab as $line) { 3245 foreach($line as $item) { 3246 if (!isset($colorTab[$item])) { 3247 $colorTab[$item] = 'hsl('.mt_rand(0, 360).', '.floor((mt_rand(0, 25))+75).'%, 50%)'; 3248 } 3249 } 3250 } 3251 3252 foreach($this->tab as $line) { 3253 echo "<tr>"; 3254 foreach($line as $item) { 3255 if ($item == 0) { 3256 echo "<td>&nbsp;</td>"; 3257 } else { 3258 echo "<td style='text-align:center;width: 4em;background:".$colorTab[$item]."'>".$item."</td>"; 3259 } 3260 } 3261 echo "</tr>"; 3262 } 3263 echo "</table>"; 3264 } 3265 3266 //---------------------------------------------------------------------- 3267 public function dumpEdges() 3268 { 3269 $style_off = '1px dotted silver;'; 3270 $style_on = '3px solid red;'; 3271 3272 $colorAlloc = array(); 3273 3274 echo "<table border='0'>"; 3275 $py = 0; 3276 foreach($this->tab_edges as $line) { 3277 $px = 0; 3278 echo "<tr>"; 3279 foreach($line as $item) { 3280 3281 $styles = 'border-top:'; 3282 if ($item[QR_AREA_N]) 3283 $styles .= $style_on; 3284 else $styles .= $style_off; 3285 3286 $styles .= 'border-bottom:'; 3287 if ($item[QR_AREA_S]) 3288 $styles .= $style_on; 3289 else $styles .= $style_off; 3290 3291 $styles .= 'border-right:'; 3292 if ($item[QR_AREA_E]) 3293 $styles .= $style_on; 3294 else $styles .= $style_off; 3295 3296 $styles .= 'border-left:'; 3297 if ($item[QR_AREA_W]) 3298 $styles .= $style_on; 3299 else $styles .= $style_off; 3300 3301 $color = ''; 3302 $grp = $this->tab[$py][$px]; 3303 3304 if ($grp>0) { 3305 if (!isset($colorAlloc[$grp])) { 3306 $colorAlloc[$grp] = 'hsl('.mt_rand(0, 360).', '.floor((mt_rand(0, 25))+75).'%, 50%)'; 3307 } 3308 3309 $color = 'background:'.$colorAlloc[$grp]; 3310 } 3311 3312 if ($grp == 0) 3313 $grp = '&nbsp;'; 3314 3315 echo "<td style='text-align:center;width:1.5em;".$styles.$color."'>".$grp."</td>"; 3316 $px++; 3317 } 3318 echo "</tr>"; 3319 $py++; 3320 } 3321 echo "</table>"; 3322 } 3323 3324 //---------------------------------------------------------------------- 3325 private static function rle(&$stringData) 3326 { 3327 $outArray = array(); 3328 $symbolArray = str_split($stringData); 3329 $last = ''; 3330 $run = 1; 3331 3332 while (count($symbolArray) > 0) { 3333 $symbol = array_shift($symbolArray); 3334 3335 if ($symbol != $last) { 3336 if ($run > 1) 3337 $outArray[] = $run; 3338 3339 if ($last != '') 3340 $outArray[] = $last; 3341 3342 $run = 1; 3343 $last = $symbol; 3344 } else { 3345 $run++; 3346 } 3347 } 3348 3349 if ($run > 1) 3350 $outArray[] = $run; 3351 3352 $outArray[] = $last; 3353 3354 $stringData = $outArray; 3355 } 3356 3357 3358 //---------------------------------------------------------------------- 3359 private function getAt($posx, $posy) 3360 { 3361 if (($posx<0)||($posy<0)||($posx>=$this->width)||($posy>=$this->width)) 3362 return 0; 3363 3364 return $this->tab[$posy][$posx]; 3365 } 3366 3367 //---------------------------------------------------------------------- 3368 private function getOnElem($elem, $deltax = 0, $deltay = 0) 3369 { 3370 $posx = $elem[0]+$deltax; 3371 $posy = $elem[1]+$deltay; 3372 3373 if (($posx<0)||($posy<0)||($posx>=$this->width)||($posy>=$this->width)) 3374 return 0; 3375 3376 return $this->tab[$posy][$posx]; 3377 } 3378 3379 //---------------------------------------------------------------------- 3380 private function addGroupElement($groupId, $h, $v, $sx, $sy) 3381 { 3382 $this->groups[$groupId]->total++; 3383 if ($h) 3384 $this->groups[$groupId]->horizontal = true; 3385 if ($v) 3386 $this->groups[$groupId]->vertical = true; 3387 $this->groups[$groupId]->points[] = array($sx, $sy, false); 3388 } 3389 3390 //---------------------------------------------------------------------- 3391 public function detectGroups() 3392 { 3393 for ($sy = 0; $sy < $this->width; $sy++) { 3394 for ($sx = 0; $sx < $this->width; $sx++) { 3395 3396 if ($this->tab[$sy][$sx] == 1) { // non-allocated 3397 3398 $gid_left = 0; 3399 $gid_top = 0; 3400 3401 $grouped = false; 3402 3403 if ($sx>0) { 3404 3405 $gid_left = $this->tab[$sy][$sx-1]; // previous on left 3406 3407 if ($gid_left > 1) { // if already in group 3408 $this->tab[$sy][$sx] = $gid_left; 3409 $grouped = true; 3410 $this->addGroupElement($gid_left, true, false, $sx, $sy); 3411 } 3412 } 3413 3414 if ($sy > 0) { 3415 3416 $gid_top = $this->tab[$sy-1][$sx]; // previous on top 3417 3418 if ($gid_top > 1) { //if in group 3419 if (!$grouped) { // and not grouped 3420 3421 $this->tab[$sy][$sx] = $gid_top; 3422 $grouped = true; 3423 3424 $this->addGroupElement($gid_top, false, true, $sx, $sy); 3425 3426 } else if($gid_top != $gid_left) { // was in left group 3427 3428 $grouped = true; 3429 3430 $this->groups[$gid_top]->vertical = true; 3431 $this->groups[$gid_top]->horizontal = true; 3432 3433 $this->groups[$gid_top]->total = $this->groups[$gid_top]->total + $this->groups[$gid_left]->total; 3434 3435 foreach($this->groups[$gid_left]->points as $elem) 3436 $this->tab[$elem[1]][$elem[0]] = $gid_top; 3437 3438 $this->groups[$gid_top]->points = array_values(array_merge($this->groups[$gid_top]->points, $this->groups[$gid_left]->points)); 3439 unset($this->groups[$gid_left]); 3440 3441 //refarb group 3442 } 3443 } 3444 } 3445 3446 if (!$grouped) { 3447 $this->curr_group++; 3448 $this->tab[$sy][$sx] = $this->curr_group; 3449 $this->groups[$this->curr_group] = new QRareaGroup($this->curr_group, $sx, $sy); 3450 } 3451 3452 } 3453 } 3454 } 3455 } 3456 3457 //---------------------------------------------------------------------- 3458 private function detectSquare($group) 3459 { 3460 $max_x = 0; 3461 $max_y = 0; 3462 $min_x = $this->width; 3463 $min_y = $this->width; 3464 3465 foreach($group->points as $elem) { 3466 $min_x = min($min_x, $elem[QR_AREA_X]); 3467 $max_x = max($max_x, $elem[QR_AREA_X]); 3468 $min_y = min($min_y, $elem[QR_AREA_Y]); 3469 $max_y = max($max_y, $elem[QR_AREA_Y]); 3470 } 3471 3472 return array($min_x, $min_y, $max_x+1, $max_y+1); 3473 } 3474 3475 //---------------------------------------------------------------------- 3476 public function detectAreas() 3477 { 3478 $squares = array(); 3479 $points = array(); 3480 $lshapes = array(); 3481 3482 foreach ($this->groups as $groupId=>&$group) { 3483 if ($group->total > 3) { 3484 3485 if ((!$group->vertical)||(!$group->horizontal)) { 3486 3487 $squareCoord = $this->detectSquare($group); 3488 array_unshift($squareCoord, QR_AREA_RECT); 3489 3490 $this->paths[] = $squareCoord; 3491 3492 } else { 3493 3494 $this->detectPaths($group); 3495 unset($group->points); 3496 3497 foreach($group->paths as &$path) 3498 self::rle($path[2]); 3499 3500 $this->paths[] = array(QR_AREA_PATH, $group->paths); 3501 } 3502 } else if (($group->total == 3)&&($group->vertical)&&($group->horizontal)) { 3503 $squareCoord = $this->detectSquare($group); 3504 $variant = 0; 3505 3506 if ($this->getOnElem($squareCoord, 0, 0) != $group->id) 3507 $variant = 0; 3508 3509 if ($this->getOnElem($squareCoord, 1, 0) != $group->id) 3510 $variant = 1; 3511 3512 if ($this->getOnElem($squareCoord, 0, 1) != $group->id) 3513 $variant = 2; 3514 3515 if ($this->getOnElem($squareCoord, 1, 1) != $group->id) 3516 $variant = 3; 3517 3518 $lshapes[] = $squareCoord[QR_AREA_X]; 3519 $lshapes[] = $squareCoord[QR_AREA_Y]; 3520 $lshapes[] = $variant; 3521 3522 } else if ($group->total >= 2) { 3523 $squareCoord = $this->detectSquare($group); 3524 $squares = array_merge($squares, $squareCoord); 3525 } else if ($group->total == 1) { 3526 $points[] = $group->points[0][0]; 3527 $points[] = $group->points[0][1]; 3528 } 3529 } 3530 3531 if (count($points) > 0) { 3532 array_unshift($points, QR_AREA_POINT); 3533 $this->paths[] = $points; 3534 } 3535 3536 if (count($squares) > 0) { 3537 array_unshift($squares, QR_AREA_RECT); 3538 $this->paths[] = $squares; 3539 } 3540 3541 if (count($lshapes) > 0) { 3542 array_unshift($lshapes, QR_AREA_LSHAPE); 3543 $this->paths[] = $lshapes; 3544 } 3545 } 3546 3547 //---------------------------------------------------------------------- 3548 private function reserveEdgeOnElem($elem, $edgeNo) 3549 { 3550 $this->tab_edges[$elem[QR_AREA_Y]][$elem[QR_AREA_X]][$edgeNo] = true; 3551 } 3552 3553 //---------------------------------------------------------------------- 3554 private function reserveEdge($px, $py, $edgeNo) 3555 { 3556 $this->tab_edges[$py][$px][$edgeNo] = true; 3557 } 3558 3559 //---------------------------------------------------------------------- 3560 private function markAdjacentEdges($group) 3561 { 3562 foreach($group->points as $elem) { 3563 if ($this->getOnElem($elem, -1, 0) == $group->id) 3564 $this->reserveEdgeOnElem($elem, QR_AREA_W); 3565 3566 if ($this->getOnElem($elem, +1, 0) == $group->id) 3567 $this->reserveEdgeOnElem($elem, QR_AREA_E); 3568 3569 if ($this->getOnElem($elem, 0, -1) == $group->id) 3570 $this->reserveEdgeOnElem($elem, QR_AREA_N); 3571 3572 if ($this->getOnElem($elem, 0, +1) == $group->id) 3573 $this->reserveEdgeOnElem($elem, QR_AREA_S); 3574 } 3575 } 3576 3577 //---------------------------------------------------------------------- 3578 private function detectPaths(&$group) 3579 { 3580 $this->markAdjacentEdges($group); 3581 3582 $elem = $group->points[0]; 3583 $waylist = $this->findPath($group, $elem[QR_AREA_X], $elem[QR_AREA_Y]); 3584 $group->paths[] = array($elem[QR_AREA_X], $elem[QR_AREA_Y], $waylist); 3585 3586 $tab = array(); 3587 foreach($group->points as $elem) { 3588 3589 $edgeTab = $this->tab_edges[$elem[QR_AREA_Y]][$elem[QR_AREA_X]]; 3590 3591 if (!( $edgeTab[QR_AREA_N] 3592 && $edgeTab[QR_AREA_E] 3593 && $edgeTab[QR_AREA_S] 3594 && $edgeTab[QR_AREA_W])) { 3595 3596 if (!$edgeTab[QR_AREA_S]) { 3597 3598 $waylistw = $this->findPath($group, $elem[QR_AREA_X], $elem[QR_AREA_Y]+1); 3599 $group->paths[] = array($elem[QR_AREA_X], $elem[QR_AREA_Y]+1, $waylistw); 3600 } 3601 } 3602 } 3603 3604 } 3605 3606 //---------------------------------------------------------------------- 3607 private function findPath($group, $sx, $sy) 3608 { 3609 $px = $sx; 3610 $py = $sy; 3611 3612 $waylist = ''; 3613 $dir = ''; 3614 $lastdir = ''; 3615 3616 $moves = array( 3617 // magic :) 3618 0=>'', 1=>'L', 2=>'T', 3=>'L', 4=>'B', 5=>'B', 6=>'B,T', 7=>'B' 3619 ,8=>'R', 9=>'R,L', 10=>'T', 11=>'L',12=>'R',13=>'R',14=>'T',15=>'' 3620 ); 3621 3622 do 3623 { 3624 $Q = ($this->getAt($px-1, $py-1) == $group->id)?1:0; 3625 $Q += ($this->getAt($px, $py-1) == $group->id)?2:0; 3626 $Q += ($this->getAt($px-1, $py) == $group->id)?4:0; 3627 $Q += ($this->getAt($px, $py) == $group->id)?8:0; 3628 3629 if ($moves[$Q] == '') 3630 throw new Exception('It should NEVER happened!'); 3631 3632 $move_expl = explode(',', $moves[$Q]); 3633 $have_way = false; 3634 3635 $dir = ''; 3636 3637 while ((count($move_expl) > 0)&&($have_way == false)) { 3638 $way = array_shift($move_expl); 3639 3640 if (($have_way==false)&&($way=='R')&&($this->tab_edges[$py][$px][QR_AREA_N]==false)) { 3641 $have_way = true; 3642 $dir = $way; 3643 $this->reserveEdge($px, $py, QR_AREA_N); 3644 $px++; 3645 } 3646 3647 if (($have_way==false)&&($way=='B')&&($this->tab_edges[$py][$px-1][QR_AREA_E]==false)) { 3648 $have_way = true; 3649 $dir = $way; 3650 $this->reserveEdge($px-1, $py, QR_AREA_E); 3651 $py++; 3652 } 3653 3654 if (($have_way==false)&&($way=='L')&&($this->tab_edges[$py-1][$px-1][QR_AREA_S]==false)) { 3655 $have_way = true; 3656 $dir = $way; 3657 $this->reserveEdge($px-1, $py-1, QR_AREA_S); 3658 $px--; 3659 } 3660 3661 if (($have_way==false)&&($way=='T')&&($this->tab_edges[$py-1][$px][QR_AREA_W]==false)) { 3662 $have_way = true; 3663 $dir = $way; 3664 $this->reserveEdge($px, $py-1, QR_AREA_W); 3665 $py--; 3666 } 3667 } 3668 3669 $waylist .= $dir; 3670 3671 } while (!(($px==$sx)&&($py==$sy))); 3672 3673 return $waylist; 3674 } 3675 } 3676 3677 /** @} */ 3678 3679 3680 3681//---- qrcanvas.php ----------------------------- 3682 3683 3684 3685 3686/* 3687 * PHP QR Code encoder 3688 * 3689 * CANVAS output 3690 * 3691 * Based on libqrencode C library distributed under LGPL 2.1 3692 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net> 3693 * 3694 * PHP QR Code is distributed under LGPL 3 3695 * Copyright (C) 2010-2013 Dominik Dzienia <deltalab at poczta dot fm> 3696 * 3697 * This library is free software; you can redistribute it and/or 3698 * modify it under the terms of the GNU Lesser General Public 3699 * License as published by the Free Software Foundation; either 3700 * version 3 of the License, or any later version. 3701 * 3702 * This library is distributed in the hope that it will be useful, 3703 * but WITHOUT ANY WARRANTY; without even the implied warranty of 3704 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 3705 * Lesser General Public License for more details. 3706 * 3707 * You should have received a copy of the GNU Lesser General Public 3708 * License along with this library; if not, write to the Free Software 3709 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 3710 */ 3711 3712 /** @addtogroup OutputGroup */ 3713 /** @{ */ 3714 3715 class QRcanvasOutput extends QRarea { 3716 3717 public function __construct($source_tab) 3718 { 3719 parent::__construct($source_tab); 3720 } 3721 3722 //---------------------------------------------------------------------- 3723 public static function encodeNum($num) 3724 { 3725 $addTab = array(0=>'', 1=>'z', 2=>'Z', 3=>'+'); 3726 $map = '0123456789abcdefghijklmnopqrstuvwxyABCDEFGHIJKLMNOPQRSTUVWXY'; 3727 $mapPos = $num % 60; 3728 $mapAdd = (int)($num / 60); 3729 3730 return $addTab[$mapAdd].$map[$mapPos]; 3731 } 3732 3733 //---------------------------------------------------------------------- 3734 public static function compact_path(&$pathTab) 3735 { 3736 if (count($pathTab) == 0) { 3737 $pathTab = ''; 3738 } else { 3739 $pathTab = count($pathTab).','.join(',', $pathTab); 3740 } 3741 } 3742 3743 //---------------------------------------------------------------------- 3744 public static function compact_points(&$pointsTab) 3745 { 3746 if (count($pointsTab) == 0) { 3747 $pointsTab = ''; 3748 } else { 3749 $compacted = ''; 3750 foreach ($pointsTab as $point) 3751 $compacted .= self::encodeNum($point); 3752 $pointsTab = $compacted; 3753 } 3754 } 3755 3756 //---------------------------------------------------------------------- 3757 public static function compactCanvasCommands($ops) 3758 { 3759 $accumulated = array(); 3760 3761 $accumulated['SR'] = array(); 3762 $accumulated['WR'] = array(); 3763 $accumulated['SP'] = array(); 3764 $accumulated['WP'] = array(); 3765 $accumulated['SB'] = array(); 3766 $accumulated['WB'] = array(); 3767 $accumulated['SO'] = array(); 3768 3769 while (count($ops) > 0) { 3770 $color = array_shift($ops); 3771 $opcode = array_shift($ops); 3772 3773 if (($opcode == 'R') || ($opcode == 'P')) { 3774 3775 do { 3776 $num = array_shift($ops); 3777 if (is_int($num)) { 3778 $accumulated[$color.$opcode][] = $num; 3779 } else { 3780 array_unshift($ops, $num); 3781 } 3782 3783 } while ((count($ops) > 0)&&(is_int($num))); 3784 3785 3786 } else if ($opcode == 'B') { 3787 3788 array_shift($ops); 3789 3790 $px = array_shift($ops); 3791 $py = array_shift($ops); 3792 3793 array_shift($ops); 3794 3795 $conftab = array(); 3796 $num = array_shift($ops); 3797 3798 while ((count($ops) > 0)&&(!($num === 'E'))) { 3799 $conftab[] = $num; 3800 $num = array_shift($ops); 3801 } 3802 3803 $cc = count($conftab); 3804 $deltas = ''; 3805 3806 $lastposx = $px; 3807 $lastposy = $py; 3808 3809 for($pos=0;$pos <$cc; $pos+=2) { 3810 3811 $dx = $lastposx - $conftab[$pos]; 3812 $dy = $lastposy - $conftab[$pos+1]; 3813 3814 $lastposx = $conftab[$pos]; 3815 $lastposy = $conftab[$pos+1]; 3816 3817 if ($dx < 0) { 3818 $deltas .= chr(ord('a')-1-$dx); 3819 } else if ($dx > 0) { 3820 $deltas .= chr(ord('A')-1+$dx); 3821 } else { 3822 $deltas .= '0'; 3823 } 3824 3825 if ($dy < 0) { 3826 $deltas .= chr(ord('a')-1-$dy); 3827 } else if ($dy > 0) { 3828 $deltas .= chr(ord('A')-1+$dy); 3829 } else { 3830 $deltas .= '0'; 3831 } 3832 3833 } 3834 3835 $deltas = strtr($deltas, array( 3836 '00'=>'1', 3837 'aa'=>'2', 3838 'aA'=>'3', 3839 'Aa'=>'4', 3840 'AA'=>'5', 3841 'aB'=>'6', 3842 'Ab'=>'7', 3843 'bA'=>'8', 3844 'Ba'=>'9' 3845 )); 3846 3847 $accumulated[$color.$opcode][] = join(',', array($px, $py, $deltas)); 3848 } else if ($opcode == 'O') { 3849 $px = array_shift($ops); 3850 $py = array_shift($ops); 3851 3852 $accumulated[$color.$opcode][] = join(',', array($px, $py)); 3853 } 3854 } 3855 3856 self::compact_points($accumulated['SR']); 3857 self::compact_points($accumulated['WR']); 3858 self::compact_points($accumulated['SP']); 3859 self::compact_points($accumulated['WP']); 3860 3861 self::compact_path($accumulated['SB']); 3862 self::compact_path($accumulated['WB']); 3863 3864 if (count($accumulated['SO']) > 0) 3865 $accumulated['SO'] = join(',',$accumulated['SO']); 3866 else $accumulated['SO'] = ''; 3867 3868 $mapping = array( 3869 'SO'=>'O', 3870 'SB'=>'B', 3871 'WB'=>'b', 3872 'SR'=>'R', 3873 'WR'=>'r', 3874 'SP'=>'P', 3875 'WP'=>'p' 3876 ); 3877 3878 $whole = array(); 3879 3880 foreach($mapping as $key=>$symb) { 3881 if ($accumulated[$key]!='') 3882 $whole[] = $symb.','.$accumulated[$key]; 3883 } 3884 3885 return join(',', $whole); 3886 } 3887 3888 3889 //---------------------------------------------------------------------- 3890 public function getCanvasOps() 3891 { 3892 $ops = array(); 3893 3894 foreach ($this->paths as $path) { 3895 switch ($path[0]) { 3896 case QR_AREA_PATH: 3897 $pNum = 0; 3898 3899 foreach($path[1] as $pathDetails) { 3900 if ($pNum == 0) { 3901 $ops[] = 'S'; 3902 } else if ($pNum > 0) { 3903 $ops[] = 'W'; 3904 } 3905 3906 $ops[] = 'B'; 3907 3908 $px = array_shift($pathDetails); 3909 $py = array_shift($pathDetails); 3910 3911 $ops[] = 'M'; 3912 $ops[] = $px; 3913 $ops[] = $py; 3914 3915 $rle_steps = array_shift($pathDetails); 3916 3917 $lastOp = ''; 3918 3919 while(count($rle_steps) > 0) { 3920 3921 $delta = 1; 3922 3923 $operator = array_shift($rle_steps); 3924 if (($operator != 'R') && ($operator != 'L') && ($operator != 'T') && ($operator != 'B')) { 3925 $delta = (int)$operator; 3926 $operator = array_shift($rle_steps); 3927 } 3928 3929 if ($operator == 'R') $px += $delta; 3930 if ($operator == 'L') $px -= $delta; 3931 if ($operator == 'T') $py -= $delta; 3932 if ($operator == 'B') $py += $delta; 3933 3934 if ($lastOp != 'T') 3935 $ops[] = 'T'; 3936 3937 $ops[] = $px; 3938 $ops[] = $py; 3939 3940 $lastOp = 'T'; 3941 } 3942 3943 $ops[] = 'E'; 3944 3945 $pNum++; 3946 } 3947 3948 break; 3949 case QR_AREA_POINT: 3950 3951 $symb = array_shift($path); 3952 3953 $ops[] = 'S'; 3954 3955 $lastOp = ''; 3956 3957 while(count($path) > 0) { 3958 $px = array_shift($path); 3959 $py = array_shift($path); 3960 3961 if ($lastOp != 'P') 3962 $ops[] = 'P'; 3963 3964 $ops[] = $px; 3965 $ops[] = $py; 3966 3967 $lastOp = 'P'; 3968 } 3969 3970 break; 3971 3972 case QR_AREA_RECT: 3973 3974 $symb = array_shift($path); 3975 3976 $ops[] = 'S'; 3977 3978 $lastOp = ''; 3979 3980 while(count($path) > 0) { 3981 $px = array_shift($path); 3982 $py = array_shift($path); 3983 $ex = array_shift($path); 3984 $ey = array_shift($path); 3985 3986 if ($lastOp != 'R') 3987 $ops[] = 'R'; 3988 3989 $ops[] = $px; 3990 $ops[] = $py; 3991 $ops[] = $ex-$px; 3992 $ops[] = $ey-$py; 3993 3994 $lastOp = 'R'; 3995 } 3996 3997 break; 3998 3999 case QR_AREA_LSHAPE: 4000 4001 $symb = array_shift($path); 4002 4003 while(count($path) > 0) { 4004 $px = array_shift($path); 4005 $py = array_shift($path); 4006 $mode = (int)array_shift($path); 4007 4008 $pxd = ($mode % 2)?1:0; 4009 $pyd = ($mode > 1)?1:0; 4010 4011 $ops[] = 'S'; 4012 $ops[] = 'R'; 4013 $ops[] = $px; 4014 $ops[] = $py; 4015 $ops[] = 2; 4016 $ops[] = 2; 4017 4018 $ops[] = 'W'; 4019 $ops[] = 'P'; 4020 $ops[] = $px+$pxd; 4021 $ops[] = $py+$pyd; 4022 } 4023 4024 break; 4025 4026 case QR_AREA_TRACKER: 4027 4028 $symb = array_shift($path); 4029 4030 $px = array_shift($path); 4031 $py = array_shift($path); 4032 4033 $ops[] = 'S'; 4034 $ops[] = 'O'; 4035 $ops[] = $px; 4036 $ops[] = $py; 4037 4038 break; 4039 } 4040 } 4041 4042 return self::compactCanvasCommands($ops); 4043 } 4044 } 4045 4046 /** @} */ 4047 4048 4049 4050 4051//---- qrsvg.php ----------------------------- 4052 4053 4054 4055 4056/* 4057 * PHP QR Code encoder 4058 * 4059 * SVG output support 4060 * 4061 * Based on libqrencode C library distributed under LGPL 2.1 4062 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net> 4063 * 4064 * PHP QR Code is distributed under LGPL 3 4065 * Copyright (C) 2010-2013 Dominik Dzienia <deltalab at poczta dot fm> 4066 * 4067 * This library is free software; you can redistribute it and/or 4068 * modify it under the terms of the GNU Lesser General Public 4069 * License as published by the Free Software Foundation; either 4070 * version 3 of the License, or any later version. 4071 * 4072 * This library is distributed in the hope that it will be useful, 4073 * but WITHOUT ANY WARRANTY; without even the implied warranty of 4074 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 4075 * Lesser General Public License for more details. 4076 * 4077 * You should have received a copy of the GNU Lesser General Public 4078 * License along with this library; if not, write to the Free Software 4079 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 4080 */ 4081 4082 /** @addtogroup OutputGroup */ 4083 /** @{ */ 4084 4085 class QRsvgOutput extends QRarea { 4086 4087 public function __construct($source_tab) 4088 { 4089 parent::__construct($source_tab); 4090 } 4091 4092 //---------------------------------------------------------------------- 4093 public function mapX($px) 4094 { 4095 return $px; 4096 } 4097 4098 //---------------------------------------------------------------------- 4099 public function mapY($py) 4100 { 4101 return $py; 4102 } 4103 4104 //---------------------------------------------------------------------- 4105 public function getRawSvg() 4106 { 4107 $lib = array(); 4108 $svg = array(); 4109 4110 $aggregate_paths = array(); 4111 4112 foreach ($this->paths as $path) { 4113 switch ($path[0]) { 4114 case QR_AREA_PATH: 4115 $pNum = 0; 4116 4117 foreach($path[1] as $pathDetails) { 4118 4119 $px = array_shift($pathDetails); 4120 $py = array_shift($pathDetails); 4121 $rle_steps = array_shift($pathDetails); 4122 4123 $aggregate_add = 'M'.$px.','.$py.' '; 4124 4125 while(count($rle_steps) > 0) { 4126 4127 $delta = 1; 4128 4129 $operator = array_shift($rle_steps); 4130 if (($operator != 'R') && ($operator != 'L') && ($operator != 'T') && ($operator != 'B')) { 4131 $delta = (int)$operator; 4132 $operator = array_shift($rle_steps); 4133 } 4134 4135 if ($operator == 'R') $aggregate_add .= 'h'.$delta; 4136 if ($operator == 'L') $aggregate_add .= 'h-'.$delta; 4137 if ($operator == 'T') $aggregate_add .= 'v-'.$delta; 4138 if ($operator == 'B') $aggregate_add .= 'v'.$delta; 4139 } 4140 4141 $aggregate_paths[] = $aggregate_add; 4142 4143 $pNum++; 4144 } 4145 4146 break; 4147 case QR_AREA_POINT: 4148 4149 $symb = array_shift($path); 4150 4151 while(count($path) > 0) { 4152 $px = array_shift($path); 4153 $py = array_shift($path); 4154 4155 $aggregate_paths[] = 'M'.$px.','.$py.' v1h1v-1h-1'; 4156 } 4157 4158 break; 4159 4160 case QR_AREA_RECT: 4161 4162 $symb = array_shift($path); 4163 4164 while(count($path) > 0) { 4165 $px = array_shift($path); 4166 $py = array_shift($path); 4167 $ex = array_shift($path); 4168 $ey = array_shift($path); 4169 4170 $w = $ex-$px; 4171 $h = $ey-$py; 4172 4173 $aggregate_paths[] = 'M'.$px.','.$py.' h'.$w.'v'.$h.'h-'.$w.'v-'.$h; 4174 } 4175 4176 break; 4177 4178 case QR_AREA_LSHAPE: 4179 4180 $symb = array_shift($path); 4181 4182 $l_shapes[0] = 'm1,0h1v2h-2v-1h1z'; 4183 $l_shapes[1] = 'h1v1h1v1h-2z'; 4184 $l_shapes[2] = 'h2v2h-1v-1h-1z'; 4185 $l_shapes[3] = 'h2v1h-1v1h-1z'; 4186 4187 while(count($path) > 0) { 4188 $px = array_shift($path); 4189 $py = array_shift($path); 4190 $mode = (int)array_shift($path); 4191 4192 $aggregate_paths[] = 'M'.$px.','.$py.' '.$l_shapes[$mode]; 4193 } 4194 4195 break; 4196 4197 case QR_AREA_TRACKER: 4198 4199 if (!isset($lib['tracker'])) { 4200 $lib['tracker'] = '<symbol id="tracker"><path d="m 0 7 0 7 7 0 0 -7 -7 0 z m 1 1 5 0 0 5 -5 0 0 -5 z m 1 1 0 3 3 0 0 -3 -3 0 z" style="fill:#000000;stroke:none"></path></symbol>'; 4201 } 4202 4203 $symb = array_shift($path); 4204 4205 $px = array_shift($path); 4206 $py = array_shift($path); 4207 4208 $svg[] = '<use x="'.$px.'" y="'.($py-7).'" xlink:href="#tracker"></use>'; 4209 4210 break; 4211 } 4212 } 4213 4214 $svg[] = '<path d="'.join(' ', $aggregate_paths).'" style="fill:#000000;stroke:none" ></path>'; 4215 4216 4217 4218 return join("\n", $lib)."\n".join("\n", $svg); 4219 } 4220 } 4221 4222 /** @} */ 4223 4224 4225 4226 4227//---- qrencode.php ----------------------------- 4228 4229 4230 4231 4232/* 4233 * PHP QR Code encoder 4234 * 4235 * Main encoder classes. 4236 * 4237 * Based on libqrencode C library distributed under LGPL 2.1 4238 * Copyright (C) 2006, 2007, 2008, 2009 Kentaro Fukuchi <fukuchi@megaui.net> 4239 * 4240 * PHP QR Code is distributed under LGPL 3 4241 * Copyright (C) 2010-2013 Dominik Dzienia <deltalab at poczta dot fm> 4242 * 4243 * This library is free software; you can redistribute it and/or 4244 * modify it under the terms of the GNU Lesser General Public 4245 * License as published by the Free Software Foundation; either 4246 * version 3 of the License, or any later version. 4247 * 4248 * This library is distributed in the hope that it will be useful, 4249 * but WITHOUT ANY WARRANTY; without even the implied warranty of 4250 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 4251 * Lesser General Public License for more details. 4252 * 4253 * You should have received a copy of the GNU Lesser General Public 4254 * License along with this library; if not, write to the Free Software 4255 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 4256 */ 4257 4258 /** @defgroup CoreGroup Standard API Core 4259 Core encoder classes */ 4260 4261 /** @addtogroup CoreGroup */ 4262 /** @{ */ 4263 4264 //########################################################################## 4265 /** 4266 Data block with raw data and it's Error Correction Code data. 4267 */ 4268 class QRrsblock { 4269 public $dataLength; 4270 public $data = array(); 4271 public $eccLength; 4272 public $ecc = array(); 4273 4274 /** Data block Constructor 4275 @param Integer $dl length of data stream 4276 @param Array $data data stream 4277 @param Integer $el ECC length 4278 @param Array $el ECC stream (modified, by reference) 4279 @param QRrsItem $rs RS encoding item 4280 */ 4281 public function __construct($dl, $data, $el, &$ecc, QRrsItem $rs) 4282 { 4283 $rs->encode_rs_char($data, $ecc); 4284 4285 $this->dataLength = $dl; 4286 $this->data = $data; 4287 $this->eccLength = $el; 4288 $this->ecc = $ecc; 4289 } 4290 }; 4291 4292 //########################################################################## 4293 /** Raw Code holder. 4294 Contains encoded code data before there are spatialy distributed into frame and masked. 4295 Here goes dividing data into blocks and calculating ECC stream. */ 4296 class QRrawcode { 4297 4298 public $version; ///< __Integer__ code Version 4299 public $datacode = array(); ///< __Array__ data stream 4300 public $ecccode = array(); ///< __Array__ ECC Stream 4301 public $blocks; ///< __Integer__ RS Blocks count 4302 public $rsblocks = array(); ///< __Array__ of RSblock, ECC code blocks 4303 public $count; ///< __Integer__ position of currently processed ECC code 4304 public $dataLength; ///< __Integer__ data stream length 4305 public $eccLength; ///< __Integer__ ECC stream length 4306 public $b1; ///< __Integer__ width of code in pixels, used as a modulo base for column overflow 4307 4308 //---------------------------------------------------------------------- 4309 /** Raw Code holder Constructor 4310 @param QRinput $input input stream 4311 */ 4312 public function __construct(QRinput $input) 4313 { 4314 $spec = array(0,0,0,0,0); 4315 4316 $this->datacode = $input->getByteStream(); 4317 if(is_null($this->datacode)) { 4318 throw new Exception('null imput string'); 4319 } 4320 4321 QRspec::getEccSpec($input->getVersion(), $input->getErrorCorrectionLevel(), $spec); 4322 4323 $this->version = $input->getVersion(); 4324 $this->b1 = QRspec::rsBlockNum1($spec); 4325 $this->dataLength = QRspec::rsDataLength($spec); 4326 $this->eccLength = QRspec::rsEccLength($spec); 4327 $this->ecccode = array_fill(0, $this->eccLength, 0); 4328 $this->blocks = QRspec::rsBlockNum($spec); 4329 4330 $ret = $this->init($spec); 4331 if($ret < 0) { 4332 throw new Exception('block alloc error'); 4333 return null; 4334 } 4335 4336 $this->count = 0; 4337 } 4338 4339 //---------------------------------------------------------------------- 4340 /** Initializes Raw Code according to current code speciffication 4341 @param Array $spec code speciffigation, as provided by QRspec 4342 */ 4343 public function init(array $spec) 4344 { 4345 $dl = QRspec::rsDataCodes1($spec); 4346 $el = QRspec::rsEccCodes1($spec); 4347 $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el); 4348 4349 4350 $blockNo = 0; 4351 $dataPos = 0; 4352 $eccPos = 0; 4353 for($i=0; $i<QRspec::rsBlockNum1($spec); $i++) { 4354 $ecc = array_slice($this->ecccode,$eccPos); 4355 $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs); 4356 $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc); 4357 4358 $dataPos += $dl; 4359 $eccPos += $el; 4360 $blockNo++; 4361 } 4362 4363 if(QRspec::rsBlockNum2($spec) == 0) 4364 return 0; 4365 4366 $dl = QRspec::rsDataCodes2($spec); 4367 $el = QRspec::rsEccCodes2($spec); 4368 $rs = QRrs::init_rs(8, 0x11d, 0, 1, $el, 255 - $dl - $el); 4369 4370 if($rs == NULL) return -1; 4371 4372 for($i=0; $i<QRspec::rsBlockNum2($spec); $i++) { 4373 $ecc = array_slice($this->ecccode,$eccPos); 4374 $this->rsblocks[$blockNo] = new QRrsblock($dl, array_slice($this->datacode, $dataPos), $el, $ecc, $rs); 4375 $this->ecccode = array_merge(array_slice($this->ecccode,0, $eccPos), $ecc); 4376 4377 $dataPos += $dl; 4378 $eccPos += $el; 4379 $blockNo++; 4380 } 4381 4382 return 0; 4383 } 4384 4385 //---------------------------------------------------------------------- 4386 /** Gets ECC code 4387 @return Integer ECC byte for current object position 4388 */ 4389 public function getCode() 4390 { 4391 $ret = null; 4392 4393 if($this->count < $this->dataLength) { 4394 $row = $this->count % $this->blocks; 4395 $col = $this->count / $this->blocks; 4396 if($col >= $this->rsblocks[0]->dataLength) { 4397 $row += $this->b1; 4398 } 4399 $ret = $this->rsblocks[$row]->data[(int) $col]; 4400 } else if($this->count < $this->dataLength + $this->eccLength) { 4401 $row = ($this->count - $this->dataLength) % $this->blocks; 4402 $col = ($this->count - $this->dataLength) / $this->blocks; 4403 $ret = $this->rsblocks[$row]->ecc[(int) $col]; 4404 } else { 4405 return 0; 4406 } 4407 $this->count++; 4408 4409 return $ret; 4410 } 4411 } 4412 4413 //########################################################################## 4414 /** 4415 __Main class to create QR-code__. 4416 QR Code symbol is a 2D barcode that can be scanned by handy terminals such as a mobile phone with CCD. 4417 The capacity of QR Code is up to 7000 digits or 4000 characters, and has high robustness. 4418 This class supports QR Code model 2, described in JIS (Japanese Industrial Standards) X0510:2004 or ISO/IEC 18004. 4419 4420 Currently the following features are not supported: ECI and FNC1 mode, Micro QR Code, QR Code model 1, Structured mode. 4421 4422 @abstract Class for generating QR-code images, SVG and HTML5 Canvas 4423 @author Dominik Dzienia 4424 @copyright 2010-2013 Dominik Dzienia and others 4425 @link http://phpqrcode.sourceforge.net 4426 @license http://www.gnu.org/copyleft/lesser.html LGPL 4427 */ 4428 4429 class QRcode { 4430 4431 public $version; ///< __Integer__ QR code version. Size of QRcode is defined as version. Version is from 1 to 40. Version 1 is 21*21 matrix. And 4 modules increases whenever 1 version increases. So version 40 is 177*177 matrix. 4432 public $width; ///< __Integer__ Width of code table. Because code is square shaped - same as height. 4433 public $data; ///< __Array__ Ready, masked code data. 4434 4435 /** Canvas JS include flag. 4436 If canvas js support library was included, we remember it static in QRcode. 4437 (because file should be included only once) 4438 */ 4439 public static $jscanvasincluded = false; 4440 4441 //---------------------------------------------------------------------- 4442 /** 4443 Encode mask 4444 Main function responsible for creating code. 4445 We get empty frame, then fill it with data from input, then select best mask and apply it. 4446 If $mask argument is greater than -1 we assume that user want's that specific mask number (ranging form 0-7) to be used. 4447 Otherwise (when $mask is -1) mask is detected using algorithm depending of global configuration, 4448 4449 @param QRinput $input data object 4450 @param Integer $mask sugested masking mode 4451 @return QRcode $this (current instance) 4452 */ 4453 public function encodeMask(QRinput $input, $mask) 4454 { 4455 if($input->getVersion() < 0 || $input->getVersion() > QRSPEC_VERSION_MAX) { 4456 throw new Exception('wrong version'); 4457 } 4458 if($input->getErrorCorrectionLevel() > QR_ECLEVEL_H) { 4459 throw new Exception('wrong level'); 4460 } 4461 4462 $raw = new QRrawcode($input); 4463 4464 QRtools::markTime('after_raw'); 4465 4466 $version = $raw->version; 4467 $width = QRspec::getWidth($version); 4468 $frame = QRspec::newFrame($version); 4469 4470 $filler = new QRframeFiller($width, $frame); 4471 if(is_null($filler)) { 4472 return NULL; 4473 } 4474 4475 // inteleaved data and ecc codes 4476 for($i=0; $i<$raw->dataLength + $raw->eccLength; $i++) { 4477 $code = $raw->getCode(); 4478 $bit = 0x80; 4479 for($j=0; $j<8; $j++) { 4480 $addr = $filler->next(); 4481 $filler->setFrameAt($addr, 0x02 | (($bit & $code) != 0)); 4482 $bit = $bit >> 1; 4483 } 4484 } 4485 4486 QRtools::markTime('after_filler'); 4487 4488 unset($raw); 4489 4490 // remainder bits 4491 $j = QRspec::getRemainder($version); 4492 for($i=0; $i<$j; $i++) { 4493 $addr = $filler->next(); 4494 $filler->setFrameAt($addr, 0x02); 4495 } 4496 4497 $frame = $filler->frame; 4498 unset($filler); 4499 4500 4501 // masking 4502 $maskObj = new QRmask(); 4503 if($mask < 0) { 4504 4505 if (QR_FIND_BEST_MASK) { 4506 $masked = $maskObj->mask($width, $frame, $input->getErrorCorrectionLevel()); 4507 } else { 4508 $masked = $maskObj->makeMask($width, $frame, (intval(QR_DEFAULT_MASK) % 8), $input->getErrorCorrectionLevel()); 4509 } 4510 } else { 4511 $masked = $maskObj->makeMask($width, $frame, $mask, $input->getErrorCorrectionLevel()); 4512 } 4513 4514 if($masked == NULL) { 4515 return NULL; 4516 } 4517 4518 QRtools::markTime('after_mask'); 4519 4520 $this->version = $version; 4521 $this->width = $width; 4522 $this->data = $masked; 4523 4524 return $this; 4525 } 4526 4527 //---------------------------------------------------------------------- 4528 /** 4529 Encode input with mask detection. 4530 Shorthand for encodeMask, without specifing particular, static mask number. 4531 4532 @param QRinput $input data object to be encoded 4533 @return 4534 */ 4535 public function encodeInput(QRinput $input) 4536 { 4537 return $this->encodeMask($input, -1); 4538 } 4539 4540 //---------------------------------------------------------------------- 4541 /** 4542 Encode string, forcing 8-bit encoding 4543 @param String $string input string 4544 @param Integer $version code version (size of code area) 4545 @param Integer $level ECC level (see: Global Constants -> Levels of Error Correction) 4546 @return QRcode $this (current instance) 4547 */ 4548 public function encodeString8bit($string, $version, $level) 4549 { 4550 if($string == NULL) { 4551 throw new Exception('empty string!'); 4552 return NULL; 4553 } 4554 4555 $input = new QRinput($version, $level); 4556 if($input == NULL) return NULL; 4557 4558 $ret = $input->append(QR_MODE_8, strlen($string), str_split($string)); 4559 if($ret < 0) { 4560 unset($input); 4561 return NULL; 4562 } 4563 return $this->encodeInput($input); 4564 } 4565 4566 //---------------------------------------------------------------------- 4567 /** 4568 Encode string, using optimal encodings. 4569 Encode string dynamically adjusting encoding for subsections of string to 4570 minimize resulting code size. For complex string it will split string into 4571 subsections: Numerical, Alphanumerical or 8-bit. 4572 @param String $string input string 4573 @param Integer $version code version (size of code area) 4574 @param String $level ECC level (see: Global Constants -> Levels of Error Correction) 4575 @param Integer $hint __QR_MODE_8__ or __QR_MODE_KANJI__, Because Kanji encoding 4576 is kind of 8 bit encoding we need to hint encoder to use Kanji mode explicite. 4577 (otherwise it may try to encode it as plain 8 bit stream) 4578 @param Boolean $casesensitive hint if given string is case-sensitive, because 4579 if not - encoder may use optimal QR_MODE_AN instead of QR_MODE_8 4580 @return QRcode $this (current instance) 4581 */ 4582 public function encodeString($string, $version, $level, $hint, $casesensitive) 4583 { 4584 4585 if($hint != QR_MODE_8 && $hint != QR_MODE_KANJI) { 4586 throw new Exception('bad hint'); 4587 return NULL; 4588 } 4589 4590 $input = new QRinput($version, $level); 4591 if($input == NULL) return NULL; 4592 4593 $ret = QRsplit::splitStringToQRinput($string, $input, $hint, $casesensitive); 4594 if($ret < 0) { 4595 return NULL; 4596 } 4597 4598 return $this->encodeInput($input); 4599 } 4600 4601 //###################################################################### 4602 /** 4603 Creates PNG image containing QR-Code. 4604 Simple helper function to create QR-Code Png image with one static call. 4605 @param String $text text string to encode 4606 @param String $outfile (optional) output file name, if __false__ outputs to browser with required headers 4607 @param Integer $level (optional) error correction level __QR_ECLEVEL_L__, __QR_ECLEVEL_M__, __QR_ECLEVEL_Q__ or __QR_ECLEVEL_H__ 4608 @param Integer $size (optional) pixel size, multiplier for each 'virtual' pixel 4609 @param Integer $margin (optional) code margin (silent zone) in 'virtual' pixels 4610 @param Boolean $saveandprint (optional) if __true__ code is outputed to browser and saved to file, otherwise only saved to file. It is effective only if $outfile is specified. 4611 */ 4612 4613 public static function png($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4, $saveandprint=false) 4614 { 4615 $enc = QRencode::factory($level, $size, $margin); 4616 return $enc->encodePNG($text, $outfile, $saveandprint=false); 4617 } 4618 4619 //---------------------------------------------------------------------- 4620 /** 4621 Creates text (1's & 0's) containing QR-Code. 4622 Simple helper function to create QR-Code text with one static call. 4623 @param String $text text string to encode 4624 @param String $outfile (optional) output file name, when __false__ file is not saved 4625 @param Integer $level (optional) error correction level __QR_ECLEVEL_L__, __QR_ECLEVEL_M__, __QR_ECLEVEL_Q__ or __QR_ECLEVEL_H__ 4626 @param Integer $size (optional) pixel size, multiplier for each 'virtual' pixel 4627 @param Integer $margin (optional) code margin (silent zone) in 'virtual' pixels 4628 @return Array containing line of code with 1 and 0 for every code line 4629 */ 4630 4631 public static function text($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4) 4632 { 4633 $enc = QRencode::factory($level, $size, $margin); 4634 return $enc->encode($text, $outfile); 4635 } 4636 4637 //---------------------------------------------------------------------- 4638 /** 4639 Creates Raw Array containing QR-Code. 4640 Simple helper function to create QR-Code array with one static call. 4641 @param String $text text string to encode 4642 @param Boolean $outfile (optional) not used, shuold be __false__ 4643 @param Integer $level (optional) error correction level __QR_ECLEVEL_L__, __QR_ECLEVEL_M__, __QR_ECLEVEL_Q__ or __QR_ECLEVEL_H__ 4644 @param Integer $size (optional) pixel size, multiplier for each 'virtual' pixel 4645 @param Integer $margin (optional) code margin (silent zone) in 'virtual' pixels 4646 @return Array containing Raw QR code 4647 */ 4648 4649 public static function raw($text, $outfile = false, $level = QR_ECLEVEL_L, $size = 3, $margin = 4) 4650 { 4651 $enc = QRencode::factory($level, $size, $margin); 4652 return $enc->encodeRAW($text, $outfile); 4653 } 4654 4655 //---------------------------------------------------------------------- 4656 /** 4657 Creates Html+JS code to draw QR-Code with HTML5 Canvas. 4658 Simple helper function to create QR-Code array with one static call. 4659 @param String $text text string to encode 4660 @param String $elemId (optional) target Canvas tag id attribute, if __false__ Canvas tag with auto id will be created 4661 @param Integer $level (optional) error correction level __QR_ECLEVEL_L__, __QR_ECLEVEL_M__, __QR_ECLEVEL_Q__ or __QR_ECLEVEL_H__ 4662 @param Integer $width (optional) CANVAS element width (sam as height) 4663 @param Integer $size (optional) pixel size, multiplier for each 'virtual' pixel 4664 @param Integer $margin (optional) code margin (silent zone) in 'virtual' pixels 4665 @param Boolean $autoInclude (optional) if __true__, required qrcanvas.js lib will be included (only once) 4666 @return String containing JavaScript creating the code, Canvas element (when $elemId is __false__) and script tag with required lib (when $autoInclude is __true__ and not yet included) 4667 */ 4668 4669 public static function canvas($text, $elemId = false, $level = QR_ECLEVEL_L, $width = false, $size = false, $margin = 4, $autoInclude = false) 4670 { 4671 $html = ''; 4672 $extra = ''; 4673 4674 if ($autoInclude) { 4675 if (!self::$jscanvasincluded) { 4676 self::$jscanvasincluded = true; 4677 echo '<script type="text/javascript" src="qrcanvas.js"></script>'; 4678 } 4679 } 4680 4681 $enc = QRencode::factory($level, 1, 0); 4682 $tab_src = $enc->encode($text, false); 4683 $area = new QRcanvasOutput($tab_src); 4684 $area->detectGroups(); 4685 $area->detectAreas(); 4686 4687 if ($elemId === false) { 4688 $elemId = 'qrcode-'.md5(mt_rand(1000,1000000).'.'.mt_rand(1000,1000000).'.'.mt_rand(1000,1000000).'.'.mt_rand(1000,1000000)); 4689 4690 if ($width == false) { 4691 if (($size !== false) && ($size > 0)) { 4692 $width = ($area->getWidth()+(2*$margin)) * $size; 4693 } else { 4694 $width = ($area->getWidth()+(2*$margin)) * 4; 4695 } 4696 } 4697 4698 $html .= '<canvas id="'.$elemId.'" width="'.$width.'" height="'.$width.'">Your browser does not support CANVAS tag! Please upgrade to modern version of FireFox, Opera, Chrome or Safari/Webkit based browser</canvas>'; 4699 } 4700 4701 if ($width !== false) { 4702 $extra .= ', '.$width.', '.$width; 4703 } 4704 4705 if ($margin !== false) { 4706 $extra .= ', '.$margin.', '.$margin; 4707 } 4708 4709 $html .= '<script>if(eval("typeof "+\'QRdrawCode\'+"==\'function\'")){QRdrawCode(QRdecompactOps(\''.$area->getCanvasOps().'\')'."\n".', \''.$elemId.'\', '.$area->getWidth().' '.$extra.');}else{alert(\'Please include qrcanvas.js!\');}</script>'; 4710 4711 return $html; 4712 } 4713 4714 //---------------------------------------------------------------------- 4715 /** 4716 Creates SVG with QR-Code. 4717 Simple helper function to create QR-Code SVG with one static call. 4718 @param String $text text string to encode 4719 @param Boolean $elemId (optional) target SVG tag id attribute, if __false__ SVG tag with auto id will be created 4720 @param String $outfile (optional) output file name, when __false__ file is not saved 4721 @param Integer $level (optional) error correction level __QR_ECLEVEL_L__, __QR_ECLEVEL_M__, __QR_ECLEVEL_Q__ or __QR_ECLEVEL_H__ 4722 @param Integer $width (optional) SVG element width (sam as height) 4723 @param Integer $size (optional) pixel size, multiplier for each 'virtual' pixel 4724 @param Integer $margin (optional) code margin (silent zone) in 'virtual' pixels 4725 @param Boolean $compress (optional) if __true__, compressed SVGZ (instead plaintext SVG) is saved to file 4726 @return String containing SVG tag 4727 */ 4728 4729 public static function svg($text, $elemId = false, $outFile = false, $level = QR_ECLEVEL_L, $width = false, $size = false, $margin = 4, $compress = false) 4730 { 4731 $enc = QRencode::factory($level, 1, 0); 4732 $tab_src = $enc->encode($text, false); 4733 $area = new QRsvgOutput($tab_src); 4734 $area->detectGroups(); 4735 $area->detectAreas(); 4736 4737 if ($elemId === false) { 4738 $elemId = 'qrcode-'.md5(mt_rand(1000,1000000).'.'.mt_rand(1000,1000000).'.'.mt_rand(1000,1000000).'.'.mt_rand(1000,1000000)); 4739 4740 if ($width == false) { 4741 if (($size !== false) && ($size > 0)) { 4742 $width = ($area->getWidth()+(2*$margin)) * $size; 4743 } else { 4744 $width = ($area->getWidth()+(2*$margin)) * 4; 4745 } 4746 } 4747 } 4748 4749 $svg = '<svg xmlns="http://www.w3.org/2000/svg" 4750 xmlns:xlink="http://www.w3.org/1999/xlink" 4751 version="1.1" 4752 baseProfile="full" 4753 viewBox="'.(-$margin).' '.(-$margin).' '.($area->getWidth()+($margin*2)).' '.($area->getWidth()+($margin*2)).'" 4754 width="'.$width.'" 4755 height="'.$width.'" 4756 id="'.$elemId.'">'."\n"; 4757 4758 $svg .= $area->getRawSvg().'</svg>'; 4759 4760 if ($outFile !== false) { 4761 $xmlPreamble = '<?xml version="1.0" encoding="UTF-8" standalone="no"'."\n"; 4762 $svgContent = $xmlPreamble.$svg; 4763 4764 if ($compress === true) { 4765 file_put_contents($outFile, gzencode($svgContent)); 4766 } else { 4767 file_put_contents($outFile, $svgContent); 4768 } 4769 } 4770 4771 return $svg; 4772 } 4773 } 4774 4775 //########################################################################## 4776 /** Fills frame with data. 4777 Each empty frame consist of markers, timing symbols and format configuration. 4778 Remaining place is place for data, and should be filled according to QR Code spec. 4779 */ 4780 class QRframeFiller { 4781 4782 public $width; ///< __Integer__ Frame width 4783 public $frame; ///< __Array__ Frame itself 4784 public $x; ///< __Integer__ current X position 4785 public $y; ///< __Integer__ current Y position 4786 public $dir; ///< __Integer__ direction 4787 public $bit; ///< __Integer__ bit 4788 4789 //---------------------------------------------------------------------- 4790 /** Frame filler Constructor. 4791 @param Integer $width frame size 4792 @param Array $frame Frame array 4793 */ 4794 public function __construct($width, &$frame) 4795 { 4796 $this->width = $width; 4797 $this->frame = $frame; 4798 $this->x = $width - 1; 4799 $this->y = $width - 1; 4800 $this->dir = -1; 4801 $this->bit = -1; 4802 } 4803 4804 //---------------------------------------------------------------------- 4805 /** Sets frame code at given position. 4806 @param Array $at position, map containing __x__ and __y__ coordinates 4807 @param Integer $val value to set 4808 */ 4809 public function setFrameAt($at, $val) 4810 { 4811 $this->frame[$at['y']][$at['x']] = chr($val); 4812 } 4813 4814 //---------------------------------------------------------------------- 4815 /** Gets frame code from given position. 4816 @param Array $at position, map containing __x__ and __y__ coordinates 4817 @return Integer value at requested position 4818 */ 4819 public function getFrameAt($at) 4820 { 4821 return ord($this->frame[$at['y']][$at['x']]); 4822 } 4823 4824 //---------------------------------------------------------------------- 4825 /** Proceed to next code point. */ 4826 public function next() 4827 { 4828 do { 4829 4830 if($this->bit == -1) { 4831 $this->bit = 0; 4832 return array('x'=>$this->x, 'y'=>$this->y); 4833 } 4834 4835 $x = $this->x; 4836 $y = $this->y; 4837 $w = $this->width; 4838 4839 if($this->bit == 0) { 4840 $x--; 4841 $this->bit++; 4842 } else { 4843 $x++; 4844 $y += $this->dir; 4845 $this->bit--; 4846 } 4847 4848 if($this->dir < 0) { 4849 if($y < 0) { 4850 $y = 0; 4851 $x -= 2; 4852 $this->dir = 1; 4853 if($x == 6) { 4854 $x--; 4855 $y = 9; 4856 } 4857 } 4858 } else { 4859 if($y == $w) { 4860 $y = $w - 1; 4861 $x -= 2; 4862 $this->dir = -1; 4863 if($x == 6) { 4864 $x--; 4865 $y -= 8; 4866 } 4867 } 4868 } 4869 if($x < 0 || $y < 0) return null; 4870 4871 $this->x = $x; 4872 $this->y = $y; 4873 4874 } while(ord($this->frame[$y][$x]) & 0x80); 4875 4876 return array('x'=>$x, 'y'=>$y); 4877 } 4878 4879 } ; 4880 4881 //########################################################################## 4882 /** QR Code encoder. 4883 Encoder is used by QRCode to create simple static code generators. */ 4884 class QRencode { 4885 4886 public $casesensitive = true; ///< __Boolean__ does input stream id case sensitive, if not encoder may use more optimal charsets 4887 public $eightbit = false; ///< __Boolean__ does input stream is 8 bit 4888 4889 public $version = 0; ///< __Integer__ code version (total size) if __0__ - will be auto-detected 4890 public $size = 3; ///< __Integer__ pixel zoom factor, multiplier to map virtual code pixels to image output pixels 4891 public $margin = 4; ///< __Integer__ margin (silent zone) size, in code pixels 4892 4893 public $structured = 0; ///< Structured QR codes. Not supported. 4894 4895 public $level = QR_ECLEVEL_L; ///< __Integer__ error correction level __QR_ECLEVEL_L__, __QR_ECLEVEL_M__, __QR_ECLEVEL_Q__ or __QR_ECLEVEL_H__ 4896 public $hint = QR_MODE_8; ///< __Integer__ encoding hint, __QR_MODE_8__ or __QR_MODE_KANJI__, Because Kanji encoding is kind of 8 bit encoding we need to hint encoder to use Kanji mode explicite. (otherwise it may try to encode it as plain 8 bit stream) 4897 4898 //---------------------------------------------------------------------- 4899 /** Encoder instances factory. 4900 @param Integer $level error correction level __QR_ECLEVEL_L__, __QR_ECLEVEL_M__, __QR_ECLEVEL_Q__ or __QR_ECLEVEL_H__ 4901 @param Integer $size pixel zoom factor, multiplier to map virtual code pixels to image output pixels 4902 @param Integer $margin margin (silent zone) size, in code pixels 4903 @return builded QRencode instance 4904 */ 4905 public static function factory($level = QR_ECLEVEL_L, $size = 3, $margin = 4) 4906 { 4907 $enc = new QRencode(); 4908 $enc->size = $size; 4909 $enc->margin = $margin; 4910 4911 switch ($level.'') { 4912 case '0': 4913 case '1': 4914 case '2': 4915 case '3': 4916 $enc->level = $level; 4917 break; 4918 case 'l': 4919 case 'L': 4920 $enc->level = QR_ECLEVEL_L; 4921 break; 4922 case 'm': 4923 case 'M': 4924 $enc->level = QR_ECLEVEL_M; 4925 break; 4926 case 'q': 4927 case 'Q': 4928 $enc->level = QR_ECLEVEL_Q; 4929 break; 4930 case 'h': 4931 case 'H': 4932 $enc->level = QR_ECLEVEL_H; 4933 break; 4934 } 4935 4936 return $enc; 4937 } 4938 4939 //---------------------------------------------------------------------- 4940 /** Encodes input into Raw code table. 4941 @param String $intext input text 4942 @param Boolean $notused (optional, not used) placeholder for similar outfile parameter 4943 @return __Array__ Raw code frame 4944 */ 4945 public function encodeRAW($intext, $notused = false) 4946 { 4947 $code = new QRcode(); 4948 4949 if($this->eightbit) { 4950 $code->encodeString8bit($intext, $this->version, $this->level); 4951 } else { 4952 $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive); 4953 } 4954 4955 return $code->data; 4956 } 4957 4958 //---------------------------------------------------------------------- 4959 /** Encodes input into binary code table. 4960 @param String $intext input text 4961 @param String $outfile (optional) output file to save code table, if __false__ file will be not saved 4962 @return __Array__ binary code frame 4963 */ 4964 public function encode($intext, $outfile = false) 4965 { 4966 $code = new QRcode(); 4967 4968 if($this->eightbit) { 4969 $code->encodeString8bit($intext, $this->version, $this->level); 4970 } else { 4971 $code->encodeString($intext, $this->version, $this->level, $this->hint, $this->casesensitive); 4972 } 4973 4974 QRtools::markTime('after_encode'); 4975 4976 $binarized = QRtools::binarize($code->data); 4977 if ($outfile!== false) { 4978 file_put_contents($outfile, join("\n", $binarized)); 4979 } 4980 4981 return $binarized; 4982 } 4983 4984 //---------------------------------------------------------------------- 4985 /** Encodes input into PNG image. 4986 @param String $intext input text 4987 @param String $outfile (optional) output file name, if __false__ outputs to browser with required headers 4988 @param Boolean $saveandprint (optional) if __true__ code is outputed to browser and saved to file, otherwise only saved to file. It is effective only if $outfile is specified. 4989 */ 4990 public function encodePNG($intext, $outfile = false, $saveandprint=false) 4991 { 4992 try { 4993 4994 ob_start(); 4995 $tab = $this->encode($intext); 4996 $err = ob_get_contents(); 4997 ob_end_clean(); 4998 4999 if ($err != '') 5000 QRtools::log($outfile, $err); 5001 5002 $maxSize = (int)(QR_PNG_MAXIMUM_SIZE / (count($tab)+2*$this->margin)); 5003 5004 QRimage::png($tab, $outfile, min(max(1, $this->size), $maxSize), $this->margin,$saveandprint); 5005 5006 } catch (Exception $e) { 5007 5008 QRtools::log($outfile, $e->getMessage()); 5009 5010 } 5011 } 5012 } 5013 5014 /** @}*/ 5015