Golang将Logo嵌入二维码

in Go with 0 comment

续前篇文章

前篇文章讲述了Nginx反向代理Golang,并搭建了一个生成二维码的接口,今天突然看到支付宝的支付二维码中间都会有一个Logo,遂产生了将自己的Logo嵌入二维码的想法。在此使用了两个Golang的开源组件
skip2/go-qrcode
nfnt/resize

go get -u github.com/nfnt/resize
go get -u github.com/skip2/go-qrcode

为什么二维码中间被Logo遮挡住了还能正常识别?

这是因为二维码本身具有容错纠错机制,容错的原理是二维码在编码过程中进行了冗余,就像是123被编码成123123,这样只要扫描到一部分二维码图片,二维码内容还是可以被全部读到。
简单来说,不同等级的二维码拥有不同的容错率:

  1. L级别 7%的字码可被修正
  2. M级别 15%的字码可被修正
  3. Q级别 25%的字码可被修正
  4. H级别 30%的字码可被修正
    由此,我们将我们的Logo放入二维码中,只要二维码的级别容错足够,他是不会影响到我们扫码使用。

Golang程序逻辑

我们可以轻易的使用skip2/go-qrcode生成一个二维码,同样的Golang自带的image库也可以轻易地读取一个图片文件,那么我们只需要:

  1. 生成一个正常的二维码图片
  2. 读取我们的logo图片
  3. 生成一个画布,画布先放入第一步产生的二维码图片,再根据大小将我们的logo图片置入画布中央
    我的文件目录是:

main.go
qr/qr.go
imgs/mlogov2_rect.png

main.go

package main

import (
    "fmt"
    "image/jpeg"
    "net/http"
    "qrcode/qr"
)

func createQr(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    if r.Form["data"] == nil {
        fmt.Println("非法数据")
        return
    }

    uri := r.Form["data"][0]
    res, err := qr.GenerateQr(uri, 1)
    if err != nil {
        fmt.Fprint(w, err)
        return
    }

    w.Write(res)
}

func createQrWithLogo(w http.ResponseWriter, r *http.Request) {
    r.ParseForm()
    if r.Form["data"] == nil {
        fmt.Println("非法数据")
        return
    }

    uri := r.Form["data"][0]
    res, err := qr.GenerateQrWithLogo(uri, 1)
    if err != nil {
        fmt.Fprint(w, err)
        return
    }

    jpeg.Encode(w, res, nil)
}

func main() {
    http.HandleFunc("/generate_qrcode", createQr)
    http.HandleFunc("/generate_qrcode_with_logo", createQrWithLogo)

    err := http.ListenAndServe("127.0.0.1:8001", nil)
    if err != nil {
        fmt.Println(err)
    }
}

qr/qr.go

package qr

import (
    "errors"
    "image"
    "image/draw"
    "os"
    "strings"

    "github.com/nfnt/resize"
    "github.com/skip2/go-qrcode"
)

const (
    logo_file = "imgs/mlogov2_rect.png"
    logo_w    = 54
    logo_h    = 54
    qr_size   = 256
    qr_level  = qrcode.High
)

var v_url = [1]string{"gavinys.com"}

func GenerateQr(uri string, isValid int) ([]byte, error) {
    var (
        res []byte
        err error
    )

    if isValid == 1 && !validUrl(uri) {
        err = errors.New("not a valid uri")
        return res, err
    }

    png, err := qrcode.Encode(uri, qr_level, qr_size)
    if err != nil {
        return res, err
    }

    return png, nil
}

func GenerateQrWithLogo(uri string, isValid int) (image.Image, error) {
    var (
        res image.Image
        err error
        q   *qrcode.QRCode
    )

    // 校验uri
    if isValid == 1 && !validUrl(uri) {
        err = errors.New("not a valid uri")
        return res, err
    }

    // 先创建一个二维码
    q, err = qrcode.New(uri, qr_level)
    if err != nil {
        err = errors.New("can not create a qrcode")
        return res, err
    }
    png := q.Image(qr_size)
    bounds := png.Bounds()

    // 通过二维码创建一个空画布
    newImg := image.NewRGBA(bounds)

    // 读取logo文件
    file, err := os.Open(logo_file)
    if err != nil {
        return res, err
    }
    defer file.Close()

    // 将file转换成image
    logo, _, err := image.Decode(file)
    if err != nil {
        return res, err
    }

    // 按比例缩放logo
    logo = resize.Resize(logo_w, 0, logo, resize.Lanczos3)

    // 在画布上分别画上二维码,缩略后的logo
    draw.Draw(newImg, newImg.Bounds(), png, png.Bounds().Min, draw.Over)
    draw.Draw(newImg, image.Rect((qr_size/2)-(logo_w/2), (qr_size/2)-(logo_h/2), (qr_size/2)+(logo_w/2), (qr_size/2)+(logo_h/2)), logo, logo.Bounds().Min, draw.Over)

    return newImg, nil
}

func validUrl(uri string) bool {
    var (
        all_host string
        host     string
        isValid  int = 0
    )

    // 将uri的域名解析出来
    all_host = strings.Split(uri, "//")[1]
    host = strings.Split(all_host, "/")[0]

    // 校验是否符合有效域名
    for _, url := range v_url {
        idx := strings.Index(host, url)
        if idx != -1 {
            isValid = 1
            break
        }
    }

    if isValid == 1 {
        return true
    }

    return false
}

运行,访问:https://qrcode.gavinys.com/generate_qrcode_with_logo?data=https://www.gavinys.com得到一张带有你私人Logo的二维码啦~
1.png

Responses