Can PDFNet SDK be used to remove non-visible PDF layers (OCGs), including content?

351 Views Asked by At

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 and their content 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 
        if (!ocgState) // layer is not visible
        {
            // need calls here to delete layer and content
        }
    } 
} 

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

Thanks in advance!

1

There are 1 best solutions below

0
On

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

    }