/*
* $Id: AnalyzePDF.java 49 2007-05-19 19:24:42Z chammer $
* Copyright (c) 2005-2007 Carsten Hammer, Bruno Lowagie
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* This class was originally published under the MPL by Carsten Hammer.
* It was a part of iText, a Java-PDF library. You can now use it under
* the MIT License; for backward compatibility you can also use it under
* the MPL version 1.1: http://www.mozilla.org/MPL/
* A copy of the MPL license is bundled with the source code FYI.
*/
package com.lowagie.tools.swing;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.table.TableModel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import com.lowagie.text.pdf.PRIndirectReference;
import com.lowagie.text.pdf.PdfArray;
import com.lowagie.text.pdf.PdfDictionary;
import com.lowagie.text.pdf.PdfName;
import com.lowagie.text.pdf.PdfObject;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfString;
import com.lowagie.tools.swing.interfaces.RecursivePdfObjectIterator;
import com.lowagie.tools.swing.treenodes.ArrayTreeNode;
import com.lowagie.tools.swing.treenodes.BookmarksTreeNode;
import com.lowagie.tools.swing.treenodes.DictionaryTreeNode;
import com.lowagie.tools.swing.treenodes.FileTreeNode;
import com.lowagie.tools.swing.treenodes.ObjectTreeNode;
import com.lowagie.tools.swing.treenodes.PageTreeNode;
import com.lowagie.tools.swing.treenodes.PagesTreeNode;
import com.lowagie.tools.swing.treenodes.StreamTreeNode;
public class AnalyzePDF extends Thread implements TreeModel, RecursivePdfObjectIterator {
/** The reader that will inspect the PDF file. */
PdfReader reader;
/** The number of the current page being read. */
protected int pagecount;
/** The progress analyzing the PDF. */
protected PageProgressDialog progressdialog;
/** The root of the tree view. */
protected DefaultMutableTreeNode root;
/** The node of the PDF file. */
protected DefaultMutableTreeNode filenode;
/** Tree Model Listeners. */
private transient Vector treeModelListeners;
/**
* Constructs an AnalyzePDF object.
*
* @param infile String
* @param progressbar PageProgressDialog
*/
public AnalyzePDF(String infile, PageProgressDialog progressbar) {
this.progressdialog = progressbar;
try {
reader = new PdfReader(infile);
root = new ObjectTreeNode("Document");
filenode = new FileTreeNode(infile, reader);
root.add(filenode);
} catch (IOException ex) {
}
pagecount = 0;
}
/**
* When an object implementing interface <code>Runnable</code> is used to
* create a thread, starting the thread causes the object's <code>run</code>
* method to be called in that separately executing thread.
*/
public void run() {
try {
PdfDictionary catalog = reader.getCatalog();
PdfDictionary rootPages = (PdfDictionary) PdfReader
.getPdfObject(catalog.get(PdfName.PAGES));
DefaultMutableTreeNode rootPagesGUI = new ObjectTreeNode(
"Pagetree " + rootPages);
filenode.add(rootPagesGUI);
iteratePages(rootPages, reader, rootPagesGUI);
PdfDictionary rootOutlines = (PdfDictionary) PdfReader
.getPdfObject(catalog.get(PdfName.OUTLINES));
if (rootOutlines != null) {
DefaultMutableTreeNode outlinetree = new ObjectTreeNode(
"Outlinetree " + rootOutlines);
filenode.add(outlinetree);
PdfObject firstindref = rootOutlines.get(PdfName.FIRST);
if (firstindref != null) {
PdfDictionary first = (PdfDictionary) PdfReader
.getPdfObject(firstindref);
if (first != null) {
iterateOutlines(first, reader, outlinetree);
}
}
}
System.out.println(" Pagecount= " + pagecount);
progressdialog.setVisible(false);
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
/**
* Returns the TableModel for the CrossReference table of the PDF.
*
* @return TableModel
*/
public TableModel getXReftable() {
return new XRefTableModel(reader);
}
/**
* Walk down the Pagetree
*
* @param page
* PdfDictionary
* @param pdfreader
* PdfReader
* @param node
* DefaultMutableTreeNode
*/
protected void iteratePages(PdfDictionary page, PdfReader pdfreader,
DefaultMutableTreeNode node) {
DefaultMutableTreeNode leaf;
PdfArray kids_array = (PdfArray) PdfReader.getPdfObject(page.get(PdfName.KIDS));
if (kids_array == null) {
node.add(new PageTreeNode(page, pagecount, this, pdfreader));
System.out.println("Page= " + (++pagecount));
} else {
leaf = new PagesTreeNode(kids_array);
node.add(leaf);
page.put(PdfName.TYPE, PdfName.PAGES);
ArrayList kids = kids_array.getArrayList();
for (int k = 0; k < kids.size(); ++k) {
PdfDictionary kid = (PdfDictionary) PdfReader
.getPdfObject((PRIndirectReference) kids.get(k));
iteratePages(kid, pdfreader, leaf);
}
}
}
/**
* Walk down the Nookmarks
*
* @param outlines
* PdfDictionary
* @param pdfreader
* PdfReader
* @param node
* DefaultMutableTreeNode
*/
protected void iterateOutlines(PdfDictionary outlines, PdfReader pdfreader,
DefaultMutableTreeNode node) {
DefaultMutableTreeNode leaf;
PdfDictionary kid = outlines;
do {
PdfString title = (PdfString) PdfReader.getPdfObject(kid.get(PdfName.TITLE));
leaf = new BookmarksTreeNode(title, kid);
node.add(leaf);
PdfDictionary first = (PdfDictionary) PdfReader.getPdfObject(kid.get(PdfName.FIRST));
if (first != null) {
iterateOutlines(first, pdfreader, leaf);
} else {
PdfDictionary se = (PdfDictionary) PdfReader.getPdfObject(kid.get(new PdfName("SE")));
if (se != null) {
iterateObjects(se, pdfreader, leaf);
}
PdfObject dest = PdfReader.getPdfObject(kid.get(PdfName.DEST));
if (dest != null) {
iterateObjects(dest, pdfreader, leaf);
}
PdfObject a = PdfReader.getPdfObject(kid.get(PdfName.A));
if (a != null) {
iterateObjects(a, pdfreader, leaf);
}
}
} while ((kid = (PdfDictionary) PdfReader.getPdfObject(kid.get(PdfName.NEXT))) != null);
}
/**
* Recursive investigate PDF Objecttree (other than pagetree objects!)
*
* @param pdfobj
* PdfObject
* @param pdfreader
* PdfReader
* @param node
* DefaultMutableTreeNode
*/
public void iterateObjects(PdfObject pdfobj, PdfReader pdfreader,
DefaultMutableTreeNode node) {
DefaultMutableTreeNode leaf;
if (pdfobj.isDictionary()) {
leaf = new DictionaryTreeNode("PdfDictionary " + pdfobj,
(PdfDictionary) pdfobj);
node.add(leaf);
Set s = ((PdfDictionary) pdfobj).getKeys();
Iterator it = s.iterator();
int i = 0;
while (it.hasNext()) {
i++;
Object obj = it.next();
PdfObject value = PdfReader
.getPdfObject(((PdfDictionary) pdfobj)
.get((PdfName) obj));
// System.out.println("Value:" + value);
ObjectTreeNode sttn = new ObjectTreeNode(obj + " " + value);
leaf.add(sttn);
if (obj.equals(PdfName.PARENT)) {
continue;
}
if (value != null) {
iterateObjects(value, pdfreader, sttn);
}
}
} else if (pdfobj.isArray()) {
leaf = new ArrayTreeNode("PdfArray " + pdfobj, (PdfArray) pdfobj);
node.add(leaf);
ArrayList kids = ((PdfArray) pdfobj).getArrayList();
for (int k = 0; k < kids.size(); ++k) {
PdfObject curkid = (PdfObject) kids.get(k);
if (curkid.isIndirect()) {
PdfObject kid = PdfReader
.getPdfObject((PRIndirectReference) kids.get(k));
if (kid != null) {
iterateObjects(kid, pdfreader, leaf);
}
} else if (curkid.isNumber()) {
} else {
PdfObject kid = (PdfObject) kids.get(k);
iterateObjects(kid, pdfreader, leaf);
}
}
} else if (pdfobj.isIndirect()) {
leaf = new ObjectTreeNode("PRIndirectReference " + pdfobj);
node.add(leaf);
PdfObject target = PdfReader
.getPdfObject(pdfobj);
if (target != null) {
iterateObjects(target, pdfreader, leaf);
}
} else if (pdfobj.isBoolean()) {
// not shown
} else if (pdfobj.isName()) {
// not shown
} else if (pdfobj.isNull()) {
// not shown
} else if (pdfobj.isNumber()) {
// not shown
} else if (pdfobj.isString()) {
// not shown
} else if (pdfobj.isStream()) {
leaf = new StreamTreeNode(pdfobj, "Stream");
node.add(leaf);
leaf = new DictionaryTreeNode("PdfDictionary " + pdfobj,
(PdfDictionary) pdfobj);
node.add(leaf);
Set s = ((PdfDictionary) pdfobj).getKeys();
Iterator it = s.iterator();
int i = 0;
while (it.hasNext()) {
i++;
Object obj = it.next();
PdfObject value = PdfReader
.getPdfObject(((PdfDictionary) pdfobj)
.get((PdfName) obj));
// System.out.println("Value:" + value);
ObjectTreeNode sttn = new ObjectTreeNode(obj + " "
+ value);
leaf.add(sttn);
if (obj.equals(PdfName.PARENT)) {
continue;
}
if (value != null) {
iterateObjects(value, pdfreader, sttn);
}
}
}
else {
leaf = new ObjectTreeNode("Unknown " + pdfobj);
node.add(leaf);
}
}
/**
* Returns the root of the tree.
*
* @return the root of the tree
*/
public Object getRoot() {
return root;
}
public int getPagecount() {
return pagecount;
}
public void updatecount() {
progressdialog.setCurrentPage(getPagecount());
}
/**
* Returns the child of <code>parent</code> at index <code>index</code>
* in the parent's child array.
*
* @param parent
* a node in the tree, obtained from this data source
* @param index
* int
* @return the child of <code>parent</code> at index <code>index</code>
*/
public Object getChild(Object parent, int index) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) parent;
return node.getChildAt(index);
}
/**
* Returns the number of children of <code>parent</code>.
*
* @param parent
* a node in the tree, obtained from this data source
* @return the number of children of the node <code>parent</code>
*/
public int getChildCount(Object parent) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) parent;
return node.getChildCount();
}
/**
* Returns <code>true</code> if <code>node</code> is a leaf.
*
* @param node
* a node in the tree, obtained from this data source
* @return true if <code>node</code> is a leaf
*/
public boolean isLeaf(Object node) {
DefaultMutableTreeNode leaf = (DefaultMutableTreeNode) node;
return leaf.isLeaf();
}
/**
* Messaged when the user has altered the value for the item identified by
* <code>path</code> to <code>newValue</code>.
*
* @param path
* path to the node that the user has altered
* @param newValue
* the new value from the TreeCellEditor
*/
public void valueForPathChanged(TreePath path, Object newValue) {
throw new RuntimeException(
"Manipulation of objecttree not yet supported!");
}
/**
* Returns the index of child in parent.
*
* @param parent
* a note in the tree, obtained from this data source
* @param child
* the node we are interested in
* @return the index of the child in the parent, or -1 if either
* <code>child</code> or <code>parent</code> are
* <code>null</code>
*/
public int getIndexOfChild(Object parent, Object child) {
DefaultMutableTreeNode parentobj = (DefaultMutableTreeNode) parent;
DefaultMutableTreeNode childobj = (DefaultMutableTreeNode) child;
return parentobj.getIndex(childobj);
}
public synchronized void removeTreeModelListener(TreeModelListener l) {
if (treeModelListeners != null && treeModelListeners.contains(l)) {
Vector v = (Vector) treeModelListeners.clone();
v.removeElement(l);
treeModelListeners = v;
}
}
public synchronized void addTreeModelListener(TreeModelListener l) {
Vector v = treeModelListeners == null ? new Vector(2)
: (Vector) treeModelListeners.clone();
if (!v.contains(l)) {
v.addElement(l);
treeModelListeners = v;
}
}
protected void fireTreeNodesChanged(TreeModelEvent e) {
if (treeModelListeners != null) {
Vector listeners = treeModelListeners;
int count = listeners.size();
for (int i = 0; i < count; i++) {
((TreeModelListener) listeners.elementAt(i))
.treeNodesChanged(e);
}
}
}
protected void fireTreeNodesInserted(TreeModelEvent e) {
if (treeModelListeners != null) {
Vector listeners = treeModelListeners;
int count = listeners.size();
for (int i = 0; i < count; i++) {
((TreeModelListener) listeners.elementAt(i))
.treeNodesInserted(e);
}
}
}
protected void fireTreeNodesRemoved(TreeModelEvent e) {
if (treeModelListeners != null) {
Vector listeners = treeModelListeners;
int count = listeners.size();
for (int i = 0; i < count; i++) {
((TreeModelListener) listeners.elementAt(i))
.treeNodesRemoved(e);
}
}
}
protected void fireTreeStructureChanged(TreeModelEvent e) {
if (treeModelListeners != null) {
Vector listeners = treeModelListeners;
int count = listeners.size();
for (int i = 0; i < count; i++) {
((TreeModelListener) listeners.elementAt(i))
.treeStructureChanged(e);
}
}
}
}