KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > etymon > pjx > PdfManager


1 /*
2   Copyright (C) Etymon Systems, Inc. <http://www.etymon.com/>
3 */

4
5 package com.etymon.pjx;
6
7 import java.io.*;
8 import java.util.*;
9
10 /**
11    Manages the modification or creation of a PDF document. This class
12    is synchronized.
13    @author Nassib Nassar
14 */

15 public final class PdfManager {
16
17     protected long _last_startxref;
18
19     /**
20        @deprecated Do not use this method.
21      */

22     public long getStartxref() {
23         return _last_startxref;
24     }
25     
26     protected static final PdfName PDFNAME_PREV = new PdfName("Prev");
27
28     protected static final PdfName PDFNAME_SIZE = new PdfName("Size");
29
30     /**
31        The collection of PDF objects that have been modified. Each
32        PDF object is keyed by its associated object number (stored
33        as type Integer).
34      */

35     protected Map _modobj;
36
37     /**
38        The total size of the cross-reference table, taking
39        together both the PDF input document and the modified
40        objects. This is also used as the next free object number.
41      */

42     protected int _modsize;
43
44     /**
45        The PDF input document.
46      */

47     protected PdfReader _reader;
48     
49     /**
50        Returns the <code>PdfReader</code> instance associated with
51        this document.
52     */

53     public PdfReader getReader() {
54         synchronized (this) {
55
56             return _reader;
57
58         }
59     }
60     
61     /**
62        The beginning offset of the cross-reference table in the
63        PDF input document.
64      */

65     protected long _startxref;
66
67     /**
68        The document's trailer dictionary, in its current (possibly
69        modified) state.
70      */

71     protected PdfDictionary _trailer;
72
73     /**
74        The cross-reference table and associated trailer read from
75        the PDF input document.
76      */

77     protected XrefTable _xt;
78
79     /**
80            Constructs a <code>PdfManager</code> representing an empty
81            PDF document. The document in this initial state contains
82            no objects and therefore does not correspond to a valid PDF
83            file.
84        @param init if false, specifies that the document contain
85        no objects (and therefore will not correspond to a valid
86        PDF file); if true, that the document be initialized with
87        a minimal set of objects to create a single blank page.
88         */

89         public PdfManager(boolean init) {
90
91         _reader = null;
92         _startxref = 0;
93         _xt = null;
94
95         _trailer = new PdfDictionary(new HashMap());
96         
97         _modsize = 1;
98         construct();
99
100         if (init) {
101             initialize();
102         }
103     }
104
105     /**
106        Creates the minimal set of objects in a newly constructed
107        document in order to create a single blank page. This
108        method assumes that the document contains no objects.
109        (This method is adapted from
110        com.etymon.pj.Pdf.createEmpty().)
111         */

112     private void initialize() {
113
114         List list;
115         Map map;
116         PdfName nameType = new PdfName("Type");
117
118         // Make a ProcSet.
119
list = new ArrayList(2);
120         list.add(new PdfName("PDF"));
121         list.add(new PdfName("Text"));
122         PdfArray procSet = new PdfArray(list);
123         int procSetId = addObject(procSet);
124
125         // Make a Resources dictionary.
126
map = new HashMap(1);
127         map.put(new PdfName("ProcSet"),
128             new PdfReference(procSetId, 0));
129         PdfDictionary resources = new PdfDictionary(map);
130         int resourcesId = addObject(resources);
131
132         // Make a MediaBox.
133
list = new ArrayList(4);
134         list.add(new PdfInteger(0));
135         list.add(new PdfInteger(0));
136         list.add(new PdfInteger(612));
137         list.add(new PdfInteger(792));
138         PdfArray mediaBox = new PdfArray(list);
139
140         // Make a Page placeholder.
141
int pageId = addObject(new PdfDictionary());
142
143         // Make a Kids array.
144
list = new ArrayList(1);
145         list.add(new PdfReference(pageId, 0));
146         PdfArray kids = new PdfArray(list);
147
148         // Make the root Pages node.
149
map = new HashMap(5);
150         map.put(nameType, new PdfName("Pages"));
151         map.put(new PdfName("Resources"), new PdfReference(resourcesId, 0));
152         map.put(new PdfName("MediaBox"), mediaBox);
153         map.put(new PdfName("Count"), new PdfInteger(1));
154         map.put(new PdfName("Kids"), kids);
155         int rootId = addObject(new PdfDictionary(map));
156
157         // Go back and recreate the Page correctly, including
158
// the Parent pointer.
159
map = new HashMap(2);
160         map.put(nameType, new PdfName("Page"));
161         map.put(new PdfName("Parent"),
162             new PdfReference(rootId, 0));
163         PdfDictionary page = new PdfDictionary(map);
164         setObject(page, pageId);
165
166         // Make the Catalog.
167
map = new HashMap(2);
168         map.put(nameType, new PdfName("Catalog"));
169         map.put(new PdfName("Pages"),
170             new PdfReference(rootId, 0));
171         int catalogId = addObject(new PdfDictionary(map));
172
173         // Create Info dictionary with default fields. ?
174

175         // Update trailer dictionary.
176
map = new HashMap(getTrailerDictionary().getMap());
177         map.put(new PdfName("Root"),
178             new PdfReference(catalogId, 0));
179         setTrailerDictionary(new PdfDictionary(map));
180         
181     }
182     
183     /**
184            Constructs a <code>PdfManager</code> representing an
185            existing PDF document.
186            @param pdfReader specifies the existing PDF document to
187            read.
188        @throws IOException
189        @throws PdfFormatException
190         */

191         public PdfManager(PdfReader pdfReader) throws IOException, PdfFormatException {
192
193         _startxref = pdfReader.readStartxref();
194         _xt = pdfReader.readXrefTable(_startxref);
195         _trailer = _xt.getTrailerDictionary();
196         _reader = pdfReader;
197         _modsize = _xt.size();
198         construct();
199     }
200
201     /**
202        Adds a PDF object to the document and assigns a new object
203        number to it.
204        @param obj the PDF object to be added.
205        @return the new object number assigned to the PDF object.
206        The generation number of the object will be 0.
207      */

208     public int addObject(PdfObject obj) {
209         synchronized (this) {
210
211             int id = _modsize++;
212             
213             setObject(obj, id);
214
215             return id;
216         }
217     }
218
219     /**
220        Retrieves the PDF object associated with a specified object
221        number.
222        @param objectNumber specifies the object number of the PDF
223        object to be retrieved.
224        @return the retrieved PDF object.
225        @throws IOException
226        @throws PdfFormatException
227     */

228     public PdfObject getObject(int objectNumber) throws IOException, PdfFormatException {
229         synchronized (this) {
230             
231             // first check hashmap
232
PdfObject obj = (PdfObject)_modobj.get(new Integer JavaDoc(objectNumber));
233             if (obj != null) {
234                 return obj;
235             }
236             
237             // next try the xref
238
if (_reader == null) {
239                 return null;
240             }
241             if (_xt.unwrapUsageArray()[objectNumber] != XrefTable.ENTRY_IN_USE) {
242                 return null;
243             }
244             long start = _xt.getIndex(objectNumber);
245             long end = _xt.estimateObjectEnd(objectNumber);
246             return _reader.readObject(start, end, true, _xt);
247         }
248         
249     }
250
251     /**
252        Retrieves the PDF object referred to by a specified
253        indirect reference object. If the specified object is not
254        an indirect reference but a direct object, that object
255        itself is returned. If the specified object is
256        <code>null</code>, then <code>null</code> is returned.
257        @param obj the indirect reference object (or direct object,
258        or <code>null</code>).
259        @return the retrieved object (or direct object).
260        @throws IOException
261        @throws PdfFormatException
262      */

263     public PdfObject getObjectIndirect(PdfObject obj) throws IOException, PdfFormatException {
264         synchronized (this) {
265
266             if (obj == null) {
267                 return null;
268             }
269             
270             if (obj instanceof PdfReference) {
271                 // this should be changed from
272
// recursive to iterative, for best
273
// efficiency
274
return getObjectIndirect( getObject( ((PdfReference)obj).getObjectNumber() ) );
275             } else {
276                 return obj;
277             }
278             
279         }
280     }
281
282     /**
283        Returns the document's trailer dictionary.
284        @return the trailer dictionary.
285      */

286     public PdfDictionary getTrailerDictionary() {
287         synchronized (this) {
288             return _trailer;
289         }
290     }
291
292     /**
293        Returns the current size of the cross-reference table,
294        taking into account any modifications that have been made
295        to the document via this PdfManager.
296        @return the size of the cross-reference table.
297      */

298     public int getXrefTableSize() {
299         synchronized (this) {
300             return _modsize;
301         }
302     }
303
304     /**
305        Performs initialization common to multiple constructors of
306        this class. This method is only intended to be called from
307        the constructors.
308      */

309     protected void construct() {
310         _modobj = new HashMap();
311     }
312
313     /**
314        Adds a PDF object to the document and assigns the specified
315        object number to it. If an object in the document is
316        already assigned to the specified object number, that
317        object is replaced with the new one.
318        @param obj the PDF object to be added.
319        @param objectNumber the object number to assign to the PDF
320        object.
321      */

322     public void setObject(PdfObject obj, int objectNumber) {
323         synchronized (this) {
324             _modobj.put(new Integer JavaDoc(objectNumber), obj);
325             if (objectNumber >= _modsize) {
326                 _modsize = objectNumber + 1;
327             }
328         }
329     }
330
331     /**
332        Assigns the document a new trailer dictionary, replacing
333        the existing one (if any).
334        @param dictionary specifies the new trailer dictionary.
335      */

336     public void setTrailerDictionary(PdfDictionary dictionary) {
337         synchronized (this) {
338             _trailer = dictionary;
339         }
340     }
341
342     /**
343        Writes the document in PDF format, including all
344        modifications made through this <code>PdfManager</code>.
345        This method is equivalent to using {@link
346        #writeDocument(PdfWriter, boolean) writeDocument(...,
347        true)}; i.e. it uses incremental update if applicable.
348        Note that this means the resultant document will be larger
349        than the original document; otherwise use {@link
350        #writeDocument(PdfWriter, boolean) writeDocument(...,
351        false)} to get a smaller resultant file, although it will
352        usually take longer to generate. The
353        <code>PdfWriter</code> should be newly created (i.e. it
354        should not have been previously used for anything); and
355        after this method has been called, the
356        <code>PdfWriter</code> should be closed and discarded.
357        @param pdfWriter specifies the PDF document to write.
358        @return the number of bytes written.
359        @throws IOException
360        @throws PdfFormatException
361      */

362     public long writeDocument(PdfWriter pdfWriter) throws IOException, PdfFormatException {
363         return writeDocument(pdfWriter, true);
364     }
365     
366     /**
367        Writes the document in PDF format, including all
368        modifications made through this <code>PdfManager</code>.
369        This method will optionally use PDF's incremental update
370        format, which often takes significantly less processing
371        time but creates a larger resultant PDF file and may be
372        slower for a reader to open (if the file has been updated
373        many times in this way). Incremental update should not be
374        used when the original document is a Linearized PDF file if
375        changes have been made that would invalidate its
376        correctness, unless the resultant PDF file will not be used
377        in an application that depends on its correct
378        Linearization. If this <code>PdfManager</code> represents
379        a new document rather than modifying an existing one, then
380        the incremental update option is not applicable and is
381        disregarded. The <code>PdfWriter</code> should be newly
382        created (i.e. it should not have been previously used for
383        anything); and after this method has been called, the
384        <code>PdfWriter</code> should be closed and discarded.
385        @param pdfWriter specifies the PDF document to write.
386        @param useIncrementalUpdate specifies whether incremental
387        update format should be used. A value of <code>true</code>
388        enables incremental update.
389        @return the number of bytes written.
390        @throws IOException
391        @throws PdfFormatException
392      */

393     public long writeDocument(PdfWriter pdfWriter, boolean useIncrementalUpdate)
394         throws IOException, PdfFormatException {
395         synchronized (this) {
396             
397             int modsize = _modsize;
398             XrefTable xt = _xt;
399             PdfReader reader = _reader;
400             Map modobj = _modobj;
401             
402             boolean incUpdate;
403             if (reader == null) {
404                 incUpdate = false;
405             } else {
406                 incUpdate = useIncrementalUpdate;
407             }
408             
409             PdfWriter w = pdfWriter;
410             long pos = 0;
411             
412             if (incUpdate) {
413                 pos += w.writeCopy(reader);
414             } else {
415                 pos += w.writeHeader();
416             }
417             
418             long[] nindex = new long[modsize];
419             int[] ngeneration = new int[modsize];
420             byte[] nusage = new byte[modsize];
421             // (maybe we should ideally clone the
422
// trailer instead of modifying the original)
423
HashMap ntrailerMap = new HashMap(_trailer.getMap());
424             
425             long[] index = null;
426             int[] generation = null;
427             byte[] usage = null;
428
429             if (reader != null) {
430                 index = xt.unwrapIndexArray();
431                 generation = xt.unwrapGenerationArray();
432                 usage = xt.unwrapUsageArray();
433             }
434             
435             int id;
436             // iterate through modified objects, adding
437
// them to the new XrefTrailer
438
for (Iterator it = modobj.keySet().iterator(); it.hasNext(); ) {
439                 Integer JavaDoc key = (Integer JavaDoc)it.next();
440                 id = key.intValue();
441                 nusage[id] = XrefTable.ENTRY_IN_USE;
442                 nindex[id] = pos;
443                 PdfObject obj = (PdfObject)modobj.get(key);
444                 
445                 // determine generation number
446
if (reader == null) {
447                     ngeneration[id] = 0;
448                 } else {
449                     if ( (id < xt.size()) && (usage[id] != XrefTable.ENTRY_UNDEFINED) ) {
450                         ngeneration[id] = generation[id];
451                     } else {
452                         ngeneration[id] = 0;
453                     }
454                 }
455                 
456                 if ( (incUpdate) || (reader == null) ) {
457                     pos += w.writeObjectIndirect(obj, id, ngeneration[id]);
458                 }
459             }
460             
461             // always keep entry 0
462
ngeneration[0] = 65535;
463             nusage[0] = XrefTable.ENTRY_FREE;
464             
465             if (!incUpdate) {
466                 for (id = 1; id < modsize; id++) {
467                     PdfObject obj = null;
468                     if ( (reader != null) && (nusage[id] == XrefTable.ENTRY_UNDEFINED) ) {
469                         if ( (id < xt.size()) &&
470                              (usage[id] != XrefTable.ENTRY_UNDEFINED) ) {
471                             if (usage[id] == XrefTable.ENTRY_IN_USE) {
472                                 long start = index[id];
473                                 long end = xt.estimateObjectEnd(id);
474                                 obj = reader.readObject(start, end, true, xt);
475                             }
476                             ngeneration[id] = generation[id];
477                             nusage[id] = usage[id];
478                         }
479                     } else {
480                         obj = (PdfObject)modobj.get(new Integer JavaDoc(id));
481                     }
482                     if (nusage[id] == XrefTable.ENTRY_IN_USE) {
483                         nindex[id] = pos;
484                         pos += w.writeObjectIndirect(obj, id, ngeneration[id]);
485                     }
486                     
487                 }
488             }
489             
490             if (incUpdate) {
491                 ntrailerMap.put(PDFNAME_PREV, new PdfLong(_startxref));
492             } else {
493                 ntrailerMap.remove(PDFNAME_PREV);
494             }
495             ntrailerMap.put(PDFNAME_SIZE, new PdfInteger(modsize));
496             
497             XrefTable nxt = XrefTable.wrap(nindex, ngeneration, nusage,
498                                new PdfDictionary(ntrailerMap));
499             
500             _last_startxref = pos;
501             
502             pos += w.writeXrefTable(nxt, pos);
503
504             return pos;
505         }
506     }
507     
508 }
509
Popular Tags