38

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
  • 996
  • 1
  • 7
  • 12

7 Answers7

45

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;
tim harrison
  • 996
  • 1
  • 7
  • 12
  • 1
    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. – suryakiran Apr 12 '11 at 07:56
  • 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. – Andreas Rehm Sep 06 '12 at 08:55
  • Using RazorPDF, BaseColor.LIGHT_GRAY is missing. In this case, chenge to `under.SetColorFill(iTextSharp.text.pdf.CMYKColor.LIGHT_GRAY);` – Leonel Sanches da Silva Oct 08 '13 at 19:00
  • 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? – Kala J Jul 10 '14 at 19:51
  • 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 :) – slee423 Apr 24 '19 at 15:45
  • How to use a pdf instead of an image or text for a watermark? – Ram Apr 04 '23 at 09:02
21

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! :)

NoOne
  • 3,851
  • 1
  • 40
  • 47
  • 3
    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. – mkl Jan 25 '14 at 22:15
  • 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](https://github.com/taithienbo/PDFStampDotNetCoreExample) – Tai Bo Jul 20 '19 at 16:55
  • 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. – Marius Popa Oct 22 '19 at 08:12
  • @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. – NoOne Oct 23 '19 at 19:42
  • @NoOne How to set multiple lines for long string? – Suresh U Feb 12 '20 at 10:02
  • @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: https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-obtain-font-metrics . If the text is rotated, you might have to use some extra math to get the placement. – NoOne Feb 13 '20 at 17:38
9
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);
trinalbadger587
  • 1,905
  • 1
  • 18
  • 36
kirk
  • 997
  • 9
  • 14
6

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);
                }
            }
        }
Connor Williams
  • 319
  • 2
  • 13
3

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();
LittleBobbyTables - Au Revoir
  • 32,008
  • 25
  • 109
  • 114
Curt
  • 31
  • 1
3

Can't you just lay down the watermark on each page after you've made it?

plinth
  • 48,267
  • 11
  • 78
  • 120
1

In iTextSharp you should be able to programmatically add a watermark e.g.

Watermark watermark = new Watermark(Image.getInstance("watermark.jpg"), 200, 420);
document.Add(watermark);
James
  • 80,725
  • 18
  • 167
  • 237
  • 2
    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 :( – tim harrison Mar 03 '10 at 17:44
  • 1
    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? – James Mar 03 '10 at 18:59