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.
PDF documents may express a digital watermark using any of the following techniques:
- Annotations: An interactive object associated with a location on the page that displays text or an image. A variety of types may be used including Text, Free text, Rubber stamp. Watermark annotations may be used to print at a fixed position and size on a page, regardless of the dimensions of the printed page.
- Page Graphics: Graphics may include text with specific style information and color, vector line art, sampled images, or shading patterns. Content representing a watermark are inserted directly into the page's content stream, possibly indistinguishable from the original content. Graphics specifically intended for watermarks may be indicated using Optional Content Groups.
- Optional Content Groups (OCG): Collections of graphics in a PDF document can be selectively drawn or hidden by document authors or consumers. An OCG can indicate its graphics are intended as a watermark for printing.
- Pagination Artifacts: Tagged PDF documents distinguish between original content and artifacts of the layout process, including pagination artifacts such as header, footers, page numbers. Content may be specifically designated to be a watermark.
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 or ArtX Components, which support a subset of available PDF annotations. Refer to PDF Marks for the mapping used with PDF annotations.
ImageGear does not export ART Marks to PDF Watermark annotations. Applications that require explicit PDF Watermark annotation support may use the basic object methods to create a PDF Watermark annotation object and appearance streams, as outlined in the PDF Reference under topic "Watermark Annotations". However, ImageGear ART can convert ART marks to PDF annotations and PDF annotations to ART marks. Please see PDF Marks for more information.
The following code snippet demonstrates adding a Text ART mark and exporting to all of the pages in a PDF document as a FreeText annotation:
C |
Copy Code
|
void ApplyTextWatermarkToAllPages(LPCSTR fileIn)
{
const int OpacityTranslucent = 100;
const AT_DIMENSION MaxHeightInImagePixels = 200;
const UINT firstPage = 0;
HMIGEAR document = 0;
unsigned int pageCount = 0;
unsigned int pageNumber = 0;
HIGEAR page = 0;
AT_DIMENSION imageWidth = 0;
AT_DIMENSION imageHeight = 0;
UINT bitsPerPixel = 0;
HIG_ARTX_PAGE artPage = 0;
AT_RECTANGLE rectangle = { 0 };
AT_ARTX_FONT font = { 10.0f, IG_ARTX_FONT_REGULAR, FALSE, "Arial" };
AT_ARTX_BORDER border = { IG_ARTX_PEN_SOLID, 2, { 128, 128, 128, 0 } };
AT_BOOL borderShading = FALSE;
AT_RGBQUAD fillColor = { 0, 0, 255, 0 };
AT_RGBQUAD textColor = { 0, 0, 0, 0 };
HIG_ARTX_TEXT mark = NULL;
HIG_ARTX_TEXT markInPage = NULL;
// Load the multi-page document.
IG_mpi_create(&document, 0);
IG_mpi_file_open((LPSTR)fileIn, document, IG_FORMAT_PDF, IG_MP_OPENMODE_READONLY);
// Get the page count.
IG_mpf_page_count_get(document, &pageCount);
for (pageNumber = 0; pageNumber < pageCount; pageNumber++)
{
// Get a handle to the page.
IG_mpi_page_get(document, pageNumber, &page);
// Get the dimensions of the image and watermark text.
IG_image_dimensions_get(page, &imageWidth, &imageHeight, &bitsPerPixel);
// Create the ArtX page.
IG_ARTX_page_create(&artPage);
// Create the text mark.
rectangle.width = imageWidth;
rectangle.height = min(MaxHeightInImagePixels, imageHeight);
rectangle.x = 1;
rectangle.y = rectangle.height / 2;
// Choose a font size that is 85% height of the box height.
font.fSize = rectangle.height * 0.85f;
IG_ARTX_text_create(&rectangle, "Draft", IG_ARTX_TEXT_DIRECT_TEXT, &font, &border,
borderShading, &fillColor, &textColor, OpacityTranslucent, NULL, NULL, &mark);
// Add the text mark to the page.
IG_ARTX_page_mark_add(artPage, mark, IG_ARTX_COORD_IMAGE, &markInPage);
IG_ARTX_page_save(artPage, page);
IG_ARTX_page_delete(artPage);
IG_ARTX_mark_delete(mark);
}
// Save the file out with the new text watermark.
IG_mpi_file_save("WatermarkText.pdf", document, firstPage, 0, pageCount,
IG_FORMAT_PDF, IG_MPI_SAVE_OVERWRITE);
// Cleanup the document and its pages.
IG_mpi_delete(document);
}
|
C++ |
Copy Code
|
void ApplyTextWatermarkToAllPages(const string& fileIn)
{
const int BorderThicknessInPixels = 2;
const int OpacityTranslucent = 100;
const AT_DIMENSION MaxHeightInImagePixels = 200;
// Load the multi-page document.
HMIGEAR document = 0;
IG_mpi_create(&document, 0);
IG_mpi_file_open((LPSTR)fileIn.c_str(), document, IG_FORMAT_PDF, IG_MP_OPENMODE_READONLY);
// Get the page count.
unsigned int pageCount = 0;
IG_mpf_page_count_get(document, &pageCount);
for (unsigned int pageNumber = 0; pageNumber < pageCount; pageNumber++)
{
// Get a handle to the page.
HIGEAR page = 0;
IG_mpi_page_get(document, pageNumber, &page);
// Get the dimensions of the image and watermark text.
AT_DIMENSION imageWidth = 0;
AT_DIMENSION imageHeight = 0;
UINT bitsPerPixel = 0;
IG_image_dimensions_get(page, &imageWidth, &imageHeight, &bitsPerPixel);
AT_DIMENSION boxWidth = imageWidth;
AT_DIMENSION boxHeight = (std::min)(MaxHeightInImagePixels, imageHeight);
// Choose a font size that is 85% height of the box height.
float fontSize = boxHeight * 0.85f;
// Create the ArtX page.
HIG_ARTX_PAGE artPage = 0;
IG_ARTX_page_create(&artPage);
// Create the text mark.
// X and Y coordinates to place image.
AT_DIMENSION x = 1;
AT_DIMENSION y = (imageHeight - boxHeight) / 2;
AT_RECTANGLE rectangle = {x, y, boxWidth, boxHeight};
AT_ARTX_FONT font = {fontSize, IG_ARTX_FONT_REGULAR, FALSE, "Arial"};
AT_ARTX_BORDER border = {IG_ARTX_PEN_SOLID, BorderThicknessInPixels, {128, 128, 128, 0}};
AT_BOOL borderShading = FALSE;
AT_RGBQUAD fillColor = {0, 0, 255, 0};
AT_RGBQUAD textColor = {0, 0, 0, 0};
HIG_ARTX_TEXT mark = NULL;
IG_ARTX_text_create(&rectangle, "Draft", IG_ARTX_TEXT_DIRECT_TEXT, &font, &border,
borderShading, &fillColor, &textColor, OpacityTranslucent, NULL, NULL, &mark);
// Add the text mark to the page.
HIG_ARTX_TEXT markInPage = NULL;
IG_ARTX_page_mark_add(artPage, mark, IG_ARTX_COORD_IMAGE, &markInPage);
IG_ARTX_page_save(artPage, page);
IG_ARTX_page_delete(artPage);
IG_ARTX_mark_delete(mark);
}
// Save the file out with the new text watermark.
const UINT firstPage = 0;
IG_mpi_file_save("WatermarkText.pdf", document, firstPage, 0, pageCount,
IG_FORMAT_PDF, IG_MPI_SAVE_OVERWRITE);
// Cleanup the document and its pages.
IG_mpi_delete(document);
}
|
Page Graphics
Watermarks expressed as PDF page graphics are added using ImageGear’s PDF page objects and elements methods. These methods provide for editing the content of graphics objects and operators described in the PDF Reference.
The following snippet demonstrates creating a 24-bpp RGB PDEImage from an ImageGear image and setting it up to be placed at the bottom left corner of a PDF page:
Adjustments are required to successfully add images utilizing alpha channels, other pixel formats and color spaces in accordance to the image XObject discussion in the PDF Reference. The AddWatermarksToPDF Sample demonstrates some of these adjustments.
C |
Copy Code
|
void CreatePDEImageFromImageGear(HIG_PDF_DOC pdfDocument, HIGEAR image,
HIG_PDE_ELEMENT *pdeImage)
{
// This snippet expects a 24 bits per pixel image.
const int bitDepth = 24;
const int bitsPerComponent = 8;
const int bytesPerPixel = bitDepth / bitsPerComponent;
HIGDIBINFO dibInfo = NULL;
AT_DIMENSION imageWidth = 0;
AT_DIMENSION imageHeight = 0;
AT_LMODE pixelAccessMode = 0;
AT_DIMENSION rasterSize = 0;
size_t imageSizeInBytes = 0;
LPAT_PIXEL pixels = NULL;
LPAT_PIXEL rasterLine = NULL;
AT_DIMENSION nativeRasterSize = 0;
LPAT_PIXEL nativeRasterLine = NULL;
int line = 0;
HIG_PDE_COLORSPACE colorSpace = 0;
HIG_PDF_ATOM colorSpaceName = IG_PDF_ATOM_NULL;
AT_PDE_FILTERARRAY filters;
AT_PDE_FILTERSPEC compressionSpec;
HIG_PDF_BASOBJ dictionary = 0;
HIG_PDF_ATOM key = IG_PDF_ATOM_NULL;
AT_PDE_IMAGEATTRS pdfImageAttributes;
AT_PDF_FIXEDMATRIX transformMatrix;
// Get the image's DIB header.
IG_image_DIB_info_get(image, &dibInfo);
imageWidth = IG_DIB_width_get(dibInfo);
imageHeight = IG_DIB_height_get(dibInfo);
// Set the pixel access mode to new.
pixelAccessMode = IG_PIX_ACCESS_MODE_NEW;
IG_gctrl_item_set("DIB.PIX_ACCESS_USE_LEGACY_MODE", AM_TID_AT_LMODE, &pixelAccessMode,
sizeof(AT_LMODE), NULL);
// Get the image pixels.
rasterSize = (imageWidth * bitDepth + (bitsPerComponent - 1)) / bitsPerComponent;
imageSizeInBytes = rasterSize * imageHeight;
pixels = (LPAT_PIXEL)malloc(imageSizeInBytes);
rasterLine = pixels;
IG_DIB_raster_size_get(image, IG_PIXEL_UNPACKED, &nativeRasterSize);
nativeRasterLine = (LPAT_PIXEL)malloc((size_t)nativeRasterSize);
// Retrieve each raster line in its native format.
for (line = 0; line < imageHeight; line++)
{
IG_DIB_raster_get(image, line, nativeRasterLine, IG_PIXEL_UNPACKED);
memcpy(rasterLine, nativeRasterLine, rasterSize);
// Go to the next raster line.
rasterLine += rasterSize;
}
free(nativeRasterLine);
// Create color space.
IG_PDF_atom_from_string("DeviceRGB", &colorSpaceName);
IG_PDE_colorspace_create(colorSpaceName, NULL, &colorSpace);
// Initialize a filter array for the filter specs.
memset(&filters, 0, sizeof(filters));
// Set image compression.
memset(&compressionSpec, 0, sizeof(AT_PDE_FILTERSPEC));
IG_PDF_basdict_create(pdfDocument, FALSE, 3, &dictionary);
IG_PDF_atom_from_string("Columns", &key);
IG_PDF_basdict_put_int(dictionary, key, FALSE, imageWidth);
IG_PDF_atom_from_string("Rows", &key);
IG_PDF_basdict_put_int(dictionary, key, FALSE, imageHeight);
IG_PDF_atom_from_string("Colors", &key);
IG_PDF_basdict_put_int(dictionary, key, FALSE, bytesPerPixel);
IG_PDF_atom_from_string("DCTDecode", &(compressionSpec.name));
compressionSpec.encodeParms = dictionary;
compressionSpec.decodeParms = dictionary;
filters.spec[filters.numFilters] = compressionSpec;
filters.numFilters++;
// Initialize image attributes.
memset(&pdfImageAttributes, 0, sizeof(AT_PDE_IMAGEATTRS));
// A PDF image is an XObject.
pdfImageAttributes.flags = IG_PDE_IMAGE_EXTERNAL;
pdfImageAttributes.width = (AT_INT32)imageWidth;
pdfImageAttributes.height = (AT_INT32)imageHeight;
pdfImageAttributes.bitsPerComponent = bitsPerComponent;
// Initialize transformation matrix.
memset(&transformMatrix, 0, sizeof(AT_PDF_FIXEDMATRIX));
transformMatrix.a = AM_PDF_DOUBLE_TO_FIXED(imageWidth);
transformMatrix.b = IG_PDF_FIXED_ZERO;
transformMatrix.c = IG_PDF_FIXED_ZERO;
transformMatrix.d = AM_PDF_DOUBLE_TO_FIXED(imageHeight);
transformMatrix.h = IG_PDF_FIXED_ZERO;
transformMatrix.v = IG_PDF_FIXED_ZERO;
// Create the PDEImage.
IG_PDE_image_create(&pdfImageAttributes, &transformMatrix, 0, colorSpace, NULL, &filters, NULL,
pixels, (LONG)(imageSizeInBytes), pdeImage);
// Release resources.
IG_PDE_colorspace_release(colorSpace);
free(pixels);
IG_DIB_info_delete(dibInfo);
}
|
C++ |
Copy Code
|
void CreatePDEImageFromImageGear(HIG_PDF_DOC pdfDocument, HIGEAR image,
HIG_PDE_ELEMENT &pdeImage)
{
// This snippet expects a 24 bits per pixel image.
const int bitDepth = 24;
const int bitsPerComponent = 8;
const int bytesPerPixel = bitDepth / bitsPerComponent;
// Get the image's DIB header.
HIGDIBINFO dibInfo = NULL;
IG_image_DIB_info_get(image, &dibInfo);
AT_DIMENSION imageWidth = IG_DIB_width_get(dibInfo);
AT_DIMENSION imageHeight = IG_DIB_height_get(dibInfo);
// Set the pixel access mode to new.
AT_LMODE pixelAccessMode = IG_PIX_ACCESS_MODE_NEW;
IG_gctrl_item_set("DIB.PIX_ACCESS_USE_LEGACY_MODE", AM_TID_AT_LMODE, &pixelAccessMode,
sizeof(AT_LMODE), NULL);
// Get the image pixels.
AT_DIMENSION rasterSize = (imageWidth * bitDepth + (bitsPerComponent - 1)) / bitsPerComponent;
size_t imageSizeInBytes = rasterSize * imageHeight;
LPAT_PIXEL pixels = new AT_PIXEL[imageSizeInBytes];
LPAT_PIXEL rasterLine = pixels;
AT_DIMENSION nativeRasterSize = 0;
IG_DIB_raster_size_get(image, IG_PIXEL_UNPACKED, &nativeRasterSize);
LPAT_PIXEL nativeRasterLine = new AT_PIXEL[nativeRasterSize];
// Retrieve each raster line in its native format.
for (int line = 0; line < imageHeight; line++)
{
IG_DIB_raster_get(image, line, nativeRasterLine, IG_PIXEL_UNPACKED);
memcpy(rasterLine, nativeRasterLine, rasterSize);
// Go to the next raster line.
rasterLine += rasterSize;
}
delete [] nativeRasterLine;
// Create color space.
HIG_PDE_COLORSPACE colorSpace = 0;
HIG_PDF_ATOM colorSpaceName = IG_PDF_ATOM_NULL;
IG_PDF_atom_from_string("DeviceRGB", &colorSpaceName);
IG_PDE_colorspace_create(colorSpaceName, NULL, &colorSpace);
// Create a filter array for the filter specs.
AT_PDE_FILTERARRAY filters;
memset(&filters, 0, sizeof(filters));
// Set image compression.
AT_PDE_FILTERSPEC compressionSpec;
HIG_PDF_BASOBJ dictionary = 0;
HIG_PDF_ATOM key = IG_PDF_ATOM_NULL;
memset(&compressionSpec, 0, sizeof(AT_PDE_FILTERSPEC));
IG_PDF_basdict_create(pdfDocument, FALSE, 3, &dictionary);
IG_PDF_atom_from_string("Columns", &key);
IG_PDF_basdict_put_int(dictionary, key, FALSE, imageWidth);
IG_PDF_atom_from_string("Rows", &key);
IG_PDF_basdict_put_int(dictionary, key, FALSE, imageHeight);
IG_PDF_atom_from_string("Colors", &key);
IG_PDF_basdict_put_int(dictionary, key, FALSE, bytesPerPixel);
IG_PDF_atom_from_string("DCTDecode", &(compressionSpec.name));
compressionSpec.encodeParms = dictionary;
compressionSpec.decodeParms = dictionary;
filters.spec[filters.numFilters] = compressionSpec;
filters.numFilters++;
// Initialize image attributes.
AT_PDE_IMAGEATTRS pdfImageAttributes;
memset(&pdfImageAttributes, 0, sizeof(AT_PDE_IMAGEATTRS));
// A PDF image is an XObject.
pdfImageAttributes.flags = IG_PDE_IMAGE_EXTERNAL;
pdfImageAttributes.width = (AT_INT32)imageWidth;
pdfImageAttributes.height = (AT_INT32)imageHeight;
pdfImageAttributes.bitsPerComponent = bitsPerComponent;
// Initialize transformation matrix.
AT_PDF_FIXEDMATRIX transformMatrix;
memset(&transformMatrix, 0, sizeof(AT_PDF_FIXEDMATRIX));
transformMatrix.a = AM_PDF_DOUBLE_TO_FIXED(imageWidth);
transformMatrix.b = IG_PDF_FIXED_ZERO;
transformMatrix.c = IG_PDF_FIXED_ZERO;
transformMatrix.d = AM_PDF_DOUBLE_TO_FIXED(imageHeight);
transformMatrix.h = IG_PDF_FIXED_ZERO;
transformMatrix.v = IG_PDF_FIXED_ZERO;
// Create the PDEImage.
IG_PDE_image_create(&pdfImageAttributes, &transformMatrix, 0, colorSpace, NULL, &filters, NULL,
pixels, (LONG)(imageSizeInBytes), &pdeImage);
// Release resources.
IG_PDE_colorspace_release(colorSpace);
delete [] pixels;
IG_DIB_info_delete(dibInfo);
}
|
The following code snippet calls the CreatePDEImageFromImageGear method from above and places the PDE image created into the content of the PDF page.
C |
Copy Code
|
void AddRGB24ImageToPdfPage(HIG_PDF_DOC pdfDocument, HIG_PDF_PAGE pdfPage, HIGEAR image)
{
HIG_PDE_CONTENT content = 0;
HIG_PDE_ELEMENT pdeImage = 0;
CreatePDEImageFromImageGear(pdfDocument, image, &pdeImage);
// Acquire the PDF page content.
IG_PDF_page_get_content(pdfPage, &content);
IG_PDE_content_add_element(content, IG_PDE_AFTER_LAST, pdeImage);
// Save the PDF content to the PDF page.
IG_PDF_page_set_content(pdfPage);
IG_PDF_page_release_content(pdfPage);
IG_PDE_element_release(pdeImage);
}
|
C++ |
Copy Code
|
void AddRGB24ImageToPdfPage(HIG_PDF_DOC pdfDocument, HIG_PDF_PAGE pdfPage, HIGEAR image)
{
HIG_PDE_ELEMENT pdeImage = 0;
CreatePDEImageFromImageGear(pdfDocument, image, pdeImage);
// Acquire the PDF page content.
HIG_PDE_CONTENT content = 0;
IG_PDF_page_get_content(pdfPage, &content);
IG_PDE_content_add_element(content, IG_PDE_AFTER_LAST, pdeImage);
// Save the PDF content to the PDF page.
IG_PDF_page_set_content(pdfPage);
IG_PDF_page_release_content(pdfPage);
IG_PDE_element_release(pdeImage);
}
|
Refer to the AddWatermarksToPDF Sample.
See the PDF Reference topic “Page Editing Objects and Elements” for additional information.
Optional Content Groups
Watermark optional content can be added to PDF pages using ImageGear’s PDF page objects and elements methods. These methods provide for editing the content of graphics objects and operators described in the PDF Reference, which provides methods for layer management.
To designate a watermark intended for printing, add an Optional Content Print Usage dictionary with Subtype name "Watermark" to that OCG, represented with a PDF layer:
C |
Copy Code
|
void CreateOptionalContentGroup(HIG_PDF_DOC pdfDocument, HIG_PDF_LAYER *layer)
{
// Create Print Usage dictionary.
HIG_PDF_BASOBJ dictionary = 0;
HIG_PDF_ATOM key = IG_PDF_ATOM_NULL;
HIG_PDF_ATOM name = IG_PDF_ATOM_NULL;
char layerName[] = "WatermarkLayer";
IG_PDF_basdict_create(pdfDocument, FALSE, 2, &dictionary);
IG_PDF_atom_from_string("Subtype", &key);
IG_PDF_atom_from_string("Watermark", &name);
IG_PDF_basdict_put_name(dictionary, key, FALSE, name);
IG_PDF_atom_from_string("PrintState", &key);
IG_PDF_atom_from_string("ON", &name);
IG_PDF_basdict_put_name(dictionary, key, FALSE, name);
IG_PDF_layer_create(pdfDocument, layerName, sizeof(layerName), layer);
IG_PDF_atom_from_string("Print", &key);
IG_PDF_layer_set_usage_info(layer, key, dictionary);
}
|
C++ |
Copy Code
|
void CreateOptionalContentGroup(HIG_PDF_DOC pdfDocument, HIG_PDF_LAYER &layer)
{
// Create Print Usage dictionary.
HIG_PDF_BASOBJ dictionary = 0;
HIG_PDF_ATOM key = IG_PDF_ATOM_NULL;
HIG_PDF_ATOM name = IG_PDF_ATOM_NULL;
IG_PDF_basdict_create(pdfDocument, FALSE, 2, &dictionary);
IG_PDF_atom_from_string("Subtype", &key);
IG_PDF_atom_from_string("Watermark", &name);
IG_PDF_basdict_put_name(dictionary, key, FALSE, name);
IG_PDF_atom_from_string("PrintState", &key);
IG_PDF_atom_from_string("ON", &name);
IG_PDF_basdict_put_name(dictionary, key, FALSE, name);
char layerName[] = "WatermarkLayer";
IG_PDF_layer_create(pdfDocument, layerName, sizeof(layerName), &layer);
IG_PDF_atom_from_string("Print", &key);
IG_PDF_layer_set_usage_info(layer, key, dictionary);
}
|
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’s PDF page objects and elements methods, which provides methods to create a marked-content sequence in accordance with the PDF Reference.
See the PDF Reference topic "Real Content and Artifacts" for additional information