1<?php 2 3/** 4 * QrCode 5 * @author Akhtar Khan <er.akhtarkhan@gmail.com> 6 * @link http://www.codeitnow.in 7 * @package https://github.com/codeitnowin/barcode-generator 8 */ 9 10namespace CodeItNow\BarcodeBundle\Utils; 11use Exception; 12use ReflectionFunction; 13 14/** 15 * Generate QR Code. 16 */ 17class QrCode 18{ 19 /** @const int Error Correction Level Low (7%) */ 20 const LEVEL_LOW = 1; 21 22 /** @const int Error Correction Level Medium (15%) */ 23 const LEVEL_MEDIUM = 0; 24 25 /** @const int Error Correction Level Quartile (25%) */ 26 const LEVEL_QUARTILE = 3; 27 28 /** @const int Error Correction Level High (30%) */ 29 const LEVEL_HIGH = 2; 30 31 /** @const string Image type png */ 32 const IMAGE_TYPE_PNG = 'png'; 33 34 /** @const string Image type gif */ 35 const IMAGE_TYPE_GIF = 'gif'; 36 37 /** @const string Image type jpeg */ 38 const IMAGE_TYPE_JPEG = 'jpeg'; 39 40 /** @const string Image type wbmp */ 41 const IMAGE_TYPE_WBMP = 'wbmp'; 42 43 /** @const int Horizontal label alignment to the center of image */ 44 const LABEL_HALIGN_CENTER = 0; 45 46 /** @const int Horizontal label alignment to the left side of image */ 47 const LABEL_HALIGN_LEFT = 1; 48 49 /** @const int Horizontal label alignment to the left border of QR Code */ 50 const LABEL_HALIGN_LEFT_BORDER = 2; 51 52 /** @const int Horizontal label alignment to the left side of QR Code */ 53 const LABEL_HALIGN_LEFT_CODE = 3; 54 55 /** @const int Horizontal label alignment to the right side of image */ 56 const LABEL_HALIGN_RIGHT = 4; 57 58 /** @const int Horizontal label alignment to the right border of QR Code */ 59 const LABEL_HALIGN_RIGHT_BORDER = 5; 60 61 /** @const int Horizontal label alignment to the right side of QR Code */ 62 const LABEL_HALIGN_RIGHT_CODE = 6; 63 64 /** @const int Vertical label alignment to the top */ 65 const LABEL_VALIGN_TOP = 1; 66 67 /** @const int Vertical label alignment to the top and hide border */ 68 const LABEL_VALIGN_TOP_NO_BORDER = 2; 69 70 /** @const int Vertical label alignment to the middle*/ 71 const LABEL_VALIGN_MIDDLE = 3; 72 73 /** @const int Vertical label alignment to the bottom */ 74 const LABEL_VALIGN_BOTTOM = 4; 75 76 /** @var string */ 77 protected $text = ''; 78 79 /** @var int */ 80 protected $size = 0; 81 82 /** @var int */ 83 protected $padding = 16; 84 85 /** @var bool */ 86 protected $draw_quiet_zone = false; 87 88 /** @var bool */ 89 protected $draw_border = false; 90 91 /** @var array */ 92 protected $color_foreground = array('r' => 0, 'g' => 0, 'b' => 0, 'a' => 0); 93 94 /** @var array */ 95 protected $color_background = array('r' => 255, 'g' => 255, 'b' => 255, 'a' => 0); 96 97 /** @var string */ 98 protected $label = ''; 99 100 /** @var int */ 101 protected $label_font_size = 16; 102 103 /** @var string */ 104 protected $label_font_path = ''; 105 106 /** @var int */ 107 protected $label_halign = self::LABEL_HALIGN_CENTER; 108 109 /** @var int */ 110 protected $label_valign = self::LABEL_VALIGN_MIDDLE; 111 112 /** @var resource */ 113 protected $image = null; 114 115 /** @var int */ 116 protected $version; 117 118 /** @var int */ 119 protected $error_correction = self::LEVEL_MEDIUM; 120 121 /** @var array */ 122 protected $error_corrections_available = array( 123 self::LEVEL_LOW, 124 self::LEVEL_MEDIUM, 125 self::LEVEL_QUARTILE, 126 self::LEVEL_HIGH, 127 ); 128 129 /** @var int */ 130 protected $module_size; 131 132 /** @var string */ 133 protected $image_type = self::IMAGE_TYPE_PNG; 134 135 /** @var array */ 136 protected $image_types_available = array( 137 self::IMAGE_TYPE_GIF, 138 self::IMAGE_TYPE_PNG, 139 self::IMAGE_TYPE_JPEG, 140 self::IMAGE_TYPE_WBMP, 141 ); 142 143 /** @var string */ 144 protected $image_path; 145 146 /** @var string */ 147 protected $path; 148 149 /** @var int */ 150 protected $structure_append_n; 151 152 /** @var int */ 153 protected $structure_append_m; 154 155 /** @var int */ 156 protected $structure_append_parity; 157 158 /** @var string */ 159 protected $structure_append_original_data; 160 161 /** 162 * Class constructor. 163 * 164 * @param string $text 165 */ 166 public function __construct($text = '') 167 { 168 $this->setPath(dirname(__DIR__)."/Resources/data"); 169 $this->setImagePath(dirname(__DIR__)."/Resources/image"); 170 $this->setLabelFontPath(dirname(__DIR__)."/Resources/font/opensans.ttf"); 171 $this->setText($text); 172 } 173 174 /** 175 * Set structure append. 176 * 177 * @param int $n 178 * @param int $m 179 * @param int $parity Parity 180 * @param string $original_data Original data 181 * 182 * @return QrCode 183 */ 184 public function setStructureAppend($n, $m, $parity, $original_data) 185 { 186 $this->structure_append_n = $n; 187 $this->structure_append_m = $m; 188 $this->structure_append_parity = $parity; 189 $this->structure_append_original_data = $original_data; 190 191 return $this; 192 } 193 194 /** 195 * Set QR Code version. 196 * 197 * @param int $version QR Code version 198 * 199 * @return QrCode 200 */ 201 public function setVersion($version) 202 { 203 if ($version <= 40 && $version >= 0) { 204 $this->version = $version; 205 } 206 207 return $this; 208 } 209 210 /** 211 * Return QR Code version. 212 * 213 * @return int 214 */ 215 public function getVersion() 216 { 217 return $this->version; 218 } 219 220 /** 221 * Set QR Code error correction level. 222 * 223 * @param mixed $error_correction Error Correction Level 224 * 225 * @return QrCode 226 */ 227 public function setErrorCorrection($error_correction) 228 { 229 if (!is_numeric($error_correction)) { 230 $level_constant = 'CodeItNow\BarcodeBundle\Utils\QrCode::LEVEL_'.strtoupper($error_correction); 231 $error_correction = constant($level_constant); 232 } 233 234 if (in_array($error_correction, $this->error_corrections_available)) { 235 $this->error_correction = $error_correction; 236 } 237 238 return $this; 239 } 240 241 /** 242 * Return QR Code error correction level. 243 * 244 * @return int 245 */ 246 public function getErrorCorrection() 247 { 248 return $this->error_correction; 249 } 250 251 /** 252 * Set QR Code module size. 253 * 254 * @param int $module_size Module size 255 * 256 * @return QrCode 257 */ 258 public function setModuleSize($module_size) 259 { 260 $this->module_size = $module_size; 261 262 return $this; 263 } 264 265 /** 266 * Return QR Code module size. 267 * 268 * @return int 269 */ 270 public function getModuleSize() 271 { 272 return $this->module_size; 273 } 274 275 /** 276 * Set image type for rendering. 277 * 278 * @param string $image_type Image type 279 * 280 * @return QrCode 281 * 282 * @throws Exception 283 */ 284 public function setImageType($image_type) 285 { 286 if (!in_array($image_type, $this->image_types_available)) { 287 throw new Exception('QRCode: image type '.$image_type.' is invalid.'); 288 } 289 290 $this->image_type = $image_type; 291 292 return $this; 293 } 294 295 /** 296 * Return image type for rendering. 297 * 298 * @return string 299 */ 300 public function getImageType() 301 { 302 return $this->image_type; 303 } 304 305 /** 306 * Set image type for rendering via extension. 307 * 308 * @param string $extension Image extension 309 * 310 * @return QrCode 311 */ 312 public function setExtension($extension) 313 { 314 if ($extension == 'jpg') { 315 $this->setImageType('jpeg'); 316 } else { 317 $this->setImageType($extension); 318 } 319 320 return $this; 321 } 322 323 /** 324 * Set path to the images directory. 325 * 326 * @param string $image_path Image directory 327 * 328 * @return QrCode 329 */ 330 public function setImagePath($image_path) 331 { 332 $this->image_path = $image_path; 333 334 return $this; 335 } 336 337 /** 338 * Return path to the images directory. 339 * 340 * @return string 341 */ 342 public function getImagePath() 343 { 344 return $this->image_path; 345 } 346 347 /** 348 * Set path to the data directory. 349 * 350 * @param string $path Data directory 351 * 352 * @return QrCode 353 */ 354 public function setPath($path) 355 { 356 $this->path = $path; 357 358 return $this; 359 } 360 361 /** 362 * Return path to the data directory. 363 * 364 * @return string 365 */ 366 public function getPath() 367 { 368 return $this->path; 369 } 370 371 /** 372 * Set text to hide in QR Code. 373 * 374 * @param string $text Text to hide 375 * 376 * @return QrCode 377 */ 378 public function setText($text) 379 { 380 $this->text = $text; 381 382 return $this; 383 } 384 385 /** 386 * Return text that will be hid in QR Code. 387 * 388 * @return string 389 */ 390 public function getText() 391 { 392 return $this->text; 393 } 394 395 /** 396 * Set QR Code size (width). 397 * 398 * @param int $size Width of the QR Code 399 * 400 * @return QrCode 401 */ 402 public function setSize($size) 403 { 404 $this->size = $size; 405 406 return $this; 407 } 408 409 /** 410 * Return QR Code size (width). 411 * 412 * @return int 413 */ 414 public function getSize() 415 { 416 return $this->size; 417 } 418 419 /** 420 * Set padding around the QR Code. 421 * 422 * @param int $padding Padding around QR Code 423 * 424 * @return QrCode 425 */ 426 public function setPadding($padding) 427 { 428 $this->padding = $padding; 429 430 return $this; 431 } 432 433 /** 434 * Return padding around the QR Code. 435 * 436 * @return int 437 */ 438 public function getPadding() 439 { 440 return $this->padding; 441 } 442 443 /** 444 * Set draw required four-module wide margin. 445 * 446 * @param bool $draw_quiet_zone State of required four-module wide margin drawing 447 * 448 * @return QrCode 449 */ 450 public function setDrawQuietZone($draw_quiet_zone) 451 { 452 $this->draw_quiet_zone = $draw_quiet_zone; 453 454 return $this; 455 } 456 457 /** 458 * Return draw required four-module wide margin. 459 * 460 * @return bool 461 */ 462 public function getDrawQuietZone() 463 { 464 return $this->draw_quiet_zone; 465 } 466 467 /** 468 * Set draw border around QR Code. 469 * 470 * @param bool $draw_border State of border drawing 471 * 472 * @return QrCode 473 */ 474 public function setDrawBorder($draw_border) 475 { 476 $this->draw_border = $draw_border; 477 478 return $this; 479 } 480 481 /** 482 * Return draw border around QR Code. 483 * 484 * @return bool 485 */ 486 public function getDrawBorder() 487 { 488 return $this->draw_border; 489 } 490 491 /** 492 * Set QR Code label (text). 493 * 494 * @param int|string $label Label to print under QR code 495 * 496 * @return QrCode 497 */ 498 public function setLabel($label) 499 { 500 $this->label = $label; 501 502 return $this; 503 } 504 505 /** 506 * Return QR Code label (text). 507 * 508 * @return string 509 */ 510 public function getLabel() 511 { 512 return $this->label; 513 } 514 515 /** 516 * Set QR Code label font size. 517 * 518 * @param int $label_font_size Font size of the QR code label 519 * 520 * @return QrCode 521 */ 522 public function setLabelFontSize($label_font_size) 523 { 524 $this->label_font_size = $label_font_size; 525 526 return $this; 527 } 528 529 /** 530 * Return QR Code label font size. 531 * 532 * @return int 533 */ 534 public function getLabelFontSize() 535 { 536 return $this->label_font_size; 537 } 538 539 /** 540 * Set QR Code label font path. 541 * 542 * @param int $label_font_path Path to the QR Code label's TTF font file 543 * 544 * @return QrCode 545 */ 546 public function setLabelFontPath($label_font_path) 547 { 548 $this->label_font_path = $label_font_path; 549 550 return $this; 551 } 552 553 /** 554 * Return path to the QR Code label's TTF font file. 555 * 556 * @return string 557 */ 558 public function getLabelFontPath() 559 { 560 return $this->label_font_path; 561 } 562 563 /** 564 * Set label horizontal alignment. 565 * 566 * @param int $label_halign Label horizontal alignment 567 * 568 * @return QrCode 569 */ 570 public function setLabelHalign($label_halign) 571 { 572 $this->label_halign = $label_halign; 573 574 return $this; 575 } 576 577 /** 578 * Return label horizontal alignment. 579 * 580 * @return int 581 */ 582 public function getLabelHalign() 583 { 584 return $this->label_halign; 585 } 586 587 /** 588 * Set label vertical alignment. 589 * 590 * @param int $label_valign Label vertical alignment 591 * 592 * @return QrCode 593 */ 594 public function setLabelValign($label_valign) 595 { 596 $this->label_valign = $label_valign; 597 598 return $this; 599 } 600 601 /** 602 * Return label vertical alignment. 603 * 604 * @return int 605 */ 606 public function getLabelValign() 607 { 608 return $this->label_valign; 609 } 610 611 /** 612 * Set foreground color of the QR Code. 613 * 614 * @param array $color_foreground RGB color 615 * 616 * @return QrCode 617 */ 618 public function setForegroundColor($color_foreground) 619 { 620 if (!isset($color_foreground['a'])) { 621 $color_foreground['a'] = 0; 622 } 623 624 $this->color_foreground = $color_foreground; 625 626 return $this; 627 } 628 629 /** 630 * Return foreground color of the QR Code. 631 * 632 * @return array 633 */ 634 public function getForegroundColor() 635 { 636 return $this->color_foreground; 637 } 638 639 /** 640 * Set background color of the QR Code. 641 * 642 * @param array $color_background RGB color 643 * 644 * @return QrCode 645 */ 646 public function setBackgroundColor($color_background) 647 { 648 if (!isset($color_background['a'])) { 649 $color_background['a'] = 0; 650 } 651 652 $this->color_background = $color_background; 653 654 return $this; 655 } 656 657 /** 658 * Return background color of the QR Code. 659 * 660 * @return array 661 */ 662 public function getBackgroundColor() 663 { 664 return $this->color_background; 665 } 666 667 /** 668 * Return the image resource. 669 * 670 * @return resource 671 */ 672 public function getImage() 673 { 674 if (empty($this->image)) { 675 $this->create(); 676 } 677 678 return $this->image; 679 } 680 681 /** 682 * Return the data URI. 683 * 684 * @return string 685 */ 686 public function getDataUri() 687 { 688 if (empty($this->image)) { 689 $this->create(); 690 } 691 692 ob_start(); 693 call_user_func('image'.$this->image_type, $this->image); 694 $contents = ob_get_clean(); 695 696 return 'data:image/'.$this->image_type.';base64,'.base64_encode($contents); 697 } 698 699 /** 700 * Render the QR Code then save it to given file name. 701 * 702 * @param string $filename File name of the QR Code 703 * 704 * @return QrCode 705 */ 706 public function save($filename) 707 { 708 $this->render($filename); 709 710 return $this; 711 } 712 713 /** 714 * Render the QR Code then save it to given file name or 715 * output it to the browser when file name omitted. 716 * 717 * @param null|string $filename File name of the QR Code 718 * @param null|string $format Format of the file (png, jpeg, jpg, gif, wbmp) 719 * 720 * @throws Exception 721 * @throws Exception 722 * 723 * @return QrCode 724 */ 725 public function render($filename = null, $format = 'png') 726 { 727 $this->create(); 728 729 if ($format == 'jpg') { 730 $format = 'jpeg'; 731 } 732 733 if (!in_array($format, $this->image_types_available)) { 734 $format = $this->image_type; 735 } 736 737 if (!function_exists('image'.$format)) { 738 throw new Exception('QRCode: function image'.$format.' does not exists.'); 739 } 740 741 if ($filename === null) { 742 $success = call_user_func('image'.$format, $this->image); 743 } else { 744 $success = call_user_func_array('image'.$format, array($this->image, $filename)); 745 } 746 747 if ($success === false) { 748 throw new Exception('QRCode: function image'.$format.' failed.'); 749 } 750 751 return $this; 752 } 753 754 /** 755 * Generate base64 image 756 * @return type 757 */ 758 public function generate(){ 759 $image = $this->get(); 760 return base64_encode($image); 761 } 762 763 /** 764 * Returns the content type corresponding to the image type. 765 * 766 * @return string 767 */ 768 public function getContentType() 769 { 770 $contentType = 'image/'.$this->image_type; 771 772 return $contentType; 773 } 774 775 /** 776 * Create QR Code and return its content. 777 * 778 * @param string|null $format Image type (gif, png, wbmp, jpeg) 779 * 780 * @throws Exception 781 * @throws Exception 782 * 783 * @return string 784 */ 785 public function get($format = null) 786 { 787 $this->create(); 788 789 if ($format == 'jpg') { 790 $format = 'jpeg'; 791 } 792 793 if (!in_array($format, $this->image_types_available)) { 794 $format = $this->image_type; 795 } 796 797 if (!function_exists('image'.$format)) { 798 throw new Exception('QRCode: function image'.$format.' does not exists.'); 799 } 800 801 ob_start(); 802 $success = call_user_func('image'.$format, $this->image); 803 804 if ($success === false) { 805 throw new Exception('QRCode: function image'.$format.' failed.'); 806 } 807 808 $content = ob_get_clean(); 809 810 return $content; 811 } 812 813 /** 814 * Create the image. 815 * 816 * @throws Exception 817 * @throws \OverflowException 818 */ 819 public function create() 820 { 821 $image_path = $this->image_path; 822 $path = $this->path; 823 824 $version_ul = 40; 825 826 $qrcode_data_string = $this->text;//Previously from $_GET["d"]; 827 828 $qrcode_error_correct = $this->error_correction;//Previously from $_GET["e"]; 829 $qrcode_module_size = $this->module_size;//Previously from $_GET["s"]; 830 $qrcode_version = $this->version;//Previously from $_GET["v"]; 831 $qrcode_image_type = $this->image_type;//Previously from $_GET["t"]; 832 833 $qrcode_structureappend_n = $this->structure_append_n;//Previously from $_GET["n"]; 834 $qrcode_structureappend_m = $this->structure_append_m;//Previously from $_GET["m"]; 835 $qrcode_structureappend_parity = $this->structure_append_parity;//Previously from $_GET["p"]; 836 $qrcode_structureappend_originaldata = $this->structure_append_original_data;//Previously from $_GET["o"]; 837 838 if ($qrcode_module_size > 0) { 839 } else { 840 if ($qrcode_image_type == 'jpeg') { 841 $qrcode_module_size = 8; 842 } else { 843 $qrcode_module_size = 4; 844 } 845 } 846 $data_length = strlen($qrcode_data_string); 847 if ($data_length <= 0) { 848 throw new Exception('QRCode: data does not exist.'); 849 } 850 $data_counter = 0; 851 if ($qrcode_structureappend_n > 1 852 && $qrcode_structureappend_n <= 16 853 && $qrcode_structureappend_m > 0 854 && $qrcode_structureappend_m <= 16) { 855 $data_value[0] = 3; 856 $data_bits[0] = 4; 857 858 $data_value[1] = $qrcode_structureappend_m - 1; 859 $data_bits[1] = 4; 860 861 $data_value[2] = $qrcode_structureappend_n - 1; 862 $data_bits[2] = 4; 863 864 $originaldata_length = strlen($qrcode_structureappend_originaldata); 865 if ($originaldata_length > 1) { 866 $qrcode_structureappend_parity = 0; 867 $i = 0; 868 while ($i < $originaldata_length) { 869 $qrcode_structureappend_parity = ($qrcode_structureappend_parity ^ ord(substr($qrcode_structureappend_originaldata, $i, 1))); 870 ++$i; 871 } 872 } 873 874 $data_value[3] = $qrcode_structureappend_parity; 875 $data_bits[3] = 8; 876 877 $data_counter = 4; 878 } 879 880 $data_bits[$data_counter] = 4; 881 882 /* --- determine encode mode */ 883 884 if (preg_match('/[^0-9]/', $qrcode_data_string) != 0) { 885 if (preg_match("/[^0-9A-Z \$\*\%\+\.\/\:\-]/", $qrcode_data_string) != 0) { 886 /* --- 8bit byte mode */ 887 888 $codeword_num_plus = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 889 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 890 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, ); 891 892 $data_value[$data_counter] = 4; 893 ++$data_counter; 894 $data_value[$data_counter] = $data_length; 895 $data_bits[$data_counter] = 8; /* #version 1-9 */ 896 $codeword_num_counter_value = $data_counter; 897 898 ++$data_counter; 899 $i = 0; 900 while ($i < $data_length) { 901 $data_value[$data_counter] = ord(substr($qrcode_data_string, $i, 1)); 902 $data_bits[$data_counter] = 8; 903 ++$data_counter; 904 ++$i; 905 } 906 } else { 907 /* ---- alphanumeric mode */ 908 909 $codeword_num_plus = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 910 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 911 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, ); 912 913 $data_value[$data_counter] = 2; 914 ++$data_counter; 915 $data_value[$data_counter] = $data_length; 916 $data_bits[$data_counter] = 9; /* #version 1-9 */ 917 $codeword_num_counter_value = $data_counter; 918 919 $alphanumeric_character_hash = array('0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, 920 '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 921 'F' => 15, 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23, 922 'O' => 24, 'P' => 25, 'Q' => 26, 'R' => 27, 'S' => 28, 'T' => 29, 'U' => 30, 'V' => 31, 923 'W' => 32, 'X' => 33, 'Y' => 34, 'Z' => 35, ' ' => 36, '$' => 37, '%' => 38, '*' => 39, 924 '+' => 40, '-' => 41, '.' => 42, '/' => 43, ':' => 44, ); 925 926 $i = 0; 927 ++$data_counter; 928 while ($i < $data_length) { 929 if (($i % 2) == 0) { 930 $data_value[$data_counter] = $alphanumeric_character_hash[substr($qrcode_data_string, $i, 1)]; 931 $data_bits[$data_counter] = 6; 932 } else { 933 $data_value[$data_counter] = $data_value[$data_counter] * 45 + $alphanumeric_character_hash[substr($qrcode_data_string, $i, 1)]; 934 $data_bits[$data_counter] = 11; 935 ++$data_counter; 936 } 937 ++$i; 938 } 939 } 940 } else { 941 /* ---- numeric mode */ 942 943 $codeword_num_plus = array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 944 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 945 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, ); 946 947 $data_value[$data_counter] = 1; 948 ++$data_counter; 949 $data_value[$data_counter] = $data_length; 950 $data_bits[$data_counter] = 10; /* #version 1-9 */ 951 $codeword_num_counter_value = $data_counter; 952 953 $i = 0; 954 ++$data_counter; 955 while ($i < $data_length) { 956 if (($i % 3) == 0) { 957 $data_value[$data_counter] = substr($qrcode_data_string, $i, 1); 958 $data_bits[$data_counter] = 4; 959 } else { 960 $data_value[$data_counter] = $data_value[$data_counter] * 10 + substr($qrcode_data_string, $i, 1); 961 if (($i % 3) == 1) { 962 $data_bits[$data_counter] = 7; 963 } else { 964 $data_bits[$data_counter] = 10; 965 ++$data_counter; 966 } 967 } 968 ++$i; 969 } 970 } 971 if (array_key_exists($data_counter, $data_bits) && $data_bits[$data_counter] > 0) { 972 ++$data_counter; 973 } 974 $i = 0; 975 $total_data_bits = 0; 976 while ($i < $data_counter) { 977 $total_data_bits += $data_bits[$i]; 978 ++$i; 979 } 980 981 $ecc_character_hash = array('L' => '1', 982 'l' => '1', 983 'M' => '0', 984 'm' => '0', 985 'Q' => '3', 986 'q' => '3', 987 'H' => '2', 988 'h' => '2', ); 989 990 if (!is_numeric($qrcode_error_correct)) { 991 $ec = @$ecc_character_hash[$qrcode_error_correct]; 992 } else { 993 $ec = $qrcode_error_correct; 994 } 995 996 if (!$ec) { 997 $ec = 0; 998 } 999 1000 $max_data_bits = 0; 1001 1002 $max_data_bits_array = array( 1003 0, 128, 224, 352, 512, 688, 864, 992, 1232, 1456, 1728, 1004 2032, 2320, 2672, 2920, 3320, 3624, 4056, 4504, 5016, 5352, 1005 5712, 6256, 6880, 7312, 8000, 8496, 9024, 9544, 10136, 10984, 1006 11640, 12328, 13048, 13800, 14496, 15312, 15936, 16816, 17728, 18672, 1007 1008 152, 272, 440, 640, 864, 1088, 1248, 1552, 1856, 2192, 1009 2592, 2960, 3424, 3688, 4184, 4712, 5176, 5768, 6360, 6888, 1010 7456, 8048, 8752, 9392, 10208, 10960, 11744, 12248, 13048, 13880, 1011 14744, 15640, 16568, 17528, 18448, 19472, 20528, 21616, 22496, 23648, 1012 1013 72, 128, 208, 288, 368, 480, 528, 688, 800, 976, 1014 1120, 1264, 1440, 1576, 1784, 2024, 2264, 2504, 2728, 3080, 1015 3248, 3536, 3712, 4112, 4304, 4768, 5024, 5288, 5608, 5960, 1016 6344, 6760, 7208, 7688, 7888, 8432, 8768, 9136, 9776, 10208, 1017 1018 104, 176, 272, 384, 496, 608, 704, 880, 1056, 1232, 1019 1440, 1648, 1952, 2088, 2360, 2600, 2936, 3176, 3560, 3880, 1020 4096, 4544, 4912, 5312, 5744, 6032, 6464, 6968, 7288, 7880, 1021 8264, 8920, 9368, 9848, 10288, 10832, 11408, 12016, 12656, 13328, 1022 ); 1023 if (!is_numeric($qrcode_version)) { 1024 $qrcode_version = 0; 1025 } 1026 if (!$qrcode_version) { 1027 /* #--- auto version select */ 1028 $i = 1 + 40 * $ec; 1029 $j = $i + 39; 1030 $qrcode_version = 1; 1031 while ($i <= $j) { 1032 if (($max_data_bits_array[$i]) >= $total_data_bits + $codeword_num_plus[$qrcode_version]) { 1033 $max_data_bits = $max_data_bits_array[$i]; 1034 break; 1035 } 1036 ++$i; 1037 ++$qrcode_version; 1038 } 1039 } else { 1040 $max_data_bits = $max_data_bits_array[$qrcode_version + 40 * $ec]; 1041 } 1042 if ($qrcode_version > $version_ul) { 1043 throw new Exception('QRCode : version too large'); 1044 } 1045 1046 $total_data_bits += $codeword_num_plus[$qrcode_version]; 1047 $data_bits[$codeword_num_counter_value] += $codeword_num_plus[$qrcode_version]; 1048 1049 $max_codewords_array = array(0, 26, 44, 70, 100, 134, 172, 196, 242, 1050 292, 346, 404, 466, 532, 581, 655, 733, 815, 901, 991, 1085, 1156, 1051 1258, 1364, 1474, 1588, 1706, 1828, 1921, 2051, 2185, 2323, 2465, 1052 2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706, ); 1053 1054 $max_codewords = $max_codewords_array[$qrcode_version]; 1055 $max_modules_1side = 17 + ($qrcode_version << 2); 1056 1057 $matrix_remain_bit = array(0, 0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 1058 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, ); 1059 1060 /* ---- read version ECC data file */ 1061 1062 $byte_num = $matrix_remain_bit[$qrcode_version] + ($max_codewords << 3); 1063 $filename = $path.'/qrv'.$qrcode_version.'_'.$ec.'.dat'; 1064 $fp1 = fopen($filename, 'rb'); 1065 $matx = fread($fp1, $byte_num); 1066 $maty = fread($fp1, $byte_num); 1067 $masks = fread($fp1, $byte_num); 1068 $fi_x = fread($fp1, 15); 1069 $fi_y = fread($fp1, 15); 1070 $rs_ecc_codewords = ord(fread($fp1, 1)); 1071 $rso = fread($fp1, 128); 1072 fclose($fp1); 1073 1074 $matrix_x_array = unpack('C*', $matx); 1075 $matrix_y_array = unpack('C*', $maty); 1076 $mask_array = unpack('C*', $masks); 1077 1078 $rs_block_order = unpack('C*', $rso); 1079 1080 $format_information_x2 = unpack('C*', $fi_x); 1081 $format_information_y2 = unpack('C*', $fi_y); 1082 1083 $format_information_x1 = array(0, 1, 2, 3, 4, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8); 1084 $format_information_y1 = array(8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 4, 3, 2, 1, 0); 1085 1086 $max_data_codewords = ($max_data_bits >> 3); 1087 1088 $filename = $path.'/rsc'.$rs_ecc_codewords.'.dat'; 1089 $fp0 = fopen($filename, 'rb'); 1090 $i = 0; 1091 $rs_cal_table_array = array(); 1092 while ($i < 256) { 1093 $rs_cal_table_array[$i] = fread($fp0, $rs_ecc_codewords); 1094 ++$i; 1095 } 1096 fclose($fp0); 1097 1098 /* --- set terminator */ 1099 1100 if ($total_data_bits <= $max_data_bits - 4) { 1101 $data_value[$data_counter] = 0; 1102 $data_bits[$data_counter] = 4; 1103 } else { 1104 if ($total_data_bits < $max_data_bits) { 1105 $data_value[$data_counter] = 0; 1106 $data_bits[$data_counter] = $max_data_bits - $total_data_bits; 1107 } else { 1108 if ($total_data_bits > $max_data_bits) { 1109 throw new \OverflowException('QRCode: overflow error'); 1110 } 1111 } 1112 } 1113 1114 /* ----divide data by 8bit */ 1115 1116 $i = 0; 1117 $codewords_counter = 0; 1118 $codewords[0] = 0; 1119 $remaining_bits = 8; 1120 1121 while ($i <= $data_counter) { 1122 $buffer = @$data_value[$i]; 1123 $buffer_bits = @$data_bits[$i]; 1124 1125 $flag = 1; 1126 while ($flag) { 1127 if ($remaining_bits > $buffer_bits) { 1128 $codewords[$codewords_counter] = ((@$codewords[$codewords_counter] << $buffer_bits) | $buffer); 1129 $remaining_bits -= $buffer_bits; 1130 $flag = 0; 1131 } else { 1132 $buffer_bits -= $remaining_bits; 1133 $codewords[$codewords_counter] = (($codewords[$codewords_counter] << $remaining_bits) | ($buffer >> $buffer_bits)); 1134 1135 if ($buffer_bits == 0) { 1136 $flag = 0; 1137 } else { 1138 $buffer = ($buffer & ((1 << $buffer_bits) - 1)); 1139 $flag = 1; 1140 } 1141 1142 ++$codewords_counter; 1143 if ($codewords_counter < $max_data_codewords - 1) { 1144 $codewords[$codewords_counter] = 0; 1145 } 1146 $remaining_bits = 8; 1147 } 1148 } 1149 ++$i; 1150 } 1151 if ($remaining_bits != 8) { 1152 $codewords[$codewords_counter] = $codewords[$codewords_counter] << $remaining_bits; 1153 } else { 1154 --$codewords_counter; 1155 } 1156 1157 /* ---- set padding character */ 1158 1159 if ($codewords_counter < $max_data_codewords - 1) { 1160 $flag = 1; 1161 while ($codewords_counter < $max_data_codewords - 1) { 1162 ++$codewords_counter; 1163 if ($flag == 1) { 1164 $codewords[$codewords_counter] = 236; 1165 } else { 1166 $codewords[$codewords_counter] = 17; 1167 } 1168 $flag = $flag * (-1); 1169 } 1170 } 1171 1172 /* ---- RS-ECC prepare */ 1173 1174 $i = 0; 1175 $j = 0; 1176 $rs_block_number = 0; 1177 $rs_temp[0] = ''; 1178 1179 while ($i < $max_data_codewords) { 1180 $rs_temp[$rs_block_number] .= chr($codewords[$i]); 1181 ++$j; 1182 1183 if ($j >= $rs_block_order[$rs_block_number + 1] - $rs_ecc_codewords) { 1184 $j = 0; 1185 ++$rs_block_number; 1186 $rs_temp[$rs_block_number] = ''; 1187 } 1188 ++$i; 1189 } 1190 1191 /* 1192 # 1193 # RS-ECC main 1194 # 1195 */ 1196 1197 $rs_block_number = 0; 1198 $rs_block_order_num = count($rs_block_order); 1199 1200 while ($rs_block_number < $rs_block_order_num) { 1201 $rs_codewords = $rs_block_order[$rs_block_number + 1]; 1202 $rs_data_codewords = $rs_codewords - $rs_ecc_codewords; 1203 1204 $rstemp = $rs_temp[$rs_block_number].str_repeat(chr(0), $rs_ecc_codewords); 1205 $padding_data = str_repeat(chr(0), $rs_data_codewords); 1206 1207 $j = $rs_data_codewords; 1208 while ($j > 0) { 1209 $first = ord(substr($rstemp, 0, 1)); 1210 1211 if ($first) { 1212 $left_chr = substr($rstemp, 1); 1213 $cal = $rs_cal_table_array[$first].$padding_data; 1214 $rstemp = $left_chr ^ $cal; 1215 } else { 1216 $rstemp = substr($rstemp, 1); 1217 } 1218 1219 --$j; 1220 } 1221 1222 $codewords = array_merge($codewords, unpack('C*', $rstemp)); 1223 1224 ++$rs_block_number; 1225 } 1226 1227 /* ---- flash matrix */ 1228 $matrix_content = array(); 1229 $i = 0; 1230 while ($i < $max_modules_1side) { 1231 $j = 0; 1232 while ($j < $max_modules_1side) { 1233 $matrix_content[$j][$i] = 0; 1234 ++$j; 1235 } 1236 ++$i; 1237 } 1238 1239 /* --- attach data */ 1240 1241 $i = 0; 1242 while ($i < $max_codewords) { 1243 $codeword_i = $codewords[$i]; 1244 $j = 8; 1245 while ($j >= 1) { 1246 $codeword_bits_number = ($i << 3) + $j; 1247 $matrix_content[ $matrix_x_array[$codeword_bits_number] ][ $matrix_y_array[$codeword_bits_number] ] = ((255 * ($codeword_i & 1)) ^ $mask_array[$codeword_bits_number]); 1248 $codeword_i = $codeword_i >> 1; 1249 --$j; 1250 } 1251 ++$i; 1252 } 1253 1254 $matrix_remain = $matrix_remain_bit[$qrcode_version]; 1255 while ($matrix_remain) { 1256 $remain_bit_temp = $matrix_remain + ($max_codewords << 3); 1257 $matrix_content[ $matrix_x_array[$remain_bit_temp] ][ $matrix_y_array[$remain_bit_temp] ] = (255 ^ $mask_array[$remain_bit_temp]); 1258 --$matrix_remain; 1259 } 1260 1261 #--- mask select 1262 1263 $min_demerit_score = 0; 1264 $hor_master = ''; 1265 $ver_master = ''; 1266 $k = 0; 1267 while ($k < $max_modules_1side) { 1268 $l = 0; 1269 while ($l < $max_modules_1side) { 1270 $hor_master = $hor_master.chr($matrix_content[$l][$k]); 1271 $ver_master = $ver_master.chr($matrix_content[$k][$l]); 1272 ++$l; 1273 } 1274 ++$k; 1275 } 1276 $i = 0; 1277 $all_matrix = $max_modules_1side * $max_modules_1side; 1278 $mask_number = 0; 1279 while ($i < 8) { 1280 $demerit_n1 = 0; 1281 $ptn_temp = array(); 1282 $bit = 1 << $i; 1283 $bit_r = (~$bit) & 255; 1284 $bit_mask = str_repeat(chr($bit), $all_matrix); 1285 $hor = $hor_master & $bit_mask; 1286 $ver = $ver_master & $bit_mask; 1287 1288 $ver_shift1 = $ver.str_repeat(chr(170), $max_modules_1side); 1289 $ver_shift2 = str_repeat(chr(170), $max_modules_1side).$ver; 1290 $ver_shift1_0 = $ver.str_repeat(chr(0), $max_modules_1side); 1291 $ver_shift2_0 = str_repeat(chr(0), $max_modules_1side).$ver; 1292 $ver_or = chunk_split(~($ver_shift1 | $ver_shift2), $max_modules_1side, chr(170)); 1293 $ver_and = chunk_split(~($ver_shift1_0 & $ver_shift2_0), $max_modules_1side, chr(170)); 1294 1295 $hor = chunk_split(~$hor, $max_modules_1side, chr(170)); 1296 $ver = chunk_split(~$ver, $max_modules_1side, chr(170)); 1297 $hor = $hor.chr(170).$ver; 1298 1299 $n1_search = '/'.str_repeat(chr(255), 5).'+|'.str_repeat(chr($bit_r), 5).'+/'; 1300 $n3_search = chr($bit_r).chr(255).chr($bit_r).chr($bit_r).chr($bit_r).chr(255).chr($bit_r); 1301 1302 $demerit_n3 = substr_count($hor, $n3_search) * 40; 1303 $demerit_n4 = floor(abs(((100 * (substr_count($ver, chr($bit_r)) / ($byte_num))) - 50) / 5)) * 10; 1304 1305 $n2_search1 = '/'.chr($bit_r).chr($bit_r).'+/'; 1306 $n2_search2 = '/'.chr(255).chr(255).'+/'; 1307 $demerit_n2 = 0; 1308 preg_match_all($n2_search1, $ver_and, $ptn_temp); 1309 foreach ($ptn_temp[0] as $str_temp) { 1310 $demerit_n2 += (strlen($str_temp) - 1); 1311 } 1312 $ptn_temp = array(); 1313 preg_match_all($n2_search2, $ver_or, $ptn_temp); 1314 foreach ($ptn_temp[0] as $str_temp) { 1315 $demerit_n2 += (strlen($str_temp) - 1); 1316 } 1317 $demerit_n2 *= 3; 1318 1319 $ptn_temp = array(); 1320 1321 preg_match_all($n1_search, $hor, $ptn_temp); 1322 foreach ($ptn_temp[0] as $str_temp) { 1323 $demerit_n1 += (strlen($str_temp) - 2); 1324 } 1325 1326 $demerit_score = $demerit_n1 + $demerit_n2 + $demerit_n3 + $demerit_n4; 1327 1328 if ($demerit_score <= $min_demerit_score || $i == 0) { 1329 $mask_number = $i; 1330 $min_demerit_score = $demerit_score; 1331 } 1332 1333 ++$i; 1334 } 1335 1336 $mask_content = 1 << $mask_number; 1337 1338 # --- format information 1339 1340 $format_information_value = (($ec << 3) | $mask_number); 1341 $format_information_array = array('101010000010010', '101000100100101', 1342 '101111001111100', '101101101001011', '100010111111001', '100000011001110', 1343 '100111110010111', '100101010100000', '111011111000100', '111001011110011', 1344 '111110110101010', '111100010011101', '110011000101111', '110001100011000', 1345 '110110001000001', '110100101110110', '001011010001001', '001001110111110', 1346 '001110011100111', '001100111010000', '000011101100010', '000001001010101', 1347 '000110100001100', '000100000111011', '011010101011111', '011000001101000', 1348 '011111100110001', '011101000000110', '010010010110100', '010000110000011', 1349 '010111011011010', '010101111101101', ); 1350 $i = 0; 1351 while ($i < 15) { 1352 $content = substr($format_information_array[$format_information_value], $i, 1); 1353 1354 $matrix_content[$format_information_x1[$i]][$format_information_y1[$i]] = $content * 255; 1355 $matrix_content[$format_information_x2[$i + 1]][$format_information_y2[$i + 1]] = $content * 255; 1356 ++$i; 1357 } 1358 1359 $mib = $max_modules_1side + 8; 1360 1361 if ($this->size == 0) { 1362 $this->size = $mib * $qrcode_module_size; 1363 if ($this->size > 1480) { 1364 throw new Exception('QRCode: image size too large'); 1365 } 1366 } 1367 1368 $image_width = $this->size + $this->padding * 2; 1369 $image_height = $this->size + $this->padding * 2; 1370 1371 if (!empty($this->label)) { 1372 if (!function_exists('imagettfbbox')) { 1373 throw new Exception('QRCode: missing function "imagettfbbox". Did you install the FreeType library?'); 1374 } 1375 $font_box = imagettfbbox($this->label_font_size, 0, $this->label_font_path, $this->label); 1376 $label_width = (int) $font_box[2] - (int) $font_box[0]; 1377 $label_height = (int) $font_box[0] - (int) $font_box[7]; 1378 1379 if ($this->label_valign == self::LABEL_VALIGN_MIDDLE) { 1380 $image_height += $label_height + $this->padding; 1381 } else { 1382 $image_height += $label_height; 1383 } 1384 } 1385 1386 $output_image = imagecreate($image_width, $image_height); 1387 imagecolorallocate($output_image, 255, 255, 255); 1388 1389 $image_path = $image_path.'/qrv'.$qrcode_version.'.png'; 1390 1391 $base_image = imagecreatefrompng($image_path); 1392 $code_size = $this->size; 1393 $module_size = function ($size = 1) use ($code_size, $base_image) { 1394 return round($code_size / imagesx($base_image) * $size); 1395 }; 1396 1397 $col[1] = imagecolorallocate($base_image, 0, 0, 0); 1398 $col[0] = imagecolorallocate($base_image, 255, 255, 255); 1399 1400 $i = 4; 1401 $mxe = 4 + $max_modules_1side; 1402 $ii = 0; 1403 while ($i < $mxe) { 1404 $j = 4; 1405 $jj = 0; 1406 while ($j < $mxe) { 1407 if ($matrix_content[$ii][$jj] & $mask_content) { 1408 imagesetpixel($base_image, $i, $j, $col[1]); 1409 } 1410 ++$j; 1411 ++$jj; 1412 } 1413 ++$i; 1414 ++$ii; 1415 } 1416 1417 if ($this->draw_quiet_zone == true) { 1418 imagecopyresampled($output_image, $base_image, $this->padding, $this->padding, 0, 0, $this->size, $this->size, $mib, $mib); 1419 } else { 1420 imagecopyresampled($output_image, $base_image, $this->padding, $this->padding, 4, 4, $this->size, $this->size, $mib - 8, $mib - 8); 1421 } 1422 1423 if ($this->draw_border == true) { 1424 $border_width = $this->padding; 1425 $border_height = $this->size + $this->padding - 1; 1426 $border_color = imagecolorallocate($output_image, 0, 0, 0); 1427 imagerectangle($output_image, $border_width, $border_width, $border_height, $border_height, $border_color); 1428 } 1429 1430 if (!empty($this->label)) { 1431 // Label horizontal alignment 1432 switch ($this->label_halign) { 1433 case self::LABEL_HALIGN_LEFT: 1434 $font_x = 0; 1435 break; 1436 1437 case self::LABEL_HALIGN_LEFT_BORDER: 1438 $font_x = $this->padding; 1439 break; 1440 1441 case self::LABEL_HALIGN_LEFT_CODE: 1442 if ($this->draw_quiet_zone == true) { 1443 $font_x = $this->padding + $module_size(4); 1444 } else { 1445 $font_x = $this->padding; 1446 } 1447 break; 1448 1449 case self::LABEL_HALIGN_RIGHT: 1450 $font_x = $this->size + ($this->padding * 2) - $label_width; 1451 break; 1452 1453 case self::LABEL_HALIGN_RIGHT_BORDER: 1454 $font_x = $this->size + $this->padding - $label_width; 1455 break; 1456 1457 case self::LABEL_HALIGN_RIGHT_CODE: 1458 if ($this->draw_quiet_zone == true) { 1459 $font_x = $this->size + $this->padding - $label_width - $module_size(4); 1460 } else { 1461 $font_x = $this->size + $this->padding - $label_width; 1462 } 1463 break; 1464 1465 default: 1466 $font_x = floor($image_width - $label_width) / 2; 1467 } 1468 1469 // Label vertical alignment 1470 switch ($this->label_valign) { 1471 case self::LABEL_VALIGN_TOP_NO_BORDER: 1472 $font_y = $image_height - $this->padding - 1; 1473 break; 1474 1475 case self::LABEL_VALIGN_BOTTOM: 1476 $font_y = $image_height; 1477 break; 1478 1479 default: 1480 $font_y = $image_height - $this->padding; 1481 } 1482 1483 $label_bg_x1 = $font_x - $module_size(2); 1484 $label_bg_y1 = $font_y - $label_height; 1485 $label_bg_x2 = $font_x + $label_width + $module_size(2); 1486 $label_bg_y2 = $font_y; 1487 1488 $color = imagecolorallocate($output_image, 0, 0, 0); 1489 $label_bg_color = imagecolorallocate($output_image, 255, 255, 255); 1490 1491 imagefilledrectangle($output_image, $label_bg_x1, $label_bg_y1, $label_bg_x2, $label_bg_y2, $label_bg_color); 1492 imagettftext($output_image, $this->label_font_size, 0, $font_x, $font_y, $color, $this->label_font_path, $this->label); 1493 } 1494 1495 $imagecolorset_function = new ReflectionFunction('imagecolorset'); 1496 $allow_alpha = $imagecolorset_function->getNumberOfParameters() == 6; 1497 1498 if ($this->color_background != null) { 1499 $index = imagecolorclosest($output_image, 255, 255, 255); 1500 if ($allow_alpha) { 1501 imagecolorset($output_image, $index, $this->color_background['r'], $this->color_background['g'], $this->color_background['b'], $this->color_background['a']); 1502 } else { 1503 imagecolorset($output_image, $index, $this->color_background['r'], $this->color_background['g'], $this->color_background['b']); 1504 } 1505 } 1506 1507 if ($this->color_foreground != null) { 1508 $index = imagecolorclosest($output_image, 0, 0, 0); 1509 if ($allow_alpha) { 1510 imagecolorset($output_image, $index, $this->color_foreground['r'], $this->color_foreground['g'], $this->color_foreground['b'], $this->color_foreground['a']); 1511 } else { 1512 imagecolorset($output_image, $index, $this->color_foreground['r'], $this->color_foreground['g'], $this->color_foreground['b']); 1513 } 1514 } 1515 1516 $this->image = $output_image; 1517 } 1518} 1519