golang中实现给gif、png、jpeg图片添加文字水印
添加水印示例
添加main文件:“watermark/main.go”
package main import ( "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 textwatermark import ( "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 = iota TopRight BottomLeft BottomRight Center ) //字体路径 var Ttf string type Water struct { Pattern string //增加按时间划分的子目录:默认没有时间划分的子目录 } func (w *Water) New(SavePath, fileName string, typeface []FontInfo) error { var subPath string subPath = w.Pattern dirs, 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 error gifimg2, _ := gif.DecodeAll(imgfile) gifs := make([]*image.Paletted, 0) x0 := 0 y0 := 0 yuan := 0 for k, gifimg := range gifimg2.Image { img := image.NewNRGBA(gifimg.Bounds()) if k == 0 { x0 = img.Bounds().Dx() y0 = img.Bounds().Dy() } fmt.Printf("%v, %v\n", img.Bounds().Dx(), img.Bounds().Dy()) if k == 0 && gifimg2.Image[k+1].Bounds().Dx() > x0 && gifimg2.Image[k+1].Bounds().Dy() > y0 { yuan = 1 break } 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) //把添加过文字的新调色板放入调色板slice gifs = 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.Image if 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 = err return nil, err2 } font, err := freetype.ParseFont(fontBytes) if err != nil { err2 = err return nil, err2 } errNum := 1 Loop: for _, t := range typeface { info := t.Message f := 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 := 0 two := 0 switch int(t.Position) { case 0: first = t.Dx two = t.Dy + int(f.PointToFixed(t.Size)>>6) case 1: first = img.Bounds().Dx() - len(info)*4 - t.Dx two = t.Dy + int(f.PointToFixed(t.Size)>>6) case 2: first = t.Dx two = img.Bounds().Dy() - t.Dy case 3: first = img.Bounds().Dx() - len(info)*4 - t.Dx two = img.Bounds().Dy() - t.Dy case 4: first = (img.Bounds().Dx() - len(info)*4) / 2 two = (img.Bounds().Dy() - t.Dy) / 2 default: errNum = 0 break Loop } // fmt.Printf("%v, %v, %v\n", first, two, info) pt := freetype.Pt(first, two) _, err = f.DrawString(info, pt) if err != nil { err2 = err break } } 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 string if 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需要翻墙,如果不能翻墙利用如下方法也可以:
逻辑
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)输出到我们最终生成的图片
效果图
实例
package main import ( "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 // 声明请求头keys for 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信息 默认设置为true c.Set("content-type", "application/json") // 设置返回格式是json } //放行所有OPTIONS方法 if method == "OPTIONS" { c.JSON(http.StatusOK, "Options Request!") } // 处理请求 c.Next() // 处理请求 } }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持猪先飞。如有错误或未考虑完全的地方,望不吝赐教。
相关文章
- 这篇文章主要介绍了golang 调用 php7详解及实例的相关资料,需要的朋友可以参考下...2017-01-15
- 下面小编就为大家带来一篇C#中添加窗口的步骤详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
- 这篇文章主要给大家分享的是pygame游戏进行声音添加的方法,这文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!...2021-10-23
- 这篇文章主要介绍了用golang实现替换某个文件中的字符串操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-25
- 这篇文章主要介绍了市面上短视频(douyin)"去水印"的工具原来是这样实现的,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-03-30
- 这篇文章主要介绍了golang在GRPC中设置client的超时时间,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-27
- 这篇文章主要介绍了解决Golang json序列化字符串时多了\的情况,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-24
Golang中的自定义类型之间的转换的实现(type conversion)
这篇文章主要介绍了Golang中的自定义类型之间的转换的实现(type conversion),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-02-21- 这篇文章主要介绍了golang与php实现计算两个经纬度之间距离的方法,结合实例形式对比分析了Go语言与php进行经纬度计算的相关数学运算技巧,需要的朋友可以参考下...2016-07-29
解决golang处理http response碰到的问题和需要注意的点
这篇文章主要介绍了解决golang处理http response碰到的问题和需要注意的点,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-16- 美图秀秀是一款不错的图片处理软件了,最近看到很多的朋友喜欢把自己的照片处理成电影字幕效果了,下面我们来看看美图秀秀怎么给照片添加电影字幕吧,希望文章对各位有帮助...2016-09-14
- 这篇文章主要介绍了golang http使用踩过的坑与填坑指南,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-27
- 一般空间商提供的服务器空间默认PHP 指令 magic_quotes_gpc是on的,也就是打开的。这时候就可以用stripslashes() 函数删除自动添加的反斜杠。用法就是:比如包含字符串的变量是$str,那么就用stripslashes() 函数处理一下...2013-10-04
- 这篇文章主要介绍了golang文件读取-按指定BUFF大小读取方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-22
- Go(又称Golang)是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。这篇文章给大家介绍golang 正则表达式的相关知识,感兴趣的朋友跟随小编一起看看吧...2021-05-07
- 这篇文章主要介绍了如何在.Net版本UEditor中添加一个普通按钮,需要的朋友可以参考下...2021-09-22
- 这篇文章主要为大家详细介绍了php图片添加文字水印实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2016-03-17
- 这次文章为大家带来的是一个比较实用的示例:利用Golang生成整数随机数,对此感兴趣的可以一起来看看。 php随机数生成一个给定范围的随机数,用 PHP 就太简单不过了,而...2017-07-06
- 红米K30至尊纪念版相机拍照怎么去水印?红米K30至尊纪念版拍照的时候总是自动添加水印,想要关闭水印,该怎么操作呢?下面我们就来看看详细的教程,需要的朋友可以参考下...2020-12-08
- 这篇文章主要介绍了golang DNS服务器的简单实现操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-05-01