KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > pdfbox > cos > COSStream


1 /**
2  * Copyright (c) 2003-2006, www.pdfbox.org
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  * 3. Neither the name of pdfbox; nor the names of its
14  * contributors may be used to endorse or promote products derived from this
15  * software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * http://www.pdfbox.org
29  *
30  */

31 package org.pdfbox.cos;
32
33 import java.io.BufferedInputStream JavaDoc;
34 import java.io.BufferedOutputStream JavaDoc;
35 import java.io.ByteArrayInputStream JavaDoc;
36 import java.io.InputStream JavaDoc;
37 import java.io.IOException JavaDoc;
38 import java.io.OutputStream JavaDoc;
39
40 import java.util.List JavaDoc;
41
42 import org.pdfbox.filter.Filter;
43 import org.pdfbox.filter.FilterManager;
44
45 import org.pdfbox.pdfparser.PDFStreamParser;
46
47 import org.pdfbox.exceptions.COSVisitorException;
48
49 import org.pdfbox.io.RandomAccess;
50 import org.pdfbox.io.RandomAccessFileInputStream;
51 import org.pdfbox.io.RandomAccessFileOutputStream;
52
53 /**
54  * This class represents a stream object in a PDF document.
55  *
56  * @author <a HREF="mailto:ben@benlitchfield.com">Ben Litchfield</a>
57  * @version $Revision: 1.39 $
58  */

59 public class COSStream extends COSDictionary
60 {
61     private static final int BUFFER_SIZE=16384;
62
63     private RandomAccess file;
64     /**
65      * The stream with all of the filters applied.
66      */

67     private RandomAccessFileOutputStream filteredStream;
68
69     /**
70      * The stream with no filters, this contains the useful data.
71      */

72     private RandomAccessFileOutputStream unFilteredStream;
73
74     /**
75      * Constructor. Creates a new stream with an empty dictionary.
76      *
77      * @param storage The intermediate storage for the stream.
78      */

79     public COSStream( RandomAccess storage )
80     {
81         super();
82         file = storage;
83     }
84
85     /**
86      * Constructor.
87      *
88      * @param dictionary The dictionary that is associated with this stream.
89      * @param storage The intermediate storage for the stream.
90      */

91     public COSStream( COSDictionary dictionary, RandomAccess storage )
92     {
93         super( dictionary );
94         file = storage;
95     }
96
97     /**
98      * This will replace this object with the data from the new object. This
99      * is used to easily maintain referential integrity when changing references
100      * to new objects.
101      *
102      * @param stream The stream that have the new values in it.
103      */

104     public void replaceWithStream( COSStream stream )
105     {
106         this.clear();
107         this.addAll( stream );
108         file = stream.file;
109         filteredStream = stream.filteredStream;
110         unFilteredStream = stream.unFilteredStream;
111     }
112
113     /**
114      * This will get the scratch file associated with this stream.
115      *
116      * @return The scratch file where this stream is being stored.
117      */

118     public RandomAccess getScratchFile()
119     {
120         return file;
121     }
122
123     /**
124      * This will get all the tokens in the stream.
125      *
126      * @return All of the tokens in the stream.
127      *
128      * @throws IOException If there is an error parsing the stream.
129      */

130     public List JavaDoc getStreamTokens() throws IOException JavaDoc
131     {
132         PDFStreamParser parser = new PDFStreamParser( this );
133         parser.parse();
134         return parser.getTokens();
135     }
136
137     /**
138      * This will get the stream with all of the filters applied.
139      *
140      * @return the bytes of the physical (endoced) stream
141      *
142      * @throws IOException when encoding/decoding causes an exception
143      */

144     public InputStream JavaDoc getFilteredStream() throws IOException JavaDoc
145     {
146         if( filteredStream == null )
147         {
148             doEncode();
149         }
150         long position = filteredStream.getPosition();
151         long length = filteredStream.getLength();
152
153         RandomAccessFileInputStream input =
154             new RandomAccessFileInputStream( file, position, length );
155         return new BufferedInputStream JavaDoc( input, BUFFER_SIZE );
156     }
157
158     /**
159      * This will get the logical content stream with none of the filters.
160      *
161      * @return the bytes of the logical (decoded) stream
162      *
163      * @throws IOException when encoding/decoding causes an exception
164      */

165     public InputStream JavaDoc getUnfilteredStream() throws IOException JavaDoc
166     {
167         InputStream JavaDoc retval = null;
168         if( unFilteredStream == null )
169         {
170             doDecode();
171         }
172
173         //if unFilteredStream is still null then this stream has not been
174
//created yet, so we should return null.
175
if( unFilteredStream != null )
176         {
177             long position = unFilteredStream.getPosition();
178             long length = unFilteredStream.getLength();
179             RandomAccessFileInputStream input =
180                 new RandomAccessFileInputStream( file, position, length );
181             retval = new BufferedInputStream JavaDoc( input, BUFFER_SIZE );
182         }
183         else
184         {
185             // We should check if the COSStream contains data, maybe it
186
// has been created with a RandomAccessFile - which is not
187
// necessary empty.
188
// In this case, the creation was been done as an input, this should
189
// be the unfiltered file, since no filter has been applied yet.
190
// if ( (file != null) &&
191
// (file.length() > 0) )
192
// {
193
// retval = new RandomAccessFileInputStream( file,
194
// 0,
195
// file.length() );
196
// }
197
// else
198
// {
199
//if there is no stream data then simply return an empty stream.
200
retval = new ByteArrayInputStream JavaDoc( new byte[0] );
201 // }
202
}
203         return retval;
204     }
205
206     /**
207      * visitor pattern double dispatch method.
208      *
209      * @param visitor The object to notify when visiting this object.
210      * @return any object, depending on the visitor implementation, or null
211      * @throws COSVisitorException If an error occurs while visiting this object.
212      */

213     public Object JavaDoc accept(ICOSVisitor visitor) throws COSVisitorException
214     {
215         return visitor.visitFromStream(this);
216     }
217
218     /**
219      * This will decode the physical byte stream applying all of the filters to the stream.
220      *
221      * @throws IOException If there is an error applying a filter to the stream.
222      */

223     private void doDecode() throws IOException JavaDoc
224     {
225 // FIXME: We shouldn't keep the same reference?
226
unFilteredStream = filteredStream;
227
228         COSBase filters = getFilters();
229         if( filters == null )
230         {
231             //then do nothing
232
}
233         else if( filters instanceof COSName )
234         {
235             doDecode( (COSName)filters );
236         }
237         else if( filters instanceof COSArray )
238         {
239             COSArray filterArray = (COSArray)filters;
240             for( int i=0; i<filterArray.size(); i++ )
241             {
242                 COSName filterName = (COSName)filterArray.get( i );
243                 doDecode( filterName );
244             }
245         }
246         else
247         {
248             throw new IOException JavaDoc( "Error: Unknown filter type:" + filters );
249         }
250     }
251
252     /**
253      * This will decode applying a single filter on the stream.
254      *
255      * @param filterName The name of the filter.
256      *
257      * @throws IOException If there is an error parsing the stream.
258      */

259     private void doDecode( COSName filterName ) throws IOException JavaDoc
260     {
261         FilterManager manager = getFilterManager();
262         Filter filter = manager.getFilter( filterName );
263         InputStream JavaDoc input;
264
265         boolean done = false;
266         IOException JavaDoc exception = null;
267         long position = unFilteredStream.getPosition();
268         long length = unFilteredStream.getLength();
269
270         if( length == 0 )
271         {
272             //if the length is zero then don't bother trying to decode
273
//some filters don't work when attempting to decode
274
//with a zero length stream. See zlib_error_01.pdf
275
unFilteredStream = new RandomAccessFileOutputStream( file );
276             done = true;
277         }
278         else
279         {
280             //ok this is a simple hack, sometimes we read a couple extra
281
//bytes that shouldn't be there, so we encounter an error we will just
282
//try again with one less byte.
283
for( int tryCount=0; !done && tryCount<5; tryCount++ )
284             {
285                 try
286                 {
287                     input = new BufferedInputStream JavaDoc(
288                         new RandomAccessFileInputStream( file, position, length ), BUFFER_SIZE );
289                     unFilteredStream = new RandomAccessFileOutputStream( file );
290                     filter.decode( input, unFilteredStream, this );
291                     done = true;
292                 }
293                 catch( IOException JavaDoc io )
294                 {
295                     length--;
296                     exception = io;
297                 }
298             }
299         }
300         if( !done )
301         {
302             throw exception;
303         }
304     }
305
306     /**
307      * This will encode the logical byte stream applying all of the filters to the stream.
308      *
309      * @throws IOException If there is an error applying a filter to the stream.
310      */

311     private void doEncode() throws IOException JavaDoc
312     {
313         filteredStream = unFilteredStream;
314
315         COSBase filters = getFilters();
316         if( filters == null )
317         {
318             //there is no filter to apply
319
}
320         else if( filters instanceof COSName )
321         {
322             doEncode( (COSName)filters );
323         }
324         else if( filters instanceof COSArray )
325         {
326             // apply filters in reverse order
327
COSArray filterArray = (COSArray)filters;
328             for( int i=filterArray.size()-1; i>=0; i-- )
329             {
330                 COSName filterName = (COSName)filterArray.get( i );
331                 doEncode( filterName );
332             }
333         }
334     }
335
336     /**
337      * This will encode applying a single filter on the stream.
338      *
339      * @param filterName The name of the filter.
340      *
341      * @throws IOException If there is an error parsing the stream.
342      */

343     private void doEncode( COSName filterName ) throws IOException JavaDoc
344     {
345         FilterManager manager = getFilterManager();
346         Filter filter = manager.getFilter( filterName );
347         InputStream JavaDoc input;
348
349         input = new BufferedInputStream JavaDoc(
350             new RandomAccessFileInputStream( file, filteredStream.getPosition(),
351                                                    filteredStream.getLength() ), BUFFER_SIZE );
352         filteredStream = new RandomAccessFileOutputStream( file );
353         filter.encode( input, filteredStream, this );
354     }
355
356     /**
357      * This will return the filters to apply to the byte stream.
358      * The method will return
359      * - null if no filters are to be applied
360      * - a COSName if one filter is to be applied
361      * - a COSArray containing COSNames if multiple filters are to be applied
362      *
363      * @return the COSBase object representing the filters
364      */

365     public COSBase getFilters()
366     {
367         return getDictionaryObject(COSName.FILTER);
368     }
369
370     /**
371      * This will create a new stream for which filtered byte should be
372      * written to. You probably don't want this but want to use the
373      * createUnfilteredStream, which is used to write raw bytes to.
374      *
375      * @return A stream that can be written to.
376      *
377      * @throws IOException If there is an error creating the stream.
378      */

379     public OutputStream JavaDoc createFilteredStream() throws IOException JavaDoc
380     {
381         filteredStream = new RandomAccessFileOutputStream( file );
382         unFilteredStream = null;
383         return new BufferedOutputStream JavaDoc( filteredStream, BUFFER_SIZE );
384     }
385
386     /**
387      * This will create a new stream for which filtered byte should be
388      * written to. You probably don't want this but want to use the
389      * createUnfilteredStream, which is used to write raw bytes to.
390      *
391      * @param expectedLength An entry where a length is expected.
392      *
393      * @return A stream that can be written to.
394      *
395      * @throws IOException If there is an error creating the stream.
396      */

397     public OutputStream JavaDoc createFilteredStream( COSBase expectedLength ) throws IOException JavaDoc
398     {
399         filteredStream = new RandomAccessFileOutputStream( file );
400         filteredStream.setExpectedLength( expectedLength );
401         unFilteredStream = null;
402         return new BufferedOutputStream JavaDoc( filteredStream, BUFFER_SIZE );
403     }
404
405     /**
406      * set the filters to be applied to the stream.
407      *
408      * @param filters The filters to set on this stream.
409      *
410      * @throws IOException If there is an error clearing the old filters.
411      */

412     public void setFilters(COSBase filters) throws IOException JavaDoc
413     {
414         setItem(COSName.FILTER, filters);
415         // kill cached filtered streams
416
filteredStream = null;
417     }
418
419     /**
420      * This will create an output stream that can be written to.
421      *
422      * @return An output stream which raw data bytes should be written to.
423      *
424      * @throws IOException If there is an error creating the stream.
425      */

426     public OutputStream JavaDoc createUnfilteredStream() throws IOException JavaDoc
427     {
428         unFilteredStream = new RandomAccessFileOutputStream( file );
429         filteredStream = null;
430         return new BufferedOutputStream JavaDoc( unFilteredStream, BUFFER_SIZE );
431     }
432 }
Popular Tags