@Override
    public void endDocument() throws SAXException {
        super.endDocument();

        td = toTextDocument();
        try {
            extractor.process(td);
        } catch (BoilerpipeProcessingException e) {
            throw new SAXException(e);
        }

        Attributes emptyAttrs = new AttributesImpl();

        // At this point we have all the information we need to either emit N paragraphs
        // of plain text (if not including markup), or we have to replay our recorded elements
        // and only emit character runs that passed the boilerpipe filters.
        if (includeMarkup) {
            BitSet validCharacterRuns = new BitSet();
            for (TextBlock block : td.getTextBlocks()) {
                if (block.isContent()) {
                    BitSet bs = block.getContainedTextElements();
                    if (bs != null) {
                        validCharacterRuns.or(bs);
                    }
                }
            }

            // Now have bits set for all valid character runs. Replay our recorded elements,
            // but only emit character runs flagged as valid.
            int curCharsIndex = headerCharOffset;

            for (RecordedElement element : elements) {
                switch (element.getElementType()) {
                    case START:
                        delegate.startElement(element.getUri(), element.getLocalName(),
                                element.getQName(), element.getAttrs());
                        // Fall through

                    case CONTINUE:
                        // Now emit characters that are valid. Note that boilerpipe
                        // pre-increments the character index, so
                        // we have to follow suit.
                        for (int i = 0; i < element.getCharacters().size(); i++) {
                            char[] chars = element.getCharacters().get(i);
                            curCharsIndex++;
                            boolean isValidCharacterRun = validCharacterRuns.get(curCharsIndex);

                            // https://issues.apache.org/jira/projects/TIKA/issues/TIKA-2683
                            // Allow exempted characters to be written
                            if (isValidCharacterRun ||
                                    (chars.length == 1 && ALLOWABLE_CHARS.contains(chars[0]))) {
                                delegate.characters(chars, 0, chars.length);
                            }

                            // https://issues.apache.org/jira/browse/TIKA-961
                            if (isValidCharacterRun && i == element.getCharacters().size() - 1 &&
                                    !Character.isWhitespace(chars[chars.length - 1])) {
                                // Only add whitespace for certain elements
                                if (XHTMLContentHandler.ENDLINE.contains(element.getLocalName())) {
                                    delegate.ignorableWhitespace(NL, 0, NL.length);
                                }
                            }
                        }
                        break;

                    case END:
                        delegate.endElement(element.getUri(), element.getLocalName(),
                                element.getQName());
                        break;

                    default:
                        throw new RuntimeException(
                                "Unhandled element type: " + element.getElementType());
                }


            }
        } else {
            for (TextBlock block : td.getTextBlocks()) {
                if (block.isContent()) {
                    delegate.startElement(XHTMLContentHandler.XHTML, "p", "p", emptyAttrs);
                    char[] chars = block.getText().toCharArray();
                    delegate.characters(chars, 0, chars.length);
                    delegate.endElement(XHTMLContentHandler.XHTML, "p", "p");
                    delegate.ignorableWhitespace(NL, 0, NL.length);
                }
            }
        }

        delegate.endElement(XHTMLContentHandler.XHTML, "body", "body");
        delegate.endElement(XHTMLContentHandler.XHTML, "html", "html");

        // We defer ending any prefix mapping until here, which is why we don't pass this
        // through to the delegate in an overridden method.
        delegate.endPrefixMapping("");

        delegate.endDocument();
    }
Retrieves the built TextDocument

@return TextDocument
tika-handlers/tika-handler-boilerpipe/src/main/java/org/apache/tika/sax/boilerpipe/BoilerpipeContentHandler.java