工作中需要提供对图片和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
}