@Override
    protected void processPages(PDPageTree pageTree) throws IOException {

        //this is a 0-indexed list of object refs for each page
        //we need this to map the mcids later...
        //TODO: is there a better way of getting these/doing the mapping?

        List<ObjectRef> pageRefs = new ArrayList<>();
        //STEP 1: get the page refs
        findPages(pageTree.getCOSObject().getDictionaryObject(COSName.KIDS), pageRefs);
        //confirm the right number of pages was found
        if (pageRefs.size() != pdDocument.getNumberOfPages()) {
            throw new IOException(new TikaException(
                    "Couldn't find the right number of page refs (" + pageRefs.size() +
                            ") for pages (" + pdDocument.getNumberOfPages() + ")"));
        }

        PDStructureTreeRoot structureTreeRoot =
                pdDocument.getDocumentCatalog().getStructureTreeRoot();

        //STEP 2: load the roleMap
        Map<String, HtmlTag> roleMap = loadRoleMap(structureTreeRoot.getRoleMap());

        //STEP 3: load all of the text, mapped to MCIDs
        Map<MCID, String> paragraphs = loadTextByMCID(pageTree, pageRefs);

        //STEP 4: now recurse the the structure tree root and output the structure
        //and the text bits from paragraphs

        try {
            recurse(structureTreeRoot.getK(), null, 0, paragraphs, roleMap);
        } catch (SAXException e) {
            throw new IOException(e);
        }

        //STEP 5: handle all the potentially unprocessed bits
        try {
            if (state.hrefAnchorBuilder.length() > 0) {
                xhtml.startElement("p");
                writeString(state.hrefAnchorBuilder.toString());
                xhtml.endElement("p");
            }
            for (MCID mcid : paragraphs.keySet()) {
                if (!state.processedMCIDs.contains(mcid)) {
                    if (mcid.mcid > -1) {
                        //TODO: LOG! piece of text that wasn't referenced  in the marked content
                        // tree
                        // but should have been.  If mcid == -1, this was a known item not part of
                        // content tree.
                    }

                    xhtml.startElement("p");
                    writeString(paragraphs.get(mcid));
                    xhtml.endElement("p");
                }
            }
        } catch (SAXException e) {
            throw new IOException(e);
        }
        //Step 6: for now, iterate through the pages again and do all the other handling
        //TODO: figure out when we're crossing page boundaries during the recursion
        // step above and do the page by page processing then...rather than dumping this
        // all here.
        for (PDPage page : pageTree) {
            startPage(page);
            endPage(page);
        }

    }
Converts the given PDF document (and related metadata) to a stream
of XHTML SAX events sent to the given content handler.

@param pdDocument PDF document
@param handler    SAX content handler
@param context
@param metadata   PDF metadata
@param config
@throws SAXException  if the content handler fails to process SAX events
@throws TikaException if there was an exception outside of per page processing
tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-pdf-module/src/main/java/org/apache/tika/parser/pdf/PDFMarkedContent2XHTML.java