ImageGear .NET v24.12 - Updated
Watermarks in a PDF
User Guide > How to Work with... > PDF > How to... > Manage PDF Content > Watermarks in a PDF

A digital watermark is any auxiliary data embedded into a document used for subsequent identification. Watermarks may provide contextual support to an author's content, but are otherwise independent of that content. For example, stamping a page as "DRAFT" or "CONFIDENTIAL" imparts meaning beyond any text or drawings present on that page.

For sample code, refer to the AddWatermarkToPDFPage sample. See Samples.

PDF documents may express a digital watermark using any of the following techniques:

Each of these techniques can be accomplished using ImageGear, with varying complexity and completeness. See the following subsections:

Annotations

Annotations are added to PDF pages by using the ImageGear.ART namespace, which supports a subset of available PDF annotations. Refer to PDF Annotations for the mapping used with PDF annotations.

ImageGear .NET does not export ART Marks to PDF Watermark annotations. Apps that require explicit PDF Watermark annotation support may use the basic object classes to create a PDF Watermark annotation object and appearance streams, as outlined in the PDF Reference under topic "Watermark Annotations".

The following sample code demonstrates creating a Text ART mark and exporting to a PDF page as a FreeText annotation:

C#
Copy Code
using System;

using ImageGear.Core;
using ImageGear.Formats;
using ImageGear.ART;
using ImageGear.Windows.Forms;

public static void AddFreeTextAnnotationToPageView(ImGearPageView igPageView, String text)
{
   const Int32 BORDER_THICKNESS_IN_PIXELS = 2;
   const Int32 BOX_OPACITY_TRANSLUCENT = 100;
   const Int32 TEXT_OPACITY_TRANSLUCENT = 100;
   const Int32 MAX_HEIGHT_IN_IMAGE_PIXELS = 200;
   Int32 boxWidth = igPageView.Page.DIB.Width;
   Int32 boxHeight = Math.Min(MAX_HEIGHT_IN_IMAGE_PIXELS, igPageView.Page.DIB.Height);

   // Choose a font size that is 85% height of the box height.
   float fontSize = Convert.ToSingle(boxHeight) * 0.85f;

   // Create a text annotation.
   ImGearARTText textAnnotation = new ImGearARTText(new ImGearRectangle(0, 0, boxWidth, boxHeight),
       text, new ImGearRGBQuad(255, 0, 0), ImGearARTTextType.DIRECT_TEXT, TEXT_OPACITY_TRANSLUCENT,
       new ImGearRGBQuad(255, 0, 0), new ImGearARTBorder(new ImGearRGBQuad(0, 0, 255),
       BORDER_THICKNESS_IN_PIXELS), true,
       BOX_OPACITY_TRANSLUCENT, "Arial", fontSize, ImGearARTFontStyles.Bold, null, null);

   // Recover the page view ART page.
   ImGearARTPage igArtPage = (ImGearARTPage)igPageView.Display.ARTPage;

   // Add the text annotation to the page view ART page.
   igArtPage.AddMark(textAnnotation, ImGearARTCoordinatesType.IMAGE_COORD);

   // Export the ART Text mark to the PDF page as a FreeText annotation.
   ImGearART.SavePage(igArtPage, igPageView.Page);

   // Cause the page view to redraw.
   igPageView.Invalidate();
}
VB.NET
Copy Code
   Public Shared Sub AddFreeTextAnnotationToPageView(ByVal igPageView As ImGearPageView, ByVal text As [String])
       Const BORDER_THICKNESS_IN_PIXELS As Int32 = 2
       Const BOX_OPACITY_TRANSLUCENT As Int32 = 100
       Const TEXT_OPACITY_TRANSLUCENT As Int32 = 100
       Const MAX_HEIGHT_IN_IMAGE_PIXELS As Int32 = 200
       Dim boxWidth As Int32 = igPageView.Page.DIB.Width
       Dim boxHeight As Int32 = Math.Min(MAX_HEIGHT_IN_IMAGE_PIXELS, igPageView.Page.DIB.Height)

       ' Choose a font size that is 85% height of the box height.
       Dim fontSize As Single = Convert.ToSingle(boxHeight) * 0.85F

       ' Create a text annotation.
       Dim textAnnotation As New ImGearARTText(New ImGearRectangle(0, 0, boxWidth, boxHeight), text, New ImGearRGBQuad(255, 0, 0), ImGearARTTextType.DIRECT_TEXT, TEXT_OPACITY_TRANSLUCENT, New ImGearRGBQuad(255, 0, 0), _
        New ImGearARTBorder(New ImGearRGBQuad(0, 0, 255), BORDER_THICKNESS_IN_PIXELS), True, BOX_OPACITY_TRANSLUCENT, "Arial", fontSize, ImGearARTFontStyles.Bold, _
        Nothing, Nothing)

       ' Recover the page view ART page.
       Dim igArtPage As ImGearARTPage = DirectCast(igPageView.Display.ARTPage, ImGearARTPage)

       ' Add the text annotation to the page view ART page.
       igArtPage.AddMark(textAnnotation, ImGearARTCoordinatesType.IMAGE_COORD)

       ' Export the ART Text mark to the PDF page as a FreeText annotation.
       ImGearART.SavePage(igArtPage, igPageView.Page)

       ' Cause the page view to redraw.
       igPageView.Invalidate()
   End Sub

Refer to the AddWatermarkToPDFPage sample. See Samples.

See the PDF Reference topic "Annotations" for additional information.

Page Graphics

Watermarks expressed as PDF page graphics are added using the ImageGear.Formats.PDF namespace. This namespace provides classes for content editing modeled after graphics objects and operators described in the PDF Reference.

The following snippet demonstrates placing a 24-bpp RGB bitmap at the bottom left corner of a PDF page:

Adjustments are required to successfully add images utilizing other pixel formats and color spaces in accordance to the image XObject discussion in the PDF reference.

C#
Copy Code
using System;
using System.IO;
using System.Windows.Forms;

using ImageGear.Core;
using ImageGear.Formats;
using ImageGear.Formats.PDF;

public static void AddRGB24ImageToPdfPage(ImGearPDFPage igPDFPage, ImGearPage igImage)
{
   const Int32 BITS_PER_BYTE = 8;
   try
   {
       // Acquire the PDF page content.
       using (ImGearPDEContent content = igPDFPage.GetContent())
       {
           // Extract pixel data from RGB24 image.
           Int32 strideInBytes = (igImage.DIB.Width * igImage.DIB.BitDepth + BITS_PER_BYTE - 1)
                                   / BITS_PER_BYTE;
           Byte[] pixelData = new Byte[strideInBytes * igImage.DIB.Height];
           for (int row = 0; row < igImage.DIB.Height; row++)
           {
               ImGearArrayRef raster = igImage.DIB.GetRaster(row);
               Array.Copy((Byte[])raster.Content, raster.Offset, pixelData, row * strideInBytes,
                   strideInBytes);
           }

           // Create a DeviceRGB PDE Colorspace for RGB24 image.
           ImGearPDEColorSpace igPDEColorSpace = new ImGearPDEColorSpace(
               new ImGearPDFAtom("DeviceRGB"));

           // Create a PDE Image attributes for image Form XObject
           ImGearPDEImageAttrs igPDEImageAttrs = new ImGearPDEImageAttrs();
           igPDEImageAttrs.Width = igImage.DIB.Width;
           igPDEImageAttrs.Height = igImage.DIB.Height;
           igPDEImageAttrs.Flags = ImGearPDEImageAttrFlags.EXTERNAL;
           igPDEImageAttrs.BitsPerComponent = 8;

           // Create a transform matrix that places the image at the bottom-left corner
           // of the PDF page.
           ImGearPDFFixedMatrix igPDETransformMatrix = new ImGearPDFFixedMatrix();
           igPDETransformMatrix.A = ImGearPDF.DoubleToFixed(((double)igImage.DIB.Width));
           igPDETransformMatrix.B = (int)ImGearPDFFixedValues.ZERO;
           igPDETransformMatrix.C = (int)ImGearPDFFixedValues.ZERO;
           igPDETransformMatrix.D = ImGearPDF.DoubleToFixed(((double)igImage.DIB.Height));
           igPDETransformMatrix.H = (int)ImGearPDFFixedValues.ZERO;
           igPDETransformMatrix.V = (int)ImGearPDFFixedValues.ZERO;

           // Create a filter to compress RGB24 pixel data using the discrete cosine
           // transform (DCT) technique based on the JPEG standard.
           ImGearPDEFilterArray igPDEFilterArray = new ImGearPDEFilterArray();
           ImGearPDFBasDict igDict = new ImGearPDFBasDict((ImGearPDFDocument)igPDFPage.Document,
                                        false, 3);
           igDict.PutInt(new ImGearPDFAtom("Columns"), false, igImage.DIB.Width);
           igDict.PutInt(new ImGearPDFAtom("Rows"), false, igImage.DIB.Height);
           igDict.PutInt(new ImGearPDFAtom("Colors"), false, 3);
           ImGearPDEFilterSpec igPDEFilterSpec = new ImGearPDEFilterSpec();
           igPDEFilterSpec.Name = new ImGearPDFAtom("DCTDecode");
           igPDEFilterSpec.DecodeParams = igDict;
           igPDEFilterSpec.EncodeParams = igDict;
           igPDEFilterArray.AddSpec(igPDEFilterSpec);

           // Create the PDE image.
           using (ImGearPDEImage igPDEImage = new ImGearPDEImage(igPDEImageAttrs,
                   igPDETransformMatrix, ImGearPDEImageDataFlags.DECODED, igPDEColorSpace, null,
                   igPDEFilterArray, null, pixelData))
           {
               // Add the PDEImage to the PDF page graphics.
               content.AddElement((int)ImGearPDEInsertElement.AFTER_LAST, igPDEImage);
               igPDFPage.SetContent();

               // Force refresh of PDF page content.
               igPDFPage.ResetDisplayCache();
           }
       }
   }
   finally
   {
       // Release the PDF page content.
       igPDFPage.ReleaseContent();
   }
}
VB.NET
Copy Code
Public Shared Sub AddRGB24ImageToPdfPage(ByVal igPDFPage As ImGearPDFPage, ByVal igImage As ImGearPage)
       Const BITS_PER_BYTE As Int32 = 8
       Try
           ' Acquire the PDF page content.
           Using content As ImGearPDEContent = igPDFPage.GetContent()
               ' Extract pixel data from RGB24 image.
               Dim strideInBytes As Int32 = (igImage.DIB.Width * igImage.DIB.BitDepth + BITS_PER_BYTE - 1) / BITS_PER_BYTE
               Dim pixelData As [Byte]() = New [Byte](strideInBytes * igImage.DIB.Height - 1) {}
               For row As Integer = 0 To igImage.DIB.Height - 1
                   Dim raster As ImGearArrayRef = igImage.DIB.GetRaster(row)
                   Array.Copy(DirectCast(raster.Content, [Byte]()), raster.Offset, pixelData, row * strideInBytes, strideInBytes)
               Next

               ' Create a DeviceRGB PDE Colorspace for RGB24 image.
               Dim igPDEColorSpace As New ImGearPDEColorSpace(New ImGearPDFAtom("DeviceRGB"))

               ' Create a PDE Image attributes for image Form XObject
               Dim igPDEImageAttrs As New ImGearPDEImageAttrs()
               igPDEImageAttrs.Width = igImage.DIB.Width
               igPDEImageAttrs.Height = igImage.DIB.Height
               igPDEImageAttrs.Flags = ImGearPDEImageAttrFlags.EXTERNAL
               igPDEImageAttrs.BitsPerComponent = 8

               ' Create a transform matrix that places the image at the bottom-left corner
               ' of the PDF page.
               Dim igPDETransformMatrix As New ImGearPDFFixedMatrix()
               igPDETransformMatrix.A = ImGearPDF.DoubleToFixed(CDbl(igImage.DIB.Width))
               igPDETransformMatrix.B = CInt(ImGearPDFFixedValues.ZERO)
               igPDETransformMatrix.C = CInt(ImGearPDFFixedValues.ZERO)
               igPDETransformMatrix.D = ImGearPDF.DoubleToFixed(CDbl(igImage.DIB.Height))
               igPDETransformMatrix.H = CInt(ImGearPDFFixedValues.ZERO)
               igPDETransformMatrix.V = CInt(ImGearPDFFixedValues.ZERO)

               ' Create a filter to compress RGB24 pixel data using the discrete cosine
               ' transform (DCT) technique based on the JPEG standard.
               Dim igPDEFilterArray As New ImGearPDEFilterArray()
               Dim igDict As New ImGearPDFBasDict(DirectCast(igPDFPage.Document, ImGearPDFDocument), False, 3)
               igDict.PutInt(New ImGearPDFAtom("Columns"), False, igImage.DIB.Width)
               igDict.PutInt(New ImGearPDFAtom("Rows"), False, igImage.DIB.Height)
               igDict.PutInt(New ImGearPDFAtom("Colors"), False, 3)
               Dim igPDEFilterSpec As New ImGearPDEFilterSpec()
               igPDEFilterSpec.Name = New ImGearPDFAtom("DCTDecode")
               igPDEFilterSpec.DecodeParams = igDict
               igPDEFilterSpec.EncodeParams = igDict
               igPDEFilterArray.AddSpec(igPDEFilterSpec)

               ' Create the PDE image.
               Using igPDEImage As New ImGearPDEImage(igPDEImageAttrs, igPDETransformMatrix, ImGearPDEImageDataFlags.DECODED, igPDEColorSpace, Nothing, igPDEFilterArray, _
                Nothing, pixelData)
                   ' Add the PDEImage to the PDF page graphics.
                   content.AddElement(CInt(ImGearPDEInsertElement.AFTER_LAST), igPDEImage)
                   igPDFPage.SetContent()

                   ' Force refresh of PDF page content.
                   igPDFPage.ResetDisplayCache()
               End Using
           End Using
       Finally
           ' Release the PDF page content.
           igPDFPage.ReleaseContent()
       End Try
   End Sub

Refer to the AddWatermarkToPDFPage sample. See Samples.

See the PDF Reference topic "Graphics" for additional information.

See ImGearPDFFixedMatrix Class for information about defining a transformation matrix.

Optional Content Groups

Watermark optional content can be added to PDF pages using the ImageGear.Formats.PDF namespace, which provides classes for layer management.

Refer to PDF Layers for guidance on using the ImageGear.Formats.PDF namespace to manage watermarks with Optional Content Groups (OCG).

To designate a watermark intended for printing, add an Optional Content Print Usage dictionary with Subtype name "Watermark" to that OCG, represented with an ImGearPDFLayer object:

C#
Copy Code
// Create Print Usage dictionary
ImGearPDFBasDict igPrintUsageDict = new ImGearPDFBasDict( igPDFDocument, false, 2 );
igPrintUsageDict.PutName( new ImGearPDFAtom("Subtype"), false, new ImGearPDFAtom( "Watermark" ));
igPrintUsageDict.PutName( new ImGearPDFAtom("PrintState"), false, new ImGearPDFAtom( "ON" ));

// Add Print Usage dictionary to OCG group named "WatermarkLayer"
ImGearPDFLayer igWatermarkLayer = igPDFDocument.CreateLayer( "WatermarkLayer" );
igWatermarkLayer.SetUsageInfo( new ImGearPDFAtom("Print"), igPrintUsageDict );

See the PDF Reference topic "Making Graphical Content Optional" for additional information.

Pagination Artifacts

Watermarks expressed as Pagination Artifacts can be added using the ImageGear.Formats.PDF namespace, which provides classes to create a marked-content sequence in accordance with the PDF Reference.

This usage is not demonstrated in any sample presently.

See the PDF Reference topic "Real Content and Artifacts" for additional information.