001: /*
002: * Copyright (c) 2007, intarsys consulting GmbH
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * - Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * - Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * - Neither the name of intarsys nor the names of its contributors may be used
015: * to endorse or promote products derived from this software without specific
016: * prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
022: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
023: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
024: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
025: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
026: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
027: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
028: * POSSIBILITY OF SUCH DAMAGE.
029: */
030: package de.intarsys.pdf.st;
031:
032: import java.io.IOException;
033: import java.util.Iterator;
034: import java.util.NoSuchElementException;
035:
036: import de.intarsys.pdf.cos.COSArray;
037: import de.intarsys.pdf.cos.COSDictionary;
038: import de.intarsys.pdf.cos.COSDocument;
039: import de.intarsys.pdf.cos.COSInteger;
040: import de.intarsys.pdf.cos.COSName;
041: import de.intarsys.pdf.cos.COSObject;
042: import de.intarsys.pdf.cos.COSObjectKey;
043: import de.intarsys.pdf.cos.COSTrailer;
044: import de.intarsys.pdf.crypt.ISystemSecurityHandler;
045: import de.intarsys.pdf.parser.COSLoadException;
046: import de.intarsys.pdf.writer.COSWriter;
047:
048: /**
049: * A section in a XRef.
050: * <p>
051: * The XRef allows random access to the objects in the PDF file.
052: * <p>
053: * A XRef section is the part of a pdf document starting with a "xref" token. It
054: * consists of several, non contiguous subsections, one additional for each
055: * incremental update.
056: */
057: public abstract class STXRefSection {
058: public static final COSName DK_XRefStm = COSName
059: .constant("XRefStm"); //$NON-NLS-1$
060:
061: private STXRefSubsection xRefSubsection;
062:
063: private long offset = -1;
064:
065: private STDocument doc;
066:
067: private STXRefSection previous;
068:
069: protected STXRefSection(STDocument doc) {
070: this (doc, -1);
071: }
072:
073: protected STXRefSection(STDocument doc, long offset) {
074: this .doc = doc;
075: this .offset = offset;
076: xRefSubsection = new STXRefSubsection(this , 0);
077: xRefSubsection.addEntry(new STXRefEntryFree(new COSObjectKey(0,
078: 65535), 0));
079: }
080:
081: public void addEntry(STXRefEntry entry) {
082: STXRefSubsection prev = null;
083: STXRefSubsection current = getXRefSubsection();
084: int number = entry.getObjectNumber();
085: while (current != null) {
086: if (number < current.getStart()) {
087: STXRefSubsection newSection = new STXRefSubsection(
088: this , number);
089: newSection.setNext(current);
090: if (prev != null) {
091: prev.setNext(newSection);
092: } else {
093: setXRefSubsection(newSection);
094: }
095: newSection.addEntry(entry);
096: return;
097: }
098: if (current.getStart() <= number
099: && number <= current.getStop()) {
100: current.addEntry(entry);
101: return;
102: }
103: prev = current;
104: current = current.getNext();
105: }
106: STXRefSubsection newSection = new STXRefSubsection(this , number);
107: newSection.setNext(null);
108: if (prev != null) {
109: prev.setNext(newSection);
110: } else {
111: setXRefSubsection(newSection);
112: }
113: newSection.addEntry(entry);
114: }
115:
116: public boolean contains(int number) {
117: STXRefSubsection current = getXRefSubsection();
118: while (current != null) {
119: if (number < current.getStart()) {
120: return false;
121: }
122: if (current.getStart() <= number
123: && number < current.getStop()) {
124: return true;
125: }
126: current = current.getNext();
127: }
128: return false;
129: }
130:
131: /**
132: * The "trailer" dictionary associated with the XRef section.
133: *
134: * @return The "trailer" dictionary associated with the XRef section.
135: */
136: public abstract COSDictionary cosGetDict();
137:
138: public COSObject cosGetEncryption() {
139: return cosGetDict().get(COSTrailer.DK_Encrypt);
140: }
141:
142: abstract public COSObject cosGetObject();
143:
144: public void cosSetEncryption(COSObject encryption) {
145: cosGetDict().put(COSTrailer.DK_Encrypt, encryption);
146: }
147:
148: protected void createNewSubsection(int pStart) {
149: }
150:
151: public abstract STXRefSection createSuccessor();
152:
153: public Iterator entryIterator() {
154: return new Iterator() {
155: private Iterator currentIterator;
156:
157: private STXRefSubsection myNext = getXRefSubsection();
158:
159: public boolean hasNext() {
160: if (currentIterator != null
161: && currentIterator.hasNext()) {
162: return true;
163: }
164: if (myNext != null) {
165: currentIterator = myNext.getEntries().iterator();
166: myNext = myNext.getNext();
167: // must enter recursive!
168: return hasNext();
169: }
170: return false;
171: }
172:
173: public Object next() {
174: if (hasNext()) {
175: return currentIterator.next();
176: }
177: throw new NoSuchElementException();
178: }
179:
180: public void remove() {
181: throw new UnsupportedOperationException();
182: }
183: };
184: }
185:
186: public STDocument getDoc() {
187: return doc;
188: }
189:
190: public STXRefEntry getEntry(int number) {
191: STXRefSubsection current = getXRefSubsection();
192: while (current != null) {
193: if (number < current.getStart()) {
194: return null;
195: }
196: if (current.getStart() <= number
197: && number < current.getStop()) {
198: return current.getEntry(number);
199: }
200: current = current.getNext();
201: }
202: return null;
203: }
204:
205: public COSArray getID() {
206: return cosGetDict().get(COSTrailer.DK_ID).asArray();
207: }
208:
209: public int getIncrementalCount() {
210: if (getPrevious() == null) {
211: return 1;
212: } else {
213: // todo maybe we should check linearization in another way
214: if (cosGetDict().get(COSTrailer.DK_Root).isNull()) {
215: // part of linearized structure
216: return getPrevious().getIncrementalCount();
217: } else {
218: return getPrevious().getIncrementalCount() + 1;
219: }
220: }
221: }
222:
223: public int getMaxObjectNumber() {
224: STXRefSubsection current = getXRefSubsection();
225: while (current != null) {
226: if (current.getNext() != null) {
227: current = current.getNext();
228: } else {
229: return current.getStop();
230: }
231: }
232: return 0;
233: }
234:
235: public long getOffset() {
236: return offset;
237: }
238:
239: public STXRefSection getPrevious() {
240: return previous;
241: }
242:
243: /**
244: * @return offset of previous trailer dict or -1 if none exists
245: */
246: public int getPreviousOffset() {
247: COSInteger value = cosGetDict().get(COSTrailer.DK_Prev)
248: .asInteger();
249: if (value == null) {
250: return -1;
251: }
252: return value.intValue();
253: }
254:
255: protected int getPreviousXRefStmOffset() {
256: COSInteger value = cosGetDict().get(DK_XRefStm).asInteger();
257: if (value == null) {
258: return -1;
259: }
260: return value.intValue();
261: }
262:
263: /**
264: * The total number of indirect objects in the document.
265: *
266: * @return The total number of indirect objects in the document.
267: */
268: public int getSize() {
269: COSInteger value = cosGetDict().get(COSTrailer.DK_Size)
270: .asInteger();
271: if (value == null) {
272: return -1;
273: }
274: return value.intValue();
275: }
276:
277: /**
278: * The object number of the first object in this section.
279: *
280: * @return The object number of the first object in this section.
281: */
282: public int getStart() {
283: return getXRefSubsection().getStart();
284: }
285:
286: public abstract AbstractXRefWriter getWriter(COSWriter cosWriter);
287:
288: protected int getXRefStmOffset() {
289: COSInteger value = cosGetDict().get(DK_XRefStm).asInteger();
290: if (value == null) {
291: return -1;
292: }
293: return value.intValue();
294: }
295:
296: /**
297: * The first subsection in this section. All other subsections are
298: * implemented as a linked list.
299: *
300: * @return The first subsection in this section.
301: */
302: public STXRefSubsection getXRefSubsection() {
303: return xRefSubsection;
304: }
305:
306: protected abstract boolean isStreamed();
307:
308: public COSObject load(int objectNumber,
309: ISystemSecurityHandler securityHandler) throws IOException,
310: COSLoadException {
311: if (contains(objectNumber)) {
312: return getEntry(objectNumber).load(getDoc(),
313: securityHandler);
314: }
315: if (getPrevious() != null) {
316: return getPrevious().load(objectNumber, securityHandler);
317: }
318: return null;
319: }
320:
321: protected void setCOSDoc(COSDocument doc) {
322: doc.add(cosGetObject());
323: }
324:
325: protected void setID(COSArray id) {
326: cosGetDict().put(COSTrailer.DK_ID, id);
327: }
328:
329: protected void setOffset(long offset) {
330: this .offset = offset;
331: }
332:
333: protected void setPrevious(STXRefSection xRefSection) {
334: this .previous = xRefSection;
335: if (getPreviousOffset() != xRefSection.getOffset()) {
336: setPreviousOffset(xRefSection.getOffset());
337: }
338: }
339:
340: protected void setPreviousOffset(long offset) {
341: cosGetDict().put(COSTrailer.DK_Prev,
342: COSInteger.create((int) offset));
343: }
344:
345: protected void setSize(int size) {
346: cosGetDict().put(COSTrailer.DK_Size, COSInteger.create(size));
347: }
348:
349: protected void setXRefStmOffset(long xrefStmOffset) {
350: cosGetDict().put(DK_XRefStm,
351: COSInteger.create((int) xrefStmOffset));
352: }
353:
354: protected void setXRefSubsection(STXRefSubsection newXRef) {
355: this .xRefSubsection = newXRef;
356: STXRefSubsection current = getXRefSubsection();
357: while (current != null) {
358: current.setXRefSection(this );
359: current = current.getNext();
360: }
361: }
362:
363: public Iterator subsectionIterator() {
364: return new Iterator() {
365: private STXRefSubsection current = getXRefSubsection();
366:
367: public boolean hasNext() {
368: return current != null;
369: }
370:
371: public Object next() {
372: if (current == null) {
373: throw new NoSuchElementException();
374: }
375: STXRefSubsection result = current;
376: current = current.getNext();
377: return result;
378: }
379:
380: public void remove() {
381: throw new UnsupportedOperationException();
382: }
383: };
384: }
385: }
|