Create and extract PDF optional content in PDF documents in .NET

Today we released XFINUM.PDF 3.4 which brings support for pdf optional content extraction. With this new version we include full support for manipulating optional content in PDF files, whether it is optional content creation or extraction.

PDF specification describes optional content as “sections of content in a PDF document that can be selectively viewed or hidden by document authors or consumers. This capability is useful in items such as CAD drawings, layered artwork, maps, and multi-language documents.”.

An optional content section is started in the page graphics using the BeginOptionalContentGroup method and it ends with a call to EndOptionalContentGroup method. The BeginOptionalContentGroup method receives as parameter a PdfOptionalContentGroup object, thus associating the section of content in the page graphics with the optional content group. The optional content group object specifies the name, visibility, print and export states. Also optional content group objects can be locked or not.

A simple optional content group is created like this:

PdfBrush greenBrush = new PdfBrush(PdfRgbColor.DarkGreen);
PdfPen bluePen = new PdfPen(PdfRgbColor.DarkBlue, 5);

PdfFixedDocument document = new PdfFixedDocument();
PdfPage page = document.Pages.Add();

PdfOptionalContentGroup ocgPage1 = new PdfOptionalContentGroup();
ocgPage1.Name = "Page 1 - Green Rectangle";
page.Graphics.BeginOptionalContentGroup(ocgPage1);
page.Graphics.DrawRectangle(bluePen, greenBrush, 20, 100, 570, 400);
page.Graphics.EndOptionalContentGroup();

document.OptionalContentProperties = new PdfOptionalContentProperties();
PdfOptionalContentDisplayTreeNode ocgPage1Node = new PdfOptionalContentDisplayTreeNode(ocgPage1);
document.OptionalContentProperties.DisplayTree.Nodes.Add(ocgPage1Node);

document.Save("SimpleOptionalContent.pdf");
Dim greenBrush As New PdfBrush(PdfRgbColor.DarkGreen)
Dim bluePen As New PdfPen(PdfRgbColor.DarkBlue, 5)

Dim document As New PdfFixedDocument()
Dim page As PdfPage = document.Pages.Add()

Dim ocgPage1 As New PdfOptionalContentGroup()
ocgPage1.Name = "Page 1 - Green Rectangle"
page.Graphics.BeginOptionalContentGroup(ocgPage1)
page.Graphics.DrawRectangle(bluePen, greenBrush, 20, 100, 570, 400)
page.Graphics.EndOptionalContentGroup()

document.OptionalContentProperties = New PdfOptionalContentProperties()
Dim ocgPage1Node As New PdfOptionalContentDisplayTreeNode(ocgPage1)
document.OptionalContentProperties.DisplayTree.Nodes.Add(ocgPage1Node)

document.Save("SimpleOptionalContent.pdf")

Multipart PDF optional content groups

PDF specification allows to create optional content groups that span over multiple content sections (non-contiguous optional content groups) and XFINIUM.PDF supports this feature. By using the same optional content group with multiple BeginOptionalContentGroup method calls all the content sections are linked to the same optional content group.

The code below shows how to create an optional content group that consists of 2 content sections with some other content between them.

PdfBrush greenBrush = new PdfBrush(PdfRgbColor.DarkGreen);
PdfBrush yellowBrush = new PdfBrush(PdfRgbColor.Yellow);
PdfPen bluePen = new PdfPen(PdfRgbColor.DarkBlue, 5);
PdfPen redPen = new PdfPen(PdfRgbColor.DarkRed, 5);

PdfFixedDocument document = new PdfFixedDocument();
PdfPage page = document.Pages.Add();

PdfOptionalContentGroup multipartOcg = new PdfOptionalContentGroup();
multipartOcg.Name = "Green Rectangles";
// Begin optional content group here.
page.Graphics.BeginOptionalContentGroup(multipartOcg);
page.Graphics.DrawRectangle(bluePen, greenBrush, 20, 200, 570, 200);
// First section of optional content group ends.
page.Graphics.EndOptionalContentGroup();

// Regular page content.
page.Graphics.DrawRectangle(redPen, yellowBrush, 250, 90, 110, 680);

// Continue the optional content group here.
page.Graphics.BeginOptionalContentGroup(multipartOcg);
page.Graphics.DrawRectangle(bluePen, greenBrush, 20, 500, 570, 200);
// Second section of optional content group ends.
page.Graphics.EndOptionalContentGroup();

document.OptionalContentProperties = new PdfOptionalContentProperties();
PdfOptionalContentDisplayTreeNode ocgPageNode = new PdfOptionalContentDisplayTreeNode(multipartOcg);
document.OptionalContentProperties.DisplayTree.Nodes.Add(ocgPageNode);

document.Save("MultipartOptionalContent.pdf");
Dim greenBrush As New PdfBrush(PdfRgbColor.DarkGreen)
Dim yellowBrush As New PdfBrush(PdfRgbColor.Yellow)
Dim bluePen As New PdfPen(PdfRgbColor.DarkBlue, 5)
Dim redPen As New PdfPen(PdfRgbColor.DarkRed, 5)

Dim document As New PdfFixedDocument()
Dim page As PdfPage = document.Pages.Add()

Dim multipartOcg As New PdfOptionalContentGroup()
multipartOcg.Name = "Green Rectangles"
' Begin optional content group here.
page.Graphics.BeginOptionalContentGroup(multipartOcg)
page.Graphics.DrawRectangle(bluePen, greenBrush, 20, 200, 570, 200)
' First section of optional content group ends.
page.Graphics.EndOptionalContentGroup()

' Regular page content.
page.Graphics.DrawRectangle(redPen, yellowBrush, 250, 90, 110, 680)

' Continue the optional content group here.
page.Graphics.BeginOptionalContentGroup(multipartOcg)
page.Graphics.DrawRectangle(bluePen, greenBrush, 20, 500, 570, 200)
' Second section of optional content group ends.
page.Graphics.EndOptionalContentGroup()

document.OptionalContentProperties = New PdfOptionalContentProperties()
Dim ocgPageNode As New PdfOptionalContentDisplayTreeNode(multipartOcg)
document.OptionalContentProperties.DisplayTree.Nodes.Add(ocgPageNode)

document.Save("MultipartOptionalContent.pdf")

Nested PDF optional content groups

Optional content groups support multiple levels of nesting (layers and sub-layers). This feature is achieved by nesting multiple BeginOptionalContentGroup method calls. When optional content groups are nested, the outer group visibility affects the inner groups visibility, so that if the outer group is hidden then the inner groups are hidden, but if the outer group is visible the visibility of the inner groups is dictated by their attributes.

Nested optional content groups can be created like this:

PdfBrush greenBrush = new PdfBrush(PdfRgbColor.DarkGreen);
PdfBrush yellowBrush = new PdfBrush(PdfRgbColor.Yellow);
PdfPen bluePen = new PdfPen(PdfRgbColor.DarkBlue, 5);
PdfPen redPen = new PdfPen(PdfRgbColor.DarkRed, 5);

PdfFixedDocument document = new PdfFixedDocument();
PdfPage page = document.Pages.Add();

PdfOptionalContentGroup ocgParent = new PdfOptionalContentGroup();
ocgParent.Name = "Parent - Green Rectangle";
// Begin the parent optional content group
page.Graphics.BeginOptionalContentGroup(ocgParent);
page.Graphics.DrawRectangle(bluePen, greenBrush, 20, 100, 570, 600);

PdfOptionalContentGroup ocgChild = new PdfOptionalContentGroup();
ocgChild.Name = "Child - Yellow Rectangle";
// Begin the child optional content group
page.Graphics.BeginOptionalContentGroup(ocgChild);
page.Graphics.DrawRectangle(redPen, yellowBrush, 100, 200, 400, 300);

page.Graphics.EndOptionalContentGroup(); // Child
page.Graphics.EndOptionalContentGroup(); // Parent

document.OptionalContentProperties = new PdfOptionalContentProperties();
PdfOptionalContentDisplayTreeNode ocgParentNode = new PdfOptionalContentDisplayTreeNode(ocgParent);
document.OptionalContentProperties.DisplayTree.Nodes.Add(ocgParentNode);
PdfOptionalContentDisplayTreeNode ocgChildNode = new PdfOptionalContentDisplayTreeNode(ocgChild);
ocgParentNode.Nodes.Add(ocgChildNode);

document.Save("NestedOptionalContent.pdf");
Dim greenBrush As New PdfBrush(PdfRgbColor.DarkGreen)
Dim yellowBrush As New PdfBrush(PdfRgbColor.Yellow)
Dim bluePen As New PdfPen(PdfRgbColor.DarkBlue, 5)
Dim redPen As New PdfPen(PdfRgbColor.DarkRed, 5)

Dim document As New PdfFixedDocument()
Dim page As PdfPage = document.Pages.Add()

Dim ocgParent As New PdfOptionalContentGroup()
ocgParent.Name = "Parent - Green Rectangle"
' Begin the parent optional content group
page.Graphics.BeginOptionalContentGroup(ocgParent)
page.Graphics.DrawRectangle(bluePen, greenBrush, 20, 100, 570, 600)

Dim ocgChild As New PdfOptionalContentGroup()
ocgChild.Name = "Child - Yellow Rectangle"
' Begin the child optional content group
page.Graphics.BeginOptionalContentGroup(ocgChild)
page.Graphics.DrawRectangle(redPen, yellowBrush, 100, 200, 400, 300)

page.Graphics.EndOptionalContentGroup() ' Child
page.Graphics.EndOptionalContentGroup() ' Parent

document.OptionalContentProperties = New PdfOptionalContentProperties()
Dim ocgParentNode As New PdfOptionalContentDisplayTreeNode(ocgParent)
document.OptionalContentProperties.DisplayTree.Nodes.Add(ocgParentNode)
Dim ocgChildNode As New PdfOptionalContentDisplayTreeNode(ocgChild)
ocgParentNode.Nodes.Add(ocgChildNode)

document.Save("NestedOptionalContent.pdf")

 Multi-page PDF optional content groups

Another interesting feature of optional content groups is that they can span over multiple pages. This is implemented by associating a single optional content group object with multiple content sections over multiple pages. In this way you can show/hide content sections over multiple pages in a single operation.

PdfBrush greenBrush = new PdfBrush(PdfRgbColor.DarkGreen);
PdfBrush yellowBrush = new PdfBrush(PdfRgbColor.Yellow);
PdfPen bluePen = new PdfPen(PdfRgbColor.DarkBlue, 5);
PdfPen redPen = new PdfPen(PdfRgbColor.DarkRed, 5);

PdfFixedDocument document = new PdfFixedDocument();
PdfPage page = document.Pages.Add();

PdfOptionalContentGroup multipageOcg = new PdfOptionalContentGroup();
multipageOcg.Name = "Multipage optional content";
// Begin the multipage optional content group.
page.Graphics.BeginOptionalContentGroup(multipageOcg);
page.Graphics.DrawRectangle(bluePen, greenBrush, 20, 200, 570, 200);
// Optional content ends.
page.Graphics.EndOptionalContentGroup();

page = document.Pages.Add();
// Optional content continues on the new page.
page.Graphics.BeginOptionalContentGroup(multipageOcg);
page.Graphics.DrawRectangle(redPen, yellowBrush, 250, 90, 110, 680);
page.Graphics.EndOptionalContentGroup();

document.OptionalContentProperties = new PdfOptionalContentProperties();
PdfOptionalContentDisplayTreeNode multipageOcgNode = new PdfOptionalContentDisplayTreeNode(multipageOcg);
document.OptionalContentProperties.DisplayTree.Nodes.Add(multipageOcgNode);

document.Save("MultipageOptionalContent.pdf");
Dim greenBrush As New PdfBrush(PdfRgbColor.DarkGreen)
Dim yellowBrush As New PdfBrush(PdfRgbColor.Yellow)
Dim bluePen As New PdfPen(PdfRgbColor.DarkBlue, 5)
Dim redPen As New PdfPen(PdfRgbColor.DarkRed, 5)

Dim document As New PdfFixedDocument()
Dim page As PdfPage = document.Pages.Add()

Dim multipageOcg As New PdfOptionalContentGroup()
multipageOcg.Name = "Multipage optional content"
// Begin the multipage optional content group.
page.Graphics.BeginOptionalContentGroup(multipageOcg)
page.Graphics.DrawRectangle(bluePen, greenBrush, 20, 200, 570, 200)
// Optional content ends.
page.Graphics.EndOptionalContentGroup()

page = document.Pages.Add()
// Optional content continues on the new page.
page.Graphics.BeginOptionalContentGroup(multipageOcg)
page.Graphics.DrawRectangle(redPen, yellowBrush, 250, 90, 110, 680)
page.Graphics.EndOptionalContentGroup()

document.OptionalContentProperties = New PdfOptionalContentProperties()
Dim multipageOcgNode As New PdfOptionalContentDisplayTreeNode(multipageOcg)
document.OptionalContentProperties.DisplayTree.Nodes.Add(multipageOcgNode)

document.Save("MultipageOptionalContent.pdf")

 PDF Optional content visual tree

PDF specification defines several structures for displaying the structure of optional content in a PDF document using a visual tree where each node corresponds to an optional content group. This visual tree does not necessary show the optional content structure as it exists physically in the PDF document, the visual tree can be built to show the author’s view over the optional content structure. For example nested optional content groups can be shown as sibling nodes in the tree or sibling optional content groups in the page content can be shown as parent-child nodes in the tree.

Also you can choose not to show some optional content groups in the tree. This technique is used when you want content to appear only when the document is printed (a watermark for example) and you do not want the end-user to change the print status of the optional content group.

The optional content visual tree is built by creating first a PdfOptionalContentProperties object and attaching it to document’s OptionalContentProperties property. The DisplayTree property of the PdfOptionalContentProperties object represents the optional content visual tree. You create a PdfOptionalContentDisplayTreeNode object for each optional content group you want to appear in the tree and add the node object to the tree. Each node object has its Nodes property that specifies the children nodes of that node.

The last lines of code in each of the code sections above show how to create the optional content visual tree for different scenarios.

PDF Optional content extraction

As it was said above an optional content group is a section of page content. This section of page content is not self-defined in terms of graphics properties, content before it affects its content and its content also affects the content that follows.

When a page with optional content is displayed, the optional content is still executed even if the optional content is not visible. For example the stroke color can be set inside the optional content and it affects the content after the optional content even if the optional content is not visible. In the same way stroke color can be set before the optional content starts and it will be used by the optional content. This makes optional content extraction quite difficult because the content in the group is affected by content outside it. Just extracting the content that appears between BeginOptionalContentGroup and EndOptionalContentGroup methods will work only in a small number of situations.

We managed to implement optional content extraction that works with almost any kind of optional content groups. We implemented an analyzer of the content outside the group so that when the optional content is extracted we create all the additional graphic properties that affect the group and the group is properly displayed when drawn on a new page.

Optional content can be extracted in 2 ways:

  1. using the PdfFile.ExtractPageOptionalContent(int pageNumber, string ocgName) method which extracts only the required objects from the Pdf file or
  2. if you already have the page in your application as a PdfPage object, you can create a PdfContentExtractor object for the page and call the PdfContentExtractor.ExtractPageOptionalContent(string ocgName) method.

The optional content group is extracted as a PdfPageOptionalContent object. The PdfPageOptionalContent class inherits from PdfFormXObject so the extracted optional content group can be drawn on another page using the PdfGraphics.DrawFormXObject method.

FileStream input = File.OpenRead("optionalcontent.pdf");
PdfFile file = new PdfFile(input);

PdfFixedDocument document = new PdfFixedDocument();
PdfPage page = document.Pages.Add();

PdfPageOptionalContent oc1 = file.ExtractPageOptionalContentGroup(4, "1");
page.Graphics.DrawFormXObject(oc1, 0, 0, page.Width / 2, page.Height / 2);
PdfPageOptionalContent oc2 = file.ExtractPageOptionalContentGroup(4, "2");
page.Graphics.DrawFormXObject(oc2, page.Width / 2, 0, page.Width / 2, page.Height / 2);
PdfPageOptionalContent oc3 = file.ExtractPageOptionalContentGroup(4, "3");
page.Graphics.DrawFormXObject(oc3, 0, page.Height / 2, page.Width / 2, page.Height / 2);
PdfPageOptionalContent oc4 = file.ExtractPageOptionalContentGroup(4, "4");
page.Graphics.DrawFormXObject(oc4, page.Width / 2, page.Height / 2, page.Width / 2, page.Height / 2);

document.Save("optionalcontentextraction.pdf")
input.Close();
Dim input As FileStream = File.OpenRead("optionalcontent.pdf")
Dim ocFile As New PdfFile(input)

Dim document As New PdfFixedDocument()
Dim page As PdfPage = document.Pages.Add()

Dim oc1 As PdfPageOptionalContent = ocFile.ExtractPageOptionalContentGroup(4, "1")
page.Graphics.DrawFormXObject(oc1, 0, 0, page.Width / 2, page.Height / 2)
Dim oc2 As PdfPageOptionalContent = ocFile.ExtractPageOptionalContentGroup(4, "2")
page.Graphics.DrawFormXObject(oc2, page.Width / 2, 0, page.Width / 2, page.Height / 2)
Dim oc3 As PdfPageOptionalContent = ocFile.ExtractPageOptionalContentGroup(4, "3")
page.Graphics.DrawFormXObject(oc3, 0, page.Height / 2, page.Width / 2, page.Height / 2)
Dim oc4 As PdfPageOptionalContent = ocFile.ExtractPageOptionalContentGroup(4, "4")
page.Graphics.DrawFormXObject(oc4, page.Width / 2, page.Height / 2, page.Width / 2, page.Height / 2)

document.Save("optionalcontentextraction.pdf")
input.Close()

All the code above is included in our Samples Explorer applications, Optional Content and Optional Content Extraction samples, available for download in our Samples page.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: