Working with PDF Layers (OCG) in C#

Communicating ideas in digital documents can be made clearer through the use of layers. PDF layers — or optional content groups (OCGs) — have been supported in PDF documents since version 1.5 of the PDF Reference.

The word “optional” is key here. OCGs enable grouping images or annotations together and controlling their visibility based on conditions such as whether a document is being viewed onscreen 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 onscreen but visible when the PDF is printed. Or, you might want to display a message on screen, but not on the printed version. You might even want the user to be able to change the language.

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 specified by an index.

  • 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 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.

The following methods are used to manage the visibility of OCGs in various document states — from zoom level, to printing, and more:

Creating PDF Layers (Optional Content Groups)

The following code shows how to add OCGs to your PDF files:

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

You can create PDF content dynamically in code, load it from a PDF, or import a text file. In this example, you’ll load an existing PDF file:

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

The next step is to create a new layer — an OCG — with the image.

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("image layer");
  if (pdf.GetStat() != GdPictureStatus.OK) return;

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

The code above is:

  • Loading and positioning our 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 our image is on to be visible when the document is printed and invisible when the document is viewed onscreen.

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

if (status == GdPictureStatus.OK)
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");
}

The code above is calling:

  • SetOCGLockedState() to prevent the reader from changing visibility.

  • SetOCGPrintState() to make the OCG visible when printed.

  • SetOCGViewState() to make the OCG invisible onscreen.

Now, 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 of 20 and 140 percent, we can set these as a minimum and maximum for when our OCG is visible.

Information

The minimum value is exclusive, and the maximum value is inclusive. So, in this example, the OCG wouldn’t be visible at 20 percent, but it would be visible at 140 percent.

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");
}

The code above uses:

  • SetOCGViewState() to make the OCG visible when the document is viewed onscreen (so we can observe the result of our zoom minimum and maximum).

  • SetOCGZoomMin() to set a minimum 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 percent is represented by 0.2.

  • SetOCGZoomMax() to set a maximum zoom level to 140 percent for when the OCG is visible by supplying a value of 1.4.

Applying OCG 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.

QR code example

We can group them together inside marked content, like so:

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");
}

In the code above:

  • We’re loading our PDF and creating a new OCG.

  • If GdPictureStatus.OK is true, we call BeginOCGMarkedContent(ocgId). This starts a new marked content group and adds the marked content to the previously created OCG. The OCG is identified by its ID, which we saved in the ocgId variable.

  • We add our text and QR code to the content.

  • Then, we 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 use SetOCGPrintState() to switch the layer off by default when printing. However, we unlocked the layer with SetOCGLockedState() so the user can enable it before printing, if necessary.