Compare commits
56 commits
Author | SHA1 | Date | |
---|---|---|---|
669a328c35 | |||
31a767886d | |||
8b04542f19 | |||
0d18ee4d3f | |||
98c666bea2 | |||
d294d79360 | |||
b4b097a927 | |||
|
c34288c982 | ||
|
2f847d5947 | ||
|
fef853ee5e | ||
|
1d4172a01f | ||
|
6b08f978b6 | ||
|
9234bd71dd | ||
|
f6fd454518 | ||
|
20014aab1b | ||
|
6aa88d953f | ||
|
019b34f8f2 | ||
|
80cb7ffd68 | ||
|
02532e6f4d | ||
|
ba2a083ab6 | ||
|
a4223da22a | ||
|
53e75b199a | ||
|
cccc7a97ea | ||
|
87b02acaf1 | ||
|
ba5dfca752 | ||
|
1bc08e3651 | ||
|
3915a04f79 | ||
|
b69ec48f20 | ||
|
8d7ec3aacd | ||
|
ade1a224fe | ||
|
244d68b51a | ||
|
3db110f2af | ||
|
26e89c7d47 | ||
|
f1f6487f0d | ||
|
8eb90511f0 | ||
|
06183fda34 | ||
|
12fb7f5809 | ||
|
2065fa60ee | ||
|
9ad0ec237f | ||
|
6905bb1079 | ||
|
b6f150856e | ||
|
5af4d9ea05 | ||
|
24252cb4f8 | ||
|
b06ff17030 | ||
|
7909ea661c | ||
|
e6742d643f | ||
|
56e03474ba | ||
|
891b52d957 | ||
|
1b36f64f5d | ||
|
fc4a5d0dfc | ||
|
1284662a43 | ||
|
b204ba9578 | ||
|
d13db8c2b6 | ||
|
6a043360ff | ||
|
6cc73c97f7 | ||
|
8bb5ffd35d |
22 changed files with 184 additions and 5175 deletions
11
.github/ISSUE_TEMPLATE.md
vendored
11
.github/ISSUE_TEMPLATE.md
vendored
|
@ -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**
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -12,4 +12,3 @@
|
|||
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
.idea/workspace.xml
|
||||
|
|
9
.idea/captcha.iml
generated
9
.idea/captcha.iml
generated
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/captcha.iml" filepath="$PROJECT_DIR$/.idea/captcha.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
15
.travis.yml
15
.travis.yml
|
@ -1,15 +0,0 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
|
||||
script:
|
||||
- go test -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
2
LICENSE
2
LICENSE
|
@ -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
|
||||
|
|
25
README.md
25
README.md
|
@ -1,20 +1,18 @@
|
|||
> Package captcha provides an easy to use, unopinionated API for captcha generation
|
||||
> Package captcha provides an easy to use, unopinionated API for captcha generation.
|
||||
|
||||
<div>
|
||||
|
||||
[](https://godoc.org/github.com/steambap/captcha)
|
||||
[](https://travis-ci.org/steambap/captcha)
|
||||
[](https://codecov.io/gh/steambap/captcha)
|
||||
[](https://goreportcard.com/report/github.com/steambap/captcha)
|
||||
[](https://pkg.go.dev/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
|
||||

|
||||
|
||||

|
||||
|
||||
## 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
|
||||
see [contributing.md](contributing.md) for more detail.
|
||||
|
||||
## License
|
||||
[MIT](LICENSE.md)
|
||||
[MIT](LICENSE)
|
||||
|
|
136
captcha.go
136
captcha.go
|
@ -2,10 +2,7 @@
|
|||
package captcha
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/golang/freetype"
|
||||
"github.com/golang/freetype/truetype"
|
||||
"golang.org/x/image/font"
|
||||
_ "embed" // embed font
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
|
@ -17,11 +14,16 @@ import (
|
|||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/golang/freetype"
|
||||
"github.com/golang/freetype/truetype"
|
||||
"golang.org/x/image/font"
|
||||
)
|
||||
|
||||
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.
|
||||
|
@ -45,6 +47,12 @@ type Options struct {
|
|||
// FontScale controls the scale of font.
|
||||
// The default is 1.0.
|
||||
FontScale float64
|
||||
// Noise controls the number of noise drawn.
|
||||
// A noise dot is drawn for every 28 pixel by default.
|
||||
// The default is 1.0.
|
||||
Noise float64
|
||||
// Palette is the set of colors to chose from
|
||||
Palette color.Palette
|
||||
|
||||
width int
|
||||
height int
|
||||
|
@ -54,10 +62,12 @@ 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,
|
||||
Noise: 1.0,
|
||||
Palette: []color.Color{},
|
||||
width: width,
|
||||
height: height,
|
||||
}
|
||||
|
@ -68,9 +78,9 @@ type SetOption func(*Options)
|
|||
|
||||
// Data is the result of captcha generation.
|
||||
// It has a `Text` field and a private `img` field that will
|
||||
// be used in `WriteImage` receiver
|
||||
// be used in `WriteImage` receiver.
|
||||
type Data struct {
|
||||
// Text is captcha solution
|
||||
// Text is captcha solution.
|
||||
Text string
|
||||
|
||||
img *image.NRGBA
|
||||
|
@ -96,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.
|
||||
|
@ -107,16 +118,16 @@ 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.
|
||||
// It returns captcha data and any freetype drawing error encountered
|
||||
// It returns captcha data and any freetype drawing error encountered.
|
||||
func New(width int, height int, option ...SetOption) (*Data, error) {
|
||||
options := newDefaultOption(width, height)
|
||||
for _, setOption := range option {
|
||||
|
@ -125,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
|
||||
}
|
||||
|
||||
|
@ -137,7 +144,7 @@ func New(width int, height int, option ...SetOption) (*Data, error) {
|
|||
}
|
||||
|
||||
// NewMathExpr creates a new captcha.
|
||||
// It will generate a image with a math expression like `1 + 2`
|
||||
// It will generate a image with a math expression like `1 + 2`.
|
||||
func NewMathExpr(width int, height int, option ...SetOption) (*Data, error) {
|
||||
options := newDefaultOption(width, height)
|
||||
for _, setOption := range option {
|
||||
|
@ -146,39 +153,60 @@ 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
|
||||
}
|
||||
|
||||
func drawNoise(img *image.NRGBA, opts *Options) {
|
||||
noiseCount := (opts.width * opts.height) / 28
|
||||
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(255)
|
||||
green := rng.Intn(255)
|
||||
blue := rng.Intn(255)
|
||||
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)}
|
||||
}
|
||||
|
@ -196,18 +224,17 @@ 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()
|
||||
flip := rng.Intn(2) == 0
|
||||
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 flip {
|
||||
if rand.Intn(2) == 0 {
|
||||
yFlip = -1.0
|
||||
}
|
||||
curveColor := randomInvertColor(opts.BackgroundColor)
|
||||
curveColor := randomColorFromOptions(opts)
|
||||
|
||||
for x1 := xStart; x1 <= xEnd; x1++ {
|
||||
y := math.Sin(math.Pi*angle*float64(x1)/float64(opts.width)) * curveHeight * yFlip
|
||||
|
@ -224,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(randomInvertColor(opts.BackgroundColor)))
|
||||
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
|
||||
|
@ -242,18 +269,27 @@ func drawText(text string, img *image.NRGBA, opts *Options) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func randomColorFromOptions(opts *Options) color.Color {
|
||||
length := len(opts.Palette)
|
||||
if length == 0 {
|
||||
return randomInvertColor(opts.BackgroundColor)
|
||||
}
|
||||
|
||||
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: uint8(255)}
|
||||
return hsva{h: hue, s: saturation, v: value, a: 255}
|
||||
}
|
||||
|
||||
func getLightness(colour color.Color) float64 {
|
||||
|
@ -294,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)
|
||||
|
||||
|
|
|
@ -3,13 +3,15 @@ package captcha
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"golang.org/x/image/font/gofont/goregular"
|
||||
"image/color"
|
||||
"image/color/palette"
|
||||
"image/gif"
|
||||
"image/jpeg"
|
||||
"math/rand"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/image/font/gofont/goregular"
|
||||
)
|
||||
|
||||
func TestNewCaptcha(t *testing.T) {
|
||||
|
@ -61,11 +63,18 @@ func TestNewCaptchaOptions(t *testing.T) {
|
|||
options.CharPreset = "1234567890"
|
||||
options.CurveNumber = 0
|
||||
options.TextLength = 6
|
||||
options.Palette = palette.WebSafe
|
||||
})
|
||||
|
||||
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) {
|
||||
|
@ -75,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
|
||||
|
||||
|
@ -89,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
|
||||
}
|
||||
|
||||
|
@ -98,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")
|
||||
|
|
|
@ -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
7
example/basic/go.mod
Normal 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
5
example/basic/go.sum
Normal 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=
|
|
@ -2,9 +2,10 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/steambap/captcha"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/steambap/captcha"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
10
example/load-font/go.mod
Normal file
10
example/load-font/go.mod
Normal 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
5
example/load-font/go.sum
Normal 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=
|
|
@ -2,10 +2,11 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/steambap/captcha"
|
||||
"golang.org/x/image/font/gofont/goregular"
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/steambap/captcha"
|
||||
"golang.org/x/image/font/gofont/goregular"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
40
fonts/gen.go
40
fonts/gen.go
|
@ -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
8
go.mod
Normal 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
4
go.sum
Normal 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=
|
10
history.md
10
history.md
|
@ -1,3 +1,13 @@
|
|||
1.3.0 / 2018-11-7
|
||||
===================
|
||||
|
||||
* Add Palette option
|
||||
|
||||
1.2.0 / 2017-12-26
|
||||
===================
|
||||
|
||||
* Add Noise option
|
||||
|
||||
1.1.0 / 2017-11-16
|
||||
===================
|
||||
|
||||
|
|
Reference in a new issue