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.cos;
031:
032: import java.security.MessageDigest;
033: import java.util.Iterator;
034: import de.intarsys.tools.locator.ILocator;
035: import de.intarsys.tools.randomaccess.IRandomAccess;
036: import de.intarsys.tools.randomaccess.RandomAccessByteArray;
037: import de.intarsys.tools.string.StringTools;
038:
039: /**
040: * The document trailer.
041: */
042: public class COSTrailer extends COSBasedObject {
043: /**
044: * The meta class implementation
045: */
046: public static class MetaClass extends COSBasedObject.MetaClass {
047: protected MetaClass(Class instanceClass) {
048: super (instanceClass);
049: }
050: }
051:
052: /** The well known attribute names */
053: public static final COSName DK_Info = COSName.constant("Info"); //$NON-NLS-1$
054:
055: public static final COSName DK_Prev = COSName.constant("Prev"); //$NON-NLS-1$
056:
057: public static final COSName DK_Root = COSName.constant("Root"); //$NON-NLS-1$
058:
059: public static final COSName DK_Size = COSName.constant("Size"); //$NON-NLS-1$
060:
061: public static final COSName DK_Encrypt = COSName
062: .constant("Encrypt"); //$NON-NLS-1$
063:
064: public static final COSName DK_ID = COSName.constant("ID"); //$NON-NLS-1$
065:
066: public static final COSName DK_XRefStm = COSName
067: .constant("XRefStm"); //$NON-NLS-1$
068:
069: /** The meta class instance */
070: public static final MetaClass META = new MetaClass(MetaClass.class
071: .getDeclaringClass());
072:
073: /** The cached catalog object */
074: private COSCatalog cachedCatalog;
075:
076: protected COSTrailer(COSObject object) {
077: super (object);
078: }
079:
080: /**
081: * Set the info dictionary containing metadata.
082: *
083: * @param infoDict
084: * The info dictionary containing metadata.
085: */
086: public void setInfoDict(COSInfoDict infoDict) {
087: setFieldObject(DK_Info, infoDict);
088: }
089:
090: /**
091: * The {@link COSInfoDict} containing metadata.
092: *
093: * @return The {@link COSInfoDict} containing metadata.
094: */
095: public COSInfoDict getInfoDict() {
096: return (COSInfoDict) COSInfoDict.META
097: .createFromCos(cosGetField(DK_Info));
098: }
099:
100: /**
101: *
102: * @return Offset of previous trailer dict or -1 if none exists
103: */
104: public int getPrev() {
105: return getFieldInt(DK_Prev, -1);
106: }
107:
108: /**
109: * @return Total number of indirect objects in the document
110: */
111: public int getSize() {
112: return getFieldInt(DK_Size, -1);
113: }
114:
115: /**
116: * Set the catalog.
117: *
118: * @param root
119: * The document catalog
120: */
121: public void setRoot(COSCatalog root) {
122: setFieldObject(DK_Root, root);
123: }
124:
125: /**
126: * Get the root object (the catalog) for the document.
127: *
128: * @return The root object (the catalog) for the document.
129: */
130: public COSCatalog getRoot() {
131: if (cachedCatalog == null) {
132: cachedCatalog = (COSCatalog) COSCatalog.META
133: .createFromCos(cosGetField(DK_Root));
134: }
135: return cachedCatalog;
136: }
137:
138: /*
139: * (non-Javadoc)
140: *
141: * @see de.intarsys.pdf.cos.COSBasedObject#initializeFromScratch()
142: */
143: protected void initializeFromScratch() {
144: super .initializeFromScratch();
145: setRoot((COSCatalog) COSCatalog.META.createNew());
146: setInfoDict((COSInfoDict) COSInfoDict.META.createNew());
147: }
148:
149: /**
150: * The /ID field of the trailer.
151: *
152: * @return The /ID field of the trailer.
153: */
154: public COSArray cosGetID() {
155: return cosGetField(DK_ID).asArray();
156: }
157:
158: /**
159: * Generates a unique file ID array (10.3).
160: */
161: public void updateFileID() {
162: COSArray fileID = cosGetField(DK_ID).asArray();
163: if ((fileID == null) || (fileID.size() == 0)) {
164: fileID = COSArray.create();
165: cosSetField(DK_ID, fileID);
166: byte[] id = createFileID();
167: COSString permanentID = COSString.create(id);
168: fileID.add(permanentID);
169: fileID.add(permanentID);
170: } else {
171: byte[] id = createFileID();
172: COSString changingID = COSString.create(id);
173: if (fileID.size() < 2) {
174: fileID.add(changingID);
175: } else {
176: fileID.set(1, changingID);
177: }
178: }
179: }
180:
181: /*
182: * <code> - include time - file location - size - document information
183: * dictionary </code>
184: *
185: * @return a byte array with the created ID
186: */
187: protected byte[] createFileID() {
188: try {
189: COSDocument cosDoc = cosGetDoc();
190: if (cosDoc == null) {
191: return null;
192: }
193: ILocator locator = cosDoc.getLocator();
194: if (locator == null) {
195: return null;
196: }
197: IRandomAccess ra = cosDoc.stGetDoc().getRandomAccess();
198: if (ra == null) {
199: ra = new RandomAccessByteArray(StringTools
200: .toByteArray("DummyValue")); //$NON-NLS-1$
201: }
202: MessageDigest digest = MessageDigest.getInstance("MD5"); //$NON-NLS-1$
203: long time = System.currentTimeMillis();
204: digest.update(String.valueOf(time).getBytes());
205: digest.update(locator.getFullName().getBytes());
206: // this is the previous length! - but should not matter
207: digest.update(String.valueOf(ra.getLength()).getBytes());
208: COSInfoDict infoDict = getInfoDict();
209: if (infoDict != null) {
210: for (Iterator it = infoDict.cosGetDict().iterator(); it
211: .hasNext();) {
212: COSObject object = (COSObject) it.next();
213: digest.update(object.stringValue().getBytes());
214: }
215: }
216: return digest.digest();
217: } catch (Exception e) {
218: throw new IllegalStateException(e);
219: }
220: }
221:
222: /**
223: * Set the /Encrypt field of the trailer.
224: *
225: * @param encryption
226: * The new encryption dictionary
227: */
228: public void cosSetEncryption(COSDictionary encryption) {
229: cosSetField(DK_Encrypt, encryption);
230: }
231:
232: /**
233: * The /Encrypt field of the trailer.
234: *
235: * @return The /Encrypt field of the trailer.
236: */
237: public COSDictionary cosGetEncryption() {
238: return cosGetField(DK_Encrypt).asDictionary();
239: }
240:
241: public void invalidateCaches() {
242: super.invalidateCaches();
243: cachedCatalog = null;
244: }
245: }
|