iText 5 itextpdf.jar Source Code

itextpdf.jar is a component in iText 5 Java library to provide core functionalities. iText Java library allows you to generate and manage PDF documents.

The Source Code files are provided at iText GitHub site.

You can compile it to generate your JAR file, using pom.xml as the build configuration file.

The source code of itextpdf-5.5.14.jar is provided below:

✍: FYIcenter.com

com/itextpdf/text/pdf/SimpleBookmark.java

/*
 *
 * This file is part of the iText (R) project.
    Copyright (c) 1998-2020 iText Group NV
 * Authors: Bruno Lowagie, Paulo Soares, et al.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
 * ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
 * OF THIRD PARTY RIGHTS
 *
 * This program 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 Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
 * http://itextpdf.com/terms-of-use/
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License,
 * a covered work must retain the producer line in every PDF that is created
 * or manipulated using iText.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the iText software without
 * disclosing the source code of your own applications.
 * These activities include: offering paid services to customers as an ASP,
 * serving PDFs on the fly in a web application, shipping iText with a closed
 * source product.
 *
 * For more information, please contact iText Software Corp. at this
 * address: sales@itextpdf.com
 */
package com.itextpdf.text.pdf;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.StringTokenizer;

import com.itextpdf.text.error_messages.MessageLocalization;
import com.itextpdf.text.xml.XMLUtil;
import com.itextpdf.text.xml.simpleparser.IanaEncodings;
import com.itextpdf.text.xml.simpleparser.SimpleXMLDocHandler;
import com.itextpdf.text.xml.simpleparser.SimpleXMLParser;
/**
 * Bookmark processing in a simple way. It has some limitations, mainly the only
 * action types supported are GoTo, GoToR, URI and Launch.
 * <p>
 * The list structure is composed by a number of HashMap, keyed by strings, one HashMap
 * for each bookmark.
 * The element values are all strings with the exception of the key "Kids" that has
 * another list for the child bookmarks.
 * <p>
 * All the bookmarks have a "Title" with the
 * bookmark title and optionally a "Style" that can be "bold", "italic" or a
 * combination of both. They can also have a "Color" key with a value of three
 * floats with values in the range 0.0 to 1.0 separated by spaces.
 * The key "Open" can have the values "true" or "false" and
 * signals the open status of the children. It's "true" by default.
 * <p>
 * The actions and the parameters can be:
 * <ul>
 * <li>"Action" = "GoTo" - "Page" | "Named"
 * <ul>
 * <li>"Page" = "3 XYZ 70 400 null" - page number followed by a destination (/XYZ is also accepted)
 * <li>"Named" = "named_destination"
 * </ul>
 * <li>"Action" = "GoToR" - "Page" | "Named" | "NamedN", "File", ["NewWindow"]
 * <ul>
 * <li>"Page" = "3 XYZ 70 400 null" - page number followed by a destination (/XYZ is also accepted)
 * <li>"Named" = "named_destination_as_a_string"
 * <li>"NamedN" = "named_destination_as_a_name"
 * <li>"File" - "the_file_to_open"
 * <li>"NewWindow" - "true" or "false"
 * </ul>
 * <li>"Action" = "URI" - "URI"
 * <ul>
 * <li>"URI" = "http://sf.net" - URI to jump to
 * </ul>
 * <li>"Action" = "Launch" - "File"
 * <ul>
 * <li>"File" - "the_file_to_open_or_execute"
 * </ul>
 * @author Paulo Soares
 */
public final class SimpleBookmark implements SimpleXMLDocHandler {

    private ArrayList<HashMap<String, Object>> topList;
    private final Stack<HashMap<String, Object>> attr = new Stack<HashMap<String, Object>>();

    /** Creates a new instance of SimpleBookmark */
    private SimpleBookmark() {
    }

    private static List<HashMap<String, Object>> bookmarkDepth(PdfReader reader, PdfDictionary outline, IntHashtable pages, boolean processCurrentOutlineOnly) {
        ArrayList<HashMap<String, Object>> list = new ArrayList<HashMap<String, Object>>();
        while (outline != null) {
            HashMap<String, Object> map = new HashMap<String, Object>();
            PdfString title = (PdfString)PdfReader.getPdfObjectRelease(outline.get(PdfName.TITLE));
            map.put("Title", 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());
                map.put("Color", PdfEncodings.convertToString(out.toByteArray(), null));
            }
            PdfNumber style = (PdfNumber)PdfReader.getPdfObjectRelease(outline.get(PdfName.F));
            if (style != null) {
                int f = style.intValue();
                String s = "";
                if ((f & 1) != 0)
                    s += "italic ";
                if ((f & 2) != 0)
                    s += "bold ";
                s = s.trim();
                if (s.length() != 0)
                    map.put("Style", s);
            }
            PdfNumber count = (PdfNumber)PdfReader.getPdfObjectRelease(outline.get(PdfName.COUNT));
            if (count != null && count.intValue() < 0)
                map.put("Open", "false");
            try {
                PdfObject dest = PdfReader.getPdfObjectRelease(outline.get(PdfName.DEST));
                if (dest != null) {
                    mapGotoBookmark(map, dest, pages); //changed by ujihara 2004-06-13
                }
                else {
                    PdfDictionary action = (PdfDictionary)PdfReader.getPdfObjectRelease(outline.get(PdfName.A));
                    if (action != null) {
                        if (PdfName.GOTO.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) {
                            dest = PdfReader.getPdfObjectRelease(action.get(PdfName.D));
                            if (dest != null) {
                                mapGotoBookmark(map, dest, pages);
                            }
                        }
                        else if (PdfName.URI.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) {
                            map.put("Action", "URI");
                            map.put("URI", ((PdfString)PdfReader.getPdfObjectRelease(action.get(PdfName.URI))).toUnicodeString());
                        }
                        else if (PdfName.JAVASCRIPT.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) {
                            map.put("Action", "JS");
                            map.put("Code", PdfReader.getPdfObjectRelease(action.get(PdfName.JS)).toString());
                        }
                        else if (PdfName.GOTOR.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) {
                            dest = PdfReader.getPdfObjectRelease(action.get(PdfName.D));
                            if (dest != null) {
                                if (dest.isString())
                                    map.put("Named", dest.toString());
                                else if (dest.isName())
                                    map.put("NamedN", PdfName.decodeName(dest.toString()));
                                else if (dest.isArray()) {
                                    PdfArray arr = (PdfArray)dest;
                                    StringBuffer s = new StringBuffer();
                                    s.append(arr.getPdfObject(0).toString());
                                    s.append(' ').append(arr.getPdfObject(1).toString());
                                    for (int k = 2; k < arr.size(); ++k)
                                        s.append(' ').append(arr.getPdfObject(k).toString());
                                    map.put("Page", s.toString());
                                }
                            }
                            map.put("Action", "GoToR");
                            PdfObject file = PdfReader.getPdfObjectRelease(action.get(PdfName.F));
                            if (file != null) {
                                if (file.isString())
                                    map.put("File", ((PdfString)file).toUnicodeString());
                                else if (file.isDictionary()) {
                                    file = PdfReader.getPdfObject(((PdfDictionary)file).get(PdfName.F));
                                    if (file.isString())
                                        map.put("File", ((PdfString)file).toUnicodeString());
                                }
                            }
                            PdfObject newWindow = PdfReader.getPdfObjectRelease(action.get(PdfName.NEWWINDOW));
                            if (newWindow != null)
                                map.put("NewWindow", newWindow.toString());
                        }
                        else if (PdfName.LAUNCH.equals(PdfReader.getPdfObjectRelease(action.get(PdfName.S)))) {
                            map.put("Action", "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())
                                    map.put("File", ((PdfString)file).toUnicodeString());
                                else if (file.isDictionary()) {
                                    file = PdfReader.getPdfObjectRelease(((PdfDictionary)file).get(PdfName.F));
                                    if (file.isString())
                                        map.put("File", ((PdfString)file).toUnicodeString());
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception e) {
                //empty on purpose
            }
            PdfDictionary first = (PdfDictionary)PdfReader.getPdfObjectRelease(outline.get(PdfName.FIRST));
            if (first != null) {
                map.put("Kids", bookmarkDepth(reader, first, pages, false));
            }
            list.add(map);
            if (!processCurrentOutlineOnly)
                outline = (PdfDictionary)PdfReader.getPdfObjectRelease(outline.get(PdfName.NEXT));
            else
                outline = null;
        }
        return list;
    }

	private static void mapGotoBookmark(HashMap<String, Object> map, PdfObject dest, IntHashtable pages)
	{
		if (dest.isString())
			map.put("Named", dest.toString());
		else if (dest.isName())
			map.put("Named", PdfName.decodeName(dest.toString()));
		else if (dest.isArray())
			map.put("Page", makeBookmarkParam((PdfArray)dest, pages)); //changed by ujihara 2004-06-13
		map.put("Action", "GoTo");
	}

	private static String makeBookmarkParam(PdfArray dest, IntHashtable pages)
	{
		StringBuffer s = new StringBuffer();
		PdfObject obj = dest.getPdfObject(0);
        if (obj.isNumber())
            s.append(((PdfNumber)obj).intValue() + 1);
        else
            s.append(pages.get(getNumber((PdfIndirectReference)obj))); //changed by ujihara 2004-06-13
		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();
	}

	/**
	 * Gets number of indirect. If type of directed indirect is PAGES, it refers PAGE object through KIDS.
	 * (Contributed by Kazuya Ujihara)
	 * @param indirect
	 * 2004-06-13
	 */
	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();
	}

    /**
     * Gets a <CODE>List</CODE> with the bookmarks. It returns <CODE>null</CODE> if
     * the document doesn't have any bookmarks.
     * @param reader the document
     * @return a <CODE>List</CODE> with the bookmarks or <CODE>null</CODE> if the
     * document doesn't have any
     */
    public static List<HashMap<String, Object>> getBookmark(PdfReader reader) {
        PdfDictionary catalog = reader.getCatalog();
        PdfObject obj = PdfReader.getPdfObjectRelease(catalog.get(PdfName.OUTLINES));
        if (obj == null || !obj.isDictionary())
            return null;
        PdfDictionary outlines = (PdfDictionary)obj;
        return SimpleBookmark.getBookmark(reader, outlines, false);
    }

    /**
    * Gets a <CODE>List</CODE> with the bookmarks that are children of <CODE>outline</CODE>. It returns <CODE>null</CODE> if
    * the document doesn't have any bookmarks.
    * @param reader the document
    * @param outline the outline dictionary to get bookmarks from
    * @param includeRoot indicates if to include <CODE>outline</CODE> parameter itself into returned list of bookmarks
    * @return a <CODE>List</CODE> with the bookmarks or <CODE>null</CODE> if the
    * document doesn't have any
    */
    public static List<HashMap<String, Object>> getBookmark(PdfReader reader, PdfDictionary outline, boolean includeRoot) {
        PdfDictionary catalog = reader.getCatalog();
        if (outline == null)
            return null;
        IntHashtable pages = new IntHashtable();
        int numPages = reader.getNumberOfPages();
        for (int k = 1; k <= numPages; ++k) {
            pages.put(reader.getPageOrigRef(k).getNumber(), k);
            reader.releasePage(k);
        }
        if (includeRoot)
            return bookmarkDepth(reader, outline, pages, true);
        else
            return bookmarkDepth(reader, (PdfDictionary)PdfReader.getPdfObjectRelease(outline.get(PdfName.FIRST)), pages, false);
    }

    /**
     * Removes the bookmark entries for a number of page ranges. The page ranges
     * consists of a number of pairs with the start/end page range. The page numbers
     * are inclusive.
     * @param list the bookmarks
     * @param pageRange the page ranges, always in pairs.
     */
    @SuppressWarnings("unchecked")
    public static void eliminatePages(List<HashMap<String, Object>> list, int pageRange[]) {
        if (list == null)
            return;
        for (Iterator<HashMap<String, Object>> it = list.listIterator(); it.hasNext();) {
            HashMap<String, Object> map = it.next();
            boolean hit = false;
            if ("GoTo".equals(map.get("Action"))) {
                String page = (String)map.get("Page");
                if (page != null) {
                    page = page.trim();
                    int idx = page.indexOf(' ');
                    int pageNum;
                    if (idx < 0)
                        pageNum = Integer.parseInt(page);
                    else
                        pageNum = Integer.parseInt(page.substring(0, idx));
                    int len = pageRange.length & 0xfffffffe;
                    for (int k = 0; k < len; k += 2) {
                        if (pageNum >= pageRange[k] && pageNum <= pageRange[k + 1]) {
                            hit = true;
                            break;
                        }
                    }
                }
            }
            List<HashMap<String, Object>> kids = (List<HashMap<String, Object>>)map.get("Kids");
            if (kids != null) {
                eliminatePages(kids, pageRange);
                if (kids.isEmpty()) {
                    map.remove("Kids");
                    kids = null;
                }
            }
            if (hit) {
                if (kids == null)
                    it.remove();
                else {
                    map.remove("Action");
                    map.remove("Page");
                    map.remove("Named");
                }
            }
        }
    }

    /**
     * For the pages in range add the <CODE>pageShift</CODE> to the page number.
     * The page ranges
     * consists of a number of pairs with the start/end page range. The page numbers
     * are inclusive.
     * @param list the bookmarks
     * @param pageShift the number to add to the pages in range
     * @param pageRange the page ranges, always in pairs. It can be <CODE>null</CODE>
     * to include all the pages
     */
    @SuppressWarnings("unchecked")
    public static void shiftPageNumbers(List<HashMap<String, Object>> list, int pageShift, int pageRange[]) {
        if (list == null)
            return;
        for (Iterator<HashMap<String, Object>> it = list.listIterator(); it.hasNext();) {
            HashMap<String, Object> map = it.next();
            if ("GoTo".equals(map.get("Action"))) {
                String page = (String)map.get("Page");
                if (page != null) {
                    page = page.trim();
                    int idx = page.indexOf(' ');
                    int pageNum;
                    if (idx < 0)
                        pageNum = Integer.parseInt(page);
                    else
                        pageNum = Integer.parseInt(page.substring(0, idx));
                    boolean hit = false;
                    if (pageRange == null)
                        hit = true;
                    else {
                        int len = pageRange.length & 0xfffffffe;
                        for (int k = 0; k < len; k += 2) {
                            if (pageNum >= pageRange[k] && pageNum <= pageRange[k + 1]) {
                                hit = true;
                                break;
                            }
                        }
                    }
                    if (hit) {
                        if (idx < 0)
                            page = Integer.toString(pageNum + pageShift);
                        else
                            page = pageNum + pageShift + page.substring(idx);
                    }
                    map.put("Page", page);
                }
            }
            List<HashMap<String, Object>> kids = (List<HashMap<String, Object>>)map.get("Kids");
            if (kids != null)
                shiftPageNumbers(kids, pageShift, pageRange);
        }
    }

    static void createOutlineAction(PdfDictionary outline, HashMap<String, Object> map, PdfWriter writer, boolean namedAsNames) {
        try {
            String action = (String)map.get("Action");
            if ("GoTo".equals(action)) {
                String p;
                if ((p = (String)map.get("Named")) != null) {
                    if (namedAsNames)
                        outline.put(PdfName.DEST, new PdfName(p));
                    else
                        outline.put(PdfName.DEST, new PdfString(p, null));
                }
                else if ((p = (String)map.get("Page")) != null) {
                    PdfArray ar = new PdfArray();
                    StringTokenizer tk = new StringTokenizer(p);
                    int n = Integer.parseInt(tk.nextToken());
                    ar.add(writer.getPageReference(n));
                    if (!tk.hasMoreTokens()) {
                        ar.add(PdfName.XYZ);
                        ar.add(new float[]{0, 10000, 0});
                    }
                    else {
                        String fn = tk.nextToken();
                        if (fn.startsWith("/"))
                            fn = fn.substring(1);
                        ar.add(new PdfName(fn));
                        for (int k = 0; k < 4 && tk.hasMoreTokens(); ++k) {
                            fn = tk.nextToken();
                            if (fn.equals("null"))
                                ar.add(PdfNull.PDFNULL);
                            else
                                ar.add(new PdfNumber(fn));
                        }
                    }
                    outline.put(PdfName.DEST, ar);
                }
            }
            else if ("GoToR".equals(action)) {
                String p;
                PdfDictionary dic = new PdfDictionary();
                if ((p = (String)map.get("Named")) != null)
                    dic.put(PdfName.D, new PdfString(p, null));
                else if ((p = (String)map.get("NamedN")) != null)
                    dic.put(PdfName.D, new PdfName(p));
                else if ((p = (String)map.get("Page")) != null){
                    PdfArray ar = new PdfArray();
                    StringTokenizer tk = new StringTokenizer(p);
                    ar.add(new PdfNumber(tk.nextToken()));
                    if (!tk.hasMoreTokens()) {
                        ar.add(PdfName.XYZ);
                        ar.add(new float[]{0, 10000, 0});
                    }
                    else {
                        String fn = tk.nextToken();
                        if (fn.startsWith("/"))
                            fn = fn.substring(1);
                        ar.add(new PdfName(fn));
                        for (int k = 0; k < 4 && tk.hasMoreTokens(); ++k) {
                            fn = tk.nextToken();
                            if (fn.equals("null"))
                                ar.add(PdfNull.PDFNULL);
                            else
                                ar.add(new PdfNumber(fn));
                        }
                    }
                    dic.put(PdfName.D, ar);
                }
                String file = (String)map.get("File");
                if (dic.size() > 0 && file != null) {
                    dic.put(PdfName.S,  PdfName.GOTOR);
                    dic.put(PdfName.F, new PdfString(file));
                    String nw = (String)map.get("NewWindow");
                    if (nw != null) {
                        if (nw.equals("true"))
                            dic.put(PdfName.NEWWINDOW, PdfBoolean.PDFTRUE);
                        else if (nw.equals("false"))
                            dic.put(PdfName.NEWWINDOW, PdfBoolean.PDFFALSE);
                    }
                    outline.put(PdfName.A, dic);
                }
            }
            else if ("URI".equals(action)) {
                String uri = (String)map.get("URI");
                if (uri != null) {
                    PdfDictionary dic = new PdfDictionary();
                    dic.put(PdfName.S, PdfName.URI);
                    dic.put(PdfName.URI, new PdfString(uri));
                    outline.put(PdfName.A, dic);
                }
            }
            else if ("JS".equals(action)) {
            	String code = (String)map.get("Code");
            	if(code != null) {
                    outline.put(PdfName.A, PdfAction.javaScript(code, writer));
            	}
            }
            else if ("Launch".equals(action)) {
                String file = (String)map.get("File");
                if (file != null) {
                    PdfDictionary dic = new PdfDictionary();
                    dic.put(PdfName.S, PdfName.LAUNCH);
                    dic.put(PdfName.F, new PdfString(file));
                    outline.put(PdfName.A, dic);
                }
            }
        }
        catch (Exception e) {
            // empty on purpose
        }
    }

    @SuppressWarnings("unchecked")
    public static Object[] iterateOutlines(PdfWriter writer, PdfIndirectReference parent, List<HashMap<String, Object>> kids, boolean namedAsNames) throws IOException {
        PdfIndirectReference refs[] = new PdfIndirectReference[kids.size()];
        for (int k = 0; k < refs.length; ++k)
            refs[k] = writer.getPdfIndirectReference();
        int ptr = 0;
        int count = 0;
        for (Iterator<HashMap<String, Object>> it = kids.listIterator(); it.hasNext(); ++ptr) {
            HashMap<String, Object> map = it.next();
            Object lower[] = null;
            List<HashMap<String, Object>> subKid = (List<HashMap<String, Object>>)map.get("Kids");
            if (subKid != null && !subKid.isEmpty())
                lower = iterateOutlines(writer, refs[ptr], subKid, namedAsNames);
            PdfDictionary outline = new PdfDictionary();
            ++count;
            if (lower != null) {
                outline.put(PdfName.FIRST, (PdfIndirectReference)lower[0]);
                outline.put(PdfName.LAST, (PdfIndirectReference)lower[1]);
                int n = ((Integer)lower[2]).intValue();
                if ("false".equals(map.get("Open"))) {
                    outline.put(PdfName.COUNT, new PdfNumber(-n));
                }
                else {
                    outline.put(PdfName.COUNT, new PdfNumber(n));
                    count += n;
                }
            }
            outline.put(PdfName.PARENT, parent);
            if (ptr > 0)
                outline.put(PdfName.PREV, refs[ptr - 1]);
            if (ptr < refs.length - 1)
                outline.put(PdfName.NEXT, refs[ptr + 1]);
            outline.put(PdfName.TITLE, new PdfString((String)map.get("Title"), PdfObject.TEXT_UNICODE));
            String color = (String)map.get("Color");
            if (color != null) {
                try {
                    PdfArray arr = new PdfArray();
                    StringTokenizer tk = new StringTokenizer(color);
                    for (int k = 0; k < 3; ++k) {
                        float f = Float.parseFloat(tk.nextToken());
                        if (f < 0) f = 0;
                        if (f > 1) f = 1;
                        arr.add(new PdfNumber(f));
                    }
                    outline.put(PdfName.C, arr);
                } catch(Exception e){} //in case it's malformed
            }
            String style = (String)map.get("Style");
            if (style != null) {
                style = style.toLowerCase();
                int bits = 0;
                if (style.indexOf("italic") >= 0)
                    bits |= 1;
                if (style.indexOf("bold") >= 0)
                    bits |= 2;
                if (bits != 0)
                    outline.put(PdfName.F, new PdfNumber(bits));
            }
            createOutlineAction(outline, map, writer, namedAsNames);
            writer.addToBody(outline, refs[ptr]);
        }
        return new Object[]{refs[0], refs[refs.length - 1], Integer.valueOf(count)};
    }

    /**
     * Exports the bookmarks to XML. Only of use if the generation is to be include in
     * some other XML document.
     * @param list the bookmarks
     * @param out the export destination. The writer is not closed
     * @param indent the indentation level. Pretty printing significant only. Use <CODE>-1</CODE> for no indents.
     * @param onlyASCII codes above 127 will always be escaped with &amp;#nn; if <CODE>true</CODE>,
     * whatever the encoding
     * @throws IOException on error
     * @since 5.0.1 (generic type in signature)
     */
    @SuppressWarnings("unchecked")
    public static void exportToXMLNode(List<HashMap<String, Object>> list, Writer out, int indent, boolean onlyASCII) throws IOException {
        String dep = "";
        if (indent != -1) {
            for (int k = 0; k < indent; ++k)
                dep += "  ";
        }
        for (HashMap<String, Object> map : list) {
            String title = null;
            out.write(dep);
            out.write("<Title ");
            List<HashMap<String, Object>> kids = null;
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                if (key.equals("Title")) {
                    title = (String) entry.getValue();
                    continue;
                }
                else if (key.equals("Kids")) {
                    kids = (List<HashMap<String, Object>>) entry.getValue();
                    continue;
                }
                else {
                    out.write(key);
                    out.write("=\"");
                    String value = (String) entry.getValue();
                    if (key.equals("Named") || key.equals("NamedN"))
                        value = SimpleNamedDestination.escapeBinaryString(value);
                    out.write(XMLUtil.escapeXML(value, onlyASCII));
                    out.write("\" ");
                }
            }
            out.write(">");
            if (title == null)
                title = "";
            out.write(XMLUtil.escapeXML(title, onlyASCII));
            if (kids != null) {
                out.write("\n");
                exportToXMLNode(kids, out, indent == -1 ? indent : indent + 1, onlyASCII);
                out.write(dep);
            }
            out.write("</Title>\n");
        }
    }

    /**
     * Exports the bookmarks to XML. The DTD for this XML is:
     * <p>
     * <pre>
     * &lt;?xml version='1.0' encoding='UTF-8'?&gt;
     * &lt;!ELEMENT Title (#PCDATA|Title)*&gt;
     * &lt;!ATTLIST Title
     *    Action CDATA #IMPLIED
     *    Open CDATA #IMPLIED
     *    Page CDATA #IMPLIED
     *    URI CDATA #IMPLIED
     *    File CDATA #IMPLIED
     *    Named CDATA #IMPLIED
     *    NamedN CDATA #IMPLIED
     *    NewWindow CDATA #IMPLIED
     *    Style CDATA #IMPLIED
     *    Color CDATA #IMPLIED
     * &gt;
     * &lt;!ELEMENT Bookmark (Title)*&gt;
     * </pre>
     * @param list the bookmarks
     * @param out the export destination. The stream is not closed
     * @param encoding the encoding according to IANA conventions
     * @param onlyASCII codes above 127 will always be escaped with &amp;#nn; if <CODE>true</CODE>,
     * whatever the encoding
     * @throws IOException on error
     * @since 5.0.1 (generic type in signature)
     */
    public static void exportToXML(List<HashMap<String, Object>> list, OutputStream out, String encoding, boolean onlyASCII) throws IOException {
        String jenc = IanaEncodings.getJavaEncoding(encoding);
        Writer wrt = new BufferedWriter(new OutputStreamWriter(out, jenc));
        exportToXML(list, wrt, encoding, onlyASCII);
    }

    /**
     * Exports the bookmarks to XML.
     * @param list the bookmarks
     * @param wrt the export destination. The writer is not closed
     * @param encoding the encoding according to IANA conventions
     * @param onlyASCII codes above 127 will always be escaped with &amp;#nn; if <CODE>true</CODE>,
     * whatever the encoding
     * @throws IOException on error
     * @since 5.0.1 (generic type in signature)
     */
    public static void exportToXML(List<HashMap<String, Object>> list, Writer wrt, String encoding, boolean onlyASCII) throws IOException {
        wrt.write("<?xml version=\"1.0\" encoding=\"");
        wrt.write(XMLUtil.escapeXML(encoding, onlyASCII));
        wrt.write("\"?>\n<Bookmark>\n");
        exportToXMLNode(list, wrt, 1, onlyASCII);
        wrt.write("</Bookmark>\n");
        wrt.flush();
    }

    /**
     * Import the bookmarks from XML.
     * @param in the XML source. The stream is not closed
     * @throws IOException on error
     * @return the bookmarks
     */
    public static List<HashMap<String, Object>> importFromXML(InputStream in) throws IOException {
        SimpleBookmark book = new SimpleBookmark();
        SimpleXMLParser.parse(book, in);
        return book.topList;
    }

    /**
     * Import the bookmarks from XML.
     * @param in the XML source. The reader is not closed
     * @throws IOException on error
     * @return the bookmarks
     */
    public static List<HashMap<String, Object>> importFromXML(Reader in) throws IOException {
        SimpleBookmark book = new SimpleBookmark();
        SimpleXMLParser.parse(book, in);
        return book.topList;
    }

    public void endDocument() {
    }

    @SuppressWarnings("unchecked")
    public void endElement(String tag) {
        if (tag.equals("Bookmark")) {
            if (attr.isEmpty())
                return;
            else
                throw new RuntimeException(MessageLocalization.getComposedMessage("bookmark.end.tag.out.of.place"));
        }
        if (!tag.equals("Title"))
            throw new RuntimeException(MessageLocalization.getComposedMessage("invalid.end.tag.1", tag));
        HashMap<String, Object> attributes = attr.pop();
        String title = (String)attributes.get("Title");
        attributes.put("Title",  title.trim());
        String named = (String)attributes.get("Named");
        if (named != null)
            attributes.put("Named", SimpleNamedDestination.unEscapeBinaryString(named));
        named = (String)attributes.get("NamedN");
        if (named != null)
            attributes.put("NamedN", SimpleNamedDestination.unEscapeBinaryString(named));
        if (attr.isEmpty())
            topList.add(attributes);
        else {
            HashMap<String, Object> parent = attr.peek();
            List<HashMap<String, Object>> kids = (List<HashMap<String, Object>>)parent.get("Kids");
            if (kids == null) {
                kids = new ArrayList<HashMap<String, Object>>();
                parent.put("Kids", kids);
            }
            kids.add(attributes);
        }
    }

    public void startDocument() {
    }

    public void startElement(String tag, Map<String, String> h) {
        if (topList == null) {
            if (tag.equals("Bookmark")) {
                topList = new ArrayList<HashMap<String, Object>>();
                return;
            }
            else
                throw new RuntimeException(MessageLocalization.getComposedMessage("root.element.is.not.bookmark.1", tag));
        }
        if (!tag.equals("Title"))
            throw new RuntimeException(MessageLocalization.getComposedMessage("tag.1.not.allowed", tag));
        HashMap<String, Object> attributes = new HashMap<String, Object>(h);
        attributes.put("Title", "");
        attributes.remove("Kids");
        attr.push(attributes);
    }

    public void text(String str) {
        if (attr.isEmpty())
            return;
        HashMap<String, Object> attributes = attr.peek();
        String title = (String)attributes.get("Title");
        title += str;
        attributes.put("Title", title);
    }
}

com/itextpdf/text/pdf/SimpleBookmark.java

 

Or download all of them as a single archive file:

File name: itextpdf-5.5.14-fyi.zip
File size: 2163839 bytes
Release date: 2009-10-09
Download 

 

iText-2.1.6.jar - iText, a JAVA-PDF library

iText layout.jar Source Code

Download and Install iText Java Library

⇑⇑ iText for PDF Generation

2021-07-03, 113140👍, 0💬