Page tree
Skip to end of metadata
Go to start of metadata


PDF files support optional content, which are also known as layers. Page content can be assigned to an optional content group, or layer. This optional content group then has settings to control whether the content is hidden for rendering and/or printing.

Optional content groups, can be complicated to work with, but the Mako API makes a good job of simplifying things for us.

With just a small amount of code, you can easily:

  • Enumerate optional content groups
  • Find out which optional content groups a node is a member of
  • Add a node to an optional content group

Enumerating Optional Content Groups

 Optional content can be found on the IDocument object, by calling getOptionalContent(...). The groups can then be found by calling getGroups(...).

The following code will enumerate and output the names of all optional content groups (layers) in the document.

const auto optionalContent = document->getOptionalContent();
if (!optionalContent)
    std::cout << "No optional content for the document." << std::endl;

const auto optionalContentGroups = optionalContent->getGroups();

std::cout << "Optional content groups found:" << std::endl;

for (const auto& optionalContentGroup : optionalContentGroups)
    std::cout << "\t" << optionalContentGroup->getName() << std::endl;

Finding Optional Content Group Membership

Sometimes it's useful to be able to find which optional content groups a node is a member of. This is fairly easy, but it requires a little knowledge of how optional content groups work first.

In PDF, optional content is structured such that only certain PDF content can be assigned to an optional content group. In Mako, this exposes is self in that only groups nodes can be assigned to an optional content group.

The code below goes will return the first optional content group a group node is a member of.

IOptionalContentGroupPtr getOptionalContentGroupForGroupNode(const IDocumentPtr& document, const IDOMGroupPtr& groupNode)
    const auto optionalContentGroupDetails = groupNode->getOptionalContentDetails();

    // Check if it has any optional content group details.
    // If not, it's not a member of any group.
    if (!optionalContentGroupDetails)
        return IOptionalContentGroupPtr();

    const auto groupReferences = optionalContentGroupDetails->getGroupReferences();

    const auto optionalContentGroups = document->getOptionalContent()->getGroups();

    // Go through each group reference in the node.
    for (const auto& groupReference : groupReferences)
        // Iterate through the optional content groups in the document
        for (const auto& optionalContentGroup : optionalContentGroups)
            // And see if one matches.
            if (groupReference->equals(optionalContentGroup->getReference()))
                // Return the first optional content group it's a member of for simplicity. However, It may be a member of multiple groups.
                return optionalContentGroup;

    return IOptionalContentGroupPtr();

If you want to see optional content group membership for a node that isn't a group, you'll need to walk up the node tree, using getParentNode(...), until you reach a group, which you can then check.

Adding a Node to an Optional Content Group

This is the easiest of all three, and required little more than two lines of code.

First, we get the optional content from the document, then we call makoNodeOptional(...) to add the node to our optional content group.

const auto optionalContent = document->getOptionalContent();
optionalContent->makeNodeOptional(myNode, myOptionalContentGroup);

Once done, the settings in the optional content group will now determine the behaviour of the node.

Rendering with Optional Content

If you need to taking into account optional content when rendering with Mako, make sure you use one of the rendering overloads which takes the document's optional content groups. If you omit the optional content groups, all the nodes will render as normal, ignoring the optional content group settings.

  • No labels