Can PDFNet SDK be used to detect and remove non-visible PDF layers (OCGs)

I am new to PDFNet SDK and still trying to piece together how some of the PDF functionality works. I am impressed by the breadth and depth of the SDK but am trying to understand how the SDK works with layers (aka Optional Content Groups or OCGs). In particular, I am trying to determine how one can detect which layers are non-visible (OFF) so that they can be removed from the PDF. I have initially focused on the classes in the pdftron.PDF.OCG Namespace, especially the Group class. This allows me to iterate the groups and get basic information such as name and current state (ON/OFF), as seen in the code snippet below:

Config init_cfg = doc.GetOCGConfig();
Context ctx = new Context(init_cfg);

Obj ocgs = doc.GetOCGs(); // Get the array of all OCGs in the document.
if (ocgs != null)
{
    int i, sz = ocgs.Size();
    for (i = 0; i < sz; ++i)
    {
        Group ocg = new Group(ocgs.GetAt(i));
        bool ocgState = ocg.GetCurrentState(ctx); // check if ocg is OFF or ON
        string ocgName = ocg.GetName();
    }
}

So far, so good. However, I do not see any way to remove layers within these classes. Am I missing something? If not, is there another way in the SDK to accomplish removing layers?

Thanks in advance!

Okay, so after much more studying of the SDK samples and much experimentation, I have found one approach which works so far with the PDFs I have tried. The solution is based on the ElementEdit sample, coupled with the Context.SetNonOCDrawing(), Context.SetOCDrawMode(), and Element.IsOCVisible() methods to leave out page elements which are not visible based on the ON/OFF state of the layer. The end result is the removal of the content for layers which are off, with the layer itself still left behind but now empty. Not perfect, but good enough for my purposes. Anyway, here is the code I came up with based on the SDK sample:

private void ProcessElements(ElementReader reader, ElementWriter writer, XSet visited)
{
Element element;
while ((element = reader.Next()) != null) // Read page contents
{

switch (element.GetType())
{
//always write these element types (visible or non-visible)
case Element.Type.e_marked_content_begin:
case Element.Type.e_marked_content_end:
case Element.Type.e_marked_content_point:
case Element.Type.e_group_begin:
case Element.Type.e_group_end:
writer.WriteElement(element);
continue;
}

// do not retain other elements that are not visible
if (!element.IsOCVisible())
continue;

// retain remaining element types if visible
switch (element.GetType())
{

// special processing for form elements
case Element.Type.e_form:
{
writer.WriteElement(element); // write Form XObject reference to current stream

Obj form_obj = element.GetXObject();
if (!visited.Contains(form_obj.GetObjNum())) // if this XObject has not been processed
{
// recursively process the Form XObject
visited.Add(form_obj.GetObjNum());
ElementWriter new_writer = new ElementWriter();

reader.FormBegin();
new_writer.Begin(form_obj, true);
ProcessElements(reader, new_writer, visited);
new_writer.End();
reader.End();
}
break;
}

default:
writer.WriteElement(element);
break;
}
}
}

private void RemoveNonVisibleLayers_Click(string filename, string outputfile)
{

PDFNet.Initialize();

try
{

// Open the test file
XSet visited = new XSet();

using (PDFDoc doc = new PDFDoc(filename))
using (ElementReader page_reader = new ElementReader())
using (ElementWriter writer = new ElementWriter())
{
doc.InitSecurityHandler();
Config init_cfg = doc.GetOCGConfig();
Context ctx = new Context(init_cfg);

ctx.SetNonOCDrawing(false);
ctx.SetOCDrawMode(Context.OCDrawMode.e_VisibleOC);

PageIterator itr;
for (itr = doc.GetPageIterator(); itr.HasNext(); itr.Next()) // Read every page
{
itr.GetPageNumber());

page_reader.Begin(itr.Current(), ctx);
writer.Begin(itr.Current(), ElementWriter.WriteMode.e_replacement, false);

ProcessElements(page_reader, writer, visited);
writer.End();
page_reader.End();
}

doc.Save(outputfile, SDFDoc.SaveOptions.e_remove_unused);
doc.Close();
}
}
catch (PDFNetException ee)
{
Console.WriteLine(ee.Message);
}

}

Hello Smleino,

Unfortunately the layer flattening is not so easy. If you completely skip a non-visible element, you skip also the changes of the graphic state made by that element. You should keep this changes. For example you have to change the transformation matrix, and you have to handle path clipping and text clipping even if the path or the text is hidden. (See section 4.10.2 of the PDF Specification.)

However, it is a doable task.

László

As László pointed out, optional content can still affect the graphics state. Below excerpt is from the spec “Making graphical content optional”

When a piece of optional content in a PDF file is determined that it shall be hidden, the following occurs:

• The content shall not be drawn.
• Graphics state operations, such as setting the colour, transformation matrix, and clipping, shall still be applied. In addition, graphics state side effects that arise from drawing operators shall be applied; in particular, the current text position shall be updated even for text wrapped in optional content. In other words, graphics state parameters that persist past the end of a marked-content section shall be the same whether the optional content is visible or not.

I think it would be beneficial if you explained why you are trying to remove non-visible graphical content? There might be an easier way to accomplish your goal.