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