org.jpedal.objects.raw.PdfObject 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
*
* ---------------
* PdfObject.java
* ---------------
*/
package org.jpedal.objects.raw;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.jpedal.fonts.StandardFonts;
import org.jpedal.io.ObjectStore;
import org.jpedal.io.PdfFileReader;
import org.jpedal.utils.LogWriter;
import org.jpedal.utils.StringUtils;
/**
* holds actual data for PDF file to process
*/
public class PdfObject implements Cloneable {
protected boolean isIndexed, maybeIndirect = false, isFullyResolved = true, isDataExternal = false;
private boolean streamMayBeCorrupt;
/**
* states
*/
public static final int DECODED = 0;
public static final int UNDECODED_REF = 1;
public static final int UNDECODED_DIRECT = 2;
private int status = DECODED;
byte[] unresolvedData = null;
// hold Other dictionary values
Map otherValues = new HashMap();
protected int pageNumber = -1;
int PDFkeyInt = -1;
// our type which may not be same as /Type
int objType = PdfDictionary.Unknown;
// key of object
private int id = -1;
protected int colorspace = PdfDictionary.Unknown, subtype = PdfDictionary.Unknown, type = PdfDictionary.Unknown;
private int BitsPerComponent = -1, BitsPerCoordinate = -1, BitsPerFlag = -1, Count = 0, FormType = -1, Length = -1, Length1 = -1, Length2 = -1,
Length3 = -1, Rotate = -1; // -1 shows unset
private float[] ArtBox, BBox, BleedBox, CropBox, Decode, Domain, Matrix, MediaBox, Range, TrimBox;
protected PdfObject ColorSpace = null, DecodeParms = null, Encoding = null, Function = null, Resources = null, Shading = null, SMask = null;
private boolean ignoreRecursion = false, ignoreStream = false;
// used by font code
protected boolean isZapfDingbats = false, isSymbol = false;
private boolean isCompressedStream = false;
protected int generalType = PdfDictionary.Unknown; // some Dictionaries can be a general type (ie /ToUnicode /Identity-H)
private String generalTypeAsString = null; // some values (ie CMAP can have unknown settings)
// flag to show if we want parents (generally NO as will scan all up tree every time to root)
protected boolean includeParent = false;
private String Creator = null, Parent = null, Name = null, S = null, Title = null;
private byte[] rawCreator, rawParent, rawName = null, rawS, rawTitle = null;
public static boolean debug = false;
protected String ref = null;
int intRef, gen;
protected boolean hasStream = false;
public byte[] stream = null;
private byte[] DecodedStream = null;
// use for caching
private long startStreamOnDisk = -1;
private PdfFileReader objReader = null;
private String cacheName = null;
private byte[][] Filter = null, TR = null;
private byte[][] keys;
private byte[][] values;
private Object[] DecodeParmsAsArray;
private PdfObject[] objs;
// used by /Other
protected Object currentKey = null;
// used to track AP
protected int parentType = -1;
private boolean isInCompressedStream;
/** used to give the number of a new XFA reference ie. 1 0 X (XFA internal form) */
private static int newXFAFormID = 1;
/** set this PdfObject up as an internal object and define its reference */
protected void setInternalReference() {
// if this is an internal object generate the next key
this.ref = (newXFAFormID++) + " 0 X";
}
protected PdfObject() {
}
public PdfObject(byte[] bytes) {}
public PdfObject(int intRef, int gen) {
setRef(intRef, gen);
}
public void setRef(int intRef, int gen) {
this.intRef = intRef;
this.gen = gen;
// force reset as may have changed
this.ref = null;
}
/**
*
* @return name of file with cached data on disk or null
*/
public String getCacheName(PdfFileReader objReader) {
if (isCached()) {
this.cacheName = null;
this.getCachedStreamFile(objReader);
}
return this.cacheName;
}
public void setRef(String ref) {
this.ref = ref;
}
public PdfObject(String ref) {
this.ref = ref;
// int ptr=ref.indexOf(" ");
// if(ptr>0)
// intRef=PdfReader.parseInt(0, ptr, StringUtils.toBytes(ref));
}
public PdfObject(int type) {
this.generalType = type;
}
protected static boolean[] deepCopy(boolean[] input) {
if (input == null) return null;
int count = input.length;
boolean[] deepCopy = new boolean[count];
System.arraycopy(input, 0, deepCopy, 0, count);
return deepCopy;
}
public int getStatus() {
return this.status;
}
public byte[] getUnresolvedData() {
return this.unresolvedData;
}
public int getPDFkeyInt() {
return this.PDFkeyInt;
}
public void setUnresolvedData(byte[] unresolvedData, int PDFkeyInt) {
this.unresolvedData = unresolvedData;
this.PDFkeyInt = PDFkeyInt;
/**
* int len=unresolvedData.length;
*
* //if ref get first value as int if(unresolvedData[len-1]=='R'){
*
* int ptr=0, ii=0; while(ii0) intRef=PdfReader.parseInt(0, ptr, unresolvedData);
*
* }/
**/
}
public void setStatus(int status) {
this.status = status;
this.unresolvedData = null;
}
protected static float[] deepCopy(float[] input) {
if (input == null) return null;
int count = input.length;
float[] deepCopy = new float[count];
System.arraycopy(input, 0, deepCopy, 0, count);
return deepCopy;
}
protected static double[] deepCopy(double[] input) {
if (input == null) return null;
int count = input.length;
double[] deepCopy = new double[count];
System.arraycopy(input, 0, deepCopy, 0, count);
return deepCopy;
}
protected static int[] deepCopy(int[] input) {
if (input == null) return null;
int count = input.length;
int[] deepCopy = new int[count];
System.arraycopy(input, 0, deepCopy, 0, count);
return deepCopy;
}
protected static byte[][] deepCopy(byte[][] input) {
if (input == null) return null;
int count = input.length;
byte[][] deepCopy = new byte[count][];
System.arraycopy(input, 0, deepCopy, 0, count);
return deepCopy;
}
public PdfObject getDictionary(int id) {
switch (id) {
case PdfDictionary.ColorSpace:
return this.ColorSpace;
case PdfDictionary.DecodeParms:
return this.DecodeParms;
case PdfDictionary.Function:
return this.Function;
case PdfDictionary.Resources:
return this.Resources;
case PdfDictionary.Shading:
return this.Shading;
case PdfDictionary.SMask:
return this.SMask;
default:
return null;
}
}
public int getGeneralType(int id) {
// special case
if (id == PdfDictionary.Encoding && this.isZapfDingbats) // note this is Enc object so local
return StandardFonts.ZAPF;
else
if (id == PdfDictionary.Encoding && this.isSymbol) // note this is Enc object so local
return StandardFonts.SYMBOL;
else
if (id == PdfDictionary.Type) return this.objType;
else return this.generalType;
}
public String getGeneralStringValue() {
return this.generalTypeAsString;
}
public void setGeneralStringValue(String generalTypeAsString) {
this.generalTypeAsString = generalTypeAsString;
}
public void setIntNumber(int id, int value) {
switch (id) {
case PdfDictionary.BitsPerComponent:
this.BitsPerComponent = value;
break;
case PdfDictionary.BitsPerCoordinate:
this.BitsPerCoordinate = value;
break;
case PdfDictionary.BitsPerFlag:
this.BitsPerFlag = value;
break;
case PdfDictionary.Count:
this.Count = value;
break;
case PdfDictionary.FormType:
this.FormType = value;
break;
case PdfDictionary.Length:
this.Length = value;
break;
case PdfDictionary.Length1:
this.Length1 = value;
break;
case PdfDictionary.Length2:
this.Length2 = value;
break;
case PdfDictionary.Length3:
this.Length3 = value;
break;
case PdfDictionary.Rotate:
this.Rotate = value;
break;
default:
}
}
public void setFloatNumber(int id, float value) {
switch (id) {
// case PdfDictionary.BitsPerComponent:
// BitsPerComponent=value;
// break;
default:
}
}
public int getInt(int id) {
switch (id) {
case PdfDictionary.BitsPerComponent:
return this.BitsPerComponent;
case PdfDictionary.BitsPerCoordinate:
return this.BitsPerCoordinate;
case PdfDictionary.BitsPerFlag:
return this.BitsPerFlag;
case PdfDictionary.Count:
return this.Count;
case PdfDictionary.FormType:
return this.FormType;
case PdfDictionary.Length:
return this.Length;
case PdfDictionary.Length1:
return this.Length1;
case PdfDictionary.Length2:
return this.Length2;
case PdfDictionary.Length3:
return this.Length3;
case PdfDictionary.Rotate:
return this.Rotate;
default:
return PdfDictionary.Unknown;
}
}
public float getFloatNumber(int id) {
switch (id) {
// case PdfDictionary.BitsPerComponent:
// return BitsPerComponent;
default:
return PdfDictionary.Unknown;
}
}
public boolean getBoolean(int id) {
switch (id) {
default:
}
return false;
}
public void setBoolean(int id, boolean value) {
switch (id) {
default:
}
}
public void setDictionary(int id, PdfObject value) {
value.id = id;
switch (id) {
case PdfDictionary.ColorSpace:
this.ColorSpace = value;
break;
case PdfDictionary.DecodeParms:
this.DecodeParms = value;
break;
case PdfDictionary.Function:
this.Function = value;
break;
case PdfDictionary.Resources:
this.Resources = value;
break;
case PdfDictionary.Shading:
this.Shading = value;
break;
case PdfDictionary.SMask:
this.SMask = value;
break;
default:
setOtherValues(id, value);
}
}
/**
* some values stored in a MAP for AP or Structurede Content
*/
protected void setOtherValues(int id, PdfObject value) {
if (this.objType == PdfDictionary.Form || this.objType == PdfDictionary.MCID || this.currentKey != null) {
// if(1==1)
// throw new RuntimeException("xx="+currentKey+" id="+id);
this.otherValues.put(this.currentKey, value);
this.currentKey = null;
}
}
public void setID(int id) {
this.id = id;
}
public int getID() {
return this.id;
}
/**
* only used internally for some forms - please do not use
*
*/
public int getParentID() {
return this.parentType;
}
public void setParentID(int parentType) {
this.parentType = parentType;
}
/**
* flag set for embedded data
*/
public boolean hasStream() {
return this.hasStream;
}
// public int setConstant(int pdfKeyType, int keyStart, int keyLength, byte[] raw) {
//
//
// return PdfDictionary.Unknown;
// }
public int setConstant(int pdfKeyType, int keyStart, int keyLength, byte[] raw) {
int PDFvalue = PdfDictionary.Unknown;
int id = 0, x = 0, next;
try {
// convert token to unique key which we can lookup
for (int i2 = keyLength - 1; i2 > -1; i2--) {
next = raw[keyStart + i2];
next = next - 48;
id = id + ((next) << x);
x = x + 8;
}
switch (id) {
// case PdfDictionary.Image:
// PDFvalue =PdfDictionary.Image;
// break;
//
// case PdfDictionary.Form:
// PDFvalue =PdfDictionary.Form;
// break;
default:
// if(pdfKeyType==PdfDictionary.Encoding){
// PDFvalue=PdfCIDEncodings.getConstant(id);
//
// if(PDFvalue==PdfDictionary.Unknown){
//
// byte[] bytes=new byte[keyLength];
//
// System.arraycopy(raw,keyStart,bytes,0,keyLength);
//
// unknownValue=new String(bytes);
// }
//
// if(debug && PDFvalue==PdfDictionary.Unknown){
// System.out.println("Value not in PdfCIDEncodings");
//
// byte[] bytes=new byte[keyLength];
//
// System.arraycopy(raw,keyStart,bytes,0,keyLength);
// System.out.println("Add to CIDEncodings and as String");
// System.out.println("key="+new String(bytes)+" "+id+" not implemented in setConstant in PdfFont Object");
//
// System.out.println("final public static int CMAP_"+new String(bytes)+"="+id+";");
// }
// }else
if (PDFvalue == -1) {
if (debug) {
byte[] bytes = new byte[keyLength];
System.arraycopy(raw, keyStart, bytes, 0, keyLength);
System.out.println("key=" + new String(bytes) + ' ' + id + " not implemented in setConstant in " + this);
System.out.println("final public static int " + new String(bytes) + '=' + id + ';');
}
}
break;
}
}
catch (Exception e) {
// tell user and log
if (LogWriter.isOutput()) LogWriter.writeLog("Exception: " + e.getMessage());
}
return id;
}
public int getParameterConstant(int key) {
int def = PdfDictionary.Unknown;
switch (key) {
case PdfDictionary.ColorSpace:
return this.colorspace;
case PdfDictionary.Subtype:
return this.subtype;
case PdfDictionary.Type:
return this.type;
}
return def;
}
/**
* common values shared between types
*/
public int setConstant(int pdfKeyType, int id) {
int PDFvalue = id;
/**
* map non-standard
*/
switch (id) {
case PdfDictionary.FontDescriptor:
PDFvalue = PdfDictionary.Font;
break;
}
switch (pdfKeyType) {
case PdfDictionary.ColorSpace:
this.colorspace = PDFvalue;
break;
case PdfDictionary.Subtype:
this.subtype = PDFvalue;
break;
case PdfDictionary.Type:
// @speed if is temp hack as picks up types on some subobjects
// if(type==PdfDictionary.Unknown)
this.type = PDFvalue;
break;
}
return PDFvalue;
}
public float[] getFloatArray(int id) {
float[] array = null;
switch (id) {
case PdfDictionary.ArtBox:
return deepCopy(this.ArtBox);
case PdfDictionary.BBox:
return deepCopy(this.BBox);
case PdfDictionary.BleedBox:
return deepCopy(this.BleedBox);
case PdfDictionary.CropBox:
return deepCopy(this.CropBox);
case PdfDictionary.Decode:
return deepCopy(this.Decode);
case PdfDictionary.Domain:
return deepCopy(this.Domain);
case PdfDictionary.Matrix:
return deepCopy(this.Matrix);
case PdfDictionary.MediaBox:
return deepCopy(this.MediaBox);
case PdfDictionary.Range:
return deepCopy(this.Range);
case PdfDictionary.TrimBox:
return deepCopy(this.TrimBox);
default:
}
return deepCopy(array);
}
public byte[][] getKeyArray(int id) {
switch (id) {
default:
}
return null;
}
public double[] getDoubleArray(int id) {
double[] array = null;
switch (id) {
default:
}
return deepCopy(array);
}
public boolean[] getBooleanArray(int id) {
boolean[] array = null;
switch (id) {
default:
}
return deepCopy(array);
}
public int[] getIntArray(int id) {
int[] array = null;
switch (id) {
default:
}
return deepCopy(array);
}
public void setFloatArray(int id, float[] value) {
switch (id) {
case PdfDictionary.ArtBox:
this.ArtBox = value;
break;
case PdfDictionary.BBox:
this.BBox = value;
break;
case PdfDictionary.BleedBox:
this.BleedBox = value;
break;
case PdfDictionary.CropBox:
this.CropBox = value;
break;
case PdfDictionary.Decode:
this.Decode = ignoreIdentity(value);
break;
case PdfDictionary.Domain:
this.Domain = value;
break;
case PdfDictionary.Matrix:
this.Matrix = value;
break;
case PdfDictionary.MediaBox:
this.MediaBox = value;
break;
case PdfDictionary.Range:
this.Range = value;
break;
case PdfDictionary.TrimBox:
this.TrimBox = value;
break;
default:
}
}
/** ignore identity value which makes no change */
private static float[] ignoreIdentity(float[] value) {
boolean isIdentity = true;
if (value != null) {
int count = value.length;
for (int aa = 0; aa < count; aa = aa + 2) {
if (value[aa] == 0f && value[aa + 1] == 1f) {
// okay
}
else {
isIdentity = false;
aa = count;
}
}
}
if (isIdentity) return null;
else return value;
}
public void setIntArray(int id, int[] value) {
switch (id) {
default:
}
}
public void setBooleanArray(int id, boolean[] value) {
switch (id) {
default:
}
}
public void setDoubleArray(int id, double[] value) {
switch (id) {
default:
}
}
public void setMixedArray(int id, byte[][] value) {
switch (id) {
case PdfDictionary.Filter:
this.Filter = value;
break;
default:
}
}
public String getStringValue(int id, int mode) {
byte[] data = null;
// get data
switch (id) {
case PdfDictionary.Name:
data = this.rawName;
break;
}
// convert
switch (mode) {
case PdfDictionary.STANDARD:
// setup first time
if (data != null) return new String(data);
else return null;
case PdfDictionary.LOWERCASE:
// setup first time
if (data != null) return new String(data);
else return null;
case PdfDictionary.REMOVEPOSTSCRIPTPREFIX:
// setup first time
if (data != null) {
int len = data.length;
if (len > 6 && data[6] == '+') { // lose ABCDEF+ if present
int length = len - 7;
byte[] newData = new byte[length];
System.arraycopy(data, 7, newData, 0, length);
return new String(newData);
}
else return new String(data);
}
else return null;
default:
throw new RuntimeException("Value not defined in getName(int,mode)");
}
}
// return as constant we can check
public int getNameAsConstant(int id) {
// return PdfDictionary.generateChecksum(0,raw.length,raw);
return PdfDictionary.Unknown;
}
public String getName(int id) {
String str = null;
switch (id) {
case PdfDictionary.Name:
// setup first time
if (this.Name == null && this.rawName != null) this.Name = new String(this.rawName);
return this.Name;
// case PdfDictionary.Parent:
//
// //setup first time
// if(Filter==null && rawParent!=null)
// Parent=new String(rawParent);
//
// return Parent;
case PdfDictionary.S:
// setup first time
if (this.S == null && this.rawS != null) this.S = new String(this.rawS);
return this.S;
default:
}
return str;
}
public String getStringKey(int id) {
String str = null;
switch (id) {
case PdfDictionary.Parent:
// setup first time
if (this.Parent == null && this.rawParent != null) this.Parent = new String(this.rawParent);
return this.Parent;
default:
}
return str;
}
public String getTextStreamValue(int id) {
String str = null;
switch (id) {
case PdfDictionary.Creator:
// setup first time
if (this.Creator == null && this.rawCreator != null) this.Creator = StringUtils.getTextString(this.rawCreator, false);
return this.Creator;
// can also be stream in OCProperties
case PdfDictionary.Name:
// setup first time
if (this.Name == null && this.rawName != null) this.Name = StringUtils.getTextString(this.rawName, false);
return this.Name;
case PdfDictionary.Title:
// setup first time
if (this.Title == null && this.rawTitle != null) this.Title = StringUtils.getTextString(this.rawTitle, false);
return this.Title;
// case PdfDictionary.Filter:
//
// //setup first time
// if(Filter==null && rawFilter!=null)
// Filter=PdfReader.getTextString(rawFilter);
//
// return Filter;
default:
}
return str;
}
public void setName(int id, byte[] value) {
switch (id) {
case PdfDictionary.Name:
this.rawName = value;
break;
case PdfDictionary.S:
this.rawS = value;
break;
case PdfDictionary.Parent:
// gets into endless loop if any obj so use sparingly
if (this.includeParent) this.rawParent = value;
break;
default:
if (this.objType == PdfDictionary.MCID) {
// if(1==1)
// throw new RuntimeException("xx="+currentKey+" id="+id);
this.otherValues.put(this.currentKey, value);
// System.out.println("id="+id+" "+value+" "+type+" "+objType+" "+this+" "+otherValues);
}
else {
}
}
}
public void setName(Object id, String value) {
this.otherValues.put(id, value);
// System.out.println("id="+id+" "+value+" "+type+" "+objType+" "+this+" "+otherValues);
}
public void setStringKey(int id, byte[] value) {
switch (id) {
case PdfDictionary.Parent:
this.rawParent = value;
break;
default:
}
}
public void setTextStreamValue(int id, byte[] value) {
switch (id) {
case PdfDictionary.Creator:
this.rawCreator = value;
break;
case PdfDictionary.Name:
this.rawName = value;
break;
case PdfDictionary.Title:
this.rawTitle = value;
break;
default:
}
}
public byte[] getDecodedStream() {
if (isCached()) {
byte[] cached = null;
try {
File f = new File(getCachedStreamFile(this.objReader));
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));
cached = new byte[(int) f.length()];
// System.out.println(cached.length+" "+DecodedStream.length);
bis.read(cached);
bis.close();
// System.out.println(new String(cached));
}
catch (Exception e) {
// tell user and log
if (LogWriter.isOutput()) LogWriter.writeLog("Exception: " + e.getMessage());
}
return cached;
}
else
return this.DecodedStream;
}
/**
* public byte[] getStream() {
*
* if(DecodedStream==null) return null;
*
* //make a a DEEP copy so we cant alter int len=DecodedStream.length; byte[] copy=new byte[len]; System.arraycopy(DecodedStream, 0, copy, 0,
* len);
*
* return copy; }/
**/
public void setStream(byte[] stream) {
this.stream = stream;
if (this.getObjectType() == PdfDictionary.ColorSpace) this.hasStream = true;
}
public void setDecodedStream(byte[] stream) {
this.DecodedStream = stream;
}
public String getObjectRefAsString() {
if (this.ref == null) this.ref = this.intRef + " " + this.gen + " R";
return this.ref;
}
public int getObjectRefID() {
// initialise if not set
if (this.intRef == 0 && this.ref != null && !this.ref.contains("[")) {
int ptr = this.ref.indexOf(' ');
// System.out.println(ref.substring(0,ptr)+"< 0) {
try {
this.intRef = Integer.parseInt(this.ref.substring(0, ptr));
}
catch (Exception e) {
// tell user and log
if (LogWriter.isOutput()) LogWriter.writeLog("Exception: " + e.getMessage());
}
int ptr2 = this.ref.indexOf('R', ptr);
if (ptr2 > 0) {
// System.out.println(ref.substring(ptr+1,ptr2-1)+"<