2

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.

wallou
  • 529
  • 1
  • 4
  • 21
  • I didn't test this, but what happens if you change `float w = img.GetWidth(); float h = img.GetHeight();` into `float w = img.GetWidth() * f; float h = img.GetHeight() * f;` where `f` is the scaling factor? For instance, make `f` equal to `0.5f` if you want to scale to 50%. – Bruno Lowagie Jul 04 '18 at 15:11
  • Hey, Thanks for your reply. Yes this works for scaling but image is not centered anymore. Furthermore what i would like to achieve is scaling the image accordingly to the document width. My image is 770 width and 582 height. I got documents with 2000+ width where the image is really small and others are 500 width where the image is huge. – wallou Jul 05 '18 at 07:29
  • The image not being centered: that's a matter of simple Math. Make the scaling depending on the document width: that's a matter of simple Math. – Bruno Lowagie Jul 05 '18 at 07:35
  • Yes it is. But i don't understand how to do it with itexts7 AddImage() method. Can you please give details on the parameters of this method http://itextsupport.com/apidocs/itext7/7.0.2/com/itextpdf/kernel/pdf/canvas/PdfCanvas.html. Thats what i should have asked in the first place. – wallou Jul 05 '18 at 07:46
  • `a`, `b`, `c`, `d`, `e`, `f`, `0`, `0`, `1` are the 9 elements in a 3 x 3 transformation matrix. Since we are working in 2d, we only take into account 6 of them, the remaining three are `0`, `0`, `1`. If we don't need rotation, then `b` and `c` are `0`. In that case, `a` is the scaling factor in the `x` direction and `d` is the scaling in the `y` direction, whereas `e` is the translation in the `x` direction and `f` is the translation in the `y` direction. I remember that I learned this in school at the age of 16 (that was 32 years ago). – Bruno Lowagie Jul 05 '18 at 07:55
  • Thanks for your answer. It is not obvious, at least for me, that a 3 x 3 transformation matrix is done with the AddImage method. i thought some of the parameters where coordinates, i realise the parameters names now. – wallou Jul 05 '18 at 08:20
  • My bad for copying some of your examples code wihtout understanding what was happening – wallou Jul 05 '18 at 08:21
  • We didn't "invent" those parameters, we just follow ISO 32000 where it's all explained. – Bruno Lowagie Jul 05 '18 at 08:21
  • it is ok for scaling the image depending on the document size, it was simple math i can cope with ;-) . But to center the image in the transformation matrix depending on the scaling factor i am a bit lost. – wallou Jul 05 '18 at 08:34
  • I've done the Math for you in my answer. – Bruno Lowagie Jul 05 '18 at 10:40
  • I dont know where i upset you. Sorry for the inconvenience. – wallou Jul 05 '18 at 13:23
  • What makes you think I'm upset? Didn't I provide a good answer, even pointing out an error you made but didn't ask about? – Bruno Lowagie Jul 05 '18 at 13:34

2 Answers2

3

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).

Bruno Lowagie
  • 75,994
  • 9
  • 109
  • 165
  • Thank you . I will give it a try as soon as i can. Again excuse me if i have asked the question in a bad way – wallou Jul 05 '18 at 13:25
  • Couldn't try until today. With your explanantions ans tips it works perfectly. Many thanks ! – wallou Jul 18 '18 at 09:32
0

Maybe you can use AddImageFittedIntoRectangle

var x = width / 2 - imageWM.ScaledWidth / 2;
var y = height / 2 - imageWM.ScaledHeight / 2;
var w = width / 3;
var h = height / 3;

over.AddImage(img, new Rectangle(x, y, w, h), false);
Orace
  • 7,822
  • 30
  • 45