Compare commits

..

No commits in common. "main" and "v0.9.0" have entirely different histories.
main ... v0.9.0

26 changed files with 5168 additions and 565 deletions

1
.gitignore vendored
View file

@ -12,3 +12,4 @@
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
.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>

10
.travis.yml Normal file
View file

@ -0,0 +1,10 @@
language: go
go:
- 1.9
script:
- go test -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017 - 2024 Weilin Shi
Copyright (c) 2017 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

@ -1,18 +1,17 @@
> Package captcha provides an easy to use, unopinionated API for captcha generation.
> Package captcha provides a simple API for captcha generation
<div>
[![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)
[![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)
</div>
## Why another captcha generator?
Because I can.
## install
```
go get concord.hectabit.org/HectaBit/captcha
go get github.com/steambap/captcha
```
## usage
@ -25,27 +24,16 @@ func handle(w http.ResponseWriter, r *http.Request) {
session.Values["captcha"] = data.Text
session.Save(r, w)
// send image data to client
data.WriteImage(w)
data.WriteTo(w)
}
```
[documentation](https://pkg.go.dev/concord.hectabit.org/HectaBit/captcha) |
[example](example/basic/main.go) |
[font example](example/load-font/main.go)
[documentation](https://godoc.org/github.com/steambap/captcha) |
[example](example/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)
[MIT](LICENSE.md)

View file

@ -1,29 +1,23 @@
// Package captcha provides an easy to use, unopinionated API for captcha generation
// Package captcha provides a simple API for captcha generation
package captcha
import (
_ "embed" // embed font
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"image"
"image/color"
"image/draw"
"image/gif"
"image/jpeg"
"image/png"
"io"
"math"
"math/rand"
"strconv"
"time"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
)
const charPreset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
//go:embed fonts/Comismsh.ttf
var ttf []byte
var rng = rand.New(rand.NewSource(time.Now().UnixNano()))
var ttfFont *truetype.Font
// Options manage captcha generation details.
@ -41,18 +35,6 @@ type Options struct {
// CurveNumber is the number of curves to draw on captcha image.
// It defaults to 2.
CurveNumber int
// FontDPI controls DPI (dots per inch) of font.
// The default is 72.0.
FontDPI float64
// 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
@ -62,12 +44,8 @@ func newDefaultOption(width, height int) *Options {
return &Options{
BackgroundColor: color.Transparent,
CharPreset: charPreset,
TextLength: 6,
TextLength: 4,
CurveNumber: 2,
FontDPI: 72.0,
FontScale: 1.0,
Noise: 1.0,
Palette: []color.Color{},
width: width,
height: height,
}
@ -78,56 +56,30 @@ 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 `WriteTo` receiver
type Data struct {
// Text is captcha solution.
// Text is captcha solution
Text string
img *image.NRGBA
}
// WriteImage encodes image data and writes to an io.Writer.
// It returns possible error from PNG encoding.
func (data *Data) WriteImage(w io.Writer) error {
// WriteTo encodes image data and writes to an io.Writer.
// It returns possible error from PNG encoding
func (data *Data) WriteTo(w io.Writer) error {
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() {
ttfFont, _ = freetype.ParseFont(ttf)
rand.Seed(time.Now().UnixNano())
}
// LoadFont let you load an external font.
func LoadFont(fontData []byte) error {
var err error
ttfFont, err = freetype.ParseFont(fontData)
return err
}
// LoadFontFromReader load an external font from an io.Reader interface.
func LoadFontFromReader(reader io.Reader) error {
b, err := io.ReadAll(reader)
ttfFont, err = freetype.ParseFont(ttf)
if err != nil {
return err
panic(err)
}
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 {
@ -136,77 +88,39 @@ func New(width int, height int, option ...SetOption) (*Data, error) {
text := randomText(options)
img := image.NewNRGBA(image.Rect(0, 0, width, height))
if err := drawWithOption(text, img, options); err != nil {
return nil, err
}
return &Data{Text: text, img: img}, nil
}
// NewMathExpr creates a new captcha.
// 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 {
setOption(options)
}
text, equation := randomEquation()
img := image.NewNRGBA(image.Rect(0, 0, width, height))
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)
draw.Draw(img, img.Bounds(), &image.Uniform{options.BackgroundColor}, image.ZP, draw.Src)
drawNoise(img, options)
drawCurves(img, options)
return drawText(text, img, options)
err := drawText(text, img, options)
if err != nil {
return nil, err
}
return &Data{Text: text, img: img}, nil
}
func randomText(opts *Options) (text string) {
n := len([]rune(opts.CharPreset))
n := len(opts.CharPreset)
for i := 0; i < opts.TextLength; i++ {
text += string([]rune(opts.CharPreset)[rand.Intn(n)])
text += string(opts.CharPreset[rng.Intn(n)])
}
return text
}
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++ {
x := rand.Intn(opts.width)
y := rand.Intn(opts.height)
x := rng.Intn(opts.width)
y := rng.Intn(opts.height)
img.Set(x, y, randomColor())
}
}
func randomColor() color.RGBA {
red := rand.Intn(256)
green := rand.Intn(256)
blue := rand.Intn(256)
red := rng.Intn(255)
green := rng.Intn(255)
blue := rng.Intn(255)
return color.RGBA{R: uint8(red), G: uint8(green), B: uint8(blue), A: uint8(255)}
}
@ -224,17 +138,18 @@ func drawSineCurve(img *image.NRGBA, opts *Options) {
if opts.width <= 40 {
xStart, xEnd = 1, opts.width-1
} else {
xStart = rand.Intn(opts.width/10) + 1
xEnd = opts.width - rand.Intn(opts.width/10) - 1
xStart = rng.Intn(opts.width/10) + 1
xEnd = opts.width - rng.Intn(opts.width/10) - 1
}
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()
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
yFlip := 1.0
if rand.Intn(2) == 0 {
if flip {
yFlip = -1.0
}
curveColor := randomColorFromOptions(opts)
curveColor := randomDarkColor()
for x1 := xStart; x1 <= xEnd; x1++ {
y := math.Sin(math.Pi*angle*float64(x1)/float64(opts.width)) * curveHeight * yFlip
@ -242,24 +157,31 @@ func drawSineCurve(img *image.NRGBA, opts *Options) {
}
}
func randomDarkColor() hsva {
hue := float64(rng.Intn(361)) / 360
saturation := 0.6 + rng.Float64()*0.2
value := 0.25 + rng.Float64()*0.2
return hsva{h: hue, s: saturation, v: value, a: uint8(255)}
}
func drawText(text string, img *image.NRGBA, opts *Options) error {
ctx := freetype.NewContext()
ctx.SetDPI(opts.FontDPI)
ctx.SetDPI(92.0)
ctx.SetClip(img.Bounds())
ctx.SetDst(img)
ctx.SetHinting(font.HintingFull)
ctx.SetFont(ttfFont)
fontSpacing := opts.width / len(text)
fontOffset := rand.Intn(fontSpacing / 2)
for idx, char := range text {
fontScale := 0.8 + rand.Float64()*0.4
fontSize := float64(opts.height) / fontScale * opts.FontScale
fontScale := 1 + rng.Float64()*0.5
fontSize := float64(opts.height) / fontScale
ctx.SetFontSize(fontSize)
ctx.SetSrc(image.NewUniform(randomColorFromOptions(opts)))
x := fontSpacing*idx + fontOffset
y := opts.height/6 + rand.Intn(opts.height/3) + int(fontSize/2)
ctx.SetSrc(image.NewUniform(randomDarkColor()))
x := fontSpacing*idx + fontSpacing/int(fontSize)
y := opts.height/6 + rng.Intn(opts.height/3) + int(fontSize/2)
pt := freetype.Pt(x, y)
if _, err := ctx.DrawString(string(char), pt); err != nil {
return err
@ -268,72 +190,3 @@ 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 - rand.Float64()*0.2
} else {
value = baseLightness + 0.3 + rand.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}
}
func getLightness(colour color.Color) float64 {
r, g, b, a := colour.RGBA()
// transparent
if a == 0 {
return 1.0
}
max := maxColor(r, g, b)
min := minColor(r, g, b)
l := (float64(max) + float64(min)) / (2 * 255)
return l
}
func maxColor(numList ...uint32) (max uint32) {
for _, num := range numList {
colorVal := num & 255
if colorVal > max {
max = colorVal
}
}
return max
}
func minColor(numList ...uint32) (min uint32) {
min = 255
for _, num := range numList {
colorVal := num & 255
if colorVal < min {
min = colorVal
}
}
return min
}
func randomEquation() (text string, equation string) {
left := 1 + rand.Intn(9)
right := 1 + rand.Intn(9)
text = strconv.Itoa(left + right)
equation = strconv.Itoa(left) + "+" + strconv.Itoa(right)
return text, equation
}

View file

@ -2,59 +2,18 @@ package captcha
import (
"bytes"
"errors"
"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) {
New(36, 12)
data, err := New(150, 50)
if err != nil {
t.Fatal(err)
}
buf := new(bytes.Buffer)
err = data.WriteImage(buf)
if err != nil {
t.Fatal(err)
}
}
func TestSmallCaptcha(t *testing.T) {
_, err := New(36, 12)
if err != nil {
t.Fatal(err)
}
}
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)
}
data.WriteTo(buf)
}
func TestNewCaptchaOptions(t *testing.T) {
@ -63,37 +22,10 @@ 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) {
_, err := NewMathExpr(150, 50)
if err != nil {
t.Fatal(err)
}
}
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) {
func TestCovNilFontError(t *testing.T) {
temp := ttfFont
ttfFont = nil
@ -102,99 +34,5 @@ func TestNilFontError(t *testing.T) {
t.Fatal("Expect to get nil font error")
}
_, err = NewMathExpr(150, 50)
if err == nil {
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
}
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) {
err := LoadFont(goregular.TTF)
if err != nil {
t.Fatal("Fail to load go font")
}
err = LoadFont([]byte("invalid"))
if err == nil {
t.Fatal("LoadFont incorrectly parse an invalid font")
}
}
func TestLoadFontFromReader(t *testing.T) {
file, err := os.Open("./fonts/Comismsh.ttf")
if err != nil {
t.Fatal("Fail to load test file")
}
if err = LoadFontFromReader(file); err != nil {
t.Fatal("Fail to load font from io.Reader")
}
}
func TestMaxColor(t *testing.T) {
var result uint32
result = maxColor()
if result != 0 {
t.Fatalf("Expect max color to be 0, got %v", result)
}
result = maxColor(1)
if result != 1 {
t.Fatalf("Expect max color to be 1, got %v", result)
}
result = maxColor(52428, 65535)
if result != 255 {
t.Fatalf("Expect max color to be 255, got %v", result)
}
var rng = rand.New(rand.NewSource(0))
for i := 0; i < 10; i++ {
result = maxColor(rng.Uint32(), rng.Uint32(), rng.Uint32())
if result > 255 {
t.Fatalf("Number out of range: %v", result)
}
}
}
func TestMinColor(t *testing.T) {
var result uint32
result = minColor()
if result != 255 {
t.Fatalf("Expect min color to be 255, got %v", result)
}
result = minColor(1)
if result != 1 {
t.Fatalf("Expect min color to be 1, got %v", result)
}
result = minColor(52428, 65535)
if result != 204 {
t.Fatalf("Expect min color to be 1, got %v", result)
}
var rng = rand.New(rand.NewSource(0))
for i := 0; i < 10; i++ {
result = minColor(rng.Uint32(), rng.Uint32(), rng.Uint32())
if result > 255 {
t.Fatalf("Number out of range: %v", result)
}
}
}

View file

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

View file

@ -1,17 +0,0 @@
First off, thank you for considering contributing to captcha. It's people like you that make captcha such a great module.
Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue, assessing changes, and helping you finalize your pull requests.
Captcha is an open source project and we love to receive contributions from our community — you! There are many ways to contribute, from writing blog posts, improving the documentation, submitting bug reports and feature requests or writing code which can be incorporated into captcha itself.
Step your can follow to start:
1. Fork it!
2. Create your feature branch: `git checkout -b my-new-feature`
3. Commit your changes: `git commit -am 'Add some feature'`
4. Push to the branch: `git push origin my-new-feature`
5. Submit a pull request :D
Ground Rules:
- Ensure all tests pass.
- Run `go fmt` before commit. Event better, make go report happy.
- Make sure you only implement ONE feature or bugfix in a pull request. Do not split one line fix in multiple commits to confuse others either.

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

@ -1,11 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Captcha</title>
</head>
<body>
<img src="/captcha-default" alt="captcha">
<img src="/captcha-math" alt="captcha">
</body>
</html>

View file

@ -1,49 +0,0 @@
package main
import (
"fmt"
"html/template"
"net/http"
"github.com/steambap/captcha"
)
func main() {
http.HandleFunc("/", indexHandle)
http.HandleFunc("/captcha-default", captchaHandle)
http.HandleFunc("/captcha-math", mathHandle)
fmt.Println("Server start at port 8080")
err := http.ListenAndServe(":8080", nil)
if err != nil {
panic(err)
}
}
func indexHandle(w http.ResponseWriter, _ *http.Request) {
doc, err := template.ParseFiles("index.html")
if err != nil {
fmt.Fprint(w, err.Error())
return
}
doc.Execute(w, nil)
}
func captchaHandle(w http.ResponseWriter, _ *http.Request) {
img, err := captcha.New(150, 50)
if err != nil {
fmt.Fprint(w, nil)
fmt.Println(err.Error())
return
}
img.WriteImage(w)
}
func mathHandle(w http.ResponseWriter, _ *http.Request) {
img, err := captcha.NewMathExpr(150, 50)
if err != nil {
fmt.Fprint(w, nil)
fmt.Println(err.Error())
return
}
img.WriteImage(w)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View file

@ -1,10 +1,10 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Load Font</title>
</head>
<body>
<img src="/captcha" alt="captcha">
</body>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Captcha</title>
</head>
<body>
<img src="/captcha" alt="captcha">
</body>
</html>

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,23 +2,16 @@ package main
import (
"fmt"
"github.com/steambap/captcha"
"html/template"
"net/http"
"github.com/steambap/captcha"
"golang.org/x/image/font/gofont/goregular"
)
func main() {
err := captcha.LoadFont(goregular.TTF)
if err != nil {
panic(err)
}
http.HandleFunc("/", indexHandle)
http.HandleFunc("/captcha", captchaHandle)
fmt.Println("Server start at port 8080")
err = http.ListenAndServe(":8080", nil)
err := http.ListenAndServe(":8080", nil)
if err != nil {
panic(err)
}
@ -34,13 +27,11 @@ func indexHandle(w http.ResponseWriter, _ *http.Request) {
}
func captchaHandle(w http.ResponseWriter, _ *http.Request) {
img, err := captcha.New(150, 50, func(options *captcha.Options) {
options.FontScale = 0.8
})
img, err := captcha.New(150, 50)
if err != nil {
fmt.Fprint(w, nil)
fmt.Println(err.Error())
return
}
img.WriteImage(w)
img.WriteTo(w)
}

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,40 +0,0 @@
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
===================
* Add LoadFontFromReader API
* Rename WriteTo to WriteImage
0.12.0 / 2017-10-07
===================
* Add FontDPI and FontScale options
0.11.0 / 2017-09-28
===================
* Add NewMathExpr API
0.10.0 / 2017-09-23
===================
* Add LoadFont API
0.9.0 / 2017-09-20
===================
* Initial release