c# itextsharp PDF creation with watermark on each page

62,363

Solution 1

After digging into it I found the best way was to add the watermark to each page as it was created. To do this I created a new class and implemented the IPdfPageEvent interface as follows:

    class PdfWriterEvents : IPdfPageEvent
    {
        string watermarkText = string.Empty;

        public PdfWriterEvents(string watermark) 
        {
            watermarkText = watermark;
        }

        public void OnOpenDocument(PdfWriter writer, Document document) { }
        public void OnCloseDocument(PdfWriter writer, Document document) { }
        public void OnStartPage(PdfWriter writer, Document document) {
            float fontSize = 80;
            float xPosition = 300;
            float yPosition = 400;
            float angle = 45;
            try
            {
                PdfContentByte under = writer.DirectContentUnder;
                BaseFont baseFont = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.EMBEDDED);
                under.BeginText();
                under.SetColorFill(BaseColor.LIGHT_GRAY);
                under.SetFontAndSize(baseFont, fontSize);
                under.ShowTextAligned(PdfContentByte.ALIGN_CENTER, watermarkText, xPosition, yPosition, angle);
                under.EndText();
            }
            catch (Exception ex)
            {
                Console.Error.WriteLine(ex.Message);
            }
        }
        public void OnEndPage(PdfWriter writer, Document document) { }
        public void OnParagraph(PdfWriter writer, Document document, float paragraphPosition) { }
        public void OnParagraphEnd(PdfWriter writer, Document document, float paragraphPosition) { }
        public void OnChapter(PdfWriter writer, Document document, float paragraphPosition, Paragraph title) { }
        public void OnChapterEnd(PdfWriter writer, Document document, float paragraphPosition) { }
        public void OnSection(PdfWriter writer, Document document, float paragraphPosition, int depth, Paragraph title) { }
        public void OnSectionEnd(PdfWriter writer, Document document, float paragraphPosition) { }
        public void OnGenericTag(PdfWriter writer, Document document, Rectangle rect, String text) { }

    }
}

This object is registered to handle the events as follows:

PdfWriter docWriter = PdfWriter.GetInstance(document, new FileStream(outputLocation, FileMode.Create));
PdfWriterEvents writerEvent = new PdfWriterEvents(watermark);
docWriter.PageEvent = writerEvent;

Solution 2

Although Tim's solution seems very nice, I have managed to do the same thing (I believe) using the following code (perhaps iTextSharp was improved a bit since then...):

    private byte[] AddWatermark(byte[] bytes, BaseFont bf)
    {
        using(var ms = new MemoryStream(10 * 1024))
        {
            using(var reader = new PdfReader(bytes))
            using(var stamper = new PdfStamper(reader, ms))
            {
                int times = reader.NumberOfPages;
                for (int i = 1; i <= times; i++)
                {
                    var dc = stamper.GetOverContent(i);
                    PdfHelper.AddWaterMark(dc, AppName, bf, 48, 35, new BaseColor(70, 70, 255), reader.GetPageSizeWithRotation(i));
                }
                stamper.Close();
            }
            return ms.ToArray();
        }
    }

    public static void AddWaterMark(PdfContentByte dc, string text, BaseFont font, float fontSize, float angle, BaseColor color, Rectangle realPageSize, Rectangle rect = null)
    {
        var gstate = new PdfGState { FillOpacity = 0.1f, StrokeOpacity = 0.3f };
        dc.SaveState();
        dc.SetGState(gstate);
        dc.SetColorFill(color);
        dc.BeginText();
        dc.SetFontAndSize(font, fontSize);
        var ps = rect ?? realPageSize; /*dc.PdfDocument.PageSize is not always correct*/
        var x = (ps.Right + ps.Left) / 2;
        var y = (ps.Bottom + ps.Top) / 2;
        dc.ShowTextAligned(Element.ALIGN_CENTER, text, x, y, angle);
        dc.EndText();
        dc.RestoreState();
    }

This will add a watermark on all pages of a PDF document that is provided as a byte array.

(You don't need to do it while creating the PDF.)

It seems working for both landscape and portrait and it probably works for documents with mixed orientations.

Cheers! :)

Solution 3

string WatermarkLocation = "D:\\Images\\superseded.png";

Document document = new Document();
PdfReader pdfReader = new PdfReader(FileLocation);
PdfStamper stamp = new PdfStamper(pdfReader, new FileStream(FileLocation.Replace(".pdf", "[temp][file].pdf"), FileMode.Create));

iTextSharp.text.Image img = iTextSharp.text.Image.GetInstance(WatermarkLocation);
img.SetAbsolutePosition(125, 300); // set the position in the document where you want the watermark to appear (0,0 = bottom left corner of the page)

PdfContentByte waterMark;
for (int page = 1; page <= pdfReader.NumberOfPages; page++)
{
    waterMark = stamp.GetOverContent(page);
    waterMark.AddImage(img);
}
stamp.FormFlattening = true;
stamp.Close();

// now delete the original file and rename the temp file to the original file
File.Delete(FileLocation);
File.Move(FileLocation.Replace(".pdf", "[temp][file].pdf"), FileLocation);

Solution 4

I used the first solution. I was having trouble getting it to work at first. I getting green underlines under all of my public voids saying that it was going to hide some inherit member.

Basically I realized that I already had added a PagePageEventHelper and I basically just cut out the code for the OnStartPage. ALSO! For some reason I had to make all of my public void's public override void.

  public override void OnStartPage(PdfWriter writer, Document document)
        {
            if (condition)
            {
                string watermarkText = "-whatever you want your watermark to say-";
                float fontSize = 80;
                float xPosition = 300;
                float yPosition = 400;
                float angle = 45;
                try
                {
                    PdfContentByte under = writer.DirectContentUnder;
                    BaseFont baseFont = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.EMBEDDED);
                    under.BeginText();
                    under.SetColorFill(iTextSharp.text.pdf.CMYKColor.LIGHT_GRAY);
                    under.SetFontAndSize(baseFont, fontSize);
                    under.ShowTextAligned(PdfContentByte.ALIGN_CENTER, watermarkText, xPosition, yPosition, angle);
                    under.EndText();
                }
                catch (Exception ex)
                {
                    Console.Error.WriteLine(ex.Message);
                }
            }
        }

Solution 5

Yes, the Watermark class seems to be no more - odd. However in the process of converting to iTextSharp 5.3, I found a simple way to add a watermark to a new document.

MemoryStream mem = new MemoryStream();

Document document = new Document();

PdfWriter writer = PdfWriter.GetInstance(document, mem);

PdfContentByte cb = writer.DirectContent;

document.Open();

document.NewPage();

Image watermark = Image.GetInstance(WATERMARK_URI);

watermark.SetAbsolutePosition(80, 200);

document.Add(watermark);

BaseFont bf = BaseFont.CreateFont(FONT, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);

cb.BeginText();

...

cb.EndText();

document.Close();
Share:
62,363
tim harrison
Author by

tim harrison

I am a man

Updated on July 21, 2021

Comments

  • tim harrison
    tim harrison almost 3 years

    I am trying to programmatically create a number of PDF documents with a watermark on each page using itextsharp (a C# port of Java's itext).

    I am able to do this after the document has been created using a PdfStamper. However this seems to involve re-opening the document reading it and then creating a new document with the watermark on each page.

    Is there a way of doing this during document creation?

  • tim harrison
    tim harrison over 14 years
    Yeah I thought about that; looking online there is a lot of sample code that includes the Watermark class - but I believe this has been removed from the library :(
  • James
    James over 14 years
    I would be shocked if they had removed it. Maybe they have just changed the namespace its in? Have you tried looking in the assembly using reflector?
  • suryakiran
    suryakiran about 13 years
    Sorry, But I didn't get watermark on the created PDF. Could you please let me know, what was the issue. I have used the exact code provided in the above.
  • Andreas Rehm
    Andreas Rehm almost 12 years
    What about landscape pages? I've tried page.Width/2 and page.Height/2 but landscape pages are treated as normal pages -> the aligned Text is not at the page center.
  • Leonel Sanches da Silva
    Leonel Sanches da Silva over 10 years
    Using RazorPDF, BaseColor.LIGHT_GRAY is missing. In this case, chenge to under.SetColorFill(iTextSharp.text.pdf.CMYKColor.LIGHT_GRAY)‌​;
  • mkl
    mkl over 10 years
    If your use case is create a new PDF and add a watermark, you might prefer the one-pass variant presented by Tim because it uses less resources. If your use case is take an existing PDF and watermark it, your solution is appropriate.
  • Kala J
    Kala J almost 10 years
    I get this error: he name 'watermark' does not exist in the current context. How do I correct for it? Currently, I'm working with two classes one that contains the generation of the pdf and your class... how do I have two classes talking to each other so that it knows what watermark is?
  • slee423
    slee423 about 5 years
    How can I add the water mark to the first page? I've a 3 page document and it's only appearing on pages 2 and 3. Would really appreciate a response :)
  • Tai Bo
    Tai Bo almost 5 years
    Thanks. This solution works great for me. I have a sample ASP.NET core project based on this code. If anyone is interested, it's on my github
  • Marius Popa
    Marius Popa over 4 years
    This solution works, thanks, but I use as a watermark an email address and apparently it's clickable. It opens the outlook app when I click it. Any ideas on how to block that? Thanks.
  • NoOne
    NoOne over 4 years
    @MariusPopa Not sure if this will work and it is a bit of a hack, but maybe you can insert some soft-hyphens (Unicode U+00AD) in the email address (e.g. before the at symbol) which will not be visible, but it will make the email detector think that it is not an email. I haven't tried that, but it might work. Most likely there is a better way to do this, but I have't really tried to find it.
  • Suresh U
    Suresh U over 4 years
    @NoOne How to set multiple lines for long string?
  • NoOne
    NoOne over 4 years
    @SureshU I guess you can split the text into lines yourself and position it by code, calling AddWaterMark() once per line. The line height should be dependent on the font used. Maybe this helps in measuring the line height: docs.microsoft.com/en-us/dotnet/framework/winforms/advanced/‌​… . If the text is rotated, you might have to use some extra math to get the placement.