aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/pdfbox/filter/FlateFilter.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/pdfbox/filter/FlateFilter.java')
-rw-r--r--src/main/java/org/pdfbox/filter/FlateFilter.java303
1 files changed, 303 insertions, 0 deletions
diff --git a/src/main/java/org/pdfbox/filter/FlateFilter.java b/src/main/java/org/pdfbox/filter/FlateFilter.java
new file mode 100644
index 0000000..c239686
--- /dev/null
+++ b/src/main/java/org/pdfbox/filter/FlateFilter.java
@@ -0,0 +1,303 @@
+/**
+ * Copyright (c) 2003-2005, www.pdfbox.org
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of pdfbox; nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * http://www.pdfbox.org
+ *
+ */
+package org.pdfbox.filter;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.DeflaterOutputStream;
+import java.util.zip.InflaterInputStream;
+
+import org.pdfbox.cos.COSDictionary;
+
+/**
+ * This is the used for the FlateDecode filter.
+ *
+ * @author Ben Litchfield (ben@benlitchfield.com)
+ * @author Marcel Kammer
+ * @version $Revision: 1.9 $
+ */
+public class FlateFilter implements Filter
+{
+ private static final int BUFFER_SIZE = 2048;
+
+ /**
+ * This will decode some compressed data.
+ *
+ * @param compressedData
+ * The compressed byte stream.
+ * @param result
+ * The place to write the uncompressed byte stream.
+ * @param options
+ * The options to use to encode the data.
+ *
+ * @throws IOException
+ * If there is an error decompressing the stream.
+ */
+
+ public void decode(InputStream compressedData, OutputStream result, COSDictionary options) throws IOException
+ {
+ COSDictionary dict = (COSDictionary) options.getDictionaryObject("DecodeParms");
+ int predictor = -1;
+ int colors = -1;
+ int bitsPerPixel = -1;
+ int columns = -1;
+ InflaterInputStream decompressor = null;
+ ByteArrayInputStream bais = null;
+ ByteArrayOutputStream baos = null;
+ if (dict!=null)
+ {
+ predictor = dict.getInt("Predictor");
+ colors = dict.getInt("Colors");
+ bitsPerPixel = options.getInt("BitsPerComponent");
+ columns = dict.getInt("Columns");
+ }
+
+ try
+ {
+ // Decompress data to temporary ByteArrayOutputStream
+ decompressor = new InflaterInputStream(compressedData);
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int amountRead;
+
+ // Decode data using given predictor
+ if (predictor==-1 || predictor == 1 && predictor == 10)
+ {
+ // decoding not needed
+ while ((amountRead = decompressor.read(buffer, 0, BUFFER_SIZE)) != -1)
+ {
+ result.write(buffer, 0, amountRead);
+ }
+ }
+ else
+ {
+ if (colors==-1 || bitsPerPixel==-1 || columns==-1)
+ {
+ throw new IOException("Could not read all parameters to decode image");
+ }
+
+ baos = new ByteArrayOutputStream();
+ while ((amountRead = decompressor.read(buffer, 0, BUFFER_SIZE)) != -1)
+ {
+ baos.write(buffer, 0, amountRead);
+ }
+ baos.flush();
+
+ // Copy data to ByteArrayInputStream for reading
+ bais = new ByteArrayInputStream(baos.toByteArray());
+ baos.close();
+ baos = null;
+
+ byte[] decodedData = decodePredictor(predictor, colors, bitsPerPixel, columns, bais);
+ bais.close();
+ bais = new ByteArrayInputStream(decodedData);
+
+ // write decoded data to result
+ while ((amountRead = bais.read(buffer)) != -1)
+ {
+ result.write(buffer, 0, amountRead);
+ }
+ bais.close();
+ bais = null;
+ }
+
+
+ result.flush();
+ }
+ finally
+ {
+ if (decompressor != null)
+ {
+ decompressor.close();
+ }
+ if (bais != null)
+ {
+ bais.close();
+ }
+ if (baos != null)
+ {
+ baos.close();
+ }
+ }
+ }
+
+ private byte[] decodePredictor(int predictor, int colors, int bitsPerComponent, int columns, InputStream data)
+ throws IOException
+ {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte[] buffer = new byte[2048];
+
+ if (predictor == 1 || predictor == 10)
+ {
+ // No prediction or PNG NONE
+ int i = 0;
+ while ((i = data.read(buffer)) != -1)
+ {
+ baos.write(buffer, 0, i);
+ }
+ }
+ else
+ {
+ // calculate sizes
+ int bpp = (colors * bitsPerComponent + 7) / 8;
+ int rowlength = (columns * colors * bitsPerComponent + 7) / 8 + bpp;
+ byte[] actline = new byte[rowlength];
+ byte[] lastline = new byte[rowlength];// Initialize lastline with
+ // Zeros according to
+ // PNG-specification
+ boolean done = false;
+ int linepredictor = predictor;
+
+ while (!done)
+ {
+ if (predictor == 15)
+ {
+ linepredictor = data.read();// read per line predictor
+ if (linepredictor == -1)
+ {
+ done = true;// reached EOF
+ break;
+ }
+ else
+ {
+ linepredictor += 10; // add 10 to tread value 1 as 11
+ }
+ // (instead of PRED NONE) and 2
+ // as 12 (instead of PRED TIFF)
+ }
+
+ // read line
+ int i = 0;
+ int offset = bpp;
+ while (offset < rowlength && ((i = data.read(actline, offset, rowlength - offset)) != -1))
+ {
+ offset += i;
+ }
+
+ // Do prediction as specified in PNG-Specification 1.2
+ switch (linepredictor)
+ {
+ case 2:// PRED TIFF SUB
+ /**
+ * @todo decode tiff
+ */
+ throw new IOException("TIFF-Predictor not supported");
+ case 11:// PRED SUB
+ for (int p = bpp; p < rowlength; p++)
+ {
+ int sub = actline[p] & 0xff;
+ int left = actline[p - bpp] & 0xff;
+ actline[p] = (byte) (sub + left);
+ }
+ break;
+ case 12:// PRED UP
+ for (int p = bpp; p < rowlength; p++)
+ {
+ int up = actline[p] & 0xff;
+ int prior = lastline[p] & 0xff;
+ actline[p] = (byte) (up + prior);
+ }
+ break;
+ case 13:// PRED AVG
+ for (int p = bpp; p < rowlength; p++)
+ {
+ int avg = actline[p] & 0xff;
+ int left = actline[p - bpp] & 0xff;
+ int up = lastline[p] & 0xff;
+ actline[p] = (byte) (avg + ((left + up) / 2));
+ }
+ break;
+ case 14:// PRED PAETH
+ for (int p = bpp; p < rowlength; p++)
+ {
+ int paeth = actline[p] & 0xff;
+ int a = actline[p - bpp] & 0xff;// left
+ int b = lastline[p] & 0xff;// upper
+ int c = lastline[p - bpp] & 0xff;// upperleft
+ int value = a + b - c;
+ int absa = Math.abs(value - a);
+ int absb = Math.abs(value - b);
+ int absc = Math.abs(value - c);
+
+ if (absa <= absb && absa <= absc)
+ {
+ actline[p] = (byte) (paeth + absa);
+ }
+ else if (absb <= absc)
+ {
+ actline[p] += (byte) (paeth + absb);
+ }
+ else
+ {
+ actline[p] += (byte) (paeth + absc);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ lastline = actline;
+ baos.write(actline, bpp, actline.length - bpp);
+ }
+ }
+
+ return baos.toByteArray();
+ }
+
+ /**
+ * This will encode some data.
+ *
+ * @param rawData
+ * The raw data to encode.
+ * @param result
+ * The place to write to encoded results to.
+ * @param options
+ * The options to use to encode the data.
+ *
+ * @throws IOException
+ * If there is an error compressing the stream.
+ */
+ public void encode(InputStream rawData, OutputStream result, COSDictionary options) throws IOException
+ {
+ DeflaterOutputStream out = new DeflaterOutputStream(result);
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int amountRead = 0;
+ while ((amountRead = rawData.read(buffer, 0, BUFFER_SIZE)) != -1)
+ {
+ out.write(buffer, 0, amountRead);
+ }
+ out.close();
+ result.flush();
+ }
+} \ No newline at end of file