001: /*
002: * $Id: PdfStream.java 2879 2007-08-06 09:10:46Z blowagie $
003: * $Name$
004: *
005: * Copyright 1999, 2000, 2001, 2002 Bruno Lowagie
006: *
007: * The contents of this file are subject to the Mozilla Public License Version 1.1
008: * (the "License"); you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the License.
014: *
015: * The Original Code is 'iText, a free JAVA-PDF library'.
016: *
017: * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
018: * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
019: * All Rights Reserved.
020: * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
021: * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
022: *
023: * Contributor(s): all the names of the contributors are added in the source code
024: * where applicable.
025: *
026: * Alternatively, the contents of this file may be used under the terms of the
027: * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
028: * provisions of LGPL are applicable instead of those above. If you wish to
029: * allow use of your version of this file only under the terms of the LGPL
030: * License and not to allow others to use your version of this file under
031: * the MPL, indicate your decision by deleting the provisions above and
032: * replace them with the notice and other provisions required by the LGPL.
033: * If you do not delete the provisions above, a recipient may use your version
034: * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
035: *
036: * This library is free software; you can redistribute it and/or modify it
037: * under the terms of the MPL as stated above or under the terms of the GNU
038: * Library General Public License as published by the Free Software Foundation;
039: * either version 2 of the License, or any later version.
040: *
041: * This library is distributed in the hope that it will be useful, but WITHOUT
042: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
043: * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
044: * details.
045: *
046: * If you didn't download this code from the following link, you should check if
047: * you aren't using an obsolete version:
048: * http://www.lowagie.com/iText/
049: */
050:
051: package com.lowagie.text.pdf;
052:
053: import java.io.ByteArrayOutputStream;
054: import java.io.IOException;
055: import java.io.InputStream;
056: import java.io.OutputStream;
057: import java.util.zip.Deflater;
058: import java.util.zip.DeflaterOutputStream;
059:
060: import com.lowagie.text.DocWriter;
061: import com.lowagie.text.Document;
062: import com.lowagie.text.ExceptionConverter;
063: import java.util.ArrayList;
064:
065: /**
066: * <CODE>PdfStream</CODE> is the Pdf stream object.
067: * <P>
068: * A stream, like a string, is a sequence of characters. However, an application can
069: * read a small portion of a stream at a time, while a string must be read in its entirety.
070: * For this reason, objects with potentially large amounts of data, such as images and
071: * page descriptions, are represented as streams.<BR>
072: * A stream consists of a dictionary that describes a sequence of characters, followed by
073: * the keyword <B>stream</B>, followed by zero or more lines of characters, followed by
074: * the keyword <B>endstream</B>.<BR>
075: * All streams must be <CODE>PdfIndirectObject</CODE>s. The stream dictionary must be a direct
076: * object. The keyword <B>stream</B> that follows the stream dictionary should be followed by
077: * a carriage return and linefeed or just a linefeed.<BR>
078: * Remark: In this version only the FLATEDECODE-filter is supported.<BR>
079: * This object is described in the 'Portable Document Format Reference Manual version 1.7'
080: * section 3.2.7 (page 60-63).<BR>
081: *
082: * @see PdfObject
083: * @see PdfDictionary
084: */
085:
086: public class PdfStream extends PdfDictionary {
087:
088: // membervariables
089:
090: /** is the stream compressed? */
091: protected boolean compressed = false;
092:
093: protected ByteArrayOutputStream streamBytes = null;
094: protected InputStream inputStream;
095: protected PdfIndirectReference ref;
096: protected int inputStreamLength = -1;
097: protected PdfWriter writer;
098: protected int rawLength;
099:
100: static final byte STARTSTREAM[] = DocWriter.getISOBytes("stream\n");
101: static final byte ENDSTREAM[] = DocWriter
102: .getISOBytes("\nendstream");
103: static final int SIZESTREAM = STARTSTREAM.length + ENDSTREAM.length;
104:
105: // constructors
106:
107: /**
108: * Constructs a <CODE>PdfStream</CODE>-object.
109: *
110: * @param bytes content of the new <CODE>PdfObject</CODE> as an array of <CODE>byte</CODE>.
111: */
112:
113: public PdfStream(byte[] bytes) {
114: super ();
115: type = STREAM;
116: this .bytes = bytes;
117: rawLength = bytes.length;
118: put(PdfName.LENGTH, new PdfNumber(bytes.length));
119: }
120:
121: /**
122: * Creates an efficient stream. No temporary array is ever created. The <CODE>InputStream</CODE>
123: * is totally consumed but is not closed. The general usage is:
124: * <p>
125: * <pre>
126: * InputStream in = ...;
127: * PdfStream stream = new PdfStream(in, writer);
128: * stream.flateCompress();
129: * writer.addToBody(stream);
130: * stream.writeLength();
131: * in.close();
132: * </pre>
133: * @param inputStream the data to write to this stream
134: * @param writer the <CODE>PdfWriter</CODE> for this stream
135: */
136: public PdfStream(InputStream inputStream, PdfWriter writer) {
137: super ();
138: type = STREAM;
139: this .inputStream = inputStream;
140: this .writer = writer;
141: ref = writer.getPdfIndirectReference();
142: put(PdfName.LENGTH, ref);
143: }
144:
145: /**
146: * Constructs a <CODE>PdfStream</CODE>-object.
147: */
148:
149: protected PdfStream() {
150: super ();
151: type = STREAM;
152: }
153:
154: /**
155: * Writes the stream length to the <CODE>PdfWriter</CODE>.
156: * <p>
157: * This method must be called and can only be called if the contructor {@link #PdfStream(InputStream,PdfWriter)}
158: * is used to create the stream.
159: * @throws IOException on error
160: * @see #PdfStream(InputStream,PdfWriter)
161: */
162: public void writeLength() throws IOException {
163: if (inputStream == null)
164: throw new UnsupportedOperationException(
165: "writeLength() can only be called in a contructed PdfStream(InputStream,PdfWriter).");
166: if (inputStreamLength == -1)
167: throw new IOException(
168: "writeLength() can only be called after output of the stream body.");
169: writer.addToBody(new PdfNumber(inputStreamLength), ref, false);
170: }
171:
172: /**
173: * Gets the raw length of the stream.
174: * @return the raw length of the stream
175: */
176: public int getRawLength() {
177: return rawLength;
178: }
179:
180: /**
181: * Compresses the stream.
182: */
183:
184: public void flateCompress() {
185: if (!Document.compress)
186: return;
187: // check if the flateCompress-method has already been
188: if (compressed) {
189: return;
190: }
191: if (inputStream != null) {
192: compressed = true;
193: return;
194: }
195: // check if a filter already exists
196: PdfObject filter = PdfReader.getPdfObject(get(PdfName.FILTER));
197: if (filter != null) {
198: if (filter.isName()) {
199: if (PdfName.FLATEDECODE.equals(filter))
200: return;
201: } else if (filter.isArray()) {
202: if (((PdfArray) filter).contains(PdfName.FLATEDECODE))
203: return;
204: } else {
205: throw new RuntimeException(
206: "Stream could not be compressed: filter is not a name or array.");
207: }
208: }
209: try {
210: // compress
211: ByteArrayOutputStream stream = new ByteArrayOutputStream();
212: DeflaterOutputStream zip = new DeflaterOutputStream(stream);
213: if (streamBytes != null)
214: streamBytes.writeTo(zip);
215: else
216: zip.write(bytes);
217: zip.close();
218: // update the object
219: streamBytes = stream;
220: bytes = null;
221: put(PdfName.LENGTH, new PdfNumber(streamBytes.size()));
222: if (filter == null) {
223: put(PdfName.FILTER, PdfName.FLATEDECODE);
224: } else {
225: PdfArray filters = new PdfArray(filter);
226: filters.add(PdfName.FLATEDECODE);
227: put(PdfName.FILTER, filters);
228: }
229: compressed = true;
230: } catch (IOException ioe) {
231: throw new ExceptionConverter(ioe);
232: }
233: }
234:
235: // public int getStreamLength(PdfWriter writer) {
236: // if (dicBytes == null)
237: // toPdf(writer);
238: // if (streamBytes != null)
239: // return streamBytes.size() + dicBytes.length + SIZESTREAM;
240: // else
241: // return bytes.length + dicBytes.length + SIZESTREAM;
242: // }
243:
244: protected void super ToPdf(PdfWriter writer, OutputStream os)
245: throws IOException {
246: super .toPdf(writer, os);
247: }
248:
249: /**
250: * @see com.lowagie.text.pdf.PdfDictionary#toPdf(com.lowagie.text.pdf.PdfWriter, java.io.OutputStream)
251: */
252: public void toPdf(PdfWriter writer, OutputStream os)
253: throws IOException {
254: if (inputStream != null && compressed)
255: put(PdfName.FILTER, PdfName.FLATEDECODE);
256: PdfEncryption crypto = null;
257: if (writer != null)
258: crypto = writer.getEncryption();
259: if (crypto != null) {
260: PdfObject filter = get(PdfName.FILTER);
261: if (filter != null) {
262: if (PdfName.CRYPT.equals(filter))
263: crypto = null;
264: else if (filter.isArray()) {
265: ArrayList af = ((PdfArray) filter).getArrayList();
266: if (!af.isEmpty()
267: && PdfName.CRYPT.equals(af.get(0)))
268: crypto = null;
269: }
270: }
271: }
272: PdfObject nn = get(PdfName.LENGTH);
273: if (crypto != null && nn != null && nn.isNumber()) {
274: int sz = ((PdfNumber) nn).intValue();
275: put(PdfName.LENGTH, new PdfNumber(crypto
276: .calculateStreamSize(sz)));
277: super ToPdf(writer, os);
278: put(PdfName.LENGTH, nn);
279: } else
280: super ToPdf(writer, os);
281: os.write(STARTSTREAM);
282: if (inputStream != null) {
283: rawLength = 0;
284: DeflaterOutputStream def = null;
285: OutputStreamCounter osc = new OutputStreamCounter(os);
286: OutputStreamEncryption ose = null;
287: OutputStream fout = osc;
288: if (crypto != null)
289: fout = ose = crypto.getEncryptionStream(fout);
290: if (compressed)
291: fout = def = new DeflaterOutputStream(fout,
292: new Deflater(Deflater.BEST_COMPRESSION), 0x8000);
293:
294: byte buf[] = new byte[4192];
295: while (true) {
296: int n = inputStream.read(buf);
297: if (n <= 0)
298: break;
299: fout.write(buf, 0, n);
300: rawLength += n;
301: }
302: if (def != null)
303: def.finish();
304: if (ose != null)
305: ose.finish();
306: inputStreamLength = osc.getCounter();
307: } else {
308: if (crypto == null) {
309: if (streamBytes != null)
310: streamBytes.writeTo(os);
311: else
312: os.write(bytes);
313: } else {
314: byte b[];
315: if (streamBytes != null) {
316: b = crypto.encryptByteArray(streamBytes
317: .toByteArray());
318: } else {
319: b = crypto.encryptByteArray(bytes);
320: }
321: os.write(b);
322: }
323: }
324: os.write(ENDSTREAM);
325: }
326:
327: /**
328: * Writes the data content to an <CODE>OutputStream</CODE>.
329: * @param os the destination to write to
330: * @throws IOException on error
331: */
332: public void writeContent(OutputStream os) throws IOException {
333: if (streamBytes != null)
334: streamBytes.writeTo(os);
335: else if (bytes != null)
336: os.write(bytes);
337: }
338:
339: /**
340: * @see com.lowagie.text.pdf.PdfObject#toString()
341: */
342: public String toString() {
343: if (get(PdfName.TYPE) == null)
344: return "Stream";
345: return "Stream of type: " + get(PdfName.TYPE);
346: }
347: }
|