您的位置:首页技术文章

golang中实现给gif、png、jpeg图片添加文字水印

【字号: 日期:2023-09-26 16:21:55浏览:48作者:馨心
添加水印示例添加main文件

“watermark/main.go”

package mainimport ( 'fmt' 'watermark/textwatermark' )func main() { SavePath := './kaf' str := textwatermark.FontInfo{18, '努力向上', textwatermark.TopLeft, 20, 20, 255, 255, 0, 255} arr := make([]textwatermark.FontInfo, 0) arr = append(arr, str) str2 := textwatermark.FontInfo{Size: 24, Message: '努力向上,涨工资', Position: textwatermark.TopLeft, Dx: 20, Dy: 40, R: 255, G: 255, B: 0, A: 255} arr = append(arr, str2) //加水印图片路径 // fileName := '123123.jpg' fileName := '17.gif' w := new(textwatermark.Water) w.Pattern = '2006/01/02' textwatermark.Ttf = './wrzh.ttf' //字体路径 err := w.New(SavePath, fileName, arr) if err != nil { fmt.Println(err) }}golang添加水印包文件

'watermark/textwatermark.go'

package textwatermarkimport ('errors''fmt''image''image/color''image/draw''image/gif''image/jpeg''image/png''io/ioutil''math/rand''os''time''github.com/golang/freetype')// 水印的位置const (TopLeft int = iotaTopRightBottomLeftBottomRightCenter)//字体路径var Ttf stringtype Water struct {Pattern string //增加按时间划分的子目录:默认没有时间划分的子目录}func (w *Water) New(SavePath, fileName string, typeface []FontInfo) error {var subPath stringsubPath = w.Patterndirs, err := createDir(SavePath, subPath)if err != nil {return err}imgfile, _ := os.Open(fileName)defer imgfile.Close()_, str, err := image.DecodeConfig(imgfile)if err != nil {return err}newName := fmt.Sprintf('%s%s.%s', dirs, getRandomString(10), str)if str == 'gif' {err = gifFontWater(fileName, newName, typeface)} else {err = staticFontWater(fileName, newName, str, typeface)}return err}//gif图片水印func gifFontWater(file, name string, typeface []FontInfo) (err error) {imgfile, _ := os.Open(file)defer imgfile.Close()var err2 errorgifimg2, _ := gif.DecodeAll(imgfile)gifs := make([]*image.Paletted, 0)x0 := 0y0 := 0yuan := 0for k, gifimg := range gifimg2.Image {img := image.NewNRGBA(gifimg.Bounds())if k == 0 {x0 = img.Bounds().Dx()y0 = img.Bounds().Dy()}fmt.Printf('%v, %vn', img.Bounds().Dx(), img.Bounds().Dy())if k == 0 && gifimg2.Image[k+1].Bounds().Dx() > x0 && gifimg2.Image[k+1].Bounds().Dy() > y0 {yuan = 1break}if x0 == img.Bounds().Dx() && y0 == img.Bounds().Dy() {for y := 0; y < img.Bounds().Dy(); y++ {for x := 0; x < img.Bounds().Dx(); x++ {img.Set(x, y, gifimg.At(x, y))}}img, err2 = common(img, typeface) //添加文字水印if err2 != nil {break}//定义一个新的图片调色板img.Bounds():使用原图的颜色域,gifimg.Palette:使用原图的调色板p1 := image.NewPaletted(gifimg.Bounds(), gifimg.Palette)//把绘制过文字的图片添加到新的图片调色板上draw.Draw(p1, gifimg.Bounds(), img, image.ZP, draw.Src)//把添加过文字的新调色板放入调色板slicegifs = append(gifs, p1)} else {gifs = append(gifs, gifimg)}}if yuan == 1 {return errors.New('gif: image block is out of bounds')} else {if err2 != nil {return err2}//保存到新文件中newfile, err := os.Create(name)if err != nil {return err}defer newfile.Close()g1 := &gif.GIF{Image: gifs,Delay: gifimg2.Delay,LoopCount: gifimg2.LoopCount,}err = gif.EncodeAll(newfile, g1)return err}}//png,jpeg图片水印func staticFontWater(file, name, status string, typeface []FontInfo) (err error) {//需要加水印的图片imgfile, _ := os.Open(file)defer imgfile.Close()var staticImg image.Imageif status == 'png' {staticImg, _ = png.Decode(imgfile)} else {staticImg, _ = jpeg.Decode(imgfile)}img := image.NewNRGBA(staticImg.Bounds())for y := 0; y < img.Bounds().Dy(); y++ {for x := 0; x < img.Bounds().Dx(); x++ {img.Set(x, y, staticImg.At(x, y))}}img, err = common(img, typeface) //添加文字水印if err != nil {return err}//保存到新文件中newfile, err := os.Create(name)if err != nil {return err}defer newfile.Close()if status == 'png' {err = png.Encode(newfile, img)} else {err = jpeg.Encode(newfile, img, &jpeg.Options{100})}return err}//添加文字水印函数func common(img *image.NRGBA, typeface []FontInfo) (*image.NRGBA, error) {var err2 error//拷贝一个字体文件到运行目录fontBytes, err := ioutil.ReadFile(Ttf)if err != nil {err2 = errreturn nil, err2}font, err := freetype.ParseFont(fontBytes)if err != nil {err2 = errreturn nil, err2}errNum := 1Loop:for _, t := range typeface {info := t.Messagef := freetype.NewContext()f.SetDPI(108)f.SetFont(font)f.SetFontSize(t.Size)f.SetClip(img.Bounds())f.SetDst(img)f.SetSrc(image.NewUniform(color.RGBA{R: t.R, G: t.G, B: t.B, A: t.A}))//第一行的文字// pt := freetype.Pt(img.Bounds().Dx()-len(info)*4-20, img.Bounds().Dy()-100)first := 0two := 0switch int(t.Position) {case 0:first = t.Dxtwo = t.Dy + int(f.PointToFixed(t.Size)>>6)case 1:first = img.Bounds().Dx() - len(info)*4 - t.Dxtwo = t.Dy + int(f.PointToFixed(t.Size)>>6)case 2:first = t.Dxtwo = img.Bounds().Dy() - t.Dycase 3:first = img.Bounds().Dx() - len(info)*4 - t.Dxtwo = img.Bounds().Dy() - t.Dycase 4:first = (img.Bounds().Dx() - len(info)*4) / 2two = (img.Bounds().Dy() - t.Dy) / 2default:errNum = 0break Loop}// fmt.Printf('%v, %v, %vn', first, two, info)pt := freetype.Pt(first, two)_, err = f.DrawString(info, pt)if err != nil {err2 = errbreak}}if errNum == 0 {err2 = errors.New('坐标值不对')}return img, err2}//定义添加的文字信息type FontInfo struct {Size float64 //文字大小Message string //文字内容Position int //文字存放位置Dx int //文字x轴留白距离Dy int //文字y轴留白距离R uint8 //文字颜色值RGBA中的R值G uint8 //文字颜色值RGBA中的G值B uint8 //文字颜色值RGBA中的B值A uint8 //文字颜色值RGBA中的A值}//生成图片名字func getRandomString(lenght int) string {str := '0123456789abcdefghijklmnopqrstuvwxyz'bytes := []byte(str)bytesLen := len(bytes)result := []byte{}r := rand.New(rand.NewSource(time.Now().UnixNano()))for i := 0; i < lenght; i++ {result = append(result, bytes[r.Intn(bytesLen)])}return string(result)}//检查并生成存放图片的目录func createDir(SavePath, subPath string) (string, error) {var dirs stringif subPath == '' {dirs = fmt.Sprintf('%s/', SavePath)} else {dirs = fmt.Sprintf('%s/%s/', SavePath, time.Now().Format(subPath))}_, err := os.Stat(dirs)if err != nil {err = os.MkdirAll(dirs, os.ModePerm)if err != nil {return '', err}}return dirs, nil}

补充:golang基础--image/draw渲染图片、利用golang/freetype库在图片上生成文字

需求

在一张A4纸上,利用image/draw标准库生成4张二维码,和该二维码的客户信息

1、二维码生成利用到的库就是image/draw,通过draw.Draw进行写入

2、然后字体渲染利用了golang/freetype开源库

https://github.com/golang/freetype/blob/master/example/freetype/main.go

安装依赖

'github.com/golang/freetype''golang.org/x/image/font'

以上的golang.org/x/image/font需要翻墙,如果不能翻墙利用如下方法也可以:

golang中实现给gif、png、jpeg图片添加文字水印

逻辑

1、通过os.Create('dst.jpg')生成一个最终的图片,该图片上画了4个二维码,和头部文字渲染

2、通过os.Open('/Users/zhiliao/zhiliao/gopro/go_safly/src/qr.png')去获取本地的一个二维码图片路径,然后通过png.Decode(file1)生成图片

3、修改二维码图片的尺寸resize.Resize(314, 314, img, resize.Lanczos3)

4、通过image.NewRGBA(image.Rect(0, 0, 827, 1169))生成一个RGBA结构体的矩形框,就好比是画布的概念

5、选渲染头部的客户信息字体,需要一个中文字体库,这个可以用Mac系统库的中文字体,或者自行下载ttf字体库,然后加载该字体

6、draw.Draw(jpg, jpg.Bounds(), bg, image.ZP, draw.Src)是进行设置渲染的参数,参数是画布、渲染开始的地方、图片来源、图片参数、渲染模式

7、然后就是设置baseline,我这里去掉了,然后就是划线,最后就是渲染字体,通过c.DrawString(s, pt)

8、接下来就是画4个二维码

draw.Draw(jpg, img.Bounds().Add(image.Pt(60, 150)), img, img.Bounds().Min, draw.Src) //截取图片的一部分draw.Draw(jpg, img.Bounds().Add(image.Pt(435, 150)), img, img.Bounds().Min, draw.Src) //截取图片的一部分draw.Draw(jpg, img.Bounds().Add(image.Pt(60, 610)), img, img.Bounds().Min, draw.Src) //截取图片的一部分draw.Draw(jpg, img.Bounds().Add(image.Pt(435, 610)), img, img.Bounds().Min, draw.Src) //截取图片的一部分

9、最后通过png.Encode(file, jpg)输出到我们最终生成的图片

效果图

golang中实现给gif、png、jpeg图片添加文字水印

实例

package mainimport ('flag''fmt''github.com/gin-gonic/gin''github.com/golang/freetype''github.com/nfnt/resize''golang.org/x/image/font''image''image/draw''image/png''io/ioutil''log''net/http''os''strings')var (dpi = flag.Float64('dpi', 72, 'screen resolution in Dots Per Inch')fontfile = flag.String('fontfile', '/Users/zhiliao/Downloads/ffffonts/simsun.ttf', 'filename of the ttf font')hinting = flag.String('hinting', 'none', 'none | full')size = flag.Float64('size', 30, 'font size in points')spacing = flag.Float64('spacing', 1.5, 'line spacing (e.g. 2 means double spaced)')wonb = flag.Bool('whiteonblack', false, 'white text on a black background'))var text = []string{'地支:沈阳市某区某镇某街道某楼某','姓名:王永飞','电话:1232131231232',}func main() {file, err := os.Create('dst.jpg')if err != nil {fmt.Println(err)}defer file.Close()file1, err := os.Open('/Users/zhiliao/zhiliao/gopro/go_safly/src/qr.png')if err != nil {fmt.Println(err)}defer file1.Close()img, _ := png.Decode(file1)//尺寸img = resize.Resize(314, 314, img, resize.Lanczos3)jpg := image.NewRGBA(image.Rect(0, 0, 827, 1169))fontRender(jpg)draw.Draw(jpg, img.Bounds().Add(image.Pt(60, 150)), img, img.Bounds().Min, draw.Src) //截取图片的一部分draw.Draw(jpg, img.Bounds().Add(image.Pt(435, 150)), img, img.Bounds().Min, draw.Src) //截取图片的一部分draw.Draw(jpg, img.Bounds().Add(image.Pt(60, 610)), img, img.Bounds().Min, draw.Src) //截取图片的一部分draw.Draw(jpg, img.Bounds().Add(image.Pt(435, 610)), img, img.Bounds().Min, draw.Src) //截取图片的一部分png.Encode(file, jpg)}func fontRender(jpg *image.RGBA) {flag.Parse()fontBytes, err := ioutil.ReadFile(*fontfile)if err != nil {log.Println(err)return}f, err := freetype.ParseFont(fontBytes)if err != nil {log.Println(err)return}fg, bg := image.Black, image.White//ruler := color.RGBA{0xdd, 0xdd, 0xdd, 0xff}//if *wonb {//fg, bg = image.White, image.Black//ruler = color.RGBA{0x22, 0x22, 0x22, 0xff}//}draw.Draw(jpg, jpg.Bounds(), bg, image.ZP, draw.Src)c := freetype.NewContext()c.SetDPI(*dpi)c.SetFont(f)c.SetFontSize(*size)c.SetClip(jpg.Bounds())c.SetDst(jpg)c.SetSrc(fg)switch *hinting {default:c.SetHinting(font.HintingNone)case 'full':c.SetHinting(font.HintingFull)}//Draw the guidelines.//for i := 0; i < 200; i++ {//jpg.Set(10, 10+i, ruler)//jpg.Set(10+i, 10, ruler)//}// Draw the text.pt := freetype.Pt(200, 10+int(c.PointToFixed(*size)>>6))for _, s := range text {_, err = c.DrawString(s, pt)if err != nil {log.Println(err)return}pt.Y += c.PointToFixed(*size * *spacing)}}func Cors() gin.HandlerFunc {return func(c *gin.Context) {method := c.Request.Method //请求方法origin := c.Request.Header.Get('Origin') //请求头部var headerKeys []string // 声明请求头keysfor k, _ := range c.Request.Header {headerKeys = append(headerKeys, k)}headerStr := strings.Join(headerKeys, ', ')if headerStr != '' {headerStr = fmt.Sprintf('access-control-allow-origin, access-control-allow-headers, %s', headerStr)} else {headerStr = 'access-control-allow-origin, access-control-allow-headers'}if origin != '' {c.Writer.Header().Set('Access-Control-Allow-Origin', '*')c.Header('Access-Control-Allow-Origin', '*') // 这是允许访问所有域c.Header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PUT, DELETE,UPDATE') //服务器支持的所有跨域请求的方法,为了避免浏览次请求的多次’预检’请求// header的类型c.Header('Access-Control-Allow-Headers', 'Authorization, Content-Length, X-CSRF-Token, Token,session,X_Requested_With,Accept, Origin, Host, Connection, Accept-Encoding, Accept-Language,DNT, X-CustomHeader, Keep-Alive, User-Agent, X-Requested-With, If-Modified-Since, Cache-Control, Content-Type, Pragma')// 允许跨域设置 可以返回其他子段c.Header('Access-Control-Expose-Headers', 'Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar') // 跨域关键设置 让浏览器可以解析c.Header('Access-Control-Max-Age', '172800') // 缓存请求信息 单位为秒c.Header('Access-Control-Allow-Credentials', 'false') // 跨域请求是否需要带cookie信息 默认设置为truec.Set('content-type', 'application/json') // 设置返回格式是json}//放行所有OPTIONS方法if method == 'OPTIONS' {c.JSON(http.StatusOK, 'Options Request!')}// 处理请求c.Next() // 处理请求}}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持优爱好网。如有错误或未考虑完全的地方,望不吝赐教。

标签: Golang
相关文章: