Browse Source

add: 活动分享图片

hsun 3 years ago
parent
commit
ff41ef523d
6 changed files with 380 additions and 4 deletions
  1. 75 0
      controller/activity/activity.go
  2. 8 4
      go.mod
  3. 1 0
      routers/activity.go
  4. BIN
      static/img/reportdetail_share.png
  5. BIN
      static/ttf/songti.ttf
  6. 296 0
      utils/drawtext.go

+ 75 - 0
controller/activity/activity.go

@@ -1,11 +1,15 @@
 package activity
 
 import (
+	"fmt"
 	"github.com/gin-gonic/gin"
 	"hongze/hongze_yb/controller/response"
+	"hongze/hongze_yb/models/tables/yb_activity"
 	"hongze/hongze_yb/services/activity"
 	"hongze/hongze_yb/services/user"
 	"hongze/hongze_yb/utils"
+	"io/ioutil"
+	"os"
 	"strconv"
 	"time"
 )
@@ -136,4 +140,75 @@ func GetActivityVoices(c *gin.Context) {
 		return
 	}
 	response.OkData("获取成功", listData, c)
+}
+
+// GetActivityShareImg 生成活动分享图片
+// @Tags 活动模块
+// @Summary  生成活动分享图片
+// @Description 生成活动分享图片
+// @Security ApiKeyAuth
+// @Param Authorization	header string true "Bearer 31a165baebe6dec616b1f8f3207b4273"
+// @Accept  json
+// @Product json
+// @Param activity_id query int true "活动ID"
+// @Success 200 {object} string "获取成功"
+// @failure 400 {string} string "获取失败"
+// @Router /activity/getActivityShareImg [get]
+func GetActivityShareImg(c *gin.Context)  {
+	reqActivityId := c.DefaultQuery("activity_id", "0")
+	if reqActivityId == "0" {
+		response.Fail("参数异常", c)
+		return
+	}
+	activityId, _ := strconv.Atoi(reqActivityId)
+	activityInfo, err := yb_activity.GetDetailById(activityId)
+	if err != nil {
+		if err == utils.ErrNoRow {
+			response.Fail("获取活动信息失败", c)
+			return
+		}
+	}
+	// 获取原分享图
+	originShareUrl := "static/img/reportdetail_share.png"
+	fp, err := os.OpenFile(originShareUrl, os.O_CREATE|os.O_APPEND, 6)
+	if err != nil {
+		response.Fail("读取封面图失败", c)
+		return
+	}
+	defer fp.Close()
+	bytes, err := ioutil.ReadAll(fp)
+	if err != nil {
+		response.Fail("读取封面图失败", c)
+		return
+	}
+	// 时间处理
+	activityDate := activityInfo.StartTime.Format("2006-01-02")
+	activityStart := activityInfo.StartTime.Format("15:04")
+	activityEnd := activityInfo.EndTime.Format("15:04")
+	activityWeek := activityInfo.StartTime.Weekday().String()
+	week := utils.StrDateTimeToWeek(activityWeek)
+	timeFormat := activityDate + " " + activityStart + "-" + activityEnd + " " + week
+	// 生成新分享图
+	var drawText []*utils.DrawTextInfo
+	text := &utils.DrawTextInfo{
+		Text: timeFormat,
+		X: 60,
+		Y: 250,
+	}
+	drawText = append(drawText, text)
+	var colorRGBA = utils.FontRGBA{
+		R: 223,
+		G: 197,
+		B: 143,
+		A: 255,
+	}
+	picByte, err := utils.DrawStringOnImage(bytes, drawText, colorRGBA)
+	if err != nil {
+		response.Fail("生成新封面图失败", c)
+		return
+	}
+
+	c.Header("Content-Type", "image/png")
+	c.Header("Content-Disposition", fmt.Sprintf("inline; filename=\"%s\"", "图片地址"))
+	c.Writer.WriteString(picByte.String())
 }

+ 8 - 4
go.mod

@@ -5,12 +5,12 @@ go 1.15
 require (
 	github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
 	github.com/aliyun/aliyun-oss-go-sdk v1.9.8
+	github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
 	github.com/fsnotify/fsnotify v1.5.1
 	github.com/gin-gonic/gin v1.7.4
 	github.com/go-playground/validator/v10 v10.9.0 // indirect
 	github.com/go-redis/redis/v8 v8.11.4
-	github.com/go-sql-driver/mysql v1.6.0 // indirect
-	github.com/golang/protobuf v1.5.2 // indirect
+	github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
 	github.com/jonboulle/clockwork v0.2.2 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
@@ -18,17 +18,21 @@ require (
 	github.com/mattn/go-isatty v0.0.14 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/op/go-logging v0.0.0-20160315200505-970db520ece7
+	github.com/satori/go.uuid v1.2.0 // indirect
 	github.com/silenceper/wechat/v2 v2.0.9
 	github.com/spf13/viper v1.9.0
 	github.com/swaggo/gin-swagger v1.3.3
 	github.com/swaggo/swag v1.7.4
 	golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
+	golang.org/x/image v0.0.0-20190802002840-cff245a6509b
 	golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42 // indirect
 	golang.org/x/text v0.3.7 // indirect
-	google.golang.org/protobuf v1.27.1 // indirect
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
-	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gorm.io/driver/mysql v1.1.3
 	gorm.io/gorm v1.22.2
+	github.com/go-sql-driver/mysql v1.6.0 // indirect
+	github.com/golang/protobuf v1.5.2 // indirect
+	google.golang.org/protobuf v1.27.1 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
 )

+ 1 - 0
routers/activity.go

@@ -13,6 +13,7 @@ func InitActivity(r *gin.Engine) {
 		rGroup.GET("/getPageList", activity.GetPageList)
 		rGroup.GET("/getActivityDetail", activity.GetActivityDetail)
 		rGroup.GET("/getActivityVoices", activity.GetActivityVoices)
+		rGroup.GET("/getActivityShareImg", activity.GetActivityShareImg)
 		rGroup.POST("/addRemind", activity.AddRemind)
 		rGroup.POST("/cancelRemind", activity.CancelRemind)
 		rGroup.POST("/registerActivity", activity.RegisterActivity)

BIN
static/img/reportdetail_share.png


BIN
static/ttf/songti.ttf


+ 296 - 0
utils/drawtext.go

@@ -0,0 +1,296 @@
+package utils
+
+import (
+	"bytes"
+	"fmt"
+	"image"
+	"image/color"
+	"image/draw"
+	"image/gif"
+	"image/jpeg"
+	"image/png"
+	"io/ioutil"
+	"net/http"
+	"os"
+
+	"github.com/golang/freetype"
+	"github.com/golang/freetype/truetype"
+	"golang.org/x/image/font"
+)
+
+//DrawTextInfo 图片绘字信息
+type DrawTextInfo struct {
+	Text string
+	X    int
+	Y    int
+}
+
+//DrawRectInfo 图片画框信息
+type DrawRectInfo struct {
+	X1 int
+	Y1 int
+	X2 int
+	Y2 int
+}
+
+//TextBrush 字体相关
+type TextBrush struct {
+	FontType  *truetype.Font
+	FontSize  float64
+	FontColor *image.Uniform
+	TextWidth int
+}
+
+//NewTextBrush 新生成笔刷
+func NewTextBrush(FontFilePath string, FontSize float64, FontColor *image.Uniform, textWidth int) (*TextBrush, error) {
+	fontFile, err := ioutil.ReadFile(FontFilePath)
+	if err != nil {
+		return nil, err
+	}
+	fontType, err := truetype.Parse(fontFile)
+	if err != nil {
+		return nil, err
+	}
+	if textWidth <= 0 {
+		textWidth = 20
+	}
+	return &TextBrush{FontType: fontType, FontSize: FontSize, FontColor: FontColor, TextWidth: textWidth}, nil
+}
+
+//DrawFontOnRGBA 图片插入文字
+func (fb *TextBrush) DrawFontOnRGBA(rgba *image.RGBA, pt image.Point, content string) {
+
+	c := freetype.NewContext()
+	c.SetDPI(72)
+	c.SetFont(fb.FontType)
+	c.SetHinting(font.HintingFull)
+	c.SetFontSize(fb.FontSize)
+	c.SetClip(rgba.Bounds())
+	c.SetDst(rgba)
+	c.SetSrc(fb.FontColor)
+
+	c.DrawString(content, freetype.Pt(pt.X, pt.Y))
+
+}
+
+//Image2RGBA Image2RGBA
+func Image2RGBA(img image.Image) *image.RGBA {
+
+	baseSrcBounds := img.Bounds().Max
+
+	newWidth := baseSrcBounds.X
+	newHeight := baseSrcBounds.Y
+
+	des := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight)) // 底板
+	//首先将一个图片信息存入jpg
+	draw.Draw(des, des.Bounds(), img, img.Bounds().Min, draw.Over)
+
+	return des
+}
+
+type FontRGBA struct {
+	R uint8
+	G uint8
+	B uint8
+	A uint8
+}
+
+//DrawStringOnImageAndSave 图片上写文字
+func DrawStringOnImageAndSave(imagePath string, imageData []byte, infos []*DrawTextInfo, colorRGBA FontRGBA) (err error) {
+	//判断图片类型
+	var backgroud image.Image
+	filetype := http.DetectContentType(imageData)
+	switch filetype {
+	case "image/jpeg", "image/jpg":
+		backgroud, err = jpeg.Decode(bytes.NewReader(imageData))
+		if err != nil {
+			fmt.Println("jpeg error")
+			return err
+		}
+
+	case "image/gif":
+		backgroud, err = gif.Decode(bytes.NewReader(imageData))
+		if err != nil {
+			return err
+		}
+
+	case "image/png":
+		backgroud, err = png.Decode(bytes.NewReader(imageData))
+		if err != nil {
+			return err
+		}
+	default:
+		return err
+	}
+	des := Image2RGBA(backgroud)
+
+	//新建笔刷
+	ttfPath := "static/ttf/songti.ttf"
+	textBrush, _ := NewTextBrush(ttfPath, 25, image.Black, 50)
+
+	//Px Py 绘图开始坐标 text要绘制的文字
+	//调整颜色
+	c := freetype.NewContext()
+	c.SetDPI(72)
+	c.SetFont(textBrush.FontType)
+	c.SetHinting(font.HintingFull)
+	c.SetFontSize(textBrush.FontSize)
+	c.SetClip(des.Bounds())
+	c.SetDst(des)
+	textBrush.FontColor = image.NewUniform(color.RGBA{
+		R: colorRGBA.R,
+		G: colorRGBA.G,
+		B: colorRGBA.B,
+		A: colorRGBA.A,
+	})
+	c.SetSrc(textBrush.FontColor)
+
+	for _, info := range infos {
+		c.DrawString(info.Text, freetype.Pt(info.X, info.Y))
+	}
+
+	//保存图片
+	fSave, err := os.Create(imagePath)
+	if err != nil {
+		return err
+	}
+	defer fSave.Close()
+
+	err = jpeg.Encode(fSave, des, nil)
+
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+//DrawRectOnImageAndSave 图片上画多个框
+func DrawRectOnImageAndSave(imagePath string, imageData []byte, infos []*DrawRectInfo) (err error) {
+	//判断图片类型
+	var backgroud image.Image
+	filetype := http.DetectContentType(imageData)
+	switch filetype {
+	case "image/jpeg", "image/jpg":
+		backgroud, err = jpeg.Decode(bytes.NewReader(imageData))
+		if err != nil {
+			fmt.Println("jpeg error")
+			return err
+		}
+
+	case "image/gif":
+		backgroud, err = gif.Decode(bytes.NewReader(imageData))
+		if err != nil {
+			return err
+		}
+
+	case "image/png":
+		backgroud, err = png.Decode(bytes.NewReader(imageData))
+		if err != nil {
+			return err
+		}
+	default:
+		return err
+	}
+	des := Image2RGBA(backgroud)
+	//新建笔刷
+	textBrush, _ := NewTextBrush("arial.ttf", 15, image.Black, 15)
+	for _, info := range infos {
+		var c *freetype.Context
+		c = freetype.NewContext()
+		c.SetDPI(72)
+		c.SetFont(textBrush.FontType)
+		c.SetHinting(font.HintingFull)
+		c.SetFontSize(textBrush.FontSize)
+		c.SetClip(des.Bounds())
+		c.SetDst(des)
+		cGreen := image.NewUniform(color.RGBA{
+			R: 0,
+			G: 0xFF,
+			B: 0,
+			A: 255,
+		})
+
+		c.SetSrc(cGreen)
+		for i := info.X1; i < info.X2; i++ {
+			c.DrawString("·", freetype.Pt(i, info.Y1))
+			c.DrawString("·", freetype.Pt(i, info.Y2))
+		}
+		for j := info.Y1; j < info.Y2; j++ {
+			c.DrawString("·", freetype.Pt(info.X1, j))
+			c.DrawString("·", freetype.Pt(info.X2, j))
+		}
+	}
+
+	//保存图片
+	fSave, err := os.Create(imagePath)
+	if err != nil {
+		return err
+	}
+	defer fSave.Close()
+
+	err = jpeg.Encode(fSave, des, nil)
+
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+//DrawStringOnImage 生成图片
+func DrawStringOnImage(imageData []byte, infos []*DrawTextInfo, colorRGBA FontRGBA) (picBytes bytes.Buffer, err error) {
+	//判断图片类型
+	var backgroud image.Image
+	filetype := http.DetectContentType(imageData)
+	switch filetype {
+	case "image/jpeg", "image/jpg":
+		backgroud, err = jpeg.Decode(bytes.NewReader(imageData))
+		if err != nil {
+			fmt.Println("jpeg error")
+			return
+		}
+
+	case "image/gif":
+		backgroud, err = gif.Decode(bytes.NewReader(imageData))
+		if err != nil {
+			return
+		}
+
+	case "image/png":
+		backgroud, err = png.Decode(bytes.NewReader(imageData))
+		if err != nil {
+			return
+		}
+	default:
+		return
+	}
+	des := Image2RGBA(backgroud)
+
+	//新建笔刷
+	ttfPath := "static/ttf/songti.ttf"
+	textBrush, _ := NewTextBrush(ttfPath, 25, image.Black, 50)
+
+	//Px Py 绘图开始坐标 text要绘制的文字
+	//调整颜色
+	c := freetype.NewContext()
+	c.SetDPI(72)
+	c.SetFont(textBrush.FontType)
+	c.SetHinting(font.HintingFull)
+	c.SetFontSize(textBrush.FontSize)
+	c.SetClip(des.Bounds())
+	c.SetDst(des)
+	textBrush.FontColor = image.NewUniform(color.RGBA{
+		R: colorRGBA.R,
+		G: colorRGBA.G,
+		B: colorRGBA.B,
+		A: colorRGBA.A,
+	})
+	c.SetSrc(textBrush.FontColor)
+
+	for _, info := range infos {
+		c.DrawString(info.Text, freetype.Pt(info.X, info.Y))
+	}
+
+	err = jpeg.Encode(&picBytes, des, nil)
+
+	return
+}