cari
Rumahpembangunan bahagian belakangtutorial php如何通过php 创建带logo二维码类

本文介绍php实现创建二维码类,支持设置尺寸,加入LOGO,描边、圆角、透明度,等处理。提供完整代码,演示实例及详细参数说明,方便大家学习使用。 

实现功能如下:

1.创建二维码
2.加入logo到二维码中
3.logo可描边
4.logo可圆角
5.logo可设透明度
6.logo图片及输出图片类型支持png,jpg,gif格式
7.可设置输出图片质量

设定参数说明:

ecc
二维码质量 L-smallest, M, Q, H-best

size
二维码尺寸 1-50

dest_file
生成的二维码图片路径

quality
生成的图片质量

logo
logo路径,为空表示不加入logo

logo_size
logo尺寸,null表示按二维码尺寸比例自动计算

logo_outline_size
logo描边尺寸,null表示按logo尺寸按比例自动计算

logo_outline_color
logo描边颜色

logo_opacity
logo不透明度 0-100

logo_radius
logo圆角角度 0-30
代码如下:

PHPQRCode.class.php

<?phprequire_once dirname(__FILE__)."/qrcode/qrlib.php";/**
 * PHP创建二维码类
 * Date:    2018-03-18
 * Author:  fdipzone
 * Version: 1.0
 *
 * Description:
 * PHP实现创建二维码类,支持设置尺寸,加入LOGO,圆角,透明度,等处理。
 *
 * Func:
 * public  set_config           设定配置
 * public  generate             创建二维码
 * private create_qrcode        创建纯二维码图片
 * private add_logo             合拼纯二维码图片与logo图片
 * private image_outline        图片对象进行描边
 * private image_fillet         图片对象进行圆角处理
 * private imagecopymerge_alpha 合拼图片并保留各自透明度
 * private create_dirs          创建目录
 * private hex2rgb              hex颜色转rgb颜色
 * private get_file_ext         获取图片类型
 */class PHPQRCode{ // class start

    /** 默认设定 */
    private $_config = array(        &#39;ecc&#39; => &#39;H&#39;,                       // 二维码质量 L-smallest, M, Q, H-best
        &#39;size&#39; => 15,                       // 二维码尺寸 1-50
        &#39;dest_file&#39; => &#39;qrcode.png&#39;,        // 创建的二维码路径
        &#39;quality&#39; => 100,                    // 图片质量
        &#39;logo&#39; => &#39;&#39;,                       // logo路径,为空表示没有logo
        &#39;logo_size&#39; => null,                // logo尺寸,null表示按二维码尺寸比例自动计算
        &#39;logo_outline_size&#39; => null,        // logo描边尺寸,null表示按logo尺寸按比例自动计算
        &#39;logo_outline_color&#39; => &#39;#FFFFFF&#39;,  // logo描边颜色
        &#39;logo_opacity&#39; => 100,              // logo不透明度 0-100
        &#39;logo_radius&#39; => 0,                 // logo圆角角度 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];
            }
        }

    }    /**
     * 创建二维码
     * @param  String $data 二维码内容
     * @return String
     */
    public function generate($data){

        // 创建临时二维码图片
        $tmp_qrcode_file = $this->create_qrcode($data);        // 合拼临时二维码图片与logo图片
        $this->add_logo($tmp_qrcode_file);        // 删除临时二维码图片
        if($tmp_qrcode_file!=&#39;&#39; && file_exists($tmp_qrcode_file)){
            unlink($tmp_qrcode_file);
        }        return file_exists($this->_config[&#39;dest_file&#39;])? $this->_config[&#39;dest_file&#39;] : &#39;&#39;;

    }    /**
     * 创建临时二维码图片
     * @param  String $data 二维码内容
     * @return String
     */
    private function create_qrcode($data){

        // 临时二维码图片
        $tmp_qrcode_file = dirname(__FILE__).&#39;/tmp_qrcode_&#39;.time().mt_rand(100,999).&#39;.png&#39;;        // 创建临时二维码
        QRcode::png($data, $tmp_qrcode_file, $this->_config[&#39;ecc&#39;], $this->_config[&#39;size&#39;], 2);        // 返回临时二维码路径
        return file_exists($tmp_qrcode_file)? $tmp_qrcode_file : &#39;&#39;;

    }    /**
     * 合拼临时二维码图片与logo图片
     * @param String $tmp_qrcode_file 临时二维码图片
     */
    private function add_logo($tmp_qrcode_file){

        // 创建目标文件夹
        $this->create_dirs(dirname($this->_config[&#39;dest_file&#39;]));        // 获取目标图片的类型
        $dest_ext = $this->get_file_ext($this->_config[&#39;dest_file&#39;]);        // 需要加入logo
        if(file_exists($this->_config[&#39;logo&#39;])){            // 创建临时二维码图片对象
            $tmp_qrcode_img = imagecreatefrompng($tmp_qrcode_file);            // 获取临时二维码图片尺寸
            list($qrcode_w, $qrcode_h, $qrcode_type) = getimagesize($tmp_qrcode_file);            // 获取logo图片尺寸及类型
            list($logo_w, $logo_h, $logo_type) = getimagesize($this->_config[&#39;logo&#39;]);            // 创建logo图片对象
            switch($logo_type){  
                case 1: $logo_img = imagecreatefromgif($this->_config[&#39;logo&#39;]); break;  
                case 2: $logo_img = imagecreatefromjpeg($this->_config[&#39;logo&#39;]); break;  
                case 3: $logo_img = imagecreatefrompng($this->_config[&#39;logo&#39;]); break;  
                default: return &#39;&#39;;  
            }            // 设定logo图片合拼尺寸,没有设定则按比例自动计算
            $new_logo_w = isset($this->_config[&#39;logo_size&#39;])? $this->_config[&#39;logo_size&#39;] : (int)($qrcode_w/5);            $new_logo_h = isset($this->_config[&#39;logo_size&#39;])? $this->_config[&#39;logo_size&#39;] : (int)($qrcode_h/5);            // 按设定尺寸调整logo图片
            $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[&#39;logo_outline_size&#39;]) || $this->_config[&#39;logo_outline_size&#39;]>0){                list($new_logo_img, $new_logo_w, $new_logo_h) = $this->image_outline($new_logo_img);
            }            // 判断是否需要圆角处理
            if($this->_config[&#39;logo_radius&#39;]>0){                $new_logo_img = $this->image_fillet($new_logo_img);
            }            // 合拼logo与临时二维码
            $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[&#39;logo_opacity&#39;]);            // 生成图片
            switch($dest_ext){                case 1: imagegif($dest_img, $this->_config[&#39;dest_file&#39;], $this->_config[&#39;quality&#39;]); break;                case 2: imagejpeg($dest_img, $this->_config[&#39;dest_file&#39;], $this->_config[&#39;quality&#39;]); break;                case 3: imagepng($dest_img, $this->_config[&#39;dest_file&#39;], (int)(($this->_config[&#39;quality&#39;]-1)/10)); break;
            } 

        // 不需要加入logo
        }else{            $dest_img = imagecreatefrompng($tmp_qrcode_file);            // 生成图片
            switch($dest_ext){                case 1: imagegif($dest_img, $this->_config[&#39;dest_file&#39;], $this->_config[&#39;quality&#39;]); break;                case 2: imagejpeg($dest_img, $this->_config[&#39;dest_file&#39;], $this->_config[&#39;quality&#39;]); break;                case 3: imagepng($dest_img, $this->_config[&#39;dest_file&#39;], (int)(($this->_config[&#39;quality&#39;]-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[&#39;logo_outline_size&#39;])? intval($img_w + $this->_config[&#39;logo_outline_size&#39;]) : $img_w + (int)($img_w/5);        $bg_h = isset($this->_config[&#39;logo_outline_size&#39;])? intval($img_h + $this->_config[&#39;logo_outline_size&#39;]) : $img_h + (int)($img_h/5);        // 创建底图对象
        $bg_img = imagecreatetruecolor($bg_w, $bg_h);        // 设置底图颜色
        $rgb = $this->hex2rgb($this->_config[&#39;logo_outline_color&#39;]);        $bgcolor = imagecolorallocate($bg_img, $rgb[&#39;r&#39;], $rgb[&#39;g&#39;], $rgb[&#39;b&#39;]);        // 填充底图颜色
        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[&#39;logo_radius&#39;];        // 执行圆角处理
        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;

    }    /** hex颜色转rgb颜色
     *  @param  String $color hex颜色
     *  @return Array
     */
    private function hex2rgb($hexcolor){
        $color = str_replace(&#39;#&#39;, &#39;&#39;, $hexcolor);        if (strlen($color) > 3) {            $rgb = array(                &#39;r&#39; => hexdec(substr($color, 0, 2)),                &#39;g&#39; => hexdec(substr($color, 2, 2)),                &#39;b&#39; => 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(                &#39;r&#39; => hexdec($r),                &#39;g&#39; => hexdec($g),                &#39;b&#39; => hexdec($b)
            );
        }        return $rgb;
    }    /** 获取图片类型 
     * @param  String $file 图片路径 
     * @return int 
     */  
    private function get_file_ext($file){
        $filename = basename($file);        list($name, $ext)= explode(&#39;.&#39;, $filename);        $ext_type = 0;        switch(strtolower($ext)){            case &#39;jpg&#39;:            case &#39;jpeg&#39;:                $ext_type = 2;                break;            case &#39;gif&#39;:                $ext_type = 1;                break;            case &#39;png&#39;:                $ext_type = 3;                break;
        }        return $ext_type;
    }

} // class end?>

demo.php

<?phprequire &#39;PHPQRCode.class.php&#39;;$config = array(        &#39;ecc&#39; => &#39;H&#39;,    // L-smallest, M, Q, H-best
        &#39;size&#39; => 12,    // 1-50
        &#39;dest_file&#39; => &#39;qrcode.png&#39;,        &#39;quality&#39; => 90,        &#39;logo&#39; => &#39;logo.jpg&#39;,        &#39;logo_size&#39; => 100,        &#39;logo_outline_size&#39; => 20,        &#39;logo_outline_color&#39; => &#39;#FFFF00&#39;,        &#39;logo_radius&#39; => 15,        &#39;logo_opacity&#39; => 100,
);// 二维码内容$data = &#39;http://weibo.com/fdipzone&#39;;// 创建二维码类$oPHPQRCode = new PHPQRCode();// 设定配置$oPHPQRCode->set_config($config);// 创建二维码$qrcode = $oPHPQRCode->generate($data);// 显示二维码echo &#39;<img src="&#39;.$qrcode.&#39;?t=&#39;.time().&#39;">&#39;;?>

 
生成的二维码图片:
这里写图片描述

本篇讲解了通过php 创建带logo二维码类,更多相关知识请关注php中文网。

相关推荐:

详解mysql重建表分区并保留数据的相关方法

关于php json_encode不支持对象私有属性的讲解

讲解PHP生成唯一RequestID类相关内容

Atas ialah kandungan terperinci 如何通过php 创建带logo二维码类. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Status Semasa PHP: Lihat trend pembangunan webStatus Semasa PHP: Lihat trend pembangunan webApr 13, 2025 am 12:20 AM

PHP tetap penting dalam pembangunan web moden, terutamanya dalam pengurusan kandungan dan platform e-dagang. 1) PHP mempunyai ekosistem yang kaya dan sokongan rangka kerja yang kuat, seperti Laravel dan Symfony. 2) Pengoptimuman prestasi boleh dicapai melalui OPCACHE dan NGINX. 3) Php8.0 memperkenalkan pengkompil JIT untuk meningkatkan prestasi. 4) Aplikasi awan asli dikerahkan melalui Docker dan Kubernet untuk meningkatkan fleksibiliti dan skalabiliti.

PHP vs Bahasa Lain: PerbandinganPHP vs Bahasa Lain: PerbandinganApr 13, 2025 am 12:19 AM

PHP sesuai untuk pembangunan web, terutamanya dalam pembangunan pesat dan memproses kandungan dinamik, tetapi tidak baik pada sains data dan aplikasi peringkat perusahaan. Berbanding dengan Python, PHP mempunyai lebih banyak kelebihan dalam pembangunan web, tetapi tidak sebaik python dalam bidang sains data; Berbanding dengan Java, PHP melakukan lebih buruk dalam aplikasi peringkat perusahaan, tetapi lebih fleksibel dalam pembangunan web; Berbanding dengan JavaScript, PHP lebih ringkas dalam pembangunan back-end, tetapi tidak sebaik JavaScript dalam pembangunan front-end.

PHP vs Python: Ciri dan Fungsi TerasPHP vs Python: Ciri dan Fungsi TerasApr 13, 2025 am 12:16 AM

PHP dan Python masing -masing mempunyai kelebihan sendiri dan sesuai untuk senario yang berbeza. 1.PHP sesuai untuk pembangunan web dan menyediakan pelayan web terbina dalam dan perpustakaan fungsi yang kaya. 2. Python sesuai untuk sains data dan pembelajaran mesin, dengan sintaks ringkas dan perpustakaan standard yang kuat. Apabila memilih, ia harus diputuskan berdasarkan keperluan projek.

PHP: Bahasa utama untuk pembangunan webPHP: Bahasa utama untuk pembangunan webApr 13, 2025 am 12:08 AM

PHP adalah bahasa skrip yang digunakan secara meluas di sisi pelayan, terutamanya sesuai untuk pembangunan web. 1.PHP boleh membenamkan HTML, memproses permintaan dan respons HTTP, dan menyokong pelbagai pangkalan data. 2.PHP digunakan untuk menjana kandungan web dinamik, data borang proses, pangkalan data akses, dan lain -lain, dengan sokongan komuniti yang kuat dan sumber sumber terbuka. 3. PHP adalah bahasa yang ditafsirkan, dan proses pelaksanaan termasuk analisis leksikal, analisis tatabahasa, penyusunan dan pelaksanaan. 4.Php boleh digabungkan dengan MySQL untuk aplikasi lanjutan seperti sistem pendaftaran pengguna. 5. Apabila debugging php, anda boleh menggunakan fungsi seperti error_reporting () dan var_dump (). 6. Mengoptimumkan kod PHP untuk menggunakan mekanisme caching, mengoptimumkan pertanyaan pangkalan data dan menggunakan fungsi terbina dalam. 7

PHP: asas banyak laman webPHP: asas banyak laman webApr 13, 2025 am 12:07 AM

Sebab mengapa PHP adalah timbunan teknologi pilihan untuk banyak laman web termasuk kemudahan penggunaannya, sokongan komuniti yang kuat, dan penggunaan yang meluas. 1) Mudah dipelajari dan digunakan, sesuai untuk pemula. 2) Mempunyai komuniti pemaju yang besar dan sumber yang kaya. 3) Digunakan secara meluas dalam platform WordPress, Drupal dan lain -lain. 4) Mengintegrasikan dengan ketat dengan pelayan web untuk memudahkan penggunaan pembangunan.

Di luar gembar -gembur: Menilai peranan PHP hari iniDi luar gembar -gembur: Menilai peranan PHP hari iniApr 12, 2025 am 12:17 AM

PHP kekal sebagai alat yang kuat dan digunakan secara meluas dalam pengaturcaraan moden, terutamanya dalam bidang pembangunan web. 1) PHP mudah digunakan dan diintegrasikan dengan lancar dengan pangkalan data, dan merupakan pilihan pertama bagi banyak pemaju. 2) Ia menyokong penjanaan kandungan dinamik dan pengaturcaraan berorientasikan objek, sesuai untuk membuat dan mengekalkan laman web dengan cepat. 3) Prestasi PHP dapat ditingkatkan dengan caching dan mengoptimumkan pertanyaan pangkalan data, dan komuniti yang luas dan ekosistem yang kaya menjadikannya masih penting dalam timbunan teknologi hari ini.

Apakah rujukan yang lemah dalam PHP dan bilakah mereka berguna?Apakah rujukan yang lemah dalam PHP dan bilakah mereka berguna?Apr 12, 2025 am 12:13 AM

Dalam PHP, rujukan lemah dilaksanakan melalui kelas lemah dan tidak akan menghalang pemungut sampah daripada menebus objek. Rujukan lemah sesuai untuk senario seperti sistem caching dan pendengar acara. Harus diingat bahawa ia tidak dapat menjamin kelangsungan hidup objek dan pengumpulan sampah mungkin ditangguhkan.

Terangkan kaedah sihir __invoke dalam PHP.Terangkan kaedah sihir __invoke dalam PHP.Apr 12, 2025 am 12:07 AM

Kaedah \ _ \ _ membolehkan objek dipanggil seperti fungsi. 1. Tentukan kaedah \ _ \ _ supaya objek boleh dipanggil. 2. Apabila menggunakan sintaks $ OBJ (...), PHP akan melaksanakan kaedah \ _ \ _ invoke. 3. Sesuai untuk senario seperti pembalakan dan kalkulator, meningkatkan fleksibiliti kod dan kebolehbacaan.

See all articles

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
3 minggu yang laluBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
3 minggu yang laluBy尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Cara Memperbaiki Audio Jika anda tidak dapat mendengar sesiapa
3 minggu yang laluBy尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Cara Membuka Segala -galanya Di Myrise
4 minggu yang laluBy尊渡假赌尊渡假赌尊渡假赌

Alat panas

Penyesuai Pelayan SAP NetWeaver untuk Eclipse

Penyesuai Pelayan SAP NetWeaver untuk Eclipse

Integrasikan Eclipse dengan pelayan aplikasi SAP NetWeaver.

DVWA

DVWA

Damn Vulnerable Web App (DVWA) ialah aplikasi web PHP/MySQL yang sangat terdedah. Matlamat utamanya adalah untuk menjadi bantuan bagi profesional keselamatan untuk menguji kemahiran dan alatan mereka dalam persekitaran undang-undang, untuk membantu pembangun web lebih memahami proses mengamankan aplikasi web, dan untuk membantu guru/pelajar mengajar/belajar dalam persekitaran bilik darjah Aplikasi web keselamatan. Matlamat DVWA adalah untuk mempraktikkan beberapa kelemahan web yang paling biasa melalui antara muka yang mudah dan mudah, dengan pelbagai tahap kesukaran. Sila ambil perhatian bahawa perisian ini

SublimeText3 versi Inggeris

SublimeText3 versi Inggeris

Disyorkan: Versi Win, menyokong gesaan kod!

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

Muat turun versi mac editor Atom

Muat turun versi mac editor Atom

Editor sumber terbuka yang paling popular