mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/1Panel-dev/1Panel.git
synced 2026-09-20 08:03:55 +08:00
63 lines
1.7 KiB
Go
63 lines
1.7 KiB
Go
package captcha
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"image"
|
|
"image/color"
|
|
"image/draw"
|
|
"image/png"
|
|
"math/rand/v2"
|
|
"sync"
|
|
|
|
"golang.org/x/image/font"
|
|
"golang.org/x/image/font/gofont/gobold"
|
|
"golang.org/x/image/font/opentype"
|
|
"golang.org/x/image/math/fixed"
|
|
)
|
|
|
|
var loadFont = sync.OnceValues(func() (*opentype.Font, error) {
|
|
return opentype.Parse(gobold.TTF)
|
|
})
|
|
|
|
func renderQuestion(question string) (string, error) {
|
|
f, err := loadFont()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
dst := image.NewRGBA(image.Rect(0, 0, captchaWidth, captchaHeight))
|
|
background := image.NewUniform(color.RGBA{R: 246, G: 248, B: 251, A: 255})
|
|
draw.Draw(dst, dst.Bounds(), background, image.Point{}, draw.Src)
|
|
chars := []rune(question)
|
|
cellWidth := (captchaWidth - 12) / len(chars)
|
|
for i, char := range chars {
|
|
face, err := opentype.NewFace(f, &opentype.FaceOptions{
|
|
Size: float64(24 + rand.IntN(5)), DPI: 72, Hinting: font.HintingFull,
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
bounds, _ := font.BoundString(face, string(char))
|
|
x := fixed.I(6+i*cellWidth+cellWidth/2) - (bounds.Min.X+bounds.Max.X)/2
|
|
y := fixed.I(captchaHeight/2+rand.IntN(7)-3) - (bounds.Min.Y+bounds.Max.Y)/2
|
|
d := font.Drawer{
|
|
Dst: dst,
|
|
Src: image.NewUniform(color.RGBA{
|
|
R: uint8(20 + rand.IntN(90)), G: uint8(20 + rand.IntN(90)),
|
|
B: uint8(20 + rand.IntN(90)), A: 255,
|
|
}),
|
|
Face: face,
|
|
Dot: fixed.Point26_6{X: x, Y: y},
|
|
}
|
|
d.DrawString(string(char))
|
|
if err := face.Close(); err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
var buf bytes.Buffer
|
|
if err := png.Encode(&buf, dst); err != nil {
|
|
return "", err
|
|
}
|
|
return "data:image/png;base64," + base64.StdEncoding.EncodeToString(buf.Bytes()), nil
|
|
}
|