001/*
002 * $Id: XfdfReader.java 4784 2011-03-15 08:33:00Z blowagie $
003 *
004 * This file is part of the iText (R) project.
005 * Copyright (c) 1998-2011 1T3XT BVBA
006 * Authors: Bruno Lowagie, Paulo Soares, et al.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU Affero General Public License version 3
010 * as published by the Free Software Foundation with the addition of the
011 * following permission added to Section 15 as permitted in Section 7(a):
012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
014 *
015 * This program is distributed in the hope that it will be useful, but
016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
017 * or FITNESS FOR A PARTICULAR PURPOSE.
018 * See the GNU Affero General Public License for more details.
019 * You should have received a copy of the GNU Affero General Public License
020 * along with this program; if not, see http://www.gnu.org/licenses or write to
021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
022 * Boston, MA, 02110-1301 USA, or download the license from the following URL:
023 * http://itextpdf.com/terms-of-use/
024 *
025 * The interactive user interfaces in modified source and object code versions
026 * of this program must display Appropriate Legal Notices, as required under
027 * Section 5 of the GNU Affero General Public License.
028 *
029 * In accordance with Section 7(b) of the GNU Affero General Public License,
030 * a covered work must retain the producer line in every PDF that is created
031 * or manipulated using iText.
032 *
033 * You can be released from the requirements of the license by purchasing
034 * a commercial license. Buying such a license is mandatory as soon as you
035 * develop commercial activities involving the iText software without
036 * disclosing the source code of your own applications.
037 * These activities include: offering paid services to customers as an ASP,
038 * serving PDFs on the fly in a web application, shipping iText with a closed
039 * source product.
040 *
041 * For more information, please contact iText Software Corp. at this
042 * address: sales@itextpdf.com
043 */
044package com.itextpdf.text.pdf;
045
046import java.io.ByteArrayInputStream;
047import java.io.FileInputStream;
048import java.io.IOException;
049import java.io.InputStream;
050import java.util.ArrayList;
051import java.util.HashMap;
052import java.util.List;
053import java.util.Map;
054import java.util.Stack;
055
056import com.itextpdf.text.error_messages.MessageLocalization;
057import com.itextpdf.text.xml.simpleparser.SimpleXMLDocHandler;
058import com.itextpdf.text.xml.simpleparser.SimpleXMLParser;
059
060/**
061 * Reads a XFDF.
062 * @author Leonard Rosenthol (leonardr@pdfsages.com)
063 */
064public class XfdfReader implements SimpleXMLDocHandler {
065        // stuff used during parsing to handle state
066        private boolean foundRoot = false;
067    private final Stack<String> fieldNames = new Stack<String>();
068    private final Stack<String> fieldValues = new Stack<String>();
069
070    // storage for the field list and their values
071        HashMap<String, String> fields;
072        /**
073         * Storage for field values if there's more than one value for a field.
074         * @since       2.1.4
075         */
076        protected HashMap<String, List<String>> listFields;
077
078        // storage for the path to referenced PDF, if any
079        String  fileSpec;
080
081   /**
082    * Reads an XFDF form.
083     * @param filename the file name of the form
084     * @throws IOException on error
085     */
086    public XfdfReader(String filename) throws IOException {
087        FileInputStream fin = null;
088        try {
089            fin = new FileInputStream(filename);
090            SimpleXMLParser.parse(this, fin);
091        }
092        finally {
093            try{if (fin != null) {fin.close();}}catch(Exception e){}
094        }
095    }
096
097    /**
098     * Reads an XFDF form.
099     * @param xfdfIn the byte array with the form
100     * @throws IOException on error
101     */
102    public XfdfReader(byte xfdfIn[]) throws IOException {
103        this(new ByteArrayInputStream(xfdfIn));
104   }
105
106    /**
107     * Reads an XFDF form.
108     * @param is an InputStream to read the form
109     * @throws IOException on error
110     * @since 5.0.1
111     */
112    public XfdfReader(InputStream is) throws IOException {
113        SimpleXMLParser.parse( this, is);
114   }
115
116    /** Gets all the fields. The map is keyed by the fully qualified
117     * field name and the value is a merged <CODE>PdfDictionary</CODE>
118     * with the field content.
119     * @return all the fields
120     */
121    public HashMap<String, String> getFields() {
122        return fields;
123    }
124
125    /** Gets the field value.
126     * @param name the fully qualified field name
127     * @return the field's value
128     */
129    public String getField(String name) {
130        return fields.get(name);
131    }
132
133    /** Gets the field value or <CODE>null</CODE> if the field does not
134     * exist or has no value defined.
135     * @param name the fully qualified field name
136     * @return the field value or <CODE>null</CODE>
137     */
138    public String getFieldValue(String name) {
139        String field = fields.get(name);
140        if (field == null)
141            return null;
142        else
143                return field;
144    }
145
146    /**
147     * Gets the field values for a list or <CODE>null</CODE> if the field does not
148     * exist or has no value defined.
149     * @param name the fully qualified field name
150     * @return the field values or <CODE>null</CODE>
151     * @since   2.1.4
152     */
153    public List<String> getListValues(String name) {
154        return listFields.get(name);
155    }
156
157    /** Gets the PDF file specification contained in the FDF.
158     * @return the PDF file specification contained in the FDF
159     */
160    public String getFileSpec() {
161        return fileSpec;
162    }
163
164    /**
165     * Called when a start tag is found.
166     * @param tag the tag name
167     * @param h the tag's attributes
168     */
169    public void startElement(String tag, Map<String, String> h)
170    {
171        if ( !foundRoot ) {
172            if (!tag.equals("xfdf"))
173                throw new RuntimeException(MessageLocalization.getComposedMessage("root.element.is.not.xfdf.1", tag));
174            else
175                foundRoot = true;
176        }
177
178        if ( tag.equals("xfdf") ){
179
180        } else if ( tag.equals("f") ) {
181                fileSpec = h.get( "href" );
182        } else if ( tag.equals("fields") ) {
183            fields = new HashMap<String, String>();             // init it!
184            listFields = new HashMap<String, List<String>>();
185        } else if ( tag.equals("field") ) {
186                String  fName = h.get( "name" );
187                fieldNames.push( fName );
188        } else if ( tag.equals("value") ) {
189                fieldValues.push( "" );
190        }
191    }
192    /**
193     * Called when an end tag is found.
194     * @param tag the tag name
195     */
196    public void endElement(String tag) {
197        if ( tag.equals("value") ) {
198            String      fName = "";
199            for (int k = 0; k < fieldNames.size(); ++k) {
200                fName += "." + fieldNames.elementAt(k);
201            }
202            if (fName.startsWith("."))
203                fName = fName.substring(1);
204            String fVal = fieldValues.pop();
205            String old = fields.put( fName, fVal );
206            if (old != null) {
207                List<String> l = listFields.get(fName);
208                if (l == null) {
209                        l = new ArrayList<String>();
210                        l.add(old);
211                }
212                l.add(fVal);
213                listFields.put(fName, l);
214            }
215        }
216        else if (tag.equals("field") ) {
217            if (!fieldNames.isEmpty())
218                fieldNames.pop();
219        }
220    }
221
222    /**
223     * Called when the document starts to be parsed.
224     */
225    public void startDocument()
226    {
227        fileSpec = "";
228    }
229    /**
230     * Called after the document is parsed.
231     */
232    public void endDocument()
233        {
234
235        }
236    /**
237     * Called when a text element is found.
238     * @param str the text element, probably a fragment.
239     */
240    public void text(String str)
241    {
242        if (fieldNames.isEmpty() || fieldValues.isEmpty())
243            return;
244
245        String val = fieldValues.pop();
246        val += str;
247        fieldValues.push(val);
248    }
249}