Search code examples
itextwatermarkitext7

Itextsharp 7 - Scaled and Centered Image as watermark


I started using itextsharp 7 a few days ago, i used to work with itextsharp 5 for years .

I don't manage to add a scaled image at the center of the page as watermark with itext7.

My code with itextsharp 5 :

using (PdfStamper pdfStamper = new PdfStamper(pdfReader, memoryStream))
{
    for (int pageIndex = 1; pageIndex <= pdfReader.NumberOfPages; pageIndex++)
    {
        pdfStamper.FormFlattening = false;

        iTextSharp.text.Rectangle pageRectangle = pdfReader.GetPageSizeWithRotation(pageIndex);
        PdfContentByte pdfData = pdfStamper.GetOverContent(pageIndex);

        PdfGState graphicsState = new PdfGState();
        graphicsState.FillOpacity = 0.4F;
        pdfData.SetGState(graphicsState);

        pdfData.BeginText();

        Image imageWM = Image.GetInstance(image_WM_Path);
        float width = pageRectangle.Width;
        float height = pageRectangle.Height;
        //scale image
        imageWM.ScaleToFit(width / 3, height / 3);
        //center image
        imageWM.SetAbsolutePosition(width / 2 - imageWM.ScaledWidth / 2, height / 2 - imageWM.ScaledHeight / 2);

        pdfData.AddImage(imageWM);
        pdfData.EndText();                       
    }
    pdfStamper.Close();
    return memoryStream.ToArray();
}

Here is with itextsharp 7 (code based on the itext 7 examples):

   PdfDocument pdfDoc = new PdfDocument(new PdfReader(sourceFile), new PdfWriter(destinationPath));
Document document = new Document(pdfDoc);
PdfCanvas over;
PdfExtGState gs1 = new PdfExtGState();
gs1.SetFillOpacity(0.5f);
int n = pdfDoc.GetNumberOfPages();
Rectangle pagesize;
float x, y;

ImageData img = ImageDataFactory.Create(image_WM_Path);
float w = img.GetWidth();
float h = img.GetHeight();

for (int i = 1; i <= n; i++)
{
    PdfPage pdfPage = pdfDoc.GetPage(i);
    pagesize = pdfDoc.GetPage(i).GetPageSize();
    pdfPage.SetIgnorePageRotationForContent(true);

    x = (pagesize.GetLeft() + pagesize.GetRight()) / 2;
    y = (pagesize.GetTop() + pagesize.GetBottom()) / 2;
    over = new PdfCanvas(pdfDoc.GetPage(i));

    over.SaveState();
    over.SetExtGState(gs1);

    over.AddImage(img, w, 0, 0, h, x - (w / 2), y - (h / 2), true);

    over.RestoreState();
}
document.Close();
pdfDoc.Close();

The image is centered but i dont manage to scale it with the AddImage method.

Maybe it is easily done but i am struggling with this.

Any help appreciated.


Solution

  • I have adapted your example to Java, but that shouldn't matter much since it's the Math that is important:

    public static final String SRC = "src/main/resources/pdfs/hello.pdf";
    public static final String DEST = "results/text/watermark.pdf";
    public static final String IMG = "src/main/resources/img/mascot.png";
    
    public static void main(String[] args) throws IOException {
        File file = new File(DEST);
        file.getParentFile().mkdirs();
        new Watermark().createPdf(SRC, DEST);
    }
    public void createPdf(String src, String dest) throws IOException {
        PdfDocument pdfDoc = new PdfDocument(
                new PdfReader(src), new PdfWriter(dest));
        Document document = new Document(pdfDoc);
        PdfCanvas over;
        PdfExtGState gs1 = new PdfExtGState();
        gs1.setFillOpacity(0.5f);
        int n = pdfDoc.getNumberOfPages();
        Rectangle pagesize;
        ImageData img = ImageDataFactory.create(IMG);
        float iW = img.getWidth();
        float iH = img.getHeight();
        float pW, pH, sW, sH, f, x, y;
    
        for (int i = 1; i <= n; i++)
        {
            PdfPage pdfPage = pdfDoc.getPage(i);
            pagesize = pdfPage.getPageSize();
    
            pW = pagesize.getWidth();
            pH = pagesize.getHeight();
            f = (pW / iW) * 0.5f;
            sW = iW * f;
            sH = iH * f;
            x = pagesize.getLeft() + (pW / 2) - (sW / 2);
            y = pagesize.getBottom() + (pH / 2) - (sH / 2);
    
            over = new PdfCanvas(pdfDoc.getPage(i));
            over.saveState();
            over.setExtGState(gs1);
            over.addImage(img, sW, 0, 0, sH, x, y);
            over.restoreState();
        }
        document.close();
        pdfDoc.close();
    }
    

    The result of this code looks like this:

    enter image description here

    That looks exactly the way I expect it.

    Some explanation.

    • I have an image mascot.png with dimensions iW x iH.
    • I have pages with dimensions pW x pH.
    • I want to scale the image so that it takes 50% of the width, hence I create a variable f with value 0.5f (50%) x ``(pW / iW)`.
    • I apply the factor f to the initial values of the images, resulting in the scaled dimensions sW x sH.
    • I define an offset for the image (x, y) by subtracting half of the scaled width and height of the middle of the page.

    Now I have the values I need for the addImage() method: over.addImage(img, sW, 0, 0, sH, x, y);

    Note: you were adding the images as an inline image. That's a bad idea because it leads to bloated PDF files, especially in the case of watermarks. By adding an image as an inline image to each page, you add the image bytes redundantly as many times as there are pages. It's much better to add the image as an Image XObject, in which case the image bytes will be added to the document only once, no matter how many times you use that same image. Please remove the true value from the parameters of the addImage() method (make a before and after PDF, and compare the file size to understand what I mean).