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}