/*
* $Id$
*
* Copyright 2011 Pirion Systems Pty Ltd, 139 Warry St,
* Fortitude Valley, Queensland, Australia
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.sun.pdfview;
import java.lang.ref.SoftReference;
/**
* An entry found in a PDFFile's xref table or xref streams, identifying
* the position of an object within the structure of a PDF, and the latest
* generation number for a given object. May also indicate that
* a given object number has been freed, and that references to objects
* with that object number should be treated as references to the null
* object.
*
* @author Luke Kirby, Pirion Systems
*/
public class PDFXrefEntry {
private static final PDFXrefEntry FREED_OBJECT =
new PDFXrefEntry(Type.FREE, -1, -1, null);
static PDFXrefEntry toBodyObject(int generation, int offset) {
return new PDFXrefEntry(Type.OBJ_IN_BODY, generation, offset, null);
}
static PDFXrefEntry toStreamObject(PDFXref stream, int index) {
// stream objects will always have generation 0
return new PDFXrefEntry(Type.OBJ_IN_STREAM, 0, index, stream);
}
static PDFXrefEntry forFreedObject() {
return FREED_OBJECT;
}
public boolean resolves(PDFXref ref) {
// it's expected that the object number is relevant
return type != Type.FREE && generation == ref.getGeneration();
}
enum Type {
/**
* Identifies a deleted object. A Type 0 reference in an xref stream,
* or an 'f' entry in an xref table.
*/
FREE,
/**
* Identifies a reference to an (uncompressed) object that isn't
* part of a (compressed) stream, like one might see in pre-1.5
* PDFs. * Identifies a deleted object. A Type 1 reference in an
* xref stream or an 'n' entry in an xref table.
*/
OBJ_IN_BODY,
/**
* Identifies a reference to an object that's in a (probably compressed)
* stream. A Type 2 reference in an xref stream.
*/
OBJ_IN_STREAM;
public static Type forTypeField(int i) {
if (i > 0 && i < 3) {
return values()[i];
} else {
// if the type field is illegal then it should be treated
// as a null object, and returning a free field will
// do the job quite nicely!
return FREE;
}
}
/**
* Make an entry for a given type based on the values of field 2
* and field 3 in an xref stream
* @param field2 the value of field 2
* @param field3 the value of field 3
* @return a corresponding entry
*/
public PDFXrefEntry makeXrefStreamEntry(int field2, int field3) {
switch (this) {
case FREE:
return forFreedObject();
case OBJ_IN_BODY:
return toBodyObject(field3, field2);
case OBJ_IN_STREAM:
return toStreamObject(new PDFXref(field2, 0), field3);
default:
throw new UnsupportedOperationException(
"Unhandled xref entry type " + this);
}
}
};
/** The type of cross-ref entry*/
private Type type;
/**
* The generation number for the entry; note that there's no need for us
* to actually store the object number itself here due to the manner
* in which this object is stored, but we require the generation number
* to ensure that dereferences to previous generations of this object number
* result in the null object being returned
*/
private int generation;
/**
* The offset into the file for in-body references, or the index of
* the object within the given stream for in-stream references.
*/
private int offset;
/** The reference to the stream for in-stream references */
private PDFXref stream;
/**
* For entries that point to an object stream, we cache the index offsets
* here rather than parsing each time or
* placing another field on PDFObject
*/
private int[] objectIndexOffsets = null;
/** A cached reference of the resolved object */
private SoftReference<PDFObject> target = null;
/**
* Class constructor
* @param type the type of entry
* @param generation the generation number of the object the entry is for
* @param offset the offset of the object for in-body references, or the
* index of an object for in-stream references; ignored for freed entries
* @param stream a reference to the stream for in-stream references
*/
private PDFXrefEntry(Type type, int generation, int offset, PDFXref stream) {
this.type = type;
this.generation = generation;
this.offset = offset;
this.stream = stream;
}
/**
* @return the offset into the file for in-body references, or the index of
* the object within the given stream for in-stream references.
*/
public int getOffset() {
return offset;
}
/**
* @return the stream of the object for in-stream references;
* <code>null</code> for other entry types
*/
public PDFXref getStream() {
return stream;
}
/**
* @return the type of entry
*/
public Type getType() {
return type;
}
/**
* @return the generation number of the object this entry is for
*/
public int getGeneration() {
return generation;
}
/**
* @return any cached reference to the object that his entry refers to;
* <code>null</code> if this entry has yet to be looked at, or its
* target has been garbage collected
*/
public PDFObject getObject() {
if (target != null) {
return (PDFObject) target.get();
}
return null;
}
/**
* Cache a reference to the target object of this entry
* @param obj the object to cache
*/
public void setObject(PDFObject obj) {
this.target = new SoftReference<PDFObject>(obj);
}
/**
* @return for entries for object streams, the offsets of each object
* in the stream, arranged by index number
*/
public int[] getObjectIndexOffsets() {
return objectIndexOffsets;
}
/**
* For entries for object streams, set the offsets of each object
* in the stream, arranged by index number
* @param objectIndexOffsets the object index offsets
*/
public void setObjectIndexOffsets(int[] objectIndexOffsets) {
this.objectIndexOffsets = objectIndexOffsets;
}
}