KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > it > stefanochizzolini > clown > documents > Pages


1 /*
2   Copyright © 2006 Stefano Chizzolini. http://clown.stefanochizzolini.it
3
4   Contributors:
5     * Stefano Chizzolini (original code developer, info@stefanochizzolini.it):
6       contributed code is Copyright © 2006 by Stefano Chizzolini.
7
8   This file should be part of the source code distribution of "PDF Clown library"
9   (the Program): see the accompanying README files for more info.
10
11   This Program is free software; you can redistribute it and/or modify it under
12   the terms of the GNU General Public License as published by the Free Software
13   Foundation; either version 2 of the License, or (at your option) any later version.
14
15   This Program is distributed in the hope that it will be useful, but WITHOUT ANY
16   WARRANTY, either expressed or implied; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more details.
18
19   You should have received a copy of the GNU General Public License along with this
20   Program (see README files); if not, go to the GNU website (http://www.gnu.org/).
21
22   Redistribution and use, with or without modification, are permitted provided that such
23   redistributions retain the above copyright notice, license and disclaimer, along with
24   this list of conditions.
25 */

26
27 package it.stefanochizzolini.clown.documents;
28
29 import it.stefanochizzolini.clown.files.File;
30 import it.stefanochizzolini.clown.objects.IPdfNumber;
31 import it.stefanochizzolini.clown.objects.PdfArray;
32 import it.stefanochizzolini.clown.objects.PdfDictionary;
33 import it.stefanochizzolini.clown.objects.PdfDirectObject;
34 import it.stefanochizzolini.clown.objects.PdfInteger;
35 import it.stefanochizzolini.clown.objects.PdfName;
36 import it.stefanochizzolini.clown.objects.PdfObjectWrapper;
37 import it.stefanochizzolini.clown.objects.PdfReference;
38 import it.stefanochizzolini.clown.util.NotImplementedException;
39
40 import java.util.ArrayList JavaDoc;
41 import java.util.Arrays JavaDoc;
42 import java.util.Collection JavaDoc;
43 import java.util.Iterator JavaDoc;
44 import java.util.List JavaDoc;
45 import java.util.ListIterator JavaDoc;
46 import java.util.NoSuchElementException JavaDoc;
47 import java.util.Stack JavaDoc;
48
49 /**
50   Document pages collection.
51 */

52 public class Pages
53   extends PdfObjectWrapper<PdfDictionary>
54   implements List JavaDoc<Page>
55 {
56   /*
57     TODO:IMPL A B-tree algorithm should be implemented to optimize the inner layout
58     of the page tree (better insertion/deletion performance). In this case, it would
59     be necessary to keep track of the modified tree nodes for incremental update.
60   */

61   // <class>
62
// <dynamic>
63
// <constructors>
64
Pages(
65     Document context
66     )
67   {
68     super(
69       context.getFile(),
70       new PdfDictionary(
71         new PdfName[]
72         {
73           PdfName.Type,
74           PdfName.Kids,
75           PdfName.Count
76         },
77         new PdfDirectObject[]
78         {
79           PdfName.Pages,
80           new PdfArray(),
81           new PdfInteger(0)
82         }
83         )
84       );
85   }
86
87   Pages(
88     PdfDirectObject baseObject
89     )
90   {
91     super(
92       baseObject,
93       null // NO container (page tree root node MUST be an indirect object [PDF:1.6:3.6.1]).
94
);
95   }
96   // </constructors>
97

98   // <interface>
99
// <public>
100
public Object JavaDoc clone(
101     Document context
102     )
103   {throw new NotImplementedException();}
104
105   // <List>
106
public void add(
107     int index,
108     Page page
109     )
110   {commonAddAll(index,Arrays.asList(page));}
111
112   public boolean addAll(
113     int index,
114     Collection JavaDoc<? extends Page> pages
115     )
116   {return commonAddAll(index,pages);}
117
118   public Page get(
119     int index
120     )
121   {
122     /*
123       NOTE: As stated in [PDF:1.6:3.6.2], to retrieve pages is a matter of diving
124       inside a B-tree. To keep it as efficient as possible, this implementation
125       does NOT adopt recursion to deepen its search, opting for an iterative strategy
126       instead.
127     */

128     int pageOffset = 0;
129     PdfDictionary parent = getBaseDataObject();
130     PdfArray kids = (PdfArray)File.resolve(parent.get(PdfName.Kids));
131     for(
132       int i = 0;
133       i < kids.size();
134       i++
135       )
136     {
137       PdfReference kidReference = (PdfReference)kids.get(i);
138       PdfDictionary kid = (PdfDictionary)File.resolve(kidReference);
139       // Is current kid a page object?
140
if(kid.get(PdfName.Type).equals(PdfName.Page)) // Page object.
141
{
142         // Did we reach the searched position?
143
if(pageOffset == index) // Vertical scan (we finished).
144
{
145           // We got it!
146
return new Page(kidReference);
147         }
148         else // Horizontal scan (go past).
149
{
150           // Cumulate current page object count!
151
pageOffset++;
152         }
153       }
154       else // Page tree node.
155
{
156         // Does the current subtree contain the searched page?
157
if(((PdfInteger)kid.get(PdfName.Count)).getValue() + pageOffset > index) // Vertical scan (deepen the search).
158
{
159           // Go down one level!
160
parent = kid;
161           kids = (PdfArray)File.resolve(parent.get(PdfName.Kids));
162           i = -1;
163         }
164         else // Horizontal scan (go past).
165
{
166           // Cumulate current subtree count!
167
pageOffset += ((PdfInteger)kid.get(PdfName.Count)).getValue();
168         }
169       }
170     }
171
172     return null;
173   }
174
175   public int indexOf(
176     Object JavaDoc page
177     )
178   {return ((Page)page).getIndex();}
179
180   public int lastIndexOf(
181     Object JavaDoc page
182     )
183   {
184     /*
185       NOTE: Each page object should NOT appear more than once inside the same document.
186     */

187     return indexOf(page);
188   }
189
190   public ListIterator JavaDoc<Page> listIterator(
191     )
192   {throw new NotImplementedException();}
193
194   public ListIterator JavaDoc<Page> listIterator(
195     int index
196     )
197   {throw new NotImplementedException();}
198
199   public Page remove(
200     int index
201     )
202   {
203     Page page = get(index);
204     remove(page);
205
206     return page;
207   }
208
209   public Page set(
210     int index,
211     Page page
212     )
213   {
214     Page old = remove(index);
215     add(index,page);
216
217     return old;
218   }
219
220   public List JavaDoc<Page> subList(
221     int fromIndex,
222     int toIndex
223     )
224   {
225   /*
226   TODO:IMPL this implementation is incoherent with the subList contract --> move to another location!
227   */

228     ArrayList JavaDoc<Page> pages = new ArrayList JavaDoc<Page>(toIndex - fromIndex);
229     int i = fromIndex;
230     Page page = get(i);
231     while(i++ < toIndex)
232     {
233       pages.add(page);
234       page = page.getNext();
235     }
236
237     return pages;
238   }
239
240   // <Collection>
241
public boolean add(
242     Page page
243     )
244   {return commonAddAll(-1,Arrays.asList(page));}
245
246   public boolean addAll(
247     Collection JavaDoc<? extends Page> pages
248     )
249   {return commonAddAll(-1,pages);}
250
251   public void clear(
252     )
253   {throw new NotImplementedException();}
254
255   public boolean contains(
256     Object JavaDoc page
257     )
258   {throw new NotImplementedException();}
259
260   public boolean containsAll(
261     Collection JavaDoc<?> pages
262     )
263   {throw new NotImplementedException();}
264
265   public boolean equals(
266     Object JavaDoc object
267     )
268   {throw new NotImplementedException();}
269
270   public int hashCode(
271     )
272   {throw new NotImplementedException();}
273
274   public boolean isEmpty(
275     )
276   {throw new NotImplementedException();}
277
278   public boolean remove(
279     Object JavaDoc page
280     )
281   {
282     Page pageObj = (Page)page;
283     PdfDictionary pageData = pageObj.getBaseDataObject();
284     // Get the parent tree node!
285
PdfDirectObject parent = pageData.get(PdfName.Parent);
286     PdfDictionary parentData = (PdfDictionary)File.resolve(parent);
287     // Get the parent's page collection!
288
PdfDirectObject kids = parentData.get(PdfName.Kids);
289     PdfArray kidsData = (PdfArray)File.resolve(kids);
290     // Remove the page!
291
kidsData.remove(pageObj.getBaseObject());
292     boolean updateParent = !File.update(kids); // Try to update the page collection.
293
// Unbind the page from its parent!
294
pageData.put(PdfName.Parent,null);
295     pageObj.update();
296     // Decrementing the pages counters...
297
do
298     {
299       // Get the page collection counter!
300
PdfDirectObject count = parentData.get(PdfName.Count);
301       IPdfNumber countData = (IPdfNumber)File.resolve(count);
302       // Decrement the counter at the current level!
303
countData.translateNumberValue(-1);
304       updateParent |= !File.update(count); // Try to update the counter.
305
// Is the parent tree node to be updated?
306
/*
307         NOTE: It avoids to update the parent tree node if its modified fields are all
308         indirect objects which perform independent updates.
309       */

310       if(updateParent)
311       {
312         File.update(parent);
313         updateParent = false; // Reset.
314
}
315
316       // Iterate upward!
317
parent = parentData.get(PdfName.Parent);
318       parentData = (PdfDictionary)File.resolve(parent);
319     } while(parent != null);
320
321     return true;
322   }
323
324   public boolean removeAll(
325     Collection JavaDoc<?> pages
326     )
327   {
328     /*
329       NOTE: The interface contract doesn't prescribe any relation among the removing-collection's
330       items, so we cannot adopt the optimized approach of the add*(...) methods family,
331       where adding-collection's items are explicitly ordered.
332     */

333     boolean changed = false;
334     for(Object JavaDoc page : pages)
335     {changed |= remove(page);}
336
337     return changed;
338   }
339
340   public boolean retainAll(
341     Collection JavaDoc<?> pages
342     )
343   {throw new NotImplementedException();}
344
345   public int size(
346     )
347   {return ((PdfInteger)getBaseDataObject().get(PdfName.Count)).getValue();}
348
349   public Page[] toArray(
350     )
351   {throw new NotImplementedException();}
352
353   public <Page> Page[] toArray(
354     Page[] pages
355     )
356   {throw new NotImplementedException();}
357
358   // <Iterable>
359
public Iterator JavaDoc<Page> iterator(
360     )
361   {
362     return new Iterator JavaDoc<Page>()
363     {
364       // <class>
365
// <dynamic>
366
// <fields>
367
/**
368         Index of the next item.
369       */

370       private int index = 0;
371       /**
372         Collection size.
373       */

374       private int size = size();
375
376       /**
377         Current level index.
378       */

379       private int levelIndex = 0;
380       /**
381         Stacked level indexes.
382       */

383       private Stack JavaDoc<Integer JavaDoc> levelIndexes = new Stack JavaDoc<Integer JavaDoc>();
384       /**
385         Current parent tree node.
386       */

387       private PdfDictionary parent = getBaseDataObject();
388       /**
389         Current child tree nodes.
390       */

391       private PdfArray kids = (PdfArray)File.resolve(parent.get(PdfName.Kids));
392       // </fields>
393

394       // <interface>
395
// <public>
396
// <Iterator>
397
public boolean hasNext(
398         )
399       {return (index < size);}
400
401       public Page next(
402         )
403       {
404         if(!hasNext())
405           throw new NoSuchElementException JavaDoc();
406
407         return getNext();
408       }
409
410       public void remove(
411         )
412       {throw new UnsupportedOperationException JavaDoc();}
413       // </Iterator>
414
// </public>
415

416       // <private>
417
private Page getNext(
418         )
419       {
420         /*
421           NOTE: As stated in [PDF:1.6:3.6.2], to retrieve pages is a matter of diving
422           inside a B-tree.
423           This is a special adaptation of the get() algorithm necessary to keep
424           a low overhead throughout the page tree scan (using the get() method
425           would have implied a nonlinear computational cost).
426         */

427         /*
428           NOTE: Algorithm:
429           1. [Vertical, down] We have to go downward the page tree till we reach
430           a page (leaf node).
431           2. [Horizontal] Then we iterate across the page collection it belongs to,
432           repeating step 1 whenever we find a subtree.
433           3. [Vertical, up] When leaf-nodes scan is complete, we go upward solving
434           parent nodes, repeating step 2.
435         */

436         while(true)
437         {
438           // Did we complete current page-tree-branch level?
439
if(kids.size() == levelIndex) // Page subtree complete.
440
{
441             // 3. Go upward one level.
442
// Restore node index at the current level!
443
levelIndex = levelIndexes.pop() + 1; // Next node (partially scanned level).
444
// Move upward!
445
parent = (PdfDictionary)File.resolve(parent.get(PdfName.Parent));
446             kids = (PdfArray)File.resolve(parent.get(PdfName.Kids));
447           }
448           else // Page subtree incomplete.
449
{
450             PdfReference kidReference = (PdfReference)kids.get(levelIndex);
451             PdfDictionary kid = (PdfDictionary)File.resolve(kidReference);
452             // Is current kid a page object?
453
if(kid.get(PdfName.Type).equals(PdfName.Page)) // Page object.
454
{
455               // 2. Page found.
456
index++; // Absolute page index.
457
levelIndex++; // Current level node index.
458

459               return new Page(kidReference);
460             }
461             else // Page tree node.
462
{
463               // 1. Go downward one level.
464
// Save node index at the current level!
465
levelIndexes.push(levelIndex);
466               // Move downward!
467
parent = kid;
468               kids = (PdfArray)File.resolve(parent.get(PdfName.Kids));
469               levelIndex = 0; // First node (new level).
470
}
471           }
472         }
473       }
474       // </private>
475
// </interface>
476
// </dynamic>
477
// </class>
478
};
479   }
480   // </Iterable>
481
// </Collection>
482
// </List>
483
// </public>
484

485   // <private>
486
/**
487     Add a collection of pages at the specified position.
488     @param index Addition position. To append, use value -1.
489     @param pages Collection of pages to add.
490   */

491   private boolean commonAddAll(
492     int index,
493     Collection JavaDoc<? extends Page> pages
494     )
495   {
496     PdfDirectObject parent;
497     PdfDictionary parentData;
498     PdfDirectObject kids;
499     PdfArray kidsData;
500     int offset;
501     // Append operation?
502
if(index == -1) // Append operation.
503
{
504       // Get the parent tree node!
505
parent = getBaseObject();
506       parentData = getBaseDataObject();
507       // Get the parent's page collection!
508
kids = parentData.get(PdfName.Kids);
509       kidsData = (PdfArray)File.resolve(kids);
510       offset = 0; // Not used.
511
}
512     else // Insert operation.
513
{
514       // Get the page currently at the specified position!
515
Page pivotPage = get(index);
516       // Get the parent tree node!
517
parent = pivotPage.getBaseDataObject().get(PdfName.Parent);
518       parentData = (PdfDictionary)File.resolve(parent);
519       // Get the parent's page collection!
520
kids = parentData.get(PdfName.Kids);
521       kidsData = (PdfArray)File.resolve(kids);
522       // Get the insertion's relative position within the parent's page collection!
523
offset = kidsData.indexOf(pivotPage.getBaseObject());
524     }
525
526     // Adding the pages...
527
for(Page page : pages)
528     {
529       // Append?
530
if(index == -1) // Append.
531
{
532         // Append the page to the collection!
533
kidsData.add(page.getBaseObject());
534       }
535       else // Insert.
536
{
537         // Insert the page into the collection!
538
kidsData.add(
539           offset++,
540           page.getBaseObject()
541           );
542       }
543       // Bind the page to the collection!
544
page.getBaseDataObject().put(PdfName.Parent,parent);
545       page.update();
546     }
547     boolean updateParent = !File.update(kids); // Try to update the page collection.
548

549     // Incrementing the pages counters...
550
do
551     {
552       // Get the page collection counter!
553
PdfDirectObject count = parentData.get(PdfName.Count);
554       IPdfNumber countData = (IPdfNumber)File.resolve(count);
555       // Increment the counter at the current level!
556
countData.translateNumberValue(pages.size());
557       updateParent |= !File.update(count); // Try to update the page counter.
558
// Is the parent tree node to be updated?
559
/*
560         NOTE: It avoids to update the parent tree node if its modified fields are all
561         indirect objects which perform independent updates.
562       */

563       if(updateParent)
564       {
565         File.update(parent);
566         updateParent = false; // Reset.
567
}
568
569       // Iterate upward!
570
parent = parentData.get(PdfName.Parent);
571       parentData = (PdfDictionary)File.resolve(parent);
572     } while(parent != null);
573
574     return true;
575   }
576   // </private>
577
// </interface>
578
// </dynamic>
579
// </class>
580
}
Popular Tags