001: /*
002: Copyright © 2006 Stefano Chizzolini. http://clown.stefanochizzolini.it
003:
004: Contributors:
005: * Stefano Chizzolini (original code developer, http://www.stefanochizzolini.it):
006: contributed code is Copyright © 2006 by Stefano Chizzolini.
007:
008: This file should be part of the source code distribution of "PDF Clown library"
009: (the Program): see the accompanying README files for more info.
010:
011: This Program is free software; you can redistribute it and/or modify it under
012: the terms of the GNU General Public License as published by the Free Software
013: Foundation; either version 2 of the License, or (at your option) any later version.
014:
015: This Program is distributed in the hope that it will be useful, but WITHOUT ANY
016: WARRANTY, either expressed or implied; without even the implied warranty of
017: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more details.
018:
019: You should have received a copy of the GNU General Public License along with this
020: Program (see README files); if not, go to the GNU website (http://www.gnu.org/).
021:
022: Redistribution and use, with or without modification, are permitted provided that such
023: redistributions retain the above copyright notice, license and disclaimer, along with
024: this list of conditions.
025: */
026:
027: package it.stefanochizzolini.clown.objects;
028:
029: import it.stefanochizzolini.clown.bytes.IOutputStream;
030: import it.stefanochizzolini.clown.files.File;
031: import it.stefanochizzolini.clown.tokens.Parser;
032: import it.stefanochizzolini.clown.tokens.XRefEntry;
033: import it.stefanochizzolini.clown.tokens.XRefEntryUsageEnum;
034:
035: /**
036: PDF indirect object.
037: */
038: public class PdfIndirectObject extends PdfObject implements
039: IPdfIndirectObject {
040: // <class>
041: // <static>
042: // <fields>
043: private final String UsageFree = "f";
044: private final String UsageInUse = "n";
045: // </fields>
046: // </static>
047:
048: // <dynamic>
049: // <fields>
050: private PdfDataObject dataObject;
051: private File file;
052: private boolean original;
053: private PdfReference reference;
054: private XRefEntry xrefEntry;
055:
056: // </fields>
057:
058: // <constructors>
059: /**
060: <h3>Remarks</h3>
061: <p>For internal use only.</p>
062: @param file Associated file.
063: @param dataObject Data object associated to the indirect object.
064: It MUST be null if the indirect object is original (i.e. coming from an existing file) or
065: free.
066: It MUST be NOT null if the indirect object is new and in-use.
067: @param xrefEntry Cross-reference entry associated to the indirect object.
068: If the indirect object is new, its offset field MUST be set to 0 (zero).
069: */
070: public PdfIndirectObject(File file, PdfDataObject dataObject,
071: XRefEntry xrefEntry) {
072: this .file = file;
073: this .dataObject = dataObject;
074: this .xrefEntry = xrefEntry;
075:
076: this .original = (xrefEntry.getOffset() != 0);
077: this .reference = new PdfReference(this , xrefEntry.getNumber(),
078: xrefEntry.getGeneration());
079: }
080:
081: // </constructors>
082:
083: // <interface>
084: // <public>
085: public File getFile() {
086: return file;
087: }
088:
089: @Override
090: public int hashCode() {
091: /*
092: NOTE: Uniqueness should be achieved XORring the (local) reference hashcode
093: with the (global) file hashcode.
094: NOTE: DO NOT directly invoke reference.hashCode() method here as
095: it would trigger an infinite loop, as it conversely relies on this method.
096: */
097: return reference.getID().hashCode() ^ file.hashCode();
098: }
099:
100: public boolean isInUse() {
101: return (xrefEntry.getUsage() == XRefEntryUsageEnum.InUse);
102: }
103:
104: public boolean isOriginal() {
105: return original;
106: }
107:
108: public void update() {
109: if (original) {
110: /*
111: NOTE: It's expected that dropOriginal() is invoked by IndirectObjects set() method;
112: such an action is delegated because clients may invoke directly set() method, skipping
113: this method.
114: */
115: file.getIndirectObjects().update(this );
116: }
117: }
118:
119: // <IPdfIndirectObject>
120: @Override
121: public Object clone(File context) {
122: return context.getIndirectObjects().addExternal(this );
123: }
124:
125: public void delete() {
126: if (file != null) {
127: /*
128: NOTE: It's expected that dropFile() is invoked by IndirectObjects remove() method;
129: such an action is delegated because clients may invoke directly remove() method, skipping
130: this method.
131: */
132: file.getIndirectObjects().remove(xrefEntry.getNumber());
133: }
134: }
135:
136: public PdfDataObject getDataObject() {
137: if (dataObject == null) {
138: /*
139: NOTE: indirect data object is null in 2 cases:
140: 1) when the entry is free (no data object at all);
141: 2) when the indirect object hasn't been initialized yet
142: because it comes from a parsed reference (late-bound data object).
143: In case 1 data object MUST keep itself null,
144: while in case 2 data object MUST be initialized.
145: */
146:
147: // Is the entry free [case 1]?
148: // NOTE: Free entries have NO indirect data object associated with.
149: if (xrefEntry.getUsage() == XRefEntryUsageEnum.Free)
150: return null;
151:
152: // In-use entry (late-bound data object [case 2]).
153: try {
154: Parser parser = file.getReader().getParser();
155: // Retrieve the associated data object among the original objects!
156: parser.seek(xrefEntry.getOffset());
157: // Skip indirect-object header!
158: parser.moveNext(4);
159: // Get the indirect data object!
160: dataObject = parser.parsePdfObject();
161: } catch (Exception e) {
162: throw new RuntimeException(e);
163: }
164: }
165:
166: return dataObject;
167: }
168:
169: public PdfIndirectObject getIndirectObject() {
170: return this ;
171: }
172:
173: public PdfReference getReference() {
174: return reference;
175: }
176:
177: // </IPdfIndirectObject>
178: // </public>
179:
180: // <internal>
181: /**
182: <h3>Remarks</h3>
183: <p>For internal use only.</p>
184: */
185: public void dropFile() {
186: file = null;
187: }
188:
189: /**
190: <h3>Remarks</h3>
191: <p>For internal use only.</p>
192: */
193: public void dropOriginal() {
194: original = false;
195: }
196:
197: String getUsage() {
198: switch (xrefEntry.getUsage()) {
199: case Free:
200: return UsageFree;
201: case InUse:
202: return UsageInUse;
203: default: // Should NEVER happen.
204: throw new RuntimeException("Invalid xref usage value.");
205: }
206: }
207:
208: /**
209: <h3>Remarks</h3>
210: <p>For internal use only.</p>
211: */
212: @Override
213: public int writeTo(IOutputStream stream) {
214: // Header.
215: int size = stream.write(reference.getID() + " obj\n");
216:
217: // Body.
218: size += getDataObject().writeTo(stream);
219:
220: // Tail.
221: size += stream.write("\nendobj\n");
222:
223: return size;
224: }
225: // </internal>
226: // </interface>
227: // </dynamic>
228: // </class>
229: }
|