All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.itextpdf.text.pdf.SimpleBookmark Maven / Gradle / Ivy

/*
 * $Id: 90321b6050f982c263a694fdc4d904c46e49decd $
 *
 * This file is part of the iText (R) project.
 * Copyright (c) 1998-2016 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: [email protected]
 */
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.
 * 

* 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. *

* 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. *

* The actions and the parameters can be: *

    *
  • "Action" = "GoTo" - "Page" | "Named" *
      *
    • "Page" = "3 XYZ 70 400 null" - page number followed by a destination (/XYZ is also accepted) *
    • "Named" = "named_destination" *
    *
  • "Action" = "GoToR" - "Page" | "Named" | "NamedN", "File", ["NewWindow"] *
      *
    • "Page" = "3 XYZ 70 400 null" - page number followed by a destination (/XYZ is also accepted) *
    • "Named" = "named_destination_as_a_string" *
    • "NamedN" = "named_destination_as_a_name" *
    • "File" - "the_file_to_open" *
    • "NewWindow" - "true" or "false" *
    *
  • "Action" = "URI" - "URI" *
      *
    • "URI" = "http://sf.net" - URI to jump to *
    *
  • "Action" = "Launch" - "File" *
      *
    • "File" - "the_file_to_open_or_execute" *
    * @author Paulo Soares */ public final class SimpleBookmark implements SimpleXMLDocHandler { private ArrayList> topList; private final Stack> attr = new Stack>(); /** Creates a new instance of SimpleBookmark */ private SimpleBookmark() { } private static List> bookmarkDepth(PdfReader reader, PdfDictionary outline, IntHashtable pages, boolean processCurrentOutlineOnly) { ArrayList> list = new ArrayList>(); while (outline != null) { HashMap map = new HashMap(); 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 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 List with the bookmarks. It returns null if * the document doesn't have any bookmarks. * @param reader the document * @return a List with the bookmarks or null if the * document doesn't have any */ public static List> 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 List with the bookmarks that are children of outline. It returns null 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 outline parameter itself into returned list of bookmarks * @return a List with the bookmarks or null if the * document doesn't have any */ public static List> 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> list, int pageRange[]) { if (list == null) return; for (Iterator> it = list.listIterator(); it.hasNext();) { HashMap 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> kids = (List>)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 pageShift 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 null * to include all the pages */ @SuppressWarnings("unchecked") public static void shiftPageNumbers(List> list, int pageShift, int pageRange[]) { if (list == null) return; for (Iterator> it = list.listIterator(); it.hasNext();) { HashMap 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> kids = (List>)map.get("Kids"); if (kids != null) shiftPageNumbers(kids, pageShift, pageRange); } } static void createOutlineAction(PdfDictionary outline, HashMap 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> 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> it = kids.listIterator(); it.hasNext(); ++ptr) { HashMap map = it.next(); Object lower[] = null; List> subKid = (List>)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 -1 for no indents. * @param onlyASCII codes above 127 will always be escaped with &#nn; if true, * whatever the encoding * @throws IOException on error * @since 5.0.1 (generic type in signature) */ @SuppressWarnings("unchecked") public static void exportToXMLNode(List> list, Writer out, int indent, boolean onlyASCII) throws IOException { String dep = ""; if (indent != -1) { for (int k = 0; k < indent; ++k) dep += " "; } for (HashMap map : list) { String title = null; out.write(dep); out.write("> 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("\n"); } } /** * Exports the bookmarks to XML. The DTD for this XML is: *

    *

         * <?xml version='1.0' encoding='UTF-8'?>
         * <!ELEMENT Title (#PCDATA|Title)*>
         * <!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
         * >
         * <!ELEMENT Bookmark (Title)*>
         * 
    * @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 &#nn; if true, * whatever the encoding * @throws IOException on error * @since 5.0.1 (generic type in signature) */ public static void exportToXML(List> 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 &#nn; if true, * whatever the encoding * @throws IOException on error * @since 5.0.1 (generic type in signature) */ public static void exportToXML(List> list, Writer wrt, String encoding, boolean onlyASCII) throws IOException { wrt.write("\n\n"); exportToXMLNode(list, wrt, 1, onlyASCII); wrt.write("\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> 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> 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 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 parent = attr.peek(); List> kids = (List>)parent.get("Kids"); if (kids == null) { kids = new ArrayList>(); parent.put("Kids", kids); } kids.add(attributes); } } public void startDocument() { } public void startElement(String tag, Map h) { if (topList == null) { if (tag.equals("Bookmark")) { topList = new ArrayList>(); 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 attributes = new HashMap(h); attributes.put("Title", ""); attributes.remove("Kids"); attr.push(attributes); } public void text(String str) { if (attr.isEmpty()) return; HashMap attributes = attr.peek(); String title = (String)attributes.get("Title"); title += str; attributes.put("Title", title); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy