如何让水印均匀覆盖PDF整页?附代码求修改方案
解决PDF水印均匀覆盖的问题
我希望通过设置行数、列数、行间距、列间距、字号等参数,让水印均匀覆盖PDF整页,但调整前几项参数时,水印要么无法添加,要么无法均匀覆盖页面。以下是我的代码,请问该如何修改?
func repeatWatermark(watermark string, setting map[string]string) string { column, row, cs, rs := mgr.parserColRow(watermark, setting) var sb1 strings.Builder for i := 0; i < column; i++ { sb1.WriteString(watermark) if i < column-1 { sb1.WriteString(strings.Repeat(" ", cs)) } } sb1Str := sb1.String() var sb2 strings.Builder for i := 0; i < row; i++ { sb2.WriteString(sb1Str) if i < row-1 { sb2.WriteString(strings.Repeat("\n", rs)) } } return sb2.String() } var FontMaps = map[string]string{ "宋体": "SimSun", "微软雅黑": "MicrosoftYaHei", } func setWatermakr(setting map[string]string) string { fontName := "SimSun" fontSize := "20" fontColor := "black" fontOpacity := "0.3" fontRotation := "45" fontScale := "1.1" fontAlign := "center" if len(setting) != 0 { if setting["fontName"] != "" { if fn, ok := FontMaps[setting["fontName"]]; ok { fontName = fn } } if setting["fontSize"] != "" { fontSize = setting["fontSize"] } if setting["fontColor"] != "" { fontColor = setting["fontColor"] } if setting["fontOpacity"] != "" { fontOpacity = setting["fontOpacity"] } if setting["fontRotation"] != "" { fontRotation = setting["fontRotation"] } if setting["fontScale"] != "" { fontScale = setting["fontScale"] } if setting["fontAlign"] != "" { fontAlign = setting["fontAlign"] } } return fmt.Sprintf("fontname:%s,points:%s,color:%s,opacity:%s,rot:%s,scale:%s abs,aligntext:%s", fontName, fontSize, fontColor, fontOpacity, fontRotation, fontScale, fontAlign) } func addWatermark(parentDir string, pdf string, watermark string, setting map[string]string) (string, error) { output := filepath.Join(filepath.Dir(pdf), "watermark.pdf") font.UserFontDir = filepath.Join(parentDir, "fonts") font.LoadUserFonts() // api.LoadConfiguration() // err := api.InstallFonts([]string{"simsun.ttc"}) // if err != nil { // logs.Sugar().Errorf("install font err: %v", err) // return pdf, err // } fonts, err := api.ListFonts() if err != nil { logs.Sugar().Errorf("list fonts err: %v", err) } logs.Sugar().Infof("list fonts:%v", fonts) desc := mgr.setWatermakr(setting) fullWatermark := mgr.repeatWatermark(watermark, setting) wm, err := api.TextWatermark(fullWatermark, desc, true, true, types.POINTS) if err != nil { logs.Sugar().Errorf("TextWatermark err: %v", err) return pdf, err } if err := api.AddWatermarksFile(pdf, output, nil, wm, nil); err != nil { logs.Sugar().Errorf("AddWatermarksFile err: %v", err) return pdf, err } logs.Sugar().Infof("pdf %s add watermark success", pdf) return output, nil }
问题分析
repeatWatermark用空格、换行模拟行列间距,但空格宽度、换行高度和实际字体尺寸、设置的间距参数不匹配,比如水印文字宽度远大于空格,导致列间距严重偏差;换行的默认行高也和设置的行间距无关,直接导致布局错乱。- 没有适配PDF页面的实际尺寸,固定生成行列的方式无法兼容不同大小的页面,要么水印超出边界,要么分布不均。
setWatermakr中的scale:%s abs参数可能存在用法错误,部分PDF处理库中abs缩放需要配合特定上下文,可能引发水印渲染异常。
修改方案
核心思路
放弃字符串拼接模拟行列的方式,改为基于PDF页面尺寸计算每个水印的精确坐标,直接生成分布均匀的水印实例。
具体代码调整
1. 新增页面尺寸获取逻辑
在addWatermark中先获取目标PDF的页面尺寸(需确保使用的API支持该方法,若不支持可更换为unipdf/gopdf等库):
func addWatermark(parentDir string, pdf string, watermark string, setting map[string]string) (string, error) { // ... 原有代码 ... // 获取PDF第一页的尺寸(单位:points,与字号单位一致) pageWidth, pageHeight, err := api.GetPageDimensions(pdf, 1) if err != nil { logs.Sugar().Errorf("get page dimensions err: %v", err) return pdf, err } // ... 后续代码 ... }
2. 重写水印生成逻辑(替换repeatWatermark)
直接计算每个水印的位置,生成独立的水印实例:
func addWatermark(parentDir string, pdf string, watermark string, setting map[string]string) (string, error) { // ... 原有代码(页面尺寸获取) ... desc := mgr.setWatermakr(setting) fontName := "SimSun" if fn, ok := FontMaps[setting["fontName"]]; ok { fontName = fn } fontSize, _ := strconv.ParseFloat(setting["fontSize"], 64) if fontSize == 0 { fontSize = 20 } // 解析行列和间距参数(假设parserColRow返回int类型) column, row, colSpacing, rowSpacing := mgr.parserColRow(watermark, setting) // 获取单个水印的实际渲染尺寸 textWidth, textHeight, err := api.GetTextDimensions(watermark, fmt.Sprintf("fontname:%s,points:%.2f", fontName, fontSize)) if err != nil { logs.Sugar().Errorf("get text dimensions err: %v", err) return pdf, err } // 计算整体偏移,确保水印在页面中居中 totalWidth := textWidth*float64(column) + float64(colSpacing)*float64(column-1) totalHeight := textHeight*float64(row) + float64(rowSpacing)*float64(row-1) startX := (pageWidth - totalWidth) / 2 startY := (pageHeight - totalHeight) / 2 // 批量生成水印 var watermarks []types.Watermark for i := 0; i < row; i++ { for j := 0; j < column; j++ { x := startX + (textWidth + float64(colSpacing))*float64(j) y := startY + (textHeight + float64(rowSpacing))*float64(i) // 生成单个位置的水印 wm, err := api.TextWatermark(watermark, fmt.Sprintf("%s,position:%.2f,%.2f", desc, x, y), true, true, types.POINTS) if err != nil { logs.Sugar().Errorf("create single watermark err: %v", err) return pdf, err } watermarks = append(watermarks, wm) } } // ... 后续代码 ... }
3. 修正setWatermakr的参数问题
去掉可能错误的abs缩放标识(若不需要绝对缩放,可根据API文档调整):
func setWatermakr(setting map[string]string) string { // ... 原有代码 ... return fmt.Sprintf("fontname:%s,points:%s,color:%s,opacity:%s,rot:%s,scale:%s,aligntext:%s", fontName, fontSize, fontColor, fontOpacity, fontRotation, fontScale, fontAlign) }
4. 批量添加水印
替换原有的单个水印添加逻辑:
func addWatermark(parentDir string, pdf string, watermark string, setting map[string]string) (string, error) { // ... 原有代码(批量水印生成) ... // 批量添加水印到PDF if err := api.AddWatermarksFile(pdf, output, nil, watermarks..., nil); err != nil { logs.Sugar().Errorf("AddWatermarksFile err: %v", err) return pdf, err } // ... 原有代码 ... }
额外注意事项
- 若使用的API不支持获取页面/文本尺寸,建议更换为
unipdf、gopdf等功能更完善的PDF处理库。 - 可添加参数校验逻辑,确保计算后的水印总尺寸不超过页面边界。
- 若需要旋转水印,需重新计算旋转后的文本边界框,避免位置偏移。
内容的提问来源于stack exchange,提问作者jiaming wei
相关产品推荐
相关产品推荐

