添加水印示例
添加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
}
//添加文字水印函數(shù)
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系統(tǒng)庫的中文字體,或者自行下載ttf字體庫,然后加載該字體
6、draw.Draw(jpg, jpg.Bounds(), bg, image.ZP, draw.Src)是進行設置渲染的參數(shù),參數(shù)是畫布、渲染開始的地方、圖片來源、圖片參數(shù)、渲染模式
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{
"地支:沈陽市某區(qū)某鎮(zhèn)某街道某樓某",
"姓名:王永飛",
"電話: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() // 處理請求
}
}
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。如有錯誤或未考慮完全的地方,望不吝賜教。
您可能感興趣的文章:- golang中切片copy復制和等號復制的區(qū)別介紹
- go切片的copy和view的使用方法
- go語言中切片與內存復制 memcpy 的實現(xiàn)操作
- Go語言中的UTF-8實現(xiàn)
- Go語言帶緩沖的通道實現(xiàn)
- go語言求任意類型切片的長度操作
- Go語言切片前或中間插入項與內置copy()函數(shù)詳解