001: /**
002: * Copyright (c) 2003-2005, www.pdfbox.org
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * 1. Redistributions of source code must retain the above copyright notice,
009: * this list of conditions and the following disclaimer.
010: * 2. Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: * 3. Neither the name of pdfbox; nor the names of its
014: * contributors may be used to endorse or promote products derived from this
015: * software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
021: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: *
028: * http://www.pdfbox.org
029: *
030: */package org.pdfbox.filter;
031:
032: import java.io.ByteArrayInputStream;
033: import java.io.ByteArrayOutputStream;
034: import java.io.IOException;
035: import java.io.InputStream;
036: import java.io.OutputStream;
037: import java.util.zip.DeflaterOutputStream;
038: import java.util.zip.InflaterInputStream;
039:
040: import org.pdfbox.cos.COSDictionary;
041:
042: /**
043: * This is the used for the FlateDecode filter.
044: *
045: * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
046: * @author Marcel Kammer
047: * @version $Revision: 1.10 $
048: */
049: public class FlateFilter implements Filter {
050: private static final int BUFFER_SIZE = 2048;
051:
052: /**
053: * This will decode some compressed data.
054: *
055: * @param compressedData
056: * The compressed byte stream.
057: * @param result
058: * The place to write the uncompressed byte stream.
059: * @param options
060: * The options to use to encode the data.
061: *
062: * @throws IOException
063: * If there is an error decompressing the stream.
064: */
065:
066: public void decode(InputStream compressedData, OutputStream result,
067: COSDictionary options) throws IOException {
068: COSDictionary dict = (COSDictionary) options
069: .getDictionaryObject("DecodeParms");
070: int predictor = -1;
071: int colors = -1;
072: int bitsPerPixel = -1;
073: int columns = -1;
074: InflaterInputStream decompressor = null;
075: ByteArrayInputStream bais = null;
076: ByteArrayOutputStream baos = null;
077: if (dict != null) {
078: predictor = dict.getInt("Predictor");
079: colors = dict.getInt("Colors");
080: bitsPerPixel = options.getInt("BitsPerComponent");
081: columns = dict.getInt("Columns");
082: }
083:
084: try {
085: // Decompress data to temporary ByteArrayOutputStream
086: decompressor = new InflaterInputStream(compressedData);
087: byte[] buffer = new byte[BUFFER_SIZE];
088: int amountRead;
089:
090: // Decode data using given predictor
091: if (predictor == -1 || predictor == 1 && predictor == 10) {
092: // decoding not needed
093: while ((amountRead = decompressor.read(buffer, 0,
094: BUFFER_SIZE)) != -1) {
095: result.write(buffer, 0, amountRead);
096: }
097: } else {
098: if (colors == -1 || bitsPerPixel == -1 || columns == -1) {
099: throw new IOException(
100: "Could not read all parameters to decode image");
101: }
102:
103: baos = new ByteArrayOutputStream();
104: while ((amountRead = decompressor.read(buffer, 0,
105: BUFFER_SIZE)) != -1) {
106: baos.write(buffer, 0, amountRead);
107: }
108: baos.flush();
109:
110: // Copy data to ByteArrayInputStream for reading
111: bais = new ByteArrayInputStream(baos.toByteArray());
112: baos.close();
113: baos = null;
114:
115: byte[] decodedData = decodePredictor(predictor, colors,
116: bitsPerPixel, columns, bais);
117: bais.close();
118: bais = new ByteArrayInputStream(decodedData);
119:
120: // write decoded data to result
121: while ((amountRead = bais.read(buffer)) != -1) {
122: result.write(buffer, 0, amountRead);
123: }
124: bais.close();
125: bais = null;
126: }
127:
128: result.flush();
129: } finally {
130: if (decompressor != null) {
131: decompressor.close();
132: }
133: if (bais != null) {
134: bais.close();
135: }
136: if (baos != null) {
137: baos.close();
138: }
139: }
140: }
141:
142: private byte[] decodePredictor(int predictor, int colors,
143: int bitsPerComponent, int columns, InputStream data)
144: throws IOException {
145: ByteArrayOutputStream baos = new ByteArrayOutputStream();
146: byte[] buffer = new byte[2048];
147:
148: if (predictor == 1 || predictor == 10) {
149: // No prediction or PNG NONE
150: int i = 0;
151: while ((i = data.read(buffer)) != -1) {
152: baos.write(buffer, 0, i);
153: }
154: } else {
155: // calculate sizes
156: int bpp = (colors * bitsPerComponent + 7) / 8;
157: int rowlength = (columns * colors * bitsPerComponent + 7)
158: / 8 + bpp;
159: byte[] actline = new byte[rowlength];
160: byte[] lastline = new byte[rowlength];// Initialize lastline with
161: // Zeros according to
162: // PNG-specification
163: boolean done = false;
164: int linepredictor = predictor;
165:
166: while (!done) {
167: if (predictor == 15) {
168: linepredictor = data.read();// read per line predictor
169: if (linepredictor == -1) {
170: done = true;// reached EOF
171: break;
172: } else {
173: linepredictor += 10; // add 10 to tread value 1 as 11
174: }
175: // (instead of PRED NONE) and 2
176: // as 12 (instead of PRED TIFF)
177: }
178:
179: // read line
180: int i = 0;
181: int offset = bpp;
182: while (offset < rowlength
183: && ((i = data.read(actline, offset, rowlength
184: - offset)) != -1)) {
185: offset += i;
186: }
187:
188: // Do prediction as specified in PNG-Specification 1.2
189: switch (linepredictor) {
190: case 2:// PRED TIFF SUB
191: /**
192: * @todo decode tiff
193: */
194: throw new IOException(
195: "TIFF-Predictor not supported");
196: case 11:// PRED SUB
197: for (int p = bpp; p < rowlength; p++) {
198: int sub = actline[p] & 0xff;
199: int left = actline[p - bpp] & 0xff;
200: actline[p] = (byte) (sub + left);
201: }
202: break;
203: case 12:// PRED UP
204: for (int p = bpp; p < rowlength; p++) {
205: int up = actline[p] & 0xff;
206: int prior = lastline[p] & 0xff;
207: actline[p] = (byte) (up + prior);
208: }
209: break;
210: case 13:// PRED AVG
211: for (int p = bpp; p < rowlength; p++) {
212: int avg = actline[p] & 0xff;
213: int left = actline[p - bpp] & 0xff;
214: int up = lastline[p] & 0xff;
215: actline[p] = (byte) (avg + ((left + up) / 2));
216: }
217: break;
218: case 14:// PRED PAETH
219: for (int p = bpp; p < rowlength; p++) {
220: int paeth = actline[p] & 0xff;
221: int a = actline[p - bpp] & 0xff;// left
222: int b = lastline[p] & 0xff;// upper
223: int c = lastline[p - bpp] & 0xff;// upperleft
224: int value = a + b - c;
225: int absa = Math.abs(value - a);
226: int absb = Math.abs(value - b);
227: int absc = Math.abs(value - c);
228:
229: if (absa <= absb && absa <= absc) {
230: actline[p] = (byte) (paeth + absa);
231: } else if (absb <= absc) {
232: actline[p] += (byte) (paeth + absb);
233: } else {
234: actline[p] += (byte) (paeth + absc);
235: }
236: }
237: break;
238: default:
239: break;
240: }
241:
242: lastline = actline;
243: baos.write(actline, bpp, actline.length - bpp);
244: }
245: }
246:
247: return baos.toByteArray();
248: }
249:
250: /**
251: * This will encode some data.
252: *
253: * @param rawData
254: * The raw data to encode.
255: * @param result
256: * The place to write to encoded results to.
257: * @param options
258: * The options to use to encode the data.
259: *
260: * @throws IOException
261: * If there is an error compressing the stream.
262: */
263: public void encode(InputStream rawData, OutputStream result,
264: COSDictionary options) throws IOException {
265: DeflaterOutputStream out = new DeflaterOutputStream(result);
266: byte[] buffer = new byte[BUFFER_SIZE];
267: int amountRead = 0;
268: while ((amountRead = rawData.read(buffer, 0, BUFFER_SIZE)) != -1) {
269: out.write(buffer, 0, amountRead);
270: }
271: out.close();
272: result.flush();
273: }
274: }
|