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__
|