개발 꿀팁/PHP

php 로고 QR 클래스 만들기

Jammie 2022. 8. 16. 17:25
반응형

이 글은 php 구현에 대해 QR코드 클래스 생성, 치수 설정 지원, LOGO 추가, 선, 필렛, 투명도 등의 처리를 소개한다.전체 코드, 데모 인스턴스 및 상세 파라미터 설명이 제공되어 학습에 편리합니다.


구현 기능은 다음과 같습니다.
1.QR코드 만들기
2.QR코드에 로고 추가
3.로고는 그릴 수 있다.
4.로고 라운드 가능
5.logo 투명도 설정 가능
6.logo 이미지 및 출력 이미지 유형은 png, jpg, gif 포맷을 지원합니다.
7.출력 이미지 품질 설정 가능

설정 매개 변수 설명:
ecc
QR 품질 L-smallest, M, Q, H-best

size
QR사이즈1-50

dest_filE
생성된 QR코드 이미지경로

quality
생성된 그림의 품질

로고
logo 경로:빈칸은 logo에 가입하지 않음을 나타냅니다.

로고_sizE
로고 사이즈, null은 QR코드 사이즈 비율에 따라 자동으로 계산됨을 나타냅니다.

logo_outline_size
로고 스트로크기null은 logo 사이즈에 비례하여 자동으로 계산됨을 나타냅니다.

logo_outline_color
로고 선 컬러

logo_opacity
로고 불투명도 0-100

로고_라디우스
로고 라운드 각도 0-30


코드는 다음과 같습니다.
PHPQRCode.class.php

<?php
require_once dirname(__FILE__)."/qrcode/qrlib.php";

/**
 * PHP QR코드 클래스 만들기
 * Date:    2018-03-18
 * Author:  fdipzone
 * Version: 1.0
 *
 * Description:
 * PHP 구현 QR코드 클래스 생성, 치수 설정 지원, 로고 추가, 필렛, 투명도 등 처리 지원。
 *
 * Func:
 * public  set_config           설정
 * public  generate             QR코드 만들기
 * private create_qrcode        순수 QR코드 그림 만들기
 * private add_logo             퓨어 QR 이미지와 로고 이미지를 합쳐서
 * private image_outline        그림 오브젝트에 테두리를 그리다
 * private image_fillet         객체를 둥글게 처리하다
 * private imagecopymerge_alpha 그림을 합쳐서 투명도를 유지하다
 * private create_dirs          디렉터리 만들기
 * private hex2rgb              헥스 색상 rgb 색상 변환
 * private get_file_ext         그림 종류 가져오기
 */
class PHPQRCode{ // class start

    /** 默认设定 */
    private $_config = array(
        'ecc' => 'H',                       // 차원 품질 L-smallest, M, Q, H-best
        'size' => 15,                       // QR사이즈 1-50
        'dest_file' => 'qrcode.png',        // 만든 QR코드 경로
        'quality' => 100,                    // 이미지 품질
        'logo' => '',                       // logo 경로, 비어 있으면 logo가 없음을 나타냅니다
        'logo_size' => null,                // 로고 사이즈, null은 QR코드 사이즈 비율로 자동 계산됨을 나타냅니다
        'logo_outline_size' => null,        // 로고 스트로크 사이즈, null은 로고 사이즈 비례 자동 계산
        'logo_outline_color' => '#FFFFFF',  // 로고 선 컬러
        'logo_opacity' => 100,              // 로고 불투명도 0-100
        'logo_radius' => 0,                 // 로고 라운드 각도 0-30
    );

    /**
     * 설정
     * @param  Array   $config 내용 설정
     */
    public function set_config($config){

        // 설정 허용 설정
        $config_keys = array_keys($this->_config);

        //들어오는 설정 가져오기, 쓰기 설정
        foreach($config_keys as $k=>$v){
            if(isset($config[$v])){
                $this->_config[$v] = $config[$v];
            }
        }

    }

    /**
     * QR코드 만들기
     * @param  String $data QR코드 내용
     * @return String
     */
    public function generate($data){

        // 임시 QR코드 그림 만들기
        $tmp_qrcode_file = $this->create_qrcode($data);

        // 임시 QR코드 이미지와 로고 이미지를 합쳐서
        $this->add_logo($tmp_qrcode_file);

        // 임시 QR코드 이미지 삭제
        if($tmp_qrcode_file!='' && file_exists($tmp_qrcode_file)){
            unlink($tmp_qrcode_file);
        }

        return file_exists($this->_config['dest_file'])? $this->_config['dest_file'] : '';

    }

    /**
     * 임시 QR코드 그림 만들기
     * @param  String $data QR코드 내
     * @return String
     */
    private function create_qrcode($data){

        // 임시 QR코드 이미지
        $tmp_qrcode_file = dirname(__FILE__).'/tmp_qrcode_'.time().mt_rand(100,999).'.png';

        // 임시 QR코드 만들기
        QRcode::png($data, $tmp_qrcode_file, $this->_config['ecc'], $this->_config['size'], 2);

        // 임시 QR코드 경로 되돌리기
        return file_exists($tmp_qrcode_file)? $tmp_qrcode_file : '';

    }

    /**
     * 임시 QR코드 이미지와 로고 이미지를 합쳐서
     * @param String $tmp_qrcode_file 임시 QR코드 이미지
     */
    private function add_logo($tmp_qrcode_file){

        // 대상 폴더 만들기
        $this->create_dirs(dirname($this->_config['dest_file']));

        // 대상 그림 종류 가져오기
        $dest_ext = $this->get_file_ext($this->_config['dest_file']);

        // 로고 가입이 필요함
        if(file_exists($this->_config['logo'])){

            // 임시 QR코드 그림 개체 만들기
            $tmp_qrcode_img = imagecreatefrompng($tmp_qrcode_file);

            //임시 QR코드 그림 크기 가져오기
            list($qrcode_w, $qrcode_h, $qrcode_type) = getimagesize($tmp_qrcode_file);

            // 로고 이미지 크기 및 종류 가져오기
            list($logo_w, $logo_h, $logo_type) = getimagesize($this->_config['logo']);

            // 로고 그림 개체 만들기
            switch($logo_type){  
                case 1: $logo_img = imagecreatefromgif($this->_config['logo']); break;  
                case 2: $logo_img = imagecreatefromjpeg($this->_config['logo']); break;  
                case 3: $logo_img = imagecreatefrompng($this->_config['logo']); break;  
                default: return '';  
            }

            // 로고 이미지 맞춤 사이즈 설정, 미설정 시 비례 자동 계산
            $new_logo_w = isset($this->_config['logo_size'])? $this->_config['logo_size'] : (int)($qrcode_w/5);
            $new_logo_h = isset($this->_config['logo_size'])? $this->_config['logo_size'] : (int)($qrcode_h/5);

            // 설정 사이즈에 따라 로고 이미지 조정
            $new_logo_img = imagecreatetruecolor($new_logo_w, $new_logo_h);
            imagecopyresampled($new_logo_img, $logo_img, 0, 0, 0, 0, $new_logo_w, $new_logo_h, $logo_w, $logo_h);

            // 선화가 필요한지 여부를 판단하다
            if(!isset($this->_config['logo_outline_size']) || $this->_config['logo_outline_size']>0){
                list($new_logo_img, $new_logo_w, $new_logo_h) = $this->image_outline($new_logo_img);
            }

            // 필렛 처리가 필요한지 여부를 판단하다
            if($this->_config['logo_radius']>0){
                $new_logo_img = $this->image_fillet($new_logo_img);
            }

            // 스펠링 로고와 임시 QR코드
            $pos_x = ($qrcode_w-$new_logo_w)/2;
            $pos_y = ($qrcode_h-$new_logo_h)/2;

            imagealphablending($tmp_qrcode_img, true);

            // 그림을 합쳐서 투명도를 유지하다
            $dest_img = $this->imagecopymerge_alpha($tmp_qrcode_img, $new_logo_img, $pos_x, $pos_y, 0, 0, $new_logo_w, $new_logo_h, $this->_config['logo_opacity']);

            // 그림 생성
            switch($dest_ext){
                case 1: imagegif($dest_img, $this->_config['dest_file'], $this->_config['quality']); break;
                case 2: imagejpeg($dest_img, $this->_config['dest_file'], $this->_config['quality']); break;
                case 3: imagepng($dest_img, $this->_config['dest_file'], (int)(($this->_config['quality']-1)/10)); break;
            } 

        // 로고 가입 필요 없음
        }else{

            $dest_img = imagecreatefrompng($tmp_qrcode_file);

            // 그림 생성
            switch($dest_ext){
                case 1: imagegif($dest_img, $this->_config['dest_file'], $this->_config['quality']); break;
                case 2: imagejpeg($dest_img, $this->_config['dest_file'], $this->_config['quality']); break;
                case 3: imagepng($dest_img, $this->_config['dest_file'], (int)(($this->_config['quality']-1)/10)); break;
            }
        }

    }

    /**
     * 그림 오브젝트에 테두리를 그리다
     * @param  Obj   $img 그림 객체
     * @return Array
     */
    private function image_outline($img){

        // 그림 너비 가져오기
        $img_w = imagesx($img);
        $img_h = imagesy($img);

        // 선 치수를 계산하고, 설정이 없으면 비례로 자동 계산
        $bg_w = isset($this->_config['logo_outline_size'])? intval($img_w + $this->_config['logo_outline_size']) : $img_w + (int)($img_w/5);
        $bg_h = isset($this->_config['logo_outline_size'])? intval($img_h + $this->_config['logo_outline_size']) : $img_h + (int)($img_h/5);

        //밑그림 개체 만들기
        $bg_img = imagecreatetruecolor($bg_w, $bg_h);

        // 밑그림 색상 설정
        $rgb = $this->hex2rgb($this->_config['logo_outline_color']);
        $bgcolor = imagecolorallocate($bg_img, $rgb['r'], $rgb['g'], $rgb['b']);

        // 밑그림 색 채우기
        imagefill($bg_img, 0, 0, $bgcolor);

        // 그림과 밑그림을 합쳐서 선명한 효과를 실현하다
        imagecopy($bg_img, $img, (int)(($bg_w-$img_w)/2), (int)(($bg_h-$img_h)/2), 0, 0, $img_w, $img_h);

        $img = $bg_img;

        return array($img, $bg_w, $bg_h);

    }

    /**
     * 그림 객체를 둥글게 처리하다
     * @param  Obj $img 그림 객체
     * @return Obj
     */
    private function image_fillet($img){

        // 그림 너비 가져오기
        $img_w = imagesx($img);
        $img_h = imagesy($img);

        // 둥근 모서리 그림 개체 만들기
        $new_img = imagecreatetruecolor($img_w, $img_h);

        //투명 채널 저장 
        imagesavealpha($new_img, true);

        // 필렛 그림
        $bg = imagecolorallocatealpha($new_img, 255, 255, 255, 127);
        imagefill($new_img, 0, 0, $bg);

        // 필렛 반경
        $r = $this->_config['logo_radius'];

        // 필렛 처리를 하다
        for($x=0; $x<$img_w; $x++){
            for($y=0; $y<$img_h; $y++){
                $rgb = imagecolorat($img, $x, $y);

                // 이미지 사각 범위 없이 직접 그림 그리기
                if(($x>=$r && $x<=($img_w-$r)) || ($y>=$r && $y<=($img_h-$r))){
                    imagesetpixel($new_img, $x, $y, $rgb);

                // 이미지 사각 범위에서 그림 선택
                }else{
                    // 상좌
                    $ox = $r; // 원심 x 좌표
                    $oy = $r; // 원심 y 좌표
                    if( ( ($x-$ox)*($x-$ox) + ($y-$oy)*($y-$oy) ) <= ($r*$r) ){
                        imagesetpixel($new_img, $x, $y, $rgb);
                    }

                    // 상우
                    $ox = $img_w-$r; // 원심 x 좌표
                    $oy = $r;        // 원심 y 좌표
                    if( ( ($x-$ox)*($x-$ox) + ($y-$oy)*($y-$oy) ) <= ($r*$r) ){
                        imagesetpixel($new_img, $x, $y, $rgb);
                    }

                    // 아래쪽 왼쪽
                    $ox = $r;        // 원심 x 좌표
                    $oy = $img_h-$r; // 원심 y 좌표
                    if( ( ($x-$ox)*($x-$ox) + ($y-$oy)*($y-$oy) ) <= ($r*$r) ){
                        imagesetpixel($new_img, $x, $y, $rgb);
                    }

                    // 아래쪽 오른쪽
                    $ox = $img_w-$r; // 원심 x 좌표
                    $oy = $img_h-$r; //원심 y 좌표
                    if( ( ($x-$ox)*($x-$ox) + ($y-$oy)*($y-$oy) ) <= ($r*$r) ){
                        imagesetpixel($new_img, $x, $y, $rgb);
                    }

                }

            }
        }

        return $new_img;

    }

    // 그림을 합쳐서 투명도를 유지하다
    private function imagecopymerge_alpha($dest_img, $src_img, $pos_x, $pos_y, $src_x, $src_y, $src_w, $src_h, $opacity){

        $w = imagesx($src_img);
        $h = imagesy($src_img);

        $tmp_img = imagecreatetruecolor($src_w, $src_h);

        imagecopy($tmp_img, $dest_img, 0, 0, $pos_x, $pos_y, $src_w, $src_h);
        imagecopy($tmp_img, $src_img, 0, 0, $src_x, $src_y, $src_w, $src_h);
        imagecopymerge($dest_img, $tmp_img, $pos_x, $pos_y, $src_x, $src_y, $src_w, $src_h, $opacity);

        return $dest_img;

    }

    /**
     * 디렉터리 만들기
     * @param  String  $path
     * @return Boolean
     */
    private function create_dirs($path){

        if(!is_dir($path)){
            return mkdir($path, 0777, true);
        }

        return true;

    }

    /** 헥스 색상 rgb 색상 변환
     *  @param  String $color hex색깔
     *  @return Array
     */
    private function hex2rgb($hexcolor){
        $color = str_replace('#', '', $hexcolor);
        if (strlen($color) > 3) {
            $rgb = array(
                'r' => hexdec(substr($color, 0, 2)),
                'g' => hexdec(substr($color, 2, 2)),
                'b' => hexdec(substr($color, 4, 2))
            );
        } else {
            $r = substr($color, 0, 1) . substr($color, 0, 1);
            $g = substr($color, 1, 1) . substr($color, 1, 1);
            $b = substr($color, 2, 1) . substr($color, 2, 1);
            $rgb = array(
                'r' => hexdec($r),
                'g' => hexdec($g),
                'b' => hexdec($b)
            );
        }
        return $rgb;
    }

    /** 그림 종류 가져오기
     * @param  String $file 그림 경로
     * @return int 
     */  
    private function get_file_ext($file){
        $filename = basename($file);
        list($name, $ext)= explode('.', $filename);

        $ext_type = 0;

        switch(strtolower($ext)){
            case 'jpg':
            case 'jpeg':
                $ext_type = 2;
                break;
            case 'gif':
                $ext_type = 1;
                break;
            case 'png':
                $ext_type = 3;
                break;
        }

        return $ext_type;
    }

} // class end
?>

demo.php

<?php
require 'PHPQRCode.class.php';

$config = array(
        'ecc' => 'H',    // L-smallest, M, Q, H-best
        'size' => 12,    // 1-50
        'dest_file' => 'qrcode.png',
        'quality' => 90,
        'logo' => 'logo.jpg',
        'logo_size' => 100,
        'logo_outline_size' => 20,
        'logo_outline_color' => '#FFFF00',
        'logo_radius' => 15,
        'logo_opacity' => 100,
);

// QR코드 내용
$data = 'http://weibo.com/fdipzone';

// QR코드 클래스 만들기
$oPHPQRCode = new PHPQRCode();

// 설정
$oPHPQRCode->set_config($config);

//QR코드 만들기
$qrcode = $oPHPQRCode->generate($data);

// QR코드 보이기
echo '<img src="'.$qrcode.'?t='.time().'">';
?>

 

반응형