March 23, 2020 | PDF

Using PDF Layers or Optional Content Groups (OCG) in GdPicture.NET


Illustration for the blog article with the text “PDF Layers”

Communicating ideas in digital documents can often be made much clearer through the use of layers. Layers have been supported in PDF documents since version 1.5 through optional content groups (OCGs), also known as PDF layers.

“Optional” is key here. OCGs allow us to group images or annotations together, then control their visibility based on conditions such as whether the document is being viewed on screen or being printed, or through a user interacting with the document to enable or disable a layer.

For example, you might want to add a watermark that’s invisible on screen but visible when the PDF is printed, or display a message on screen, but not on the printed version. Or perhaps you want the user to be able to change the language.

In this article, we’ll demonstrate how to create and configure OCGs — layers in a PDF — using C# and GdPicture.NET.

Prerequisites

The examples below have been written and tested in C# on .NET Core 3.1 using Visual Studio 16.4.5, but GdPicture.NET is also available for .NET Framework 4.5. Other IDEs should also work so long as you’re able to reference assemblies.

To work along, you can download the following and include them in your project:

If your project is targeting .NET Core, you should also install the NuGet package System.Drawing.Common.

Then just download and install a copy of the GdPicture.NET SDK, following the instructions here.

Creating an OCG Watermark

For the first example, let’s look at how you can use GdPicture.NET to add watermarks to your PDF files in code.

To use all the features of the toolkit, you need to unlock it with a license key (which is supplied with the 60-day trial) in code:

var licenseManager = new LicenseManager();
licenseManager.RegisterKEY("###");

GdPicture.NET can create PDF content dynamically in code, load it from a PDF, or import a text file. In this example, we’ll load our existing PDF file.

using var pdf = new GdPicturePDF();
var status = pdf.LoadFromFile(@"orpalis-demo.pdf");

Yes, that’s a C# 8 using declaration you see! We’re going with using because we’re on our best behavior and know that we need to be sure any resources being held are released.

The next step is to create a new layer—an OCG—with the image we’re using for our watermark.

if (status == GdPictureStatus.OK)
{
  var imageName = pdf.AddJpegImageFromFile("orpalis-logo.jpg");
  if (pdf.GetStat() != GdPictureStatus.OK) return;

  if (pdf.DrawImage(imageName,
      (pdf.GetPageWidth() - 400) / 2,
      (pdf.GetPageHeight() - 109) / 2,
      400, 109) != GdPictureStatus.OK) return;

  var ocgId = pdf.NewOCG("Watermark image layer");
  if (pdf.GetStat() != GdPictureStatus.OK) return;

  if (pdf.SetImageOptional(imageName, ocgId) != GdPictureStatus.OK) return;
}


What’s happening in this code? We’re completing three tasks:

  • Loading and positioning our watermark image.
  • Creating a new optional content group with NewOCG()and giving it a title.
  • Adding the image as content to the newly created OCG with SetImageOptional().

It’s a good idea to check the status of operations returning a GdPictureStatus enumeration, or you can call the GetStat()method directly following a call to methods that return some other value.

Controlling When the OCG is Visible

We want the layer that our watermark is on to be visible when the document is printed and invisible when the document is viewed on screen.

We probably also want to lock the view, so readers can’t change its visibility.

if ((pdf.SetOCGLockedState(ocgId, true) == GdPictureStatus.OK) &&
    (pdf.SetOCGPrintState(ocgId, PdfOcgState.StateOn) == GdPictureStatus.OK) &&
    (pdf.SetOCGViewState(ocgId, PdfOcgState.StateOff) == GdPictureStatus.OK))
{
    pdf.SaveToFile("orpalis-demo-watermarked.pdf");
}

What’s happening in this code? We’re calling:

  • SetOCGLockedState() to prevent the reader from changing visibility.
  • SetOCGPrintState()to make the OCG visible when printed.
  • SetOCGViewState() to make the OCG invisible on screen.

That was our last bit of code for adding the watermark, so we’ll use the status of these final three method calls to decide whether to save our new PDF file with SaveToFile().

It’s also possible to display an OCG depending on the current zoom level in the viewing application. For example, if we have an image overlay that’s only effective for the user between the zoom levels 20% and 140%, we can set these as a minimum and maximum for when our OCG is visible.

Note: The min value is exclusive and the max is inclusive. So, in this example, the OCG would not be visible at exactly 20% but would be visible at 140%.

To see this in action, swap out the last piece of code above with the following:

if ((pdf.SetOCGViewState(ocgId, PdfOcgState.StateOff) == GdPictureStatus.OK) &&
    (pdf.SetOCGZoomMin(ocgId, 0.2f) == GdPictureStatus.OK) &&
    (pdf.SetOCGZoomMax(ocgId, 1.4f) == GdPictureStatus.OK))
{
  pdf.SaveToFile("zoom-controlled-visibilty.pdf");
}

What’s happening in this code? We’re using:

  • SetOCGViewState() to make the OCG visible when the document is viewed on screen (so we can observe the result of our zoom min and max).
  • SetOCGZoomMin() to set a min zoom level for the OCG being visible. It takes a float for the zoom limit, so we need to add a trailing f after the number. Our minimum of 20% is represented by 0.2.
  • SetOCGZoomMax() to set a max zoom level for when the OCG is visible to 140% by supplying a value of 1.4.

Applying OCGs to Document Content

We’ve seen how to use an OCG for an image, but what if we want other things, like text and charts, on one of our layers?

A PDF feature called marked content lets you group specified content together into a single collection. You can then control the visibility of that collection as a single item.

Perhaps we want to generate a QR code and add instructions above it and an alternative phone number below. That’s three separate elements:

We can group them together inside marked content.

using var pdf = new GdPicturePDF();

pdf.LoadFromFile(@"orpalis-demo.pdf");
var ocgId = pdf.NewOCG("Demo Marked Content Layer");
var fontName = pdf.AddStandardFont(PdfStandardFont.PdfStandardFontHelveticaBold);

if (pdf.GetStat() == GdPictureStatus.OK &&

    pdf.BeginOCGMarkedContent(ocgId) == GdPictureStatus.OK &&

    pdf.DrawTextBox(fontName,
              155, 220, 360, 272,
              TextAlignment.TextAlignmentCenter,
              TextAlignment.TextAlignmentCenter,
              "Scan to visit our website", true) == GdPictureStatus.OK &&

    pdf.DrawBarcodeQrCode("https://www.gdpicture.com/download-gdpicture/",  
              BarcodeQREncodingMode.BarcodeQREncodingModeAlphaNumeric, 
              BarcodeQRErrorCorrectionLevel.BarcodeQRErrorCorrectionLevelM,
              0, 4, 200, 100, Color.Black) == GdPictureStatus.OK &&

    pdf.DrawTextBox(fontName,
              150, 50, 380, 102,
              TextAlignment.TextAlignmentCenter,
              TextAlignment.TextAlignmentCenter,
              "Or phone us on 123-456-7890",
              true) == GdPictureStatus.OK &&

    pdf.EndOCGMarkedContent() == GdPictureStatus.OK &&

    pdf.SetOCGLockedState(ocgId, false) == GdPictureStatus.OK &&

    pdf.SetOCGPrintState(ocgId, PdfOcgState.StateOff) == GdPictureStatus.OK)

{
    pdf.SaveToFile("marked-content-layer.pdf");
}


What’s happening in this code?

  • We’re loading our PDF and creating a new OCG.
  • If GdPictureStatus.OK, we start a new marked content group by calling BeginOCGMarkedContent(ocgId) to add the marked content to the new OCG we made, identified by ocgId.
  • We add our text and QR code to the content, then signal the end of the collection by calling EndOCGMarkedContent().
  • As it’s an optional content group, we can control when the OCG is visible. In this case, we’ve chosen to switch the layer off by default when printing, with SetOCGPrintState(), but we unlocked the layer with SetOCGLockedState() so the user can enable it before printing, if necessary.

The Complete OCG API

We looked at using GdPicture.NET with C# to create OCGs and control when they’re visible on screen or in print. We also saw how to lock PDF layers to prevent a user from changing our defined visibility controls. To do this, we created collections of content, grouped together in a single OCG, and applied the same visibility controls over them as we did for a single entity.

The OCG API contains more than we can cover in a single article. The following lists everything in the SDK for working with PDF OCGs.

API CRUD Operations and Other Core Features

These are some of the key OCG methods you should know about for creating and managing OCGs.

  • NewOCG() creates a new optional content group with a given title.
  • GetOCG() gets the unique identifier for an OCG.
  • GetOCGCount() returns the number of OCGs in a PDF.
  • SetOCGTitle() sets the OCG title, which will be visible in the viewer’s application.
  • GetOCGTitle() gets the OCG title that you set with SetOCGTitle().
  • SetImageOptional() adds an image as content to a newly created OCG.
  • DeleteOCG() removes the OCG layer and optionally removes the content as well.
  • FlattenVisibleOCGs() flattens multiple layers so all visible OCGs become a single layer and any that were hidden are discarded.

API Features for Controlling Visibility

These OCG methods are specifically used to manage the visibility of OCGs in various document states, from zoom level to printing and more.

Next Steps

GdPicture.NET is a powerful document imaging SDK that supports a broad range of file formats and .NET frameworks. PDF OCGs are a welcome feature for developers to leverage the additional clarity that comes with communicating concepts and ideas through layers.

The SDK is available to download here. When you’ve completed the installation, follow the instructions in the README to start the fully-featured 60-day trial.

About the Author – Ben Hall

Ben is an Expert .NET Software Engineer at the United Kingdom Hydrographic Office. He previously worked for 9 years as a school teacher, teaching programming and Computer Science. He enjoys making complex topics accessible and practical for busy developers.


Tags: