工作中需要提供对图片和PDF文件打上变量文字的水印,查了不少资料,调试了很多,最终开发完成。
代码如下:
package utils import ( "bytes" "fmt" "image" "image/jpeg" "image/png" "io" "math" "mime/multipart" "strings" "github.com/pdfcpu/pdfcpu/pkg/api" "github.com/pdfcpu/pdfcpu/pkg/pdfcpu" "github.com/golang/freetype/truetype" "golang.org/x/image/font/gofont/goregular" "github.com/fogleman/gg" "github.com/pkg/errors" "golang.org/x/image/bmp" "golang.org/x/image/webp" ) const ( Rgba = 0.6 //透明度 Radians = -30 //旋转度 IntervalWidth = 24 //水印间隔宽 IntervalHeight = 100 //水印间隔高 FontSize = 24 ) //scalefactor比例因子:1, 字体大小points:12, 透明度opacity:0.6, 旋转角度rotation:30 const PdfFontConfDesc = "sc:1,points:12,opacity:0.6,rot:30" func AddTextWaterForImage(file multipart.File, fileName string, text string) ([]byte, error) { defer file.Close() //文件转换为image对象 var imageFile image.Image if strings.HasSuffix(fileName, "png") { i, err := png.Decode(file) if err != nil { return nil, err } imageFile = i } if strings.HasSuffix(fileName, "jpeg") || strings.HasSuffix(fileName, "jpg") { i, err := jpeg.Decode(file) if err != nil { return nil, err } imageFile = i } if strings.HasSuffix(fileName, "bmp") { i, err := bmp.Decode(file) if err != nil { return nil, err } imageFile = i } if strings.HasSuffix(fileName, "webp") { i, err := webp.Decode(file) if err != nil { return nil, err } imageFile = i } if imageFile == nil { return nil, errors.New(fmt.Sprintf("invalid file suffix:%s", fileName)) } //将源文件作为底层画布 dc := gg.NewContextForImage(imageFile) //加载字体对象,本身加载16进制字体,无需预先加载 font, err := truetype.Parse(goregular.TTF) if err != nil { return nil, err } //默认字体大小格式等,不设置会有默认值 face := truetype.NewFace(font, &truetype.Options{Size: FontSize}) //设置字体 dc.SetFontFace(face) //设置颜色,透明度 dc.SetRGBA(Rgba, Rgba, Rgba, Rgba) //画布的 x/y轴大小 maxX := dc.Width() maxY := dc.Height() //设置旋转度,后两个字段代表以 画布中心为 旋转点 dc.RotateAbout(gg.Radians(Radians), float64(maxX/2), float64(maxY/2)) //连续水印 textW, _ := dc.MeasureString(text) //文字宽度 width := int(math.Ceil(textW)) for i := -maxX / 2; i <= maxX+maxX/2; i += width + IntervalWidth { for j := -maxY / 2; j <= maxY+maxY/2; j += IntervalHeight { dc.DrawString(text, float64(i), float64(j)) } } // 将生成的图片转换成buffer buffer := bytes.NewBuffer(make([]byte, 0, 512)) err = jpeg.Encode(buffer, dc.Image(), &jpeg.Options{ Quality: jpeg.DefaultQuality, }) if err != nil { return nil, err } return buffer.Bytes(), nil } func AddTextWaterForPdf(file multipart.File, text string) ([]byte, error) { //文字制作为pdf水印 //该工具会保证单行文字全部显示,所以会根据文字长度等比例强行缩放文字大小,所以需要根据传入的文字长度来设置每行的文字次数; count := 4 textLen := len(text) switch { case textLen > 25 && textLen <= 40: count = 3 case textLen > 40 && textLen <= 60: count = 2 case textLen > 60: count = 1 } var sb1 strings.Builder for i := 0; i < count; i++ { sb1.WriteString(text) if i < count-1 { sb1.WriteString(" ") //文字间隔 } } //单行文字制作完成 sb1Str := sb1.String() //拼接成多行文字 var sb2 strings.Builder for i := 0; i < 10; i++ { //最多10行文字 sb2.WriteString(sb1Str) if i < 9 { sb2.WriteString("\n \n \n \n \n \n \n \n \n \n") } } //制作成文字水印 textWm, err := pdfcpu.ParseTextWatermarkDetails(sb2.String(), PdfFontConfDesc, true) if err != nil { return nil, err } //将水印添加到pdf文件并生成新文件 pdfWriter := &pdfWriter{} var writer io.WriteSeeker = pdfWriter err = api.AddWatermarks(file, writer, nil, textWm, nil) if err != nil { return nil, err } return pdfWriter.buf, nil }