- pdf
import (
"bytes"
"github.com/pdfcpu/pdfcpu/pkg/api"
"github.com/pdfcpu/pdfcpu/pkg/font"
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/model"
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/types"
"github.com/pdfcpu/pdfcpu/pkg/pdfcpu/validate"
"io"
"strconv"
)
func addWatermark(text string, in io.ReadSeeker) (*bytes.Buffer, error) {
desc := hackWM(0)
var wm *model.Watermark
wm, err := api.TextWatermark(text, desc, true, false, types.POINTS)
if err != nil {
return nil, err
}
realFontSize := wm.ScaledFontSize
if realFontSize <= 0 {
realFontSize = wm.FontSize
}
lineHeight := font.LineHeight(wm.FontName, realFontSize)
singleLineWidth := font.TextWidth(text, wm.FontName, realFontSize)
targetLineWidth := singleLineWidth
targetText := text
lineSpace := int(lineHeight)
var ctxCpu *model.Context
ctxCpu, err = readValidateAndOptimize(in, api.LoadConfiguration())
if err != nil {
return nil, err
}
var dms []types.Dim
dms, err = ctxCpu.PageDims()
if err != nil {
return nil, err
}
var wmsSliceMap = map[int][]*model.Watermark{}
for k, dm := range dms {
curPage := k + 1
dy := 0
for dm.Width >= targetLineWidth {
targetText += " " + text
targetLineWidth = font.TextWidth(targetText, wm.FontName, realFontSize)
}
for float64(dy)*-1 < dm.Height {
wm, err = api.TextWatermark(targetText, hackWM(dy), true, false, types.POINTS)
wmsSliceMap[curPage] = append(wmsSliceMap[curPage], wm)
dy -= lineSpace + int(lineHeight)
}
}
out := new(bytes.Buffer)
err = api.AddWatermarksSliceMap(in, out, wmsSliceMap, nil)
if err != nil {
return nil, err
}
return out, nil
}
- jpg、png
package watermark
import (
"bytes"
"fmt"
"github.com/disintegration/imaging"
"github.com/golang/freetype"
"golang.org/x/image/colornames"
"image"
"image/color"
"image/draw"
"image/gif"
"image/jpeg"
"image/png"
"io"
"os"
"github.com/golang/freetype/truetype"
)
func AddTextWatermark2Pic(text string, source io.ReadSeeker, inch float64) ([]byte, error) {
wm, err := NewWatermarker(
WithWatermarkString(text),
WithWatermarkColor(colornames.Map["grey"]),
WithSkew(30),
WithStepX(250),
WithStepY(130),
WithFontPath("src/font/NotoSansSC-Regular.ttf"),
)
if inch == 0 {
inch = 15.6
}
wm.inch = inch
if err != nil {
fmt.Println("创建自定义Watermarker出错:", err)
return nil, err
}
watermarkedData, err := wm.AddWatermark(source, "image/jpeg")
if err != nil {
fmt.Println("添加水印出错:", err)
return nil, err
}
return watermarkedData, nil
}
func main() {
customColor := color.RGBA{R: 255, G: 0, B: 0, A: 80}
wm, err := NewWatermarker(
WithWatermarkString("自定义水印"),
WithWatermarkColor(customColor),
WithSkew(30),
WithStepX(250),
WithStepY(130),
WithFontPath("customFont.ttf"),
)
if err != nil {
fmt.Println("创建自定义Watermarker出错:", err)
return
}
file, err := os.Open("path_to_image.jpg")
if err != nil {
fmt.Println("打开图像文件出错:", err)
return
}
defer file.Close()
watermarkedData, err := wm.AddWatermark(file, "image/jpeg")
if err != nil {
fmt.Println("添加水印出错:", err)
return
}
outputFile, err := os.Create("watermarked_image.jpg")
if err != nil {
fmt.Println("创建输出文件出错:", err)
return
}
defer outputFile.Close()
_, err = outputFile.Write(watermarkedData)
if err != nil {
fmt.Println("写入输出文件出错:", err)
return
}
fmt.Println("水印图像已成功保存为 watermarked_image.jpg")
}
type Watermarker struct {
watermarkString string
watermarkColor *image.Uniform
skew float64
stepX, stepY int
font *truetype.Font
fontSize float64
dpi float64
inch float64
}
func (w *Watermarker) SetDpi(dpi float64) {
w.dpi = dpi
}
type WatermarkerOptions struct {
WatermarkString *string
WatermarkColor *color.RGBA
Skew *float64
StepX, StepY *int
FontPath *string
}
type WatermarkerOptionSetter func(*WatermarkerOptions)
func WithWatermarkString(s string) WatermarkerOptionSetter {
return func(opt *WatermarkerOptions) {
opt.WatermarkString = &s
}
}
func WithWatermarkColor(c color.RGBA) WatermarkerOptionSetter {
return func(opt *WatermarkerOptions) {
opt.WatermarkColor = &c
}
}
func WithSkew(s float64) WatermarkerOptionSetter {
return func(opt *WatermarkerOptions) {
opt.Skew = &s
}
}
func WithStepX(x int) WatermarkerOptionSetter {
return func(opt *WatermarkerOptions) {
opt.StepX = &x
}
}
func WithStepY(y int) WatermarkerOptionSetter {
return func(opt *WatermarkerOptions) {
opt.StepY = &y
}
}
func WithFontPath(path string) WatermarkerOptionSetter {
return func(opt *WatermarkerOptions) {
opt.FontPath = &path
}
}
func NewWatermarker(setters ...WatermarkerOptionSetter) (*Watermarker, error) {
opt := &WatermarkerOptions{}
for _, setter := range setters {
setter(opt)
}
watermarkString := "Default Watermark"
if opt.WatermarkString != nil {
watermarkString = *opt.WatermarkString
}
watermarkColor := color.RGBA{R: 128, G: 128, B: 128, A: 80}
if opt.WatermarkColor != nil {
watermarkColor = *opt.WatermarkColor
}
skew := 22.5
if opt.Skew != nil {
skew = *opt.Skew
}
stepX := 240
if opt.StepX != nil {
stepX = *opt.StepX
}
stepY := 120
if opt.StepY != nil {
stepY = *opt.StepY
}
fontPath := "SourceHanSansCN-Bold.ttf"
if opt.FontPath != nil {
fontPath = *opt.FontPath
}
fontBytes, err := os.ReadFile(fontPath)
if err != nil {
return nil, fmt.Errorf("读取字体文件出错: %w", err)
}
fnt, err := truetype.Parse(fontBytes)
if err != nil {
return nil, fmt.Errorf("解析字体出错: %w", err)
}
return &Watermarker{
watermarkString: watermarkString,
watermarkColor: image.NewUniform(watermarkColor),
skew: skew,
stepX: stepX,
stepY: stepY,
font: fnt,
fontSize: 12,
}, nil
}
func (w *Watermarker) GifAddWaterMark(imgFile io.Reader) ([]byte, error) {
img, err := gif.DecodeAll(imgFile)
if err != nil {
return nil, fmt.Errorf("解码GIF出错: %w", err)
}
newGIF := &gif.GIF{}
for i, frame := range img.Image {
newImg := image.NewPaletted(frame.Bounds(), frame.Palette)
draw.Draw(newImg, newImg.Bounds(), frame, frame.Bounds().Min, draw.Src)
w.writeWatermark(newImg)
newGIF.Image = append(newGIF.Image, newImg)
newGIF.Delay = append(newGIF.Delay, img.Delay[i])
newGIF.Disposal = append(newGIF.Disposal, img.Disposal[i])
}
var buf bytes.Buffer
err = gif.EncodeAll(&buf, newGIF)
if err != nil {
return nil, fmt.Errorf("编码GIF出错: %w", err)
}
return buf.Bytes(), nil
}
func (w *Watermarker) ImageAddWaterMark(imgFile io.Reader, format string) ([]byte, error) {
img, _, err := image.Decode(imgFile)
if err != nil {
return nil, fmt.Errorf("解码图像出错: %w", err)
}
newImg := image.NewNRGBA(img.Bounds())
w.SetDpi(150)
draw.Draw(newImg, newImg.Bounds(), img, img.Bounds().Min, draw.Src)
w.writeWatermark(newImg)
var buf bytes.Buffer
switch format {
case "jpeg", "jpg":
err = jpeg.Encode(&buf, newImg, &jpeg.Options{Quality: 100})
case "png":
err = png.Encode(&buf, newImg)
default:
return nil, fmt.Errorf("不支持的图像格式: %s", format)
}
if err != nil {
return nil, fmt.Errorf("编码图像出错: %w", err)
}
return buf.Bytes(), nil
}
func (w *Watermarker) writeWatermark(newImg draw.Image) {
for y := -w.stepY; y <= newImg.Bounds().Max.Y+w.stepY; y += w.stepY {
for x := -w.stepX; x <= newImg.Bounds().Max.X+w.stepX; x += w.stepX {
offsetX := 0
if (y/w.stepY)%2 == 1 {
offsetX = w.stepX / 2
}
c := freetype.NewContext()
c.SetDPI(w.dpi)
c.SetFont(w.font)
c.SetFontSize(w.fontSize)
c.SetSrc(w.watermarkColor)
textImg := image.NewRGBA(image.Rect(0, 0, len(w.watermarkString)*int(c.PointToFixed(12)>>6), int(c.PointToFixed(12*1.5)>>6)))
c.SetClip(textImg.Bounds())
c.SetDst(textImg)
pt := freetype.Pt(0, int(c.PointToFixed(12)>>6))
_, _ = c.DrawString(w.watermarkString, pt)
rotated := imaging.Rotate(textImg, w.skew, image.Transparent)
draw.Draw(newImg, rotated.Bounds().Add(image.Pt(x+offsetX, y)), rotated, image.Pt(0, 0), draw.Over)
}
}
}
func (w *Watermarker) AddWatermark(file io.Reader, contentType string) ([]byte, error) {
var fileType string
switch contentType {
case "image/jpeg":
fileType = "jpeg"
case "image/jpg":
fileType = "jpg"
case "image/png":
fileType = "png"
case "image/gif":
fileType = "gif"
default:
return nil, fmt.Errorf("不支持的文件类型: %s", contentType)
}
switch fileType {
case "gif":
return w.GifAddWaterMark(file)
default:
return w.ImageAddWaterMark(file, fileType)
}
}