001/*
002 * $Id: FdfReader.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;
045import java.io.IOException;
046import java.io.InputStream;
047import java.net.URL;
048import java.util.HashMap;
049/** Reads an FDF form and makes the fields available
050 * @author Paulo Soares
051 */
052public class FdfReader extends PdfReader {
053
054    HashMap<String, PdfDictionary> fields;
055    String fileSpec;
056    PdfName encoding;
057
058    /** Reads an FDF form.
059     * @param filename the file name of the form
060     * @throws IOException on error
061     */
062    public FdfReader(String filename) throws IOException {
063        super(filename);
064    }
065
066    /** Reads an FDF form.
067     * @param pdfIn the byte array with the form
068     * @throws IOException on error
069     */
070    public FdfReader(byte pdfIn[]) throws IOException {
071        super(pdfIn);
072    }
073
074    /** Reads an FDF form.
075     * @param url the URL of the document
076     * @throws IOException on error
077     */
078    public FdfReader(URL url) throws IOException {
079        super(url);
080    }
081
082    /** Reads an FDF form.
083     * @param is the <CODE>InputStream</CODE> containing the document. The stream is read to the
084     * end but is not closed
085     * @throws IOException on error
086     */
087    public FdfReader(InputStream is) throws IOException {
088        super(is);
089    }
090
091    @Override
092    protected void readPdf() throws IOException {
093        fields = new HashMap<String, PdfDictionary>();
094        try {
095            tokens.checkFdfHeader();
096            rebuildXref();
097            readDocObj();
098        }
099        finally {
100            try {
101                tokens.close();
102            }
103            catch (Exception e) {
104                // empty on purpose
105            }
106        }
107        readFields();
108    }
109
110    protected void kidNode(PdfDictionary merged, String name) {
111        PdfArray kids = merged.getAsArray(PdfName.KIDS);
112        if (kids == null || kids.isEmpty()) {
113            if (name.length() > 0)
114                name = name.substring(1);
115            fields.put(name, merged);
116        }
117        else {
118            merged.remove(PdfName.KIDS);
119            for (int k = 0; k < kids.size(); ++k) {
120                PdfDictionary dic = new PdfDictionary();
121                dic.merge(merged);
122                PdfDictionary newDic = kids.getAsDict(k);
123                PdfString t = newDic.getAsString(PdfName.T);
124                String newName = name;
125                if (t != null)
126                    newName += "." + t.toUnicodeString();
127                dic.merge(newDic);
128                dic.remove(PdfName.T);
129                kidNode(dic, newName);
130            }
131        }
132    }
133
134    protected void readFields() {
135        catalog = trailer.getAsDict(PdfName.ROOT);
136        PdfDictionary fdf = catalog.getAsDict(PdfName.FDF);
137        if (fdf == null)
138            return;
139        PdfString fs = fdf.getAsString(PdfName.F);
140        if (fs != null)
141            fileSpec = fs.toUnicodeString();
142        PdfArray fld = fdf.getAsArray(PdfName.FIELDS);
143        if (fld == null)
144            return;
145        encoding = fdf.getAsName(PdfName.ENCODING);
146        PdfDictionary merged = new PdfDictionary();
147        merged.put(PdfName.KIDS, fld);
148        kidNode(merged, "");
149    }
150
151    /** Gets all the fields. The map is keyed by the fully qualified
152     * field name and the value is a merged <CODE>PdfDictionary</CODE>
153     * with the field content.
154     * @return all the fields
155     */
156    public HashMap<String, PdfDictionary> getFields() {
157        return fields;
158    }
159
160    /** Gets the field dictionary.
161     * @param name the fully qualified field name
162     * @return the field dictionary
163     */
164    public PdfDictionary getField(String name) {
165        return fields.get(name);
166    }
167
168    /**
169     * Gets a byte[] containing a file that is embedded in the FDF.
170     * @param name the fully qualified field name
171     * @return the bytes of the file
172     * @throws IOException
173     * @since 5.0.1
174     */
175    public byte[] getAttachedFile(String name) throws IOException {
176        PdfDictionary field = fields.get(name);
177        if (field != null) {
178                PdfIndirectReference ir = (PRIndirectReference)field.get(PdfName.V);
179                PdfDictionary filespec = (PdfDictionary)getPdfObject(ir.getNumber());
180                PdfDictionary ef = filespec.getAsDict(PdfName.EF);
181                ir = (PRIndirectReference)ef.get(PdfName.F);
182                PRStream stream = (PRStream)getPdfObject(ir.getNumber());
183                return getStreamBytes(stream);
184        }
185                return new byte[0];
186    }
187
188    /**
189     * Gets the field value or <CODE>null</CODE> if the field does not
190     * exist or has no value defined.
191     * @param name the fully qualified field name
192     * @return the field value or <CODE>null</CODE>
193     */
194    public String getFieldValue(String name) {
195        PdfDictionary field = fields.get(name);
196        if (field == null)
197            return null;
198        PdfObject v = getPdfObject(field.get(PdfName.V));
199        if (v == null)
200            return null;
201        if (v.isName())
202            return PdfName.decodeName(((PdfName)v).toString());
203        else if (v.isString()) {
204            PdfString vs = (PdfString)v;
205            if (encoding == null || vs.getEncoding() != null)
206                return vs.toUnicodeString();
207            byte b[] = vs.getBytes();
208            if (b.length >= 2 && b[0] == (byte)254 && b[1] == (byte)255)
209                return vs.toUnicodeString();
210            try {
211                if (encoding.equals(PdfName.SHIFT_JIS))
212                    return new String(b, "SJIS");
213                else if (encoding.equals(PdfName.UHC))
214                    return new String(b, "MS949");
215                else if (encoding.equals(PdfName.GBK))
216                    return new String(b, "GBK");
217                else if (encoding.equals(PdfName.BIGFIVE))
218                    return new String(b, "Big5");
219            }
220            catch (Exception e) {
221            }
222            return vs.toUnicodeString();
223        }
224        return null;
225    }
226
227    /** Gets the PDF file specification contained in the FDF.
228     * @return the PDF file specification contained in the FDF
229     */
230    public String getFileSpec() {
231        return fileSpec;
232    }
233}