it.flavianopetrocchi.jpdfbookmarks.itextbookmarksconverter.iTextBookmarksConverter.java Source code

Java tutorial

Introduction

Here is the source code for it.flavianopetrocchi.jpdfbookmarks.itextbookmarksconverter.iTextBookmarksConverter.java

Source

/*
 * ITextBookmarksConverter.java
 *
 * Copyright (c) 2010 Flaviano Petrocchi <flavianopetrocchi at gmail.com>.
 * All rights reserved.
 *
 * This file is part of JPdfBookmarks.
 *
 * JPdfBookmarks is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * JPdfBookmarks is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with JPdfBookmarks.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * Portions of this code are derived from iText-2.1.7 source code in
 * com/lowagie/text/pdf/SimpleBookmark.java which has the following copyright
 * and license.
 *
 * Copyright 2003 by Paulo Soares.
 *
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the License.
 *
 * The Original Code is 'iText, a free JAVA-PDF library'.
 *
 * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
 * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
 * All Rights Reserved.
 * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
 * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
 *
 * Contributor(s): all the names of the contributors are added in the source code
 * where applicable.
 *
 * Alternatively, the contents of this file may be used under the terms of the
 * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
 * provisions of LGPL are applicable instead of those above.  If you wish to
 * allow use of your version of this file only under the terms of the LGPL
 * License and not to allow others to use your version of this file under
 * the MPL, indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by the LGPL.
 * If you do not delete the provisions above, a recipient may use your version
 * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the MPL as stated above or under the terms of the GNU
 * Library General Public License as published by the Free Software Foundation;
 * either version 2 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
 * details.
 *
 * If you didn't download this code from the following link, you should check if
 * you aren't using an obsolete version:
 * http://www.lowagie.com/iText/
 */
package it.flavianopetrocchi.jpdfbookmarks.itextbookmarksconverter;

import com.lowagie.text.DocumentException;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.ByteBuffer;
import com.lowagie.text.pdf.IntHashtable;
import com.lowagie.text.pdf.PdfArray;
import com.lowagie.text.pdf.PdfBoolean;
import com.lowagie.text.pdf.PdfDictionary;
import com.lowagie.text.pdf.PdfIndirectReference;
import com.lowagie.text.pdf.PdfName;
import com.lowagie.text.pdf.PdfNumber;
import com.lowagie.text.pdf.PdfObject;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfRectangle;
import com.lowagie.text.pdf.PdfString;
import com.lowagie.text.pdf.PdfWriter;
import com.lowagie.text.pdf.PdfStamper;
import it.flavianopetrocchi.jpdfbookmarks.bookmark.Bookmark;
import it.flavianopetrocchi.jpdfbookmarks.bookmark.BookmarkType;
import it.flavianopetrocchi.jpdfbookmarks.bookmark.IBookmarksConverter;
import it.flavianopetrocchi.utilities.Ut;
import java.awt.Color;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class iTextBookmarksConverter implements IBookmarksConverter {

    private PdfReader reader;
    private PdfStamper stamper;
    private Bookmark rootBookmark;
    private String filePath;
    private List outline;
    private boolean showOnOpen = false;
    private HashMap namesAsString;
    private HashMap namesAsName;
    private IntHashtable pages;

    public iTextBookmarksConverter() {
    }

    public iTextBookmarksConverter(String string) throws IOException {
        open(string);
    }

    @Override
    public void close() throws IOException {
        if (reader != null) {
            reader.close();
        }
        reader = null;
        stamper = null;
        outline = null;
        rootBookmark = null;
        filePath = null;
        namesAsString = null;
    }

    @Override
    public Bookmark getRootBookmark(boolean convertNamedDestinations) {
        if (rootBookmark == null) {
            if (convertNamedDestinations) {
                reader.consolidateNamedDestinations();
            }
            rootBookmark = getBookmark();
        }
        return rootBookmark;
    }

    //    @Override
    //    public boolean isBookmarksEditingPermitted() {
    //        boolean permitted = true;
    //        if (reader.isEncrypted()) {
    //            int permissions = reader.getPermissions();
    //            if ((permissions & PdfWriter.ALLOW_ASSEMBLY) == PdfWriter.ALLOW_ASSEMBLY) {
    //                permitted = true;
    //            } else {
    //                permitted = false;
    //            }
    //        }
    //        return permitted;
    //    }

    @Override
    public boolean isEncryped() {
        return reader.isEncrypted();
    }

    @Override
    public final void open(String pdfPath) throws IOException {
        //        if (reader != null) {
        //            close();
        //        }
        //        this.filePath = pdfPath;
        //        //byte[] fileBytes = Ut.getBytesFromFile(new File(pdfPath));
        //        //random access seems to slow down loading a lot
        //        //reader = new PdfReader(new RandomAccessFileOrArray(fileBytes), null);
        //        //reader = new PdfReader(fileBytes);
        //        reader = new PdfReader(pdfPath);
        //        //reader = new PdfReader(new RandomAccessFileOrArray(this.filePath), null);
        //        int preferences = reader.getSimpleViewerPreferences();
        //        if ((preferences & PdfWriter.PageModeUseOutlines) == 0) {
        //            showOnOpen = false;
        //        } else {
        //            showOnOpen = true;
        //        }
        open(pdfPath, null);
    }

    @Override
    public void open(String pdfPath, byte[] password) throws IOException {
        if (reader != null) {
            close();
        }
        this.filePath = pdfPath;
        if (password != null) {
            reader = new PdfReader(pdfPath, password);
        } else {
            reader = new PdfReader(pdfPath);
        }
        int preferences = reader.getSimpleViewerPreferences();
        if ((preferences & PdfWriter.PageModeUseOutlines) == 0) {
            showOnOpen = false;
        } else {
            showOnOpen = true;
        }
    }

    @Override
    public boolean isBookmarksEditingPermitted() {
        //for what we are concerned if we can write bookmarks it is not encrypted
        //return ((reader.getPermissions() & PdfWriter.ALLOW_ASSEMBLY) == PdfWriter.ALLOW_ASSEMBLY);
        return reader.isOpenedWithFullPermissions();
    }

    @Override
    public void rebuildBookmarksFromTreeNodes(Bookmark root) {
        Bookmark current;
        try {
            current = (Bookmark) root.getFirstChild();
        } catch (Exception exc) {
            current = null;
        }

        outline = new ArrayList();
        while (current != null) {
            outline.add(rebuildBookmarksRecursive(current));
            current = (Bookmark) current.getNextSibling();
        }

    }

    private HashMap rebuildBookmarksRecursive(Bookmark node) {

        HashMap bookmarkMap = bookmarkToHashMap(node);

        Bookmark current;
        try {
            current = (Bookmark) node.getFirstChild();
        } catch (Exception exc) {
            current = null;
        }

        ArrayList kids = new ArrayList();
        while (current != null) {
            HashMap childMap = rebuildBookmarksRecursive(current);
            if (childMap != null) {
                kids.add(childMap);
            }
            current = (Bookmark) current.getNextSibling();
        }
        if (!kids.isEmpty()) {
            bookmarkMap.put("Kids", kids);
        }

        return bookmarkMap;
    }

    private HashMap bookmarkToHashMap(Bookmark bookmark) {
        HashMap map = new HashMap();

        map.put("Title", bookmark.getTitle().trim());

        if (bookmark.isOpened()) {
            map.put("Open", "true");
        } else {
            map.put("Open", "false");
        }

        if (bookmark.getColor().equals(Color.black) == false) {
            float[] rgb = bookmark.getColor().getRGBColorComponents(null);
            map.put("Color", String.valueOf(rgb[0]) + " " + String.valueOf(rgb[1]) + " " + String.valueOf(rgb[2]));
        }
        String style = "";
        if (bookmark.isBold()) {
            style += "bold ";
        }
        if (bookmark.isItalic()) {
            style += "italic ";
        }
        if (!style.isEmpty()) {
            map.put("Style", style.trim());
        }

        BookmarkType type = bookmark.getType();

        if (type == BookmarkType.Launch) {
            map.put("Action", "Launch");
            map.put("File", bookmark.getFileToLaunch());
        } else if (type == BookmarkType.Uri) {
            map.put("Action", "URI");
            map.put("URI", bookmark.getUri());
        } else if (type == BookmarkType.Hide) {
            map.put("Action", "Hide");
            map.put("FieldName", bookmark.getFieldNameToHide());
            map.put("Hide", String.valueOf(bookmark.isHide()));
        } else if (type != BookmarkType.Unknown) {
            if (bookmark.isRemoteDestination()) {
                map.put("Action", "GoToR");
                String path = Ut.onWindowsReplaceBackslashWithSlash(bookmark.getRemoteFilePath());
                map.put("File", path);
                map.put("NewWindow", String.valueOf(bookmark.isNewWindow()));
            } else {
                map.put("Action", "GoTo");
                //                if (type == BookmarkType.Named) {
                //                    map.put("Named", bookmark.getNamedDestination());
                //                }
            }

            if (type == BookmarkType.Named) {
                if (bookmark.isNamedAsName()) {
                    map.put("NamedN", bookmark.getNamedDestination());
                } else {
                    map.put("Named", bookmark.getNamedDestination());
                }
            } else {
                StringBuilder pageDest = new StringBuilder();
                if (bookmark.isRemoteDestination()) {
                    pageDest.append(String.valueOf(bookmark.getPageNumber() - 1));
                } else {
                    pageDest.append(String.valueOf(bookmark.getPageNumber()));
                }
                int left = bookmark.getLeft();
                int right = bookmark.getRight();
                int top = bookmark.getTop();
                int bottom = bookmark.getBottom();
                float zoom = bookmark.getZoom();
                if (type == BookmarkType.TopLeftZoom) {
                    pageDest.append(" XYZ ");
                    pageDest.append(left == -1 ? "null" : left).append(" ");
                    pageDest.append(top == -1 ? "null" : top).append(" ").append(zoom);
                } else if (type == BookmarkType.FitPage) {
                    pageDest.append(" Fit");
                } else if (type == BookmarkType.FitWidth) {
                    pageDest.append(" FitH ").append(top == -1 ? "null" : top);
                } else if (type == BookmarkType.FitHeight) {
                    pageDest.append(" FitV ").append(left == -1 ? "null" : left);
                } else if (type == BookmarkType.FitRect) {
                    pageDest.append(" FitR ").append(left == -1 ? "null" : left).append(" ")
                            .append(bottom == -1 ? "null" : bottom).append(" ").append(right == -1 ? "null" : right)
                            .append(" ").append(top == -1 ? "null" : top);
                } else if (type == BookmarkType.FitContent) {
                    pageDest.append(" FitB");
                } else if (type == BookmarkType.FitContentWidth) {
                    pageDest.append(" FitBH ").append(top == -1 ? "null" : top);
                } else if (type == BookmarkType.FitContentHeight) {
                    pageDest.append(" FitBV ").append(left == -1 ? "null" : left);
                }
                map.put("Page", pageDest.toString());
            }
        } else {
            //Unknown type
        }

        ArrayList<Bookmark> chainedBookmarks = bookmark.getChainedBookmarks();
        if (!chainedBookmarks.isEmpty()) {
            ArrayList<HashMap> chainedBookmarksMap = new ArrayList<HashMap>();
            for (Bookmark b : chainedBookmarks) {
                chainedBookmarksMap.add(bookmarkToHashMap(b));
            }
            map.put("Next", chainedBookmarksMap);
        }

        return map;
    }

    @Override
    public void createUnencryptedCopy(File tmpFile) throws IOException {
        try {
            PdfStamper tmpStamper = new PdfStamper(reader, new FileOutputStream(tmpFile));
            tmpStamper.close();
        } catch (DocumentException ex) {
        }
    }

    @Override
    public void save(String filePath) throws IOException {
        save(filePath, null);
    }

    @Override
    public void save(String filePath, byte[] ownerPassword) throws IOException {
        save(filePath, null, null);
    }

    public void save(String path, byte[] userPassword, byte[] ownerPassword) throws IOException {
        try {
            File tmp = File.createTempFile("jpdf", ".pdf");
            tmp.deleteOnExit();
            Ut.copyFile(this.filePath, tmp.getPath());
            PdfReader tmpReader;
            if (ownerPassword != null) {
                tmpReader = new PdfReader(tmp.getPath(), ownerPassword);
            } else if (userPassword != null) {
                tmpReader = new PdfReader(tmp.getPath(), userPassword);
            } else {
                tmpReader = new PdfReader(tmp.getPath());
            }

            stamper = new PdfStamper(tmpReader, new FileOutputStream(path));
            if (outline != null) {
                stamper.setOutlines(outline);
            }
            int preferences = reader.getSimpleViewerPreferences();
            if (showOnOpen) {
                preferences |= PdfWriter.PageModeUseOutlines;
            } else {
                preferences = preferences & ~PdfWriter.PageModeUseOutlines;
            }
            stamper.setViewerPreferences(preferences);

            if (ownerPassword != null || userPassword != null) {
                //                tmpReader.computeUserPassword();
                if (ownerPassword == null && userPassword != null) {
                    ownerPassword = userPassword;
                }
                stamper.setEncryption(userPassword, ownerPassword, tmpReader.getPermissions(),
                        tmpReader.getCryptoMode());
            }
            stamper.close();
            tmp.delete();
        } catch (DocumentException ex) {
        }
    }

    private void initPages() {
        pages = new IntHashtable();
        int numPages = reader.getNumberOfPages();
        for (int k = 1; k <= numPages; ++k) {
            pages.put(reader.getPageOrigRef(k).getNumber(), k);
            reader.releasePage(k);
        }
    }

    private Bookmark getBookmark() {
        PdfDictionary catalog = reader.getCatalog();
        PdfObject obj = PdfReader.getPdfObjectRelease(catalog.get(PdfName.OUTLINES));
        if (obj == null || !obj.isDictionary()) {
            return null;
        }
        PdfDictionary outlines = (PdfDictionary) obj;
        //        pages = new IntHashtable();
        //        int numPages = reader.getNumberOfPages();
        //        for (int k = 1; k <= numPages; ++k) {
        //            pages.put(reader.getPageOrigRef(k).getNumber(), k);
        //            reader.releasePage(k);
        //        }
        initPages();

        Bookmark root = new Bookmark();
        root.setTitle("Root Bookmark");
        bookmarkDepth(root, (PdfDictionary) PdfReader.getPdfObjectRelease(outlines.get(PdfName.FIRST)));
        //        bookmarkDepthIterative(root, outlines);
        return root;
    }

    private void setActionInBookmark(Bookmark bookmark, PdfDictionary action) {
        PdfObject dest;
        if (PdfName.GOTO.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) {
            dest = PdfReader.getPdfObjectRelease(action.get(PdfName.D));
            if (dest != null) {
                mapGotoBookmark(bookmark, dest);
            }
        } else if (PdfName.URI.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) {
            bookmark.setType(BookmarkType.Uri);
            bookmark.setUri(((PdfString) PdfReader.getPdfObjectRelease(action.get(PdfName.URI))).toUnicodeString());
        } else if (PdfName.GOTOR.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) {
            bookmark.setRemoteDestination(true);
            PdfObject file = PdfReader.getPdfObjectRelease(action.get(PdfName.F));
            if (file != null) {
                if (file.isString()) {
                    String path = Ut.onWindowsReplaceBackslashWithSlash(((PdfString) file).toUnicodeString());
                    bookmark.setRemoteFilePath(path);
                } else if (file.isDictionary()) {
                    file = PdfReader.getPdfObject(((PdfDictionary) file).get(PdfName.F));
                    if (file.isString()) {
                        bookmark.setRemoteFilePath(((PdfString) file).toUnicodeString());
                    }
                }
            }
            dest = PdfReader.getPdfObjectRelease(action.get(PdfName.D));
            if (dest != null) {
                if (dest.isString()) {
                    bookmark.setNamedDestination(dest.toString());
                } else if (dest.isName()) {
                    bookmark.setNamedDestination(PdfName.decodeName(dest.toString()));
                    bookmark.setNamedAsName(true);
                } else if (dest.isArray()) {
                    PdfArray arr = (PdfArray) dest;
                    PdfReader remoteReader;
                    try {
                        //                        File remoteFile = new File(bookmark.getRemoteFilePath());
                        //                        if (!remoteFile.isAbsolute()) {
                        //                            File openedFile = new File(filePath);
                        //                            String containingFolder = openedFile.getParent();
                        //                            String remotePath = containingFolder + File.separator + bookmark.getRemoteFilePath();
                        //                            remoteFile = new File(remotePath);
                        //                        }
                        File remoteFile = Ut.createAbsolutePath(new File(filePath),
                                new File(bookmark.getRemoteFilePath()));
                        remoteReader = new PdfReader(remoteFile.getCanonicalPath());
                        makeBookmarkParam(remoteReader, bookmark, arr, null);
                        remoteReader.close();
                    } catch (IOException ex) {
                        //System.out.println(ex.getMessage());
                    } finally {
                    }
                }
            }
            PdfObject newWindow = PdfReader.getPdfObjectRelease(action.get(PdfName.NEWWINDOW));
            if (newWindow != null) {
                bookmark.setNewWindow(((PdfBoolean) newWindow).booleanValue());
            }
        } else if (PdfName.LAUNCH.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) {
            bookmark.setType(BookmarkType.Launch);
            PdfObject file = PdfReader.getPdfObjectRelease(action.get(PdfName.F));
            if (file == null) {
                file = PdfReader.getPdfObjectRelease(action.get(PdfName.WIN));
            }
            if (file != null) {
                if (file.isString()) {
                    bookmark.setFileToLaunch(((PdfString) file).toUnicodeString());
                } else if (file.isDictionary()) {
                    file = PdfReader.getPdfObjectRelease(((PdfDictionary) file).get(PdfName.F));
                    if (file.isString()) {
                        bookmark.setFileToLaunch(((PdfString) file).toUnicodeString());
                    }
                }
            }
        } else if (PdfName.HIDE.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) {
            bookmark.setType(BookmarkType.Hide);
            PdfObject annotation = PdfReader.getPdfObjectRelease(action.get(PdfName.T));
            if (annotation != null) {
                if (annotation.isDictionary()) {
                } else if (annotation.isArray()) {
                } else if (annotation.isString()) {
                    bookmark.setFieldNameToHide(((PdfString) annotation).toUnicodeString());
                }
            }
            PdfBoolean hide = (PdfBoolean) PdfReader.getPdfObjectRelease(action.get(PdfName.H));
            if (hide != null) {
                bookmark.setHide(hide.booleanValue());
            }
        } else {
            bookmark.setType(BookmarkType.Unknown);
        }
    }

    private void setActionsRecursive(Bookmark bookmark, PdfDictionary action) {

        setActionInBookmark(bookmark, action);

        PdfObject next = PdfReader.getPdfObjectRelease(action.get(PdfName.NEXT));
        if (next != null) {
            if (next.isArray()) {
                PdfArray actions = (PdfArray) next;
                for (int i = 0; i < actions.size(); i++) {
                    Bookmark b = new Bookmark();
                    action = actions.getAsDict(i);
                    setActionsRecursive(b, action);
                    bookmark.addChainedBookmark(b);
                }
            } else if (next.isDictionary()) {
                Bookmark b = new Bookmark();
                action = (PdfDictionary) next;
                setActionsRecursive(b, action);
                bookmark.addChainedBookmark(b);
            }
        }
    }

    private Bookmark bookmarkFromDictionary(PdfDictionary outline) {
        if (outline == null) {
            return null;
        }
        Bookmark bookmark = new Bookmark();
        PdfString title = (PdfString) PdfReader.getPdfObjectRelease(outline.get(PdfName.TITLE));
        bookmark.setTitle(title.toUnicodeString());
        PdfArray color = (PdfArray) PdfReader.getPdfObjectRelease(outline.get(PdfName.C));
        if (color != null && color.size() == 3) {
            ByteBuffer out = new ByteBuffer();
            out.append(color.getAsNumber(0).floatValue()).append(' ');
            out.append(color.getAsNumber(1).floatValue()).append(' ');
            out.append(color.getAsNumber(2).floatValue());
            bookmark.setColor(new Color(color.getAsNumber(0).floatValue(), color.getAsNumber(1).floatValue(),
                    color.getAsNumber(2).floatValue()));
        }
        PdfNumber style = (PdfNumber) PdfReader.getPdfObjectRelease(outline.get(PdfName.F));
        if (style != null) {
            int f = style.intValue();
            if ((f & 1) != 0) {
                bookmark.setItalic(true);
            }
            if ((f & 2) != 0) {
                bookmark.setBold(true);
            }
        }
        PdfNumber count = (PdfNumber) PdfReader.getPdfObjectRelease(outline.get(PdfName.COUNT));
        if (count != null && count.intValue() < 0) {
            bookmark.setOpened(false);
        } else {
            bookmark.setOpened(true);
        }
        try {
            PdfObject dest = PdfReader.getPdfObjectRelease(outline.get(PdfName.DEST));
            if (dest != null) {
                mapGotoBookmark(bookmark, dest);
            } else {
                PdfDictionary action = (PdfDictionary) PdfReader.getPdfObjectRelease(outline.get(PdfName.A));
                if (action != null) {
                    setActionsRecursive(bookmark, action);
                } else {
                    bookmark.setType(BookmarkType.Unknown);
                }
            }
        } catch (Exception e) {
            //empty on purpose
        }
        return bookmark;
    }

    //    private void bookmarkDepthIterative(Bookmark root,
    //            PdfDictionary outlineDict) {
    //        Stack<Bookmark> fathers = new Stack<Bookmark>();
    //        Stack<PdfDictionary> fathersDict = new Stack<PdfDictionary>();
    //        Bookmark bookmark = null;
    //        PdfDictionary dict = null;
    //
    //        fathers.push(root);
    //        //fathersDict.push(outlineDict);
    //
    //        //Bookmark fatherBkmk = fathers.peek();
    //        dict = (PdfDictionary) PdfReader.getPdfObjectRelease(
    //                    outlineDict.get(PdfName.FIRST));
    //
    //        //iterate brothers
    //        while (dict != null) {
    //            fathersDict.push(dict); //presume it's a father
    //            fathers.push(bookmarkFromDictionary(dict));
    //
    //            //try to get its child
    //            dict = (PdfDictionary) PdfReader.getPdfObjectRelease(
    //                    dict.get(PdfName.FIRST));
    //            //iterate children
    //            while (dict != null) {
    //                //presume the child is a father
    //                fathersDict.push(dict);
    //                fathers.push(bookmarkFromDictionary(dict));
    //
    //                dict = (PdfDictionary) PdfReader.getPdfObjectRelease(
    //                        dict.get(PdfName.FIRST));
    //            }
    //
    //            dict = fathersDict.pop();
    //            bookmark = fathers.pop();
    //            fathers.peek().add(bookmark);
    //
    //            dict = (PdfDictionary) PdfReader.getPdfObjectRelease(
    //                    dict.get(PdfName.NEXT));
    //            while (dict == null && !fathersDict.isEmpty()) {
    //                dict = fathersDict.pop();
    //                bookmark = fathers.pop();
    //                fathers.peek().add(bookmark);
    //                dict = (PdfDictionary) PdfReader.getPdfObjectRelease(
    //                    dict.get(PdfName.NEXT));
    //            }
    //        }
    //    }
    private void bookmarkDepth(Bookmark father, PdfDictionary outline) {
        Bookmark bookmark = null;
        while (outline != null) {
            bookmark = bookmarkFromDictionary(outline);
            PdfDictionary first = (PdfDictionary) PdfReader.getPdfObjectRelease(outline.get(PdfName.FIRST));
            if (first != null) {
                bookmarkDepth(bookmark, first);
            }
            father.add(bookmark);
            outline = (PdfDictionary) PdfReader.getPdfObjectRelease(outline.get(PdfName.NEXT));
        }

    }

    private void mapGotoBookmark(Bookmark bookmark, PdfObject dest) {
        if (dest.isString()) {
            if (namesAsString == null) {
                namesAsString = reader.getNamedDestinationFromStrings();
            }
            bookmark.setType(BookmarkType.Named);
            bookmark.setNamedDestination(dest.toString());
            PdfArray namedDest = getNamedDestination(reader, dest.toString(), false);
            Bookmark namedTarget = new Bookmark();
            namedTarget.setTitle(dest.toString());
            makeBookmarkParam(reader, namedTarget, (PdfArray) namedDest, pages);
            bookmark.setNamedTarget(namedTarget);
        } else if (dest.isName()) {
            if (namesAsName == null) {
                namesAsName = reader.getNamedDestinationFromNames();
            }
            bookmark.setType(BookmarkType.Named);
            String name = PdfName.decodeName(dest.toString());
            bookmark.setNamedDestination(name);
            bookmark.setNamedAsName(true);
            PdfArray namedDest = getNamedDestination(reader, name, true);
            Bookmark namedTarget = new Bookmark();
            namedTarget.setTitle(name);
            makeBookmarkParam(reader, namedTarget, (PdfArray) namedDest, pages);
            bookmark.setNamedTarget(namedTarget);
        } else if (dest.isArray()) {
            makeBookmarkParam(reader, bookmark, (PdfArray) dest, pages);
        }
    }

    private PdfArray getNamedDestination(PdfReader reader, String dest, boolean fromNames) {
        PdfArray arr = null;
        for (Iterator it = namesAsString.entrySet().iterator(); it.hasNext();) {
            Map.Entry entry = (Map.Entry) it.next();
            String key = (String) entry.getKey();
            if (fromNames) {
                if (dest.equals(key)) {
                    arr = (PdfArray) entry.getValue();
                    break;
                }
            } else if (dest.equals(key)) {
                arr = (PdfArray) entry.getValue();
                break;
            }
        }
        return arr;
    }

    private String makeBookmarkParam(PdfReader reader, Bookmark bookmark, PdfArray dest, IntHashtable pages) {
        StringBuilder s = new StringBuilder();
        PdfObject obj = dest.getPdfObject(0);
        if (obj.isNumber()) {
            s.append(((PdfNumber) obj).intValue() + 1);
            bookmark.setPageNumber(((PdfNumber) obj).intValue() + 1);
        } else {
            if (pages == null) {
                initPages();
            }
            if (pages != null) {
                s.append(pages.get(getNumber((PdfIndirectReference) obj))); //changed by ujihara 2004-06-13
                bookmark.setPageNumber(pages.get(getNumber((PdfIndirectReference) obj)));
            }
        }

        Rectangle pageSize = null;
        //if (bookmark.getType().equals(BookmarkType.GoToFile) == false) {
        //if (!bookmark.isRemoteDestination()) {
        pageSize = reader.getPageSize(bookmark.getPageNumber());
        //}

        if (pageSize != null) {
            bookmark.setPageWidth(pageSize.getWidth());
            bookmark.setPageHeight(pageSize.getHeight());
        }

        String destType = dest.getPdfObject(1).toString();
        PdfObject[] params = new PdfObject[dest.size()];
        for (int k = 2; k < dest.size(); ++k) {
            params[k - 2] = dest.getPdfObject(k);
        }
        if (destType.equals("/XYZ")) {
            bookmark.setType(BookmarkType.TopLeftZoom);
            if (!params[0].isNull()) {
                bookmark.setLeft(((PdfNumber) params[0]).intValue());
            }
            if (!params[1].isNull()) {
                bookmark.setTop(((PdfNumber) params[1]).intValue());
            }
            if (pageSize != null) {
                bookmark.setThousandthsLeft(thousandthsHorizontal(bookmark.getLeft(), pageSize));
                bookmark.setThousandthsTop(thousandthsVertical(bookmark.getTop(), pageSize));
            }
            if (!params[2].isNull()) {
                bookmark.setZoom(((PdfNumber) params[2]).floatValue());
            }
        } else if (destType.equals("/FitH")) {
            bookmark.setType(BookmarkType.FitWidth);
            if (!params[0].isNull()) {
                bookmark.setTop(((PdfNumber) params[0]).intValue());
            }
            if (pageSize != null) {
                bookmark.setThousandthsTop(thousandthsVertical(bookmark.getTop(), pageSize));
            }
        } else if (destType.equals("/FitV")) {
            bookmark.setType(BookmarkType.FitHeight);
            if (!params[0].isNull()) {
                bookmark.setLeft(((PdfNumber) params[0]).intValue());
            }
            if (pageSize != null) {
                bookmark.setThousandthsLeft(thousandthsHorizontal(bookmark.getLeft(), pageSize));
            }
        } else if (destType.equals("/FitBH")) {
            bookmark.setType(BookmarkType.FitContentWidth);
            if (!params[0].isNull()) {
                bookmark.setTop(((PdfNumber) params[0]).intValue());
            }
            if (pageSize != null) {
                bookmark.setThousandthsTop(thousandthsVertical(bookmark.getTop(), pageSize));
            }
        } else if (destType.equals("/FitBV")) {
            bookmark.setType(BookmarkType.FitContentHeight);
            if (!params[0].isNull()) {
                bookmark.setLeft(((PdfNumber) params[0]).intValue());
            }
            if (pageSize != null) {
                bookmark.setThousandthsLeft(thousandthsHorizontal(bookmark.getLeft(), pageSize));
            }
        } else if (destType.equals("/Fit")) {
            bookmark.setType(BookmarkType.FitPage);
        } else if (destType.equals("/FitB")) {
            bookmark.setType(BookmarkType.FitContent);
        } else if (destType.equals("/FitR")) {
            bookmark.setType(BookmarkType.FitRect);
            if (!params[0].isNull()) {
                bookmark.setLeft(((PdfNumber) params[0]).intValue());
            }
            if (!params[1].isNull()) {
                bookmark.setBottom(((PdfNumber) params[1]).intValue());
            }
            if (!params[2].isNull()) {
                bookmark.setRight(((PdfNumber) params[2]).intValue());
            }
            if (!params[3].isNull()) {
                bookmark.setTop(((PdfNumber) params[3]).intValue());
            }
            if (pageSize != null) {
                bookmark.setThousandthsLeft(thousandthsHorizontal(bookmark.getLeft(), pageSize));
                bookmark.setThousandthsTop(thousandthsVertical(bookmark.getTop(), pageSize));
                bookmark.setThousandthsRight(thousandthsHorizontal(bookmark.getRight(), pageSize));
                bookmark.setThousandthsBottom(thousandthsVertical(bookmark.getBottom(), pageSize));
            }
        }

        s.append(' ').append(dest.getPdfObject(1).toString().substring(1));
        for (int k = 2; k < dest.size(); ++k) {
            s.append(' ').append(dest.getPdfObject(k).toString());
        }
        return s.toString();
    }

    private static int getNumber(PdfIndirectReference indirect) {
        PdfDictionary pdfObj = (PdfDictionary) PdfReader.getPdfObjectRelease(indirect);
        if (pdfObj.contains(PdfName.TYPE) && pdfObj.get(PdfName.TYPE).equals(PdfName.PAGES)
                && pdfObj.contains(PdfName.KIDS)) {
            PdfArray kids = (PdfArray) pdfObj.get(PdfName.KIDS);
            indirect = (PdfIndirectReference) kids.getPdfObject(0);
        }
        return indirect.getNumber();
    }

    public static int thousandthsHorizontal(int x, Rectangle pageRect) {
        int thousandths = -1;
        if (x != -1) {
            try {
                thousandths = (int) (x * 1000.0f / pageRect.getWidth());
            } catch (Exception exc) {
            }
        }
        return thousandths;
    }

    public static int thousandthsVertical(int y, Rectangle pageRect) {
        int thousandths = -1;
        if (y != -1) {
            try {
                thousandths = 1000 - Math.round(y * 1000.0f / pageRect.getHeight());
            } catch (Exception exc) {
            }
        }
        return thousandths;
    }

    @Override
    public boolean showBookmarksOnOpen() {
        return showOnOpen;
    }

    @Override
    public void setShowBookmarksOnOpen(boolean show) {
        showOnOpen = show;
    }

    @Override
    public float getPageWidth(int pageNumber) {
        Rectangle pageSize = reader.getPageSize(pageNumber);
        return pageSize.getWidth();
    }

    @Override
    public float getPageHeight(int pageNumber) {
        Rectangle pageSize = reader.getPageSize(pageNumber);
        return pageSize.getHeight();
    }

    @Override
    public int getCountOfPages() {
        return reader.getNumberOfPages();
    }

    public String getOpenedFilePath() {
        return filePath;
    }

    public ArrayList<AnnotationRect> getLinks(int currentPage, boolean convertNamedDestinations) {

        ArrayList<AnnotationRect> bookmarkLinks = new ArrayList<AnnotationRect>();
        ArrayList links = null;
        if (reader != null) {
            if (convertNamedDestinations) {
                reader.consolidateNamedDestinations();
            }
            links = reader.getAnnotations(currentPage);
            for (int i = 0; i < links.size(); i++) {
                AnnotationRect annoRect = new AnnotationRect();
                Bookmark bookmark = new Bookmark();
                PdfDictionary annot = (PdfDictionary) links.get(i);
                try {
                    PdfObject dest = PdfReader.getPdfObjectRelease(annot.get(PdfName.DEST));
                    if (dest != null) {
                        mapGotoBookmark(bookmark, dest);
                    } else {
                        PdfDictionary action = (PdfDictionary) PdfReader.getPdfObjectRelease(annot.get(PdfName.A));
                        if (action != null) {
                            setActionsRecursive(bookmark, action);
                        } else {
                            bookmark.setType(BookmarkType.Unknown);
                        }
                    }
                } catch (Exception e) {
                    //empty on purpose
                }
                PdfObject obj = (PdfObject) annot.get(PdfName.RECT);
                if (obj instanceof PdfArray) {
                    PdfArray rc = (PdfArray) obj;
                    annoRect.llx = (int) rc.getAsNumber(0).floatValue();
                    annoRect.lly = (int) rc.getAsNumber(1).floatValue();
                    annoRect.urx = (int) rc.getAsNumber(2).floatValue();
                    annoRect.ury = (int) rc.getAsNumber(3).floatValue();
                }
                annoRect.bookmark = bookmark;
                bookmarkLinks.add(annoRect);
            }
        }

        return bookmarkLinks;
    }

}