Compare commits

...

46 commits
v1.3.0 ... main

Author SHA1 Message Date
669a328c35 Moved to concord 2024-07-10 16:49:19 +01:00
31a767886d Update captcha.go 2024-05-02 00:29:35 +01:00
8b04542f19 Update README.md 2024-05-02 00:25:23 +01:00
0d18ee4d3f Update README.md 2024-05-02 00:24:46 +01:00
98c666bea2 Delete .github/workflows/go.yml 2024-05-02 00:23:57 +01:00
d294d79360 Update example/basic/go.mod 2024-05-02 00:23:41 +01:00
b4b097a927 Update go.mod 2024-05-02 00:22:56 +01:00
Weilin Shi
c34288c982 update deps 2024-01-13 09:53:15 +08:00
Weilin Shi
2f847d5947 upgrade x/image 2023-07-26 14:04:50 +08:00
dependabot[bot]
fef853ee5e
build(deps): bump golang.org/x/image from 0.1.0 to 0.5.0 (#10)
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.1.0 to 0.5.0.
- [Release notes](https://github.com/golang/image/releases)
- [Commits](https://github.com/golang/image/compare/v0.1.0...v0.5.0)

---
updated-dependencies:
- dependency-name: golang.org/x/image
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-09 17:02:21 +08:00
Zhiger
1d4172a01f
fix: wrong randomText when unicode preset (#8) 2023-02-16 21:06:39 +08:00
Weilin Shi
6b08f978b6 update go.mod and license 2023-01-01 08:34:24 +08:00
Weilin Shi
9234bd71dd update deps 2022-12-05 16:17:24 +08:00
Weilin Shi
f6fd454518 fix #7 2022-06-27 13:00:35 +08:00
Weilin Shi
20014aab1b new custom generator api 2022-06-10 12:16:34 +08:00
Weilin Shi
6aa88d953f test go 1.18 2022-03-22 15:23:39 +08:00
Weilin Shi
019b34f8f2 update deps and license 2022-01-06 20:44:35 +08:00
Weilin Shi
80cb7ffd68 update readme 2021-09-28 16:44:12 +08:00
Weilin Shi
02532e6f4d fix old code style 2021-08-25 15:21:16 +08:00
Weilin Shi
ba2a083ab6 update code of conduct 2021-08-24 21:15:10 +08:00
Weilin Shi
a4223da22a fix typo and lint 2021-07-28 21:26:53 +08:00
Weilin Shi
53e75b199a upgrade to use embed from Go 1.16 2021-07-28 21:22:23 +08:00
Weilin Shi
cccc7a97ea remove used section in readme 2021-06-22 14:14:22 +08:00
Weilin Shi
87b02acaf1 update compatibility notes 2021-06-22 14:13:41 +08:00
Weilin Shi
ba5dfca752 go mod tidy 2021-06-15 21:23:55 +08:00
Weilin Shi
1bc08e3651 happy new year 2021-01-01 09:50:41 +08:00
Weilin Shi
3915a04f79 go tide module 2020-10-13 16:39:37 +08:00
Weilin Shi
b69ec48f20 fix pkg go dev link 2020-09-22 11:04:26 +08:00
Weilin Shi
8d7ec3aacd use pkg go dev 2020-09-22 10:58:43 +08:00
Weilin Shi
ade1a224fe use main branch 2020-09-22 10:51:17 +08:00
Weilin Shi
244d68b51a use v2 actions 2020-09-10 11:36:21 +08:00
Takuya Kaneda
3db110f2af
Make New() concurrency-safe (#6) 2020-07-19 21:25:17 +08:00
Weilin Shi
26e89c7d47 update deps 2020-07-08 14:09:46 +08:00
Weilin Shi
f1f6487f0d shorten test func name 2020-02-18 15:13:05 +08:00
Weilin Shi
8eb90511f0 Fix link to license in readme 2020-01-13 15:18:25 +08:00
Weilin Shi
06183fda34 Update license year 2020-01-06 16:25:31 +08:00
Weilin Shi
12fb7f5809 Update deps 2019-11-19 17:16:45 +08:00
Weilin Shi
2065fa60ee remove travis and add GitHub workflow 2019-10-24 15:56:39 +08:00
Weilin Shi
9ad0ec237f
Add GitHub workflow 2019-10-24 15:38:14 +08:00
Weilin Shi
6905bb1079 Add Golang v1.13 test in travis config 2019-09-05 16:51:23 +08:00
Weilin Shi
b6f150856e Clean up 2019-07-04 14:15:45 +08:00
Weilin Shi
5af4d9ea05
Merge pull request #3 from s3rj1k/go.mod
add go.mod support
2019-05-13 12:14:48 +08:00
s3rj1k
24252cb4f8 add go.mod support
Signed-off-by: s3rj1k <evasive.gyron@gmail.com>
2019-05-10 14:14:40 +03:00
Weilin Shi
b06ff17030 update license and add anti-996 license 2019-04-19 08:51:01 +08:00
Weilin Shi
7909ea661c Add go 1.12 to travis config 2019-02-28 10:37:10 +08:00
Weilin Shi
e6742d643f Add golang 1.11 to travis config 2019-02-03 12:00:59 +08:00
15 changed files with 135 additions and 5135 deletions

View file

@ -1,11 +0,0 @@
**Do you want to request a *feature* or report a *bug*?**
**If this is a feature request, what is motivation for changing the behavior?**
**If it is a bug, which version of captcha are you using?**
**What is the current behavior?**
**What is the expected behavior?**
**Step to reproduce the bug or other relevant information**

View file

@ -1,16 +0,0 @@
language: go
go:
- 1.8.x
- 1.9.x
- 1.10.x
script:
- go test -race -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)
notifications:
email:
on_success: never

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017 - Present Weilin Shi
Copyright (c) 2017 - 2024 Weilin Shi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -2,19 +2,17 @@
<div>
[![GoDoc](https://godoc.org/github.com/steambap/captcha?status.svg)](https://godoc.org/github.com/steambap/captcha)
[![Build Status](https://travis-ci.org/steambap/captcha.svg)](https://travis-ci.org/steambap/captcha)
[![codecov](https://codecov.io/gh/steambap/captcha/branch/master/graph/badge.svg)](https://codecov.io/gh/steambap/captcha)
[![Go Report Card](https://goreportcard.com/badge/github.com/steambap/captcha)](https://goreportcard.com/report/github.com/steambap/captcha)
[![PkgGoDev](https://pkg.go.dev/badge/concord.hectabit.org/HectaBit/captcha)](https://pkg.go.dev/concord.hectabit.org/HectaBit/captcha)
[![Go Report Card](https://goreportcard.com/badge/concord.hectabit.org/HectaBit/captcha)](https://goreportcard.com/report/concord.hectabit.org/HectaBit/captcha)
</div>
## Why another captcha generator?
I want a simple and framework-independent way to generate captcha. It also should be flexible, at least allow me to pick my favorite font.
Because I can.
## install
```
go get github.com/steambap/captcha
go get concord.hectabit.org/HectaBit/captcha
```
## usage
@ -32,17 +30,22 @@ func handle(w http.ResponseWriter, r *http.Request) {
```
[documentation](https://godoc.org/github.com/steambap/captcha) |
[example](example/basic/main.go)
[documentation](https://pkg.go.dev/concord.hectabit.org/HectaBit/captcha) |
[example](example/basic/main.go) |
[font example](example/load-font/main.go)
## sample image
![image](example/captcha.png)
![image](example/captcha-math.png)
## Compatibility
This package uses embed package from Go 1.16. If for some reasons you have to use pre 1.16 version of Go, reference pre 1.4 version of this module in your go.mod.
## Contributing
If your found a bug, please contribute!
see [contributing.md](contributing.md) for more detail.
## License
[MIT](LICENSE.md)
[MIT](LICENSE)

View file

@ -2,7 +2,7 @@
package captcha
import (
"bytes"
_ "embed" // embed font
"image"
"image/color"
"image/draw"
@ -22,7 +22,8 @@ import (
const charPreset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
var rng = rand.New(rand.NewSource(time.Now().UnixNano()))
//go:embed fonts/Comismsh.ttf
var ttf []byte
var ttfFont *truetype.Font
// Options manage captcha generation details.
@ -61,7 +62,7 @@ func newDefaultOption(width, height int) *Options {
return &Options{
BackgroundColor: color.Transparent,
CharPreset: charPreset,
TextLength: 4,
TextLength: 6,
CurveNumber: 2,
FontDPI: 72.0,
FontScale: 1.0,
@ -105,6 +106,7 @@ func (data *Data) WriteGIF(w io.Writer, o *gif.Options) error {
func init() {
ttfFont, _ = freetype.ParseFont(ttf)
rand.Seed(time.Now().UnixNano())
}
// LoadFont let you load an external font.
@ -116,12 +118,12 @@ func LoadFont(fontData []byte) error {
// LoadFontFromReader load an external font from an io.Reader interface.
func LoadFontFromReader(reader io.Reader) error {
var buf bytes.Buffer
if _, err := io.Copy(&buf, reader); err != nil {
b, err := io.ReadAll(reader)
if err != nil {
return err
}
return LoadFont(buf.Bytes())
return LoadFont(b)
}
// New creates a new captcha.
@ -134,11 +136,7 @@ func New(width int, height int, option ...SetOption) (*Data, error) {
text := randomText(options)
img := image.NewNRGBA(image.Rect(0, 0, width, height))
draw.Draw(img, img.Bounds(), &image.Uniform{options.BackgroundColor}, image.ZP, draw.Src)
drawNoise(img, options)
drawCurves(img, options)
err := drawText(text, img, options)
if err != nil {
if err := drawWithOption(text, img, options); err != nil {
return nil, err
}
@ -155,21 +153,42 @@ func NewMathExpr(width int, height int, option ...SetOption) (*Data, error) {
text, equation := randomEquation()
img := image.NewNRGBA(image.Rect(0, 0, width, height))
draw.Draw(img, img.Bounds(), &image.Uniform{options.BackgroundColor}, image.ZP, draw.Src)
drawNoise(img, options)
drawCurves(img, options)
err := drawText(equation, img, options)
if err != nil {
if err := drawWithOption(equation, img, options); err != nil {
return nil, err
}
return &Data{Text: text, img: img}, nil
}
// NewCustomGenerator creates a new captcha based on a custom text generator.
func NewCustomGenerator(
width int, height int, generator func() (anwser string, question string), option ...SetOption,
) (*Data, error) {
options := newDefaultOption(width, height)
for _, setOption := range option {
setOption(options)
}
answer, question := generator()
img := image.NewNRGBA(image.Rect(0, 0, width, height))
if err := drawWithOption(question, img, options); err != nil {
return nil, err
}
return &Data{Text: answer, img: img}, nil
}
func drawWithOption(text string, img *image.NRGBA, options *Options) error {
draw.Draw(img, img.Bounds(), &image.Uniform{options.BackgroundColor}, image.Point{}, draw.Src)
drawNoise(img, options)
drawCurves(img, options)
return drawText(text, img, options)
}
func randomText(opts *Options) (text string) {
n := len(opts.CharPreset)
n := len([]rune(opts.CharPreset))
for i := 0; i < opts.TextLength; i++ {
text += string(opts.CharPreset[rng.Intn(n)])
text += string([]rune(opts.CharPreset)[rand.Intn(n)])
}
return text
@ -178,16 +197,16 @@ func randomText(opts *Options) (text string) {
func drawNoise(img *image.NRGBA, opts *Options) {
noiseCount := (opts.width * opts.height) / int(28.0/opts.Noise)
for i := 0; i < noiseCount; i++ {
x := rng.Intn(opts.width)
y := rng.Intn(opts.height)
x := rand.Intn(opts.width)
y := rand.Intn(opts.height)
img.Set(x, y, randomColor())
}
}
func randomColor() color.RGBA {
red := rng.Intn(256)
green := rng.Intn(256)
blue := rng.Intn(256)
red := rand.Intn(256)
green := rand.Intn(256)
blue := rand.Intn(256)
return color.RGBA{R: uint8(red), G: uint8(green), B: uint8(blue), A: uint8(255)}
}
@ -205,14 +224,14 @@ func drawSineCurve(img *image.NRGBA, opts *Options) {
if opts.width <= 40 {
xStart, xEnd = 1, opts.width-1
} else {
xStart = rng.Intn(opts.width/10) + 1
xEnd = opts.width - rng.Intn(opts.width/10) - 1
xStart = rand.Intn(opts.width/10) + 1
xEnd = opts.width - rand.Intn(opts.width/10) - 1
}
curveHeight := float64(rng.Intn(opts.height/6) + opts.height/6)
yStart := rng.Intn(opts.height*2/3) + opts.height/6
angle := 1.0 + rng.Float64()
curveHeight := float64(rand.Intn(opts.height/6) + opts.height/6)
yStart := rand.Intn(opts.height*2/3) + opts.height/6
angle := 1.0 + rand.Float64()
yFlip := 1.0
if rng.Intn(2) == 0 {
if rand.Intn(2) == 0 {
yFlip = -1.0
}
curveColor := randomColorFromOptions(opts)
@ -232,15 +251,15 @@ func drawText(text string, img *image.NRGBA, opts *Options) error {
ctx.SetFont(ttfFont)
fontSpacing := opts.width / len(text)
fontOffset := rng.Intn(fontSpacing / 2)
fontOffset := rand.Intn(fontSpacing / 2)
for idx, char := range text {
fontScale := 0.8 + rng.Float64()*0.4
fontScale := 0.8 + rand.Float64()*0.4
fontSize := float64(opts.height) / fontScale * opts.FontScale
ctx.SetFontSize(fontSize)
ctx.SetSrc(image.NewUniform(randomColorFromOptions(opts)))
x := fontSpacing*idx + fontOffset
y := opts.height/6 + rng.Intn(opts.height/3) + int(fontSize/2)
y := opts.height/6 + rand.Intn(opts.height/3) + int(fontSize/2)
pt := freetype.Pt(x, y)
if _, err := ctx.DrawString(string(char), pt); err != nil {
return err
@ -256,19 +275,19 @@ func randomColorFromOptions(opts *Options) color.Color {
return randomInvertColor(opts.BackgroundColor)
}
return opts.Palette[rng.Intn(length)]
return opts.Palette[rand.Intn(length)]
}
func randomInvertColor(base color.Color) color.Color {
baseLightness := getLightness(base)
var value float64
if baseLightness >= 0.5 {
value = baseLightness - 0.3 - rng.Float64()*0.2
value = baseLightness - 0.3 - rand.Float64()*0.2
} else {
value = baseLightness + 0.3 + rng.Float64()*0.2
value = baseLightness + 0.3 + rand.Float64()*0.2
}
hue := float64(rng.Intn(361)) / 360
saturation := 0.6 + rng.Float64()*0.2
hue := float64(rand.Intn(361)) / 360
saturation := 0.6 + rand.Float64()*0.2
return hsva{h: hue, s: saturation, v: value, a: 255}
}
@ -311,8 +330,8 @@ func minColor(numList ...uint32) (min uint32) {
}
func randomEquation() (text string, equation string) {
left := 1 + rng.Intn(9)
right := 1 + rng.Intn(9)
left := 1 + rand.Intn(9)
right := 1 + rand.Intn(9)
text = strconv.Itoa(left + right)
equation = strconv.Itoa(left) + "+" + strconv.Itoa(right)

View file

@ -69,6 +69,12 @@ func TestNewCaptchaOptions(t *testing.T) {
NewMathExpr(100, 34, func(options *Options) {
options.BackgroundColor = color.Black
})
NewCustomGenerator(100, 34, func() (anwser string, question string) {
return "4", "2x2?"
}, func(o *Options) {
o.BackgroundColor = color.Black
})
}
func TestNewMathExpr(t *testing.T) {
@ -78,7 +84,16 @@ func TestNewMathExpr(t *testing.T) {
}
}
func TestCovNilFontError(t *testing.T) {
func TestNewCustomGenerator(t *testing.T) {
_, err := NewCustomGenerator(150, 50, func() (anwser string, question string) {
return "1", "2"
})
if err != nil {
t.Fatal(err)
}
}
func TestNilFontError(t *testing.T) {
temp := ttfFont
ttfFont = nil
@ -92,6 +107,13 @@ func TestCovNilFontError(t *testing.T) {
t.Fatal("Expect to get nil font error")
}
_, err = NewCustomGenerator(150, 50, func() (anwser string, question string) {
return "1", "2"
})
if err == nil {
t.Fatal("Expect to get nil font error")
}
ttfFont = temp
}
@ -101,7 +123,7 @@ func (errReader) Read(_ []byte) (int, error) {
return 0, errors.New("")
}
func TestCovReaderErr(t *testing.T) {
func TestReaderErr(t *testing.T) {
err := LoadFontFromReader(errReader{})
if err == nil {
t.Fatal("Expect to get io.Reader error")

View file

@ -1 +1 @@
[https://contributor-covenant.org/version/1/4/](https://contributor-covenant.org/version/1/4/)
[https://www.contributor-covenant.org/version/2/1/code_of_conduct/](https://www.contributor-covenant.org/version/2/1/code_of_conduct/)

7
example/basic/go.mod Normal file
View file

@ -0,0 +1,7 @@
module concord.hectabit.org/HectaBit/captcha/example/basic
go 1.12
replace concord.hectabit.org/HectaBit/captcha => ../../
require concord.hectabit.org/HectaBit/captcha v0.0.0-00010101000000-000000000000

5
example/basic/go.sum Normal file
View file

@ -0,0 +1,5 @@
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec h1:arXJwtMuk5vqI1NHX0UTnNw977rYk5Sl4jQqHj+hun4=
golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

10
example/load-font/go.mod Normal file
View file

@ -0,0 +1,10 @@
module github.com/steambap/captcha/example/load-font
go 1.12
replace github.com/steambap/captcha => ../../
require (
github.com/steambap/captcha v0.0.0-00010101000000-000000000000
golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec
)

5
example/load-font/go.sum Normal file
View file

@ -0,0 +1,5 @@
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec h1:arXJwtMuk5vqI1NHX0UTnNw977rYk5Sl4jQqHj+hun4=
golang.org/x/image v0.0.0-20190507092727-e4e5bf290fec/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

5016
font.go

File diff suppressed because it is too large Load diff

View file

@ -1,40 +0,0 @@
package main
import (
"bytes"
"fmt"
"go/format"
"io/ioutil"
"log"
"path/filepath"
)
// This program generates a go file for Comismsh font
func main() {
src, err := ioutil.ReadFile("Comismsh.ttf")
if err != nil {
log.Fatal(err)
}
buf := new(bytes.Buffer)
fmt.Fprint(buf, "// DO NOT EDIT. This file is generated.\n\n")
fmt.Fprint(buf, "package captcha\n\n")
fmt.Fprint(buf, "// The following is Comismsh TrueType font data.\n")
fmt.Fprint(buf, "var ttf = []byte{")
for i, x := range src {
if i&15 == 0 {
buf.WriteByte('\n')
}
fmt.Fprintf(buf, "%#02x,", x)
}
fmt.Fprint(buf, "\n}\n")
dst, err := format.Source(buf.Bytes())
if err != nil {
log.Fatal(err)
}
if err := ioutil.WriteFile(filepath.Join("../", "font.go"), dst, 0666); err != nil {
log.Fatal(err)
}
}

8
go.mod Normal file
View file

@ -0,0 +1,8 @@
module concord.hectabit.org/HectaBit/captcha
go 1.20
require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
golang.org/x/image v0.15.0
)

4
go.sum Normal file
View file

@ -0,0 +1,4 @@
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=