• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    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中切片copy复制和等号复制的区别介绍
    • go切片的copy和view的使用方法
    • go语言中切片与内存复制 memcpy 的实现操作
    • Go语言中的UTF-8实现
    • Go语言带缓冲的通道实现
    • go语言求任意类型切片的长度操作
    • Go语言切片前或中间插入项与内置copy()函数详解
    上一篇:Go语言带缓冲的通道实现
    下一篇:Go语言中的UTF-8实现
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

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