class PdfStructureTreeRoot extends PdfDictionary {

    private final Map<Integer, PdfObject> parentTree = new HashMap<>();
    private final PdfIndirectReference reference;

    /** Next key to be used for adding to the parentTree */
    private int parentTreeNextKey = 0;

    /** Map which connects [page number] with corresponding [parentTree entry key]  */
    private final Map<Integer, Integer> pageKeysMap = new HashMap<>();

    /**
     * Holds value of property writer.
     */
    private final PdfWriter writer;

    /**
     * Creates a new instance of PdfStructureTreeRoot
     */
    PdfStructureTreeRoot(PdfWriter writer) {
        super(PdfName.STRUCTTREEROOT);
        this.writer = writer;
        reference = writer.getPdfIndirectReference();
    }

    /**
     * Maps the user tags to the standard tags. The mapping will allow a standard application to make some sense of the
     * tagged document whatever the user tags may be.
     *
     * @param used     the user tag
     * @param standard the standard tag
     */
    public void mapRole(PdfName used, PdfName standard) {
        PdfDictionary rm = (PdfDictionary) get(PdfName.ROLEMAP);
        if (rm == null) {
            rm = new PdfDictionary();
            put(PdfName.ROLEMAP, rm);
        }
        rm.put(used, standard);
    }

    /**
     * Gets the writer.
     *
     * @return the writer
     */
    public PdfWriter getWriter() {
        return this.writer;
    }

    /**
     * Gets the reference this object will be written to.
     *
     * @return the reference this object will be written to
     * @since 2.1.6 method removed in 2.1.5, but restored in 2.1.6
     */
    public PdfIndirectReference getReference() {
        return this.reference;
    }

    /**
     * Adds a reference to the existing (already added to the document) object to the parentTree.
     * This method can be used when the object is need to be referenced via /StructParent key.
     *
     * @return key which define the object record key (e.g. in /NUMS array)
     */
    public int addExistingObject(PdfIndirectReference reference) {
        int key = parentTreeNextKey;
        parentTree.put(key, reference);
        parentTreeNextKey++;
        return key;
    }

    void setPageMark(int pageNumber, PdfIndirectReference reference) {
        PdfArray pageArray = (PdfArray) parentTree.get(getOrCreatePageKey(pageNumber));
        pageArray.add(reference);
    }

    /**
     * Returns array ID for a page-related entry or creates a new one if not exists.
     * Can be used for STRUCTPARENTS tag value
     *
     * @param pageNumber number of page for which the ID is required
     * @return Optional with array ID, empty Optional otherwise
     */
    int getOrCreatePageKey(int pageNumber) {<FILL_FUNCTION_BODY>}

    private void nodeProcess(PdfDictionary dictionary, PdfIndirectReference reference)
            throws IOException {
        PdfObject obj = dictionary.get(PdfName.K);
        if (obj != null && obj.isArray() && !((PdfArray) obj).getElements().isEmpty() && !((PdfArray) obj).getElements()
                .get(0).isNumber()) {
            PdfArray ar = (PdfArray) obj;
            for (int k = 0; k < ar.size(); ++k) {
                PdfObject pdfObj = ar.getDirectObject(k);

                if (pdfObj instanceof PdfStructureElement e) {
                    ar.set(k, e.getReference());
                    nodeProcess(e, e.getReference());
                } else if (pdfObj instanceof PdfIndirectReference) {
                    ar.set(k, pdfObj);
                }
            }
        }
        if (reference != null) {
            writer.addToBody(dictionary, reference);
        }
    }

    void buildTree() throws IOException {
        Map<Integer, PdfIndirectReference> numTree = new HashMap<>();
        for (Integer i : parentTree.keySet()) {
            PdfObject pdfObj = parentTree.get(i);
            if (pdfObj instanceof PdfIndirectReference pdfRef) {
                //saving the reference to the object which was already added to the body
                numTree.put(i, pdfRef);
            } else {
                numTree.put(i, writer.addToBody(pdfObj).getIndirectReference());
            }
        }
        PdfDictionary dicTree = PdfNumberTree.writeTree(numTree, writer);
        if (dicTree != null) {
            put(PdfName.PARENTTREE, writer.addToBody(dicTree).getIndirectReference());
        }

        nodeProcess(this, reference);
    }
}

        Integer entryForPageArray = pageKeysMap.get(pageNumber);
        if (entryForPageArray == null) {
            //putting page array
            PdfArray ar = new PdfArray();
            entryForPageArray = parentTreeNextKey;
            parentTree.put(entryForPageArray, ar);
            parentTreeNextKey++;
            pageKeysMap.put(pageNumber, entryForPageArray);
        }
        return entryForPageArray;
    
<methods>public void <init>() ,public void <init>(com.lowagie.text.pdf.PdfName) ,public void clear() ,public boolean contains(com.lowagie.text.pdf.PdfName) ,public com.lowagie.text.pdf.PdfObject get(com.lowagie.text.pdf.PdfName) ,public com.lowagie.text.pdf.PdfArray getAsArray(com.lowagie.text.pdf.PdfName) ,public com.lowagie.text.pdf.PdfBoolean getAsBoolean(com.lowagie.text.pdf.PdfName) ,public com.lowagie.text.pdf.PdfDictionary getAsDict(com.lowagie.text.pdf.PdfName) ,public com.lowagie.text.pdf.PdfIndirectReference getAsIndirectObject(com.lowagie.text.pdf.PdfName) ,public com.lowagie.text.pdf.PdfName getAsName(com.lowagie.text.pdf.PdfName) ,public com.lowagie.text.pdf.PdfNumber getAsNumber(com.lowagie.text.pdf.PdfName) ,public com.lowagie.text.pdf.PdfStream getAsStream(com.lowagie.text.pdf.PdfName) ,public com.lowagie.text.pdf.PdfString getAsString(com.lowagie.text.pdf.PdfName) ,public com.lowagie.text.pdf.PdfObject getDirectObject(com.lowagie.text.pdf.PdfName) ,public Set<com.lowagie.text.pdf.PdfName> getKeys() ,public Set<Entry<com.lowagie.text.pdf.PdfName,com.lowagie.text.pdf.PdfObject>> getKeysAndValues() ,public boolean isCatalog() ,public boolean isFont() ,public boolean isOutlineTree() ,public boolean isPage() ,public boolean isPages() ,public void merge(com.lowagie.text.pdf.PdfDictionary) ,public void mergeDifferent(com.lowagie.text.pdf.PdfDictionary) ,public void put(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfObject) ,public void putAll(com.lowagie.text.pdf.PdfDictionary) ,public void putEx(com.lowagie.text.pdf.PdfName, com.lowagie.text.pdf.PdfObject) ,public void remove(com.lowagie.text.pdf.PdfName) ,public int size() ,public void toPdf(com.lowagie.text.pdf.PdfWriter, java.io.OutputStream) throws java.io.IOException,public java.lang.String toString() <variables>public static final com.lowagie.text.pdf.PdfName CATALOG,public static final com.lowagie.text.pdf.PdfName FONT,public static final com.lowagie.text.pdf.PdfName OUTLINES,public static final com.lowagie.text.pdf.PdfName PAGE,public static final com.lowagie.text.pdf.PdfName PAGES,private com.lowagie.text.pdf.PdfName dictionaryType,protected Map<com.lowagie.text.pdf.PdfName,com.lowagie.text.pdf.PdfObject> hashMap