File Coverage

blib/lib/CAM/PDF/Annot.pm
Criterion Covered Total %
statement 70 72 97.2
branch 15 22 68.1
condition 1 2 50.0
subroutine 9 9 100.0
pod 2 2 100.0
total 97 107 90.6


line stmt bran cond sub pod time code
1             package CAM::PDF::Annot;
2              
3 2     2   50743 use 5.010000;
  2         7  
  2         65  
4 2     2   11 use strict;
  2         2  
  2         53  
5 2     2   8 use warnings;
  2         14  
  2         72  
6              
7             our $VERSION = '0.09';
8              
9 2     2   10 use base qw(CAM::PDF);
  2         2  
  2         3260  
10 2     2   95896 use Data::Dumper;
  2         22864  
  2         2358  
11              
12             =head1 NAME
13              
14             CAM::PDF::Annot - Perl extension for appending annotations on PDFs
15              
16             =head1 SYNOPSIS
17              
18             use strict;
19             use CAM::PDF::Annot;
20             my $pdf = CAM::PDF::Annot->new( 'pdf1.pdf' );
21             my $otherDoc = CAM::PDF::Annot->new( 'pdf2.pdf' );
22             for my $page ( 1 .. $pdf->numPages() ) {
23             my %refs;
24             for my $annotRef ( @{$pdf->getAnnotations( $page )} ) {
25             $otherDoc->appendAnnotation( $page, $pdf, $annotRef, \%refs );
26             }
27             }
28             $otherDoc->output('pdf_merged.pdf');
29              
30              
31             =head1 DESCRIPTION
32              
33             CAM::PDF::Annot is an extension to C to ease the appending of
34             Annotation objects to pdf documents.
35              
36             =head2 EXPORT
37              
38             This module does not export any functions.
39              
40             =cut
41              
42             =head2 METHODS
43              
44             =over
45              
46             =item CAM::PDF::Annot->new( 'file.pdf' );
47              
48             Constructor method, same as C.
49              
50             =cut
51              
52             #sub new {
53             # my $class = shift;
54             # my $self = $class->SUPER::new( @_ );
55             #
56             # bless $self, $class;
57             #}
58              
59             =item $doc->appendAnnotation($page, $doc, $annotRef, $refKeys) *NEW*
60              
61             Duplicate an annotation object from another PDF document and add it to this
62             document. It also copies its appearance object and Popup object. In case
63             this is a Text Subtype Annot object (a Reply to another Annot object) it
64             recurses to append the Annot object it refers to (using the IRT reference
65             of the object).
66              
67             It was only tested for annotations of /Type /Annot and /Subtype
68             /Square, /Circle, /Polygon and /Text. It is hardcoded to not allow any other
69             subtypes (sometime in the future this may change).
70              
71             It takes a hash reference C<$refKeys> and adds the altered keys so it can
72             be used across calls and update references across objects (and avoid
73             adding the same object more than once).
74              
75             =cut
76              
77             sub appendAnnotation($$$\%) {
78 28     28 1 3836 my ( $self, $page, $otherDoc, $otherAnnotRef, $refKeys ) = @_;
79              
80             # Sanity check: it only appends objects of /Type /Annot /Subtype /Square|Circle|Polygon|Text
81             # returns an empty hash reference
82 28 100       72 return {} if ( $otherDoc->getValue( $otherAnnotRef )->{Subtype}{value} !~ /(Square|Circle|Polygon|Text)/ );
83              
84             # If document does not have annots in this page, create an annots property
85 14 100       22865 unless ( exists $self->getPage( $page )->{Annots} ) {
86 2         1541 $self->getPage( $page )->{Annots} = CAM::PDF::Node->new('array',[], scalar $self->getPageObjnum( $page ),'0');
87             }
88              
89             # get this page's annotation object it will be widely used
90 14         1244 my $annots = $self->getPage( $page )->{Annots};
91             # dereferences the previous value in case the annots object was originaly a reference to the object itself...
92 14         125 $annots = $self->dereference( $annots->{value} )->{value} while $annots->{type} eq 'reference';
93              
94             # append the annot object based on the object number
95 14         60 my $newkey = $self->appendObject( $otherDoc, $otherAnnotRef->{value}, 0 );
96             # store the refkey for later
97 14         25813 $$refKeys{$otherAnnotRef->{value}} = $newkey;
98              
99             # append a reference to this annot to the annotations object of this page
100 14         79 my $annotRef = CAM::PDF::Node->new('reference', "$newkey", $self->getPageObjnum( $page ), '0');
101 14         436 push @{$annots->{value}}, $annotRef;
  14         42  
102              
103             # Append the appearance object (if it exists)
104 14         52 $self->_appendAppearanceObject( $otherDoc, $annotRef, $refKeys );
105              
106             # Append the popup object (if it exists)
107 14         97 $self->_appendPopupObject( $page, $otherDoc, $annotRef, { $otherAnnotRef->{value} => $newkey }, $refKeys );
108              
109             # Verify if it has an IRT reference (meaning, if it refers to another annotation)
110 14         55 my $annotVal = $self->getValue( $annotRef );
111 14 100       345 if ( exists $annotVal->{IRT} ) {
112             # Check if it is a reference to an already added object
113 8 50       69 unless ( exists $refKeys->{$annotVal->{IRT}{value}} ) {
114             # In this case the IRT must be added
115 0         0 $self->appendAnnotation( $page, $otherDoc, $annotVal->{IRT}, $refKeys );
116             }
117             }
118              
119             # Since the annots object was altered, let's flag it
120             # I dont know if it is necessary to store it in cache but it seems to work
121 14         45 $self->{objcache}{$annots->{objnum}} = $self->dereference( $annots->{objnum} );
122 14         142 $self->{changes}{$annots->{objnum}} = 1;
123 14         39 $self->{versions}{$annots->{objnum}} = -1;
124              
125             # Now, update all the references for the object
126 14         47 $self->changeRefKeys( $self->{objcache}{$newkey}, $refKeys );
127              
128 14 50       4388 if (wantarray) {
129 0         0 return ($newkey, %$refKeys);
130             }
131             else {
132 14         59 return $newkey;
133             }
134             }
135              
136             sub _appendAppearanceObject() {
137 14     14   34 my ( $self, $otherDoc, $annotRef, $refKeys ) = @_;
138 14         51 my $annotVal = $self->getValue( $annotRef );
139 14         374 my %refs =();
140              
141             # Check if this annot has a reference to an APeareance object
142             # (it is expected it will have it...)
143 14 50       51 if ( exists $annotVal->{AP} ) {
144 14         43 my $ap = $self->getValue( $annotVal->{AP} );
145             # Check if it wasn't already added before
146 14 50       163 unless ( exists $refKeys->{$ap->{N}{value}} ) {
147 14         64 my $apNkey = $self->appendObject( $otherDoc, $ap->{N}{value}, 0 );
148              
149             # keep track of this addition
150 14         40634 $$refKeys{$ap->{N}{value}} = $apNkey;
151 14         51 $refs{$ap->{N}{value}} = $apNkey;
152             }
153             # Apparently only for reply cases (in which the APearance object seems to have more than one element
154 14 100       61 if ( exists $ap->{D} ) {
155 8 50       43 unless ( exists $refKeys->{$ap->{D}{value}} ) {
156 8         41 my $apDkey = $self->appendObject( $otherDoc, $ap->{D}{value}, 0 );
157            
158             # keep track of this addition
159 8         22688 $$refKeys{$ap->{D}{value}} = $apDkey;
160 8         35 $refs{$ap->{D}{value}} = $apDkey;
161             }
162             }
163             }
164 14         59 return %refs;
165             }
166              
167             sub _appendPopupObject() {
168 14     14   40 my ( $self, $page, $otherDoc, $annotRef, $parentKeys, $refKeys ) = @_;
169 14         49 my $annotVal = $self->getValue( $annotRef );
170 14         409 my $annots = $self->getPage( $page )->{Annots};
171 14         139 my %refs =();
172              
173             # Now check if it has a reference to a popup object
174             # (it is bound to have it...)
175             # And also check if it wasnt already added
176 14 50       56 if ( exists $annotVal->{Popup} ) {
177 14 50       60 unless ( exists $refKeys->{$annotVal->{Popup}{value}} ) {
178 14         62 my $pupkey = $self->appendObject( $otherDoc, $annotVal->{Popup}{value}, 0 );
179 14         20500 $$refKeys{$annotVal->{Popup}{value}} = $pupkey;
180 14         42 $refs{$annotVal->{Popup}{value}} = $pupkey;
181            
182             # change its parent reference
183 14         74 $self->changeRefKeys( $self->{objcache}{$pupkey}, $parentKeys );
184              
185             # it also gets a place on the Annots property of the page object
186 14         2151 my $pupRef = $self->copyObject( $annotVal->{Popup} );
187             # change the keys in the newly created one to reflect the appended annotation object
188 14         2291 $self->changeRefKeys( $pupRef, { $pupRef->{value} => $pupkey } );
189 14         420 $self->setObjNum( $pupRef, $annots->{objnum} );
190 14         345 push @{$annots->{value}}, $pupRef;
  14         45  
191             }
192             }
193 14         43 return %refs;
194             }
195              
196             =item $doc->getAnnotations( $page )
197              
198             Returns an array reference to the Annots array of the page. The array
199             contains CAM::PDF::Nodes (see C) of type 'reference' refering
200             to the annotations.
201              
202             =cut
203              
204             sub getAnnotations($) {
205 4     4 1 42274 my ( $self, $p ) = @_;
206 4   50     25 return $self->getValue( $self->getPage( $p )->{Annots} ) || [];
207             }
208              
209             1;
210             __END__