Compare commits

..

No commits in common. "main" and "v1.0.0" have entirely different histories.
main ... v1.0.0

22 changed files with 5174 additions and 247 deletions

11
.github/ISSUE_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,11 @@
**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
View file

@ -12,3 +12,4 @@
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.glide/ .glide/
.idea/workspace.xml

9
.idea/captcha.iml generated Normal file
View file

@ -0,0 +1,9 @@
<?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 Normal file
View file

@ -0,0 +1,8 @@
<?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 Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

13
.travis.yml Normal file
View file

@ -0,0 +1,13 @@
language: go
go:
- 1.9
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 MIT License
Copyright (c) 2017 - 2024 Weilin Shi Copyright (c) 2017 - Present Weilin Shi
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -1,18 +1,20 @@
> 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> <div>
[![PkgGoDev](https://pkg.go.dev/badge/concord.hectabit.org/HectaBit/captcha)](https://pkg.go.dev/concord.hectabit.org/HectaBit/captcha) [![GoDoc](https://godoc.org/github.com/steambap/captcha?status.svg)](https://godoc.org/github.com/steambap/captcha)
[![Go Report Card](https://goreportcard.com/badge/concord.hectabit.org/HectaBit/captcha)](https://goreportcard.com/report/concord.hectabit.org/HectaBit/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)
</div> </div>
## Why another captcha generator? ## Why another captcha generator?
Because I can. 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.
## install ## install
``` ```
go get concord.hectabit.org/HectaBit/captcha go get github.com/steambap/captcha
``` ```
## usage ## usage
@ -30,22 +32,17 @@ func handle(w http.ResponseWriter, r *http.Request) {
``` ```
[documentation](https://pkg.go.dev/concord.hectabit.org/HectaBit/captcha) | [documentation](https://godoc.org/github.com/steambap/captcha) |
[example](example/basic/main.go) | [example](example/basic/main.go)
[font example](example/load-font/main.go)
## sample image ## sample image
![image](example/captcha.png) ![image](example/captcha.png)
![image](example/captcha-math.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 ## Contributing
If your found a bug, please contribute! If your found a bug, please contribute!
see [contributing.md](contributing.md) for more detail. see [contributing.md](contributing.md) for more detail
## License ## License
[MIT](LICENSE) [MIT](LICENSE.md)

View file

@ -2,28 +2,24 @@
package captcha package captcha
import ( import (
_ "embed" // embed font "bytes"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"image" "image"
"image/color" "image/color"
"image/draw" "image/draw"
"image/gif"
"image/jpeg"
"image/png" "image/png"
"io" "io"
"math" "math"
"math/rand" "math/rand"
"strconv" "strconv"
"time" "time"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
) )
const charPreset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" const charPreset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
//go:embed fonts/Comismsh.ttf var rng = rand.New(rand.NewSource(time.Now().UnixNano()))
var ttf []byte
var ttfFont *truetype.Font var ttfFont *truetype.Font
// Options manage captcha generation details. // Options manage captcha generation details.
@ -47,12 +43,6 @@ type Options struct {
// FontScale controls the scale of font. // FontScale controls the scale of font.
// The default is 1.0. // The default is 1.0.
FontScale float64 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 width int
height int height int
@ -62,12 +52,10 @@ func newDefaultOption(width, height int) *Options {
return &Options{ return &Options{
BackgroundColor: color.Transparent, BackgroundColor: color.Transparent,
CharPreset: charPreset, CharPreset: charPreset,
TextLength: 6, TextLength: 4,
CurveNumber: 2, CurveNumber: 2,
FontDPI: 72.0, FontDPI: 72.0,
FontScale: 1.0, FontScale: 1.0,
Noise: 1.0,
Palette: []color.Color{},
width: width, width: width,
height: height, height: height,
} }
@ -78,35 +66,22 @@ type SetOption func(*Options)
// Data is the result of captcha generation. // Data is the result of captcha generation.
// It has a `Text` field and a private `img` field that will // 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 { type Data struct {
// Text is captcha solution. // Text is captcha solution
Text string Text string
img *image.NRGBA img *image.NRGBA
} }
// WriteImage encodes image data and writes to an io.Writer. // WriteImage encodes image data and writes to an io.Writer.
// It returns possible error from PNG encoding. // It returns possible error from PNG encoding
func (data *Data) WriteImage(w io.Writer) error { func (data *Data) WriteImage(w io.Writer) error {
return png.Encode(w, data.img) return png.Encode(w, data.img)
} }
// WriteJPG encodes image data in JPEG format and writes to an io.Writer.
// It returns possible error from JPEG encoding.
func (data *Data) WriteJPG(w io.Writer, o *jpeg.Options) error {
return jpeg.Encode(w, data.img, o)
}
// WriteGIF encodes image data in GIF format and writes to an io.Writer.
// It returns possible error from GIF encoding.
func (data *Data) WriteGIF(w io.Writer, o *gif.Options) error {
return gif.Encode(w, data.img, o)
}
func init() { func init() {
ttfFont, _ = freetype.ParseFont(ttf) ttfFont, _ = freetype.ParseFont(ttf)
rand.Seed(time.Now().UnixNano())
} }
// LoadFont let you load an external font. // LoadFont let you load an external font.
@ -118,16 +93,16 @@ func LoadFont(fontData []byte) error {
// LoadFontFromReader load an external font from an io.Reader interface. // LoadFontFromReader load an external font from an io.Reader interface.
func LoadFontFromReader(reader io.Reader) error { func LoadFontFromReader(reader io.Reader) error {
b, err := io.ReadAll(reader) var buf bytes.Buffer
if err != nil { if _, err := io.Copy(&buf, reader); err != nil {
return err return err
} }
return LoadFont(b) return LoadFont(buf.Bytes())
} }
// New creates a new captcha. // 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) { func New(width int, height int, option ...SetOption) (*Data, error) {
options := newDefaultOption(width, height) options := newDefaultOption(width, height)
for _, setOption := range option { for _, setOption := range option {
@ -136,7 +111,11 @@ func New(width int, height int, option ...SetOption) (*Data, error) {
text := randomText(options) text := randomText(options)
img := image.NewNRGBA(image.Rect(0, 0, width, height)) img := image.NewNRGBA(image.Rect(0, 0, width, height))
if err := drawWithOption(text, img, options); err != nil { 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 {
return nil, err return nil, err
} }
@ -144,7 +123,7 @@ func New(width int, height int, option ...SetOption) (*Data, error) {
} }
// NewMathExpr creates a new captcha. // 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) { func NewMathExpr(width int, height int, option ...SetOption) (*Data, error) {
options := newDefaultOption(width, height) options := newDefaultOption(width, height)
for _, setOption := range option { for _, setOption := range option {
@ -153,60 +132,39 @@ func NewMathExpr(width int, height int, option ...SetOption) (*Data, error) {
text, equation := randomEquation() text, equation := randomEquation()
img := image.NewNRGBA(image.Rect(0, 0, width, height)) img := image.NewNRGBA(image.Rect(0, 0, width, height))
if err := drawWithOption(equation, img, options); err != nil { 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 {
return nil, err return nil, err
} }
return &Data{Text: text, img: img}, nil 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) { func randomText(opts *Options) (text string) {
n := len([]rune(opts.CharPreset)) n := len(opts.CharPreset)
for i := 0; i < opts.TextLength; i++ { for i := 0; i < opts.TextLength; i++ {
text += string([]rune(opts.CharPreset)[rand.Intn(n)]) text += string(opts.CharPreset[rng.Intn(n)])
} }
return text return text
} }
func drawNoise(img *image.NRGBA, opts *Options) { func drawNoise(img *image.NRGBA, opts *Options) {
noiseCount := (opts.width * opts.height) / int(28.0/opts.Noise) noiseCount := (opts.width * opts.height) / 28
for i := 0; i < noiseCount; i++ { for i := 0; i < noiseCount; i++ {
x := rand.Intn(opts.width) x := rng.Intn(opts.width)
y := rand.Intn(opts.height) y := rng.Intn(opts.height)
img.Set(x, y, randomColor()) img.Set(x, y, randomColor())
} }
} }
func randomColor() color.RGBA { func randomColor() color.RGBA {
red := rand.Intn(256) red := rng.Intn(255)
green := rand.Intn(256) green := rng.Intn(255)
blue := rand.Intn(256) blue := rng.Intn(255)
return color.RGBA{R: uint8(red), G: uint8(green), B: uint8(blue), A: uint8(255)} return color.RGBA{R: uint8(red), G: uint8(green), B: uint8(blue), A: uint8(255)}
} }
@ -224,17 +182,18 @@ func drawSineCurve(img *image.NRGBA, opts *Options) {
if opts.width <= 40 { if opts.width <= 40 {
xStart, xEnd = 1, opts.width-1 xStart, xEnd = 1, opts.width-1
} else { } else {
xStart = rand.Intn(opts.width/10) + 1 xStart = rng.Intn(opts.width/10) + 1
xEnd = opts.width - rand.Intn(opts.width/10) - 1 xEnd = opts.width - rng.Intn(opts.width/10) - 1
} }
curveHeight := float64(rand.Intn(opts.height/6) + opts.height/6) curveHeight := float64(rng.Intn(opts.height/6) + opts.height/6)
yStart := rand.Intn(opts.height*2/3) + opts.height/6 yStart := rng.Intn(opts.height*2/3) + opts.height/6
angle := 1.0 + rand.Float64() angle := 1.0 + rng.Float64()
flip := rng.Intn(2) == 0
yFlip := 1.0 yFlip := 1.0
if rand.Intn(2) == 0 { if flip {
yFlip = -1.0 yFlip = -1.0
} }
curveColor := randomColorFromOptions(opts) curveColor := randomInvertColor(opts.BackgroundColor)
for x1 := xStart; x1 <= xEnd; x1++ { for x1 := xStart; x1 <= xEnd; x1++ {
y := math.Sin(math.Pi*angle*float64(x1)/float64(opts.width)) * curveHeight * yFlip y := math.Sin(math.Pi*angle*float64(x1)/float64(opts.width)) * curveHeight * yFlip
@ -251,15 +210,15 @@ func drawText(text string, img *image.NRGBA, opts *Options) error {
ctx.SetFont(ttfFont) ctx.SetFont(ttfFont)
fontSpacing := opts.width / len(text) fontSpacing := opts.width / len(text)
fontOffset := rand.Intn(fontSpacing / 2) fontOffset := rng.Intn(fontSpacing / 2)
for idx, char := range text { for idx, char := range text {
fontScale := 0.8 + rand.Float64()*0.4 fontScale := 0.8 + rng.Float64()*0.4
fontSize := float64(opts.height) / fontScale * opts.FontScale fontSize := float64(opts.height) / fontScale * opts.FontScale
ctx.SetFontSize(fontSize) ctx.SetFontSize(fontSize)
ctx.SetSrc(image.NewUniform(randomColorFromOptions(opts))) ctx.SetSrc(image.NewUniform(randomInvertColor(opts.BackgroundColor)))
x := fontSpacing*idx + fontOffset x := fontSpacing*idx + fontOffset
y := opts.height/6 + rand.Intn(opts.height/3) + int(fontSize/2) y := opts.height/6 + rng.Intn(opts.height/3) + int(fontSize/2)
pt := freetype.Pt(x, y) pt := freetype.Pt(x, y)
if _, err := ctx.DrawString(string(char), pt); err != nil { if _, err := ctx.DrawString(string(char), pt); err != nil {
return err return err
@ -269,27 +228,18 @@ func drawText(text string, img *image.NRGBA, opts *Options) error {
return nil 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 { func randomInvertColor(base color.Color) color.Color {
baseLightness := getLightness(base) baseLightness := getLightness(base)
var value float64 var value float64
if baseLightness >= 0.5 { if baseLightness >= 0.5 {
value = baseLightness - 0.3 - rand.Float64()*0.2 value = baseLightness - 0.3 - rng.Float64()*0.2
} else { } else {
value = baseLightness + 0.3 + rand.Float64()*0.2 value = baseLightness + 0.3 + rng.Float64()*0.2
} }
hue := float64(rand.Intn(361)) / 360 hue := float64(rng.Intn(361)) / 360
saturation := 0.6 + rand.Float64()*0.2 saturation := 0.6 + rng.Float64()*0.2
return hsva{h: hue, s: saturation, v: value, a: 255} return hsva{h: hue, s: saturation, v: value, a: uint8(255)}
} }
func getLightness(colour color.Color) float64 { func getLightness(colour color.Color) float64 {
@ -330,8 +280,8 @@ func minColor(numList ...uint32) (min uint32) {
} }
func randomEquation() (text string, equation string) { func randomEquation() (text string, equation string) {
left := 1 + rand.Intn(9) left := 1 + rng.Intn(9)
right := 1 + rand.Intn(9) right := 1 + rng.Intn(9)
text = strconv.Itoa(left + right) text = strconv.Itoa(left + right)
equation = strconv.Itoa(left) + "+" + strconv.Itoa(right) equation = strconv.Itoa(left) + "+" + strconv.Itoa(right)

View file

@ -2,16 +2,11 @@ package captcha
import ( import (
"bytes" "bytes"
"errors" "golang.org/x/image/font/gofont/goregular"
"image/color" "image/color"
"image/color/palette"
"image/gif"
"image/jpeg"
"math/rand" "math/rand"
"os" "os"
"testing" "testing"
"golang.org/x/image/font/gofont/goregular"
) )
func TestNewCaptcha(t *testing.T) { func TestNewCaptcha(t *testing.T) {
@ -20,10 +15,7 @@ func TestNewCaptcha(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
err = data.WriteImage(buf) data.WriteImage(buf)
if err != nil {
t.Fatal(err)
}
} }
func TestSmallCaptcha(t *testing.T) { func TestSmallCaptcha(t *testing.T) {
@ -33,48 +25,17 @@ func TestSmallCaptcha(t *testing.T) {
} }
} }
func TestEncodeJPG(t *testing.T) {
data, err := New(150, 50)
if err != nil {
t.Fatal(err)
}
buf := new(bytes.Buffer)
err = data.WriteJPG(buf, &jpeg.Options{Quality: 70})
if err != nil {
t.Fatal(err)
}
}
func TestEncodeGIF(t *testing.T) {
data, err := New(150, 50)
if err != nil {
t.Fatal(err)
}
buf := new(bytes.Buffer)
err = data.WriteGIF(buf, &gif.Options{})
if err != nil {
t.Fatal(err)
}
}
func TestNewCaptchaOptions(t *testing.T) { func TestNewCaptchaOptions(t *testing.T) {
New(100, 34, func(options *Options) { New(100, 34, func(options *Options) {
options.BackgroundColor = color.Opaque options.BackgroundColor = color.Opaque
options.CharPreset = "1234567890" options.CharPreset = "1234567890"
options.CurveNumber = 0 options.CurveNumber = 0
options.TextLength = 6 options.TextLength = 6
options.Palette = palette.WebSafe
}) })
NewMathExpr(100, 34, func(options *Options) { NewMathExpr(100, 34, func(options *Options) {
options.BackgroundColor = color.Black 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) { func TestNewMathExpr(t *testing.T) {
@ -84,16 +45,7 @@ func TestNewMathExpr(t *testing.T) {
} }
} }
func TestNewCustomGenerator(t *testing.T) { func TestCovNilFontError(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 temp := ttfFont
ttfFont = nil ttfFont = nil
@ -107,29 +59,9 @@ func TestNilFontError(t *testing.T) {
t.Fatal("Expect to get nil font error") 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 ttfFont = temp
} }
type errReader struct{}
func (errReader) Read(_ []byte) (int, error) {
return 0, errors.New("")
}
func TestReaderErr(t *testing.T) {
err := LoadFontFromReader(errReader{})
if err == nil {
t.Fatal("Expect to get io.Reader error")
}
}
func TestLoadFont(t *testing.T) { func TestLoadFont(t *testing.T) {
err := LoadFont(goregular.TTF) err := LoadFont(goregular.TTF)
if err != nil { if err != nil {

View file

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

View file

@ -1,7 +0,0 @@
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

View file

@ -1,5 +0,0 @@
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=

View file

@ -2,10 +2,9 @@ package main
import ( import (
"fmt" "fmt"
"github.com/steambap/captcha"
"html/template" "html/template"
"net/http" "net/http"
"github.com/steambap/captcha"
) )
func main() { func main() {

View file

@ -1,10 +0,0 @@
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
)

View file

@ -1,5 +0,0 @@
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=

View file

@ -2,11 +2,10 @@ package main
import ( import (
"fmt" "fmt"
"html/template"
"net/http"
"github.com/steambap/captcha" "github.com/steambap/captcha"
"golang.org/x/image/font/gofont/goregular" "golang.org/x/image/font/gofont/goregular"
"html/template"
"net/http"
) )
func main() { func main() {

5016
font.go Normal file

File diff suppressed because it is too large Load diff

40
fonts/gen.go Normal file
View file

@ -0,0 +1,40 @@
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
View file

@ -1,8 +0,0 @@
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
View file

@ -1,4 +0,0 @@
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=

View file

@ -1,18 +1,3 @@
1.3.0 / 2018-11-7
===================
* Add Palette option
1.2.0 / 2017-12-26
===================
* Add Noise option
1.1.0 / 2017-11-16
===================
* Add WriteJPG and WriteGIF API
1.0.0 / 2017-10-10 1.0.0 / 2017-10-10
=================== ===================