aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/pdfbox/pdfviewer
diff options
context:
space:
mode:
authortknall <tknall@7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c>2006-12-01 12:20:24 +0000
committertknall <tknall@7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c>2006-12-01 12:20:24 +0000
commit6025b6016517c6d898d8957d1d7e03ba71431912 (patch)
treeb15bd6fa5ffe9588a9bca3f2b8a7e358f83b6eba /src/main/java/org/pdfbox/pdfviewer
parentd2c77e820ab4aba8235d71275755021347b3ad10 (diff)
downloadpdf-as-3-6025b6016517c6d898d8957d1d7e03ba71431912.tar.gz
pdf-as-3-6025b6016517c6d898d8957d1d7e03ba71431912.tar.bz2
pdf-as-3-6025b6016517c6d898d8957d1d7e03ba71431912.zip
Initial import of release 2.2.REL-2.2@923
git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@4 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c
Diffstat (limited to 'src/main/java/org/pdfbox/pdfviewer')
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/ArrayEntry.java83
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/MapEntry.java105
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/PDFPagePanel.java102
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/PDFTreeCellRenderer.java145
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/PDFTreeModel.java332
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/PageDrawer.java268
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/PageWrapper.java118
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/ReaderBottomPanel.java86
-rw-r--r--src/main/java/org/pdfbox/pdfviewer/package.html9
9 files changed, 1248 insertions, 0 deletions
diff --git a/src/main/java/org/pdfbox/pdfviewer/ArrayEntry.java b/src/main/java/org/pdfbox/pdfviewer/ArrayEntry.java
new file mode 100644
index 0000000..e229587
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/ArrayEntry.java
@@ -0,0 +1,83 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+/**
+ * This is a simple class that will contain an index and a value.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class ArrayEntry
+{
+ private int index;
+ private Object value;
+
+ /**
+ * This will get the value for this entry.
+ *
+ * @return The value for this entry.
+ */
+ public Object getValue()
+ {
+ return value;
+ }
+
+ /**
+ * This will set the value for this entry.
+ *
+ * @param val the new value for this entry.
+ */
+ public void setValue(Object val)
+ {
+ this.value = val;
+ }
+
+ /**
+ * This will get the index of the array entry.
+ *
+ * @return The 0-based index into the array
+ */
+ public int getIndex()
+ {
+ return index;
+ }
+
+ /**
+ * This will set the index value.
+ *
+ * @param i The new index value.
+ */
+ public void setIndex(int i)
+ {
+ index = i;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfviewer/MapEntry.java b/src/main/java/org/pdfbox/pdfviewer/MapEntry.java
new file mode 100644
index 0000000..9fd73e5
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/MapEntry.java
@@ -0,0 +1,105 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+import org.pdfbox.cos.COSName;
+
+
+/**
+ * This is a simple class that will contain a key and a value.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.2 $
+ */
+public class MapEntry
+{
+ private Object key;
+ private Object value;
+
+ /**
+ * Get the key for this entry.
+ *
+ * @return The entry's key.
+ */
+ public Object getKey()
+ {
+ return key;
+ }
+
+ /**
+ * This will set the key for this entry.
+ *
+ * @param k the new key for this entry.
+ */
+ public void setKey(Object k)
+ {
+ key = k;
+ }
+
+ /**
+ * This will get the value for this entry.
+ *
+ * @return The value for this entry.
+ */
+ public Object getValue()
+ {
+ return value;
+ }
+
+ /**
+ * This will set the value for this entry.
+ *
+ * @param val the new value for this entry.
+ */
+ public void setValue(Object val)
+ {
+ this.value = val;
+ }
+
+ /**
+ * This will output a string representation of this class.
+ *
+ * @return A string representation of this class.
+ */
+ public String toString()
+ {
+ String retval = null;
+ if( key instanceof COSName )
+ {
+ retval = ((COSName)key).getName();
+ }
+ else
+ {
+ retval = "" +key;
+ }
+ return retval;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfviewer/PDFPagePanel.java b/src/main/java/org/pdfbox/pdfviewer/PDFPagePanel.java
new file mode 100644
index 0000000..1d28bd3
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/PDFPagePanel.java
@@ -0,0 +1,102 @@
+/**
+ * Copyright (c) 2004-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+
+import java.io.IOException;
+
+import javax.swing.JPanel;
+
+import org.pdfbox.pdmodel.PDPage;
+
+import org.pdfbox.pdmodel.common.PDRectangle;
+
+/**
+ * This is a simple JPanel that can be used to display a PDF page.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.3 $
+ */
+public class PDFPagePanel extends JPanel
+{
+
+ private PDPage page;
+ private PageDrawer drawer = null;
+ private Dimension pageDimension = null;
+
+ /**
+ * Constructor.
+ *
+ * @throws IOException If there is an error creating the Page drawing objects.
+ */
+ public PDFPagePanel() throws IOException
+ {
+ drawer = new PageDrawer();
+ }
+
+ /**
+ * This will set the page that should be displayed in this panel.
+ *
+ * @param pdfPage The page to draw.
+ */
+ public void setPage( PDPage pdfPage )
+ {
+ page = pdfPage;
+ PDRectangle pageSize = page.findMediaBox();
+ int rotation = page.findRotation();
+ pageDimension = pageSize.createDimension();
+ if( rotation == 90 || rotation == 270 )
+ {
+ pageDimension = new Dimension( pageDimension.height, pageDimension.width );
+ }
+ setSize( pageDimension );
+ setBackground( java.awt.Color.white );
+ }
+
+ /**
+ * @see JPanel#paint( Graphics )
+ */
+ public void paint(Graphics g )
+ {
+ try
+ {
+ g.setColor( getBackground() );
+ g.fillRect( 0, 0, getWidth(), getHeight() );
+ drawer.drawPage( g, page, pageDimension );
+ }
+ catch( IOException e )
+ {
+ e.printStackTrace();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfviewer/PDFTreeCellRenderer.java b/src/main/java/org/pdfbox/pdfviewer/PDFTreeCellRenderer.java
new file mode 100644
index 0000000..98c1f5a
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/PDFTreeCellRenderer.java
@@ -0,0 +1,145 @@
+/**
+ * Copyright (c) 2003, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+import java.awt.Component;
+
+import javax.swing.JTree;
+
+import javax.swing.tree.DefaultTreeCellRenderer;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSNull;
+import org.pdfbox.cos.COSFloat;
+import org.pdfbox.cos.COSInteger;
+import org.pdfbox.cos.COSStream;
+import org.pdfbox.cos.COSString;
+
+/**
+ * A class to render tree cells for the pdfviewer.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.5 $
+ */
+public class PDFTreeCellRenderer extends DefaultTreeCellRenderer
+{
+ /**
+ * @see DefaultTreeCellRenderer#getTreeCellRendererComponent( JTree, Object, boolean,
+ * boolean, boolean, int, boolean )
+ */
+ public Component getTreeCellRendererComponent(
+ JTree tree,
+ Object nodeValue,
+ boolean isSelected,
+ boolean expanded,
+ boolean leaf,
+ int row,
+ boolean componentHasFocus)
+ {
+ nodeValue = convertToTreeObject( nodeValue );
+ return super.getTreeCellRendererComponent( tree, nodeValue, isSelected, expanded, leaf,
+ row, componentHasFocus );
+ }
+
+ private Object convertToTreeObject( Object nodeValue )
+ {
+ if( nodeValue instanceof MapEntry )
+ {
+ MapEntry entry = (MapEntry)nodeValue;
+ COSName key = (COSName)entry.getKey();
+ COSBase value = (COSBase)entry.getValue();
+ nodeValue = key.getName() + ":" + convertToTreeObject( value );
+ }
+ else if( nodeValue instanceof COSFloat )
+ {
+ nodeValue = "" + ((COSFloat)nodeValue).floatValue();
+ }
+ else if( nodeValue instanceof COSInteger )
+ {
+ nodeValue = "" + ((COSInteger)nodeValue).intValue();
+ }
+ else if( nodeValue instanceof COSString )
+ {
+ nodeValue = ((COSString)nodeValue).getString();
+ }
+ else if( nodeValue instanceof COSName )
+ {
+ nodeValue = ((COSName)nodeValue).getName();
+ }
+ else if( nodeValue instanceof ArrayEntry )
+ {
+ ArrayEntry entry = (ArrayEntry)nodeValue;
+ nodeValue = "[" + entry.getIndex() + "]" + convertToTreeObject( entry.getValue() );
+ }
+ else if( nodeValue instanceof COSNull )
+ {
+ nodeValue = "null";
+ }
+ else if( nodeValue instanceof COSDictionary )
+ {
+ COSDictionary dict = (COSDictionary)nodeValue;
+ if( nodeValue instanceof COSStream )
+ {
+ nodeValue = "Stream";
+ }
+ else
+ {
+ nodeValue = "Dictionary";
+ }
+
+ COSName type = (COSName)dict.getDictionaryObject( COSName.TYPE );
+ if( type != null )
+ {
+ nodeValue = nodeValue + "(" + type.getName();
+ COSName subType = (COSName)dict.getDictionaryObject( COSName.SUBTYPE );
+ if( subType != null )
+ {
+ nodeValue = nodeValue + ":" + subType.getName();
+ }
+
+ nodeValue = nodeValue + ")";
+ }
+ }
+ else if( nodeValue instanceof COSArray )
+ {
+ nodeValue="Array";
+ }
+ else if( nodeValue instanceof COSString )
+ {
+ nodeValue = ((COSString)nodeValue).getString();
+ }
+ return nodeValue;
+
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfviewer/PDFTreeModel.java b/src/main/java/org/pdfbox/pdfviewer/PDFTreeModel.java
new file mode 100644
index 0000000..368a2ca
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/PDFTreeModel.java
@@ -0,0 +1,332 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+/**
+ * A tree model that uses a cos document.
+ *
+ *
+ * @author wurtz
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.8 $
+ */
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeModel;
+
+//import java.awt.event.*;
+import javax.swing.event.TreeModelListener;
+
+import org.pdfbox.cos.COSArray;
+import org.pdfbox.cos.COSBase;
+import org.pdfbox.cos.COSDictionary;
+import org.pdfbox.cos.COSDocument;
+import org.pdfbox.cos.COSName;
+import org.pdfbox.cos.COSObject;
+
+import org.pdfbox.pdmodel.PDDocument;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class to model a PDF document as a tree structure.
+ *
+ * @author Ben Litchfield (ben@csh.rit.edu)
+ * @version $Revision: 1.8 $
+ */
+public class PDFTreeModel implements TreeModel
+{
+ private PDDocument document;
+
+ /**
+ * constructor.
+ */
+ public PDFTreeModel()
+ {
+ //default constructor
+ }
+
+ /**
+ * Constructor to take a document.
+ *
+ * @param doc The document to display in the tree.
+ */
+ public PDFTreeModel(PDDocument doc)
+ {
+ setDocument(doc);
+ }
+
+ /**
+ * Set the document to display in the tree.
+ *
+ * @param doc The document to display in the tree.
+ */
+ public void setDocument(PDDocument doc)
+ {
+ document = doc;
+ }
+ /**
+ * Adds a listener for the <code>TreeModelEvent</code>
+ * posted after the tree changes.
+ *
+ * @param l the listener to add
+ * @see #removeTreeModelListener
+ *
+ */
+ public void addTreeModelListener(TreeModelListener l)
+ {
+ //required for interface
+ }
+
+ /**
+ * Returns the child of <code>parent</code> at index <code>index</code>
+ * in the parent's
+ * child array. <code>parent</code> must be a node previously obtained
+ * from this data source. This should not return <code>null</code>
+ * if <code>index</code>
+ * is a valid index for <code>parent</code> (that is <code>index >= 0 &&
+ * index < getChildCount(parent</code>)).
+ *
+ * @param parent a node in the tree, obtained from this data source
+ * @param index The index into the parent object to location the child object.
+ * @return the child of <code>parent</code> at index <code>index</code>
+ *
+ */
+ public Object getChild(Object parent, int index)
+ {
+ Object retval = null;
+ if( parent instanceof COSArray )
+ {
+ ArrayEntry entry = new ArrayEntry();
+ entry.setIndex( index );
+ entry.setValue( ((COSArray)parent).getObject( index ) );
+ retval = entry;
+ }
+ else if( parent instanceof COSDictionary )
+ {
+ COSDictionary dict = ((COSDictionary)parent);
+ List keys = dict.keyList();
+ Collections.sort( keys );
+ Object key = keys.get( index );
+ Object value = dict.getDictionaryObject( (COSName)key );
+ MapEntry entry = new MapEntry();
+ entry.setKey( key );
+ entry.setValue( value );
+ retval = entry;
+ }
+ else if( parent instanceof MapEntry )
+ {
+ retval = getChild( ((MapEntry)parent).getValue(), index );
+ }
+ else if( parent instanceof ArrayEntry )
+ {
+ retval = getChild( ((ArrayEntry)parent).getValue(), index );
+ }
+ else if( parent instanceof COSDocument )
+ {
+ retval = ((COSDocument)parent).getObjects().get( index );
+ }
+ else if( parent instanceof COSObject )
+ {
+ retval = ((COSObject)parent).getObject();
+ }
+ else
+ {
+ throw new RuntimeException( "Unknown COS type " + parent.getClass().getName() );
+ }
+ return retval;
+ }
+
+ /** Returns the number of children of <code>parent</code>.
+ * Returns 0 if the node
+ * is a leaf or if it has no children. <code>parent</code> must be a node
+ * previously obtained from this data source.
+ *
+ * @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)
+ {
+ int retval = 0;
+ if( parent instanceof COSArray )
+ {
+ retval = ((COSArray)parent).size();
+ }
+ else if( parent instanceof COSDictionary )
+ {
+ retval = ((COSDictionary)parent).size();
+ }
+ else if( parent instanceof MapEntry )
+ {
+ retval = getChildCount( ((MapEntry)parent).getValue() );
+ }
+ else if( parent instanceof ArrayEntry )
+ {
+ retval = getChildCount( ((ArrayEntry)parent).getValue() );
+ }
+ else if( parent instanceof COSDocument )
+ {
+ retval = ((COSDocument)parent).getObjects().size();
+ }
+ else if( parent instanceof COSObject )
+ {
+ retval = 1;
+ }
+ return retval;
+ }
+
+ /** Returns the index of child in parent. If <code>parent</code>
+ * is <code>null</code> or <code>child</code> is <code>null</code>,
+ * returns -1.
+ *
+ * @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)
+ {
+ int retval = -1;
+ if( parent != null && child != null )
+ {
+ if( parent instanceof COSArray )
+ {
+ COSArray array = (COSArray)parent;
+ if( child instanceof ArrayEntry )
+ {
+ ArrayEntry arrayEntry = (ArrayEntry)child;
+ retval = arrayEntry.getIndex();
+ }
+ else
+ {
+ retval = array.indexOf( (COSBase)child );
+ }
+ }
+ else if( parent instanceof COSDictionary )
+ {
+ MapEntry entry = (MapEntry)child;
+ COSDictionary dict = (COSDictionary)parent;
+ List keys = dict.keyList();
+ Collections.sort( keys );
+ for( int i=0; retval == -1 && i<keys.size(); i++ )
+ {
+ if( keys.get( i ).equals( entry.getKey() ) )
+ {
+ retval = i;
+ }
+ }
+ }
+ else if( parent instanceof MapEntry )
+ {
+ retval = getIndexOfChild( ((MapEntry)parent).getValue(), child );
+ }
+ else if( parent instanceof ArrayEntry )
+ {
+ retval = getIndexOfChild( ((ArrayEntry)parent).getValue(), child );
+ }
+ else if( parent instanceof COSDocument )
+ {
+ retval = ((COSDocument)parent).getObjects().indexOf( child );
+ }
+ else if( parent instanceof COSObject )
+ {
+ retval = 0;
+ }
+ else
+ {
+ throw new RuntimeException( "Unknown COS type " + parent.getClass().getName() );
+ }
+ }
+ return retval;
+ }
+
+ /** Returns the root of the tree. Returns <code>null</code>
+ * only if the tree has no nodes.
+ *
+ * @return the root of the tree
+ *
+ */
+ public Object getRoot()
+ {
+ return document.getDocument().getTrailer();
+ }
+
+ /** Returns <code>true</code> if <code>node</code> is a leaf.
+ * It is possible for this method to return <code>false</code>
+ * even if <code>node</code> has no children.
+ * A directory in a filesystem, for example,
+ * may contain no files; the node representing
+ * the directory is not a leaf, but it also has no children.
+ *
+ * @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)
+ {
+ boolean isLeaf = !(node instanceof COSDictionary ||
+ node instanceof COSArray ||
+ node instanceof COSDocument ||
+ node instanceof COSObject ||
+ (node instanceof MapEntry && !isLeaf(((MapEntry)node).getValue()) ) ||
+ (node instanceof ArrayEntry && !isLeaf(((ArrayEntry)node).getValue()) ));
+ return isLeaf;
+ }
+
+ /** Removes a listener previously added with
+ * <code>addTreeModelListener</code>.
+ *
+ * @see #addTreeModelListener
+ * @param l the listener to remove
+ *
+ */
+
+ public void removeTreeModelListener(TreeModelListener l)
+ {
+ //required for interface
+ }
+
+ /** Messaged when the user has altered the value for the item identified
+ * by <code>path</code> to <code>newValue</code>.
+ * If <code>newValue</code> signifies a truly new value
+ * the model should post a <code>treeNodesChanged</code> event.
+ *
+ * @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)
+ {
+ //required for interface
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfviewer/PageDrawer.java b/src/main/java/org/pdfbox/pdfviewer/PageDrawer.java
new file mode 100644
index 0000000..7c39f7b
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/PageDrawer.java
@@ -0,0 +1,268 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+
+import java.awt.geom.GeneralPath;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.pdfbox.pdmodel.PDPage;
+
+import org.pdfbox.pdmodel.font.PDFont;
+
+import org.pdfbox.util.PDFStreamEngine;
+import org.pdfbox.util.ResourceLoader;
+import org.pdfbox.util.TextPosition;
+
+/**
+ * This will paint a page in a PDF document to a graphics context.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.17 $
+ */
+public class PageDrawer extends PDFStreamEngine
+{
+
+ private Graphics2D graphics;
+ private Dimension pageSize;
+ private PDPage page;
+
+ private List lineSubPaths = new ArrayList();
+ private GeneralPath linePath = new GeneralPath();
+ private Color strokingColor = Color.BLACK;
+ private Color nonStrokingColor = Color.BLACK;
+
+ /**
+ * Default constructor, loads properties from file.
+ *
+ * @throws IOException If there is an error loading properties from the file.
+ */
+ public PageDrawer() throws IOException
+ {
+ super( ResourceLoader.loadProperties( "Resources/PageDrawer.properties" ) );
+ }
+
+ /**
+ * This will draw the page to the requested context.
+ *
+ * @param g The graphics context to draw onto.
+ * @param p The page to draw.
+ * @param pageDimension The size of the page to draw.
+ *
+ * @throws IOException If there is an IO error while drawing the page.
+ */
+ public void drawPage( Graphics g, PDPage p, Dimension pageDimension ) throws IOException
+ {
+ graphics = (Graphics2D)g;
+ page = p;
+ pageSize = pageDimension;
+
+ graphics.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
+ processStream( page, page.findResources(), page.getContents().getStream() );
+ // Transformations should be done in order
+ // 1 - Translate
+ // 2 - Rotate
+ // 3 - Scale
+ // Refer to PDFReference p176 (or 188 in xpdf)
+ //AffineTransform transform = graphics.getTransform();
+ //transform.setToTranslate( 0, page.findMediaBox().getHeight()/2 );
+ //transform.setToRotation((double)p.getRotation());
+ //transform.setTransform( 1, 0, 0, 1, 0, 0 );
+ //transform.setToScale( 1, 1 );
+
+ //AffineTransform rotation = graphics.getTransform();
+ //rotation.rotate( (page.findRotation() * Math.PI) / 180d );
+ //graphics.setTransform( rotation );
+ }
+
+ /**
+ * You should override this method if you want to perform an action when a
+ * string is being shown.
+ *
+ * @param text The string to display.
+ */
+ protected void showCharacter( TextPosition text )
+ {
+ //should use colorspaces for the font color but for now assume that
+ //the font color is black
+ try
+ {
+ graphics.setColor( Color.black );
+ PDFont font = text.getFont();
+ font.drawString( text.getCharacter(), graphics, text.getFontSize(), text.getXScale(), text.getYScale(),
+ text.getX(), text.getY() );
+ }
+ catch( IOException io )
+ {
+ io.printStackTrace();
+ }
+ }
+
+ /**
+ * Get the graphics that we are currently drawing on.
+ *
+ * @return The graphics we are drawing on.
+ */
+ public Graphics2D getGraphics()
+ {
+ return graphics;
+ }
+
+ /**
+ * Get the page that is currently being drawn.
+ *
+ * @return The page that is being drawn.
+ */
+ public PDPage getPage()
+ {
+ return page;
+ }
+
+ /**
+ * Get the size of the page that is currently being drawn.
+ *
+ * @return The size of the page that is being drawn.
+ */
+ public Dimension getPageSize()
+ {
+ return pageSize;
+ }
+
+ /**
+ * Fix the y coordinate based on page rotation.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @return The updated y coordinate.
+ */
+ public double fixY( double x, double y )
+ {
+ double retval = y;
+ int rotation = page.findRotation();
+ if( rotation == 0 )
+ {
+ retval = pageSize.getHeight() - y;
+ }
+ else if( rotation == 90 )
+ {
+ retval = y;
+ }
+ return retval;
+ }
+
+ /**
+ * Get the current line path to be drawn.
+ *
+ * @return The current line path to be drawn.
+ */
+ public GeneralPath getLinePath()
+ {
+ return linePath;
+ }
+
+ /**
+ * Set the line path to draw.
+ *
+ * @param newLinePath Set the line path to draw.
+ */
+ public void setLinePath(GeneralPath newLinePath)
+ {
+ linePath = newLinePath;
+ }
+
+ /**
+ * Get the current list of line paths to be drawn.
+ *
+ * @return The current list of line paths to be drawn.
+ */
+ public List getLineSubPaths()
+ {
+ return lineSubPaths;
+ }
+
+ /**
+ * Set the list of line paths to draw.
+ *
+ * @param newLineSubPaths Set the list of line paths to draw.
+ */
+ public void setLineSubPaths(List newLineSubPaths)
+ {
+ lineSubPaths = newLineSubPaths;
+ }
+
+ /**
+ * Get the non stroking color.
+ *
+ * @return The non stroking color.
+ */
+ public Color getNonStrokingColor()
+ {
+ return nonStrokingColor;
+ }
+
+ /**
+ * Set the non stroking color.
+ *
+ * @param newNonStrokingColor The non stroking color.
+ */
+ public void setNonStrokingColor(Color newNonStrokingColor)
+ {
+ nonStrokingColor = newNonStrokingColor;
+ }
+
+ /**
+ * Get the stroking color.
+ *
+ * @return The stroking color.
+ */
+ public Color getStrokingColor()
+ {
+ return strokingColor;
+ }
+
+ /**
+ * Set the stroking color.
+ *
+ * @param newStrokingColor The stroking color.
+ */
+ public void setStrokingColor(Color newStrokingColor)
+ {
+ strokingColor = newStrokingColor;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfviewer/PageWrapper.java b/src/main/java/org/pdfbox/pdfviewer/PageWrapper.java
new file mode 100644
index 0000000..b2dc275
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/PageWrapper.java
@@ -0,0 +1,118 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+import java.awt.Dimension;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+import java.io.IOException;
+
+import javax.swing.JPanel;
+
+import org.pdfbox.PDFReader;
+import org.pdfbox.pdmodel.PDPage;
+
+/**
+ * A class to handle some prettyness around a single PDF page.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @version $Revision: 1.4 $
+ */
+public class PageWrapper implements MouseMotionListener
+{
+ private JPanel pageWrapper = new JPanel();
+ private PDFPagePanel pagePanel = null;
+ private PDFReader reader = null;
+
+ private static final int SPACE_AROUND_DOCUMENT = 20;
+
+ /**
+ * Constructor.
+ *
+ * @param aReader The reader application that holds this page.
+ *
+ * @throws IOException If there is an error creating the page drawing objects.
+ */
+ public PageWrapper( PDFReader aReader ) throws IOException
+ {
+ reader = aReader;
+ pagePanel = new PDFPagePanel();
+ pageWrapper.setLayout( null );
+ pageWrapper.add( pagePanel );
+ pagePanel.setLocation( SPACE_AROUND_DOCUMENT, SPACE_AROUND_DOCUMENT );
+ pageWrapper.setBorder( javax.swing.border.LineBorder.createBlackLineBorder() );
+ pagePanel.addMouseMotionListener( this );
+ }
+
+ /**
+ * This will display the PDF page in this component.
+ *
+ * @param page The PDF page to display.
+ */
+ public void displayPage( PDPage page )
+ {
+ pagePanel.setPage( page );
+ pagePanel.setPreferredSize( pagePanel.getSize() );
+ Dimension d = pagePanel.getSize();
+ d.width+=(SPACE_AROUND_DOCUMENT*2);
+ d.height+=(SPACE_AROUND_DOCUMENT*2);
+
+ pageWrapper.setPreferredSize( d );
+ pageWrapper.validate();
+ }
+
+ /**
+ * This will get the JPanel that can be displayed.
+ *
+ * @return The panel with the displayed PDF page.
+ */
+ public JPanel getPanel()
+ {
+ return pageWrapper;
+ }
+
+ /**
+ * @see MouseMotionListener#mouseDragged(MouseEvent)
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ //do nothing when mouse moves.
+ }
+
+ /**
+ * @see MouseMotionListener#mouseMoved( MouseEvent )
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ //reader.getBottomStatusPanel().getStatusLabel().setText( e.getX() + "," + (pagePanel.getHeight() - e.getY()) );
+ reader.getBottomStatusPanel().getStatusLabel().setText( e.getX() + "," + e.getY() );
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/pdfbox/pdfviewer/ReaderBottomPanel.java b/src/main/java/org/pdfbox/pdfviewer/ReaderBottomPanel.java
new file mode 100644
index 0000000..516d2c2
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/ReaderBottomPanel.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.pdfviewer;
+
+import java.awt.Dimension;
+
+import javax.swing.JPanel;
+
+import javax.swing.JLabel;
+import java.awt.FlowLayout;
+/**
+ * A panel to display at the bottom of the window for status and other stuff.
+ *
+ * @author Ben Litchfield (ben@pdfbox.org)
+ * @version $Revision: 1.1 $
+ */
+public class ReaderBottomPanel extends JPanel
+{
+
+ private JLabel statusLabel = null;
+
+ /**
+ * This is the default constructor.
+ */
+ public ReaderBottomPanel()
+ {
+ super();
+ initialize();
+ }
+
+ /**
+ * This method initializes this.
+ */
+ private void initialize()
+ {
+ FlowLayout flowLayout1 = new FlowLayout();
+ this.setLayout(flowLayout1);
+ this.setComponentOrientation(java.awt.ComponentOrientation.LEFT_TO_RIGHT);
+ this.setPreferredSize( new Dimension( 1000, 20 ) );
+ flowLayout1.setAlignment(java.awt.FlowLayout.LEFT);
+ this.add(getStatusLabel(), null);
+ }
+
+ /**
+ * This method initializes status label.
+ *
+ * @return javax.swing.JLabel
+ */
+ public JLabel getStatusLabel()
+ {
+ if (statusLabel == null)
+ {
+ statusLabel = new JLabel();
+ statusLabel.setText("Ready");
+ }
+ return statusLabel;
+ }
+ }
diff --git a/src/main/java/org/pdfbox/pdfviewer/package.html b/src/main/java/org/pdfbox/pdfviewer/package.html
new file mode 100644
index 0000000..85ec4b6
--- /dev/null
+++ b/src/main/java/org/pdfbox/pdfviewer/package.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<html>
+<head>
+
+</head>
+<body>
+The pdfviewer package contains classes to graphically display information about a PDF document.
+</body>
+</html>