All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jpedal.io.PdfFilteredReader Maven / Gradle / Ivy

The newest version!
/*
 * ===========================================
 * Java Pdf Extraction Decoding Access Library
 * ===========================================
 *
 * Project Info:  http://www.idrsolutions.com
 * Help section for developers at http://www.idrsolutions.com/java-pdf-library-support/
 *
 * (C) Copyright 1997-2013, IDRsolutions and Contributors.
 *
 * 	This file is part of JPedal
 *
     This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


 *
 * ---------------
 * PdfFilteredReader.java
 * ---------------
 */
package org.jpedal.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import org.jpedal.io.filter.ASCII85;
import org.jpedal.io.filter.ASCIIHex;
import org.jpedal.io.filter.CCITT;
import org.jpedal.io.filter.Flate;
import org.jpedal.io.filter.LZW;
import org.jpedal.io.filter.PdfFilter;
import org.jpedal.io.filter.RunLength;
import org.jpedal.objects.raw.PdfArrayIterator;
import org.jpedal.objects.raw.PdfDictionary;
import org.jpedal.objects.raw.PdfObject;
import org.jpedal.utils.LogWriter;

/**
 * Adds the abilty to decode streams to the PdfFileReader class
 */
public class PdfFilteredReader {

	private final static int A85 = 1116165;

	private final static int AHx = 1120328;

	private final static int ASCII85Decode = 1582784916;

	private final static int ASCIIHexDecode = 2074112677;

	final public static int CCITTFaxDecode = 2108391315;

	private final static int CCF = 1250070;

	private final static int Crypt = 1112096855;

	final public static int DCTDecode = 1180911742;

	final public static int Fl = 5692;

	final public static int FlateDecode = 2005566619;

	final public static int JBIG2Decode = 1247500931;

	final public static int JPXDecode = 1399277700;

	private final static int LZW = 1845799;

	private final static int LZWDecode = 1566984326;

	private final static int RL = 8732;

	private final static int RunLengthDecode = -1815163937;

	/** list of cached objects to delete */
	private final Map cachedObjects = new HashMap();

	private BufferedOutputStream streamCache = null;

	private BufferedInputStream bis = null;

	private boolean hasError = false;

	public byte[] decodeFilters(PdfObject[] DecodeParmsArray, byte[] data, PdfArrayIterator filters, int width, int height, String cacheName)
			throws Exception {

		this.streamCache = null;
		this.bis = null;

		final boolean debug = false;

		// get count and set global setting
		int parmsCount = DecodeParmsArray.length;
		PdfObject DecodeParms = DecodeParmsArray[0];

		byte[] globalData = null;// used by JBIG but needs to be read now so we can decode
		if (DecodeParms != null) {
			PdfObject Globals = DecodeParms.getDictionary(PdfDictionary.JBIG2Globals);
			if (Globals != null) globalData = Globals.getDecodedStream();
		}

		boolean isCached = (cacheName != null);

		int filterType, filterCount = filters.getTokenCount();

		if (debug) System.out.println("=================filterCount=" + filterCount + " DecodeParms=" + DecodeParms);

		int counter = 0;
		boolean resetDataToNull;
		// allow for no filters
		if (filterCount > 0) {

			// set each time for filter
			PdfFilter filter;
			// resetDataToNull=true;

			if (debug) System.out.println("---------filterCount=" + filterCount + " hasMore" + filters.hasMoreTokens() + " parmsCount=" + parmsCount);

			/**
			 * apply each filter in turn to data
			 */
			while (filters.hasMoreTokens()) {

				filterType = filters.getNextValueAsConstant(true);
				resetDataToNull = false;

				// pick up specific Params if set
				if (parmsCount > 1) {
					DecodeParms = DecodeParmsArray[counter];

					globalData = null;
					if (DecodeParms != null) {
						PdfObject Globals = DecodeParms.getDictionary(PdfDictionary.JBIG2Globals);
						if (Globals != null) globalData = Globals.getDecodedStream();
					}
				}

				if (debug) System.out.println("---------filter=" + getFilterName(filterType) + " DecodeParms=" + DecodeParms + ' ' + cacheName + ' '
						+ isCached);

				if (isCached && cacheName != null && (filterType == Crypt || filterType == DCTDecode || filterType == JPXDecode)) {

					counter++;
					continue;
				}

				// handle cached objects
				if (isCached && cacheName != null) setupCachedObjectForDecoding(cacheName);

				// apply decode
				if (filterType == FlateDecode || filterType == Fl) {
					filter = new Flate(DecodeParms);
				}
				else
					if (filterType == PdfFilteredReader.ASCII85Decode || filterType == PdfFilteredReader.A85) {
						filter = new ASCII85(DecodeParms);
					}
					else
						if (filterType == CCITTFaxDecode || filterType == CCF) {
							filter = new CCITT(DecodeParms, width, height);
						}
						else
							if (filterType == LZWDecode || filterType == LZW) {
								filter = new LZW(DecodeParms, width, height);
								resetDataToNull = true;

							}
							else
								if (filterType == RunLengthDecode || filterType == RL) {
									filter = new RunLength(DecodeParms);
								}
								else
									if (filterType == JBIG2Decode) {

										// To work we need to add data as an input to JSD to save the resulting data.
										// We do not write back to stream.

										if (data == null) { // hack to read into byte[] and spool back for cache

											data = new byte[this.bis.available()];
											this.bis.read(data);

											int ptr = -1;
											// resize as JBIG fussy
											for (int ii = data.length - 1; ii > 9; ii--) {
												if (data[ii] == 'e' && data[ii + 1] == 'n' && data[ii + 2] == 'd' && data[ii + 3] == 's'
														&& data[ii + 4] == 't' && data[ii + 5] == 'r' && data[ii + 6] == 'e' && data[ii + 7] == 'a'
														&& data[ii + 8] == 'm') {
													ptr = ii - 1;
													ii = -1;
												}
											}

											if (ptr != -1) {

												if (data[ptr] == 10 && data[ptr - 1] == 13) ptr--;

												byte[] tmp = data;
												data = new byte[ptr];
												System.arraycopy(tmp, 0, data, 0, ptr);
											}

											data = JBIG2.JBIGDecode(data, globalData);

											this.streamCache.write(data);
											data = null;

										}
										else {
											data = JBIG2.JBIGDecode(data, globalData);
										}

										filter = null;

									}
									else
										if (filterType == ASCIIHexDecode || filterType == AHx) {
											filter = new ASCIIHex(DecodeParms);
										}
										else
											if (filterType == Crypt) { // just pass though
												filter = null;
											}
											else {
												filter = null; // handled elsewhere
											}

				if (filter != null) {
					try {
						if (data != null) data = filter.decode(data);
						else
							if (this.bis != null) filter.decode(this.bis, this.streamCache, cacheName, this.cachedObjects);

						// flag we may have issues in stream
						if (!this.hasError && filter.hasError()) this.hasError = true;

					}
					catch (Exception ee) {

						if (LogWriter.isOutput()) LogWriter.writeLog("Exception " + ee + " in " + getFilterName(filterType) + " decompression");

						if (resetDataToNull) data = null;
					}
				}

				if (isCached) {
					if (this.bis != null) this.bis.close();

					if (this.streamCache != null) {
						this.streamCache.flush();
						this.streamCache.close();
					}
				}

				counter++;
			}
		}

		return data;
	}

	private static String getFilterName(int filterType) {
		switch (filterType) {

			case A85:
				return "A85";

			case AHx:
				return "AHx";

			case ASCII85Decode:
				return "ASCII85Decode";

			case ASCIIHexDecode:
				return "ASCIIHexDecode";

			case CCITTFaxDecode:
				return "CCITTFaxDecode";

			case CCF:
				return "CCF";

			case Crypt:
				return "Crypt";

			case DCTDecode:
				return "DCTDecode";

			case Fl:
				return "Fl";

			case FlateDecode:
				return "FlateDecode";

			case JBIG2Decode:
				return "JBIG2Decode";

			case JPXDecode:
				return "";

			case LZW:
				return "";

			case LZWDecode:
				return "";

			case RL:
				return "";

			case RunLengthDecode:
				return "";

			default:
				return "Unknown";
		}
	}

	private void setupCachedObjectForDecoding(String cacheName) throws IOException {
		// rename file
		File tempFile2 = File.createTempFile("jpedal", ".raw", new File(ObjectStore.temp_dir));
		this.cachedObjects.put(tempFile2.getAbsolutePath(), "x"); // store to
																	// delete when
																	// PDF closed
		ObjectStore.copy(cacheName, tempFile2.getAbsolutePath());
		File rawFile = new File(cacheName);
		rawFile.delete();

		// where its going after decompression
		this.streamCache = new BufferedOutputStream(new FileOutputStream(cacheName));

		// where data is coming from
		this.bis = new BufferedInputStream(new FileInputStream(tempFile2));
	}

	public boolean hasError() {
		return this.hasError;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy