Skip to content
Add Image Watermark

Add Image Watermark

PdfPage.AddWatermarkImage puts an image over a page’s existing content, which is the usual way to apply a logo or a scanned stamp to a whole document. It takes an already-built model.XObjectImage rather than a file path, so loading and embedding the image is your job, and doing it once lets the same object be reused across every page.

Doing it

goImg, _, err := image.Decode(watermarkFile)
if err != nil {
    return err
}

img, err := model.DefaultImageHandler{}.NewImageFromGoImage(goImg)
if err != nil {
    return err
}

xImage, err := model.NewXObjectImageFromImage(img, nil, nil)
if err != nil {
    return err
}

for i := 0; i < numPages; i++ {
    page, err := pdfReader.GetPage(i + 1)
    if err != nil {
        return err
    }
    err = page.AddWatermarkImage(xImage, model.WatermarkImageOptions{
        Alpha:      0.5,
        FitToWidth: true,
    })
    if err != nil {
        return err
    }
}

Passing nil for the colorspace and encoder lets NewXObjectImageFromImage choose: a JPEG source is embedded as-is, anything else gets an encoder derived from the pixel data.

Build the XObjectImage outside the page loop. Every page then references the same stream object, so the output holds one copy of the image data no matter how many pages carry the watermark.

Nothing is written by these calls. Convert the reader with pdfReader.ToWriter(nil) and call WriteToFile on the writer.

Sizing

The two boolean options combine into four behaviors, and only one of them is generally what you want.

FitToWidthPreserveAspectRatioResult
falsefalseWidth is the image’s pixel count in points; height is stretched to the full page height. Horizontally centered.
truefalseStretched to fill the whole page, distorted.
falsetrueDrawn at its pixel size in points, aspect preserved, centered both ways.
truetrueScaled to the page width, aspect preserved, vertically centered.

PreserveAspectRatio: false means the height is set to the page height regardless of the image, so the common mistake, WatermarkImageOptions{Alpha: 0.5} and nothing else, gives a vertically stretched image at its original pixel width.

FitToWidth: false also means the drawn width is the image’s pixel width read as points. A 2000 pixel logo becomes 2000 points wide, more than three times a letter page. Either set FitToWidth or resample the image before embedding it.

Limitations

Alpha has no default. A zero value is written to the graphics state as an opacity of 0, so model.WatermarkImageOptions{} adds a completely invisible watermark and returns no error. This differs from WatermarkTextOptions, which substitutes 0.5 for a zero Alpha.

Angle rotates the form XObject about its own origin, and the offsets that re-center it afterwards are only approximate. The vertical correction is computed from the image’s pixel width rather than its drawn width, so a nonzero Angle combined with FitToWidth on a high-resolution image pushes the watermark a long way off center. Check the output whenever you rotate.

Calling the method twice on a page stacks two watermarks. There is no replace, and no check for an existing one.

The watermark goes on top of everything, including form fields and annotations, since it is appended after the existing content stream. It cannot be placed underneath the page content with this method.

Run the example

The example applies one image to every page of an input document and writes the result through a PdfWriter. addWatermarkImage is the whole of it; the image is loaded once before the page loop.

git clone https://github.com/unidoc/unipdf-examples.git
cd unipdf-examples/watermarks
go run pdf_watermark_image.go input.pdf watermark.jpg output.pdf

If this is your first time using UniPDF, follow the getting started guide to create an API key and set up your development environment.

View the full source
Last updated on