File Coverage

blib/lib/PDF/API3/Compat/API2/Basic/PDF/Pages.pm
Criterion Covered Total %
statement 15 168 8.9
branch 0 84 0.0
condition 0 35 0.0
subroutine 5 22 22.7
pod 11 17 64.7
total 31 326 9.5


line stmt bran cond sub pod time code
1             #=======================================================================
2             # ____ ____ _____ _ ____ ___ ____
3             # | _ \| _ \| ___| _ _ / \ | _ \_ _| |___ \
4             # | |_) | | | | |_ (_) (_) / _ \ | |_) | | __) |
5             # | __/| |_| | _| _ _ / ___ \| __/| | / __/
6             # |_| |____/|_| (_) (_) /_/ \_\_| |___| |_____|
7             #
8             # A Perl Module Chain to faciliate the Creation and Modification
9             # of High-Quality "Portable Document Format (PDF)" Files.
10             #
11             #=======================================================================
12             #
13             # THIS IS A REUSED PERL MODULE, FOR PROPER LICENCING TERMS SEE BELOW:
14             #
15             #
16             # Copyright Martin Hosken
17             #
18             # No warranty or expression of effectiveness, least of all regarding
19             # anyone's safety, is implied in this software or documentation.
20             #
21             # This specific module is licensed under the Perl Artistic License.
22             #
23             #
24             # $Id: Pages.pm,v 2.1 2007/05/10 06:44:43 areibens Exp $
25             #
26             #=======================================================================
27             package PDF::API3::Compat::API2::Basic::PDF::Pages;
28            
29 1     1   5 use strict;
  1         2  
  1         38  
30 1     1   5 use vars qw(@ISA %inst);
  1         1  
  1         64  
31             @ISA = qw(PDF::API3::Compat::API2::Basic::PDF::Dict);
32 1     1   12 no warnings qw[ deprecated recursion uninitialized ];
  1         1  
  1         32  
33            
34 1     1   744 use PDF::API3::Compat::API2::Basic::PDF::Dict;
  1         3  
  1         35  
35 1     1   855 use PDF::API3::Compat::API2::Basic::PDF::Utils;
  1         4  
  1         2349  
36            
37             %inst = map {$_ => 1} qw(Parent Type);
38            
39             =head1 NAME
40            
41             PDF::API3::Compat::API2::Basic::PDF::Pages - a PDF pages hierarchical element. Inherits from L
42            
43             =head1 DESCRIPTION
44            
45             A Pages object is the parent to other pages objects or to page objects
46             themselves.
47            
48             =head1 METHODS
49            
50             =head2 PDF::API3::Compat::API2::Basic::PDF::Pages->new($pdfs,$parent)
51            
52             This creates a new Pages object. Notice that $parent here is not the
53             file context for the object but the parent pages object for this
54             pages. If we are using this class to create a root node, then $parent
55             should point to the file context, which is identified by not having a
56             Type of Pages. $pdfs is the file object (or objects) in which to
57             create the new Pages object.
58            
59             =cut
60            
61             sub new
62             {
63 0     0 1   my ($class, $pdfs, $parent) = @_;
64 0           my ($self);
65            
66 0 0         $class = ref $class if ref $class;
67 0           $self = $class->SUPER::new($pdfs, $parent);
68 0           $self->{'Type'} = PDFName("Pages");
69 0 0         $self->{'Parent'} = $parent if defined $parent;
70 0           $self->{'Count'} = PDFNum(0);
71 0           $self->{'Kids'} = PDF::API3::Compat::API2::Basic::PDF::Array->new;
72 0 0         $self->{' outto'} = ref $pdfs eq 'ARRAY' ? $pdfs : [$pdfs];
73 0           $self->out_obj(1);
74            
75 0           $self;
76             }
77            
78            
79             sub init
80             {
81 0     0 0   my ($self, $pdf) = @_;
82 0           $self->{' outto'} = [$pdf];
83 0           $self;
84             }
85            
86             =head2 $p->out_obj($isnew)
87            
88             Tells all the files that this thing is destined for that they should output this
89             object come time to output. If this object has no parent, then it must be the
90             root. So set as the root for the files in question and tell it to be output too.
91             If $isnew is set, then call new_obj rather than out_obj to create as a new
92             object in the file.
93            
94             =cut
95            
96             sub out_obj
97             {
98 0     0 1   my ($self, $isnew) = @_;
99            
100 0           foreach (@{$self->{' outto'}})
  0            
101             {
102 0 0         if ($isnew)
103 0           { $_->new_obj($self); }
104             else
105 0           { $_->out_obj($self); }
106            
107 0 0         unless (defined $self->{'Parent'})
108             {
109 0           $_->{'Root'}{'Pages'} = $self;
110 0           $_->out_obj($_->{'Root'});
111             }
112             }
113 0           $self;
114             }
115            
116            
117             =head2 $p->find_page($pnum)
118            
119             Returns the given page, using the page count values in the pages tree. Pages
120             start at 0.
121            
122             =cut
123            
124             sub find_page
125             {
126 0     0 1   my ($self, $pnum) = @_;
127 0           my ($top) = $self->get_top;
128            
129 0           $top->find_page_recurse(\$pnum);
130             }
131            
132            
133             sub find_page_recurse
134             {
135 0     0 0   my ($self, $rpnum) = @_;
136 0           my ($res, $k);
137            
138 0 0         if ($self->{'Count'}->realise->val <= $$rpnum)
139             {
140 0           $$rpnum -= $self->{'Count'}->val;
141 0           return undef;
142             }
143            
144 0           foreach $k ($self->{'Kids'}->realise->elementsof)
145             {
146 0 0         if ($k->{'Type'}->realise->val eq 'Page')
    0          
147             {
148 0 0         return $k if ($$rpnum == 0);
149 0           $$rpnum--;
150             }
151             elsif ($res = $k->realise->find_page_recurse($rpnum))
152 0           { return $res; }
153             }
154 0           return undef;
155             }
156            
157             =head2 $p->add_page($page, $pnum)
158            
159             Inserts the page before the given $pnum. $pnum can be -ve to count from the END
160             of the document. -1 is after the last page. Likewise $pnum can be greater than the
161             number of pages currently in the document, to append.
162            
163             This method only guarantees to provide a reasonable pages tree if pages are
164             appended or prepended to the document. Pages inserted in the middle of the
165             document may simply be inserted in the appropriate leaf in the pages tree without
166             adding any new branches or leaves. To tidy up such a mess, it is best to call
167             $p->rebuild_tree to rebuild the pages tree into something efficient.
168            
169             =cut
170            
171             sub add_page
172             {
173 0     0 1   my ($self, $page, $pnum) = @_;
174 0           my ($top) = $self->get_top;
175 0           my ($ppage, $ppages, $pindex, $ppnum);
176            
177 0 0 0       $pnum = -1 unless (defined $pnum && $pnum <= $top->{'Count'}->val);
178 0 0         if ($pnum == -1)
179 0           { $ppage = $top->find_page($top->{'Count'}->val - 1); }
180             else
181             {
182 0 0         $pnum = $top->{'Count'}->val + $pnum + 1 if ($pnum < 0);
183 0           $ppage = $top->find_page($pnum);
184             }
185            
186 0 0         if (defined $ppage->{'Parent'})
187 0           { $ppages = $ppage->{'Parent'}->realise; }
188             else
189 0           { $ppages = $self; }
190            
191 0           $ppnum = scalar $ppages->{'Kids'}->realise->elementsof;
192            
193 0 0         if ($pnum == -1)
194 0           { $pindex = -1; }
195             else
196             {
197 0           for ($pindex = 0; $pindex < $ppnum; $pindex++)
198 0 0         { last if ($ppages->{'Kids'}{' val'}[$pindex] eq $ppage); }
199 0 0         $pindex = -1 if ($pindex == $ppnum);
200             }
201            
202 0           $ppages->add_page_recurse($page->realise, $pindex);
203 0           for ($ppages = $page->{'Parent'}; defined $ppages->{'Parent'}; $ppages = $ppages->{'Parent'}->realise)
204 0           { $ppages->out_obj->{'Count'}->realise->{'val'}++; }
205 0           $ppages->out_obj->{'Count'}->realise->{'val'}++;
206 0           $page;
207             }
208            
209            
210             sub add_page_recurse
211             {
212 0     0 0   my ($self, $page, $index) = @_;
213 0           my ($newpages, $ppages, $pindex, $ppnum);
214            
215 0 0 0       if (scalar $self->{'Kids'}->elementsof >= 8 && $self->{'Parent'} && $index < 1)
      0        
216             {
217 0           $ppages = $self->{'Parent'}->realise;
218 0           $newpages = $self->new($self->{' outto'}, $ppages);
219 0 0         if ($ppages)
220             {
221 0           $ppnum = scalar $ppages->{'Kids'}->realise->elementsof;
222 0           for ($pindex = 0; $pindex < $ppnum; $pindex++)
223 0 0         { last if ($ppages->{'Kids'}{' val'}[$pindex] eq $self); }
224 0 0         $pindex = -1 if ($pindex == $ppnum);
225 0           $ppages->add_page_recurse($newpages, $pindex);
226             }
227             }
228             else
229 0           { $newpages = $self->out_obj; }
230            
231 0 0         if ($index < 0)
232 0           { push (@{$newpages->{'Kids'}->realise->{' val'}}, $page); }
  0            
233             else
234 0           { splice (@{$newpages->{'Kids'}{' val'}}, $index, 0, $page); }
  0            
235 0           $page->{'Parent'} = $newpages;
236             }
237            
238            
239             =head2 $root_pages = $p->rebuild_tree([@pglist])
240            
241             Rebuilds the pages tree to make a nice balanced tree that conforms to Adobe
242             recommendations. If passed a pglist then the tree is built for that list of
243             pages. No check is made of whether the pglist contains pages.
244            
245             Returns the top of the tree for insertion in the root object.
246            
247             =cut
248            
249             sub rebuild_tree
250             {
251 0     0 1   my ($self, @pglist) = @_;
252             }
253            
254            
255             =head2 @pglist = $p->get_pages
256            
257             Returns a list of page objects in the document in page order
258            
259             =cut
260            
261             sub get_pages
262             {
263 0     0 1   my ($self) = @_;
264            
265 0           return $self->get_top->get_kids;
266             }
267            
268            
269             # only call this on the top level or anything you want pages below
270             sub get_kids
271             {
272 0     0 0   my ($self) = @_;
273 0           my ($pgref, @pglist);
274            
275 0           foreach $pgref ($self->{'Kids'}->elementsof)
276             {
277 0           $pgref->realise;
278 0 0         if ($pgref->{'Type'}->val =~ m/^Pages$/oi)
279 0           { push (@pglist, $pgref->get_kids()); }
280             else
281 0           { push (@pglist, $pgref); }
282             }
283 0           @pglist;
284             }
285             =head2 $p->find_prop($key)
286            
287             Searches up through the inheritance tree to find a property.
288            
289             =cut
290            
291             sub find_prop
292             {
293 0     0 0   my ($self, $prop) = @_;
294            
295 0 0         if (defined $self->{$prop})
    0          
296             {
297 0 0 0       if (ref $self->{$prop} && $self->{$prop}->isa("PDF::API3::Compat::API2::Basic::PDF::Objind"))
298 0           { return $self->{$prop}->realise; }
299             else
300 0           { return $self->{$prop}; }
301             } elsif (defined $self->{'Parent'})
302 0           { return $self->{'Parent'}->find_prop($prop); }
303 0           return undef;
304             }
305            
306            
307             =head2 $p->add_font($pdf, $font)
308            
309             Creates or edits the resource dictionary at this level in the hierarchy. If
310             the font is already supported even through the hierarchy, then it is not added.
311            
312             =cut
313            
314             sub add_font
315             {
316 0     0 1   my ($self, $font, $pdf) = @_;
317 0           my ($name) = $font->{'Name'}->val;
318 0           my ($dict) = $self->find_prop('Resources');
319 0           my ($rdict);
320            
321 0 0 0       return $self if ($dict ne "" && defined $dict->{'Font'} && defined $dict->{'Font'}{$name});
      0        
322 0 0         unless (defined $self->{'Resources'})
323             {
324 0 0         $dict = $dict ne "" ? $dict->copy($pdf) : PDFDict();
325 0           $self->{'Resources'} = $dict;
326             }
327             else
328 0           { $dict = $self->{'Resources'}; }
329 0 0         $dict->{'Font'} = PDFDict() unless defined $self->{'Resources'}{'Font'};
330 0           $rdict = $dict->{'Font'}->val;
331 0 0         $rdict->{$name} = $font unless ($rdict->{$name});
332 0 0 0       if (ref $dict ne 'HASH' && $dict->is_obj($pdf))
333 0           { $pdf->out_obj($dict); }
334 0 0 0       if (ref $rdict ne 'HASH' && $rdict->is_obj($pdf))
335 0           { $pdf->out_obj($rdict); }
336 0           $self;
337             }
338            
339            
340             =head2 $p->bbox($xmin, $ymin, $xmax, $ymax, [$param])
341            
342             Specifies the bounding box for this and all child pages. If the values are
343             identical to those inherited then no change is made. $param specifies the attribute
344             name so that other 'bounding box'es can be set with this method.
345            
346             =cut
347            
348             sub bbox
349             {
350 0     0 1   my ($self, @bbox) = @_;
351 0   0       my ($str) = $bbox[4] || 'MediaBox';
352 0           my ($inh) = $self->find_prop($str);
353 0           my ($test, $i, $e);
354            
355 0 0         if ($inh ne "")
356             {
357 0           $test = 1; $i = 0;
  0            
358 0           foreach $e ($inh->elementsof)
359 0           { $test &= $e->val == $bbox[$i++]; }
360 0 0 0       return $self if $test && $i == 4;
361             }
362            
363 0           $inh = PDF::API3::Compat::API2::Basic::PDF::Array->new;
364 0           foreach $e (@bbox[0..3])
365 0           { $inh->add_elements(PDFNum($e)); }
366 0           $self->{$str} = $inh;
367 0           $self;
368             }
369            
370            
371             =head2 $p->proc_set(@entries)
372            
373             Ensures that the current resource contains all the entries in the proc_sets
374             listed. If necessary it creates a local resource dictionary to achieve this.
375            
376             =cut
377            
378             sub proc_set
379             {
380 0     0 1   my ($self, @entries) = @_;
381 0           my (@temp) = @entries;
382 0           my ($dict, $e);
383            
384 0           $dict = $self->find_prop('Resource');
385 0 0 0       if ($dict ne "" && defined $dict->{'ProcSet'})
386             {
387 0           foreach $e ($dict->{'ProcSet'}->elementsof)
388 0           { @temp = grep($_ ne $e, @temp); }
389 0 0         return $self if (scalar @temp == 0);
390 0 0         @entries = @temp if defined $self->{'Resources'};
391             }
392            
393 0 0         unless (defined $self->{'Resources'})
394 0 0         { $self->{'Resources'} = $dict ne "" ? $dict->copy : PDFDict(); }
395            
396 0 0         $self->{'Resources'}{'ProcSet'} = PDFArray() unless defined $self->{'ProcSet'};
397            
398 0           foreach $e (@entries)
399 0           { $self->{'Resources'}{'ProcSet'}->add_elements(PDFName($e)); }
400 0           $self;
401             }
402            
403             sub empty
404             {
405 0     0 1   my ($self) = @_;
406 0 0         my ($parent) = $self->{'Parent'} if defined ($self->{'Parent'});
407            
408 0           $self->SUPER::empty;
409 0 0         $self->{'Parent'} = $parent if defined $parent;
410 0           $self;
411             }
412            
413             sub dont_copy
414 0   0 0 0   { return $inst{$_[1]} || $_[0]->SUPER::dont_copy($_[1]); }
415            
416            
417             =head2 $p->get_top
418            
419             Returns the top of the pages tree
420            
421             =cut
422            
423             sub get_top
424             {
425 0     0 1   my ($self) = @_;
426 0           my ($p);
427            
428 0           for ($p = $self; defined $p->{'Parent'}; $p = $p->{'Parent'})
429             { }
430            
431 0           $p->realise;
432             }
433            
434             1;
435             __END__