You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让水印均匀覆盖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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 02:10:54