File Coverage

blib/lib/CAM/PDF/Annot.pm
Criterion Covered Total %
statement 69 71 97.1
branch 15 22 68.1
condition 1 2 50.0
subroutine 9 9 100.0
pod 2 2 100.0
total 96 106 90.5


line stmt bran cond sub pod time code
1             package CAM::PDF::Annot;
2              
3 2     2   90808 use 5.010000;
  2         7  
4 2     2   10 use strict;
  2         4  
  2         40  
5 2     2   8 use warnings;
  2         4  
  2         94  
6              
7             our $VERSION = '0.10';
8              
9 2     2   12 use base qw(CAM::PDF);
  2         4  
  2         2100  
10 2     2   70234 use Data::Dumper;
  2         13511  
  2         1730  
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 4792 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       55 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       32485 unless ( exists $self->getPage( $page )->{Annots} ) {
86 2         2100 $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         1426 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         128 $annots = $self->dereference( $annots->{value} )->{value} while $annots->{type} eq 'reference';
93              
94             # append the annot object based on the object number
95 14         47 my $newkey = $self->appendObject( $otherDoc, $otherAnnotRef->{value}, 0 );
96             # store the refkey for later
97 14         24130 $$refKeys{$otherAnnotRef->{value}} = $newkey;
98              
99             # append a reference to this annot to the annotations object of this page
100 14         60 my $annotRef = CAM::PDF::Node->new('reference', "$newkey", $self->getPageObjnum( $page ), '0');
101 14         531 push @{$annots->{value}}, $annotRef;
  14         38  
102              
103             # Append the appearance object (if it exists)
104 14         51 $self->_appendAppearanceObject( $otherDoc, $annotRef, $refKeys );
105              
106             # Append the popup object (if it exists)
107 14         67 $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         47 my $annotVal = $self->getValue( $annotRef );
111 14 100       417 if ( exists $annotVal->{IRT} ) {
112             # Check if it is a reference to an already added object
113 8 50       40 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         37 $self->{objcache}{$annots->{objnum}} = $self->dereference( $annots->{objnum} );
122 14         158 $self->{changes}{$annots->{objnum}} = 1;
123 14         31 $self->{versions}{$annots->{objnum}} = -1;
124              
125             # Now, update all the references for the object
126 14         40 $self->changeRefKeys( $self->{objcache}{$newkey}, $refKeys );
127              
128 14 50       6332 if (wantarray) {
129 0         0 return ($newkey, %$refKeys);
130             }
131             else {
132 14         44 return $newkey;
133             }
134             }
135              
136             sub _appendAppearanceObject() {
137 14     14   30 my ( $self, $otherDoc, $annotRef, $refKeys ) = @_;
138 14         41 my $annotVal = $self->getValue( $annotRef );
139 14         429 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       42 if ( exists $annotVal->{AP} ) {
144 14         34 my $ap = $self->getValue( $annotVal->{AP} );
145             # Check if it wasn't already added before
146 14 50       131 unless ( exists $refKeys->{$ap->{N}{value}} ) {
147 14         45 my $apNkey = $self->appendObject( $otherDoc, $ap->{N}{value}, 0 );
148              
149             # keep track of this addition
150 14         44097 $$refKeys{$ap->{N}{value}} = $apNkey;
151 14         34 $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       49 if ( exists $ap->{D} ) {
155 8 50       30 unless ( exists $refKeys->{$ap->{D}{value}} ) {
156 8         29 my $apDkey = $self->appendObject( $otherDoc, $ap->{D}{value}, 0 );
157            
158             # keep track of this addition
159 8         25232 $$refKeys{$ap->{D}{value}} = $apDkey;
160 8         24 $refs{$ap->{D}{value}} = $apDkey;
161             }
162             }
163             }
164 14         35 return %refs;
165             }
166              
167             sub _appendPopupObject() {
168 14     14   35 my ( $self, $page, $otherDoc, $annotRef, $parentKeys, $refKeys ) = @_;
169 14         37 my $annotVal = $self->getValue( $annotRef );
170 14         449 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       39 if ( exists $annotVal->{Popup} ) {
177 14 50       47 unless ( exists $refKeys->{$annotVal->{Popup}{value}} ) {
178 14         39 my $pupkey = $self->appendObject( $otherDoc, $annotVal->{Popup}{value}, 0 );
179 14         23946 $$refKeys{$annotVal->{Popup}{value}} = $pupkey;
180 14         36 $refs{$annotVal->{Popup}{value}} = $pupkey;
181            
182             # change its parent reference
183 14         62 $self->changeRefKeys( $self->{objcache}{$pupkey}, $parentKeys );
184              
185             # it also gets a place on the Annots property of the page object
186 14         3085 my $pupRef = $self->copyObject( $annotVal->{Popup} );
187             # change the keys in the newly created one to reflect the appended annotation object
188 14         2495 $self->changeRefKeys( $pupRef, { $pupRef->{value} => $pupkey } );
189 14         599 $self->setObjNum( $pupRef, $annots->{objnum} );
190 14         578 push @{$annots->{value}}, $pupRef;
  14         53  
191             }
192             }
193 14         40 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 58466 my ( $self, $p ) = @_;
206 4   50     22 return $self->getValue( $self->getPage( $p )->{Annots} ) || [];
207             }
208              
209             1;
210             __END__