index.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. package captcha
  2. import (
  3. "bytes"
  4. "github.com/fogleman/gg"
  5. "github.com/golang/freetype/truetype"
  6. "golang.org/x/image/font"
  7. "math/rand"
  8. "time"
  9. )
  10. type Captcha struct {
  11. }
  12. const Chars = "ABCDEFGHIJKMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789"
  13. // GetRandStr 生成验证码字符串(可存入redis或者放入其他缓存中)
  14. func (Captcha) GetRandStr(n int) (randStr string) {
  15. charsLen := len(Chars)
  16. if n > 10 {
  17. n = 10
  18. }
  19. rand.Seed(time.Now().UnixNano())
  20. for i := 0; i < n; i++ {
  21. randIndex := rand.Intn(charsLen)
  22. randStr += Chars[randIndex : randIndex+1]
  23. }
  24. return randStr
  25. }
  26. // ImgText 生成图片byte数据可以生成base64数据或者生成img图片文件
  27. func (Captcha) ImgText(width, height int, text string) (b []byte) {
  28. textLen := len(text)
  29. dc := gg.NewContext(width, height)
  30. bgR, bgG, bgB, bgA := getRandColorRange(240, 255)
  31. dc.SetRGBA255(bgR, bgG, bgB, bgA)
  32. dc.Clear()
  33. // 干扰线
  34. for i := 0; i < 10; i++ {
  35. x1, y1 := getRandPos(width, height)
  36. x2, y2 := getRandPos(width, height)
  37. r, g, b, a := getRandColor(255)
  38. w := float64(rand.Intn(3) + 1)
  39. dc.SetRGBA255(r, g, b, a)
  40. dc.SetLineWidth(w)
  41. dc.DrawLine(x1, y1, x2, y2)
  42. dc.Stroke()
  43. }
  44. fontSize := float64(height/2) + 5
  45. face := loadFontFace(fontSize)
  46. dc.SetFontFace(face)
  47. for i := 0; i < len(text); i++ {
  48. r, g, b, _ := getRandColor(100)
  49. dc.SetRGBA255(r, g, b, 255)
  50. fontPosX := float64(width/textLen*i) + fontSize*0.6
  51. writeText(dc, text[i:i+1], float64(fontPosX), float64(height/2))
  52. }
  53. buffer := bytes.NewBuffer(nil)
  54. dc.EncodePNG(buffer)
  55. b = buffer.Bytes()
  56. return
  57. }
  58. // 渲染文字
  59. func writeText(dc *gg.Context, text string, x, y float64) {
  60. xfload := 5 - rand.Float64()*10 + x
  61. yfload := 5 - rand.Float64()*10 + y
  62. radians := 40 - rand.Float64()*80
  63. dc.RotateAbout(gg.Radians(radians), x, y)
  64. dc.DrawStringAnchored(text, xfload, yfload, 0.2, 0.5)
  65. dc.RotateAbout(-1*gg.Radians(radians), x, y)
  66. dc.Stroke()
  67. }
  68. // 随机坐标
  69. func getRandPos(width, height int) (x float64, y float64) {
  70. x = rand.Float64() * float64(width)
  71. y = rand.Float64() * float64(height)
  72. return x, y
  73. }
  74. // 随机颜色
  75. func getRandColor(maxColor int) (r, g, b, a int) {
  76. r = int(uint8(rand.Intn(maxColor)))
  77. g = int(uint8(rand.Intn(maxColor)))
  78. b = int(uint8(rand.Intn(maxColor)))
  79. a = int(uint8(rand.Intn(255)))
  80. return r, g, b, a
  81. }
  82. // 随机颜色范围
  83. func getRandColorRange(miniColor, maxColor int) (r, g, b, a int) {
  84. if miniColor > maxColor {
  85. miniColor = 0
  86. maxColor = 255
  87. }
  88. r = int(uint8(rand.Intn(maxColor-miniColor) + miniColor))
  89. g = int(uint8(rand.Intn(maxColor-miniColor) + miniColor))
  90. b = int(uint8(rand.Intn(maxColor-miniColor) + miniColor))
  91. a = int(uint8(rand.Intn(maxColor-miniColor) + miniColor))
  92. return r, g, b, a
  93. }
  94. // 加载字体
  95. func loadFontFace(points float64) font.Face {
  96. // 这里是将字体TTF文件转换成了 byte 数据保存成了一个 go 文件
  97. // 通过truetype.Parse可以将 byte 类型的数据转换成TTF字体类型
  98. f, err := truetype.Parse(COMICSAN)
  99. if err != nil {
  100. panic(err)
  101. }
  102. face := truetype.NewFace(f, &truetype.Options{
  103. Size: points,
  104. })
  105. return face
  106. }