├── example ├── qr.png └── logo.png ├── go.mod ├── go.sum ├── LICENSE ├── cmd └── qrlogo │ └── main.go ├── README.md └── qrlogo.go /example/qr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/divan/qrlogo/HEAD/example/qr.png -------------------------------------------------------------------------------- /example/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/divan/qrlogo/HEAD/example/logo.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/divan/qrlogo 2 | 3 | go 1.14 4 | 5 | require github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086 h1:RYiqpb2ii2Z6J4x0wxK46kvPBbFuZcdhS+CIztmYgZs= 2 | github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo= 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ivan Daniluk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cmd/qrlogo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/divan/qrlogo" 7 | "image" 8 | _ "image/png" 9 | "os" 10 | ) 11 | 12 | var ( 13 | input = flag.String("i", "logo.png", "Logo to be placed over QR code") 14 | output = flag.String("o", "qr.png", "Output filename") 15 | size = flag.Int("size", 512, "Image size in pixels") 16 | ) 17 | 18 | func main() { 19 | flag.Usage = Usage 20 | flag.Parse() 21 | 22 | if flag.NArg() != 1 { 23 | flag.Usage() 24 | os.Exit(1) 25 | } 26 | 27 | text := flag.Arg(0) 28 | 29 | file, err := os.Open(*input) 30 | errcheck(err, "Failed to open logo:") 31 | defer file.Close() 32 | 33 | logo, _, err := image.Decode(file) 34 | errcheck(err, "Failed to decode PNG with logo:") 35 | 36 | qr, err := qrlogo.Encode(text, logo, *size) 37 | errcheck(err, "Failed to encode QR:") 38 | 39 | out, err := os.Create(*output) 40 | errcheck(err, "Failed to open output file:") 41 | out.Write(qr.Bytes()) 42 | out.Close() 43 | 44 | fmt.Println("Done! Written QR image to", *output) 45 | } 46 | 47 | // Usage overloads flag.Usage. 48 | func Usage() { 49 | fmt.Fprintln(os.Stderr, "Usage: qrlogo [options] text") 50 | flag.PrintDefaults() 51 | } 52 | 53 | func errcheck(err error, str string) { 54 | if err != nil { 55 | fmt.Println(str, err) 56 | os.Exit(1) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QRLogo 2 | [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/divan/qrlogo) 3 | 4 | 5 | ## Intro 6 | QRLogo is a small library and command line to generate QR images with small logo placed in the center. 7 | 8 | Thanks to redundancy in the QE codes nature, code remains readable even if part of its covered by image, i.e. logo. It allows to create nice branded-like QR codes. 9 | 10 | ![Demo](example/qr.png) 11 | 12 | ## Install 13 | To install command line tool: 14 | 15 | ```go get github.com/divan/qrlogo/cmd/qrlogo``` 16 | 17 | Or only lib (basically, the same, but without installing binary): 18 | 19 | ```go get github.com/divan/qrlogo``` 20 | 21 | ## Usage 22 | ### Command line tool 23 | For command line tool, it's enough to specify input image (i.e. logo) and text or URL to be encoded: 24 | 25 | ```qrlogo -i logo.png http://githib.com/divan/qrlogo``` 26 | 27 | Output will be written to qr.png file in PNG format. 28 | 29 | You can also specify output filename and image size (512px by default). See `-help` output for details. 30 | 31 | Note, that resizing logo image is up to you, it will be placed on top of resulting QR code without resizing or guessing proportions. 32 | 33 | ### Library 34 | For the library, usually it's enough to call only `qrlogo.Encode()` function, but if you need more precise controls, create new `qrlogo.Encoder` and set the values you need. See [Documentation](https://godoc.org/github.com/divan/qrlogo) for more details. 35 | 36 | ## License 37 | MIT 38 | 39 | -------------------------------------------------------------------------------- /qrlogo.go: -------------------------------------------------------------------------------- 1 | package qrlogo 2 | 3 | import ( 4 | "bytes" 5 | "image" 6 | "image/color" 7 | "image/png" 8 | 9 | qr "github.com/skip2/go-qrcode" 10 | ) 11 | 12 | // Encoder defines settings for QR/Overlay encoder. 13 | type Encoder struct { 14 | AlphaThreshold int 15 | GreyThreshold int 16 | QRLevel qr.RecoveryLevel 17 | } 18 | 19 | // DefaultEncoder is the encoder with default settings. 20 | var DefaultEncoder = Encoder{ 21 | AlphaThreshold: 2000, // FIXME: don't remember where this came from 22 | GreyThreshold: 30, // in percent 23 | QRLevel: qr.Highest, // recommended, as logo steals some redundant space 24 | } 25 | 26 | // Encode encodes QR image, adds logo overlay and renders result as PNG. 27 | func Encode(str string, logo image.Image, size int) (*bytes.Buffer, error) { 28 | return DefaultEncoder.Encode(str, logo, size) 29 | } 30 | 31 | // Encode encodes QR image, adds logo overlay and renders result as PNG. 32 | func (e Encoder) Encode(str string, logo image.Image, size int) (*bytes.Buffer, error) { 33 | var buf bytes.Buffer 34 | 35 | code, err := qr.New(str, e.QRLevel) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | img := code.Image(size) 41 | e.overlayLogo(img, logo) 42 | 43 | err = png.Encode(&buf, img) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | return &buf, nil 49 | } 50 | 51 | // overlayLogo blends logo to the center of the QR code, 52 | // changing all colors to black. 53 | func (e Encoder) overlayLogo(dst, src image.Image) { 54 | grey := uint32(^uint16(0)) * uint32(e.GreyThreshold) / 100 55 | alphaOffset := uint32(e.AlphaThreshold) 56 | offset := dst.Bounds().Max.X/2 - src.Bounds().Max.X/2 57 | for x := 0; x < src.Bounds().Max.X; x++ { 58 | for y := 0; y < src.Bounds().Max.Y; y++ { 59 | if r, g, b, alpha := src.At(x, y).RGBA(); alpha > alphaOffset { 60 | col := color.Black 61 | if r > grey && g > grey && b > grey { 62 | col = color.White 63 | } 64 | dst.(*image.Paletted).Set(x+offset, y+offset, col) 65 | } 66 | } 67 | } 68 | } 69 | --------------------------------------------------------------------------------