File Coverage

blib/lib/CAM/PDF/Annot.pm
Criterion Covered Total %
statement 17 75 22.6
branch 0 22 0.0
condition 0 2 0.0
subroutine 6 10 60.0
pod 3 3 100.0
total 26 112 23.2


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